Compare commits

...

9 Commits

Author SHA1 Message Date
663fe39070 添加缺失的 doxy 2026-02-17 14:13:57 +08:00
6b75e028ff 修复右结合绑定力错误 2026-02-17 14:03:48 +08:00
878157c2fc 完成Parser定义以及表达式解析 2026-02-14 23:03:46 +08:00
35e479fd05 完成表达式Ast定义。修改了format文件 2026-02-14 18:00:46 +08:00
51e831cc6a 添加了注释文档 2026-02-14 15:32:11 +08:00
35b98c4d7f 完成Lexer实现,100%可靠 2026-02-14 14:54:44 +08:00
877253cbbc 完成 Error定义和ErrorLog. 以及一些相关的东西 2026-02-13 23:11:37 +08:00
cfcdfde170 结构调整2 2026-02-12 14:55:48 +08:00
5e75402b43 项目结构调整 2026-02-12 14:55:34 +08:00
58 changed files with 3819 additions and 8 deletions

View File

@@ -6,13 +6,13 @@ Language: Cpp
AccessModifierOffset: -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: DontAlign
# 连续赋值时,对齐所有等号
AlignConsecutiveAssignments: false
AlignConsecutiveAssignments: true
# 连续声明时,对齐所有声明的变量名
AlignConsecutiveDeclarations: false
AlignConsecutiveDeclarations: true
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
AlignEscapedNewlines: Right
@@ -33,13 +33,13 @@ AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
AllowShortFunctionsOnASingleLine: Inline
AllowShortFunctionsOnASingleLine: Empty
# 允许短的if语句保持在同一行
AllowShortIfStatementsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: false
# 允许短的循环保持在同一行
AllowShortLoopsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
@@ -109,7 +109,6 @@ BreakStringLiterals: false
# 每行字符的限制0表示没有限制
ColumnLimit: 120
CompactNamespaces: true
# 构造函数的初始化列表要么都在同一行,要么都各自一行
@@ -157,7 +156,7 @@ PointerAlignment: Right
ReflowComments: true
# 允许排序#include
SortIncludes: false
SortIncludes: true
# 允许排序 using 声明
SortUsingDeclarations: false

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# Xmake cache
.xmake/
build/
# MacOS Cache
.DS_Store
.vscode
.VSCodeCounter
/test.fig

View File

Before

Width:  |  Height:  |  Size: 633 KiB

After

Width:  |  Height:  |  Size: 633 KiB

14
src/Ast/Ast.hpp Normal file
View 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
View 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

View 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);
}
};
};

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
};

View 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
View 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

View 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>;
};

View 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>;
};

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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(&current)),
// "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
View 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 &currentPosition() { 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
View 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
View 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
View 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
View 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
View 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';
}

View 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
View 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
View 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
View File

61
xmake.lua Normal file
View 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")