Compare commits

...

17 Commits

Author SHA1 Message Date
PuqiAR 68e6552d07 Rewrite: C++ → Rust, Lexer + Error system complete
全量 Rust 重写 Fig 编译器前端。

Lexer: 43 个测试,千行压力测试通过,完整 UTF-8 支持(中文标识符/字符串),运算符最长匹配打表,单行/多行注释 + 错误恢复。

Error System: Diagnostic trait + Warning/Error/Critical 三级严重度,源码上下文渲染(Unicode 波浪线、中英双语 i18n),SourcePosition/SourceRange 为 LSP 准备,Related 关联信息,thrower 宏追踪编译器源码位置。

Build: cargo + build.rs(git hash + 编译时间戳)。

删除全部 C++ 源码。
2026-07-23 22:50:37 +08:00
PuqiAR 82c7c7178f Refactor: 更新OpCode枚举,添加详细注释 2026-07-23 13:01:56 +08:00
PuqiAR 9f7fe79a2e Made some preparations to refactor semantic analysis and type system 2026-07-21 22:52:17 +08:00
PuqiAR 0f5304001c 优化注释 2026-07-04 19:32:01 +08:00
PuqiAR 680197aafe Refactor: 重构Parser和AST结构,以支持新的语言特性
- 更新了 ParserTest,以改进文件路径处理和输出格式。
- 在 StmtParser 中新增了 parseConstDecl 和 parseForStmt 方法,用于处理常量声明和 for 循环。
- TypeExpr现归类为Expr。TypeExpr属于Expr,语义阶段视为Expr
- 添加了新的 AST 节点:PostfixExpr、TernaryExpr、ForStmt 和 ImportStmt,用于表示新的语法结构。
2026-06-06 22:12:04 +08:00
PuqiAR 4f87078a87 Plan 2026-06-01 18:52:06 +08:00
PuqiAR 9338c21449 *(无用) feat: 在VM.cpp中添加likely属性以优化分支预测
refact: 在xmake.lua中优化构建设置
2026-05-10 21:25:05 +08:00
PuqiAR 98de782760 feat: 增加repl入口,-r/--repl。 添加计时选项。 -- 我发现一个问题,analyzer没法保存环境。完了。 2026-04-30 21:24:11 +08:00
PuqiAR fafa2b4946 feat: 在解析器中实现 Lambda 和 new 表达式
- 增加了对 Lambda 表达式的初步解析支持,包括参数处理和返回类型。Lambda闭包尚未支持。
- 引入了用于对象初始化的新的表达式,支持可选的命名参数。
- 改进了表达式语法错误的错误报告。
- 更新了解析器和分析器以处理新的表达式类型并验证其语义。
- 修改了现有测试以涵盖新功能并确保其正确性。
- 改进了各种解析和语义错误的诊断。
2026-04-12 10:07:51 +08:00
PuqiAR 570a87c3cd feat: 增加函数类型表达式支持,更新解析器和分析器 2026-03-18 17:30:09 +08:00
PuqiAR e1d9812f92 refact:实现参数解析器和入口点
- 新增了一个名为 ArgumentParser 的类来处理命令行参数,其中包括用于显示帮助、显示版本和显示许可证的标志。
- 更新了 main.cpp 以使用 ArgumentParser 来改进命令行界面。
- 创建了 Entry.cpp 和 Entry.hpp 来封装虚拟机执行逻辑,从而实现更好的关注点分离。
- 调整了 xmake.lua 以包含 ArgumentParser 和 Entry 组件的新源文件。
- 强化了命令行使用的错误处理和用户反馈。
2026-03-14 14:18:21 +08:00
PuqiAR 6bcc98bdb3 删除无用的东西233 2026-03-13 20:11:46 +08:00
PuqiAR 91b5a0e384 feat: 增加一个很简单的repl(还不能用) 写了readme 2026-03-13 01:28:43 +08:00
PuqiAR c0eacfd236 refactor: 修改Disassembler使用CoreIO的stream 2026-03-11 21:51:58 +08:00
PuqiAR 51a939ac45 对编译器和虚拟机进行重构,以支持闭包和垃圾回收功能
- 去除了不再使用的结构,并更新了编译器以处理新的闭包语义。
- 改进了 Compiler,使其能够生成带有源位置跟踪的指令。
- 在 FunctionObject 和 VM 中引入了作用域变量管理,以支持动态闭包。
- 实现了使用标记-扫描(Mark-And-Sweep) (Tri-Color tracing) 算法的垃圾回收机制,包括对作用域变量的处理。
- 在 VM 中增加了函数加载和作用域变量检索的支持。
- 更新了对象模型,包括引入 InstanceObject 并改进内存管理。
- 添加了用于调试的全局变量打印功能。
2026-03-11 16:53:10 +08:00
PuqiAR 0f635ccf2b 重构类型系统并改进诊断功能
- 更新了类型系统,新增了类型并优化了结构。
- 引入了基类型和派生类,用于函数、结构体和接口类型。
- 实现了类型上下文,用于管理内置类型和类型解析。
- 添加了诊断类,用于收集和报告警告和错误。
- 通过改进错误处理增强了虚拟机执行,以应对递归限制问题。
- 实现了反汇编器,将字节码转换为代码,以改善调试和分析。
- 添加了新的抽象语法树节点,用于成员表达式、对象初始化、接口和结构体定义。
- 引入了语义错误测试,包括重定义、未声明的变量和无效的结构字段。
2026-03-10 12:33:17 +08:00
PuqiAR 90448006ff refactor: 引入 Arena 内存池并优化指令分发,为类型系统重构做准备 2026-03-08 15:59:55 +08:00
111 changed files with 2675 additions and 41141 deletions
-33
View File
@@ -1,33 +0,0 @@
FROM ubuntu:24.04
# 1. 设置镜像源(可选,用于加速)
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# 2. 一次性安装所有依赖(工具链、库、CI工具)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
wget gnupg ca-certificates \
clang-19 lld-19 libc++-19-dev libc++abi-19-dev \
mingw-w64 g++-mingw-w64 \
git tar curl \
python3 python3-pip python3.12-venv libpython3.12 \
&& rm -rf /var/lib/apt/lists/* && \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 && \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 && \
ln -sf /usr/bin/ld.lld-19 /usr/bin/ld.lld
# 3. 安装xmake
RUN wget -O /usr/local/bin/xmake \
https://git.fig-lang.cn/PuqiAR/xmake-binary-copy/raw/commit/989d1f2dabb0bc8d5981a5f900c2cf7c2ac78ee4/xmake-bundle-v3.0.5.linux.x86_64 && \
chmod +x /usr/local/bin/xmake
# 4. 创建非root用户
RUN useradd -m -s /bin/bash builder
USER builder
WORKDIR /home/builder
RUN xmake --version | head -1 && \
clang++ --version | head -1 && \
git --version && \
echo "✅ 核心工具就绪"
-213
View File
@@ -1,213 +0,0 @@
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
Language: Cpp
# BasedOnStyle: LLVM
# 访问说明符(public、private等)的偏移
AccessModifierOffset: -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
AlignAfterOpenBracket: DontAlign
# 连续赋值时,对齐所有等号
AlignConsecutiveAssignments: true
# 连续声明时,对齐所有声明的变量名
AlignConsecutiveDeclarations: true
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
AlignEscapedNewlines: Right
# 水平对齐二元和三元表达式的操作数
AlignOperands: true
# 对齐连续的尾随的注释
AlignTrailingComments: true
# 允许函数声明的所有参数在放在下一行
AllowAllParametersOfDeclarationOnNextLine: true
# 允许短的块放在同一行
AllowShortBlocksOnASingleLine: true
# 允许短的case标签放在同一行
AllowShortCaseLabelsOnASingleLine: true
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
AllowShortFunctionsOnASingleLine: Empty
# 允许短的if语句保持在同一行
AllowShortIfStatementsOnASingleLine: false
# 允许短的循环保持在同一行
AllowShortLoopsOnASingleLine: false
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
AlwaysBreakAfterReturnType: None
# 总是在多行string字面量前换行
AlwaysBreakBeforeMultilineStrings: false
# 总是在template声明后换行
AlwaysBreakTemplateDeclarations: true
# false表示函数实参要么都在同一行,要么都各自一行
BinPackArguments: false
# false表示所有形参要么都在同一行,要么都各自一行
BinPackParameters: false
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
BraceWrapping:
# class定义后面
AfterClass: true
# 控制语句后面
AfterControlStatement: true
# enum定义后面
AfterEnum: true
# 函数定义后面
AfterFunction: true
# 命名空间定义后面
AfterNamespace: true
# struct定义后面
AfterStruct: true
# union定义后面
AfterUnion: true
# extern之后
AfterExternBlock: false
# catch之前
BeforeCatch: true
# else之前
BeforeElse: true
# 缩进大括号
IndentBraces: false
# 分离空函数
SplitEmptyFunction: true
# 分离空语句
SplitEmptyRecord: false
# 分离空命名空间
SplitEmptyNamespace: false
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
BreakBeforeBinaryOperators: NonAssignment
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
# 注:这里认为语句块也属于函数
BreakBeforeBraces: Custom
# 在三元运算符前换行
BreakBeforeTernaryOperators: false
# 在构造函数的初始化列表的冒号后换行
BreakConstructorInitializers: AfterColon
#BreakInheritanceList: AfterColon
BreakStringLiterals: false
# 每行字符的限制,0表示没有限制
ColumnLimit: 100
CompactNamespaces: true
# 构造函数的初始化列表要么都在同一行,要么都各自一行
ConstructorInitializerAllOnOneLineOrOnePerLine: true
# 构造函数的初始化列表的缩进宽度
ConstructorInitializerIndentWidth: 4
# 延续的行的缩进宽度
ContinuationIndentWidth: 4
# 去除C++11的列表初始化的大括号{后和}前的空格
Cpp11BracedListStyle: true
# 继承最常用的指针和引用的对齐方式
DerivePointerAlignment: false
# 固定命名空间注释
FixNamespaceComments: true
# 缩进case标签
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
# 缩进宽度
IndentWidth: 4
# 函数返回类型换行时,缩进函数声明或函数定义的函数名
IndentWrappedFunctionNames: false
# 保留在块开始处的空行
KeepEmptyLinesAtTheStartOfBlocks: false
# 连续空行的最大数量
MaxEmptyLinesToKeep: 1
# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
NamespaceIndentation: All
# 指针和引用的对齐: Left, Right, Middle
PointerAlignment: Right
# 允许重新排版注释
ReflowComments: true
# 允许排序#include
SortIncludes: true
# 允许排序 using 声明
SortUsingDeclarations: false
# 在C风格类型转换后添加空格
SpaceAfterCStyleCast: true
# true -> (int) 0.1 false-> (int)0.1
# 在Template 关键字后面添加空格
SpaceAfterTemplateKeyword: true
# 在赋值运算符之前添加空格
SpaceBeforeAssignmentOperators: true
# SpaceBeforeCpp11BracedList: true
# SpaceBeforeCtorInitializerColon: true
# SpaceBeforeInheritanceColon: true
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
SpaceBeforeParens: ControlStatements
# SpaceBeforeRangeBasedForLoopColon: true
# 在空的圆括号中添加空格
SpaceInEmptyParentheses: false
# 在尾随的评论前添加的空格数(只适用于//)
SpacesBeforeTrailingComments: 1
# 在尖括号的<后和>前添加空格
SpacesInAngles: false
# 在C风格类型转换的括号中添加空格
SpacesInCStyleCastParentheses: false
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
SpacesInContainerLiterals: true
# 在圆括号的(后和)前添加空格
SpacesInParentheses: false
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
SpacesInSquareBrackets: false
# 标准: Cpp03, Cpp11, Auto
Standard: Auto
# tab宽度
TabWidth: 4
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
UseTab: Never
-365
View File
@@ -1,365 +0,0 @@
name: Release Build
on:
push:
tags: ["*"]
workflow_dispatch:
inputs:
version:
description: "版本号 (例如: v1.0.0)"
required: true
default: "dev-build"
jobs:
build-linux-x64:
runs-on: ubuntu
container:
image: git.fig-lang.cn/puqiar/fig-ci:base-latest
options: --network=host
steps:
- name: 验证构建环境
run: |
echo "=== 环境验证开始 ==="
xmake --version
clang++ --version | head -1
echo "=== 环境验证通过 ==="
- name: 检出代码
run: |
git clone https://git.fig-lang.cn/${{ github.repository }} .
git checkout ${{ github.ref }}
- name: 设置版本和提交信息
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}"
else
VERSION="${{ github.ref_name }}"
fi
echo "构建版本: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV
# 拿提交消息
COMMIT_MSG=$(git log -3 --pretty=%B)
echo "COMMIT_MSG<<EOF" >> $GITHUB_ENV
echo "$COMMIT_MSG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- name: 构建项目 (Linux)
run: |
echo "开始构建Linux版本..."
xmake f -p linux -a x86_64 -m release -y
xmake build -j$(nproc) Fig
echo "Linux构建成功。"
# 🔧 新增:构建Linux平台安装器
- name: 构建Linux安装器
run: |
echo "开始构建Linux安装器..."
cd Installer/ConsoleInstaller
python3 -m venv venv
. venv/bin/activate
pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
# 安装依赖并构建
pip install --upgrade pip
pip install -r requirements.txt
python3 -m PyInstaller -F -n FigSetup-Linux --distpath ./dist/linux main.py
echo "Linux安装器构建完成"
- name: 打包Linux发布文件
run: |
VERSION="${{ env.VERSION }}"
PACKAGE_NAME="Fig-${VERSION}-linux-x86_64"
mkdir -p "${PACKAGE_NAME}"
cp build/linux/x86_64/release/Fig "${PACKAGE_NAME}/"
if [ -d "src/Module/Library" ]; then
cp -r src/Module/Library "${PACKAGE_NAME}/"
echo "已包含Library目录。"
fi
tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}"
sha256sum "${PACKAGE_NAME}.tar.gz" > "${PACKAGE_NAME}.sha256"
echo "Linux打包完成: ${PACKAGE_NAME}.tar.gz"
- name: 发布Linux版本到Gitea
env:
GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
VERSION="${{ env.VERSION }}"
if [ -z "$VERSION" ]; then
VERSION="${{ github.ref_name }}"
fi
COMMIT_MSG="${{ env.COMMIT_MSG }}"
API="https://git.fig-lang.cn/api/v1/repos/${{ github.repository }}"
echo "正在检查版本 $VERSION 的发布状态..."
# 准备 JSON 数据
VERSION="$VERSION" COMMIT_MSG="$COMMIT_MSG" python3 -c "import json, os; print(json.dumps({'tag_name': os.environ.get('VERSION', ''), 'name': 'Fig ' + os.environ.get('VERSION', ''), 'body': os.environ.get('COMMIT_MSG', ''), 'draft': False, 'prerelease': False}))" > release_body.json
# 1. 尝试获取已有发布
RESPONSE_TAG=$(curl -sS -H "Authorization: token $GITEA_TOKEN" "$API/releases/tags/$VERSION" 2>/dev/null || echo '{"id":0}')
RELEASE_ID=$(echo "$RESPONSE_TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "0" ]; then
echo "✅ 找到已有发布 (ID: $RELEASE_ID),正在更新说明..."
curl -sS -X PATCH -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d @release_body.json \
"$API/releases/$RELEASE_ID" > /dev/null
else
echo "未找到已有发布,准备创建新发布..."
RESPONSE=$(curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d @release_body.json \
"$API/releases" 2>/dev/null || echo '{"id":0}')
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
# 再次尝试获取,防止并发冲突
RESPONSE_TAG=$(curl -sS -H "Authorization: token $GITEA_TOKEN" "$API/releases/tags/$VERSION" 2>/dev/null || echo '{"id":0}')
RELEASE_ID=$(echo "$RESPONSE_TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
fi
fi
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
echo "❌ 错误:无法获取或创建发布 ID"
exit 1
fi
echo "✅ 使用发布 ID: $RELEASE_ID 进行上传"
# 上传资产
PACKAGE_ZIP="Fig-$VERSION-linux-x86_64.tar.gz"
PACKAGE_SHA="Fig-$VERSION-linux-x86_64.sha256"
echo "正在上传 $PACKAGE_ZIP ..."
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$PACKAGE_ZIP" \
"$API/releases/$RELEASE_ID/assets?name=$PACKAGE_ZIP" > /dev/null
echo "正在上传 $PACKAGE_SHA ..."
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: text/plain" \
--data-binary "@$PACKAGE_SHA" \
"$API/releases/$RELEASE_ID/assets?name=$PACKAGE_SHA" > /dev/null
# 🔧 上传Linux安装器
INSTALLER="Installer/ConsoleInstaller/dist/linux/FigSetup-Linux"
if [ -f "$INSTALLER" ]; then
echo "正在上传Linux安装器..."
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$INSTALLER" \
"$API/releases/$RELEASE_ID/assets?name=FigSetup-Linux" > /dev/null
fi
echo "✅ Linux版本发布完成!"
build-windows-x64:
runs-on: windows
steps:
- name: 验证Windows工具链
run: |
Set-ExecutionPolicy Bypass -Scope Process -Force
# 检查 xmake
if (Get-Command xmake -ErrorAction SilentlyContinue) {
Write-Host '✅ xmake 已安装'
xmake --version
} else { Write-Host '警告: xmake 未找到' }
# 检查 clang++
if (Get-Command clang++ -ErrorAction SilentlyContinue) {
Write-Host '✅ clang++ 已安装'
clang++ --version
} else { Write-Host '警告: clang++ 未找到' }
- name: 检出代码
run: |
$env:Path = "C:\Program Files\Git\cmd;$env:Path"
git clone https://git.fig-lang.cn/$env:GITHUB_REPOSITORY .
git checkout ${{ github.ref }}
- name: 设置版本和提交信息
run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') {
$VERSION = $env:INPUT_VERSION
if (-not $VERSION) { $VERSION = $env:VERSION_INPUT }
if (-not $VERSION) { $VERSION = "dev-build" }
} else {
$VERSION = "${{ github.ref_name }}"
}
Write-Host "构建版本: $VERSION"
# 确保无 BOM 的 UTF8
[System.IO.File]::AppendAllText($env:GITHUB_ENV, "VERSION=$VERSION`n")
# 提交消息
$COMMIT_MSG = git log -3 --pretty=%B
[System.IO.File]::AppendAllText($env:GITHUB_ENV, "COMMIT_MSG<<EOF`n$COMMIT_MSG`nEOF`n")
- name: 构建项目 (Windows Native)
run: |
xmake f -p windows -a x86_64 -m release -y
xmake build -j $env:NUMBER_OF_PROCESSORS Fig
Write-Host 'Windows构建成功。'
# 🔧 新增:构建Windows平台安装器
- name: 构建Windows安装器
run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Write-Host "开始构建Windows安装器..."
cd Installer\ConsoleInstaller
pip install -r requirements.txt
pyinstaller -F -i logo.ico -n FigSetup --distpath .\dist\windows main.py
Write-Host "Windows安装器构建完成"
- name: 打包Windows发布文件
run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$VERSION = $env:VERSION
if (-not $VERSION) {
$VERSION = "${{ github.ref_name }}"
Write-Host "⚠️ 警告:从环境变量获取 VERSION 失败,回退到 github.ref_name: $VERSION"
}
Write-Host "打包版本: $VERSION"
$PACKAGE_NAME = "Fig-${VERSION}-windows-x86_64"
Write-Host "打包名称: $PACKAGE_NAME"
New-Item -ItemType Directory -Force -Path $PACKAGE_NAME
# 查找可执行文件
if (Test-Path 'build\windows\x86_64\release\Fig.exe') {
Copy-Item 'build\windows\x86_64\release\Fig.exe' $PACKAGE_NAME\
} elseif (Test-Path 'build\windows\x86_64\release\Fig') {
Copy-Item 'build\windows\x86_64\release\Fig' $PACKAGE_NAME\Fig.exe
} else {
Write-Host '错误:未找到构建输出文件'
exit 1
}
if (Test-Path 'src\Module\Library') {
Copy-Item -Recurse 'src\Module\Library' $PACKAGE_NAME\
}
# 压缩文件
$ZIP_NAME = "$PACKAGE_NAME.zip"
Compress-Archive -Path $PACKAGE_NAME -DestinationPath $ZIP_NAME
# 生成校验文件
$Hash = Get-FileHash $ZIP_NAME -Algorithm SHA256
"$($Hash.Hash) $ZIP_NAME" | Out-File "$PACKAGE_NAME.sha256" -Encoding UTF8
Write-Host "Windows打包完成: $ZIP_NAME"
- name: 发布Windows版本到Gitea
env:
GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$VERSION = $env:VERSION
$COMMIT_MSG = $env:COMMIT_MSG
if (-not $VERSION) {
$VERSION = "${{ github.ref_name }}"
Write-Host "⚠️ 警告:从环境变量获取 VERSION 失败,回退到 github.ref_name: $VERSION"
}
if (-not $VERSION) {
Write-Host "❌ 错误:版本号仍然为空,无法创建发布。"
exit 1
}
$REPO = $env:GITHUB_REPOSITORY
$API = "https://git.fig-lang.cn/api/v1/repos/$REPO"
$TOKEN = $env:GITEA_TOKEN
$HEADERS = @{
Authorization = "token $TOKEN"
'Content-Type' = 'application/json'
}
$ZIP_FILE = "Fig-$VERSION-windows-x86_64.zip"
$HASH_FILE = "Fig-$VERSION-windows-x86_64.sha256"
$INSTALLER_PATH = "Installer\ConsoleInstaller\dist\windows\FigSetup.exe"
if (-not (Test-Path $ZIP_FILE)) {
Write-Host "❌ 错误:找不到ZIP文件 $ZIP_FILE"
exit 1
}
$CREATE_BODY = @{
tag_name = $VERSION
name = "Fig $VERSION"
body = $COMMIT_MSG
draft = $false
prerelease = $false
} | ConvertTo-Json -Compress
Write-Host "正在检查版本 $VERSION 的发布状态..."
$RELEASE_ID = $null
try {
$EXISTING = Invoke-RestMethod -Uri "$API/releases/tags/$VERSION" -Headers $HEADERS -ErrorAction Stop
if ($EXISTING -and $EXISTING.id) {
$RELEASE_ID = $EXISTING.id
Write-Host "✅ 找到已有发布 (ID: $RELEASE_ID),正在更新..."
Invoke-RestMethod -Method Patch -Uri "$API/releases/$RELEASE_ID" -Headers $HEADERS -Body $CREATE_BODY -ErrorAction Stop | Out-Null
}
} catch {
Write-Host "未找到已有发布,准备创建新发布..."
}
if (-not $RELEASE_ID) {
try {
Write-Host "正在创建新发布: $VERSION ..."
$RESPONSE = Invoke-RestMethod -Method Post -Uri "$API/releases" -Headers $HEADERS -Body $CREATE_BODY -ErrorAction Stop
$RELEASE_ID = $RESPONSE.id
Write-Host "✅ 发布创建成功 (ID: $RELEASE_ID)"
} catch {
$err = $_.Exception.Message
Write-Host "❌ 创建发布失败: $err"
# 最后一次尝试:再次尝试按标签获取,防止由于并发导致的冲突
try {
$RETRY = Invoke-RestMethod -Uri "$API/releases/tags/$VERSION" -Headers $HEADERS -ErrorAction Stop
$RELEASE_ID = $RETRY.id
Write-Host "✅ 重试获取发布成功 (ID: $RELEASE_ID)"
} catch {
Write-Host "❌ 无法获取或创建发布 ID"
exit 1
}
}
}
# 上传资产
Write-Host "正在上传文件..."
$ASSETS = @(
@{ Name = $ZIP_FILE; Path = $ZIP_FILE; ContentType = "application/octet-stream" },
@{ Name = $HASH_FILE; Path = $HASH_FILE; ContentType = "text/plain" }
)
if (Test-Path $INSTALLER_PATH) {
$ASSETS += @{ Name = "FigSetup.exe"; Path = $INSTALLER_PATH; ContentType = "application/octet-stream" }
}
foreach ($asset in $ASSETS) {
Write-Host "正在上传 $($asset.Name) ..."
try {
# 如果资产已存在,Gitea 可能会报错,这里简单处理
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=$($asset.Name)" `
-Headers @{ Authorization = "token $TOKEN"; 'Content-Type' = $asset.ContentType } `
-InFile $asset.Path -ErrorAction SilentlyContinue | Out-Null
} catch {
Write-Host "⚠️ 上传 $($asset.Name) 失败,可能已存在。"
}
}
Write-Host "✅ Windows版本发布完成!"
+4
View File
@@ -9,3 +9,7 @@ build/
.VSCodeCounter
/test.fig
# Added by cargo
/target
Generated
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "fig"
version = "0.6.0"
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "fig"
version = "0.6.0"
edition = "2021"
[dependencies]
+11
View File
@@ -0,0 +1,11 @@
function fib(n)
if (n <= 1) then
return n
else
return fib(n - 1) + fib(n - 2) end
end
local start = os.clock()
local result = fib(30)
local endt = os.clock()
print(result, " cost: ", (endt - start) * 1000, "ms")
+2 -2
View File
@@ -1,11 +1,11 @@
from time import time as tt
def fib(x:int) -> int:
if x <= 1: return x;
if x <= 1: return x
return fib(x-1) + fib(x-2)
if __name__ == '__main__':
t0 = tt()
result = fib(30)
result = fib(35)
t1 = tt()
print('cost: ',t1-t0, 'result:', result)
-9
View File
@@ -13,15 +13,6 @@ furnished to do so, subject to the following conditions:
copies or substantial portions of the Software.
2. This project includes code from the following projects with their respective licenses:
- argparse (MIT License)
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- magic_enum (MIT License)
Copyright (c) 2019 - 2024 Daniil Goncharov
+30
View File
@@ -1,2 +1,32 @@
# Fig
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
</picture>
> **🔔 Main Repository: https://git.fig-lang.cn/PuqiAR/Fig**
> *GitHub is only a mirror. Please submit issues and PRs to the main repository.*
[English](./README.md) | [中文](./README_zh-CN.md)
![License](https://img.shields.io/badge/license-MIT-blue)
![Status](https://img.shields.io/badge/status-0.6.0--alpha-yellow)
![Rust](https://img.shields.io/badge/Rust-100%25-orange)
![cargo](https://img.shields.io/badge/cargo-build-green)
![Platform](https://img.shields.io/badge/Windows%20·%20Linux%20·%20macOS-lightgrey)
**Fig** is a programming language that blends dynamic typing with optional static type annotations, built on reference-based value semantics.
> **v0.6.0 — Rust rewrite in progress.**
## Progress
```
Lexer ████████████████████ 100% (43 tests, all pass)
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
VM ░░░░░░░░░░░░░░░░░░░░ 0%
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
```
+32
View File
@@ -0,0 +1,32 @@
# Fig
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
</picture>
> **🔔 主仓库:https://git.fig-lang.cn/PuqiAR/Fig**
> *GitHub 仅为镜像,Issue 与 PR 请提交至主仓库*
[English](./README.md) | [中文](./README_zh-CN.md)
![License](https://img.shields.io/badge/license-MIT-blue)
![Status](https://img.shields.io/badge/status-0.6.0--alpha-yellow)
![Rust](https://img.shields.io/badge/Rust-100%25-orange)
![cargo](https://img.shields.io/badge/cargo-构建-green)
![Platform](https://img.shields.io/badge/Windows%20·%20Linux%20·%20macOS-lightgrey)
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。
> **v0.6.0 — Rust 重写进行中。**
## 进度
```
Lexer ████████████████████ 100% (43 测试, 全部通过)
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
VM ░░░░░░░░░░░░░░░░░░░░ 0%
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
```
+63
View File
@@ -0,0 +1,63 @@
use std::process::Command;
fn main() {
let hash = Command::new("git")
.args(["rev-parse", "--short=7", "HEAD"])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.unwrap_or_default()
.trim()
.to_string();
println!("cargo:rustc-env=GIT_HASH={}", hash);
println!("cargo:rustc-env=BUILD_TIME={}", chrono_build_time());
// 编译器 IDmacOS 上用 clang/LLVMLinux 上用 GCCWindows 上用 MSVC
#[cfg(target_os = "macos")]
{ println!("cargo:rustc-env=FIG_COMPILER_ID=LLVM"); }
#[cfg(target_os = "linux")]
{ println!("cargo:rustc-env=FIG_COMPILER_ID=GCC"); }
#[cfg(target_os = "windows")]
{ println!("cargo:rustc-env=FIG_COMPILER_ID=MSVC"); }
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
{ println!("cargo:rustc-env=FIG_COMPILER_ID=unknown"); }
}
// chrono 还没引入,先用简单方式
fn chrono_build_time() -> String {
// RFC 3339 without nanoseconds: "2026-07-23T13:14:00+08:00"
// 但纯 std 拿不到时区,用环境变量 SOURCE_DATE_EPOCH 或 UTC
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let secs = now % 86400;
let days = now / 86400;
// 简单计算 UTC 时间,避免引入 chrono
let hour = (secs / 3600) % 24;
let min = (secs / 60) % 60;
let sec = secs % 60;
// 粗略日期计算(从 1970-01-01 开始)
let (y, mo, d) = civil_from_days(days as i64);
format!("{y:04}-{mo:02}-{d:02} {hour:02}:{min:02}:{sec:02} UTC")
}
// 从 Unix epoch 天数反算年月日
fn civil_from_days(days: i64) -> (i64, u32, u32) {
let z = days + 719468;
let era = if z >= 0 { z } else { z - 146096 } / 146097;
let doe = (z - era * 146097) as u32;
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let y = yoe as i64 + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
let y = if m <= 2 { y + 1 } else { y };
(y, m, d)
}
-3
View File
@@ -1,3 +0,0 @@
-std=c++2b
-static
-stdlib=libc++
-23
View File
@@ -1,23 +0,0 @@
/*!
@file src/Ast/Ast.hpp
@brief Ast总链接
@author PuqiAR (im@puqiar.top)
@date 2026-02-17
*/
#pragma once
#include <Ast/Expr/CallExpr.hpp>
#include <Ast/Expr/IdentiExpr.hpp>
#include <Ast/Expr/IndexExpr.hpp>
#include <Ast/Expr/InfixExpr.hpp>
#include <Ast/Expr/LiteralExpr.hpp>
#include <Ast/Expr/PrefixExpr.hpp>
#include <Ast/Stmt/ControlFlowStmts.hpp>
#include <Ast/Stmt/ExprStmt.hpp>
#include <Ast/Stmt/FnDefStmt.hpp>
#include <Ast/Stmt/IfStmt.hpp>
#include <Ast/Stmt/VarDecl.hpp>
#include <Ast/Stmt/WhileStmt.hpp>
#include <Ast/TypeExpr.hpp>
-156
View File
@@ -1,156 +0,0 @@
/*!
@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 <Sema/Type.hpp>
#include <cstdint>
namespace Fig
{
enum class AstType : std::uint8_t
{
AstNode, // 基类
Program, // 程序
Expr, // 表达式
Stmt, // 语句
BlockStmt, // 块语句
/* Expressions */
IdentiExpr, // 标识符表达式
LiteralExpr, // 字面量表达式
PrefixExpr, // 一元 前缀表达式
InfixExpr, // 二元 中缀表达式
IndexExpr, // 后缀表达式,索引
CallExpr, // 后缀表达式,函数调用
/* Statements */
ExprStmt, // 表达式语句,如 println(1)
VarDecl, // 变量声明
IfStmt, // If语句
ElseIfStmt, // ElseIf语句,不准悬空,平铺式else if
WhileStmt, // while语句
FnDefStmt, // func函数定义语句
ReturnStmt, // 返回语句
BreakStmt, // break语句
ContinueStmt, // continue语句
/* Type Expressions */
TypeExpr, // 基类
NamedTypeExpr, // 命名类型,支持namespace, std.file这样的
// 泛型等...
};
struct AstNode
{
AstType type = AstType::AstNode;
SourceLocation location;
virtual String toString() const = 0;
virtual ~AstNode() {};
};
struct TypeExpr : public AstNode
{
TypeExpr()
{
type = AstType::TypeExpr;
}
virtual ~TypeExpr() = default;
};
struct Program;
struct Expr : public AstNode
{
TypeInfo *resolvedType = nullptr;
// TODO: 可选的常量折叠
// 拓展 isConstExpr和 constValue槽位
Expr()
{
type = AstType::Expr;
}
};
struct Stmt : public AstNode
{
bool isPublic;
Stmt()
{
type = AstType::Stmt;
}
};
struct Program final : public AstNode
{
DynArray<Stmt *> nodes;
Program()
{
type = AstType::Program;
}
Program(DynArray<Stmt *> _nodes)
{
type = AstType::Program;
nodes = std::move(_nodes);
if (!_nodes.empty())
{
location = std::move(_nodes.back()->location);
}
}
virtual String toString() const override
{
return "<Program>";
}
};
struct BlockStmt final : public Stmt
{
DynArray<Stmt *> nodes;
BlockStmt()
{
type = AstType::BlockStmt;
}
BlockStmt(DynArray<Stmt *> _nodes)
{
type = AstType::BlockStmt;
nodes = std::move(_nodes);
if (!_nodes.empty())
{
location = std::move(_nodes.back()->location);
}
}
virtual String toString() const override
{
return "<BlockStmt>";
}
};
}; // namespace Fig
namespace std
{
template <>
struct std::formatter<Fig::AstNode *, char>
{
constexpr auto parse(std::format_parse_context &ctx)
{
return ctx.begin();
}
template <typename FormatContext>
auto format(const Fig::AstNode *_node, FormatContext &ctx) const
{
return std::format_to(ctx.out(), "{}", _node->toString().toStdString());
}
};
}; // namespace std
-60
View File
@@ -1,60 +0,0 @@
/*!
@file src/Ast/Expr/CallExpr.hpp
@brief CallExpr等定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-17
*/
#pragma once
#include <Ast/Base.hpp>
#include <Deps/Deps.hpp>
namespace Fig
{
struct FnCallArgs
{
DynArray<Expr *> args;
size_t size() const
{
return args.size();
}
String toString() const
{
String str = "(";
for (const Expr *expr : args)
{
if (expr != args.front())
{
str += ", ";
}
str += expr->toString();
}
str += ")";
return str;
}
};
struct CallExpr final : public Expr
{
Expr *callee;
FnCallArgs args;
CallExpr()
{
type = AstType::CallExpr;
}
CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args))
{
type = AstType::CallExpr;
}
virtual String toString() const override
{
return std::format("<CallExpr: '{}{}'>", callee->toString(), args.toString());
}
};
} // namespace Fig
-48
View File
@@ -1,48 +0,0 @@
/*!
@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;
// Analyzer槽位
// 寻址空间
bool isGlobal = false; // 是否全局, 全局变量存哈希/堆
// 仅 isGlobal = false 有效
int resolvedDepth = -1; // 用于获取闭包上值, -1 代表未解析
// 栈内槽位
int localId = -1; // 局部变量id, -1 未解析
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);
}
};
};
-35
View File
@@ -1,35 +0,0 @@
/*!
@file src/Ast/Expr/IndexExpr.hpp
@brief IndexExpr定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-17
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct IndexExpr final : public Expr
{
Expr *base;
Expr *index;
IndexExpr()
{
type = AstType::IndexExpr;
}
IndexExpr(Expr *_base, Expr *_index) : base(_base), index(_index)
{
type = AstType::IndexExpr;
location = base->location;
}
virtual String toString() const override
{
return std::format("<IndexExpr: '{}[{}]'>", base->toString(), index->toString());
}
};
}; // namespace Fig
-39
View File
@@ -1,39 +0,0 @@
/*!
@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
View File
@@ -1,36 +0,0 @@
/*!
@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
View File
@@ -1,39 +0,0 @@
/*!
@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
View File
@@ -1,184 +0,0 @@
/*!
@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
-92
View File
@@ -1,92 +0,0 @@
/*!
@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, // 取引用 &
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
};
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, // .
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
};
constexpr unsigned int GetOperatorsSize()
{
// 获取全部运算符的数量
return static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(BinaryOperator::Count);
}
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
-72
View File
@@ -1,72 +0,0 @@
/*!
@file src/Ast/Stmt/ControlFlowStmts
@brief 控制流语句 return, break, continue 定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-27
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct ReturnStmt final : public Stmt
{
Expr *value;
ReturnStmt()
{
type = AstType::ReturnStmt;
}
ReturnStmt(Expr *_value, SourceLocation _location) : value(_value)
{
type = AstType::ReturnStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<ReturnStmt '{}'>", value->toString());
}
};
struct BreakStmt final : public Stmt
{
BreakStmt()
{
type = AstType::BreakStmt;
}
BreakStmt(SourceLocation _location)
{
type = AstType::BreakStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return "<BreakStmt>";
}
};
struct ContinueStmt final : public Stmt
{
ContinueStmt()
{
type = AstType::ContinueStmt;
}
ContinueStmt(SourceLocation _location)
{
type = AstType::ContinueStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return "<ContinueStmt>";
}
};
}; // namespace Fig
-35
View File
@@ -1,35 +0,0 @@
/*!
@file src/Ast/Stmt/ExprStmt.hpp
@brief ExprStmt定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct ExprStmt final : public Stmt
{
Expr *expr;
ExprStmt()
{
type = AstType::ExprStmt;
}
ExprStmt(Expr *_expr) :
expr(_expr)
{
type = AstType::ExprStmt;
location = _expr->location;
}
virtual String toString() const override
{
return std::format("<ExprStmt: {}>", expr->toString());
}
};
}
-102
View File
@@ -1,102 +0,0 @@
/*!
@file src/Ast/Stmt/FnDefStmt.hpp
@brief FnDefStmt定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-25
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct Param
{
String name;
SourceLocation location;
TypeInfo *resolvedType = nullptr;
int localId = -1;
virtual String toString() const = 0;
};
struct PosParam final : public Param
{
TypeExpr *type;
Expr *defaultValue;
PosParam() {}
PosParam(String _name, TypeExpr *_type, Expr *_defaultValue, SourceLocation _location) :
type(_type), defaultValue(_defaultValue)
{
name = std::move(_name);
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<Pos {}: {}{}>",
name,
(type ? type->toString() : "Any"),
(defaultValue ? " =" + defaultValue->toString() : ""));
}
};
/*
(public) func foo([name: (type) (= default value)]) (-> return type)
{
...
}
*/
struct FnDefStmt final : public Stmt
{
String name;
DynArray<Param *> params;
TypeExpr *returnType;
BlockStmt *body;
TypeInfo *resolvedReturnType = nullptr;
int localId = -1;
FnDefStmt()
{
type = AstType::FnDefStmt;
}
FnDefStmt(bool _isPublic,
String _name,
DynArray<Param *> _params,
TypeExpr *_returnType,
BlockStmt *_body,
SourceLocation _location) :
name(std::move(_name)), params(std::move(_params)), returnType(_returnType), body(_body)
{
type = AstType::FnDefStmt;
isPublic = _isPublic;
location = std::move(_location);
}
virtual String toString() const override
{
String pStr;
for (const Param *p : params)
{
if (p != *params.begin())
{
pStr += ", ";
}
pStr += p->toString();
}
return std::format("<FnDefStmt {}{}({}) -> {} {{{}}}>",
(isPublic ? "public " : ""),
name,
pStr,
(returnType ? returnType->toString() : "Any"),
body->toString());
}
};
}; // namespace Fig
-69
View File
@@ -1,69 +0,0 @@
/*!
@file src/Ast/Stmt/IfStmt.hpp
@brief IfStmt定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-20
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct ElseIfStmt final : public Stmt
{
Expr *cond;
BlockStmt *consequent;
ElseIfStmt()
{
type = AstType::ElseIfStmt;
}
ElseIfStmt(Expr *_cond, BlockStmt *_consequent, SourceLocation _location) :
cond(_cond), consequent(_consequent)
{
type = AstType::ElseIfStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<ElseIf ({}) then {}>", cond->toString(), consequent->toString());
}
};
struct IfStmt final : public Stmt
{
Expr *cond;
BlockStmt *consequent;
DynArray<ElseIfStmt *> elifs;
BlockStmt *alternate;
IfStmt()
{
type = AstType::IfStmt;
}
IfStmt(Expr *_cond,
BlockStmt *_consequent,
DynArray<ElseIfStmt *> _elifs,
BlockStmt *_alternate,
SourceLocation _location) :
cond(_cond), consequent(_consequent), elifs(std::move(_elifs)), alternate(_alternate)
{
type = AstType::IfStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<If ({}) then {}, else if * {}, else {}>",
cond->toString(),
consequent->toString(),
elifs.size(),
alternate->toString());
}
};
}; // namespace Fig
-47
View File
@@ -1,47 +0,0 @@
/*!
@file src/Ast/Stmt/VarDecl.hpp
@brief VarDecl定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-17
*/
#pragma once
#include <Ast/Base.hpp>
#include <Deps/Deps.hpp>
namespace Fig
{
struct VarDecl final : public Stmt
{
String name;
TypeExpr *typeSpecifier;
bool isInfer; // 是否用了 := 类型推断
Expr *initExpr;
int localId = -1;
VarDecl()
{
type = AstType::VarDecl;
}
VarDecl(bool _isPublic, String _name, TypeExpr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
name(std::move(_name)),
typeSpecifier(_typeSpecifier),
isInfer(_isInfer),
initExpr(_initExpr) // location 指向关键字 var/const位置
{
type = AstType::VarDecl;
isPublic = _isPublic;
location = std::move(_location);
}
virtual String toString() const override
{
const String &typeSpecifierString = (typeSpecifier ? typeSpecifier->toString() : "Any");
const String &initExprString = (initExpr ? initExpr->toString() : "(disprovided)");
return std::format("<VarDecl {}: {} = {}>", name, typeSpecifierString, initExprString);
}
};
}; // namespace Fig
-37
View File
@@ -1,37 +0,0 @@
/*!
@file src/Ast/Stmt/WhileStmt.hpp
@brief WhileStmt定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-24
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct WhileStmt final : public Stmt
{
Expr *cond;
BlockStmt *body;
WhileStmt()
{
type = AstType::WhileStmt;
}
WhileStmt(Expr *_cond, BlockStmt *_body, SourceLocation _location) :
cond(_cond),
body(_body)
{
type = AstType::WhileStmt;
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<WhileStmt ({}) {{{}}}>", cond->toString(), body->toString());
}
};
}; // namespace Fig
-36
View File
@@ -1,36 +0,0 @@
/*!
@file src/Ast/TypeExpr.hpp
@brief TypeExpr定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-25
*/
#pragma once
#include <Ast/Base.hpp>
namespace Fig
{
struct NamedTypeExpr final : public TypeExpr
{
DynArray<String> path; // {"std", "file"} etc.
NamedTypeExpr()
{
type = AstType::NamedTypeExpr;
}
NamedTypeExpr(DynArray<String> _path, SourceLocation _location) :
path(std::move(_path))
{
type = AstType::NamedTypeExpr;
location = std::move(_location);
}
virtual String toString() const override
{
return std::format("<NamedTypeExpr '{}'>", path);
}
};
};
-80
View File
@@ -1,80 +0,0 @@
/*!
@file src/Bytecode/Bytecode.hpp
@brief 字节码Bytecode定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-18
*/
#pragma once
#include <cstdint>
#pragma once
#include <cstdint>
namespace Fig
{
// 定长 32-bit
using Instruction = std::uint32_t;
enum class OpCode : std::uint8_t
{
Exit, // 结束运行
LoadK, // iABx 模式: R[A] = Constants[Bx]
LoadTrue, // iABC: R[A] = true
LoadFalse, // iABC: R[A] = false
LoadNull, // iABC: R[A] = null
FastCall, // iABC: A: ProtoIdx, B: 函数起始寄存器
Call, // 动态派发 iABC: A: 函数体对象寄存器 B: 函数起始寄存器
Return, // iABC 模式: 返回 R[A] 的值
LoadFn, // 惰性装修, iABx: R[A] = new FunctionObject...
Jmp, // iAsBx: ip += sBx 无条件跳转
JmpIfFalse, // iAsBx: 如果 R[A] 为假, ip += sBx
Mov, // iABx: R[A] = R[Bx]
Add, // iABC: R[A] = R[B] + R[C]
Sub, // iABC: R[A] = R[B] - R[C]
Mul, // iABC: R[A] = R[B] * R[C]
Div, // iABC: R[A] = R[B] / R[C]
Mod, // iABC: R[A] = R[B] % R[C]
BitXor, // iABC: R[A] = R[B] ^ R[C]
Equal, // iABC: R[A] = R[B] == R[C]
NotEqual, // iABC: R[A] = R[B] != R[C]
Greater, // iABC: R[A] = R[B] > R[C]
Less, // iABC: R[A] = R[B] < R[C]
GreaterEqual, // iABC: R[A] = R[B] >= R[C]
LessEqual, // iABC: R[A] = R[B] <= R[C]
Count, // 哨兵
};
namespace Op
{
// [OpCode: 8] [A: 8] [Bx: 16]
[[nodiscard]] inline constexpr Instruction iABx(OpCode op, std::uint8_t a, std::uint16_t bx)
{
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
| (static_cast<std::uint32_t>(bx) << 16);
}
// [OpCode: 8] [A: 8] [B: 8] [C: 8]
[[nodiscard]] inline constexpr Instruction iABC(
OpCode op, std::uint8_t a, std::uint8_t b, std::uint8_t c)
{
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
| (static_cast<std::uint32_t>(b) << 16) | (static_cast<std::uint32_t>(c) << 24);
}
[[nodiscard]]
inline constexpr Instruction iAsBx(OpCode op, std::uint8_t a, std::int16_t sbx)
{
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
| (static_cast<std::uint32_t>(static_cast<std::uint16_t>(sbx)) << 16);
}
} // namespace Op
} // namespace Fig
-66
View File
@@ -1,66 +0,0 @@
#include <Compiler/Compiler.hpp>
#include <Core/Core.hpp>
#include <Deps/Deps.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <SourceManager/SourceManager.hpp>
#include <iostream>
#include <print>
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);
Parser parser(lexer, manager, fileName);
const auto &program_result = parser.Parse();
if (!program_result)
{
ReportError(program_result.error(), manager);
return 1;
}
Program *program = *program_result;
Compiler compiler(fileName, manager);
const auto &comp_result = compiler.Compile(program);
if (!comp_result)
{
ReportError(comp_result.error(), manager);
return 1;
}
CompiledModule *compiledModule = *comp_result;
size_t cnt = 0;
for (Proto *proto : compiledModule->protos)
{
std::cout << "=====================\n"
<< "Proto: " << cnt++ << '\n';
std::cout << "=== Constant Pool ===" << '\n';
for (size_t i = 0; i < proto->constants.size(); ++i)
{
std::print("[{}] {}\n", i, proto->constants[i].ToString());
}
DumpCode(proto->code);
std::cout << "\nMax Stack Size: " << (int) proto->maxStack << std::endl;
}
return 0;
}
-36
View File
@@ -1,36 +0,0 @@
/*!
@file src/Compiler/Compiler.cpp
@brief 编译器实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-18
*/
#include <Compiler/Compiler.hpp>
namespace Fig
{
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
{
current->freeReg = 0;
for (Stmt *stmt : program->nodes)
{
auto result = compileStmt(static_cast<Stmt *>(stmt));
if (!result)
{
return std::unexpected(result.error());
}
}
if (mainFuncIndex != -1)
{
std::uint8_t baseReg = AllocReg();
Emit(Op::iABC(OpCode::FastCall, mainFuncIndex, baseReg, 0));
}
Emit(Op::iABC(OpCode::Exit, 0, 0, 0)); // 一定要退出,这是虚拟机退出信号,否则ub
CompiledModule *compiledModule = new CompiledModule(fileName, allProtos);
return compiledModule;
}
}; // namespace Fig
-384
View File
@@ -1,384 +0,0 @@
/*!
@file src/Compiler/Compiler.hpp
@brief 编译器定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Ast/Ast.hpp>
#include <Bytecode/Bytecode.hpp>
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Object/Object.hpp>
#include <SourceManager/SourceManager.hpp>
#include <cassert>
#include <iostream>
namespace Fig
{
// 编译产物-函数
struct Proto
{
DynArray<Instruction> code;
DynArray<Value> constants;
std::uint8_t maxStack = 0; // 函数运行所需寄存器数量
};
struct LocalVar
{
int localId; // AST 传来的纯数字 ID
std::uint8_t reg; // 分配到的物理寄存器 ID
int depth; // 物理作用域深度(用于 EndScope 释放寄存器)
};
inline constexpr int MAX_LOCALS = 250;
inline constexpr int MAX_CONSTANTS = UINT16_MAX + 1;
// 任何跨函数、跨模块的编译,都压入弹出这个 State
struct FuncState
{
String name;
FuncState *enclosing = nullptr; // 指向外层状态 (支持闭包)
Proto *proto = nullptr;
std::uint8_t freeReg = 0;
int scopeDepth = 0;
DynArray<LocalVar> locals;
std::uint8_t fastRegMap[UINT8_MAX + 1]; // 256, 索引 = localId, 值 = 寄存器 id
FuncState(String _name, FuncState *enc = nullptr) : name(std::move(_name)), enclosing(enc)
{
proto = new Proto();
std::fill_n(fastRegMap, 256, UINT8_MAX); // 255代表未分配
}
// 注意:这里不 delete proto,因为 proto 是要作为编译产物吐出去的
};
struct CompiledModule
{
String name; // 供调试/打印
DynArray<Proto *> protos; // 扁平化函数原型
CompiledModule(String _name, DynArray<Proto *> _protos) :
name(std::move(_name)), protos(std::move(_protos))
{
}
~CompiledModule()
{
for (auto *p : protos)
{
delete p;
}
}
};
class Compiler
{
private:
String fileName;
SourceManager &manager;
FuncState *current = nullptr; // 永远指向当前正在编译的上下文
int mainFuncIndex = -1;
HashMap<int, int> globalFuncMap; // localid -> ProtoIdx
public:
DynArray<Proto *> allProtos;
struct FuncStateProtector
{
Compiler *compiler;
FuncState *prevState;
[[nodiscard]]
FuncStateProtector(Compiler *comp, FuncState *newState) :
compiler(comp), prevState(comp->current)
{
compiler->current = newState;
}
~FuncStateProtector()
{
compiler->current = prevState;
}
};
Compiler(String _fileName, SourceManager &_manager) :
fileName(std::move(_fileName)), manager(_manager)
{
// 初始化顶级作用域
current = new FuncState("global", nullptr);
allProtos.push_back(current->proto); // 最顶层, bootstrapper
}
~Compiler()
{
// 内存清理 (如果有异常中断)
while (current != nullptr)
{
FuncState *prev = current->enclosing;
delete current;
current = prev;
}
}
Result<CompiledModule *, Error> Compile(Program *program);
private:
void PushState(String _name)
{
current = new FuncState(std::move(_name));
}
Proto *PopState()
{
FuncState *oldState = current;
Proto *finishedProto = oldState->proto;
current = oldState->enclosing;
delete oldState;
return finishedProto;
}
std::uint8_t AllocReg()
{
if (current->freeReg >= 255)
{
assert(false && "Register overflow!");
}
std::uint8_t reg = current->freeReg++;
if (current->freeReg > current->proto->maxStack)
{
current->proto->maxStack = current->freeReg;
}
return reg;
}
void FreeReg(std::uint8_t reg)
{
// 如果这个寄存器被局部变量使用,不释放直接 Return
for (const auto &local : current->locals)
{
if (local.reg == reg)
{
return; // 拒绝释放,保护局部变量生命周期
}
}
// 如果它是纯粹的临时计算结果),释放
if (reg == current->freeReg - 1)
{
current->freeReg--;
}
}
void Emit(Instruction inst)
{
current->proto->code.push_back(inst);
}
std::uint16_t AddConstant(Value v)
{
auto it =
std::find(current->proto->constants.begin(), current->proto->constants.end(), v);
if (it != current->proto->constants.end())
{
return std::distance(current->proto->constants.begin(), it);
}
current->proto->constants.push_back(v);
return static_cast<std::uint16_t>(current->proto->constants.size() - 1);
}
void BeginScope()
{
current->scopeDepth++;
}
void EndScope()
{
current->scopeDepth--;
while (!current->locals.empty() && current->locals.back().depth > current->scopeDepth)
{
FreeReg(current->locals.back().reg);
current->locals.pop_back();
}
}
std::uint8_t DeclareLocal(int localId)
{
std::uint8_t reg = AllocReg();
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
current->fastRegMap[localId] = reg;
return reg;
}
std::uint8_t DeclareLocal(int localId, std::uint8_t reg) // 表示复用哪个寄存器
{
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
current->fastRegMap[localId] = reg;
return reg;
}
// 发射一条跳转指令,并返回它在代码数组里的绝对索引 (Index)
int EmitJump(OpCode op, std::uint8_t aReg = 0)
{
// 预填 0
Emit(Op::iAsBx(op, aReg, 0));
return current->proto->code.size() - 1;
}
// 填真实偏移量到那条指令里
void PatchJump(int instructionIndex)
{
// 目标地址就是当前代码数组的末尾
int target = current->proto->code.size();
// 相对偏移量 = 目标地址 - 指令自身所在的地址 - 1
// (因为 VM 里的 ip 在取指后会自动 +1,所以偏移要减去 1)
int offset = target - instructionIndex - 1;
if (offset < INT16_MIN || offset > INT16_MAX)
{
assert(false && "PatchJump: Jump offset exceeds 16-bit signed limit!");
}
Instruction &inst = current->proto->code[instructionIndex];
inst = (inst & 0x0000FFFF)
| (static_cast<Instruction>(static_cast<std::uint16_t>(offset)) << 16);
}
SourceLocation makeSourceLocation(AstNode *node)
{
SourceLocation location = node->location; // copy
location.functionName = current->name;
location.fileName = fileName;
return location;
}
Result<std::uint8_t, Error> compileIdentiExpr(IdentiExpr *);
Result<std::uint8_t, Error> compileLiteral(LiteralExpr *);
Result<std::uint8_t, Error> compileAssignment(
InfixExpr *); // 编译赋值,由 CompileInfixExpr调用
Result<std::uint8_t, Error> compileInfixExpr(InfixExpr *);
Result<std::uint8_t, Error> compileLeftValue(
Expr *); // 左值对象,可以是变量、结构体字段或模块对象
Result<std::uint8_t, Error> compileCallExpr(CallExpr *);
Result<std::uint8_t, Error> compileExpr(Expr *);
/* Statements */
Result<void, Error> compileVarDecl(VarDecl *);
Result<void, Error> compileBlockStmt(BlockStmt *);
Result<void, Error> compileIfStmt(IfStmt *);
Result<void, Error> compileWhileStmt(WhileStmt *);
Result<void, Error> compileFnDefStmt(FnDefStmt *);
Result<void, Error> compileReturnStmt(ReturnStmt *);
Result<void, Error> compileStmt(Stmt *);
};
inline void DisassembleInstruction(Instruction inst, std::size_t index)
{
// 提取OpCode (低 8 位)
auto op = static_cast<OpCode>(inst & 0xFF);
std::string_view opName = magic_enum::enum_name(op);
// 所有指令至少都有 A 操作数 (8~15 位)
std::uint8_t a = (inst >> 8) & 0xFF;
// 地址补零,指令名左对齐占 10 字符
std::cout << std::format("{:04d} {:<10} ", index, opName);
switch (op)
{
case OpCode::Exit: {
break;
}
case OpCode::Mov: {
// iABx 模式
std::uint16_t bx = (inst >> 16) & 0xFFFF;
std::cout << std::format("R{:<3} R[{}]", a, bx);
break;
}
case OpCode::LoadK: {
// iABx 模式:解析 Bx (16~31 位)
std::uint16_t bx = (inst >> 16) & 0xFFFF;
std::cout << std::format("R{:<3} K[{}]", a, bx);
break;
}
case OpCode::Jmp:
case OpCode::JmpIfFalse: {
// iAsBx
std::int16_t sbx = static_cast<std::uint16_t>(inst >> 16);
std::cout << std::format("R{:<3} [{}]", a, sbx);
break;
}
case OpCode::FastCall:
{
std::uint8_t b = (inst >> 16) & 0xFF;
std::cout << std::format("Proto{:<3} R[{}]+", a, b);
break;
}
case OpCode::Call:
{
std::uint8_t b = (inst >> 16) & 0xFF;
std::cout << std::format("R{:<3} R[{}]+", a, b);
break;
}
case OpCode::LoadTrue:
case OpCode::LoadFalse:
case OpCode::LoadNull:
case OpCode::Add:
case OpCode::Sub:
case OpCode::Mul:
case OpCode::Div:
case OpCode::Mod: {
// iABC 模式:解析 B (16~23 位) 和 C (24~31 位)
std::uint8_t b = (inst >> 16) & 0xFF;
std::uint8_t c = (inst >> 24) & 0xFF;
std::cout << std::format("R{:<3} R{:<3} R{}", a, b, c);
break;
}
case OpCode::Return: {
// iA 模式:只用到了 A
std::cout << std::format("R{}", a);
break;
}
case OpCode::LoadFn: {
std::uint16_t bx = (inst >> 16) & 0xFFFF;
std::cout << std::format("R{:<3} Proto[{}]", a, bx);
break;
}
// default: {
// std::cout << "?";
// break;
// }
}
std::cout << '\n';
}
inline void DumpCode(const DynArray<Instruction> &code)
{
std::cout << " Bytecode\n";
for (std::size_t i = 0; i < code.size(); ++i)
{
DisassembleInstruction(code[i], i);
}
}
}; // namespace Fig
-334
View File
@@ -1,334 +0,0 @@
/*!
@file src/Compiler/ExprCompiler.cpp
@brief 编译器实现(表达式部分)
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#include <Compiler/Compiler.hpp>
namespace Fig
{
Result<std::uint8_t, Error> Compiler::compileIdentiExpr(IdentiExpr *ie)
{
// TODO: 处理全局变量和闭包 Upvalue
std::uint8_t targetReg = current->fastRegMap[ie->localId];
if (targetReg == UINT8_MAX)
{
assert(false && "Compiler Bug: Encountered unmapped localId in fastRegMap!");
}
return targetReg;
}
Result<std::uint8_t, Error> Compiler::compileLiteral(
LiteralExpr *lit) // 编译字面量, 负责转换 token -> Value
{
const Token &token = lit->token;
String lexeme = manager.GetSub(token.index, token.length);
if (!token.isLiteral())
{
assert(false && "CompileLiteral: token is not literal");
}
Value v;
if (token.type == TokenType::LiteralNull)
{
v = Value::GetNullInstance();
}
else if (token.type == TokenType::LiteralTrue)
{
v = Value::GetTrueInstance();
}
else if (token.type == TokenType::LiteralFalse)
{
v = Value::GetFalseInstance();
}
else if (token.type == TokenType::LiteralNumber)
{
// TODO: 更换为无异常手写数字解析版本 (charconv也可)
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
{
// 非整数
double d = std::stod(lexeme.toStdString());
v = Value::FromDouble(d);
}
else
{
std::int32_t i = std::stoi(lexeme.toStdString());
v = Value::FromInt(i);
}
}
else
{
assert("false" && "CompileLiteral: unsupport literal");
}
std::uint8_t targetReg = AllocReg();
if (current->proto->constants.size() >= MAX_CONSTANTS)
{
return std::unexpected(Error(ErrorType::TooManyConstants,
std::format("constant limit exceeded: {}", MAX_CONSTANTS),
"How did you write such code? try global variable or split file",
makeSourceLocation(lit)));
}
std::uint16_t kIndex = AddConstant(v);
Emit(Op::iABx(OpCode::LoadK, targetReg, kIndex));
return targetReg;
}
Result<std::uint8_t, Error> Compiler::compileAssignment(
InfixExpr *infix) // 编译赋值,由 CompileInfixExpr调用
{
// op必须为 =
const auto &_lhsReg = compileLeftValue(infix->left); // 必须为左值对象
if (!_lhsReg)
{
return _lhsReg;
}
std::uint8_t lhsReg = *_lhsReg;
const auto &_rhsReg = compileExpr(infix->right);
std::uint8_t rhsReg = *_rhsReg;
FreeReg(rhsReg);
switch (infix->op)
{
case BinaryOperator::Assign: {
Emit(Op::iABx(OpCode::Mov, lhsReg, rhsReg)); // lhsReg = rhsReg
break;
}
case BinaryOperator::AddAssign: {
Emit(Op::iABC(OpCode::Add, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg + rhsReg
break;
}
case BinaryOperator::SubAssign: {
Emit(Op::iABC(OpCode::Sub, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg - rhsReg
break;
}
case BinaryOperator::MultiplyAssign: {
Emit(Op::iABC(OpCode::Mul, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg * rhsReg
break;
}
case BinaryOperator::DivideAssign: {
Emit(Op::iABC(OpCode::Div, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg / rhsReg
break;
}
case BinaryOperator::ModuloAssign: {
Emit(Op::iABC(OpCode::Mod, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg % rhsReg
break;
}
case BinaryOperator::BitXorAssign: {
Emit(Op::iABC(OpCode::BitXor, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg ^ rhsReg
break;
}
default: {
assert(false && "CompileAssignment: op unsupported yet");
}
}
return lhsReg; // 返回赋值的结果,支持连续赋值
}
Result<std::uint8_t, Error> Compiler::compileInfixExpr(
InfixExpr *infix) // 编译中缀表达式,返回一个存放结果的寄存器 ID
{
if (infix->op >= BinaryOperator::Assign && infix->op <= BinaryOperator::BitXorAssign)
{
return compileAssignment(infix);
}
const auto &_lhsReg = compileExpr(infix->left);
if (!_lhsReg)
{
return _lhsReg;
}
std::uint8_t lhsReg = *_lhsReg;
const auto &_rhsReg = compileExpr(infix->right);
if (!_rhsReg)
{
return _rhsReg;
}
std::uint8_t rhsReg = *_rhsReg;
FreeReg(rhsReg);
FreeReg(lhsReg);
std::uint8_t resultReg = AllocReg();
switch (infix->op)
{
case BinaryOperator::Add: {
Emit(Op::iABC(OpCode::Add, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Subtract: {
Emit(Op::iABC(OpCode::Sub, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Multiply: {
Emit(Op::iABC(OpCode::Mul, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Divide: {
Emit(Op::iABC(OpCode::Div, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Modulo: {
Emit(Op::iABC(OpCode::Mod, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Greater: {
Emit(Op::iABC(OpCode::Greater, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::GreaterEqual: {
Emit(Op::iABC(OpCode::GreaterEqual, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Less: {
Emit(Op::iABC(OpCode::Less, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::LessEqual: {
Emit(Op::iABC(OpCode::LessEqual, resultReg, lhsReg, rhsReg));
break;
}
case BinaryOperator::Equal: {
Emit(Op::iABC(OpCode::Equal, resultReg, lhsReg, rhsReg));
break;
}
default: assert(false && "CompileInfixExpr: op unsupported yet");
}
return resultReg;
}
Result<std::uint8_t, Error> Compiler::compileLeftValue(
Expr *expr) // 左值对象,可以是变量、结构体字段或模块对象
{
switch (expr->type)
{
case AstType::IdentiExpr:
return compileIdentiExpr(static_cast<IdentiExpr *>(expr));
// TODO: 数组切片(a[0])或对象属性(a.b)
default:
// Analyzer 有漏洞(编译器内部
// 直接崩溃
assert(false && "Compiler Bug: Invalid L-value bypassed Analyzer!");
return 0;
}
}
Result<std::uint8_t, Error> Compiler::compileCallExpr(CallExpr *expr)
{
bool isStatic = false; // 是否为单纯的 fn(...) 静态函数调用
int protoIdx = -1;
if (expr->callee->type == AstType::IdentiExpr)
{
IdentiExpr *id = static_cast<IdentiExpr *>(expr->callee);
// 如果是函数名且深度为 0 (全局/扁平函数池)
if (id->resolvedType->tag == TypeTag::Function && id->resolvedDepth == 0)
{
if (globalFuncMap.contains(id->localId))
{
isStatic = true;
protoIdx = globalFuncMap[id->localId];
}
}
}
std::uint8_t baseReg = AllocReg();
if (!isStatic)
{
auto calleeRes = compileExpr(expr->callee);
if (!calleeRes)
{
return calleeRes;
}
if (*calleeRes != baseReg)
{
Emit(Op::iABx(OpCode::Mov, baseReg, *calleeRes));
}
}
for (size_t i = 0; i < expr->args.size(); ++i)
{
std::uint8_t argTarget = AllocReg();
auto argRes = compileExpr(expr->args.args[i]);
if (!argRes)
{
return argRes;
}
if (*argRes != argTarget)
{
Emit(Op::iABx(OpCode::Mov, argTarget, *argRes));
}
}
std::uint8_t expectRet = 1;
if (isStatic)
{
Emit(Op::iABC(OpCode::FastCall, (std::uint8_t) protoIdx, baseReg, expectRet));
}
else
{
Emit(Op::iABC(OpCode::Call, baseReg, baseReg, expectRet));
}
for (size_t i = 0; i < expr->args.args.size(); ++i)
{
current->freeReg--;
}
return baseReg; // 返回值起点
}
Result<std::uint8_t, Error> Compiler::compileExpr(
Expr *expr) // 编译表达式,必定返回一个存放结果的寄存器 ID
{
switch (expr->type)
{
case AstType::Stmt:
case AstType::Expr:
case AstType::AstNode: assert(false && "CompileExpr: bad node type"); break;
case AstType::IdentiExpr: {
return compileLeftValue(expr); // 左值直接转换成右值
}
case AstType::LiteralExpr: {
LiteralExpr *lit = static_cast<LiteralExpr *>(expr);
auto result = compileLiteral(lit);
if (!result)
{
return std::unexpected(result.error());
}
std::uint8_t targetReg = *result;
return targetReg;
}
case AstType::InfixExpr: {
return compileInfixExpr(static_cast<InfixExpr *>(expr));
}
case AstType::CallExpr: {
return compileCallExpr(static_cast<CallExpr *>(expr));
}
}
}
} // namespace Fig
-282
View File
@@ -1,282 +0,0 @@
/*!
@file src/Compiler/StmtCompiler.cpp
@brief 编译器实现(语句部分)
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#include <Compiler/Compiler.hpp>
namespace Fig
{
Result<void, Error> Compiler::compileVarDecl(VarDecl *varDecl)
{
if (current->freeReg > MAX_LOCALS)
{
return std::unexpected(Error(ErrorType::TooManyLocals,
std::format("local limit exceeded: {}", MAX_LOCALS),
"try split function or use arrays/structs...",
makeSourceLocation(varDecl)));
}
std::uint8_t varReg;
if (varDecl->initExpr)
{
auto result = compileExpr(varDecl->initExpr);
if (!result)
{
return std::unexpected(result.error());
}
std::uint8_t resultReg = *result;
varReg = DeclareLocal(varDecl->localId, resultReg); // 复用临时计算结果寄存器
}
else
{
// TODO: 未初始化提供初始值
varReg = DeclareLocal(varDecl->localId);
}
return Result<void, Error>();
}
Result<void, Error> Compiler::compileBlockStmt(BlockStmt *blockStmt)
{
for (Stmt *stmt : blockStmt->nodes)
{
auto result = compileStmt(stmt);
if (!result)
{
return result;
}
}
return {};
}
Result<void, Error> Compiler::compileIfStmt(IfStmt *stmt)
{
/*
if cond1
{
}
else if cond2 #1
{
}
else if cond3 #2
{
}
else #3
{
}
quit #4
Bytecode:
JmpIfFalse cond1 #1
; consequent内容
; ...
; if条件为true, 跳过所有 else/elseif
Jmp #4
; #1
JmpIfFalse cond2 #2
; consequent
Jmp #4
; #2
JmpIfFalse cond3 #3
; consequent
Jmp #4
; #3
; 没有一次执行分支
; else部分
; ...
#4
*/
std::vector<int> exitJumps; // 所有分支都要跳到最后,收集所有jump最后回填
const auto &condResult = compileExpr(stmt->cond);
if (!condResult)
{
return std::unexpected(condResult.error());
}
std::uint8_t condReg = *condResult;
int jumpToNext = EmitJump(OpCode::JmpIfFalse, condReg);
FreeReg(condReg);
const auto &blockResult = compileStmt(stmt->consequent);
if (!blockResult)
{
return blockResult;
}
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完if直接跳到出口
PatchJump(jumpToNext); // 回填,跳到下一个else/elseif
for (auto *elif : stmt->elifs)
{
const auto &elifCondResult = compileExpr(elif->cond);
if (!elifCondResult)
return std::unexpected(elifCondResult.error());
std::uint8_t elifCondReg = *elifCondResult;
jumpToNext = EmitJump(OpCode::JmpIfFalse, elifCondReg);
FreeReg(elifCondReg);
const auto &blockResult = compileStmt(elif->consequent);
if (!blockResult)
{
return blockResult;
}
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完else if,跳到出口
PatchJump(jumpToNext); // 跳到下一个分支
}
if (stmt->alternate)
{
auto result = compileStmt(stmt->alternate);
if (!result)
{
return result;
}
}
for (int exitIndex : exitJumps)
{
PatchJump(exitIndex); // 回填所有跳转出口的指令
}
return {};
}
Result<void, Error> Compiler::compileWhileStmt(WhileStmt *stmt)
{
int beginIns = current->proto->code.size() - 1;
auto condRegResult = compileExpr(stmt->cond);
if (!condRegResult)
{
return std::unexpected(condRegResult.error());
}
std::uint8_t condReg = *condRegResult;
int exitJump = EmitJump(OpCode::JmpIfFalse, condReg);
auto bodyResult = compileBlockStmt(stmt->body);
if (!bodyResult)
{
return bodyResult;
}
Emit(Op::iAsBx(
OpCode::Jmp, 0, beginIns - current->proto->code.size())); // 回到开头对condition求值
PatchJump(exitJump);
return {};
}
Result<void, Error> Compiler::compileFnDefStmt(FnDefStmt *stmt)
{
std::uint8_t funcReg = DeclareLocal(stmt->localId);
// 创建子函数编译状态
// 传入 current 作为 enclosing,用于后续支持闭包 Upvalue 查找
FuncState childState(stmt->name, current);
allProtos.push_back(childState.proto);
std::uint16_t protoIdx = static_cast<std::uint16_t>(allProtos.size() - 1);
globalFuncMap[stmt->localId] = protoIdx; // 把函数的local id映射到protoIdx
{
FuncStateProtector stateGuard(this, &childState);
AllocReg();
// 将参数映射为子函数的初始局部变量 (R0, R1...)
for (Param *p : stmt->params)
{
PosParam *posParam = static_cast<PosParam *>(p); // TODO: 其他参数支持...
// 按顺序分配寄存器
DeclareLocal(posParam->localId);
}
// B编译函数体语句
auto bodyRes = compileStmt(stmt->body);
if (!bodyRes)
return bodyRes;
// 隐式返回 null
std::uint8_t resReg = AllocReg();
Emit(Op::iABC(OpCode::LoadNull, resReg, 0, 0));
Emit(Op::iABC(OpCode::Return, resReg, 0, 0));
}
// 5. 检查是否是 main 函数
if (stmt->name == U"main")
{
this->mainFuncIndex = protoIdx;
}
Emit(Op::iABx(OpCode::LoadFn, funcReg, protoIdx));
return {};
}
Result<void, Error> Compiler::compileReturnStmt(ReturnStmt *stmt)
{
auto res = compileExpr(stmt->value);
if (!res)
{
return std::unexpected(res.error());
}
Emit(Op::iABC(OpCode::Return, *res, 0, 0));
return {};
}
Result<void, Error> Compiler::compileStmt(Stmt *stmt) // 编译语句
{
switch (stmt->type)
{
case AstType::ExprStmt: {
ExprStmt *exprStmt = static_cast<ExprStmt *>(stmt);
Expr *expr = exprStmt->expr;
auto result = compileExpr(expr);
if (!result)
{
return std::unexpected(result.error());
}
FreeReg(*result);
break;
}
case AstType::VarDecl: {
return compileVarDecl(static_cast<VarDecl *>(stmt));
}
case AstType::BlockStmt: {
return compileBlockStmt(static_cast<BlockStmt *>(stmt));
}
case AstType::IfStmt: {
return compileIfStmt(static_cast<IfStmt *>(stmt));
}
case AstType::WhileStmt: {
return compileWhileStmt(static_cast<WhileStmt *>(stmt));
}
case AstType::FnDefStmt: {
return compileFnDefStmt(static_cast<FnDefStmt *>(stmt));
}
case AstType::ReturnStmt: {
return compileReturnStmt(static_cast<ReturnStmt *>(stmt));
}
}
return Result<void, Error>();
}
}; // namespace Fig
-13
View File
@@ -1,13 +0,0 @@
/*!
@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>
-47
View File
@@ -1,47 +0,0 @@
/*!
@file src/Core/CoreIO.cpp
@brief 标准输入输出链接
@author PuqiAR (im@puqiar.top)
@date 2026-02-14
*/
#include <Core/CoreIO.hpp>
#include <Core/CoreInfos.hpp>
#ifdef _WIN32
#include <windows.h>
#endif
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
void InitConsoleIO()
{
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
}
};
-20
View File
@@ -1,20 +0,0 @@
/*!
@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();
void InitConsoleIO();
}; // namespace Fig::CoreIO
-67
View File
@@ -1,67 +0,0 @@
/*!
@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
View File
@@ -1,25 +0,0 @@
/*!
@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
View File
@@ -1,17 +0,0 @@
/*!
@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
View File
@@ -1,59 +0,0 @@
/*!
@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
View File
@@ -1,34 +0,0 @@
/*!
@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
View File
@@ -1,14 +0,0 @@
/*!
@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
View File
@@ -1,20 +0,0 @@
/*!
@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
View File
@@ -1,130 +0,0 @@
/*!
@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'_'; }
};
};
File diff suppressed because it is too large Load Diff
-144
View File
@@ -1,144 +0,0 @@
/*!
@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";
}
-173
View File
@@ -1,173 +0,0 @@
/*!
@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";
case RedeclarationError: return "RedeclarationError";
case UseUndeclaredIdentifier: return "UseUndeclaredIdentifier";
case NotAnLvalue: return "NotAnLvalue";
case TypeError: return "TypeError";
case TooManyLocals: return "TooManyLocals";
case TooManyConstants: return "TooManyConstants";
// 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;
err << '\n';
}
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
-177
View File
@@ -1,177 +0,0 @@
/*!
@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,
// analyzer errors
RedeclarationError,
UseUndeclaredIdentifier,
NotAnLvalue,
TypeError,
// compile errors
TooManyLocals,
TooManyConstants,
};
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
-19
View File
@@ -1,19 +0,0 @@
#include <LSP/LSPServer.hpp>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
int main()
{
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY);
#endif
Fig::LspServer server;
server.Run();
return 0;
}
-167
View File
@@ -1,167 +0,0 @@
#pragma once
#include <Ast/Ast.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <Sema/Analyzer.hpp>
#include <Error/Error.hpp>
#include <SourceManager/SourceManager.hpp>
#include <charconv> // C++17/20 字符串转数字
#include <iostream>
#include <string>
#include <Utils/json/json.hpp>
using json = nlohmann::json;
namespace Fig
{
class LspServer
{
public:
void Run()
{
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
while (true)
{
std::string line;
if (!std::getline(std::cin, line))
break; // 退出循环
// 解析 HTTP 风格的 Header: "Content-Length: 123\r"
if (line.starts_with("Content-Length: "))
{
std::size_t length = 0;
// 使用 C++ 极速的 from_chars 替代 stoi
auto res = std::from_chars(line.data() + 16, line.data() + line.size(), length);
if (res.ec != std::errc())
continue;
// 消费 Header 和 Body 之间那行空的 "\r\n"
std::string emptyLine;
std::getline(std::cin, emptyLine);
// 读取对应字节的 JSON Body
std::string jsonBody(length, '\0');
std::cin.read(jsonBody.data(), length);
// 派发给路由
HandleMessage(jsonBody);
}
}
}
private:
void HandleMessage(const std::string &rawJson)
{
json req = json::parse(rawJson, nullptr, false);
if (req.is_discarded())
return; // 忽略解析失败的报文
std::string method = req.value("method", "");
if (method == "initialize")
{
Respond(req["id"], R"({
"capabilities": {
"textDocumentSync": 1,
"hoverProvider": true
}
})");
}
else if (method == "textDocument/didOpen")
{
std::string uri = req["params"]["textDocument"]["uri"];
std::string text = req["params"]["textDocument"]["text"];
PublishDiagnostics(uri, text);
}
else if (method == "textDocument/didChange")
{
std::string uri = req["params"]["textDocument"]["uri"];
std::string text = req["params"]["contentChanges"][0]["text"];
PublishDiagnostics(uri, text);
}
}
void Respond(int id, const std::string &resultJsonString)
{
std::string response = "{\"jsonrpc\":\"2.0\",\"id\":" + std::to_string(id)
+ ",\"result\":" + resultJsonString + "}";
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
<< response << std::flush;
}
void SendDiagnostics(const std::string &uri, const Error *err = nullptr)
{
json diagnostics = json::array(); // 默认空数组,代码无错时擦除红线
if (err)
{
// LSP 规定行列号必须从 0 开始算
int startLine = err->location.sp.line - 1;
int startChar = err->location.sp.column - 1;
int endLine = startLine;
int endChar = startChar + err->location.sp.tok_length;
std::string fullMessage = err->message.toStdString();
if (!err->suggestion.empty())
{
fullMessage += " 💡suggestion: " + err->suggestion.toStdString();
}
diagnostics.push_back(
{{"range",
{{"start", {{"line", startLine}, {"character", startChar}}},
{"end", {{"line", endLine}, {"character", endChar}}}}},
{"severity", 1}, // 1 = 致命错误红线
{"source", "Fig LSP Server"},
{"message", fullMessage}});
}
// 组装 Notification
json notification = {{"jsonrpc", "2.0"},
{"method", "textDocument/publishDiagnostics"},
{"params", {{"uri", uri}, {"diagnostics", diagnostics}}}};
std::string response = notification.dump();
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
<< response << std::flush;
}
void PublishDiagnostics(const std::string &uri, const std::string &sourceCode)
{
SourceManager manager;
manager.LoadFromMemory(sourceCode);
Lexer lexer(sourceCode, "");
Parser parser(lexer, manager, "");
// 1. 语法检查拦截
auto parserResult = parser.Parse();
if (!parserResult)
{
SendDiagnostics(uri, &parserResult.error());
return;
}
Program *program = *parserResult;
Analyzer analyzer(manager);
// 语义检查拦截
auto analyzerResult = analyzer.Analyze(program);
if (!analyzerResult)
{
SendDiagnostics(uri, &analyzerResult.error());
return;
}
// 3. 一切完美,发射空数组清空过去的错误红线
SendDiagnostics(uri, nullptr);
}
};
} // namespace Fig
-328
View File
@@ -1,328 +0,0 @@
/*!
@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 (!rd.isAtEnd() && 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());
// 下划线表示法(1_000_000)
while (!rd.isAtEnd() && state == State::ScanDec && (rd.current() == U'_' || CharUtils::isDigit(rd.current())))
{
tok.length++;
rd.next();
}
// 小数点
if (rd.currentIf() == U'.')
{
tok.length++;
rd.next();
if (!CharUtils::isDigit(rd.currentIf()))
{
return std::unexpected(Error(ErrorType::InvalidNumberLiteral,
"need matissa",
"insert matissa",
makeSourceLocation(rd.currentPosition())));
}
while (!rd.isAtEnd() && CharUtils::isDigit(rd.current()))
{
tok.length++;
rd.next();
}
}
// 科学计数法
if (rd.currentIf() == U'e')
{
tok.length++;
char32_t peek = rd.peekIf();
if (peek == U'+' || peek == U'-') // ae+b, ae-b
{
tok.length++;
rd.skip(2); // consume `e`, +/-
}
else if (CharUtils::isDigit(peek)) // aeb 情况
{
rd.next(); // `e`
}
if (!CharUtils::isDigit(rd.currentIf()))
{
return std::unexpected(Error(ErrorType::InvalidNumberLiteral,
"need exponent for scientific notation",
"insert exponent",
makeSourceLocation(rd.currentPosition())));
}
while (!rd.isAtEnd() && 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();
Token tok(rd.currentIndex(), 1, TokenType::LiteralString); // "
rd.next(); // skip " / '
while (true)
{
if (state == State::ScanStringDQ && rd.current() == U'"')
{
tok.length++;
rd.next(); // skip '"'
break;
}
else if (state == State::ScanStringSQ && rd.current() == U'\'')
{
tok.length++;
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 (!CharUtils::isAsciiSpace(current)) // 检查 EOF
break;
rd.next();
}
}
Result<Token, Error> Lexer::NextToken()
{
if (rd.isAtEnd())
{
return Token(rd.getEOFIndex(), 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
-189
View File
@@ -1,189 +0,0 @@
/*!
@file src/Lexer/Lexer.hpp
@brief 词法分析器(materialized lexeme)
@author PuqiAR (im@puqiar.top)
@date 2026-02-13
*/
#pragma once
#include <Core/SourceLocations.hpp>
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Token/Token.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();
}
inline size_t getEOFIndex() const
{
return 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
-44
View File
@@ -1,44 +0,0 @@
#include <Error/Error.hpp>
#include <Lexer/Lexer.hpp>
#include <Token/Token.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)
{
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 << " at " << token.index << '\n';
break;
}
std::cout << lexeme << " --> " << type << '\n';
}
}
-25
View File
@@ -1,25 +0,0 @@
/*!
@file src/Object/FunctionObject.hpp
@brief 函数对象定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-28
*/
#pragma once
#include <Object/ObjectBase.hpp>
namespace Fig
{
// 运行时闭包对象 (24字节 Base + 8字节 Proto指针 = 32 bytes)
struct Proto;
struct FunctionObject final : public Object
{
Proto *proto; // 指向编译器生成的只读字节码与常量池
// TODO: 实现闭包时 加一个 Upvalue 指针数组
// Value* upvalues;
};
} // namespace Fig
-39
View File
@@ -1,39 +0,0 @@
/*!
@file src/Object/Object.hpp
@brief 值表示实现 (NaN Boxing) 和 堆对象函数的实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#include <Object/ObjectBase.hpp>
namespace Fig
{
String Value::ToString() const
{
if (IsNull())
{
return "null";
}
else if (IsInt())
{
return std::to_string(AsInt());
}
else if (IsDouble())
{
return std::format("{}", AsDouble());
}
else if (IsBool())
{
return (AsBool() ? "true" : "false");
}
else if (IsObject())
{
return "Object"; // TODO: 分派
}
else
{
return "Unknow";
}
}
}; // namespace Fig
-13
View File
@@ -1,13 +0,0 @@
/*!
@file src/Object/Object.hpp
@brief 值系统总文件
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Object/ObjectBase.hpp>
#include <Object/StringObject.hpp>
#include <Object/StructObject.hpp>
#include <Object/FunctionObject.hpp>
-231
View File
@@ -1,231 +0,0 @@
/*!
@file src/Object/ObjectBase.hpp
@brief 值表示定义 (NaN Boxing) uint64
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <bit>
#include <cstdint>
#include <Deps/Deps.hpp>
namespace Fig
{
struct Object; // 前置声明
/*
正常来说直接 Value = std::uint64_t会更快
但是这样会带来隐式转换的问题
因此我们封装成一个类,这样速度不会损失很多。
(release模式编译器会直接优化, 速度和uint64_t直接表示一样快)
*/
class Value
{
private:
std::uint64_t v_; // 唯一的物理成员 sizeof(Value) 永远是 8 字节。
// --- 私有掩码常量 ---
static constexpr std::uint64_t QNAN_MASK = 0x7ffc000000000000;
static constexpr std::uint64_t SIGN_BIT = 0x8000000000000000;
// 专门给 Int32 预留的高位 Tag
static constexpr std::uint32_t INT_TAG_HIGH = 0x7FFD0000;
// 基础原语 Tag
static constexpr std::uint64_t TAG_NULL = 1;
static constexpr std::uint64_t TAG_FALSE = 2;
static constexpr std::uint64_t TAG_TRUE = 3;
// 私有底层构造:仅供内部组装使用
constexpr explicit Value(uint64_t raw) : v_(raw) {}
public:
// 默认构造为 Null,保证未初始化变量也是安全的
constexpr Value()
{
*this = GetNullInstance();
}
[[nodiscard]] static constexpr Value FromDouble(double d)
{
uint64_t raw = std::bit_cast<uint64_t>(d);
// 清洗非法的 NaN
if ((raw & QNAN_MASK) == QNAN_MASK)
return Value(QNAN_MASK);
return Value(raw);
}
[[nodiscard]] static constexpr Value FromInt(std::int32_t i)
{
// 移位构造,彻底阻断符号扩展漏洞
return Value((static_cast<uint64_t>(INT_TAG_HIGH) << 32) | static_cast<uint32_t>(i));
}
[[nodiscard]] static constexpr Value &GetTrueInstance()
{
static Value trueInstance(QNAN_MASK | TAG_TRUE);
return trueInstance;
}
[[nodiscard]] static constexpr Value &GetFalseInstance()
{
static Value falseInstance(QNAN_MASK | TAG_FALSE);
return falseInstance;
}
[[nodiscard]] static constexpr Value &FromBool(bool b)
{
return (b ? GetTrueInstance() : GetFalseInstance());
}
[[nodiscard]] static constexpr Value &GetNullInstance()
{
static Value nullInstance(QNAN_MASK | TAG_NULL);
return nullInstance;
}
[[nodiscard]] static Value FromObject(Object *ptr)
{
return Value(reinterpret_cast<uint64_t>(ptr) | SIGN_BIT | QNAN_MASK);
}
// 类型检查 (Is)
[[nodiscard]] constexpr bool IsDouble() const
{
return (v_ & QNAN_MASK) != QNAN_MASK;
}
[[nodiscard]] constexpr bool IsInt() const
{
// 安全的高 32 位移位判定
return static_cast<uint32_t>(v_ >> 32) == INT_TAG_HIGH;
}
[[nodiscard]] constexpr bool IsNumber() const
{
return IsDouble() || IsInt();
}
[[nodiscard]] constexpr bool IsNull() const
{
return v_ == (QNAN_MASK | TAG_NULL);
}
[[nodiscard]] constexpr bool IsBool() const
{
return (v_ | 1) == (QNAN_MASK | TAG_TRUE);
}
[[nodiscard]] constexpr bool IsObject() const
{
return (v_ & (SIGN_BIT | QNAN_MASK)) == (SIGN_BIT | QNAN_MASK);
}
// 提取数据 (Unbox / As)
[[nodiscard]] constexpr double AsDouble() const
{
return std::bit_cast<double>(v_);
}
[[nodiscard]] constexpr int32_t AsInt() const
{
return static_cast<int32_t>(v_);
}
// 核心辅助:泛型数字提取。算术指令可以直接用这个,免去手写 if 分支
// 若不是 int/double 会导致非常恐怖的问题
[[nodiscard]] constexpr double CastToDouble() const
{
return IsInt() ? static_cast<double>(AsInt()) : AsDouble();
}
[[nodiscard]] constexpr bool AsBool() const
{
return v_ == (QNAN_MASK | TAG_TRUE);
}
[[nodiscard]] struct Object *AsObject() const
{
return reinterpret_cast<struct Object *>(v_ & ~(SIGN_BIT | QNAN_MASK));
}
// 重载
// 暴露原生值用于硬核位运算或 Hash 计算
[[nodiscard]] constexpr uint64_t Raw() const
{
return v_;
}
// 让 VM 的 OP_EQ 指令极简:`if (RA == RB)`
[[nodiscard]] constexpr bool operator==(const Value &other) const
{
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 double进行C++比对
if (IsDouble() && other.IsDouble())
{
return AsDouble() == other.AsDouble();
}
// 直接比较 64 位整数内存,所以堆对象比较为地址(运算符重载由Compiler处理)
return v_ == other.v_;
}
[[nodiscard]] constexpr bool operator!=(const Value &other) const
{
return !(*this == other);
}
// 类函数
[[nodiscard]]
String ToString() const;
};
/*
C风格继承 + 手动分发
禁止任何 virtual 达到最高效率
*/
enum class ObjectType : uint8_t
{
String,
Function,
Struct,
Instance,
};
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
// Total 24 bytes size
struct Object
{
Object *next; // 8 bytes: gc链表
StructObject *klass; // 8 bytes: 一切皆对象,父类指针
ObjectType type; // 1 byte : 类型
bool isMarked = false; // 1 byte : gc标记
// + 6 bytes padding
constexpr bool isString() const
{
return type == ObjectType::String;
}
constexpr bool isFunction() const
{
return type == ObjectType::Function;
}
constexpr bool isStruct() const
{
return type == ObjectType::Struct;
}
constexpr bool isInstance() const
{
return type == ObjectType::Instance;
}
};
} // namespace Fig
-20
View File
@@ -1,20 +0,0 @@
#include <Object/ObjectBase.hpp>
#include <iomanip>
#include <iostream>
#include <cmath>
#include <numbers>
int main()
{
using namespace Fig;
Value null;
Value d = Value::FromDouble(-std::numbers::pi);
Value i = Value::FromInt(-2143242);
Value b = Value::FromBool(false);
std::cout << null.ToString() << '\n';
std::cout << d.ToString() << '\n';
std::cout << i.ToString() << '\n';
std::cout << b.ToString() << '\n';
}
-29
View File
@@ -1,29 +0,0 @@
/*!
@file src/Object/StringObject.hpp
@brief 字符串对象标识
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Object/ObjectBase.hpp>
namespace Fig
{
/*
// Total 24 bytes size
struct Object
{
Object *next; // 8 bytes: gc链表
Struct *klass; // 8 bytes: 一切皆对象,父类指针
ObjectType type; // 1 byte : 类型
bool isMarked = false; // 1 byte : gc标记
// + 6 bytes padding
};
*/
struct StringObject final : public Object
{
String data; // 40 bytes
};
};
-54
View File
@@ -1,54 +0,0 @@
/*!
@file src/Object/StructObject.hpp
@brief 结构体类型 StructObject 定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Ast/Operator.hpp>
#include <Object/ObjectBase.hpp>
namespace Fig
{
/*
// Total 24 bytes size
struct Object
{
Object *next; // 8 bytes: gc链表
Struct *klass; // 8 bytes: 一切皆对象,父类指针
ObjectType type; // 1 byte : 类型
bool isMarked = false; // 1 byte : gc标记
// + 6 bytes padding
};
*/
struct StructObject final : public Object
{
String name; // 元信息(仅供调试/打印/反射)
// 内存布局信息
std::uint8_t fieldCount;
Object *operators[GetOperatorsSize()];
/*
运算符重载,nullptr代表无重载
一般为 NativeFunction / Function
排列:
[unary operators ]( binary operators]
0 - UnaryOperators::Count BinaryOperators::Count
*/
Object *GetUnaryOperator(UnaryOperator _op)
{
std::uint8_t idx = static_cast<std::uint8_t>(_op);
return operators[idx];
}
Object *GetBinaryOperator(BinaryOperator _op)
{
std::uint16_t idx = static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(_op);
return operators[idx];
}
};
}; // namespace Fig
-265
View File
@@ -1,265 +0,0 @@
/*!
@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时调用
{
StateProtector p(this, {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调用
{
StateProtector p(this, {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
{
StateProtector p(this, {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
{
StateProtector p(this, {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;
}
Result<IndexExpr *, Error> Parser::parseIndexExpr(
Expr *base) // 由 parseExpression调用, 当前token为 `[`
{
StateProtector p(this, {State::ParsingIndexExpr});
const Token &lbracket_token = consumeToken(); // consume `[`
const auto &index_result = parseExpression();
if (!index_result)
{
return std::unexpected(index_result.error());
}
if (currentToken().type != TokenType::RightBracket) // `]`
{
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed brackets",
"insert `]`",
makeSourceLocation(lbracket_token)));
}
consumeToken(); // consume `]`
IndexExpr *indexExpr = new IndexExpr(base, *index_result);
return indexExpr;
}
Result<CallExpr *, Error> Parser::parseCallExpr(
Expr *callee) // 由 parseExpression调用, 当前token为 `(`
{
StateProtector p(this, {State::ParsingCallExpr});
const Token &lparen_token = consumeToken(); // consume `(`
FnCallArgs callArgs;
// 空参数列表
if (currentToken().type == TokenType::RightParen)
{
consumeToken(); // consume `)`
return new CallExpr(callee, callArgs);
}
while (true)
{
if (currentToken().type == TokenType::EndOfFile)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"fn call has unclosed parenthese",
"insert `)`",
makeSourceLocation(lparen_token)));
}
const auto &arg_result = parseExpression();
if (!arg_result)
return std::unexpected(arg_result.error());
callArgs.args.push_back(*arg_result);
if (currentToken().type == TokenType::RightParen)
{
consumeToken(); // consume `)`
break;
}
if (currentToken().type != TokenType::Comma)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"expected `,` or `)` in argument list",
"insert `,`",
makeSourceLocation(currentToken())));
}
consumeToken(); // consume `,`
}
return new CallExpr(callee, callArgs);
}
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;
}
else if (token.type == TokenType::LeftParen)
{
const Token &lparen_token = consumeToken(); // consume `(`
const auto &expr_result = parseExpression(0);
if (!expr_result)
{
return expr_result;
}
const Token &rparen_token = consumeToken(); // consume `)`
if (rparen_token.type != TokenType::RightParen)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed parenthese",
"insert `)`",
makeSourceLocation(lparen_token)));
}
lhs = *expr_result;
}
if (!lhs)
{
return std::unexpected(Error(ErrorType::ExpectedExpression,
"expected expression",
"insert expressions",
makeSourceLocation(prevToken())));
}
while (true)
{
token = currentToken();
if (shouldTerminate())
{
break;
}
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
{
BinaryOperator op = TokenToBinaryOp(token);
BindingPower lbp = GetBinaryOpLBp(op);
if (rbp >= lbp)
{
// 前操作数的右绑定力比当前操作数的左绑定力大
// lhs被吸走
break;
}
auto result = parseInfixExpr(lhs);
if (!result)
{
resetTermintors();
return result;
}
lhs = *result;
}
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
// 而不用走正常路径
else if (token.type == TokenType::LeftBracket) // `[`
{
const auto &expr_result = parseIndexExpr(lhs);
if (!expr_result)
{
resetTermintors();
return expr_result;
}
lhs = *expr_result;
}
else if (token.type == TokenType::LeftParen) // `(`
{
const auto &expr_result = parseCallExpr(lhs);
if (!expr_result)
{
resetTermintors();
return expr_result;
}
lhs = *expr_result;
}
else
{
return std::unexpected(Error(ErrorType::ExpectedExpression,
"expression unexpectedly ended",
"insert expressions",
makeSourceLocation(token)));
}
}
return lhs;
}
}; // namespace Fig
-31
View File
@@ -1,31 +0,0 @@
/*!
@file src/Parser/Parser.cpp
@brief 语法分析器(Pratt + 手动递归下降) 实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-14
*/
#include <Parser/Parser.hpp>
namespace Fig
{
Result<Program *, Error> Parser::Parse()
{
Program *program = new Program;
while (!isEOF)
{
auto result = parseStatement();
if (!result)
{
return std::unexpected(result.error());
}
Stmt *stmt = *result;
if (!stmt)
{
continue;
}
program->nodes.push_back(stmt);
}
return program;
}
}; // namespace Fig
-321
View File
@@ -1,321 +0,0 @@
/*!
@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];
}
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)
{
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;
}
inline bool match(TokenType type)
{
if (currentToken().type == type)
{
consumeToken();
return true;
}
return false;
}
inline Error makeUnexpectTokenError(const String &stmtType,
const String &expect,
const Token &tokenGot,
std::source_location loc = std::source_location::current())
{
return Error(ErrorType::SyntaxError,
std::format("expect '{}' in {}, got `{}`",
expect,
stmtType,
magic_enum::enum_name(tokenGot.type)),
"none",
makeSourceLocation(tokenGot),
loc);
}
inline Error makeExpectSemicolonError(
std::source_location loc = std::source_location::current())
{
return Error(ErrorType::SyntaxError,
"expect ';' after statement",
"insert ';'",
makeSourceLocation(currentToken()),
loc);
}
public:
struct State
{
enum StateType : std::uint8_t
{
Standby,
ParsingLiteralExpr,
ParsingIdentiExpr,
ParsingInfixExpr,
ParsingPrefixExpr,
ParsingIndexExpr,
ParsingCallExpr,
ParsingVarDecl,
ParsingIf,
ParsingWhile,
ParsingFnDefStmt,
ParsingReturn,
ParsingBreak,
ParsingContinue,
ParsingNamedTypeExpr,
} type = StateType::Standby;
std::unordered_set<TokenType> stopAt = {};
};
private:
const std::unordered_set<TokenType> &getBaseTerminators()
{
static const std::unordered_set<TokenType> baseTerminators = {TokenType::Semicolon,
TokenType::RightParen,
TokenType::RightBracket,
TokenType::RightBrace,
TokenType::Comma,
TokenType::EndOfFile};
return baseTerminators;
}
std::unordered_set<TokenType> &getTerminators() // 返回固定的终止符
{
/*
Syntax terminators:
; ) ] } , EOF
*/
static std::unordered_set<TokenType> terminators(getBaseTerminators());
return terminators;
}
void resetTermintors()
{
getTerminators() = getBaseTerminators();
}
bool shouldTerminate() // 判断是否终结
{
const Token &token = currentToken();
const auto &terminators = getTerminators();
if (terminators.contains(token.type))
{
return true;
}
for (auto it = stateStack.rbegin(); it < stateStack.rend(); ++it)
{
if (it->stopAt.contains(token.type))
{
return true;
}
}
return false;
}
DynArray<State> stateStack;
State &currentState()
{
return stateStack.back();
}
void pushState(State _state)
{
stateStack.push_back(std::move(_state));
}
void popState()
{
if (!stateStack.empty())
{
stateStack.pop_back();
}
}
class StateProtector
{
Parser *parser;
public:
StateProtector(Parser *p, const State &newState) : parser(p)
{
parser->pushState(newState);
}
~StateProtector()
{
parser->popState();
}
// 禁止拷贝
StateProtector(const StateProtector &) = delete;
StateProtector &operator=(const StateProtector &) = delete;
};
public:
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
lexer(_lexer), srcManager(_srcManager), fileName(std::move(_fileName))
{
pushState(State());
}
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(currentState().type).data());
}
/* TypeExpressions */
Result<NamedTypeExpr *, Error> parseNamedTypeExpr(); // 当前token为identifier
Result<TypeExpr *, Error> parseTypeExpr();
/* 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
Result<IndexExpr *, Error> parseIndexExpr(
Expr *); // 由 parseExpression调用, 当前token为 `[`
Result<CallExpr *, Error> parseCallExpr(Expr *); // 由 parseExpression调用, 当前token为 `(`
Result<Expr *, Error> parseExpression(BindingPower = 0);
/* Statements */
Result<BlockStmt *, Error> parseBlockStmt(); // 当前token为 {
Result<VarDecl *, Error> parseVarDecl(bool); // 由 parseStatement调用, 当前token为 var
Result<IfStmt *, Error> parseIfStmt(); // 由 parseStatement调用, 当前token为 if
Result<WhileStmt *, Error> parseWhileStmt(); // 由 parseStatement调用, 当前token为 while
Result<DynArray<Param *>, Error> parseFnParams(); // 由 parseFnDefStmt或lambda调用
Result<FnDefStmt *, Error> parseFnDefStmt(bool); // 由 parseStatement调用, 当前token为 func
Result<ReturnStmt *, Error> parseReturnStmt(); // 由 parseStatement调用, 当前token为 return
// continue break直接由parseStatement一步解析
Result<Stmt *, Error> parseStatement();
public:
Result<Program *, Error> Parse();
};
#define SET_STOP_AT(...) currentState().stopAt = {__VA_ARGS__};
}; // namespace Fig
-32
View File
@@ -1,32 +0,0 @@
#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);
auto result = parser.Parse();
if (!result)
{
ReportError(result.error(), srcManager);
return 1;
}
Program *program = *result;
for (Stmt *stmt : program->nodes)
{
std::cout << stmt->toString() << '\n';
}
}
-542
View File
@@ -1,542 +0,0 @@
/*!
@file src/Parser/StmtParser.hpp
@brief 语法分析器(Pratt + 手动递归下降) 语句解析实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#include <Parser/Parser.hpp>
namespace Fig
{
Result<BlockStmt *, Error> Parser::parseBlockStmt() // 当前token为 {
{
SourceLocation location = makeSourceLocation(consumeToken()); // consume `{`
BlockStmt *stmt = new BlockStmt();
while (true)
{
if (isEOF)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed braces in block stmt",
"insert '}'",
location));
}
if (match(TokenType::RightBrace))
{
break;
}
auto result = parseStatement();
if (!result)
{
return std::unexpected(result.error());
}
stmt->nodes.push_back(*result);
}
return stmt;
}
Result<VarDecl *, Error> Parser::parseVarDecl(
bool isPublic) // 由 parseStatement调用, 当前token为 var
{
StateProtector p(this, {State::ParsingVarDecl});
SourceLocation location = makeSourceLocation(consumeToken()); // consume `var`
if (currentToken().type != TokenType::Identifier)
{
return std::unexpected(makeUnexpectTokenError("VarDecl", "var name", currentToken()));
}
const String &name = srcManager.GetSub(currentToken().index, currentToken().length);
consumeToken(); // consume name
TypeExpr *typeSpeicifer = nullptr;
if (match(TokenType::Colon)) // `:`
{
// SET_STOP_AT(TokenType::Walrus, TokenType::Assign);
auto result = parseTypeExpr();
if (!result)
{
return std::unexpected(result.error());
}
typeSpeicifer = *result;
}
Expr *initExpr = nullptr;
bool isInfer = false;
if (match(TokenType::Assign))
{
auto result = parseExpression();
if (!result)
{
return std::unexpected(result.error());
}
initExpr = *result;
}
else if (match(TokenType::Walrus)) // :=
{
if (typeSpeicifer) // 指定了类型同时使用 :=
{
return std::unexpected(Error(ErrorType::SyntaxError,
"used type infer but specifying the type",
"change `:=` to '='",
makeSourceLocation(prevToken()) // :=
));
}
auto result = parseExpression();
if (!result)
{
return std::unexpected(result.error());
}
initExpr = *result;
isInfer = true; // 使用类型自动推断 :=
}
if (!match(TokenType::Semicolon))
{
return std::unexpected(makeExpectSemicolonError());
}
VarDecl *varDecl = new VarDecl(isPublic, name, typeSpeicifer, isInfer, initExpr, location);
return varDecl;
}
Result<IfStmt *, Error> Parser::parseIfStmt() // 由 parseStatement调用, 当前token is if
{
StateProtector p(this, {State::ParsingIf});
SourceLocation location = makeSourceLocation(consumeToken()); // consume `if`
Expr *cond = nullptr;
if (match(TokenType::LeftParen)) // match and consume `(`
{
const Token &lpToken = prevToken();
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
const auto &result = parseExpression(0);
if (!result)
{
return std::unexpected(result.error());
}
if (!match(TokenType::RightParen))
{
delete *result;
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed parenthese in if condition",
"insert `)`",
makeSourceLocation(lpToken)));
}
cond = *result;
}
else
{
SET_STOP_AT(TokenType::LeftBrace);
auto result = parseExpression(0);
if (!result)
{
return std::unexpected(result.error());
}
cond = *result;
}
if (currentToken().type != TokenType::LeftBrace)
{
return std::unexpected(
makeUnexpectTokenError("IfStmt", "LeftBrace `{`", currentToken()));
}
auto result = parseBlockStmt();
if (!result)
{
return std::unexpected(result.error());
}
BlockStmt *consequent = *result;
DynArray<ElseIfStmt *> elifs;
BlockStmt *alternate = nullptr;
while (match(TokenType::Else))
{
SourceLocation elseLocation = makeSourceLocation(prevToken());
if (match(TokenType::If))
{
// else if
if (alternate)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"else if after else",
"remove else if",
elseLocation));
}
Expr *cond = nullptr;
if (match(TokenType::LeftParen)) // `(`
{
const Token &lpToken = prevToken();
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
const auto &result = parseExpression(0);
if (!result)
{
return std::unexpected(result.error());
}
if (!match(TokenType::RightParen))
{
delete *result;
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed parenthese in if condition",
"insert `)`",
makeSourceLocation(lpToken)));
}
cond = *result;
}
else
{
SET_STOP_AT(TokenType::LeftBrace);
auto result = parseExpression(0);
if (!result)
{
return std::unexpected(result.error());
}
cond = *result;
}
if (currentToken().type != TokenType::LeftBrace)
{
return std::unexpected(
makeUnexpectTokenError("ElseIfStmt", "LeftBrace `{`", currentToken()));
}
auto result = parseBlockStmt();
if (!result)
{
return std::unexpected(result.error());
}
BlockStmt *consequent = *result;
ElseIfStmt *elif = new ElseIfStmt(cond, consequent, elseLocation);
elifs.push_back(elif);
}
else
{
// else
if (alternate)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"duplicate else in if stmt",
"remove it",
elseLocation));
}
if (currentToken().type != TokenType::LeftBrace)
{
return std::unexpected(
makeUnexpectTokenError("ElseStmt", "LeftBrace `{`", currentToken()));
}
auto result = parseBlockStmt();
if (!result)
{
return std::unexpected(result.error());
}
alternate = *result;
}
}
IfStmt *ifStmt = new IfStmt(cond, consequent, elifs, alternate, location);
return ifStmt;
}
Result<WhileStmt *, Error> Parser::parseWhileStmt() // 由 parseStatement调用, 当前token为 while
{
StateProtector p(this, {State::ParsingWhile});
SourceLocation location = makeSourceLocation(consumeToken()); // consume `while`
Expr *cond = nullptr;
if (match(TokenType::LeftParen))
{
const Token &lpToken = prevToken();
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
auto result = parseExpression();
if (!result)
{
return std::unexpected(result.error());
}
if (!match(TokenType::RightParen))
{
delete *result;
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed parenthese in while condition",
"insert ')'",
makeSourceLocation(lpToken)));
}
cond = *result;
}
else
{
SET_STOP_AT(TokenType::LeftBrace);
auto result = parseExpression();
if (!result)
{
return std::unexpected(result.error());
}
cond = *result;
}
if (currentToken().type != TokenType::LeftBrace)
{
delete cond;
return std::unexpected(
makeUnexpectTokenError("while stmt", "left brace '{'", currentToken()));
}
auto result = parseBlockStmt();
if (!result)
{
delete cond;
return std::unexpected(result.error());
}
BlockStmt *body = *result;
WhileStmt *whileStmt = new WhileStmt(cond, body, location);
return whileStmt;
}
Result<DynArray<Param *>, Error> Parser::parseFnParams() // 由 parseFnDefStmt或lambda调用
{
StateProtector p(this, {State::ParsingFnDefStmt});
const Token &lpToken = consumeToken(); // consume `(`
DynArray<Param *> params;
while (true)
{
if (isEOF)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"unclosed parenthese in function parameters",
"insert ')'",
makeSourceLocation(lpToken)));
}
if (match(TokenType::RightParen))
{
break;
}
const Token &nToken = consumeToken();
SourceLocation location = makeSourceLocation(nToken);
const String &name = srcManager.GetSub(nToken.index, nToken.length);
// TODO: 支持剩余参数解析...
TypeExpr *type = nullptr;
if (match(TokenType::Colon)) // :
{
auto result = parseTypeExpr();
if (!result)
{
return std::unexpected(result.error());
}
type = *result;
}
Expr *defaultValue = nullptr;
if (match(TokenType::Assign)) // =
{
SET_STOP_AT(TokenType::Comma, TokenType::RightParen, TokenType::LeftBrace); // , ) {
auto result = parseExpression();
if (!result)
{
if (type)
{
delete type;
}
return std::unexpected(result.error());
}
defaultValue = *result;
}
PosParam *posParam = new PosParam(name, type, defaultValue, location);
params.push_back(posParam);
if (match(TokenType::Comma))
{
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("fn params", "param name", currentToken()));
}
}
}
return params;
}
Result<FnDefStmt *, Error> Parser::parseFnDefStmt(
bool isPublic) // 由 parseStatement调用, 当前token为 func
{
SourceLocation location = makeSourceLocation(
consumeToken()); // 无论是否加了public, location都设置为 func token (我懒 :D)
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("fn def stmt", "function name", currentToken()));
}
const Token &nameToken = consumeToken(); // consume name
const String &name = srcManager.GetSub(nameToken.index, nameToken.length);
if (currentToken().type != TokenType::LeftParen)
{
return std::unexpected(
makeUnexpectTokenError("fn def stmt", "lparen '('", currentToken()));
}
DynArray<Param *> params;
auto paraResult = parseFnParams();
if (!paraResult)
{
return std::unexpected(paraResult.error());
}
params = *paraResult;
TypeExpr *returnType = nullptr;
if (match(TokenType::RightArrow)) // ->
{
auto result = parseTypeExpr();
if (!result)
{
return std::unexpected(result.error());
}
returnType = *result;
}
if (currentToken().type != TokenType::LeftBrace)
{
return std::unexpected(
makeUnexpectTokenError("fn def stmt", "function body '{'", currentToken()));
}
BlockStmt *body = nullptr;
auto bodyResult = parseBlockStmt();
if (!bodyResult)
{
if (returnType)
{
delete returnType;
}
return std::unexpected(bodyResult.error());
}
body = *bodyResult;
FnDefStmt *fnDef = new FnDefStmt(isPublic, name, params, returnType, body, location);
return fnDef;
}
Result<ReturnStmt *, Error>
Parser::parseReturnStmt() // 由 parseStatement调用, 当前token为 return
{
StateProtector p(this, {State::ParsingReturn});
SourceLocation location = makeSourceLocation(consumeToken()); // consume `return`
auto result = parseExpression();
if (!result)
{
return std::unexpected(result.error());
}
Expr *value = *result;
ReturnStmt *returnStmt = new ReturnStmt(value, location);
if (!match(TokenType::Semicolon))
{
return std::unexpected(makeExpectSemicolonError());
}
return returnStmt;
}
Result<Stmt *, Error> Parser::parseStatement()
{
StateProtector p(this, {State::Standby});
if (currentToken().type == TokenType::Public)
{
consumeToken(); // consume `public`
if (currentToken().type == TokenType::Variable)
{
return parseVarDecl(true);
}
if (currentToken().type == TokenType::Function)
{
return parseFnDefStmt(true);
}
return std::unexpected(
makeUnexpectTokenError("public", "var/const/func/struct", currentToken()));
}
if (currentToken().type == TokenType::LeftBrace)
{
return parseBlockStmt();
}
if (currentToken().type == TokenType::Variable)
{
return parseVarDecl(false);
}
if (currentToken().type == TokenType::If)
{
return parseIfStmt();
}
if (currentToken().type == TokenType::While)
{
return parseWhileStmt();
}
if (currentToken().type == TokenType::Function)
{
return parseFnDefStmt(false);
}
if (currentToken().type == TokenType::Return)
{
return parseReturnStmt();
}
if (match(TokenType::Break))
{
SourceLocation location = makeSourceLocation(prevToken());
if (!match(TokenType::Semicolon))
{
return std::unexpected(makeExpectSemicolonError());
}
BreakStmt *breakStmt = new BreakStmt(location);
return breakStmt;
}
if (match(TokenType::Continue))
{
SourceLocation location = makeSourceLocation(prevToken());
if (!match(TokenType::Semicolon))
{
return std::unexpected(makeExpectSemicolonError());
}
ContinueStmt *continueStmt = new ContinueStmt(location);
return continueStmt;
}
if (isEOF)
{
return nullptr;
}
const auto &expr_result = parseExpression();
if (!expr_result)
{
return std::unexpected(expr_result.error());
}
ExprStmt *exprStmt = new ExprStmt(*expr_result);
if (!match(TokenType::Semicolon))
{
return std::unexpected(makeExpectSemicolonError());
}
return exprStmt;
}
}; // namespace Fig
-56
View File
@@ -1,56 +0,0 @@
/*!
@file src/Parser/TypeExprParser.cpp
@brief 类型表达式解析器实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-25
*/
#pragma once
#include <Parser/Parser.hpp>
namespace Fig
{
Result<NamedTypeExpr *, Error> Parser::parseNamedTypeExpr() // 当前token为identifier
{
StateProtector p(this, {State::ParsingNamedTypeExpr});
SourceLocation location = makeSourceLocation(currentToken());
DynArray<String> path;
while (true)
{
const Token &subPathTok = consumeToken();
const String &subPath = srcManager.GetSub(subPathTok.index, subPathTok.length);
path.push_back(subPath);
if (match(TokenType::Dot))
{
if (!currentToken().isIdentifier())
{
return std::unexpected(
makeUnexpectTokenError("named type expr", "identifier", currentToken()));
}
}
else
{
break;
}
}
NamedTypeExpr *namedTypeExpr = new NamedTypeExpr(path, location);
return namedTypeExpr;
}
Result<TypeExpr *, Error> Parser::parseTypeExpr()
{
// TODO: 泛型表达式解析
if (currentToken().isIdentifier())
{
return parseNamedTypeExpr();
}
else
{
return std::unexpected(makeUnexpectTokenError("type expr", "name/...", currentToken()));
}
}
}; // namespace Fig
-662
View File
@@ -1,662 +0,0 @@
/*!
@file src/Sema/Analyzer.hpp
@brief 前端类型检查器实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#include <Sema/Analyzer.hpp>
namespace Fig
{
Result<TypeInfo *, Error> Analyzer::resolveType(TypeExpr *typeExpr)
{
NamedTypeExpr *nte = dynamic_cast<NamedTypeExpr *>(typeExpr);
TypeInfo *type = nullptr;
if (nte)
{
type = typeCtx.ResolveTypePath(nte->path);
if (!type)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("no such type `{}` exists", nte->toString()),
"none",
makeSourceLocation(typeExpr)));
}
// ...
}
else
{
// ...
}
return type;
}
Result<void, Error> Analyzer::analyzeVarDecl(VarDecl *stmt)
{
auto sym = env.Resolve(stmt->name);
if (sym != std::nullopt && sym->depth == env.GetDepth())
{
return std::unexpected(Error(ErrorType::RedeclarationError,
std::format("variable `{}` has already defined in this scope", stmt->name),
"change its name",
makeSourceLocation(stmt)));
}
TypeInfo *initType = typeCtx.GetNull();
if (stmt->initExpr)
{
const auto &res = analyzeExpr(stmt->initExpr);
if (!res)
{
return res;
}
initType = stmt->initExpr->resolvedType;
}
TypeInfo *declaredType = typeCtx.GetAny();
if (stmt->typeSpecifier)
{
auto result = resolveType(stmt->typeSpecifier);
if (!result)
{
return std::unexpected(result.error());
}
declaredType = *result;
}
if (stmt->isInfer)
{
declaredType = initType;
}
else if (stmt->initExpr && declaredType != typeCtx.GetAny() && declaredType != initType)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("cannot assign type `{}` to variable {} which speicifer type is '{}'",
initType->name,
stmt->name,
declaredType->name),
"none",
makeSourceLocation(stmt->initExpr)));
}
stmt->localId = env.Define(stmt->name, declaredType, stmt->isPublic, false);
return {};
}
Result<void, Error> Analyzer::analyzeIfStmt(IfStmt *stmt)
{
auto condRes = analyzeExpr(stmt->cond);
if (!condRes)
{
return condRes;
}
if (stmt->cond->resolvedType != typeCtx.GetAny()
&& stmt->cond->resolvedType != typeCtx.GetBool())
{
return std::unexpected(Error(ErrorType::TypeError,
std::format(
"if condition must be boolean, got `{}`", stmt->cond->resolvedType->name),
"ensure condition is boolean",
makeSourceLocation(stmt->cond)));
}
auto consequentRes = analyzeStmt(stmt->consequent);
if (!consequentRes)
{
return consequentRes;
}
for (ElseIfStmt *elif : stmt->elifs)
{
auto condRes = analyzeExpr(elif->cond);
if (elif->cond->resolvedType != typeCtx.GetAny()
&& elif->cond->resolvedType != typeCtx.GetBool())
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("else if condition must be boolean, got `{}`",
elif->cond->resolvedType->name),
"ensure condition is boolean",
makeSourceLocation(elif->cond)));
}
auto consequentRes = analyzeStmt(elif->consequent);
if (!consequentRes)
{
return consequentRes;
}
}
if (stmt->alternate)
{
auto alternateRes = analyzeStmt(stmt->alternate);
if (!alternateRes)
{
return alternateRes;
}
}
return {};
}
Result<void, Error> Analyzer::analyzeWhileStmt(WhileStmt *stmt)
{
auto condRes = analyzeExpr(stmt->cond);
if (!condRes)
{
return condRes;
}
if (stmt->cond->resolvedType != typeCtx.GetAny()
&& stmt->cond->resolvedType != typeCtx.GetBool())
{
return std::unexpected(Error(ErrorType::TypeError,
std::format(
"while condition must be boolean, got `{}`", stmt->cond->resolvedType->name),
"ensure condition is boolean",
makeSourceLocation(stmt->cond)));
}
auto bodyRes = analyzeStmt(stmt->body);
if (!bodyRes)
{
return bodyRes;
}
return {};
}
Result<void, Error> Analyzer::analyzeFnDefStmt(FnDefStmt *stmt)
{
auto sym = env.Resolve(stmt->name);
if (sym != std::nullopt && sym->depth == env.GetDepth())
{
return std::unexpected(Error(ErrorType::RedeclarationError,
std::format("function `{}` has already defined in this scope", stmt->name),
"change its name",
makeSourceLocation(stmt)));
}
stmt->resolvedReturnType = typeCtx.GetAny(); // 默认Any
if (stmt->returnType)
{
auto result = resolveType(stmt->returnType);
if (!result)
{
return std::unexpected(result.error());
}
stmt->resolvedReturnType = *result;
}
stmt->localId = env.Define(stmt->name,
typeCtx.GetFunction(),
stmt->isPublic,
true); // 函数定义语句定义的函数为常量
env.EnterFunction();
env.EnterScope();
PosParam *lastDefaultValueP = nullptr;
for (Param *p : stmt->params)
{
PosParam *posParam = dynamic_cast<PosParam *>(p);
if (posParam)
{
posParam->resolvedType = typeCtx.GetAny();
if (posParam->type)
{
auto result = resolveType(posParam->type);
if (!result)
{
return std::unexpected(result.error());
}
posParam->resolvedType = *result;
}
if (!posParam->defaultValue && lastDefaultValueP)
{
return std::unexpected(Error(ErrorType::SyntaxError,
std::format("no-default parameter `{}` follows default parameter '{}'",
posParam->name,
lastDefaultValueP->name),
"reorder parameters",
posParam->location));
}
if (posParam->defaultValue)
{
lastDefaultValueP = posParam;
auto result = analyzeExpr(posParam->defaultValue);
if (!result)
{
return result;
}
if (posParam->resolvedType != typeCtx.GetAny()
&& posParam->defaultValue->resolvedType != posParam->resolvedType)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format(
"in function '{}', parameter '{}' expects type '{}', but got default value type `{}`",
stmt->name,
posParam->name,
posParam->resolvedType->name,
posParam->defaultValue->resolvedType->name),
"none",
makeSourceLocation(posParam->defaultValue)));
}
}
posParam->localId =
env.Define(posParam->name, posParam->resolvedType, false, false);
}
else
{
// ... 其他参数解析
}
}
ReturnTypeProtector p(this, stmt->resolvedReturnType);
auto bodyRes = analyzeStmt(stmt->body);
env.LeaveScope();
env.LeaveFunction();
if (!bodyRes)
{
return bodyRes;
}
return {};
}
Result<void, Error> Analyzer::analyzeReturnStmt(ReturnStmt *stmt)
{
if (!currentReturnType)
{
return std::unexpected(Error(ErrorType::SyntaxError,
"return outside function",
"remove `return ...`",
makeSourceLocation(stmt)));
}
auto result = analyzeExpr(stmt->value);
if (!result)
{
return result;
}
TypeInfo *valueType = stmt->value->resolvedType;
if (!currentReturnType->isAny() && !valueType->isAny() && currentReturnType != valueType)
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("return type mismatch: expects '{}', got `{}`",
currentReturnType->name,
stmt->value->resolvedType->name),
"none",
makeSourceLocation(stmt->value)));
}
return {};
}
Result<void, Error> Analyzer::analyzeIdentiExpr(IdentiExpr *expr)
{
auto sym = env.Resolve(expr->name);
if (sym == std::nullopt)
{
return std::unexpected(Error(ErrorType::UseUndeclaredIdentifier,
std::format("`{}` has not been defined", expr->name),
"none",
makeSourceLocation(expr)));
}
// TODO: 引入 Module 跨文件 import,检查 isPublic
expr->localId = sym->localId;
expr->resolvedType = sym->type;
expr->resolvedDepth = sym->depth;
expr->isGlobal = (sym->depth == 0);
return {};
}
Result<void, Error> Analyzer::analyzeInfixExpr(InfixExpr *expr)
{
auto resL = analyzeExpr(expr->left);
if (!resL)
return std::unexpected(resL.error());
auto resR = analyzeExpr(expr->right);
if (!resR)
return std::unexpected(resR.error());
TypeInfo *lType = expr->left->resolvedType;
TypeInfo *rType = expr->right->resolvedType;
switch (expr->op)
{
// 算术族 (+, -, *, /, **)
case BinaryOperator::Add:
if (lType == typeCtx.GetString() && rType == typeCtx.GetString())
{
expr->resolvedType = typeCtx.GetString();
break;
}
[[fallthrough]];
case BinaryOperator::Subtract:
case BinaryOperator::Multiply:
case BinaryOperator::Divide:
case BinaryOperator::Power:
if (lType == typeCtx.GetInt() && rType == typeCtx.GetInt())
{
expr->resolvedType = typeCtx.GetInt();
}
else if ((lType == typeCtx.GetInt() || lType == typeCtx.GetDouble())
&& (rType == typeCtx.GetInt() || rType == typeCtx.GetDouble()))
{
expr->resolvedType = typeCtx.GetDouble();
}
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
{
expr->resolvedType = typeCtx.GetAny();
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"invalid operands for arithmetic operation",
"ensure both sides are numbers (Int or Double)",
makeSourceLocation(expr->right)));
}
break;
// 整数特化族 (%, &, |, ^, <<, >>)
case BinaryOperator::Modulo:
case BinaryOperator::BitAnd:
case BinaryOperator::BitOr:
case BinaryOperator::BitXor:
case BinaryOperator::ShiftLeft:
case BinaryOperator::ShiftRight:
if (lType == typeCtx.GetInt() && rType == typeCtx.GetInt())
{
expr->resolvedType = typeCtx.GetInt();
}
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
{
expr->resolvedType = typeCtx.GetAny();
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"bitwise and modulo operations require Int operands",
"cast operands to Int before operation",
makeSourceLocation(expr->right)));
}
break;
// 比较族 (==, !=, <, >, <=, >=)
case BinaryOperator::Equal:
case BinaryOperator::NotEqual:
case BinaryOperator::Less:
case BinaryOperator::Greater:
case BinaryOperator::LessEqual:
case BinaryOperator::GreaterEqual:
case BinaryOperator::Is:
if (lType != typeCtx.GetAny() && rType != typeCtx.GetAny()
&& lType != rType) // lType == rType放行
{
if (!((lType == typeCtx.GetInt() && rType == typeCtx.GetDouble())
|| (lType == typeCtx.GetDouble() && rType == typeCtx.GetInt())))
{
return std::unexpected(Error(ErrorType::TypeError,
"cannot compare different types",
"ensure both sides of the comparison are of the same type",
makeSourceLocation(expr)));
}
}
// TODO: 支持Struct后进行检查,右操作数是 Struct才合理
// 如 1.2 is Int --> false
// 1 is Int --> true
expr->resolvedType = typeCtx.GetBool();
break;
// 逻辑族 (&&, ||)
case BinaryOperator::LogicalAnd:
case BinaryOperator::LogicalOr:
if (lType == typeCtx.GetBool() && rType == typeCtx.GetBool())
{
expr->resolvedType = typeCtx.GetBool();
}
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
{
expr->resolvedType = typeCtx.GetBool();
}
else
{
return std::unexpected(Error(ErrorType::TypeError,
"logical operators require Bool operands",
"use boolean expressions",
makeSourceLocation(expr)));
}
break;
// 纯赋值与复合赋值族 (=, +=, -=, ...)
case BinaryOperator::Assign:
case BinaryOperator::AddAssign:
case BinaryOperator::SubAssign:
case BinaryOperator::MultiplyAssign:
case BinaryOperator::DivideAssign:
case BinaryOperator::ModuloAssign:
case BinaryOperator::BitXorAssign:
// 左侧必须是合法的 L-Value
if (!isValidLvalue(expr->left))
{
return std::unexpected(Error(ErrorType::NotAnLvalue,
"invalid assignment target",
"left side must be a variable, property, or indexable target",
makeSourceLocation(expr->left) // 错误精准定位到左侧节点
));
}
// 类型匹配拦截 (纯赋值)
if (expr->op == BinaryOperator::Assign)
{
if (lType != typeCtx.GetAny() && rType != typeCtx.GetAny() && lType != rType)
{
if (!(lType == typeCtx.GetDouble() && rType == typeCtx.GetInt()))
{ // 允许 Int 赋给 Double
return std::unexpected(Error(ErrorType::TypeError,
"cannot assign value to variable of different type",
"ensure the assigned value matches the declared type",
makeSourceLocation(expr->right)));
}
}
}
expr->resolvedType = lType;
break;
// 成员访问 (.)
case BinaryOperator::MemberAccess:
if (lType != typeCtx.GetStruct() && lType != typeCtx.GetAny())
{
return std::unexpected(Error(ErrorType::TypeError,
"member access requires a Struct object",
"check if the left side evaluates to an object",
makeSourceLocation(expr->left)));
}
if (expr->right->type != AstType::IdentiExpr)
{
return std::unexpected(Error(ErrorType::SyntaxError,
std::format("expect field name after member access '.', got {}",
expr->right->toString()),
"none",
makeSourceLocation(expr->right)));
}
expr->resolvedType = typeCtx.GetAny();
break;
default:
return std::unexpected(Error(ErrorType::TypeError,
"unknown binary operator in static analysis",
"this is likely an internal compiler error",
makeSourceLocation(expr)));
}
return {};
}
Result<void, Error> Analyzer::analyzeCallExpr(CallExpr *expr)
{
auto calleeRes = analyzeExpr(expr->callee);
if (!calleeRes)
{
return calleeRes;
}
if (expr->callee->resolvedType != typeCtx.GetAny()
&& expr->callee->resolvedType != typeCtx.GetFunction())
{
return std::unexpected(Error(ErrorType::TypeError,
std::format("object `{}` is not callable", expr->callee->toString()),
"none",
makeSourceLocation(expr->callee)));
}
for (auto *arg : expr->args.args)
{
auto argRes = analyzeExpr(arg);
if (!argRes)
{
return argRes;
}
}
expr->resolvedType = typeCtx.GetAny();
return {};
}
Result<void, Error> Analyzer::analyzeStmt(Stmt *stmt)
{
if (!stmt)
return {};
switch (stmt->type)
{
case AstType::VarDecl: return analyzeVarDecl(static_cast<VarDecl *>(stmt));
case AstType::ExprStmt: {
auto *exprStmt = static_cast<ExprStmt *>(stmt);
return analyzeExpr(exprStmt->expr); // 表达式语句只需要推导内部表达式即可
}
case AstType::BlockStmt: {
auto *block = static_cast<BlockStmt *>(stmt);
env.EnterScope(); // 进入新大括号,作用域深度 +1
for (auto *s : block->nodes)
{
auto res = analyzeStmt(s);
if (!res)
return std::unexpected(res.error());
}
env.LeaveScope(); // 离开大括号,自动销毁局部类型记录
return {};
}
case AstType::IfStmt: {
return analyzeIfStmt(static_cast<IfStmt *>(stmt));
}
case AstType::WhileStmt: {
return analyzeWhileStmt(static_cast<WhileStmt *>(stmt));
}
case AstType::FnDefStmt: {
return analyzeFnDefStmt(static_cast<FnDefStmt *>(stmt));
}
case AstType::ReturnStmt: {
return analyzeReturnStmt(static_cast<ReturnStmt *>(stmt));
}
// TODO: 其他语句分析
// default:
// return std::unexpected(Error(ErrorType::TypeError,
// "unsupported statement type in analyzer",
// "internal compiler error",
// makeSourceLocation(stmt)));
}
return {};
}
Result<void, Error> Analyzer::analyzeExpr(Expr *expr)
{
if (!expr)
return {};
switch (expr->type)
{
case AstType::LiteralExpr: {
auto *lit = static_cast<LiteralExpr *>(expr);
switch (lit->token.type)
{
case TokenType::LiteralTrue:
case TokenType::LiteralFalse: lit->resolvedType = typeCtx.GetBool(); break;
case TokenType::LiteralNull: lit->resolvedType = typeCtx.GetNull(); break;
case TokenType::LiteralNumber: {
const String &lexeme = manager.GetSub(lit->token.index, lit->token.length);
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
{
lit->resolvedType = typeCtx.GetDouble();
}
else
{
lit->resolvedType = typeCtx.GetInt();
}
break;
}
case TokenType::LiteralString: {
lit->resolvedType = typeCtx.GetString();
break;
}
default: {
lit->resolvedType = typeCtx.GetAny();
break;
}
}
return {};
}
case AstType::IdentiExpr: {
return analyzeIdentiExpr(static_cast<IdentiExpr *>(expr));
}
case AstType::InfixExpr: {
return analyzeInfixExpr(static_cast<InfixExpr *>(expr));
}
case AstType::CallExpr: {
return analyzeCallExpr(static_cast<CallExpr *>(expr));
}
// TODO: PrefixExpr (前缀), CallExpr (函数调用), MemberExpr (属性访问)
default:
// 对于还没实现的表达式,默认降级为 Any 防止崩溃
expr->resolvedType = typeCtx.GetAny();
return {};
}
}
Result<void, Error> Analyzer::Analyze(Program *program)
{
for (auto *stmt : program->nodes)
{
auto res = analyzeStmt(stmt);
if (!res)
return std::unexpected(res.error()); // 遇到任何错误,立刻中断并向上传递
}
return {};
}
}; // namespace Fig
-100
View File
@@ -1,100 +0,0 @@
/*!
@file src/Sema/Analyzer.hpp
@brief 前端类型检查器定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <Sema/Environment.hpp>
#include <Sema/Type.hpp>
#include <Ast/Ast.hpp>
#include <Deps/Deps.hpp>
namespace Fig
{
class Analyzer
{
private:
Environment env;
SourceManager &manager;
TypeContext typeCtx;
TypeInfo *currentReturnType = nullptr; // 正在分析的函数,预期返回类型
struct ReturnTypeProtector
{
Analyzer *analyzer;
TypeInfo *prevCurrentReturnType;
[[nodiscard]]
ReturnTypeProtector(Analyzer *_analyzer, TypeInfo *current) :
analyzer(_analyzer), prevCurrentReturnType(_analyzer->currentReturnType)
{
analyzer->currentReturnType = current;
}
~ReturnTypeProtector()
{
analyzer->currentReturnType = prevCurrentReturnType;
}
ReturnTypeProtector(const ReturnTypeProtector &) = delete;
ReturnTypeProtector &operator=(const ReturnTypeProtector &) = delete;
};
SourceLocation makeSourceLocation(
AstNode *ast, std::source_location loc = std::source_location::current())
{
return SourceLocation(ast->location.sp,
ast->location.fileName,
"[internal analyzer]",
loc.function_name());
}
bool isValidLvalue(Expr *expr)
{
if (expr->type == AstType::IdentiExpr)
{
return true;
}
if (expr->type == AstType::InfixExpr)
{
InfixExpr *infix = static_cast<InfixExpr *>(expr);
if (infix->op == BinaryOperator::MemberAccess)
{
return true;
}
}
if (expr->type == AstType::IndexExpr)
{
return true;
}
return false;
}
Result<TypeInfo *, Error> resolveType(TypeExpr *);
Result<void, Error> analyzeVarDecl(VarDecl *);
Result<void, Error> analyzeIfStmt(IfStmt *);
Result<void, Error> analyzeWhileStmt(WhileStmt *);
Result<void, Error> analyzeFnDefStmt(FnDefStmt *);
Result<void, Error> analyzeReturnStmt(ReturnStmt *);
Result<void, Error> analyzeIdentiExpr(IdentiExpr *);
Result<void, Error> analyzeInfixExpr(InfixExpr *);
Result<void, Error> analyzeCallExpr(CallExpr *);
Result<void, Error> analyzeStmt(Stmt *);
Result<void, Error> analyzeExpr(Expr *);
public:
Result<void, Error> Analyze(Program *);
Analyzer(SourceManager &_manager) : manager(_manager), typeCtx() {}
};
}; // namespace Fig
-50
View File
@@ -1,50 +0,0 @@
#include <Sema/Analyzer.hpp>
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Lexer/Lexer.hpp>
#include <Parser/Parser.hpp>
#include <SourceManager/SourceManager.hpp>
#include <iostream>
int main()
{
using namespace Fig;
const String &fileName = "test.fig";
const String &filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
SourceManager manager(filePath);
manager.Read();
if (!manager.read)
{
std::cerr << "Read file failed \n";
return 1;
}
Lexer lexer(manager.GetSource(), fileName);
Parser parser(lexer, manager, fileName);
auto result = parser.Parse();
if (!result)
{
ReportError(result.error(), manager);
return 1;
}
Program *program = *result;
Analyzer analyzer(manager);
const auto &analyzeResult = analyzer.Analyze(program);
if (!analyzeResult)
{
ReportError(analyzeResult.error(), manager);
return 1;
}
std::cout << "Analyze successfully, PROGRAM OK\n";
return 0;
}
-147
View File
@@ -1,147 +0,0 @@
/*!
@file src/Sema/Environment.hpp
@brief 符号和作用域环境定义
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <Deps/Deps.hpp>
#include <Error/Error.hpp>
#include <Sema/Type.hpp>
#include <cassert>
#include <optional>
namespace Fig
{
// 记录在 Analyzer 中的符号元数据
struct Symbol
{
String name;
TypeInfo *type;
bool isPublic;
int depth; // 词法作用域深度
bool isConstant; // 是否是 const 声明的不可变常量 (用于报错: 尝试修改常量)
int localId = -1; // Analyzer 虚拟槽位分配
};
// 作用域回档水位线
struct ScopeWatermark
{
std::size_t symbolCount;
int savedLocalId;
};
// 语义分析函数上下文 (隔离局部变量 ID 空间)
struct SemaFuncState
{
SemaFuncState *enclosing = nullptr;
int currentDepth = 0;
int nextLocalId = 0;
DynArray<ScopeWatermark> scopeStack;
};
class Environment
{
private:
DynArray<Symbol> symbols;
SemaFuncState *current = nullptr;
public:
Environment()
{
current = new SemaFuncState();
}
~Environment()
{
while (current)
{
SemaFuncState *prev = current->enclosing;
delete current;
current = prev;
}
}
// 函数边界控
void EnterFunction()
{
SemaFuncState *newState = new SemaFuncState();
newState->enclosing = current;
current = newState;
}
void LeaveFunction()
{
assert(current && "Environment: Unmatched LeaveFunction");
SemaFuncState *oldState = current;
current = oldState->enclosing;
delete oldState;
}
// 词法作用域控制
void EnterScope()
{
current->currentDepth++;
current->scopeStack.push_back({symbols.size(), current->nextLocalId});
}
void LeaveScope()
{
assert(current->currentDepth > 0 && "Environment: Unmatched LeaveScope");
current->currentDepth--;
assert(!current->scopeStack.empty());
ScopeWatermark archive = current->scopeStack.back();
current->scopeStack.pop_back();
// 物理截断符号表,回滚槽位发号器以复用物理寄存器
while (symbols.size() > archive.symbolCount)
{
symbols.pop_back();
}
current->nextLocalId = archive.savedLocalId;
}
// 符号操作
// 注册符号, 返回分配的 localId。调用前内部执行同级作用域重定义断言
int Define(const String &name, TypeInfo *type, bool isPublic, bool isConst)
{
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
{
if (it->depth < current->currentDepth)
break;
if (it->name == name)
{
assert(false && "Environment.Define: redefinition");
}
}
int allocatedId = current->nextLocalId++;
symbols.push_back({name, type, isPublic, current->currentDepth, isConst, allocatedId});
return allocatedId;
}
// 解析符号。找不到返回 nullopt
std::optional<Symbol> Resolve(const String &name)
{
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
{
if (it->name == name)
return *it;
}
return std::nullopt;
}
int GetDepth() const
{
return current->currentDepth;
}
};
} // namespace Fig
-136
View File
@@ -1,136 +0,0 @@
/*!
@file src/Sema/Type.hpp
@brief 前端类型检查的类型定义和类型驻留池
@author PuqiAR (im@puqiar.top)
@date 2026-02-23
*/
#pragma once
#include <Deps/Deps.hpp>
#include <cstdint>
namespace Fig
{
enum class TypeTag : std::uint8_t
{
Any, // 动态类型底线
Null, // 空值
Int,
Double,
Bool,
String,
Function,
Struct,
};
struct TypeInfo
{
TypeTag tag;
String name; // 完整路径序列化, 如 Int, std.file.File
bool isAny() const
{
return tag == TypeTag::Any;
}
};
// 全局唯一类型驻留池
class TypeContext
{
private:
DynArray<TypeInfo *> allTypes;
// 缓存
TypeInfo *typeAny;
TypeInfo *typeNull;
TypeInfo *typeInt;
TypeInfo *typeDouble;
TypeInfo *typeBool;
TypeInfo *typeString;
TypeInfo *typeFunction;
TypeInfo *typeStruct;
public:
TypeInfo *GetAny()
{
return typeAny;
}
TypeInfo *GetNull()
{
return typeNull;
}
TypeInfo *GetInt()
{
return typeInt;
}
TypeInfo *GetDouble()
{
return typeDouble;
}
TypeInfo *GetBool()
{
return typeBool;
}
TypeInfo *GetString()
{
return typeString;
}
TypeInfo *GetFunction()
{
return typeFunction;
}
TypeInfo *GetStruct()
{
return typeStruct;
}
TypeInfo *ResolveTypePath(const String &fullName)
{
for (auto *t : allTypes)
{
if (t->name == fullName)
return t;
}
return nullptr; // 没找到该类型
}
TypeInfo *ResolveTypePath(const DynArray<String> &path)
{
// TODO: 支持Module 系统, 查 Module 的导出表
String fullName = path.empty() ? "" : path[0];
for (size_t i = 1; i < path.size(); ++i)
{
fullName += "." + path[i];
}
return ResolveTypePath(fullName);
}
~TypeContext()
{
for (TypeInfo *t : allTypes)
delete t;
}
TypeContext()
{
typeAny = createBuiltin(TypeTag::Any, "Any");
typeNull = createBuiltin(TypeTag::Null, "Null");
typeInt = createBuiltin(TypeTag::Int, "Int");
typeDouble = createBuiltin(TypeTag::Double, "Double");
typeBool = createBuiltin(TypeTag::Bool, "Bool");
typeString = createBuiltin(TypeTag::String, "String");
typeFunction = createBuiltin(TypeTag::Function, "Function");
typeStruct = createBuiltin(TypeTag::Struct, "Struct");
}
private:
TypeInfo *createBuiltin(TypeTag tag, String name)
{
TypeInfo *t = new TypeInfo{tag, std::move(name)};
allTypes.push_back(t);
return t;
}
};
}; // namespace Fig
-146
View File
@@ -1,146 +0,0 @@
/*!
@file src/SourceManager/SourceManager.hpp
@brief 源代码管理
@author PuqiAR (im@puqiar.top)
@date 2026-02-14
*/
#pragma once
#include <Core/SourceLocations.hpp>
#include <Deps/Deps.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 (!source.empty() && source.back() == U'\n')
source.pop_back();
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;
}
String &GetSource()
{
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};
}
void LoadFromMemory(String src)
{
source = std::move(src);
read = true;
preprocessLineIndices();
}
};
}; // namespace Fig
-96
View File
@@ -1,96 +0,0 @@
/*!
@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
View File
@@ -1,168 +0,0 @@
/*!
@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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-44
View File
@@ -1,44 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_ALL_HPP
#define NEARGYE_MAGIC_ENUM_ALL_HPP
#include "magic_enum.hpp"
#include "magic_enum_containers.hpp"
#include "magic_enum_flags.hpp"
#include "magic_enum_format.hpp"
#include "magic_enum_fuse.hpp"
#include "magic_enum_iostream.hpp"
#include "magic_enum_switch.hpp"
#include "magic_enum_utility.hpp"
#endif // NEARGYE_MAGIC_ENUM_ALL_HPP
File diff suppressed because it is too large Load Diff
-222
View File
@@ -1,222 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP
#define NEARGYE_MAGIC_ENUM_FLAGS_HPP
#include "magic_enum.hpp"
#if defined(__clang__)
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
#elif defined(_MSC_VER)
# pragma warning(push)
#endif
namespace magic_enum {
namespace detail {
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
constexpr U values_ors() noexcept {
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
auto ors = U{0};
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
ors |= static_cast<U>(values_v<E, S>[i]);
}
return ors;
}
} // namespace magic_enum::detail
// Returns name from enum-flags value.
// If enum-flags value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] auto enum_flags_name(E value, char_type sep = static_cast<char_type>('|')) -> detail::enable_if_t<E, string> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
string name;
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (static_cast<U>(value) & v) != 0) {
if (const auto n = detail::names_v<D, S>[i]; !n.empty()) {
check_value |= v;
if (!name.empty()) {
name.append(1, sep);
}
name.append(n.data(), n.size());
} else {
return {}; // Value out of range.
}
}
}
if (check_value != 0 && check_value == static_cast<U>(value)) {
return name;
}
return {}; // Invalid value or out of range.
}
// Obtains enum-flags value from integer value.
// Returns optional with enum-flags value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else {
if constexpr (detail::is_sparse_v<D, S>) {
auto check_value = U{0};
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (value & v) != 0) {
check_value |= v;
}
}
if (check_value != 0 && check_value == value) {
return static_cast<D>(value);
}
} else {
constexpr auto min = detail::min_v<D, S>;
constexpr auto max = detail::values_ors<D, S>();
if (value >= min && value <= max) {
return static_cast<D>(value);
}
}
return {}; // Invalid value or out of range.
}
}
// Obtains enum-flags value from name.
// Returns optional with enum-flags value.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
constexpr auto S = detail::enum_subtype::flags;
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
if constexpr (detail::count_v<D, S> == 0) {
static_cast<void>(value);
return {}; // Empty enum.
} else {
auto result = U{0};
while (!value.empty()) {
const auto d = detail::find(value, '|');
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
auto f = U{0};
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
if (detail::cmp_equal(s, detail::names_v<D, S>[i], p)) {
f = static_cast<U>(enum_value<D, S>(i));
result |= f;
break;
}
}
if (f == U{0}) {
return {}; // Invalid value or out of range.
}
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
}
if (result != U{0}) {
return static_cast<D>(result);
}
return {}; // Invalid value or out of range.
}
}
// Checks whether enum-flags contains value with such value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
return static_cast<bool>(enum_flags_cast<D>(static_cast<U>(value)));
}
// Checks whether enum-flags contains value with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
using D = std::decay_t<E>;
return static_cast<bool>(enum_flags_cast<D>(value));
}
// Checks whether enum-flags contains enumerator with such name.
template <typename E, typename BinaryPredicate = std::equal_to<>>
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
using D = std::decay_t<E>;
return static_cast<bool>(enum_flags_cast<D>(value, std::move(p)));
}
// Checks whether `flags set` contains `flag`.
// Note: If `flag` equals 0, it returns false, as 0 is not a flag.
template <typename E>
constexpr auto enum_flags_test(E flags, E flag) noexcept -> detail::enable_if_t<E, bool> {
using U = underlying_type_t<E>;
return static_cast<U>(flag) && ((static_cast<U>(flags) & static_cast<U>(flag)) == static_cast<U>(flag));
}
// Checks whether `lhs flags set` and `rhs flags set` have common flags.
// Note: If `lhs flags set` or `rhs flags set` equals 0, it returns false, as 0 is not a flag, and therfore cannot have any matching flag.
template <typename E>
constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t<E, bool> {
using U = underlying_type_t<E>;
return (static_cast<U>(lhs) & static_cast<U>(rhs)) != 0;
}
} // namespace magic_enum
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#elif defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP
-114
View File
@@ -1,114 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP
#define NEARGYE_MAGIC_ENUM_FORMAT_HPP
#include "magic_enum.hpp"
#include "magic_enum_flags.hpp"
#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT)
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT 1
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
#endif
namespace magic_enum::customize {
// customize enum to enable/disable automatic std::format
template <typename E>
constexpr bool enum_format_enabled() noexcept {
return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT;
}
} // magic_enum::customize
#if defined(__cpp_lib_format)
#ifndef MAGIC_ENUM_USE_STD_MODULE
#include <format>
#endif
template <typename E>
struct std::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
template <class FormatContext>
auto format(E e, FormatContext& ctx) const {
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
using D = std::decay_t<E>;
if constexpr (magic_enum::detail::supported<D>::value) {
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
} else {
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
}
}
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
}
};
#endif
#if defined(FMT_VERSION)
#include <fmt/format.h>
template <typename E>
struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : fmt::formatter<std::string_view> {
template <class FormatContext>
auto format(E e, FormatContext& ctx) const {
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
using D = std::decay_t<E>;
if constexpr (magic_enum::detail::supported<D>::value) {
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
} else {
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
}
}
}
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
}
};
#endif
#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE)
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
#endif
#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP
-89
View File
@@ -1,89 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP
#define NEARGYE_MAGIC_ENUM_FUSE_HPP
#include "magic_enum.hpp"
namespace magic_enum {
namespace detail {
template <typename E>
constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
if (hash) {
if (const auto index = enum_index(value)) {
return (*hash << log2((enum_count<E>() << 1) - 1)) | *index;
}
}
return {};
}
template <typename E>
constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
return fuse_one_enum(0, value);
}
template <typename E, typename... Es>
constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
return fuse_one_enum(fuse_enum(tail...), head);
}
template <typename... Es>
constexpr auto typesafe_fuse_enum(Es... values) noexcept {
enum class enum_fuse_t : std::uintmax_t;
const auto fuse = fuse_enum(values...);
if (fuse) {
return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
}
return optional<enum_fuse_t>{};
}
} // namespace magic_enum::detail
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
template <typename... Es>
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
static_assert((detail::log2(enum_count<std::decay_t<Es>>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
#else
const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
#endif
return MAGIC_ENUM_ASSERT(fuse), fuse;
}
} // namespace magic_enum
#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP
@@ -1,117 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
#define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
#include "magic_enum.hpp"
#include "magic_enum_flags.hpp"
#ifndef MAGIC_ENUM_USE_STD_MODULE
#include <iosfwd>
#endif
namespace magic_enum {
namespace ostream_operators {
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
using D = std::decay_t<E>;
using U = underlying_type_t<D>;
if constexpr (detail::supported<D>::value) {
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
for (const auto c : name) {
os.put(c);
}
return os;
}
} else {
if (const auto name = enum_name<D>(value); !name.empty()) {
for (const auto c : name) {
os.put(c);
}
return os;
}
}
}
return (os << static_cast<U>(value));
}
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
return value ? (os << *value) : os;
}
} // namespace magic_enum::ostream_operators
namespace istream_operators {
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
using D = std::decay_t<E>;
std::basic_string<Char, Traits> s;
is >> s;
if constexpr (detail::supported<D>::value) {
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
if (const auto v = enum_flags_cast<D>(s)) {
value = *v;
} else {
is.setstate(std::basic_ios<Char>::failbit);
}
} else {
if (const auto v = enum_cast<D>(s)) {
value = *v;
} else {
is.setstate(std::basic_ios<Char>::failbit);
}
}
} else {
is.setstate(std::basic_ios<Char>::failbit);
}
return is;
}
} // namespace magic_enum::istream_operators
namespace iostream_operators {
using magic_enum::ostream_operators::operator<<;
using magic_enum::istream_operators::operator>>;
} // namespace magic_enum::iostream_operators
} // namespace magic_enum
#endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
-195
View File
@@ -1,195 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
#include "magic_enum.hpp"
namespace magic_enum {
namespace detail {
struct default_result_type {};
template <typename T>
struct identity {
using type = T;
};
struct nonesuch {};
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
struct invoke_result : identity<nonesuch> {};
template <typename F, typename V>
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
template <typename F, typename V>
using invoke_result_t = typename invoke_result<F, V>::type;
template <typename E, enum_subtype S, typename F, std::size_t... I>
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
static_assert(std::is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
if constexpr (count_v<E, S> == 0) {
return identity<nonesuch>{};
} else {
return std::common_type<invoke_result_t<F, enum_constant<values_v<E, S>[I]>>...>{};
}
}
template <typename E, enum_subtype S, typename Result, typename F>
constexpr auto result_type() noexcept {
static_assert(std::is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
constexpr auto seq = std::make_index_sequence<count_v<E, S>>{};
using R = typename decltype(common_invocable<E, S, F>(seq))::type;
if constexpr (std::is_same_v<Result, default_result_type>) {
if constexpr (std::is_same_v<R, nonesuch>) {
return identity<void>{};
} else {
return identity<R>{};
}
} else {
if constexpr (std::is_convertible_v<R, Result>) {
return identity<Result>{};
} else if constexpr (std::is_convertible_v<Result, R>) {
return identity<R>{};
} else {
return identity<nonesuch>{};
}
}
}
template <typename E, enum_subtype S, typename Result, typename F, typename D = std::decay_t<E>, typename R = typename decltype(result_type<D, S, Result, F>())::type>
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
#if !defined(MAGIC_ENUM_ENABLE_HASH) && !defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
template <typename T = void>
inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
template <>
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
template <std::size_t I, std::size_t End, typename R, typename E, enum_subtype S, typename F, typename Def>
constexpr decltype(auto) constexpr_switch_impl(F&& f, E value, Def&& def) {
if constexpr(I < End) {
constexpr auto v = enum_constant<enum_value<E, I, S>()>{};
if (value == v) {
if constexpr (std::is_invocable_r_v<R, F, decltype(v)>) {
return static_cast<R>(std::forward<F>(f)(v));
} else {
return def();
}
} else {
return constexpr_switch_impl<I + 1, End, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
}
} else {
return def();
}
}
template <typename R, typename E, enum_subtype S, typename F, typename Def>
constexpr decltype(auto) constexpr_switch(F&& f, E value, Def&& def) {
static_assert(is_enum_v<E>, "magic_enum::detail::constexpr_switch requires enum type.");
if constexpr (count_v<E, S> == 0) {
return def();
} else {
return constexpr_switch_impl<0, count_v<E, S>, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
}
}
#endif
} // namespace magic_enum::detail
template <typename Result = detail::default_result_type, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
std::forward<F>(f),
value,
detail::default_result_type_lambda<R>);
#else
return detail::constexpr_switch<R, D, S>(
std::forward<F>(f),
value,
detail::default_result_type_lambda<R>);
#endif
}
template <typename Result = detail::default_result_type, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value) {
return enum_switch<Result, E, S>(std::forward<F>(f), value);
}
template <typename Result, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
std::forward<F>(f),
value,
[&result]() -> R { return std::forward<Result>(result); });
#else
return detail::constexpr_switch<R, D, S>(
std::forward<F>(f),
value,
[&result]() -> R { return std::forward<Result>(result); });
#endif
}
template <typename Result, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
return enum_switch<Result, E, S>(std::forward<F>(f), value, std::forward<Result>(result));
}
} // namespace magic_enum
template <>
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
template <typename T>
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
template <typename T>
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP
-138
View File
@@ -1,138 +0,0 @@
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.7
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP
#define NEARGYE_MAGIC_ENUM_UTILITY_HPP
#include "magic_enum.hpp"
namespace magic_enum {
namespace detail {
template <typename E, enum_subtype S, typename F, std::size_t... I>
constexpr auto for_each(F&& f, std::index_sequence<I...>) {
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> || ...);
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> && ...);
if constexpr (has_void_return) {
(f(enum_constant<values_v<E, S>[I]>{}), ...);
} else if constexpr (all_same_return) {
return std::array{f(enum_constant<values_v<E, S>[I]>{})...};
} else {
return std::tuple{f(enum_constant<values_v<E, S>[I]>{})...};
}
}
template <typename E, enum_subtype S, typename F,std::size_t... I>
constexpr bool all_invocable(std::index_sequence<I...>) {
if constexpr (count_v<E, S> == 0) {
return false;
} else {
return (std::is_invocable_v<F, enum_constant<values_v<E, S>[I]>> && ...);
}
}
} // namespace magic_enum::detail
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, detail::enable_if_t<E, int> = 0>
constexpr auto enum_for_each(F&& f) {
using D = std::decay_t<E>;
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};
if constexpr (detail::all_invocable<D, S, F>(sep)) {
return detail::for_each<D, S>(std::forward<F>(f), sep);
} else {
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
}
}
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_next_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
if (const auto i = enum_index<D, S>(value)) {
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) + n);
if (index >= 0 && index < count) {
return enum_value<D, S>(static_cast<std::size_t>(index));
}
}
return {};
}
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_next_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
if (const auto i = enum_index<D, S>(value)) {
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) + n) % count) + count) % count);
if (index >= 0 && index < count) {
return enum_value<D, S>(static_cast<std::size_t>(index));
}
}
return MAGIC_ENUM_ASSERT(false), value;
}
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_prev_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
using D = std::decay_t<E>;
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
if (const auto i = enum_index<D, S>(value)) {
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) - n);
if (index >= 0 && index < count) {
return enum_value<D, S>(static_cast<std::size_t>(index));
}
}
return {};
}
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
[[nodiscard]] constexpr auto enum_prev_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
using D = std::decay_t<E>;
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
if (const auto i = enum_index<D, S>(value)) {
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) - n) % count) + count) % count);
if (index >= 0 && index < count) {
return enum_value<D, S>(static_cast<std::size_t>(index));
}
}
return MAGIC_ENUM_ASSERT(false), value;
}
} // namespace magic_enum
#endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP
-230
View File
@@ -1,230 +0,0 @@
/*!
@file src/VM/VM.cpp
@brief 虚拟机核心执行引擎实现
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#include <VM/VM.hpp>
// Computed GOTO!!!
#define BINARY_ARITHMETIC_OP(opName, op) \
do_##opName: \
{ \
std::uint8_t a = decodeA(inst); \
std::uint8_t b = decodeB(inst); \
std::uint8_t c = decodeC(inst); \
Value lhs = currentFrame->registerBase[b]; \
Value rhs = currentFrame->registerBase[c]; \
if (lhs.IsInt() && rhs.IsInt()) [[likely]] \
{ \
currentFrame->registerBase[a] = Value::FromInt(lhs.AsInt() op rhs.AsInt()); \
} \
else if (lhs.IsDouble() && rhs.IsDouble()) [[likely]] \
{ \
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsDouble() op rhs.AsDouble()); \
} \
else if (lhs.IsInt() && rhs.IsDouble()) [[likely]] \
{ \
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsInt() op rhs.AsDouble()); \
} \
else if (lhs.IsDouble() && rhs.IsInt()) [[likely]] \
{ \
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsDouble() op rhs.AsInt()); \
} \
else \
{ \
assert(false && "VM Runtime Error: Unsupported types for arithmetic operation"); \
} \
DISPATCH(); \
}
#define BINARY_COMPARE_OP(opName, op) \
do_##opName: \
{ \
std::uint8_t a = decodeA(inst); \
std::uint8_t b = decodeB(inst); \
std::uint8_t c = decodeC(inst); \
Value lhs = currentFrame->registerBase[b]; \
Value rhs = currentFrame->registerBase[c]; \
if (lhs.IsInt() && rhs.IsInt()) [[likely]] \
{ \
currentFrame->registerBase[a] = (lhs.AsInt() op rhs.AsInt()) ? \
Value::GetTrueInstance() : \
Value::GetFalseInstance(); \
} \
else if (lhs.IsDouble() && rhs.IsDouble()) [[likely]] \
{ \
currentFrame->registerBase[a] = (lhs.AsDouble() op rhs.AsDouble()) ? \
Value::GetTrueInstance() : \
Value::GetFalseInstance(); \
} \
else if (lhs.IsInt() && rhs.IsDouble()) [[likely]] \
{ \
currentFrame->registerBase[a] = (lhs.AsInt() op rhs.AsDouble()) ? \
Value::GetTrueInstance() : \
Value::GetFalseInstance(); \
} \
else if (lhs.IsDouble() && rhs.IsInt()) [[likely]] \
{ \
currentFrame->registerBase[a] = (lhs.AsDouble() op rhs.AsInt()) ? \
Value::GetTrueInstance() : \
Value::GetFalseInstance(); \
} \
else \
{ \
assert(false && "VM Runtime Error: Unsupported types for comparison"); \
} \
DISPATCH(); \
}
namespace Fig
{
Result<Value, Error> VM::Execute(CompiledModule *compiledModule)
{
Proto *entry = compiledModule->protos[0];
pushFrame(entry, registers);
// 🔥 必须与 Bytecode.hpp 中的 OpCode 枚举严格一一对应!
static const void *dispatchTable[] = {&&do_Exit,
&&do_LoadK,
&&do_LoadTrue,
&&do_LoadFalse,
&&do_LoadNull,
&&do_FastCall,
&&do_Call,
&&do_Return,
&&do_LoadFn,
&&do_Jmp,
&&do_JmpIfFalse,
&&do_Mov,
&&do_Add,
&&do_Sub,
&&do_Mul,
&&do_Div,
&&do_Mod,
&&do_BitXor,
&&do_Equal,
&&do_NotEqual,
&&do_Greater,
&&do_Less,
&&do_GreaterEqual,
&&do_LessEqual,
&&do_Count};
Instruction inst;
// 🔥 核心分发引擎:取指 -> 直接查表并 Jump
#define DISPATCH() \
do \
{ \
inst = *(currentFrame->ip++); \
goto *dispatchTable[inst & 0xFF]; \
} while (0)
// 引擎点火!
DISPATCH();
do_Exit: {
[[unlikely]] return Value::GetNullInstance();
}
do_LoadK: {
std::uint8_t a = decodeA(inst);
std::uint16_t bx = decodeBx(inst);
currentFrame->registerBase[a] = currentFrame->getConstant(bx);
DISPATCH();
}
do_LoadTrue: {
std::uint8_t a = decodeA(inst);
currentFrame->registerBase[a] = Value::GetTrueInstance();
DISPATCH();
}
do_LoadFalse: {
std::uint8_t a = decodeA(inst);
currentFrame->registerBase[a] = Value::GetFalseInstance();
DISPATCH();
}
do_LoadNull: {
std::uint8_t a = decodeA(inst);
currentFrame->registerBase[a] = Value::GetNullInstance();
DISPATCH();
}
do_FastCall: {
std::uint8_t a = decodeA(inst);
Proto *proto = compiledModule->protos[a];
std::uint8_t baseReg = decodeB(inst);
pushFrame(proto, currentFrame->registerBase + baseReg);
DISPATCH();
}
do_Call: {
// TODO: FunctionObject 动态解包
DISPATCH();
}
do_Return: {
std::uint8_t a = decodeA(inst);
*currentFrame->registerBase = currentFrame->registerBase[a];
popFrame();
DISPATCH();
}
do_LoadFn: {
// std::uint8_t a = decodeA(inst);
// std::uint16_t bx = decodeBx(inst);
// TODO: R[a] = new FunctionObject(compiledModule->protos[bx])
DISPATCH();
}
do_Jmp: {
std::int16_t sbx = decodeSBx(inst);
currentFrame->ip += sbx;
DISPATCH();
}
do_JmpIfFalse: {
std::uint8_t a = decodeA(inst);
Value &v = currentFrame->registerBase[a];
if (!v.AsBool())
{
std::int16_t sbx = decodeSBx(inst);
currentFrame->ip += sbx;
}
DISPATCH();
}
do_Mov: {
std::uint8_t a = decodeA(inst);
std::uint16_t bx = decodeBx(inst);
currentFrame->registerBase[a] = currentFrame->registerBase[bx];
DISPATCH();
}
BINARY_ARITHMETIC_OP(Add, +);
BINARY_ARITHMETIC_OP(Sub, -);
BINARY_ARITHMETIC_OP(Mul, *);
BINARY_ARITHMETIC_OP(Div, /);
do_Mod:
do_BitXor:
assert(false && "VM: Mod and BitXor not fully implemented yet!");
DISPATCH();
BINARY_COMPARE_OP(Equal, ==);
BINARY_COMPARE_OP(NotEqual, !=);
BINARY_COMPARE_OP(Greater, >);
BINARY_COMPARE_OP(Less, <);
BINARY_COMPARE_OP(GreaterEqual, >=);
BINARY_COMPARE_OP(LessEqual, <=);
do_Count: {
assert(false && "Hit Count sentinel!");
return Value::GetNullInstance();
}
}
}; // namespace Fig
-113
View File
@@ -1,113 +0,0 @@
/*!
@file src/VM/VM.hpp
@brief 虚拟机核心执行引擎
@author PuqiAR (im@puqiar.top)
@date 2026-02-19
*/
#pragma once
#include <Compiler/Compiler.hpp>
#include <Object/Object.hpp>
#include <cassert>
#include <iostream> // debug
#include <print>
namespace Fig
{
struct CallFrame
{
Proto *proto; // 当前执行的原型
Instruction *ip; // 当前指令指针
Value *registerBase; // 寄存器起点
inline Value getConstant(std::uint16_t idx)
{
return proto->constants[idx];
}
};
class VM
{
private:
static constexpr unsigned int MAX_REGISTERS = 1024;
// 一次性分配
Value registers[MAX_REGISTERS];
DynArray<CallFrame> frames;
CallFrame *currentFrame;
public:
VM()
{
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
{
registers[i] = Value::GetNullInstance();
}
}
private:
void pushFrame(Proto *proto, Value *base)
{
frames.push_back({
proto,
proto->code.data(),
base
});
currentFrame = &frames.back();
}
void popFrame()
{
frames.pop_back();
if (!frames.empty())
{
currentFrame = &frames.back();
}
}
inline OpCode decodeOpCode(Instruction inst)
{
return static_cast<OpCode>(inst & 0xFF);
}
inline std::uint8_t decodeA(Instruction inst)
{
return (inst >> 8) & 0xFF;
}
inline std::uint16_t decodeBx(Instruction inst)
{
return (inst >> 16) & 0xFFFF;
}
inline std::uint8_t decodeB(Instruction inst)
{
return (inst >> 16) & 0xFF;
}
inline std::uint8_t decodeC(Instruction inst)
{
return (inst >> 24) & 0xFF;
}
inline std::int16_t decodeSBx(Instruction inst)
{
return static_cast<std::int16_t>(inst >> 16);
}
public:
// 执行入口:接收 Proto
Result<Value, Error> Execute(CompiledModule *);
inline void PrintRegisters()
{
std::cout << "=== Registers ===" << '\n';
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
{
Value &v = registers[i];
if (!v.IsNull())
{
std::println("[{}] {}", i, v.ToString());
}
}
}
};
} // namespace Fig
-5
View File
@@ -1,5 +0,0 @@
/*
哈哈!
VM的测试?
就是main.cpp啦!全流程执行!
*/
+48
View File
@@ -0,0 +1,48 @@
/*
src/core/build_info.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use crate::core::colors;
pub struct BuildInfo
{
pub version: &'static str,
pub git_hash: &'static str,
pub build_time: &'static str,
pub platform: String,
}
pub fn get() -> BuildInfo
{
BuildInfo
{
version: env!("CARGO_PKG_VERSION"),
git_hash: env!("GIT_HASH"),
build_time: env!("BUILD_TIME"),
platform: format!(
"{} [{} | {}]",
std::env::consts::OS,
env!("FIG_COMPILER_ID"),
std::env::consts::ARCH
),
}
}
pub fn print_header(sub_system: &str)
{
let info = get();
print!(
"{}{} v{} {}(Build {} {} {}){}\n\n",
colors::BOLD,
sub_system,
info.version,
colors::DIM,
info.git_hash,
info.build_time,
info.platform,
colors::RESET,
);
}
+32
View File
@@ -0,0 +1,32 @@
/*
src/core/colors.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
pub const RESET: &str = "\x1b[0m";
pub const BOLD: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m";
pub const YELLOW: &str = "\x1b[33m";
pub const CYAN: &str = "\x1b[36m";
pub const BRIGHT_RED: &str = "\x1b[91m";
pub const BRIGHT_CYAN: &str = "\x1b[96m";
pub const GRAY: &str = "\x1b[90m";
pub const LIGHT_GRAY: &str = "\x1b[37m";
pub const ORANGE: &str = "\x1b[38;2;217;119;6m";
pub const PURPLE: &str = "\x1b[38;2;168;85;247m"; // 亮紫色 error 主色
pub const CRITICAL_RED: &str = "\x1b[38;2;239;68;68m"; // critical
pub const ACCENT_BLUE: &str = "\x1b[38;2;59;130;246m";
// 源码行号
pub const LINE_NO: &str = "\x1b[38;2;59;130;246m"; // 蓝,和 error 区分
// 源码文本
pub const LIGHT_BLUE: &str = "\x1b[38;2;96;165;250m";
pub const SOURCE_TEXT: &str = "\x1b[38;2;180;180;180m"; // 浅灰,比 DIM 亮
+18
View File
@@ -0,0 +1,18 @@
/*
src/core/mod.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
pub mod source;
pub mod colors;
pub mod build_info;
#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum Lang
{
en_US,
zh_CN,
}
+124
View File
@@ -0,0 +1,124 @@
/*
src/core/source.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use std::path::PathBuf;
#[derive(Debug, Clone, Copy)]
pub struct SourcePosition
{
pub file_id: usize,
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct SourceRange
{
pub file_id: usize,
pub start_line: usize,
pub start_column: usize,
pub end_line: usize,
pub end_column: usize,
}
// SourceFile / SourceManager
pub struct SourceFile
{
pub path: PathBuf,
pub source: String,
line_offsets: Vec<usize>,
}
impl SourceFile
{
pub fn from_string(name: &str, source: String) -> Self
{
let line_offsets = Self::build_line_offsets(&source);
Self { path: PathBuf::from(name), source, line_offsets }
}
pub fn from_path(path: PathBuf) -> Result<Self, std::io::Error>
{
let source = std::fs::read_to_string(&path)?;
let line_offsets = Self::build_line_offsets(&source);
Ok(Self { path, source, line_offsets })
}
pub fn read(path: PathBuf) -> Result<Self, std::io::Error>
{
let source = std::fs::read_to_string(&path)?;
let line_offsets = Self::build_line_offsets(&source);
Ok(Self { path, source, line_offsets })
}
fn build_line_offsets(source: &str) -> Vec<usize>
{
std::iter::once(0)
.chain(
source
.bytes()
.enumerate()
.filter(|(_, b)| *b == b'\n')
.map(|(i, _)| i + 1),
)
.collect()
}
pub fn line_offsets(&self) -> &[usize] { &self.line_offsets }
pub fn offset_to_position(&self, offset: usize) -> (usize, usize)
{
let line = match self.line_offsets.binary_search(&offset) {
Ok(line) => line,
Err(line) => line.saturating_sub(1),
};
let col = offset - self.line_offsets[line] + 1;
(line + 1, col)
}
pub fn get_line(&self, offset: usize) -> &str
{
let (line, _) = self.offset_to_position(offset);
let start = self.line_offsets[line - 1];
let end = self.source[start..]
.find('\n')
.map(|i| start + i)
.unwrap_or(self.source.len());
&self.source[start..end]
}
}
pub struct SourceManager
{
files: Vec<SourceFile>,
}
impl SourceManager
{
pub fn new() -> Self
{
Self { files: Vec::new() }
}
pub fn add_file(&mut self, file: SourceFile) -> usize
{
let id = self.files.len();
self.files.push(file);
id
}
pub fn get(&self, id: usize) -> Option<&SourceFile>
{
self.files.get(id)
}
pub fn source(&self, id: usize) -> Option<&str>
{
self.files.get(id).map(|f| f.source.as_str())
}
}
@@ -0,0 +1,63 @@
/*
src/error/definitions/invalid_escape_sequence.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
#[derive(Debug)]
pub struct InvalidEscapeSequenceError {
file_id: usize,
start_line: usize,
start_column: usize,
escape_sequence: String,
thrower: Option<Thrower>,
}
impl InvalidEscapeSequenceError {
pub fn new(file_id: usize, start_line: usize, start_column: usize, escape_sequence: String) -> Self {
InvalidEscapeSequenceError {
file_id,
start_line,
start_column,
escape_sequence,
thrower: None,
}
}
pub fn with_thrower(mut self, thrower: Thrower) -> Self {
self.thrower = Some(thrower);
self
}
}
impl Diagnostic for InvalidEscapeSequenceError {
fn error_type(&self) -> ErrorType {
ErrorType::InvalidEscapeSequence
}
fn message(&self, lang: crate::core::Lang) -> String {
match lang {
crate::core::Lang::en_US => format!(
"Invalid escape sequence: {} at line {}, column {}",
self.escape_sequence, self.start_line, self.start_column
),
crate::core::Lang::zh_CN => format!(
"无效的转义序列: {} 在第 {} 行,第 {}",
self.escape_sequence, self.start_line, self.start_column
),
}
}
fn span(&self) -> crate::core::source::SourceRange {
crate::core::source::SourceRange {
file_id: self.file_id,
start_line: self.start_line,
start_column: self.start_column,
end_line: self.start_line,
end_column: self.start_column + self.escape_sequence.len(),
}
}
}
@@ -0,0 +1,72 @@
/*
src/error/definitions/invalid_number_literal.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use crate::core;
use crate::core::source::SourceRange;
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
#[derive(Debug)]
pub struct InvalidNumberLiteralError {
file_id: usize,
start_line: usize,
start_column: usize,
end_column: usize,
thrower: Option<Thrower>,
}
impl InvalidNumberLiteralError {
pub fn new(file_id: usize, start_line: usize, start_column: usize, end_column: usize) -> Self {
InvalidNumberLiteralError {
file_id,
start_line,
start_column,
end_column,
thrower: None,
}
}
pub fn with_thrower(mut self, thrower: Thrower) -> Self {
self.thrower = Some(thrower);
self
}
}
impl Diagnostic for InvalidNumberLiteralError {
fn error_type(&self) -> ErrorType {
ErrorType::InvalidNumberLiteral
}
fn message(&self, lang: core::Lang) -> String {
match lang {
core::Lang::en_US => {
format!(
"Invalid number literal at line {}, column {}",
self.start_line, self.start_column,
)
}
core::Lang::zh_CN => {
format!(
"无效的数字字面量,位于第 {} 行,第 {}",
self.start_line, self.start_column,
)
}
}
}
fn span(&self) -> SourceRange {
SourceRange {
file_id: self.file_id,
start_line: self.start_line,
start_column: self.start_column,
end_line: self.start_line,
end_column: self.end_column,
}
}
fn thrower(&self) -> Option<Thrower> {
self.thrower
}
}
+12
View File
@@ -0,0 +1,12 @@
/*
src/error/definitions/mod.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
pub mod unterminated_string_literal;
pub mod unexpected_character;
pub mod invalid_number_literal;
pub mod invalid_escape_sequence;
pub mod unterminated_comment;
@@ -0,0 +1,90 @@
/*
src/error/definitions/unexpected_character.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use crate::core;
use crate::core::source::SourceRange;
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
#[derive(Debug)]
pub struct UnexpectedCharacterError
{
file_id: usize,
character: char,
line: usize,
column: usize,
thrower: Option<Thrower>,
}
impl UnexpectedCharacterError
{
pub fn new(file_id: usize, character: char, line: usize, column: usize) -> Self
{
UnexpectedCharacterError
{
file_id,
character,
line,
column,
thrower: None,
}
}
pub fn with_thrower(mut self, t: Thrower) -> Self
{
self.thrower = Some(t);
self
}
}
impl Diagnostic for UnexpectedCharacterError
{
fn error_type(&self) -> ErrorType
{
ErrorType::UnexpectedCharacter
}
fn message(&self, lang: crate::core::Lang) -> String
{
let ch = match (self.character, lang) {
(' ', crate::core::Lang::zh_CN) => "<空格>",
(' ', _) => "<space>",
('\n', crate::core::Lang::zh_CN) => "<换行>",
('\n', _) => "<newline>",
('\t', crate::core::Lang::zh_CN) => "<制表符>",
('\t', _) => "<tab>",
('\r', crate::core::Lang::zh_CN) => "<回车>",
('\r', _) => "<carriage return>",
(other, crate::core::Lang::zh_CN) =>
return format!("意外的字符 '{}',位于第 {} 行,第 {}", other, self.line, self.column),
(other, _) =>
return format!("Unexpected character '{}' at line {}, column {}", other, self.line, self.column),
};
match lang {
crate::core::Lang::zh_CN =>
format!("意外的字符 {},位于第 {} 行,第 {}", ch, self.line, self.column),
_ =>
format!("Unexpected character {} at line {}, column {}", ch, self.line, self.column),
}
}
fn span(&self) -> SourceRange
{
SourceRange
{
file_id: self.file_id,
start_line: self.line,
start_column: self.column,
end_line: self.line,
end_column: self.column + 1,
}
}
fn thrower(&self) -> Option<Thrower>
{
self.thrower
}
}
@@ -0,0 +1,83 @@
/*
src/error/definitions/unterminated_comment.rs
Part of The Fig Project, under the MIT License.
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
See LICENSE for details.
*/
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
use crate::core;
use crate::core::source::SourceRange;
#[derive(Debug)]
pub struct UnterminatedCommentError
{
file_id: usize,
start_line: usize,
start_column: usize,
end_line: usize,
end_column: usize,
thrower: Option<Thrower>,
}
impl UnterminatedCommentError
{
pub fn new(file_id: usize, start_line: usize, start_column: usize, end_line: usize, end_column: usize) -> Self
{
UnterminatedCommentError
{
file_id,
start_line,
start_column,
end_line,
end_column,
thrower: None,
}
}
pub fn with_thrower(mut self, t: Thrower) -> Self
{
self.thrower = Some(t);
self
}
}
impl Diagnostic for UnterminatedCommentError
{
fn error_type(&self) -> ErrorType
{
ErrorType::UnterminatedComment
}
fn message(&self, lang: core::Lang) -> String
{
match lang
{
core::Lang::en_US =>
{
format!("Unterminated block comment starting at line {}, column {}", self.start_line, self.start_column)
}
core::Lang::zh_CN =>
{
format!("未终止的多行注释,起始于第 {} 行,第 {}", self.start_line, self.start_column)
}
}
}
fn span(&self) -> SourceRange
{
SourceRange
{
file_id: self.file_id,
start_line: self.start_line,
start_column: self.start_column,
end_line: self.end_line,
end_column: self.end_column,
}
}
fn thrower(&self) -> Option<Thrower>
{
self.thrower
}
}

Some files were not shown because too many files have changed in this diff Show More