Compare commits
9 Commits
642ce66f75
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 663fe39070 | |||
| 6b75e028ff | |||
| 878157c2fc | |||
| 35e479fd05 | |||
| 51e831cc6a | |||
| 35b98c4d7f | |||
| 877253cbbc | |||
| cfcdfde170 | |||
| 5e75402b43 |
@@ -6,13 +6,13 @@ Language: Cpp
|
|||||||
AccessModifierOffset: -4
|
AccessModifierOffset: -4
|
||||||
|
|
||||||
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: DontAlign
|
||||||
|
|
||||||
# 连续赋值时,对齐所有等号
|
# 连续赋值时,对齐所有等号
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: true
|
||||||
|
|
||||||
# 连续声明时,对齐所有声明的变量名
|
# 连续声明时,对齐所有声明的变量名
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: true
|
||||||
|
|
||||||
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
||||||
AlignEscapedNewlines: Right
|
AlignEscapedNewlines: Right
|
||||||
@@ -33,13 +33,13 @@ AllowShortBlocksOnASingleLine: true
|
|||||||
AllowShortCaseLabelsOnASingleLine: true
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
|
||||||
# 允许短的if语句保持在同一行
|
# 允许短的if语句保持在同一行
|
||||||
AllowShortIfStatementsOnASingleLine: true
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
|
||||||
# 允许短的循环保持在同一行
|
# 允许短的循环保持在同一行
|
||||||
AllowShortLoopsOnASingleLine: true
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
|
||||||
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
||||||
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
||||||
@@ -109,7 +109,6 @@ BreakStringLiterals: false
|
|||||||
|
|
||||||
# 每行字符的限制,0表示没有限制
|
# 每行字符的限制,0表示没有限制
|
||||||
ColumnLimit: 120
|
ColumnLimit: 120
|
||||||
|
|
||||||
CompactNamespaces: true
|
CompactNamespaces: true
|
||||||
|
|
||||||
# 构造函数的初始化列表要么都在同一行,要么都各自一行
|
# 构造函数的初始化列表要么都在同一行,要么都各自一行
|
||||||
@@ -157,7 +156,7 @@ PointerAlignment: Right
|
|||||||
ReflowComments: true
|
ReflowComments: true
|
||||||
|
|
||||||
# 允许排序#include
|
# 允许排序#include
|
||||||
SortIncludes: false
|
SortIncludes: true
|
||||||
|
|
||||||
# 允许排序 using 声明
|
# 允许排序 using 声明
|
||||||
SortUsingDeclarations: false
|
SortUsingDeclarations: false
|
||||||
|
|||||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Xmake cache
|
||||||
|
.xmake/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# MacOS Cache
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.VSCodeCounter
|
||||||
|
|
||||||
|
/test.fig
|
||||||
|
Before Width: | Height: | Size: 633 KiB After Width: | Height: | Size: 633 KiB |
14
src/Ast/Ast.hpp
Normal file
14
src/Ast/Ast.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Ast.hpp
|
||||||
|
@brief Ast总链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Expr/IdentiExpr.hpp>
|
||||||
|
#include <Ast/Expr/InfixExpr.hpp>
|
||||||
|
#include <Ast/Expr/LiteralExpr.hpp>
|
||||||
|
#include <Ast/Expr/PrefixExpr.hpp>
|
||||||
|
|
||||||
51
src/Ast/Base.hpp
Normal file
51
src/Ast/Base.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Base.hpp
|
||||||
|
@brief AstNode基类定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class AstType : std::uint8_t
|
||||||
|
{
|
||||||
|
AstNode, // 基类
|
||||||
|
Expr, // 表达式
|
||||||
|
Stmt, // 语句
|
||||||
|
|
||||||
|
IdentiExpr, // 标识符表达式
|
||||||
|
LiteralExpr, // 字面量表达式
|
||||||
|
PrefixExpr, // 一元 前缀表达式
|
||||||
|
InfixExpr, // 二元 中缀表达式
|
||||||
|
};
|
||||||
|
struct AstNode
|
||||||
|
{
|
||||||
|
AstType type = AstType::AstNode;
|
||||||
|
SourceLocation location;
|
||||||
|
|
||||||
|
virtual String toString() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr : public AstNode
|
||||||
|
{
|
||||||
|
Expr()
|
||||||
|
{
|
||||||
|
type = AstType::Expr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stmt : public AstNode
|
||||||
|
{
|
||||||
|
Stmt()
|
||||||
|
{
|
||||||
|
type = AstType::Stmt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
36
src/Ast/Expr/IdentiExpr.hpp
Normal file
36
src/Ast/Expr/IdentiExpr.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/IdentiExpr.hpp
|
||||||
|
@brief IdentiExpr定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct IdentiExpr final : Expr
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
|
||||||
|
IdentiExpr()
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentiExpr(String _name, SourceLocation _loc)
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
name = std::move(_name);
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<IdentiExpr: {}>", name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
39
src/Ast/Expr/InfixExpr.hpp
Normal file
39
src/Ast/Expr/InfixExpr.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/InfixExpr.hpp
|
||||||
|
@brief 中缀表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct InfixExpr final : Expr
|
||||||
|
{
|
||||||
|
Expr *left;
|
||||||
|
BinaryOperator op;
|
||||||
|
Expr *right;
|
||||||
|
|
||||||
|
InfixExpr()
|
||||||
|
{
|
||||||
|
type = AstType::InfixExpr;
|
||||||
|
}
|
||||||
|
InfixExpr(Expr *_left, BinaryOperator _op, Expr *_right) :
|
||||||
|
left(_left), op(_op), right(_right)
|
||||||
|
{
|
||||||
|
type = AstType::InfixExpr;
|
||||||
|
location = _left->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<InfixExpr: '{}' {} '{}'>", left->toString(), magic_enum::enum_name(op), right->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
36
src/Ast/Expr/LiteralExpr.hpp
Normal file
36
src/Ast/Expr/LiteralExpr.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/LiteralExpr.hpp
|
||||||
|
@brief 字面量表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct LiteralExpr final : Expr
|
||||||
|
{
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
LiteralExpr()
|
||||||
|
{
|
||||||
|
type = AstType::LiteralExpr;
|
||||||
|
}
|
||||||
|
LiteralExpr(const Token& token, SourceLocation _location) : token(token)
|
||||||
|
{
|
||||||
|
type = AstType::LiteralExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(token.type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
39
src/Ast/Expr/PrefixExpr.hpp
Normal file
39
src/Ast/Expr/PrefixExpr.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/PrefixExpr.hpp
|
||||||
|
@brief 前缀表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct PrefixExpr final : Expr
|
||||||
|
{
|
||||||
|
UnaryOperator op;
|
||||||
|
Expr *operand;
|
||||||
|
|
||||||
|
PrefixExpr()
|
||||||
|
{
|
||||||
|
type = AstType::PrefixExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrefixExpr(UnaryOperator _op, Expr *_operand) :
|
||||||
|
op(_op), operand(_operand)
|
||||||
|
{
|
||||||
|
type = AstType::PrefixExpr;
|
||||||
|
location = _operand->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<PrefixExpr: {} '{}'>", magic_enum::enum_name(op), operand->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
184
src/Ast/Operator.cpp
Normal file
184
src/Ast/Operator.cpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.cpp
|
||||||
|
@brief 运算符定义内函数实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, UnaryOperator> unaryOpMap{
|
||||||
|
{TokenType::Tilde, UnaryOperator::BitNot},
|
||||||
|
{TokenType::Minus, UnaryOperator::Negate},
|
||||||
|
{TokenType::Not, UnaryOperator::Not},
|
||||||
|
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
||||||
|
};
|
||||||
|
return unaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, BinaryOperator> binaryOpMap{
|
||||||
|
{TokenType::Plus, BinaryOperator::Add},
|
||||||
|
{TokenType::Minus, BinaryOperator::Subtract},
|
||||||
|
{TokenType::Asterisk, BinaryOperator::Multiply},
|
||||||
|
{TokenType::Slash, BinaryOperator::Divide},
|
||||||
|
{TokenType::Percent, BinaryOperator::Modulo},
|
||||||
|
|
||||||
|
{TokenType::Equal, BinaryOperator::Equal},
|
||||||
|
{TokenType::NotEqual, BinaryOperator::NotEqual},
|
||||||
|
{TokenType::Less, BinaryOperator::Less},
|
||||||
|
{TokenType::Greater, BinaryOperator::Greater},
|
||||||
|
{TokenType::LessEqual, BinaryOperator::LessEqual},
|
||||||
|
{TokenType::GreaterEqual, BinaryOperator::GreaterEqual},
|
||||||
|
|
||||||
|
{TokenType::Is, BinaryOperator::Is},
|
||||||
|
|
||||||
|
{TokenType::And, BinaryOperator::LogicalAnd},
|
||||||
|
{TokenType::Or, BinaryOperator::LogicalOr},
|
||||||
|
|
||||||
|
{TokenType::Power, BinaryOperator::Power},
|
||||||
|
|
||||||
|
{TokenType::Assign, BinaryOperator::Assign},
|
||||||
|
{TokenType::PlusEqual, BinaryOperator::AddAssign},
|
||||||
|
{TokenType::MinusEqual, BinaryOperator::SubAssign},
|
||||||
|
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
||||||
|
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
||||||
|
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
||||||
|
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||||
|
|
||||||
|
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||||
|
{TokenType::Ampersand, BinaryOperator::BitAnd},
|
||||||
|
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||||
|
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||||
|
|
||||||
|
{TokenType::Dot, BinaryOperator::MemberAccess},
|
||||||
|
};
|
||||||
|
return binaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赋值 < 三元 < 逻辑或 < 逻辑与 < 位运算 < 比较 < 位移 < 加减 < 乘除 < 幂 < 一元 < 成员访问 < (后缀)
|
||||||
|
|
||||||
|
/*
|
||||||
|
暂划分:
|
||||||
|
二元运算符:0 - 20000
|
||||||
|
一元运算符:20001 - 40000
|
||||||
|
后缀/成员/其他:40001 - 60001
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<UnaryOperator, BindingPower> unbpm{
|
||||||
|
{UnaryOperator::BitNot, 20001},
|
||||||
|
{UnaryOperator::Negate, 20001},
|
||||||
|
{UnaryOperator::Not, 20001},
|
||||||
|
{UnaryOperator::AddressOf, 20001},
|
||||||
|
};
|
||||||
|
return unbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<BinaryOperator, BindingPower> bnbpm{
|
||||||
|
{BinaryOperator::Assign, 100},
|
||||||
|
{BinaryOperator::AddAssign, 100},
|
||||||
|
{BinaryOperator::SubAssign, 100},
|
||||||
|
{BinaryOperator::MultiplyAssign, 100},
|
||||||
|
{BinaryOperator::DivideAssign, 100},
|
||||||
|
{BinaryOperator::ModuloAssign, 100},
|
||||||
|
{BinaryOperator::BitXorAssign, 100},
|
||||||
|
|
||||||
|
{BinaryOperator::LogicalOr, 500},
|
||||||
|
{BinaryOperator::LogicalAnd, 550},
|
||||||
|
|
||||||
|
{BinaryOperator::BitOr, 1000},
|
||||||
|
{BinaryOperator::BitXor, 1100},
|
||||||
|
{BinaryOperator::BitAnd, 1200},
|
||||||
|
|
||||||
|
{BinaryOperator::Equal, 2000},
|
||||||
|
{BinaryOperator::NotEqual, 2000},
|
||||||
|
|
||||||
|
{BinaryOperator::Less, 2100},
|
||||||
|
{BinaryOperator::LessEqual, 2100},
|
||||||
|
{BinaryOperator::Greater, 2100},
|
||||||
|
{BinaryOperator::GreaterEqual, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::Is, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::ShiftLeft, 3000},
|
||||||
|
{BinaryOperator::ShiftRight, 3000},
|
||||||
|
|
||||||
|
{BinaryOperator::Add, 4000},
|
||||||
|
{BinaryOperator::Subtract, 4000},
|
||||||
|
{BinaryOperator::Multiply, 4500},
|
||||||
|
{BinaryOperator::Divide, 4500},
|
||||||
|
|
||||||
|
{BinaryOperator::Power, 5000},
|
||||||
|
|
||||||
|
{BinaryOperator::MemberAccess, 40001},
|
||||||
|
};
|
||||||
|
return bnbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator op)
|
||||||
|
{
|
||||||
|
return GetUnaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return GetBinaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
右结合,左绑定力 >= 右
|
||||||
|
a = b = c
|
||||||
|
a = (b = c)
|
||||||
|
*/
|
||||||
|
case BinaryOperator::Assign:
|
||||||
|
case BinaryOperator::AddAssign:
|
||||||
|
case BinaryOperator::SubAssign:
|
||||||
|
case BinaryOperator::MultiplyAssign:
|
||||||
|
case BinaryOperator::DivideAssign:
|
||||||
|
case BinaryOperator::ModuloAssign:
|
||||||
|
case BinaryOperator::BitXorAssign:
|
||||||
|
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
左结合, 左绑定力 < 右
|
||||||
|
a * b * c
|
||||||
|
(a * b) * c
|
||||||
|
*/
|
||||||
|
return GetBinaryOpLBp(op) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary /* = true*/)
|
||||||
|
{
|
||||||
|
if (binary)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
return GetUnaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetUnaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
82
src/Ast/Operator.hpp
Normal file
82
src/Ast/Operator.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.hpp
|
||||||
|
@brief 运算符定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class UnaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
BitNot, // 位运算取反 ~
|
||||||
|
Negate, // 取反 -
|
||||||
|
Not, // 逻辑非 ! / not
|
||||||
|
AddressOf, // 取引用 &
|
||||||
|
};
|
||||||
|
enum class BinaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
Add, // 加 +
|
||||||
|
Subtract, // 减 -
|
||||||
|
Multiply, // 乘 *
|
||||||
|
Divide, // 除 /
|
||||||
|
Modulo, // 取模 %
|
||||||
|
|
||||||
|
Equal, // 等于 ==
|
||||||
|
NotEqual, // 不等于 !=
|
||||||
|
Less, // 小于 <
|
||||||
|
Greater, // 大于 >
|
||||||
|
LessEqual, // 小于等于 <=
|
||||||
|
GreaterEqual, // 大于等于 >=
|
||||||
|
|
||||||
|
Is, // is操作符
|
||||||
|
|
||||||
|
LogicalAnd, // 逻辑与 && / and
|
||||||
|
LogicalOr, // 逻辑或 || / or
|
||||||
|
|
||||||
|
Power, // 幂运算 **
|
||||||
|
|
||||||
|
Assign, // 赋值(修改) =
|
||||||
|
AddAssign, // +=
|
||||||
|
SubAssign, // -=
|
||||||
|
MultiplyAssign, // *=
|
||||||
|
DivideAssign, // /=
|
||||||
|
ModuloAssign, // %=
|
||||||
|
BitXorAssign, // ^=
|
||||||
|
|
||||||
|
// 位运算
|
||||||
|
BitAnd, // 按位与 &
|
||||||
|
BitOr, // 按位或 |
|
||||||
|
BitXor, // 异或 ^
|
||||||
|
ShiftLeft, // 左移
|
||||||
|
ShiftRight, // 右移
|
||||||
|
|
||||||
|
// 成员访问
|
||||||
|
MemberAccess, // .
|
||||||
|
};
|
||||||
|
|
||||||
|
using BindingPower = unsigned int;
|
||||||
|
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap();
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap();
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap();
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap();
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator);
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator);
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator);
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary = true);
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &);
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &);
|
||||||
|
}; // namespace Fig
|
||||||
13
src/Core/Core.hpp
Normal file
13
src/Core/Core.hpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/Core.hpp
|
||||||
|
@brief Core总合集
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
#include <Core/RuntimeTime.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
37
src/Core/CoreIO.cpp
Normal file
37
src/Core/CoreIO.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreIO.cpp
|
||||||
|
@brief 标准输入输出链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
|
||||||
|
namespace Fig::CoreIO
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) || defined(__APPLE__) || defined (__linux__) || defined (__unix__)
|
||||||
|
std::ostream &GetStdOut()
|
||||||
|
{
|
||||||
|
static std::ostream &out = std::cout;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdErr()
|
||||||
|
{
|
||||||
|
static std::ostream &err = std::cerr;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdLog()
|
||||||
|
{
|
||||||
|
static std::ostream &log = std::clog;
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
std::istream &GetStdCin()
|
||||||
|
{
|
||||||
|
static std::istream &cin = std::cin;
|
||||||
|
return cin;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// link
|
||||||
|
#endif
|
||||||
|
};
|
||||||
18
src/Core/CoreIO.hpp
Normal file
18
src/Core/CoreIO.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreIO.hpp
|
||||||
|
@brief 标准输入输出链接定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Fig::CoreIO
|
||||||
|
{
|
||||||
|
std::ostream &GetStdOut();
|
||||||
|
std::ostream &GetStdErr();
|
||||||
|
std::ostream &GetStdLog();
|
||||||
|
std::istream &GetStdCin();
|
||||||
|
};
|
||||||
67
src/Core/CoreInfos.hpp
Normal file
67
src/Core/CoreInfos.hpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreInfos.hpp
|
||||||
|
@brief 核心系统信息
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/String/String.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#define __FCORE_VERSION "0.5.0-alpha"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define __FCORE_PLATFORM "Windows"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define __FCORE_PLATFORM "Apple"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define __FCORE_PLATFORM "Linux"
|
||||||
|
#elif defined(__unix__)
|
||||||
|
#define __FCORE_PLATFORM "Unix"
|
||||||
|
#else
|
||||||
|
#define __FCORE_PLATFORM "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "llvm-mingw"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "MinGW"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "GCC"
|
||||||
|
#endif
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "Clang"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define __FCORE_COMPILER "MSVC"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SIZE_MAX == 18446744073709551615ull
|
||||||
|
#define __FCORE_ARCH "64"
|
||||||
|
#else
|
||||||
|
#define __FCORE_ARCH "86"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __FCORE_LINK_DEPS
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
||||||
|
inline constexpr std::string_view LICENSE = "MIT";
|
||||||
|
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
||||||
|
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
||||||
|
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||||
|
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||||
|
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||||
|
}; // namespace Core
|
||||||
|
}; // namespace Fig
|
||||||
25
src/Core/RuntimeTime.cpp
Normal file
25
src/Core/RuntimeTime.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/RuntimeTime.cpp
|
||||||
|
@brief 系统时间库实现(steady_clock)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/RuntimeTime.hpp>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace Fig::Time
|
||||||
|
{
|
||||||
|
Clock::time_point start_time;
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
static bool flag = false;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
start_time = Clock::now();
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
17
src/Core/RuntimeTime.hpp
Normal file
17
src/Core/RuntimeTime.hpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/RuntimeTime.hpp
|
||||||
|
@brief 系统时间库定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace Fig::Time
|
||||||
|
{
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
extern Clock::time_point start_time; // since process start
|
||||||
|
void init();
|
||||||
|
};
|
||||||
59
src/Core/SourceLocations.hpp
Normal file
59
src/Core/SourceLocations.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/SourceLocations
|
||||||
|
@brief SourcePosition + SourceLocation定义,全局代码定位
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct SourcePosition
|
||||||
|
{
|
||||||
|
size_t line, column, tok_length;
|
||||||
|
|
||||||
|
SourcePosition() { line = column = tok_length = 0; }
|
||||||
|
SourcePosition(size_t _line, size_t _column, size_t _tok_length)
|
||||||
|
{
|
||||||
|
line = _line;
|
||||||
|
column = _column;
|
||||||
|
tok_length = _tok_length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourceLocation
|
||||||
|
{
|
||||||
|
SourcePosition sp;
|
||||||
|
|
||||||
|
Deps::String fileName;
|
||||||
|
Deps::String packageName;
|
||||||
|
Deps::String functionName;
|
||||||
|
|
||||||
|
SourceLocation() {}
|
||||||
|
SourceLocation(SourcePosition _sp,
|
||||||
|
Deps::String _fileName,
|
||||||
|
Deps::String _packageName,
|
||||||
|
Deps::String _functionName)
|
||||||
|
{
|
||||||
|
sp = std::move(_sp);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
packageName = std::move(_packageName);
|
||||||
|
functionName = std::move(_functionName);
|
||||||
|
}
|
||||||
|
SourceLocation(size_t line,
|
||||||
|
size_t column,
|
||||||
|
size_t tok_length,
|
||||||
|
Deps::String _fileName,
|
||||||
|
Deps::String _packageName,
|
||||||
|
Deps::String _functionName)
|
||||||
|
{
|
||||||
|
sp = SourcePosition(line, column, tok_length);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
packageName = std::move(_packageName);
|
||||||
|
functionName = std::move(_functionName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
34
src/Deps/Deps.hpp
Normal file
34
src/Deps/Deps.hpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/Deps.hpp
|
||||||
|
@brief 依赖库集合
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
#include <Deps/DynArray/DynArray.hpp>
|
||||||
|
#include <Deps/HashMap/HashMap.hpp>
|
||||||
|
#include <Deps/String/CharUtils.hpp>
|
||||||
|
#include <Deps/String/String.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <expected>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
#ifdef __FCORE_LINK_DEPS
|
||||||
|
using Deps::String;
|
||||||
|
using Deps::HashMap;
|
||||||
|
using Deps::CharUtils;
|
||||||
|
using Deps::DynArray;
|
||||||
|
|
||||||
|
template <class _Tp, class _Err>
|
||||||
|
using Result = std::expected<_Tp, _Err>;
|
||||||
|
#endif
|
||||||
|
}; // namespace Fig
|
||||||
14
src/Deps/DynArray/DynArray.hpp
Normal file
14
src/Deps/DynArray/DynArray.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/DynArray/DynArray
|
||||||
|
@brief 依赖库DynArray定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
template<class _Tp, class _Allocator = std::allocator<_Tp>>
|
||||||
|
using DynArray = std::vector<_Tp, _Allocator>;
|
||||||
|
};
|
||||||
20
src/Deps/HashMap/HashMap.hpp
Normal file
20
src/Deps/HashMap/HashMap.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/HashMap/HashMap.hpp
|
||||||
|
@brief 依赖库HashMap定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
template <class _Key,
|
||||||
|
class _Tp,
|
||||||
|
class _Hash = std::hash<_Key>,
|
||||||
|
class _Pred = std::equal_to<_Key>,
|
||||||
|
class _Alloc = std::allocator<std::pair<const _Key, _Tp> >>
|
||||||
|
using HashMap = std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>;
|
||||||
|
};
|
||||||
130
src/Deps/String/CharUtils.hpp
Normal file
130
src/Deps/String/CharUtils.hpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/String/CharUtils.hpp
|
||||||
|
@brief char32_t type实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
class CharUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using U32 = char32_t;
|
||||||
|
|
||||||
|
// ===== 基础 =====
|
||||||
|
|
||||||
|
static constexpr bool isValidScalar(U32 c) noexcept { return c <= 0x10FFFF && !(c >= 0xD800 && c <= 0xDFFF); }
|
||||||
|
|
||||||
|
static constexpr bool isAscii(U32 c) noexcept { return c <= 0x7F; }
|
||||||
|
|
||||||
|
static constexpr bool isControl(U32 c) noexcept { return (c <= 0x1F) || (c == 0x7F); }
|
||||||
|
|
||||||
|
static constexpr bool isPrintable(U32 c) noexcept { return !isControl(c); }
|
||||||
|
|
||||||
|
// ===== ASCII 分类 =====
|
||||||
|
|
||||||
|
static constexpr bool isAsciiLower(U32 c) noexcept { return c >= U'a' && c <= U'z'; }
|
||||||
|
static constexpr bool isAsciiUpper(U32 c) noexcept { return c >= U'A' && c <= U'Z'; }
|
||||||
|
static constexpr bool isAsciiAlpha(U32 c) noexcept { return isAsciiLower(c) || isAsciiUpper(c); }
|
||||||
|
static constexpr bool isAsciiDigit(U32 c) noexcept { return c >= U'0' && c <= U'9'; }
|
||||||
|
|
||||||
|
static constexpr bool isAsciiHexDigit(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return isAsciiDigit(c) || (c >= U'a' && c <= U'f') || (c >= U'A' && c <= U'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool isAsciiSpace(U32 c) noexcept { return c == U' ' || (c >= 0x09 && c <= 0x0D); }
|
||||||
|
|
||||||
|
static constexpr bool isAsciiPunct(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return (c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode White_Space =====
|
||||||
|
|
||||||
|
static constexpr bool isSpace(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiSpace(c);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0x0085:
|
||||||
|
case 0x00A0:
|
||||||
|
case 0x1680:
|
||||||
|
case 0x2000:
|
||||||
|
case 0x2001:
|
||||||
|
case 0x2002:
|
||||||
|
case 0x2003:
|
||||||
|
case 0x2004:
|
||||||
|
case 0x2005:
|
||||||
|
case 0x2006:
|
||||||
|
case 0x2007:
|
||||||
|
case 0x2008:
|
||||||
|
case 0x2009:
|
||||||
|
case 0x200A:
|
||||||
|
case 0x2028:
|
||||||
|
case 0x2029:
|
||||||
|
case 0x202F:
|
||||||
|
case 0x205F:
|
||||||
|
case 0x3000: return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode Decimal_Number =====
|
||||||
|
|
||||||
|
static constexpr bool isDigit(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiDigit(c);
|
||||||
|
|
||||||
|
return (c >= 0x0660 && c <= 0x0669) || (c >= 0x06F0 && c <= 0x06F9) || (c >= 0x0966 && c <= 0x096F)
|
||||||
|
|| (c >= 0x09E6 && c <= 0x09EF) || (c >= 0x0A66 && c <= 0x0A6F) || (c >= 0x0AE6 && c <= 0x0AEF)
|
||||||
|
|| (c >= 0x0B66 && c <= 0x0B6F) || (c >= 0x0BE6 && c <= 0x0BEF) || (c >= 0x0C66 && c <= 0x0C6F)
|
||||||
|
|| (c >= 0x0CE6 && c <= 0x0CEF) || (c >= 0x0D66 && c <= 0x0D6F) || (c >= 0x0E50 && c <= 0x0E59)
|
||||||
|
|| (c >= 0x0ED0 && c <= 0x0ED9) || (c >= 0x0F20 && c <= 0x0F29) || (c >= 0x1040 && c <= 0x1049)
|
||||||
|
|| (c >= 0x17E0 && c <= 0x17E9) || (c >= 0x1810 && c <= 0x1819) || (c >= 0xFF10 && c <= 0xFF19);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode Letter =====
|
||||||
|
|
||||||
|
static constexpr bool isAlpha(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiAlpha(c);
|
||||||
|
|
||||||
|
return (c >= 0x00C0 && c <= 0x02AF) || (c >= 0x0370 && c <= 0x052F) || (c >= 0x0530 && c <= 0x058F)
|
||||||
|
|| (c >= 0x0590 && c <= 0x05FF) || (c >= 0x0600 && c <= 0x06FF) || (c >= 0x0900 && c <= 0x097F)
|
||||||
|
|| (c >= 0x3040 && c <= 0x30FF) || (c >= 0x3100 && c <= 0x312F) || (c >= 0x4E00 && c <= 0x9FFF)
|
||||||
|
|| (c >= 0xAC00 && c <= 0xD7AF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 标点 / 符号 / 分隔符(工程近似)=====
|
||||||
|
|
||||||
|
static constexpr bool isPunct(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiPunct(c);
|
||||||
|
return (c >= 0x2000 && c <= 0x206F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool isSymbol(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return (c >= 0x20A0 && c <= 0x20CF) || // currency
|
||||||
|
(c >= 0x2100 && c <= 0x214F) || // letterlike
|
||||||
|
(c >= 0x2190 && c <= 0x21FF) || // arrows
|
||||||
|
(c >= 0x2600 && c <= 0x26FF) || // misc symbols
|
||||||
|
(c >= 0x1F300 && c <= 0x1FAFF); // emoji block
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 组合 =====
|
||||||
|
|
||||||
|
static constexpr bool isAlnum(U32 c) noexcept { return isAlpha(c) || isDigit(c); }
|
||||||
|
|
||||||
|
static constexpr bool isHexDigit(U32 c) noexcept { return isAsciiHexDigit(c); }
|
||||||
|
|
||||||
|
static constexpr bool isIdentifierStart(U32 c) noexcept { return isAlpha(c) || c == U'_'; }
|
||||||
|
|
||||||
|
static constexpr bool isIdentifierContinue(U32 c) noexcept { return isAlnum(c) || c == U'_'; }
|
||||||
|
};
|
||||||
|
};
|
||||||
1094
src/Deps/String/String.hpp
Normal file
1094
src/Deps/String/String.hpp
Normal file
File diff suppressed because it is too large
Load Diff
144
src/Deps/String/StringTest.cpp
Normal file
144
src/Deps/String/StringTest.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/String/StringTest.cpp
|
||||||
|
@brief String类测试代码
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include "String.hpp"
|
||||||
|
|
||||||
|
using Fig::Deps::String;
|
||||||
|
|
||||||
|
static void test_ascii_sso()
|
||||||
|
{
|
||||||
|
String s("hello");
|
||||||
|
assert(s.size() == 5);
|
||||||
|
assert(s[0] == U'h');
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
s.push_back(U'!');
|
||||||
|
assert(s.toStdString() == "hello!");
|
||||||
|
|
||||||
|
s.pop_back();
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
assert(s.starts_with("he"));
|
||||||
|
assert(s.ends_with("lo"));
|
||||||
|
assert(s.contains(U'e'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ascii_heap()
|
||||||
|
{
|
||||||
|
String a("abcdefghijklmnopqrstuvwxyz"); // > SSO
|
||||||
|
assert(a.size() == 26);
|
||||||
|
|
||||||
|
String b("123");
|
||||||
|
a += b;
|
||||||
|
|
||||||
|
assert(a.ends_with("123"));
|
||||||
|
assert(a.find(U'1') == 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_utf8_decode()
|
||||||
|
{
|
||||||
|
String s("你好");
|
||||||
|
assert(s.size() == 2);
|
||||||
|
assert(s.toStdString() == "你好");
|
||||||
|
|
||||||
|
s.push_back(U'!');
|
||||||
|
assert(s.toStdString() == "你好!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_concat_modes()
|
||||||
|
{
|
||||||
|
String a("abc");
|
||||||
|
String b("你好");
|
||||||
|
|
||||||
|
String c = a + b;
|
||||||
|
assert(c.size() == 5);
|
||||||
|
assert(c.toStdString() == "abc你好");
|
||||||
|
|
||||||
|
String d = b + a;
|
||||||
|
assert(d.toStdString() == "你好abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_substr_erase_insert()
|
||||||
|
{
|
||||||
|
String s("abcdef");
|
||||||
|
|
||||||
|
String sub = s.substr(2, 3);
|
||||||
|
assert(sub.toStdString() == "cde");
|
||||||
|
|
||||||
|
s.erase(2, 2);
|
||||||
|
assert(s.toStdString() == "abef");
|
||||||
|
|
||||||
|
s.insert(2, String("CD"));
|
||||||
|
assert(s.toStdString() == "abCDef");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_replace()
|
||||||
|
{
|
||||||
|
String s("hello world");
|
||||||
|
s.replace(6, 5, String("Fig"));
|
||||||
|
assert(s.toStdString() == "hello Fig");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_find_rfind()
|
||||||
|
{
|
||||||
|
String s("abcabcabc");
|
||||||
|
|
||||||
|
assert(s.find(String("abc")) == 0);
|
||||||
|
assert(s.find(String("abc"), 1) == 3);
|
||||||
|
assert(s.rfind(String("abc")) == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_compare()
|
||||||
|
{
|
||||||
|
String a("abc");
|
||||||
|
String b("abd");
|
||||||
|
String c("abc");
|
||||||
|
|
||||||
|
assert(a.compare(b) < 0);
|
||||||
|
assert(b.compare(a) > 0);
|
||||||
|
assert(a.compare(c) == 0);
|
||||||
|
assert(a == c);
|
||||||
|
assert(a != b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_resize_append()
|
||||||
|
{
|
||||||
|
String s("abc");
|
||||||
|
s.resize(5, U'x');
|
||||||
|
assert(s.toStdString() == "abcxx");
|
||||||
|
|
||||||
|
s.append(3, U'y');
|
||||||
|
assert(s.toStdString() == "abcxxyyy");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_std_interop()
|
||||||
|
{
|
||||||
|
std::string stds = "hello";
|
||||||
|
String s(stds);
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
s += " world";
|
||||||
|
assert(s.toStdString() == "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_ascii_sso();
|
||||||
|
test_ascii_heap();
|
||||||
|
test_utf8_decode();
|
||||||
|
test_concat_modes();
|
||||||
|
test_substr_erase_insert();
|
||||||
|
test_replace();
|
||||||
|
test_find_rfind();
|
||||||
|
test_compare();
|
||||||
|
test_resize_append();
|
||||||
|
test_std_interop();
|
||||||
|
|
||||||
|
std::cout << "All String tests passed.\n";
|
||||||
|
}
|
||||||
165
src/Error/Error.cpp
Normal file
165
src/Error/Error.cpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Error.cpp
|
||||||
|
@brief 错误报告实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/Core.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
void ColoredPrint(const char *color, const char *msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColoredPrint(const char *color, const String &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MultipleStr(const char *c, size_t n)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
for (size_t i = 0; i < n; ++i) { buf += c; }
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ErrorTypeToString(ErrorType type)
|
||||||
|
{
|
||||||
|
using enum ErrorType;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case UnusedSymbol: return "UnusedSymbol";
|
||||||
|
|
||||||
|
case MayBeNull: return "MaybeNull";
|
||||||
|
|
||||||
|
case UnterminatedString: return "UnterminatedString";
|
||||||
|
case UnterminatedComments: return "UnterminatedComments";
|
||||||
|
case InvalidNumberLiteral: return "InvalidNumberLiteral";
|
||||||
|
case InvalidCharacter: return "InvalidCharacter";
|
||||||
|
case InvalidSymbol: return "InvalidSymbol";
|
||||||
|
|
||||||
|
case ExpectedExpression: return "ExpectedExpression";
|
||||||
|
case SyntaxError: return "SyntaxError";
|
||||||
|
|
||||||
|
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintSystemInfos()
|
||||||
|
{
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
std::stringstream build_info;
|
||||||
|
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '['
|
||||||
|
<< Core::COMPILER << ']' << '\n'
|
||||||
|
<< " Build Time: " << Core::COMPILE_TIME;
|
||||||
|
|
||||||
|
const std::string &build_info_str = build_info.str();
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
err << build_info_str << '\n';
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||||
|
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||||
|
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||||
|
|
||||||
|
namespace TC = TerminalColors;
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
|
||||||
|
uint8_t level = ErrorLevel(error.type);
|
||||||
|
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
||||||
|
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||||
|
|
||||||
|
err << "🔥 "
|
||||||
|
<< level_color
|
||||||
|
//<< '(' << level_name << ')'
|
||||||
|
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
|
||||||
|
<< TC::Reset << '\n';
|
||||||
|
|
||||||
|
const SourceLocation &location = error.location;
|
||||||
|
|
||||||
|
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
|
||||||
|
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
|
||||||
|
<< TC::Cyan << ')' << TC::Reset << '\n';
|
||||||
|
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||||
|
|
||||||
|
// 尝试打印上3行 下2行
|
||||||
|
|
||||||
|
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
||||||
|
while (!srcManager.HasLine(line_end)) { --line_end; }
|
||||||
|
while (!srcManager.HasLine(line_start)) { ++line_start; }
|
||||||
|
|
||||||
|
const auto &getLineNumWidth = [](size_t l) {
|
||||||
|
unsigned int cnt = 0;
|
||||||
|
while (l != 0)
|
||||||
|
{
|
||||||
|
l = l / 10;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
};
|
||||||
|
unsigned int max_line_number_width = getLineNumWidth(line_end);
|
||||||
|
for (size_t i = line_start; i <= line_end; ++i)
|
||||||
|
{
|
||||||
|
unsigned int offset = 2 + 2 + 1;
|
||||||
|
// ' └─ '
|
||||||
|
if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; }
|
||||||
|
else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err << MultipleStr(" ", offset);
|
||||||
|
}
|
||||||
|
unsigned int cur_line_number_width = getLineNumWidth(i);
|
||||||
|
|
||||||
|
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset;
|
||||||
|
err << " │ " << srcManager.GetLine(i) << '\n';
|
||||||
|
if (i == location.sp.line)
|
||||||
|
{
|
||||||
|
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
||||||
|
err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||||
|
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
||||||
|
|
||||||
|
err << MultipleStr(" ", error_col_offset)
|
||||||
|
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color
|
||||||
|
<< error.message << TC::Reset << "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err << "\n";
|
||||||
|
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||||
|
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n";
|
||||||
|
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
||||||
|
assert(srcManager.HasLine(error.location.sp.line));
|
||||||
|
|
||||||
|
PrintSystemInfos();
|
||||||
|
PrintErrorInfo(error, srcManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportErrors(const std::vector<Error> &errors, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
std::ostream &ost = CoreIO::GetStdErr();
|
||||||
|
PrintSystemInfos();
|
||||||
|
for (const auto &err : errors)
|
||||||
|
{
|
||||||
|
PrintErrorInfo(err, srcManager);
|
||||||
|
ost << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace Fig
|
||||||
167
src/Error/Error.hpp
Normal file
167
src/Error/Error.hpp
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Error.hpp
|
||||||
|
@brief Error定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <SourceManager/SourceManager.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#include <source_location>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
0-1000 Minor
|
||||||
|
1001-2000 Medium
|
||||||
|
2001-3000 Critical
|
||||||
|
*/
|
||||||
|
enum class ErrorType : unsigned int
|
||||||
|
{
|
||||||
|
/* Minor */
|
||||||
|
UnusedSymbol = 0,
|
||||||
|
|
||||||
|
/* Medium */
|
||||||
|
MayBeNull = 1001,
|
||||||
|
|
||||||
|
/* Critical */
|
||||||
|
|
||||||
|
// lexer errors
|
||||||
|
UnterminatedString = 2001,
|
||||||
|
UnterminatedComments,
|
||||||
|
InvalidNumberLiteral,
|
||||||
|
InvalidCharacter,
|
||||||
|
InvalidSymbol,
|
||||||
|
|
||||||
|
// parser errors
|
||||||
|
ExpectedExpression,
|
||||||
|
SyntaxError,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *ErrorTypeToString(ErrorType type);
|
||||||
|
|
||||||
|
struct Error
|
||||||
|
{
|
||||||
|
ErrorType type;
|
||||||
|
String message;
|
||||||
|
String suggestion;
|
||||||
|
|
||||||
|
SourceLocation location;
|
||||||
|
std::source_location thrower_loc;
|
||||||
|
|
||||||
|
Error() {}
|
||||||
|
Error(ErrorType _type,
|
||||||
|
const String &_message,
|
||||||
|
const String &_suggestion,
|
||||||
|
const SourceLocation &_location,
|
||||||
|
const std::source_location &_throwerloc = std::source_location::current())
|
||||||
|
{
|
||||||
|
type = _type;
|
||||||
|
message = _message;
|
||||||
|
suggestion = _suggestion;
|
||||||
|
location = _location;
|
||||||
|
thrower_loc = _throwerloc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace TerminalColors
|
||||||
|
{
|
||||||
|
constexpr const char *Reset = "\033[0m";
|
||||||
|
constexpr const char *Bold = "\033[1m";
|
||||||
|
constexpr const char *Dim = "\033[2m";
|
||||||
|
constexpr const char *Italic = "\033[3m";
|
||||||
|
constexpr const char *Underline = "\033[4m";
|
||||||
|
constexpr const char *Blink = "\033[5m";
|
||||||
|
constexpr const char *Reverse = "\033[7m"; // 前背景反色
|
||||||
|
constexpr const char *Hidden = "\033[8m"; // 隐藏文本
|
||||||
|
constexpr const char *Strike = "\033[9m"; // 删除线
|
||||||
|
|
||||||
|
constexpr const char *Black = "\033[30m";
|
||||||
|
constexpr const char *Red = "\033[31m";
|
||||||
|
constexpr const char *Green = "\033[32m";
|
||||||
|
constexpr const char *Yellow = "\033[33m";
|
||||||
|
constexpr const char *Blue = "\033[34m";
|
||||||
|
constexpr const char *Magenta = "\033[35m";
|
||||||
|
constexpr const char *Cyan = "\033[36m";
|
||||||
|
constexpr const char *White = "\033[37m";
|
||||||
|
|
||||||
|
constexpr const char *LightBlack = "\033[90m";
|
||||||
|
constexpr const char *LightRed = "\033[91m";
|
||||||
|
constexpr const char *LightGreen = "\033[92m";
|
||||||
|
constexpr const char *LightYellow = "\033[93m";
|
||||||
|
constexpr const char *LightBlue = "\033[94m";
|
||||||
|
constexpr const char *LightMagenta = "\033[95m";
|
||||||
|
constexpr const char *LightCyan = "\033[96m";
|
||||||
|
constexpr const char *LightWhite = "\033[97m";
|
||||||
|
|
||||||
|
constexpr const char *DarkRed = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *DarkGreen = "\033[38;2;0;100;0m";
|
||||||
|
constexpr const char *DarkYellow = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *DarkBlue = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *DarkMagenta = "\033[38;2;100;0;100m";
|
||||||
|
constexpr const char *DarkCyan = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *DarkGray = "\033[38;2;64;64;64m";
|
||||||
|
constexpr const char *Gray = "\033[38;2;128;128;128m";
|
||||||
|
constexpr const char *Silver = "\033[38;2;192;192;192m";
|
||||||
|
|
||||||
|
constexpr const char *Navy = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *RoyalBlue = "\033[38;2;65;105;225m";
|
||||||
|
constexpr const char *ForestGreen = "\033[38;2;34;139;34m";
|
||||||
|
constexpr const char *Olive = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *Teal = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *Maroon = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *Purple = "\033[38;2;128;0;128m";
|
||||||
|
constexpr const char *Orange = "\033[38;2;255;165;0m";
|
||||||
|
constexpr const char *Gold = "\033[38;2;255;215;0m";
|
||||||
|
constexpr const char *Pink = "\033[38;2;255;192;203m";
|
||||||
|
constexpr const char *Crimson = "\033[38;2;220;20;60m";
|
||||||
|
|
||||||
|
constexpr const char *OnBlack = "\033[40m";
|
||||||
|
constexpr const char *OnRed = "\033[41m";
|
||||||
|
constexpr const char *OnGreen = "\033[42m";
|
||||||
|
constexpr const char *OnYellow = "\033[43m";
|
||||||
|
constexpr const char *OnBlue = "\033[44m";
|
||||||
|
constexpr const char *OnMagenta = "\033[45m";
|
||||||
|
constexpr const char *OnCyan = "\033[46m";
|
||||||
|
constexpr const char *OnWhite = "\033[47m";
|
||||||
|
|
||||||
|
constexpr const char *OnLightBlack = "\033[100m";
|
||||||
|
constexpr const char *OnLightRed = "\033[101m";
|
||||||
|
constexpr const char *OnLightGreen = "\033[102m";
|
||||||
|
constexpr const char *OnLightYellow = "\033[103m";
|
||||||
|
constexpr const char *OnLightBlue = "\033[104m";
|
||||||
|
constexpr const char *OnLightMagenta = "\033[105m";
|
||||||
|
constexpr const char *OnLightCyan = "\033[106m";
|
||||||
|
constexpr const char *OnLightWhite = "\033[107m";
|
||||||
|
|
||||||
|
constexpr const char *OnDarkBlue = "\033[48;2;0;0;128m";
|
||||||
|
constexpr const char *OnGreenYellow = "\033[48;2;173;255;47m";
|
||||||
|
constexpr const char *OnOrange = "\033[48;2;255;165;0m";
|
||||||
|
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
||||||
|
}; // namespace TerminalColors
|
||||||
|
|
||||||
|
inline uint8_t ErrorLevel(ErrorType t)
|
||||||
|
{
|
||||||
|
unsigned int id = static_cast<int>(t);
|
||||||
|
if (id <= 1000)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (id > 1000 && id <= 2000)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (id > 2000)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportError(const Error &error, const SourceManager &srcManager);
|
||||||
|
}; // namespace Fig
|
||||||
287
src/Lexer/Lexer.cpp
Normal file
287
src/Lexer/Lexer.cpp
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Lexer/Lexer.cpp
|
||||||
|
@brief 词法分析器(materialized lexeme)实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
总则:
|
||||||
|
Lexer不涉及语义部分,语义为Parser及之后的部分确定!
|
||||||
|
确定边界 --> 分词
|
||||||
|
无法确定 --> 错误的源,报错
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanComments()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 2, TokenType::Comments);
|
||||||
|
rd.skip(2); // 跳过 //
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
if (rd.current() == U'\n')
|
||||||
|
{
|
||||||
|
rd.next(); // skip '\n'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rd.next();
|
||||||
|
} while (rd.hasNext());
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanMultilineComments()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 2, TokenType::Comments);
|
||||||
|
SourcePosition startPos = rd.currentPosition();
|
||||||
|
rd.skip(2); // 跳过 / *
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::UnterminatedComments,
|
||||||
|
"unterminated multiline comments",
|
||||||
|
"insert '*/'",
|
||||||
|
makeSourceLocation(startPos)));
|
||||||
|
}
|
||||||
|
if (rd.current() == U'*' && rd.peekIf() == U'/')
|
||||||
|
{
|
||||||
|
rd.skip(2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanIdentifierOrKeyword()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 1, TokenType::Identifier);
|
||||||
|
String value; // 用于判断是标识符还是关键字
|
||||||
|
value.push_back(rd.produce()); // 加入第一个
|
||||||
|
|
||||||
|
while (CharUtils::isIdentifierContinue(rd.current())) // continue: _ / 0-9 / aA - zZ
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
value.push_back(rd.produce());
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Token::keywordMap.contains(value))
|
||||||
|
{
|
||||||
|
tok.type = Token::keywordMap.at(value);
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanNumberLiteral()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::LiteralNumber);
|
||||||
|
state = State::ScanDec;
|
||||||
|
|
||||||
|
if (rd.current() == U'0')
|
||||||
|
{
|
||||||
|
char32_t _peek = std::tolower(rd.peekIf());
|
||||||
|
if (_peek == U'b')
|
||||||
|
{
|
||||||
|
state = State::ScanBin;
|
||||||
|
rd.skip(2); // 跳过 0b
|
||||||
|
tok.length += 2;
|
||||||
|
}
|
||||||
|
else if (_peek == U'x')
|
||||||
|
{
|
||||||
|
state = State::ScanHex;
|
||||||
|
rd.skip(2); // 跳过 0x
|
||||||
|
tok.length += 2;
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// return std::unexpected(Error(ErrorType::InvalidNumberLiteral,
|
||||||
|
// std::format("bad number postfix 0{}", String(_peek)),
|
||||||
|
// "correct it",
|
||||||
|
// makeSourceLocation(rd.currentPosition())));
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char32_t current = rd.current();
|
||||||
|
if (state == State::ScanDec && !CharUtils::isDigit(current))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == State::ScanHex && !CharUtils::isHexDigit(current))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == State::ScanBin && current != U'0' && current != U'1')
|
||||||
|
{
|
||||||
|
// return std::unexpected(
|
||||||
|
// Error(ErrorType::InvalidNumberLiteral,
|
||||||
|
// std::format("invalid binary number literal, scanning '{}'", String(¤t)),
|
||||||
|
// "correct it",
|
||||||
|
// makeSourceLocation(rd.currentPosition())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
} while (!rd.isAtEnd());
|
||||||
|
|
||||||
|
// 科学计数法
|
||||||
|
while (!rd.isAtEnd() && state == State::ScanDec
|
||||||
|
&& (rd.current() == U'e' || rd.current() == U'E' || rd.current() == U'_' || rd.current() == U'+'
|
||||||
|
|| rd.current() == U'-' || CharUtils::isDigit(rd.current())))
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
Result<Token, Error> Lexer::scanStringLiteral()
|
||||||
|
{
|
||||||
|
state = (rd.current() == U'"' ? State::ScanStringDQ : Lexer::State::ScanStringSQ);
|
||||||
|
|
||||||
|
SourcePosition startPos = rd.currentPosition();
|
||||||
|
|
||||||
|
rd.next(); // skip " / '
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::LiteralString);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (state == State::ScanStringDQ && rd.current() == U'"')
|
||||||
|
{
|
||||||
|
rd.next(); // skip '"'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (state == State::ScanStringSQ && rd.current() == U'\'')
|
||||||
|
{
|
||||||
|
rd.next(); // skip `'`
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::UnterminatedString,
|
||||||
|
"unterminated string literal",
|
||||||
|
std::format("insert '{}'", String((state == State::ScanStringDQ ? "\"" : "'"))),
|
||||||
|
makeSourceLocation(startPos)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
Result<Token, Error> Lexer::scanPunct()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::Illegal);
|
||||||
|
|
||||||
|
auto startsWith = [&](const String &prefix) -> bool {
|
||||||
|
for (const auto &p : Token::punctMap)
|
||||||
|
{
|
||||||
|
const String &op = p.first;
|
||||||
|
if (op.starts_with(prefix))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
String sym;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
String candidate = sym + rd.current();
|
||||||
|
if (startsWith(candidate))
|
||||||
|
{
|
||||||
|
rd.next();
|
||||||
|
tok.length++;
|
||||||
|
sym = candidate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!rd.isAtEnd() && CharUtils::isPunct(rd.current()));
|
||||||
|
|
||||||
|
if (!Token::punctMap.contains(sym))
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::InvalidSymbol,
|
||||||
|
std::format("invalid symbol `{}`", sym),
|
||||||
|
"correct it",
|
||||||
|
makeSourceLocation(rd.currentPosition())));
|
||||||
|
}
|
||||||
|
tok.type = Token::punctMap.at(sym);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skipWhitespaces()
|
||||||
|
{
|
||||||
|
while (!rd.isAtEnd())
|
||||||
|
{
|
||||||
|
char32_t current = rd.current();
|
||||||
|
if (current == EOF || !CharUtils::isAsciiSpace(current)) // 检查 EOF
|
||||||
|
break;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::NextToken()
|
||||||
|
{
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return Token(rd.currentIndex(), 0, TokenType::EndOfFile);
|
||||||
|
}
|
||||||
|
if (rd.current() == U'\0')
|
||||||
|
{
|
||||||
|
return Token(rd.currentIndex(), 1, TokenType::EndOfFile);
|
||||||
|
}
|
||||||
|
if (rd.current() == U'/' && rd.peekIf() == U'/')
|
||||||
|
{
|
||||||
|
return scanComments();
|
||||||
|
}
|
||||||
|
else if (rd.current() == U'/' && rd.peekIf() == U'*')
|
||||||
|
{
|
||||||
|
return scanMultilineComments();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isIdentifierStart(rd.current()))
|
||||||
|
{
|
||||||
|
return scanIdentifierOrKeyword();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isDigit(rd.current()))
|
||||||
|
{
|
||||||
|
return scanNumberLiteral();
|
||||||
|
}
|
||||||
|
else if (rd.current() == U'"' || rd.current() == U'\'')
|
||||||
|
{
|
||||||
|
return scanStringLiteral();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isPunct(rd.current()))
|
||||||
|
{
|
||||||
|
return scanPunct();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isSpace(rd.current()))
|
||||||
|
{
|
||||||
|
skipWhitespaces();
|
||||||
|
return NextToken();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(
|
||||||
|
ErrorType::InvalidCharacter,
|
||||||
|
std::format("invalid character '{}' (U+{})", String(rd.current()), static_cast<int>(rd.current())),
|
||||||
|
"correct it",
|
||||||
|
makeSourceLocation(rd.currentPosition())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace Fig
|
||||||
165
src/Lexer/Lexer.hpp
Normal file
165
src/Lexer/Lexer.hpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Lexer/Lexer.hpp
|
||||||
|
@brief 词法分析器(materialized lexeme)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class SourceReader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
String source;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
SourcePosition pos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SourceReader()
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
pos.line = pos.column = 1;
|
||||||
|
}
|
||||||
|
SourceReader(const String &_source) // copy
|
||||||
|
{
|
||||||
|
source = _source;
|
||||||
|
index = 0;
|
||||||
|
pos.line = pos.column = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePosition ¤tPosition() { return pos; }
|
||||||
|
|
||||||
|
inline char32_t current() const
|
||||||
|
{
|
||||||
|
assert(index < source.length() && "SourceReader: get current failed, index out of range");
|
||||||
|
return source[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t currentIf() const
|
||||||
|
{
|
||||||
|
if (index >= source.length())
|
||||||
|
{
|
||||||
|
return U'\0';
|
||||||
|
}
|
||||||
|
return source[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hasNext() const { return index < source.length() - 1; }
|
||||||
|
|
||||||
|
inline char32_t peek() const
|
||||||
|
{
|
||||||
|
assert((index + 1) < source.length() && "SourceReader: get peek failed, index out of range");
|
||||||
|
return source[index + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t peekIf() const
|
||||||
|
{
|
||||||
|
if ((index + 1) < source.length()) { return source[index + 1]; }
|
||||||
|
return 0xFFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t produce()
|
||||||
|
{
|
||||||
|
// returns current rune, then next
|
||||||
|
char32_t c = current();
|
||||||
|
next();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void next()
|
||||||
|
{
|
||||||
|
char32_t consumed = currentIf();
|
||||||
|
|
||||||
|
++index;
|
||||||
|
if (consumed == U'\n')
|
||||||
|
{
|
||||||
|
++pos.line;
|
||||||
|
pos.column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++pos.column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void skip(size_t n)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < n; ++i) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t currentIndex() const { return index; }
|
||||||
|
|
||||||
|
inline bool isAtEnd() const { return index >= source.length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Lexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class State : uint8_t
|
||||||
|
{
|
||||||
|
Error,
|
||||||
|
Standby,
|
||||||
|
End,
|
||||||
|
|
||||||
|
ScanComments, // 单行注释
|
||||||
|
ScanMultilineComments, // 多行注释
|
||||||
|
ScanIdentifier, // 关键字也算
|
||||||
|
|
||||||
|
ScanDec, // 十进制数字, 如 1.2 31, 3.14e+3, 1_000_0000
|
||||||
|
ScanBin, // 二进制数字, 如 0b0001 / 0B0001
|
||||||
|
ScanHex, // 十六进制数字, 如 0xABCD / 0XabCd
|
||||||
|
ScanStringDQ, // 双引号字符串, 如 "hello, world!"
|
||||||
|
ScanStringSQ, // 单引号字符串, 如 'hello'
|
||||||
|
ScanBool, // 布尔字面量, true / false
|
||||||
|
ScanNull, // 空值字面量, null
|
||||||
|
|
||||||
|
ScanPunct, // 符号
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
String fileName;
|
||||||
|
SourceReader rd;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Result<Token, Error> scanComments();
|
||||||
|
Result<Token, Error> scanMultilineComments();
|
||||||
|
|
||||||
|
Result<Token, Error> scanIdentifierOrKeyword();
|
||||||
|
|
||||||
|
Result<Token, Error> scanNumberLiteral();
|
||||||
|
Result<Token, Error> scanStringLiteral(); // 支持多行
|
||||||
|
// Result<Token, Error> scanBoolLiteral(); 由 scanIdentifier...扫描
|
||||||
|
// Result<Token, Error> scanLiteralNull(); 由 scanIdentifier...扫描
|
||||||
|
|
||||||
|
Result<Token, Error> scanPunct();
|
||||||
|
|
||||||
|
void skipWhitespaces();
|
||||||
|
|
||||||
|
public:
|
||||||
|
State state = State::Standby;
|
||||||
|
|
||||||
|
Lexer() {}
|
||||||
|
Lexer(const String &source, String _fileName)
|
||||||
|
{
|
||||||
|
rd = SourceReader(source);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation makeSourceLocation(SourcePosition current_pos)
|
||||||
|
{
|
||||||
|
current_pos.tok_length = 1;
|
||||||
|
return SourceLocation(
|
||||||
|
current_pos, fileName, "[internal lexer]", String(magic_enum::enum_name(state).data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> NextToken();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
43
src/Lexer/LexerTest.cpp
Normal file
43
src/Lexer/LexerTest.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include <Error/Error.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using namespace Fig;
|
||||||
|
|
||||||
|
String fileName = "test.fig";
|
||||||
|
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||||
|
|
||||||
|
SourceManager manager(filePath);
|
||||||
|
manager.Read();
|
||||||
|
|
||||||
|
if (!manager.read)
|
||||||
|
{
|
||||||
|
std::cerr << "Couldn't read file";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer(manager.GetSource(), fileName);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result.has_value())
|
||||||
|
{
|
||||||
|
ReportError(result.error(), manager);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const Token &token = *result;
|
||||||
|
const String &lexeme = manager.GetSub(token.index, token.length);
|
||||||
|
const auto &type = magic_enum::enum_name(token.type);
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
std::cout << "EOF: " << type << '\n';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::cout << lexeme << " --> " << type << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/Parser/ExprParser.cpp
Normal file
160
src/Parser/ExprParser.cpp
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/ExprParser.hpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 表达式解析实现 (pratt)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
Result<LiteralExpr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
||||||
|
{
|
||||||
|
state = State::ParsingLiteralExpr;
|
||||||
|
const Token &literal_token = consumeToken();
|
||||||
|
LiteralExpr *node = new LiteralExpr(literal_token, makeSourcelocation(literal_token));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
Result<IdentiExpr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||||
|
{
|
||||||
|
state = State::ParsingIdentiExpr;
|
||||||
|
const Token &identifier = consumeToken();
|
||||||
|
IdentiExpr *node =
|
||||||
|
new IdentiExpr(srcManager.GetSub(identifier.index, identifier.length), makeSourcelocation(identifier));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<InfixExpr *, Error> Parser::parseInfixExpr(Expr *lhs) // 当前token为 op
|
||||||
|
{
|
||||||
|
state = State::ParsingInfixExpr;
|
||||||
|
const Token &op_token = consumeToken();
|
||||||
|
BinaryOperator op = TokenToBinaryOp(op_token);
|
||||||
|
BindingPower rbp = GetBinaryOpRBp(op);
|
||||||
|
|
||||||
|
const auto &rhs_result = parseExpression(rbp);
|
||||||
|
if (!rhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(rhs_result.error());
|
||||||
|
}
|
||||||
|
Expr *rhs = *rhs_result;
|
||||||
|
|
||||||
|
InfixExpr *node = new InfixExpr(lhs, op, rhs);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PrefixExpr *, Error> Parser::parsePrefixExpr() // 当前token为op
|
||||||
|
{
|
||||||
|
state = State::ParsingPrefixExpr;
|
||||||
|
const Token &op_token = consumeToken();
|
||||||
|
UnaryOperator op = TokenToUnaryOp(op_token);
|
||||||
|
|
||||||
|
BindingPower rbp = GetUnaryOpRBp(op);
|
||||||
|
const auto &rhs_result = parseExpression(rbp);
|
||||||
|
if (!rhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(rhs_result.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *rhs = *rhs_result;
|
||||||
|
PrefixExpr *node = new PrefixExpr(op, rhs);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<TokenType> Parser::getTerminators() // 返回当前state的终止条件(终止符)
|
||||||
|
{
|
||||||
|
using enum State;
|
||||||
|
|
||||||
|
static const std::unordered_set<TokenType> baseTerminators = {TokenType::EndOfFile, TokenType::Semicolon};
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
default: return baseTerminators;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Parser::shouldTerminate()
|
||||||
|
{
|
||||||
|
const Token &token = currentToken();
|
||||||
|
const auto &terminators = getTerminators();
|
||||||
|
return terminators.contains(token.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
||||||
|
{
|
||||||
|
Expr *lhs = nullptr;
|
||||||
|
Token token = currentToken();
|
||||||
|
|
||||||
|
if (token.isIdentifier())
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parseIdentiExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
else if (token.isLiteral())
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parseLiteralExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
else if (IsTokenOp(token.type, false)) // 是否是一元运算符
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parsePrefixExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lhs)
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||||
|
"expected expression",
|
||||||
|
"insert expressions",
|
||||||
|
makeSourcelocation(prevToken())));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (shouldTerminate())
|
||||||
|
{
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = currentToken();
|
||||||
|
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
||||||
|
{
|
||||||
|
BinaryOperator op = TokenToBinaryOp(token);
|
||||||
|
BindingPower lbp = GetBinaryOpLBp(op);
|
||||||
|
if (rbp >= lbp)
|
||||||
|
{
|
||||||
|
// 前操作数的右绑定力比当前操作数的左绑定力大
|
||||||
|
// lhs被吸走
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &result = parseInfixExpr(lhs);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
lhs = *result;
|
||||||
|
}
|
||||||
|
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
||||||
|
// 而不用走正常路径
|
||||||
|
else if (0) {}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
18
src/Parser/Parser.cpp
Normal file
18
src/Parser/Parser.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/Parser.cpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
DynArray<AstNode *> Parser::parseAll()
|
||||||
|
{
|
||||||
|
DynArray<AstNode *> nodes;
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
};
|
||||||
162
src/Parser/Parser.hpp
Normal file
162
src/Parser/Parser.hpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/Parser.hpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Ast.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Lexer &lexer;
|
||||||
|
SourceManager &srcManager;
|
||||||
|
|
||||||
|
size_t index = 0; // token在buffer下标
|
||||||
|
DynArray<Token> buffer;
|
||||||
|
|
||||||
|
String fileName;
|
||||||
|
|
||||||
|
bool isEOF = false;
|
||||||
|
|
||||||
|
Token nextToken()
|
||||||
|
{
|
||||||
|
assert(!isEOF && "nextToken: eof but called nextToken");
|
||||||
|
if (index + 1 < buffer.size())
|
||||||
|
{
|
||||||
|
return buffer[++index];
|
||||||
|
}
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
ReportError(result.error(), srcManager);
|
||||||
|
std::exit(-1);
|
||||||
|
}
|
||||||
|
const Token &token = result.value();
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
isEOF = true;
|
||||||
|
}
|
||||||
|
buffer.push_back(token);
|
||||||
|
index++;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token prevToken()
|
||||||
|
{
|
||||||
|
if (buffer.size() < 2)
|
||||||
|
{
|
||||||
|
return currentToken();
|
||||||
|
}
|
||||||
|
return buffer[buffer.size() - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token currentToken()
|
||||||
|
{
|
||||||
|
if (buffer.empty())
|
||||||
|
{
|
||||||
|
return nextToken();
|
||||||
|
}
|
||||||
|
return buffer.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
Token peekToken(size_t lookahead = 1)
|
||||||
|
{
|
||||||
|
assert(!isEOF && "peekToken: eof but called peekToken");
|
||||||
|
|
||||||
|
size_t peekIndex = index + lookahead;
|
||||||
|
while (peekIndex >= buffer.size() && !isEOF)
|
||||||
|
{
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
ReportError(result.error(), srcManager);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
const Token &token = result.value();
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
isEOF = true;
|
||||||
|
}
|
||||||
|
buffer.push_back(token);
|
||||||
|
}
|
||||||
|
if (peekIndex >= buffer.size()) // 没有那么多token
|
||||||
|
{
|
||||||
|
return buffer.back(); // back是EOF Token
|
||||||
|
}
|
||||||
|
return buffer[peekIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token consumeToken()
|
||||||
|
{
|
||||||
|
if (isEOF)
|
||||||
|
return buffer.back();
|
||||||
|
Token current = currentToken();
|
||||||
|
nextToken();
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class State : std::uint8_t
|
||||||
|
{
|
||||||
|
Standby,
|
||||||
|
|
||||||
|
ParsingLiteralExpr,
|
||||||
|
ParsingIdentiExpr,
|
||||||
|
|
||||||
|
ParsingInfixExpr,
|
||||||
|
ParsingPrefixExpr,
|
||||||
|
} state;
|
||||||
|
|
||||||
|
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
|
||||||
|
lexer(_lexer), srcManager(_srcManager), fileName(std::move(_fileName))
|
||||||
|
{
|
||||||
|
state = State::Standby;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SourceLocation makeSourcelocation(const Token &tok)
|
||||||
|
{
|
||||||
|
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
||||||
|
return SourceLocation(
|
||||||
|
SourcePosition(
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
tok.length
|
||||||
|
), fileName, "[internal parser]", magic_enum::enum_name(state).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expressions */
|
||||||
|
Result<LiteralExpr *, Error> parseLiteralExpr(); // 当前token为literal时调用
|
||||||
|
Result<IdentiExpr *, Error> parseIdentiExpr(); // 当前token为Identifier调用
|
||||||
|
|
||||||
|
Result<InfixExpr *, Error> parseInfixExpr(Expr *); // 由 parseExpression递归调用, 当前token为op
|
||||||
|
Result<PrefixExpr *, Error> parsePrefixExpr(); // 由 parseExpression递归调用, 当前token为op
|
||||||
|
|
||||||
|
std::unordered_set<TokenType> getTerminators(); // 返回当前state的终止条件(终止符)
|
||||||
|
bool shouldTerminate(); // 通过state判断该不该终止表达式解析
|
||||||
|
|
||||||
|
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||||
|
|
||||||
|
/* Statements */
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DynArray<AstNode *> parseAll();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
29
src/Parser/ParserTest.cpp
Normal file
29
src/Parser/ParserTest.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using namespace Fig;
|
||||||
|
|
||||||
|
String fileName = "test.fig";
|
||||||
|
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||||
|
SourceManager srcManager(filePath);
|
||||||
|
|
||||||
|
String source = srcManager.Read();
|
||||||
|
if (!srcManager.read)
|
||||||
|
{
|
||||||
|
std::cerr << "Couldn't read file";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer(source, fileName);
|
||||||
|
Parser parser(lexer, srcManager, fileName);
|
||||||
|
// const auto &result = parser.parseExpression();
|
||||||
|
// if (!result)
|
||||||
|
// {
|
||||||
|
// ReportError(result.error(), srcManager);
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
// Expr *expr = *result;
|
||||||
|
// std::cout << expr->toString() << '\n';
|
||||||
|
}
|
||||||
127
src/SourceManager/SourceManager.hpp
Normal file
127
src/SourceManager/SourceManager.hpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*!
|
||||||
|
@file src/SourceManager/SourceManager.hpp
|
||||||
|
@brief 源代码管理
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class SourceManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
String filePath;
|
||||||
|
String source;
|
||||||
|
std::vector<String> lines;
|
||||||
|
std::vector<size_t> lineStartIndex; // 每行在整个源字符串中的起始 index
|
||||||
|
|
||||||
|
void preprocessLineIndices()
|
||||||
|
{
|
||||||
|
lineStartIndex.clear();
|
||||||
|
lineStartIndex.push_back(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < source.length(); ++i)
|
||||||
|
{
|
||||||
|
if (source[i] == U'\n')
|
||||||
|
{
|
||||||
|
lineStartIndex.push_back(i + 1);
|
||||||
|
}
|
||||||
|
else if (source[i] == U'\r')
|
||||||
|
{
|
||||||
|
// 处理 CRLF,只在 \n 处记录
|
||||||
|
if (i + 1 < source.length() && source[i + 1] == U'\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lineStartIndex.push_back(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool read = false;
|
||||||
|
String &Read()
|
||||||
|
{
|
||||||
|
std::fstream fs(filePath.toStdString());
|
||||||
|
if (!fs.is_open())
|
||||||
|
{
|
||||||
|
read = false;
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(fs, line))
|
||||||
|
{
|
||||||
|
source += line + '\n';
|
||||||
|
lines.push_back(String(line));
|
||||||
|
}
|
||||||
|
if (lines.empty())
|
||||||
|
{
|
||||||
|
lines.push_back(String()); // 填充一个空的
|
||||||
|
}
|
||||||
|
read = true;
|
||||||
|
preprocessLineIndices();
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceManager() {}
|
||||||
|
SourceManager(String _path) { filePath = std::move(_path); }
|
||||||
|
|
||||||
|
bool HasLine(int64_t _line) const
|
||||||
|
{
|
||||||
|
return _line <= lines.size() && _line >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &GetLine(size_t _line) const
|
||||||
|
{
|
||||||
|
assert(_line <= lines.size() && "SourceManager: GetLine failed, index out of range");
|
||||||
|
return lines[_line - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
String GetSub(size_t _index_start, size_t _length) const
|
||||||
|
{
|
||||||
|
return source.substr(_index_start, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &GetSource() const
|
||||||
|
{
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<size_t, size_t> GetLineColumn(size_t index) const
|
||||||
|
{
|
||||||
|
if (lineStartIndex.empty())
|
||||||
|
{
|
||||||
|
return {1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp index 到合法范围(Parser报错可能传入EOF位置)
|
||||||
|
// size_t lastLine = lineStartIndex.size() - 1;
|
||||||
|
if (index < lineStartIndex[0])
|
||||||
|
{
|
||||||
|
return {1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// upper_bound 找到第一个 > index 的行起点
|
||||||
|
auto it = std::ranges::upper_bound(lineStartIndex.begin(), lineStartIndex.end(), index);
|
||||||
|
|
||||||
|
size_t line;
|
||||||
|
if (it == lineStartIndex.begin())
|
||||||
|
{
|
||||||
|
line = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = static_cast<size_t>(it - lineStartIndex.begin() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t column = index - lineStartIndex[line] + 1;
|
||||||
|
return {line + 1, column};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
96
src/Token/Token.cpp
Normal file
96
src/Token/Token.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Token/Token.cpp
|
||||||
|
@brief Token实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
const HashMap<String, TokenType> Token::punctMap = {
|
||||||
|
// 三字符
|
||||||
|
{String("..."), TokenType::TripleDot},
|
||||||
|
// 双字符
|
||||||
|
{String("=="), TokenType::Equal},
|
||||||
|
{String("!="), TokenType::NotEqual},
|
||||||
|
{String("<="), TokenType::LessEqual},
|
||||||
|
{String(">="), TokenType::GreaterEqual},
|
||||||
|
{String("<<"), TokenType::ShiftLeft},
|
||||||
|
{String(">>"), TokenType::ShiftRight},
|
||||||
|
{String("+="), TokenType::PlusEqual},
|
||||||
|
{String("-="), TokenType::MinusEqual},
|
||||||
|
{String("*="), TokenType::AsteriskEqual},
|
||||||
|
{String("/="), TokenType::SlashEqual},
|
||||||
|
{String("%="), TokenType::PercentEqual},
|
||||||
|
{String("^="), TokenType::CaretEqual},
|
||||||
|
{String("++"), TokenType::DoublePlus},
|
||||||
|
{String("--"), TokenType::DoubleMinus},
|
||||||
|
{String("&&"), TokenType::DoubleAmpersand},
|
||||||
|
{String("||"), TokenType::DoublePipe},
|
||||||
|
{String(":="), TokenType::Walrus},
|
||||||
|
{String("**"), TokenType::Power},
|
||||||
|
{String("->"), TokenType::RightArrow},
|
||||||
|
{String("=>"), TokenType::DoubleArrow},
|
||||||
|
|
||||||
|
// 单字符
|
||||||
|
{String("+"), TokenType::Plus},
|
||||||
|
{String("-"), TokenType::Minus},
|
||||||
|
{String("*"), TokenType::Asterisk},
|
||||||
|
{String("/"), TokenType::Slash},
|
||||||
|
{String("%"), TokenType::Percent},
|
||||||
|
{String("^"), TokenType::Caret},
|
||||||
|
{String("&"), TokenType::Ampersand},
|
||||||
|
{String("|"), TokenType::Pipe},
|
||||||
|
{String("~"), TokenType::Tilde},
|
||||||
|
{String("="), TokenType::Assign},
|
||||||
|
{String("<"), TokenType::Less},
|
||||||
|
{String(">"), TokenType::Greater},
|
||||||
|
{String("."), TokenType::Dot},
|
||||||
|
{String(","), TokenType::Comma},
|
||||||
|
{String(":"), TokenType::Colon},
|
||||||
|
{String(";"), TokenType::Semicolon},
|
||||||
|
{String("'"), TokenType::SingleQuote},
|
||||||
|
{String("\""), TokenType::DoubleQuote},
|
||||||
|
{String("("), TokenType::LeftParen},
|
||||||
|
{String(")"), TokenType::RightParen},
|
||||||
|
{String("["), TokenType::LeftBracket},
|
||||||
|
{String("]"), TokenType::RightBracket},
|
||||||
|
{String("{"), TokenType::LeftBrace},
|
||||||
|
{String("}"), TokenType::RightBrace},
|
||||||
|
{String("?"), TokenType::Question},
|
||||||
|
{String("!"), TokenType::Not},
|
||||||
|
};
|
||||||
|
|
||||||
|
const HashMap<String, TokenType> Token::keywordMap{
|
||||||
|
{String("and"), TokenType::And},
|
||||||
|
{String("or"), TokenType::Or},
|
||||||
|
{String("not"), TokenType::Not},
|
||||||
|
{String("import"), TokenType::Import},
|
||||||
|
{String("func"), TokenType::Function},
|
||||||
|
{String("var"), TokenType::Variable},
|
||||||
|
{String("const"), TokenType::Const},
|
||||||
|
// {String("final"), TokenType::Final},
|
||||||
|
{String("while"), TokenType::While},
|
||||||
|
{String("for"), TokenType::For},
|
||||||
|
{String("if"), TokenType::If},
|
||||||
|
{String("else"), TokenType::Else},
|
||||||
|
{String("new"), TokenType::New},
|
||||||
|
{String("struct"), TokenType::Struct},
|
||||||
|
{String("interface"), TokenType::Interface},
|
||||||
|
{String("impl"), TokenType::Implement},
|
||||||
|
{String("is"), TokenType::Is},
|
||||||
|
{String("public"), TokenType::Public},
|
||||||
|
{String("return"), TokenType::Return},
|
||||||
|
{String("break"), TokenType::Break},
|
||||||
|
{String("continue"), TokenType::Continue},
|
||||||
|
{String("try"), TokenType::Try},
|
||||||
|
{String("catch"), TokenType::Catch},
|
||||||
|
{String("throw"), TokenType::Throw},
|
||||||
|
{String("Finally"), TokenType::Finally},
|
||||||
|
{String("as"), TokenType::As},
|
||||||
|
{String("true"), TokenType::LiteralTrue},
|
||||||
|
{String("false"), TokenType::LiteralFalse},
|
||||||
|
{String("null"), TokenType::LiteralNull},
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
168
src/Token/Token.hpp
Normal file
168
src/Token/Token.hpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Token/Token.hpp
|
||||||
|
@brief Token定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
#include <Utils/magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class TokenType : int8_t
|
||||||
|
{
|
||||||
|
Illegal = -1,
|
||||||
|
EndOfFile = 0,
|
||||||
|
|
||||||
|
Comments,
|
||||||
|
|
||||||
|
Identifier,
|
||||||
|
|
||||||
|
/* Keywords */
|
||||||
|
Package, // package
|
||||||
|
And, // and
|
||||||
|
Or, // or
|
||||||
|
Not, // not
|
||||||
|
Import, // import
|
||||||
|
Function, // func
|
||||||
|
Variable, // var
|
||||||
|
Const, // const
|
||||||
|
// Final, // final
|
||||||
|
While, // while
|
||||||
|
For, // for
|
||||||
|
If, // if
|
||||||
|
Else, // else
|
||||||
|
New, // new
|
||||||
|
Struct, // struct
|
||||||
|
Interface, // interface
|
||||||
|
Implement, // impl
|
||||||
|
Is, // is
|
||||||
|
Public, // public
|
||||||
|
Return, // return
|
||||||
|
Break, // break
|
||||||
|
Continue, // continue
|
||||||
|
Try, // try
|
||||||
|
Catch, // catch
|
||||||
|
Throw, // throw
|
||||||
|
Finally, // finally
|
||||||
|
As, // as
|
||||||
|
|
||||||
|
// TypeNull, // Null
|
||||||
|
// TypeInt, // Int
|
||||||
|
// TypeDeps::String, // Deps::String
|
||||||
|
// TypeBool, // Bool
|
||||||
|
// TypeDouble, // Double
|
||||||
|
|
||||||
|
/* Literal Types */
|
||||||
|
LiteralNumber, // number (int,float...)
|
||||||
|
LiteralString, // string
|
||||||
|
|
||||||
|
LiteralTrue, // true <-- keyword
|
||||||
|
LiteralFalse, // false <-- keyword
|
||||||
|
LiteralNull, // null (Null unique instance) <-- keyword
|
||||||
|
|
||||||
|
/* Punct */
|
||||||
|
Plus, // +
|
||||||
|
Minus, // -
|
||||||
|
Asterisk, // *
|
||||||
|
Slash, // /
|
||||||
|
Percent, // %
|
||||||
|
Caret, // ^
|
||||||
|
Ampersand, // &
|
||||||
|
Pipe, // |
|
||||||
|
Tilde, // ~
|
||||||
|
ShiftLeft, // <<
|
||||||
|
ShiftRight, // >>
|
||||||
|
// Exclamation, // !
|
||||||
|
Question, // ?
|
||||||
|
Assign, // =
|
||||||
|
Less, // <
|
||||||
|
Greater, // >
|
||||||
|
Dot, // .
|
||||||
|
Comma, // ,
|
||||||
|
Colon, // :
|
||||||
|
Semicolon, // ;
|
||||||
|
SingleQuote, // '
|
||||||
|
DoubleQuote, // "
|
||||||
|
// Backtick, // `
|
||||||
|
// At, // @
|
||||||
|
// Hash, // #
|
||||||
|
// Dollar, // $
|
||||||
|
// Backslash, // '\'
|
||||||
|
// Underscore, // _
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftBracket, // [
|
||||||
|
RightBracket, // ]
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
// LeftArrow, // <-
|
||||||
|
RightArrow, // ->
|
||||||
|
DoubleArrow, // =>
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
LessEqual, // <=
|
||||||
|
GreaterEqual, // >=
|
||||||
|
|
||||||
|
PlusEqual, // +=
|
||||||
|
MinusEqual, // -=
|
||||||
|
AsteriskEqual, // *=
|
||||||
|
SlashEqual, // /=
|
||||||
|
PercentEqual, // %=
|
||||||
|
CaretEqual, // ^=
|
||||||
|
|
||||||
|
DoublePlus, // ++
|
||||||
|
DoubleMinus, // --
|
||||||
|
DoubleAmpersand, // &&
|
||||||
|
DoublePipe, // ||
|
||||||
|
Walrus, // :=
|
||||||
|
Power, // **
|
||||||
|
|
||||||
|
TripleDot, // ... for variadic parameter
|
||||||
|
};
|
||||||
|
|
||||||
|
class Token final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const HashMap<String, TokenType> punctMap;
|
||||||
|
static const HashMap<String, TokenType> keywordMap;
|
||||||
|
|
||||||
|
size_t index, length;
|
||||||
|
// 源文件中的下标 Token长度
|
||||||
|
TokenType type;
|
||||||
|
|
||||||
|
Token() : index(0), length(0), type(TokenType::Illegal) {};
|
||||||
|
Token(size_t _index, size_t _length, TokenType _type) : index(_index), length(_length), type(_type) {}
|
||||||
|
Deps::String toString() const
|
||||||
|
{
|
||||||
|
return Deps::String(std::format("Token'{}' at {}, len {}", magic_enum::enum_name(type), index, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIdentifier() const { return type == TokenType::Identifier; }
|
||||||
|
bool isLiteral() const
|
||||||
|
{
|
||||||
|
return type == TokenType::LiteralNull || type == TokenType::LiteralTrue || type == TokenType::LiteralFalse
|
||||||
|
|| type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token &operator=(const Token &other)
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
index = other.index;
|
||||||
|
length = other.length;
|
||||||
|
type = other.type;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
0
src/main.cpp
Normal file
0
src/main.cpp
Normal file
61
xmake.lua
Normal file
61
xmake.lua
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
add_rules("mode.debug", "mode.release")
|
||||||
|
add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"})
|
||||||
|
|
||||||
|
set_policy("run.autobuild", false)
|
||||||
|
|
||||||
|
if is_plat("linux") then
|
||||||
|
-- Linux: clang + libc++
|
||||||
|
set_toolchains("clang")
|
||||||
|
add_cxxflags("-stdlib=libc++")
|
||||||
|
add_ldflags("-stdlib=libc++")
|
||||||
|
elseif is_plat("windows") then
|
||||||
|
-- 1. CI cross (Linux -> Windows)
|
||||||
|
-- 2. local dev (Windows + llvm-mingw)
|
||||||
|
set_toolchains("mingw") -- llvm-mingw
|
||||||
|
add_ldflags("-Wl,--stack,268435456")
|
||||||
|
-- set_toolchains("clang")
|
||||||
|
-- static lib
|
||||||
|
-- add_ldflags("-target x86_64-w64-mingw32", "-static")
|
||||||
|
-- add_cxxflags("-stdlib=libc++")
|
||||||
|
-- add_ldflags("-stdlib=libc++")
|
||||||
|
end
|
||||||
|
|
||||||
|
set_languages("c++23")
|
||||||
|
add_includedirs("src")
|
||||||
|
|
||||||
|
add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"")
|
||||||
|
|
||||||
|
target("StringTest")
|
||||||
|
add_files("src/Deps/String/StringTest.cpp")
|
||||||
|
|
||||||
|
target("LexerTest")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Lexer/LexerTest.cpp")
|
||||||
|
|
||||||
|
target("ParserTest")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Ast/Operator.cpp")
|
||||||
|
add_files("src/Parser/ExprParser.cpp")
|
||||||
|
add_files("src/Parser/Parser.cpp")
|
||||||
|
|
||||||
|
add_files("src/Parser/ParserTest.cpp")
|
||||||
|
|
||||||
|
target("Fig")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Ast/Operator.cpp")
|
||||||
|
add_files("src/Parser/ExprParser.cpp")
|
||||||
|
add_files("src/Parser/Parser.cpp")
|
||||||
|
|
||||||
|
add_files("src/main.cpp")
|
||||||
Reference in New Issue
Block a user