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++ 源码。
This commit is contained in:
@@ -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
.clang-format
213
.clang-format
@@ -1,213 +0,0 @@
|
||||
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
|
||||
# 访问说明符(public、private等)的偏移
|
||||
AccessModifierOffset: -4
|
||||
|
||||
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
|
||||
# 连续赋值时,对齐所有等号
|
||||
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
|
||||
@@ -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版本发布完成!"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,4 +8,8 @@ build/
|
||||
.vscode
|
||||
.VSCodeCounter
|
||||
|
||||
/test.fig
|
||||
/test.fig
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "fig"
|
||||
version = "0.6.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
187
README.md
187
README.md
@@ -1,7 +1,5 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
@@ -13,183 +11,22 @@
|
||||
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
-~28ms%20(i5--13490f)-brightgreen)
|
||||
|
||||
**Fig** is a programming language that blends dynamic typing with optional static type annotations, built on reference-based value semantics. It offers the flexibility of scripting languages while providing optional type constraints for more robust code.
|
||||
**Fig** is a programming language that blends dynamic typing with optional static type annotations, built on reference-based value semantics.
|
||||
|
||||
> ⚠️ **Fig is currently at version 0.5.0-alpha, and its syntax and APIs are subject to change.** Feel free to try it out and provide feedback, but do not use it in production environments.
|
||||
> **v0.6.0 — Rust rewrite in progress.**
|
||||
|
||||
```fig
|
||||
// Get a feel for Fig
|
||||
var flexible = 10 // dynamic type, can be any value
|
||||
flexible = "hello" // works
|
||||
## Progress
|
||||
|
||||
var fixed := 20 // fixed to Int type
|
||||
// fixed = 3.14 // error! type mismatch
|
||||
|
||||
func greet(name) => "Hello, " + name + "!"
|
||||
println(greet("Fig")) // output: Hello, Fig!
|
||||
```
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
### 🎭 Dynamic & Static Blending
|
||||
Variables are dynamic by default (`Any` type), but you can lock their type using `:=` or `: Type` for static safety.
|
||||
|
||||
```fig
|
||||
var any; // type is Any
|
||||
any = 42; // now points to an Int
|
||||
any = [1,2,3]; // now points to a List
|
||||
|
||||
var safe := 100; // inferred as Int, can only be assigned Int values
|
||||
safe = 200; // ✅
|
||||
// safe = "oops" // ❌ compile error
|
||||
Lexer ████████████████████ 100% (43 tests, all pass)
|
||||
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
VM ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
```
|
||||
|
||||
### 🔗 Reference-Based Value Semantics
|
||||
All objects are immutable; variables are just "names" that refer to objects. Multiple variables can share the same object, and modifications to complex types (like lists) are visible to all references—this is the essence of references.
|
||||
|
||||
```fig
|
||||
var a := [1, 2, 3];
|
||||
var b := a; // b and a refer to the same list
|
||||
a[0] = 99; // modify the list content
|
||||
println(b) // output: [99, 2, 3] — b sees the change
|
||||
|
||||
// Need a true copy? Use deep copy (syntax may change)
|
||||
var c := new List{a}; // deep copy of a
|
||||
c[0] = 0;
|
||||
println(a) // still [99, 2, 3]
|
||||
```
|
||||
|
||||
### 🏗 Object-Oriented Features (OOP)
|
||||
Fig provides lightweight OOP support based on structs, with polymorphism via interfaces.
|
||||
|
||||
- **Structs as classes**: defined with `struct`, can have fields and methods.
|
||||
- **Access control**: `public` keyword; fields are private by default.
|
||||
- **Concise construction**:
|
||||
```fig
|
||||
var p1 := new Point{1, 2}; // positional
|
||||
var p2 := new Point{x: 2, y: 3}; // named
|
||||
var x := 5, y := 10;
|
||||
var p3 := new Point{y, x}; // shorthand, auto field matching
|
||||
```
|
||||
- **Interfaces and implementations**:
|
||||
```fig
|
||||
interface Drawable { draw() -> String; }
|
||||
struct Circle { radius: Double }
|
||||
impl Drawable for Circle {
|
||||
draw() => "⚪ radius: " + radius;
|
||||
}
|
||||
```
|
||||
- **No inheritance, polymorphism via interfaces** (currently implicit `this`, may change to explicit `self.xxx` in the future).
|
||||
|
||||
### 🧩 Functional Features
|
||||
Functions are first-class citizens, with support for closures and concise arrow syntax.
|
||||
|
||||
```fig
|
||||
func multiplier(factor) {
|
||||
return func (n) => n * factor; // closure (upvalue)
|
||||
}
|
||||
|
||||
var double = multiplier(2);
|
||||
println(double(5)) // output: 10
|
||||
```
|
||||
|
||||
### ⚡ High Performance
|
||||
**Recursive fib(30) takes only 28ms** (i5-13490f, Windows 11, DDR4 2667, clang 21.1.8, libc++, C++23) — excellent performance among dynamic languages.
|
||||
|
||||
Fig achieves high performance through a series of low-level optimizations:
|
||||
- **Register-based VM**: replaces the tree-walk interpreter, dramatically improving execution speed.
|
||||
- **FastCall optimization**: global ordinary functions can be called directly as prototypes, avoiding runtime unwrapping overhead (traditional calls require checking if an object is a function and unwrapping it).
|
||||
- **Window Slicing** (originated from Lua): a stack sliding window technique that efficiently manages function calls and closures (upvalues), reducing memory allocations.
|
||||
- **Upvalue mechanism**: lightweight closure implementation, making functional programming performant.
|
||||
- **Computed Goto**: leverages GCC/Clang extensions to speed up bytecode dispatch.
|
||||
- **NaN-Boxing**: efficient storage and type tagging to boost dynamic typing performance.
|
||||
|
||||
#### Performance Comparison
|
||||
Rough comparison of recursive fib(30) execution times (environment may vary, for reference only):
|
||||
|
||||
| Language Type | Language | Time (ms) | Notes |
|
||||
|---------------|----------------|-----------|-------|
|
||||
| AOT compiled | C (clang -O2) | ~0.05 | Nanoseconds, as a speed reference |
|
||||
| AOT compiled | Rust (--release) | ~0.06 | Same as above |
|
||||
| **Dynamic** | **Fig** | **~28** | **Register VM + optimizations** |
|
||||
| Dynamic | Lua 5.4 | ~35 | Classic dynamic language |
|
||||
| Dynamic | Python 3.11 | ~450 | CPython |
|
||||
| Dynamic | Node.js 20 | ~60 | V8 engine |
|
||||
| Dynamic | Ruby 3.2 | ~800 | CRuby |
|
||||
|
||||
*Fig performs admirably among dynamic languages, approaching Lua and Node.js, and significantly outperforming Python and Ruby.*
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Installation
|
||||
|
||||
#### Option 1: Download Pre-built Binaries
|
||||
Download the binary for your platform from the [main repository Releases](https://git.fig-lang.cn/PuqiAR/Fig/releases) or the [GitHub mirror](https://github.com/PuqiAR/Fig/releases), extract it, and add it to your PATH.
|
||||
|
||||
#### Option 2: Build from Source with xmake
|
||||
```bash
|
||||
# Clone the main repository (GitHub is a mirror)
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
cd Fig
|
||||
|
||||
# Install xmake if you haven't: https://xmake.io
|
||||
|
||||
# Build (must use clang because computed goto requires compiler support)
|
||||
xmake f --toolchain=clang
|
||||
xmake
|
||||
|
||||
# After building, the binary is in the build/ directory
|
||||
```
|
||||
|
||||
### Run Your First Fig Program
|
||||
Create a file `hello.fig`:
|
||||
```fig
|
||||
import std.io; // io module must be imported; println and others reside here
|
||||
println("Hello, Fig!");
|
||||
```
|
||||
Run it:
|
||||
```bash
|
||||
./build/fig hello.fig
|
||||
```
|
||||
Or if you added `fig` to your PATH:
|
||||
```bash
|
||||
fig hello.fig
|
||||
```
|
||||
|
||||
## 📊 Current Status & Roadmap
|
||||
|
||||
| Status | Module | Description |
|
||||
|--------|----------------------------|-------------|
|
||||
| ✅ | Frontend | Lexer, parser, AST, semantic analysis |
|
||||
| ✅ | Core semantics | Variables, functions, closures, structs, interfaces, type system |
|
||||
| ✅ | Built-in types | 13 built-in types (Any, Null, Int, String, Bool, Double, Function, StructType, StructInstance, List, Map, Module, InterfaceType) |
|
||||
| 🚧 | Register VM | Original tree-walk interpreter deprecated; VM in development |
|
||||
| 🚧 | Garbage Collection | Basic GC implemented, optimizing |
|
||||
| 📝 | Standard Library | IO preliminarily available; more libraries to come |
|
||||
| 📝 | FFI | Foreign function interface (C ABI) |
|
||||
| 📝 | LSP | Language Server Protocol support |
|
||||
|
||||
## 📚 Documentation & Community
|
||||
|
||||
- **Documentation**: Check the [`/docs`](./docs) directory (continuously updated)
|
||||
- **Example Code**: [`/ExampleCodes`](./ExampleCodes) contains various usage demonstrations
|
||||
- **Contributing**: Issues and PRs are welcome at the main repository
|
||||
- Report bugs
|
||||
- Discuss features
|
||||
- Improve documentation
|
||||
- **License**: MIT © PuqiAR
|
||||
- **Author**: PuqiAR · [im@puqiar.top](mailto:im@puqiar.top)
|
||||
- **Links**:
|
||||
- [Main Repository](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||
- [GitHub Mirror](https://github.com/PuqiAR/Fig)
|
||||
|
||||
---
|
||||
|
||||
**Fig** is evolving rapidly. If you're intrigued, give it a try or join us in shaping its future!
|
||||
187
README_zh-CN.md
187
README_zh-CN.md
@@ -1,7 +1,5 @@
|
||||
# Fig
|
||||
|
||||
## tmd赶工代码质量太差了暑假我要重构
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||
@@ -13,183 +11,22 @@
|
||||
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
-~28ms%20(i5--13490f)-brightgreen)
|
||||
|
||||
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。它既保留了脚本语言的灵活性,又提供了可选的类型约束,让代码更健壮。
|
||||
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。
|
||||
|
||||
> ⚠️ **Fig 当前为 0.5.0-alpha 版本,语法和 API 仍可能发生变动。** 欢迎试用和反馈,但请勿用于生产环境。
|
||||
> **v0.6.0 — Rust 重写进行中。**
|
||||
|
||||
```fig
|
||||
// 试试 Fig 的感觉
|
||||
var flexible = 10 // 动态类型,可以是任意值
|
||||
flexible = "hello" // 没问题
|
||||
## 进度
|
||||
|
||||
var fixed := 20 // 固定为 Int 类型
|
||||
// fixed = 3.14 // 错误!类型不匹配
|
||||
|
||||
func greet(name) => "Hello, " + name + "!"
|
||||
println(greet("Fig")) // 输出: Hello, Fig!
|
||||
```
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
### 🎭 动态与静态的融合
|
||||
变量默认是动态的(`Any` 类型),但你可以随时用 `:=` 或 `: Type` 锁定类型,享受静态检查的安全感。
|
||||
|
||||
```fig
|
||||
var any; // 类型为 Any
|
||||
any = 42; // 现在指向 Int
|
||||
any = [1,2,3]; // 现在指向 List
|
||||
|
||||
var safe := 100; // 推断为 Int,之后只能赋 Int 值
|
||||
safe = 200; // ✅
|
||||
// safe = "oops" // ❌ 编译错误
|
||||
Lexer ████████████████████ 100% (43 测试, 全部通过)
|
||||
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
VM ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||
```
|
||||
|
||||
### 🔗 基于引用的值语义
|
||||
所有对象都是不可变的,变量只是指向对象的“名字”。多个变量可以共享同一个对象,修改操作会创建新对象,但复杂类型(如列表)的修改会反映在所有引用上——这正是引用的含义。
|
||||
|
||||
```fig
|
||||
var a := [1, 2, 3];
|
||||
var b := a; // b 和 a 指向同一个列表
|
||||
a[0] = 99; // 修改列表内容
|
||||
println(b) // 输出: [99, 2, 3] —— 因为 b 也看到了变化
|
||||
|
||||
// 需要真正的副本?用深拷贝 (语法可能变动)
|
||||
var c := new List{a}; // 深拷贝 a
|
||||
c[0] = 0;
|
||||
println(a) // 仍是 [99, 2, 3]
|
||||
```
|
||||
|
||||
### 🏗 面向对象特性 (OOP)
|
||||
Fig 提供基于结构体的轻量面向对象支持,通过接口实现多态。
|
||||
|
||||
- **结构体即类**:用 `struct` 定义,可包含字段和方法
|
||||
- **访问控制**:`public` 关键字,默认为私有
|
||||
- **简洁构造**:
|
||||
```fig
|
||||
var p1 := new Point{1, 2}; // 位置参数
|
||||
var p2 := new Point{x: 2, y: 3}; // 命名参数
|
||||
var x := 5, y := 10;
|
||||
var p3 := new Point{y, x}; // 变量名简写,自动匹配字段
|
||||
```
|
||||
- **接口与实现**:
|
||||
```fig
|
||||
interface Drawable { draw() -> String; }
|
||||
struct Circle { radius: Double }
|
||||
impl Drawable for Circle {
|
||||
draw() => "⚪ 半径: " + radius;
|
||||
}
|
||||
```
|
||||
- **无继承,用接口实现多态**(当前为隐式 `this`,未来可能改为显式 `self.xxx`)
|
||||
|
||||
### 🧩 函数式特性
|
||||
函数是一等公民,支持闭包和简洁的箭头语法。
|
||||
|
||||
```fig
|
||||
func multiplier(factor) {
|
||||
return func (n) => n * factor; // 闭包(upvalue)
|
||||
}
|
||||
|
||||
var double = multiplier(2);
|
||||
println(double(5)) // 输出: 10
|
||||
```
|
||||
|
||||
### ⚡ 高性能实现
|
||||
**递归 fib(30) 仅需 28ms** (i5-13490f, Windows 11, DDR4 2667, clang 21.1.8, libc++, C++23) —— 在动态语言中表现优异。
|
||||
|
||||
Fig 通过一系列底层优化实现高性能:
|
||||
- **寄存器虚拟机**:替代树遍历解释器,执行效率大幅提升。
|
||||
- **FastCall 优化**:全局普通函数直接调用原型(proto),避免运行时解包开销(传统调用需检查是否为函数对象并解包)。
|
||||
- **Window Slicing**(源自 Lua):栈滑动窗口技术,高效管理函数调用和闭包(upvalue),减少内存分配。
|
||||
- **Upvalue 机制**:轻量闭包实现,让函数式编程无性能负担。
|
||||
- **Computed Goto**:利用 GCC/Clang 扩展,加速字节码分发。
|
||||
- **NaN-Boxing**:高效存储和类型判断,提升动态类型性能。
|
||||
|
||||
#### 性能对比
|
||||
以下为递归计算 fib(30) 的粗略对比(不同环境可能存在差异,仅供参考):
|
||||
|
||||
| 语言类型 | 语言 | 时间 (ms) | 备注 |
|
||||
|--------|------|----------|------|
|
||||
| AOT 编译 | C (clang -O2) | ~0.05 | 纳秒级,作为速度参照 |
|
||||
| AOT 编译 | Rust (--release) | ~0.06 | 同上 |
|
||||
| **动态语言** | **Fig** | **~28** | **寄存器 VM + 上述优化** |
|
||||
| 动态语言 | Lua 5.4 | ~35 | 经典的动态语言 |
|
||||
| 动态语言 | Python 3.11 | ~450 | CPython |
|
||||
| 动态语言 | Node.js 20 | ~60 | V8 引擎 |
|
||||
| 动态语言 | Ruby 3.2 | ~800 | CRuby |
|
||||
|
||||
*Fig 在动态语言阵营中表现出色,接近 Lua 和 Node.js 的水平,远超 Python 和 Ruby。*
|
||||
|
||||
## 🚀 快速上手
|
||||
|
||||
### 安装
|
||||
|
||||
#### 方法一:下载预编译二进制
|
||||
从 [主仓库 Releases](https://git.fig-lang.cn/PuqiAR/Fig/releases) 或 [GitHub 镜像](https://github.com/PuqiAR/Fig/releases) 下载对应平台的二进制,解压后加入 PATH。
|
||||
|
||||
#### 方法二:使用 xmake 从源码编译
|
||||
```bash
|
||||
# 克隆主仓库(GitHub 为镜像)
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
cd Fig
|
||||
|
||||
# 安装 xmake(如未安装):https://xmake.io
|
||||
|
||||
# 编译(必须用 clang,因为 computed goto 需要编译器支持)
|
||||
xmake f --toolchain=clang
|
||||
xmake
|
||||
|
||||
# 编译完成后,二进制位于 build/ 目录下
|
||||
```
|
||||
|
||||
### 运行第一个 Fig 程序
|
||||
创建文件 `hello.fig`:
|
||||
```fig
|
||||
import std.io; // io 模块必须导入,println 等函数位于其中
|
||||
println("Hello, Fig!");
|
||||
```
|
||||
运行:
|
||||
```bash
|
||||
./build/fig hello.fig
|
||||
```
|
||||
或已加入 PATH:
|
||||
```bash
|
||||
fig hello.fig
|
||||
```
|
||||
|
||||
## 📊 当前状态与路线图
|
||||
|
||||
| 状态 | 模块 | 说明 |
|
||||
|------|------|------|
|
||||
| ✅ | 前端 | 词法/语法分析、AST、语义分析 |
|
||||
| ✅ | 核心语义 | 变量、函数、闭包、结构体、接口、类型系统 |
|
||||
| ✅ | 内置类型 | 13 种内置类型(Any, Null, Int, String, Bool, Double, Function, StructType, StructInstance, List, Map, Module, InterfaceType) |
|
||||
| 🚧 | 寄存器虚拟机 | 原树遍历解释器已废弃,VM 开发中 |
|
||||
| 🚧 | 垃圾回收 | 基础 GC 已实现,正在优化 |
|
||||
| 📝 | 标准库 | IO 已初步可用,更多库待完善 |
|
||||
| 📝 | FFI | 外部函数接口(C ABI) |
|
||||
| 📝 | LSP | 语言服务器协议支持 |
|
||||
|
||||
## 📚 文档与社区
|
||||
|
||||
- **文档**:查看 [`/docs`](./docs) 目录(持续更新)
|
||||
- **示例代码**:[`/ExampleCodes`](./ExampleCodes) 包含各种用法演示
|
||||
- **贡献**:欢迎提交 Issue 或 PR(主仓库)
|
||||
- 报告 bug
|
||||
- 讨论特性
|
||||
- 改进文档
|
||||
- **许可证**:MIT © PuqiAR
|
||||
- **作者**:PuqiAR · [im@puqiar.top](mailto:im@puqiar.top)
|
||||
- **相关链接**:
|
||||
- [主仓库](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||
- [GitHub 镜像](https://github.com/PuqiAR/Fig)
|
||||
|
||||
---
|
||||
|
||||
**Fig** 还在快速迭代中,如果你对它感兴趣,不妨试一试,或者加入我们一起塑造它的未来!
|
||||
63
build.rs
Normal file
63
build.rs
Normal 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());
|
||||
|
||||
// 编译器 ID:macOS 上用 clang/LLVM,Linux 上用 GCC,Windows 上用 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)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
-std=c++2b
|
||||
-static
|
||||
-stdlib=libc++
|
||||
@@ -1,33 +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/LambdaExpr.hpp>
|
||||
#include <Ast/Expr/LiteralExpr.hpp>
|
||||
#include <Ast/Expr/MemberExpr.hpp>
|
||||
#include <Ast/Expr/NewExpr.hpp>
|
||||
#include <Ast/Expr/PrefixExpr.hpp>
|
||||
#include <Ast/Expr/TernaryExpr.hpp>
|
||||
#include <Ast/Expr/PostfixExpr.hpp>
|
||||
|
||||
#include <Ast/Stmt/ControlFlowStmts.hpp>
|
||||
#include <Ast/Stmt/ExprStmt.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
#include <Ast/Stmt/IfStmt.hpp>
|
||||
#include <Ast/Stmt/ImplStmt.hpp>
|
||||
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||
#include <Ast/Stmt/ForStmt.hpp>
|
||||
#include <Ast/Stmt/ImportStmt.hpp>
|
||||
#include <Ast/Stmt/VarDecl.hpp>
|
||||
#include <Ast/Stmt/WhileStmt.hpp>
|
||||
#include <Ast/TypeExpr.hpp>
|
||||
167
src/Ast/Base.hpp
167
src/Ast/Base.hpp
@@ -1,167 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Base.hpp
|
||||
@brief AstNode基类定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#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,
|
||||
MemberExpr, // obj.prop
|
||||
NewExpr, // new Point{}
|
||||
LambdaExpr,
|
||||
TernaryExpr, // cond ? then : else
|
||||
PostfixExpr, // expr++ / expr--
|
||||
|
||||
/* Statements */
|
||||
ExprStmt,
|
||||
VarDecl,
|
||||
IfStmt,
|
||||
ElseIfStmt,
|
||||
WhileStmt,
|
||||
FnDefStmt,
|
||||
StructDefStmt,
|
||||
InterfaceDefStmt,
|
||||
ImplStmt, // impl Document for File {}
|
||||
ReturnStmt,
|
||||
BreakStmt,
|
||||
ContinueStmt,
|
||||
ForStmt, // for loop
|
||||
ImportStmt, // import
|
||||
|
||||
/* Type Expressions */
|
||||
TypeExpr,
|
||||
NamedTypeExpr, // 废弃,用 IdentiExpr/MemberExpr/ApplyExpr 替代
|
||||
NullableTypeExpr, // 废弃,用 NullableExpr 替代
|
||||
FnTypeExpr,
|
||||
ApplyExpr, // 泛型实例化: List<Int>
|
||||
NullableExpr, // 可空后缀: Int?
|
||||
};
|
||||
|
||||
struct AstNode
|
||||
{
|
||||
AstType type = AstType::AstNode;
|
||||
SourceLocation location;
|
||||
|
||||
virtual String toString() const = 0;
|
||||
virtual ~AstNode() {};
|
||||
};
|
||||
|
||||
struct Expr : public AstNode
|
||||
{
|
||||
// 语义分析后填充
|
||||
Type resolvedType;
|
||||
|
||||
Expr()
|
||||
{
|
||||
type = AstType::Expr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Stmt : public AstNode
|
||||
{
|
||||
bool isPublic = false;
|
||||
Stmt()
|
||||
{
|
||||
type = AstType::Stmt;
|
||||
}
|
||||
};
|
||||
|
||||
struct Program final : public AstNode
|
||||
{
|
||||
DynArray<Stmt *> nodes;
|
||||
Program()
|
||||
{
|
||||
type = AstType::Program;
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return "<Program>";
|
||||
}
|
||||
};
|
||||
|
||||
struct BlockStmt final : public Stmt
|
||||
{
|
||||
DynArray<Stmt *> nodes;
|
||||
BlockStmt()
|
||||
{
|
||||
type = AstType::BlockStmt;
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return "<BlockStmt>";
|
||||
}
|
||||
};
|
||||
|
||||
// --- Type Expressions (inherit Expr — 类型即值) ---
|
||||
|
||||
struct TypeExpr : public Expr
|
||||
{
|
||||
TypeExpr() { type = AstType::TypeExpr; }
|
||||
virtual ~TypeExpr() = default;
|
||||
};
|
||||
|
||||
// ApplyExpr: 泛型实例化,List<Int> → ApplyExpr(base, [Int])
|
||||
struct ApplyExpr final : public Expr
|
||||
{
|
||||
Expr *base; // 基础类型表达式
|
||||
DynArray<Expr *> args; // 泛型参数
|
||||
|
||||
ApplyExpr() { type = AstType::ApplyExpr; }
|
||||
ApplyExpr(Expr *_base, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
base(_base), args(std::move(_args))
|
||||
{
|
||||
type = AstType::ApplyExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
String s = base->toString() + "<";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (i) s += ", ";
|
||||
s += args[i]->toString();
|
||||
}
|
||||
s += ">";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// NullableExpr: 可空后缀 Int? → NullableExpr(Int)
|
||||
struct NullableExpr final : public Expr
|
||||
{
|
||||
Expr *inner;
|
||||
|
||||
NullableExpr() { type = AstType::NullableExpr; }
|
||||
NullableExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override
|
||||
{
|
||||
return inner->toString() + "?";
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,61 +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, SourceLocation _location) : callee(_callee), args(std::move(_args))
|
||||
{
|
||||
type = AstType::CallExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<CallExpr: '{}{}'>", callee->toString(), args.toString());
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/IdentiExpr.hpp
|
||||
@brief 标识符表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct IdentiExpr final : public Expr
|
||||
{
|
||||
String name;
|
||||
Symbol *resolvedSymbol = nullptr; // 语义分析后填充,Compiler 唯一的依赖
|
||||
|
||||
IdentiExpr()
|
||||
{
|
||||
type = AstType::IdentiExpr;
|
||||
}
|
||||
IdentiExpr(String _name, SourceLocation _location) : name(std::move(_name))
|
||||
{
|
||||
type = AstType::IdentiExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<IdentiExpr '{}'>", name);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,70 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/LambdaExpr.hpp
|
||||
@brief Lambda表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct LambdaExpr final : public Expr
|
||||
{
|
||||
// func (params) [-> return type] ([=> expr] / [ {stmt} ])
|
||||
|
||||
DynArray<Param *> params;
|
||||
Expr *returnType;
|
||||
AstNode *body; // expr/blockstmt
|
||||
bool isExprBody;
|
||||
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
LambdaExpr()
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
}
|
||||
|
||||
LambdaExpr(
|
||||
DynArray<Param *> _params,
|
||||
Expr *_returnType,
|
||||
AstNode *_body,
|
||||
bool _isExprBody,
|
||||
SourceLocation _location) :
|
||||
params(std::move(_params)),
|
||||
returnType(_returnType),
|
||||
body(_body),
|
||||
isExprBody(_isExprBody)
|
||||
{
|
||||
type = AstType::LambdaExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String specifying = "<LambdaExpr 'func (";
|
||||
for (auto &p : params)
|
||||
{
|
||||
if (p != params.front())
|
||||
{
|
||||
specifying += ", ";
|
||||
}
|
||||
specifying += p->toString();
|
||||
}
|
||||
if (isExprBody)
|
||||
{
|
||||
specifying += ") => ";
|
||||
specifying += body->toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
specifying += ") {";
|
||||
specifying += body->toString();
|
||||
specifying.push_back(U'}');
|
||||
}
|
||||
specifying += "'>";
|
||||
return specifying;
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,27 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/LiteralExpr.hpp
|
||||
@brief 字面量表达式定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct LiteralExpr final : public Expr
|
||||
{
|
||||
Token literal;
|
||||
|
||||
LiteralExpr() { type = AstType::LiteralExpr; }
|
||||
LiteralExpr(const Token &_literal, SourceLocation _location) : literal(_literal)
|
||||
{
|
||||
type = AstType::LiteralExpr;
|
||||
location = std::move(_location);
|
||||
}
|
||||
|
||||
virtual String toString() const override {
|
||||
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(literal.type));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/MemberExpr.hpp
|
||||
@brief 成员访问表达式定义:obj.member
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct MemberExpr final : public Expr
|
||||
{
|
||||
Expr *target; // 访问对象
|
||||
String name; // 成员名字
|
||||
|
||||
MemberExpr()
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
|
||||
MemberExpr(Expr *_t, String _n, SourceLocation _loc) : target(_t), name(std::move(_n))
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<MemberExpr {}.{}>", target->toString(), name);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,50 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/ObjectInitExpr.hpp
|
||||
@brief 对象初始化表达式 AST 定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct NewExpr final : public Expr
|
||||
{
|
||||
struct Arg
|
||||
{
|
||||
String name;
|
||||
Expr *value;
|
||||
};
|
||||
Expr *typeExpr;
|
||||
DynArray<Arg> args;
|
||||
|
||||
NewExpr()
|
||||
{
|
||||
type = AstType::NewExpr;
|
||||
}
|
||||
NewExpr(Expr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||
typeExpr(_te), args(std::move(_args))
|
||||
{
|
||||
type = AstType::NewExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String res = "<NewExpr 'new " + typeExpr->toString() + "{";
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
if (!args[i].name.empty())
|
||||
res += args[i].name + ": ";
|
||||
res += args[i].value->toString();
|
||||
if (i < args.size() - 1)
|
||||
res += ", ";
|
||||
}
|
||||
res += "}'>";
|
||||
return res;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,35 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/PostfixExpr.hpp
|
||||
@brief expr++ / expr--
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Operator.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct PostfixExpr final : public Expr
|
||||
{
|
||||
UnaryOperator op;
|
||||
Expr *operand;
|
||||
|
||||
PostfixExpr() { type = AstType::PostfixExpr; }
|
||||
|
||||
PostfixExpr(UnaryOperator _op, Expr *_operand) :
|
||||
op(_op), operand(_operand)
|
||||
{
|
||||
type = AstType::PostfixExpr;
|
||||
location = _operand->location;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<PostfixExpr: {} '{}'>",
|
||||
operand->toString(), magic_enum::enum_name(op));
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Expr/TernaryExpr.hpp
|
||||
@brief cond ? then : else
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct TernaryExpr final : public Expr
|
||||
{
|
||||
Expr *cond;
|
||||
Expr *thenExpr;
|
||||
Expr *elseExpr;
|
||||
|
||||
TernaryExpr() { type = AstType::TernaryExpr; }
|
||||
|
||||
TernaryExpr(Expr *_cond, Expr *_then, Expr *_else, SourceLocation _loc) :
|
||||
cond(_cond), thenExpr(_then), elseExpr(_else)
|
||||
{
|
||||
type = AstType::TernaryExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<TernaryExpr: {} ? {} : {}>",
|
||||
cond->toString(), thenExpr->toString(), elseExpr->toString());
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,193 +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},
|
||||
{TokenType::DoublePlus, UnaryOperator::Increment},
|
||||
{TokenType::DoubleMinus, UnaryOperator::Decrement},
|
||||
};
|
||||
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::DoubleAmpersand, BinaryOperator::LogicalAnd},
|
||||
{TokenType::DoublePipe, 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::Caret, BinaryOperator::BitXor},
|
||||
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||
|
||||
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||
{TokenType::Ampersand, BinaryOperator::BitAnd},
|
||||
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||
|
||||
{TokenType::As, BinaryOperator::As},
|
||||
};
|
||||
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},
|
||||
{UnaryOperator::Increment, 20001},
|
||||
{UnaryOperator::Decrement, 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::As, 2100},
|
||||
|
||||
{BinaryOperator::ShiftLeft, 3000},
|
||||
{BinaryOperator::ShiftRight, 3000},
|
||||
|
||||
{BinaryOperator::Add, 4000},
|
||||
{BinaryOperator::Subtract, 4000},
|
||||
{BinaryOperator::Multiply, 4500},
|
||||
{BinaryOperator::Divide, 4500},
|
||||
{BinaryOperator::Modulo, 4500},
|
||||
|
||||
{BinaryOperator::Power, 5000},
|
||||
};
|
||||
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;
|
||||
|
||||
case BinaryOperator::As: 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
|
||||
@@ -1,96 +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, // 取引用 &
|
||||
Increment, // ++
|
||||
Decrement, // --
|
||||
|
||||
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, // 右移
|
||||
|
||||
As, // as
|
||||
|
||||
// 成员访问
|
||||
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
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/FnDefStmt.hpp
|
||||
@brief 函数定义 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Sema/Environment.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Param : public AstNode {
|
||||
String name;
|
||||
Expr *typeSpecifier;
|
||||
Expr *defaultValue;
|
||||
Type resolvedType;
|
||||
Param() { type = AstType::AstNode; }
|
||||
virtual ~Param() = default;
|
||||
};
|
||||
|
||||
struct PosParam final : public Param {
|
||||
PosParam(String _n, Expr *_ts, Expr *_dv, SourceLocation _loc) {
|
||||
name = std::move(_n); typeSpecifier = _ts; defaultValue = _dv; location = std::move(_loc);
|
||||
}
|
||||
virtual String toString() const override { return name; }
|
||||
};
|
||||
|
||||
struct FnDefStmt final : public Stmt {
|
||||
String name;
|
||||
DynArray<Param *> params;
|
||||
Expr *returnTypeSpecifier;
|
||||
BlockStmt *body;
|
||||
Type resolvedReturnType;
|
||||
Symbol *resolvedSymbol = nullptr; // 连接物理符号
|
||||
|
||||
int protoIndex = -1; // 在CompiledModule扁平化protos的下标
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
|
||||
FnDefStmt() { type = AstType::FnDefStmt; }
|
||||
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, Expr *_rt, BlockStmt *_b, SourceLocation _loc)
|
||||
: name(std::move(_n)), params(std::move(_pa)), returnTypeSpecifier(_rt), body(_b)
|
||||
{
|
||||
type = AstType::FnDefStmt; isPublic = _p; location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override {
|
||||
return std::format("<FnDefStmt '{}'>", name);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ForStmt.hpp
|
||||
@brief for loop
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ForStmt final : public Stmt
|
||||
{
|
||||
Stmt *init;
|
||||
Expr *cond;
|
||||
Expr *step;
|
||||
BlockStmt *body;
|
||||
|
||||
ForStmt() { type = AstType::ForStmt; }
|
||||
|
||||
ForStmt(Stmt *_init, Expr *_cond, Expr *_step, BlockStmt *_body, SourceLocation _loc) :
|
||||
init(_init), cond(_cond), step(_step), body(_body)
|
||||
{
|
||||
type = AstType::ForStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override { return "<ForStmt>"; }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -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
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ImplStmt.hpp
|
||||
@brief 实现块 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ImplStmt final : public Stmt
|
||||
{
|
||||
Expr *interfaceType;
|
||||
Expr *structType;
|
||||
DynArray<FnDefStmt *> methods;
|
||||
|
||||
ImplStmt(Expr *_it, Expr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
|
||||
interfaceType(_it), structType(_st), methods(std::move(_m))
|
||||
{
|
||||
type = AstType::ImplStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail =
|
||||
(interfaceType ? interfaceType->toString() + " for " : "") + structType->toString();
|
||||
return std::format("<ImplStmt '{}'>", detail);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,33 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/ImportStmt.hpp
|
||||
@brief import
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ImportStmt final : public Stmt
|
||||
{
|
||||
String path;
|
||||
bool isFileImport;
|
||||
|
||||
ImportStmt() { type = AstType::ImportStmt; }
|
||||
|
||||
ImportStmt(String _path, bool _isFileImport, SourceLocation _loc) :
|
||||
path(std::move(_path)), isFileImport(_isFileImport)
|
||||
{
|
||||
type = AstType::ImportStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<ImportStmt '{}'{}>", path, isFileImport ? " [file]" : "");
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,39 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/InterfaceDefStmt.hpp
|
||||
@brief 接口定义 AST 节点
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct InterfaceDefStmt final : public Stmt
|
||||
{
|
||||
struct Method
|
||||
{
|
||||
String name;
|
||||
DynArray<Expr*> params;
|
||||
Expr *retType;
|
||||
SourceLocation location;
|
||||
};
|
||||
|
||||
bool isPublic;
|
||||
String name;
|
||||
DynArray<Method> methods;
|
||||
|
||||
InterfaceDefStmt() { type = AstType::InterfaceDefStmt; }
|
||||
|
||||
InterfaceDefStmt(bool _p, String _n, DynArray<Method> _m, SourceLocation _loc)
|
||||
: isPublic(_p), name(std::move(_n)), methods(std::move(_m))
|
||||
{
|
||||
type = AstType::InterfaceDefStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override { return "<InterfaceDefStmt>"; }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,59 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/Stmt/StructDefStmt.hpp
|
||||
@brief 结构体定义 AST 节点
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Ast/Base.hpp>
|
||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct StructDefStmt final : public Stmt
|
||||
{
|
||||
struct Field
|
||||
{
|
||||
bool isPublic;
|
||||
bool typeInfer;
|
||||
|
||||
String name;
|
||||
Expr *type;
|
||||
Expr *initExpr;
|
||||
};
|
||||
bool isPublic;
|
||||
String name;
|
||||
DynArray<String> typeParameters;
|
||||
DynArray<Field> fields;
|
||||
DynArray<FnDefStmt *> methods;
|
||||
|
||||
StructDefStmt()
|
||||
{
|
||||
type = AstType::StructDefStmt;
|
||||
}
|
||||
StructDefStmt(bool _p,
|
||||
String _n,
|
||||
DynArray<String> _tp,
|
||||
DynArray<Field> _f,
|
||||
DynArray<FnDefStmt *> _m,
|
||||
SourceLocation _loc) :
|
||||
isPublic(_p),
|
||||
name(std::move(_n)),
|
||||
typeParameters(std::move(_tp)),
|
||||
fields(std::move(_f)),
|
||||
methods(std::move(_m))
|
||||
{
|
||||
type = AstType::StructDefStmt;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = name;
|
||||
if (!typeParameters.empty())
|
||||
{
|
||||
detail += "<...>";
|
||||
}
|
||||
return std::format("<StructDefStmt '{}'>", detail);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -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;
|
||||
Expr *typeSpecifier;
|
||||
bool isInfer; // 是否用了 := 类型推断
|
||||
Expr *initExpr;
|
||||
|
||||
int localId = -1;
|
||||
|
||||
VarDecl()
|
||||
{
|
||||
type = AstType::VarDecl;
|
||||
}
|
||||
|
||||
VarDecl(bool _isPublic, String _name, Expr *_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
|
||||
@@ -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
|
||||
@@ -1,100 +0,0 @@
|
||||
/*!
|
||||
@file src/Ast/TypeExpr.hpp
|
||||
@brief 类型表达式 AST 定义:支持泛型与空安全
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Base.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct NamedTypeExpr final : public TypeExpr
|
||||
{
|
||||
DynArray<String> path;
|
||||
DynArray<Expr *> arguments;
|
||||
|
||||
NamedTypeExpr()
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
}
|
||||
NamedTypeExpr(DynArray<String> _p, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||
path(std::move(_p)), arguments(std::move(_args))
|
||||
{
|
||||
type = AstType::NamedTypeExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = "";
|
||||
for (size_t i = 0; i < path.size(); ++i)
|
||||
{
|
||||
detail += path[i];
|
||||
if (i < path.size() - 1)
|
||||
detail += ".";
|
||||
}
|
||||
if (!arguments.empty())
|
||||
{
|
||||
detail += "<";
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
detail += arguments[i]->toString();
|
||||
if (i < arguments.size() - 1)
|
||||
detail += ", ";
|
||||
}
|
||||
detail += ">";
|
||||
}
|
||||
return std::format("<NamedTypeExpr '{}'>", detail);
|
||||
}
|
||||
};
|
||||
|
||||
struct NullableTypeExpr final : public TypeExpr
|
||||
{
|
||||
Expr *inner;
|
||||
|
||||
NullableTypeExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||
{
|
||||
type = AstType::NullableTypeExpr;
|
||||
location = std::move(_loc);
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
return std::format("<NullableTypeExpr '{}?'>", inner->toString());
|
||||
}
|
||||
};
|
||||
|
||||
struct FnTypeExpr final : public TypeExpr
|
||||
{
|
||||
// func (paratypes...) -> return_type
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
Expr *returnType;
|
||||
|
||||
FnTypeExpr(DynArray<Expr *> _paraTypes, Expr *_returnType) :
|
||||
paraTypes(std::move(_paraTypes)), returnType(_returnType)
|
||||
{
|
||||
type = AstType::FnTypeExpr;
|
||||
}
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
String detail = "<FnTypeExpr 'func (";
|
||||
|
||||
for (auto &pt : paraTypes)
|
||||
{
|
||||
if (pt != paraTypes.front())
|
||||
{
|
||||
detail += ", ";
|
||||
}
|
||||
detail += pt->toString();
|
||||
}
|
||||
detail += ") -> ";
|
||||
detail += returnType->toString();
|
||||
detail += "'>";
|
||||
|
||||
return detail;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,122 +0,0 @@
|
||||
/*!
|
||||
@file src/Bytecode/Bytecode.hpp
|
||||
@brief 字节码Bytecode定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Core/SourceLocations.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
using Instruction = std::uint32_t;
|
||||
|
||||
enum class OpCode : std::uint8_t
|
||||
{
|
||||
// 控制流
|
||||
Exit, // iAsBx, return sBx
|
||||
Exit_MaxRecursionDepthExceeded, // iAsBx, fatal: 超出最大递归深度
|
||||
|
||||
// 常量加载
|
||||
LoadK, // iABx, R(A) = K(Bx)
|
||||
LoadTrue, // iABC, R(A) = true
|
||||
LoadFalse, // iABC, R(A) = false
|
||||
LoadNull, // iABC, R(A) = null
|
||||
|
||||
// 函数调用
|
||||
FastCall, // iABC, call Proto[A], args from R(B)
|
||||
Call, // iABC, call R(A), args from R(B)
|
||||
Return, // iABC, return R(A)
|
||||
|
||||
// 闭包
|
||||
LoadFn, // iABx, R(A) = new Closure(Proto[Bx])
|
||||
|
||||
// 跳转
|
||||
Jmp, // iAsBx, PC += sBx
|
||||
JmpIfFalse, // iAsBx, if !R(A) then PC += 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) (WIP)
|
||||
BitXor, // iABC, R(A) = R(B) ^ R(C) (WIP)
|
||||
|
||||
// 快速整数算术(仅 int,无类型检查)
|
||||
IntFastAdd, // iABC, R(A) = R(B) + R(C) (int)
|
||||
IntFastSub, // iABC, R(A) = R(B) - R(C) (int)
|
||||
IntFastMul, // iABC, R(A) = R(B) * R(C) (int)
|
||||
// 结果可能为非整数
|
||||
IntFastDiv, // iABC, R(A) = (double)R(B) / R(C) (int)
|
||||
|
||||
// 比较
|
||||
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)
|
||||
|
||||
// 变量存取
|
||||
GetGlobal, // iABx, R(A) = G(Bx)
|
||||
SetGlobal, // iABx, G(Bx) = R(A)
|
||||
GetUpval, // iABC, R(A) = *Upval(B)
|
||||
SetUpval, // iABC, *Upval(B) = R(A)
|
||||
Copy, // iABC, R(A) = R(B)
|
||||
|
||||
Count // 哨兵
|
||||
};
|
||||
|
||||
namespace Op
|
||||
{
|
||||
[[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);
|
||||
}
|
||||
|
||||
[[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
|
||||
|
||||
struct UpvalueInfo
|
||||
{
|
||||
uint8_t index;
|
||||
bool isLocal;
|
||||
};
|
||||
|
||||
struct Proto
|
||||
{
|
||||
String name;
|
||||
DynArray<Instruction> code;
|
||||
DynArray<SourceLocation *> locations;
|
||||
DynArray<Value> constants;
|
||||
DynArray<UpvalueInfo> upvalues;
|
||||
uint8_t maxRegisters = 0;
|
||||
uint8_t numParams = 0;
|
||||
};
|
||||
|
||||
struct CompiledModule
|
||||
{
|
||||
DynArray<Proto *> protos;
|
||||
};
|
||||
|
||||
} // namespace Fig
|
||||
@@ -1,102 +0,0 @@
|
||||
/*!
|
||||
@file src/Bytecode/Disassembler.cpp
|
||||
@brief 字节码反汇编器实现
|
||||
*/
|
||||
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
void Disassembler::DisassembleModule(const CompiledModule *module, std::ostream &stream)
|
||||
{
|
||||
if (!module) return;
|
||||
stream << "--- Module Disassembly ---" << std::endl;
|
||||
for (auto *proto : module->protos)
|
||||
{
|
||||
DisassembleProto(proto);
|
||||
}
|
||||
}
|
||||
|
||||
void Disassembler::DisassembleProto(const Proto *proto, std::ostream &stream)
|
||||
{
|
||||
if (!proto) return;
|
||||
|
||||
stream << std::format("\n--- Proto: {} (Regs: {}, Params: {}) ---\n",
|
||||
proto->name, proto->maxRegisters, proto->numParams);
|
||||
|
||||
for (size_t i = 0; i < proto->code.size(); ++i)
|
||||
{
|
||||
Instruction inst = proto->code[i];
|
||||
OpCode op = static_cast<OpCode>(inst & 0xFF);
|
||||
uint8_t a = (inst >> 8) & 0xFF;
|
||||
|
||||
stream << std::format("[{:04}] {:<12} ", i, magic_enum::enum_name(op));
|
||||
|
||||
Format fmt = GetFormat(op);
|
||||
if (fmt == Format::ABC)
|
||||
{
|
||||
uint8_t b = (inst >> 16) & 0xFF;
|
||||
uint8_t c = (inst >> 24) & 0xFF;
|
||||
stream << std::format("A:{:<3} B:{:<3} C:{:<3}", a, b, c);
|
||||
}
|
||||
else if (fmt == Format::ABx)
|
||||
{
|
||||
uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||
stream << std::format("A:{:<3} Bx:{:<5}", a, bx);
|
||||
|
||||
// 自动关联常量池
|
||||
if (op == OpCode::LoadK && bx < proto->constants.size())
|
||||
{
|
||||
stream << std::format(" ; {}", proto->constants[bx].ToString());
|
||||
}
|
||||
}
|
||||
else if (fmt == Format::AsBx)
|
||||
{
|
||||
int16_t sbx = static_cast<int16_t>((inst >> 16) & 0xFFFF);
|
||||
stream << std::format("A:{:<3} sBx:{:<5}", a, sbx);
|
||||
|
||||
// 计算跳转绝对地址
|
||||
if (op == OpCode::Jmp || op == OpCode::JmpIfFalse)
|
||||
{
|
||||
stream << std::format(" ; to [{:04}]", i + sbx + 1);
|
||||
}
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
if (!proto->constants.empty())
|
||||
{
|
||||
stream << "Constants:\n";
|
||||
for (size_t i = 0; i < proto->constants.size(); ++i)
|
||||
{
|
||||
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
|
||||
}
|
||||
}
|
||||
stream << "--- End Disassembly ---\n";
|
||||
}
|
||||
|
||||
Disassembler::Format Disassembler::GetFormat(OpCode op)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case OpCode::LoadK:
|
||||
case OpCode::Mov:
|
||||
case OpCode::GetGlobal:
|
||||
case OpCode::SetGlobal:
|
||||
case OpCode::LoadFn:
|
||||
return Format::ABx;
|
||||
|
||||
case OpCode::Exit:
|
||||
case OpCode::Exit_MaxRecursionDepthExceeded:
|
||||
case OpCode::Jmp:
|
||||
case OpCode::JmpIfFalse:
|
||||
return Format::AsBx;
|
||||
|
||||
default:
|
||||
return Format::ABC;
|
||||
}
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,35 +0,0 @@
|
||||
/*!
|
||||
@file src/Bytecode/Disassembler.hpp
|
||||
@brief 字节码反汇编器:物理还原指令语义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Core/CoreIO.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Disassembler
|
||||
{
|
||||
public:
|
||||
// 反汇编整个模块
|
||||
static void DisassembleModule(const CompiledModule *module, std::ostream & = CoreIO::GetStdOut());
|
||||
|
||||
// 反汇编单个函数原型
|
||||
static void DisassembleProto(const Proto *proto, std::ostream & = CoreIO::GetStdOut());
|
||||
|
||||
private:
|
||||
enum class Format
|
||||
{
|
||||
ABC,
|
||||
ABx,
|
||||
AsBx
|
||||
};
|
||||
|
||||
static Format GetFormat(OpCode op);
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,64 +0,0 @@
|
||||
/*!
|
||||
@file src/Compiler/Compiler.hpp
|
||||
@brief 编译器定义:物理直连 Bootstrapper
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <Object/Object.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
using Register = std::uint8_t;
|
||||
|
||||
class Compiler
|
||||
{
|
||||
private:
|
||||
static constexpr Register MAX_REGISTERS = 250;
|
||||
static constexpr Register NO_REG = 255;
|
||||
|
||||
struct FuncState
|
||||
{
|
||||
Proto *proto;
|
||||
Register freereg;
|
||||
FuncState *enclosing;
|
||||
HashMap<Value, int> constantMap;
|
||||
|
||||
FuncState(Proto *p, FuncState *e)
|
||||
: proto(p), freereg(p->numParams), enclosing(e) {}
|
||||
};
|
||||
|
||||
FuncState *current = nullptr;
|
||||
CompiledModule *module = nullptr;
|
||||
SourceManager &manager;
|
||||
Diagnostics &diag;
|
||||
|
||||
HashMap<String, int> globalIDMap;
|
||||
int getGlobalID(const String& name);
|
||||
|
||||
Result<Register, Error> allocateReg(const SourceLocation &loc);
|
||||
void freeReg(Register count = 1);
|
||||
int addConstant(Value val);
|
||||
|
||||
void emit(Instruction inst, SourceLocation *loc);
|
||||
|
||||
Result<void, Error> compileStmt(Stmt *stmt);
|
||||
Result<Register, Error> compileExpr(Expr *expr, Register target = NO_REG);
|
||||
|
||||
public:
|
||||
Compiler(SourceManager &m, Diagnostics &d) : manager(m), diag(d) {}
|
||||
Result<CompiledModule *, Error> Compile(Program *)
|
||||
{
|
||||
auto *mod = new CompiledModule();
|
||||
auto *boot = new Proto();
|
||||
boot->name = "[bootstrapper]";
|
||||
boot->maxRegisters = 1;
|
||||
boot->code.push_back(Op::iAsBx(OpCode::Exit, 0, 0));
|
||||
mod->protos.push_back(boot);
|
||||
return mod;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,49 +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()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleCP(CP_UTF8);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
@@ -1,143 +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;
|
||||
|
||||
const std::string LOGO =
|
||||
R"(
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒██████████▓▒▒▒▒
|
||||
▒▒▒▒▒██████████▓▒▒▒▒
|
||||
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
)";
|
||||
|
||||
const std::string LICENSE_TEXT =
|
||||
R"(
|
||||
MIT License (Fig)
|
||||
|
||||
Copyright (c) 2026 PuqiAR <im@puqiar.top>
|
||||
|
||||
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:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
2. This project includes code from the following projects with their respective licenses:
|
||||
- magic_enum (MIT License)
|
||||
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||
|
||||
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.
|
||||
|
||||
- json (MIT LICENSE) (for LSP Server JSON-RPC)
|
||||
Copyright (c) 2013-2026 Niels Lohmann
|
||||
|
||||
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.
|
||||
|
||||
)";
|
||||
|
||||
}; // namespace Core
|
||||
}; // namespace Fig
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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
@@ -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";
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*!
|
||||
@file src/Error/Diagnostics.hpp
|
||||
@brief 诊断信息收集器:用于存储警告与非致命错误
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Error/Error.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Diagnostics
|
||||
{
|
||||
private:
|
||||
DynArray<Error> errors;
|
||||
|
||||
public:
|
||||
void Report(Error err)
|
||||
{
|
||||
errors.push_back(std::move(err));
|
||||
}
|
||||
|
||||
// 统一打印所有收集到的信息
|
||||
void EmitAll(const SourceManager &manager)
|
||||
{
|
||||
for (const auto &err : errors)
|
||||
{
|
||||
ReportError(err, manager);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasErrors() const
|
||||
{
|
||||
for (const auto &err : errors)
|
||||
{
|
||||
if (!err.IsWarning()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const DynArray<Error>& GetErrors() const { return errors; }
|
||||
|
||||
void Clear() { errors.clear(); }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,200 +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 UnnecessarySemicolon: return "UnnecessarySemicolon";
|
||||
|
||||
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";
|
||||
|
||||
case RegisterOverflow: return "RegisterOverflow";
|
||||
case InternalError:
|
||||
return "InternalError";
|
||||
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||
}
|
||||
return "UnknownError";
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,185 +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,
|
||||
UnnecessarySemicolon,
|
||||
TrailingComma,
|
||||
|
||||
/* 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,
|
||||
|
||||
// 编译器内部约束
|
||||
RegisterOverflow,
|
||||
InternalError,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 新增:适配诊断系统的辅助判定
|
||||
bool IsWarning() const { return static_cast<unsigned int>(type) < 2000; }
|
||||
};
|
||||
|
||||
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
|
||||
@@ -1,180 +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, "");
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, "", diagnostics);
|
||||
|
||||
// 语法检查拦截
|
||||
auto parserResult = parser.Parse();
|
||||
if (!parserResult)
|
||||
{
|
||||
SendDiagnostics(uri, &parserResult.error());
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &diag : diagnostics.GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
Program *program = *parserResult;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
|
||||
// 语义检查拦截
|
||||
auto analyzerResult = analyzer.Analyze(program);
|
||||
if (!analyzerResult)
|
||||
{
|
||||
SendDiagnostics(uri, &analyzerResult.error());
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &diag : analyzer.GetDiagnostics().GetErrors())
|
||||
{
|
||||
SendDiagnostics(uri, &diag);
|
||||
}
|
||||
|
||||
// 一切完美,发射空数组清空过去的错误红线
|
||||
SendDiagnostics(uri, nullptr);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -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(¤t)),
|
||||
// "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
|
||||
@@ -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 ¤tPosition()
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline char32_t current() const
|
||||
{
|
||||
assert(index < source.length() && "SourceReader: get current failed, index out of range");
|
||||
return source[index];
|
||||
}
|
||||
|
||||
inline char32_t currentIf() const
|
||||
{
|
||||
if (index >= source.length())
|
||||
{
|
||||
return U'\0';
|
||||
}
|
||||
return source[index];
|
||||
}
|
||||
|
||||
inline bool hasNext() const
|
||||
{
|
||||
return index < source.length() - 1;
|
||||
}
|
||||
|
||||
inline char32_t peek() const
|
||||
{
|
||||
assert((index + 1) < source.length() && "SourceReader: get peek failed, index out of range");
|
||||
return source[index + 1];
|
||||
}
|
||||
|
||||
inline char32_t peekIf() const
|
||||
{
|
||||
if ((index + 1) < source.length())
|
||||
{
|
||||
return source[index + 1];
|
||||
}
|
||||
return 0xFFFD;
|
||||
}
|
||||
|
||||
inline char32_t produce()
|
||||
{
|
||||
// returns current rune, then next
|
||||
char32_t c = current();
|
||||
next();
|
||||
return c;
|
||||
}
|
||||
|
||||
inline void next()
|
||||
{
|
||||
char32_t consumed = currentIf();
|
||||
|
||||
++index;
|
||||
if (consumed == U'\n')
|
||||
{
|
||||
++pos.line;
|
||||
pos.column = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pos.column;
|
||||
}
|
||||
}
|
||||
|
||||
inline void skip(size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t currentIndex() const
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
inline bool isAtEnd() const
|
||||
{
|
||||
return index >= source.length();
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*!
|
||||
@file src/Object/FunctionObject.hpp
|
||||
@brief 函数对象定义
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// Upvalue (Stack Open / Heap Closed)
|
||||
struct Upvalue
|
||||
{
|
||||
Value *location; // Open 状态指向 VM 的 registerBase[x],Closed 状态指向下面的 closedValue
|
||||
Value closedValue; // 栈帧销毁时,数据物理迁移至此
|
||||
Upvalue *next; // 侵入式链表,供 VM 追踪当前 Open 的 Upvalue
|
||||
std::uint32_t refCount = 0; // 多少个闭包正在使用
|
||||
};
|
||||
|
||||
struct FunctionObject final : public Object
|
||||
{
|
||||
String name;
|
||||
Proto *proto; // 静态只读字节码
|
||||
std::uint8_t paraCount;
|
||||
std::uint32_t upvalueCount; // 捕获数量
|
||||
|
||||
// 柔性数组
|
||||
Upvalue *upvalues[];
|
||||
|
||||
FunctionObject(const String &_name, Proto *_proto, std::uint32_t _upvalueCount) :
|
||||
name(_name), proto(_proto), paraCount(_proto->numParams), upvalueCount(_upvalueCount)
|
||||
{
|
||||
type = ObjectType::Function;
|
||||
}
|
||||
|
||||
~FunctionObject() = default;
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,16 +0,0 @@
|
||||
/*!
|
||||
@file src/Object/InstanceObject.hpp
|
||||
@brief 实例(InstanceObject)定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-10
|
||||
*/
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct InstanceObject final : public Object
|
||||
{
|
||||
Value fields[];
|
||||
};
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*!
|
||||
@file src/Object/Object.hpp
|
||||
@brief 值表示实现 (NaN Boxing) 和 堆对象函数的实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
*/
|
||||
|
||||
#include <Object/Object.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())
|
||||
{
|
||||
Object *obj = AsObject();
|
||||
if (!obj)
|
||||
return "<Bad Object>";
|
||||
|
||||
// 物理分发, 扔掉虚函数!awa
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::String: {
|
||||
auto *strObj = static_cast<StringObject *>(obj);
|
||||
return strObj->data;
|
||||
}
|
||||
case ObjectType::Function: {
|
||||
auto *fnObj = static_cast<FunctionObject *>(obj);
|
||||
return std::format("<Function: {}>", fnObj->name);
|
||||
}
|
||||
case ObjectType::Struct: {
|
||||
auto *structObj = static_cast<StructObject *>(obj);
|
||||
return std::format("<Struct: {}>", structObj->name);
|
||||
}
|
||||
case ObjectType::Instance: {
|
||||
// 利用你预留的神级指针 klass 溯源
|
||||
if (obj->klass)
|
||||
{
|
||||
return std::format("<Instance: {}>", obj->klass->name);
|
||||
}
|
||||
return "<Instance: Unknown>";
|
||||
}
|
||||
default: return "<Corrupted Object>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknow";
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
@@ -1,14 +0,0 @@
|
||||
/*!
|
||||
@file src/Object/Object.hpp
|
||||
@brief 值系统总文件
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-02-19
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Object/FunctionObject.hpp>
|
||||
#include <Object/InstanceObject.hpp>
|
||||
#include <Object/ObjectBase.hpp>
|
||||
#include <Object/StringObject.hpp>
|
||||
#include <Object/StructObject.hpp>
|
||||
@@ -1,283 +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,
|
||||
TypeObj,
|
||||
};
|
||||
|
||||
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
||||
|
||||
enum class GCColor : std::uint8_t
|
||||
{
|
||||
White = 0, // 垃圾(或新对象)!
|
||||
Gray = 1, // 已发现,子节点待扫描
|
||||
Black = 2, // 存活
|
||||
};
|
||||
|
||||
// Total 24 bytes size
|
||||
struct Object
|
||||
{
|
||||
Object *next; // 8 bytes: gc链表
|
||||
StructObject *klass; // 8 bytes: 一切皆对象,父类指针
|
||||
ObjectType type; // 1 byte : 类型
|
||||
GCColor color = GCColor::White; // 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;
|
||||
}
|
||||
|
||||
constexpr bool isTypeObj() const
|
||||
{
|
||||
return type == ObjectType::TypeObj;
|
||||
}
|
||||
};
|
||||
|
||||
enum class TypeTag : uint8_t
|
||||
{
|
||||
Null,
|
||||
Int,
|
||||
Double,
|
||||
String,
|
||||
Bool,
|
||||
Any,
|
||||
Type,
|
||||
Function,
|
||||
Struct,
|
||||
Interface,
|
||||
};
|
||||
|
||||
struct TypeObject : Object
|
||||
{
|
||||
String name;
|
||||
TypeTag tag;
|
||||
|
||||
TypeObject(String _name, TypeTag _t) : name(std::move(_name)), tag(_t)
|
||||
{
|
||||
type = ObjectType::TypeObj;
|
||||
klass = nullptr; // TypeObject 自身的 klass 指向 TypeTypeObject,初始化后设置
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<Fig::Value>
|
||||
{
|
||||
std::size_t operator()(const Fig::Value &v) const noexcept
|
||||
{
|
||||
return std::hash<std::uint64_t>()(v.Raw());
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -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';
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
};
|
||||
@@ -1,52 +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>
|
||||
#include <Sema/Type.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 FieldMeta
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
};
|
||||
|
||||
struct StructObject final : public Object
|
||||
{
|
||||
String name;
|
||||
std::uint8_t fieldCount;
|
||||
FieldMeta *fields; // [fieldCount]
|
||||
Object *operators[GetOperatorsSize()];
|
||||
// operators: [UnaryOp 0..N][BinaryOp 0..N], nullptr = 无重载
|
||||
|
||||
Object *GetUnaryOperator(UnaryOperator _op)
|
||||
{
|
||||
return operators[static_cast<std::uint8_t>(_op)];
|
||||
}
|
||||
|
||||
Object *GetBinaryOperator(BinaryOperator _op)
|
||||
{
|
||||
return operators[static_cast<std::uint8_t>(UnaryOperator::Count)
|
||||
+ static_cast<std::uint8_t>(_op)];
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,549 +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<Expr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
||||
{
|
||||
StateProtector p(this, {State::ParsingLiteralExpr});
|
||||
|
||||
const Token &literal_token = consumeToken();
|
||||
LiteralExpr *node =
|
||||
arena.Allocate<LiteralExpr>(literal_token, makeSourceLocation(literal_token));
|
||||
return node;
|
||||
}
|
||||
Result<Expr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||
{
|
||||
StateProtector p(this, {State::ParsingIdentiExpr});
|
||||
|
||||
const Token &identifier = consumeToken();
|
||||
IdentiExpr *node = arena.Allocate<IdentiExpr>(
|
||||
srcManager.GetSub(identifier.index, identifier.length), makeSourceLocation(identifier));
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<Expr *, 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 = arena.Allocate<InfixExpr>(lhs, op, rhs);
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<Expr *, 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 = arena.Allocate<PrefixExpr>(op, rhs);
|
||||
return node;
|
||||
}
|
||||
|
||||
Result<Expr *, 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 = arena.Allocate<IndexExpr>(base, *index_result);
|
||||
return indexExpr;
|
||||
}
|
||||
|
||||
Result<Expr *, Error>
|
||||
Parser::parseCallExpr(Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
||||
{
|
||||
StateProtector p(this, {State::ParsingCallExpr});
|
||||
|
||||
const Token &lparen_token = consumeToken(); // consume `(`
|
||||
const SourceLocation &location = makeSourceLocation(lparen_token);
|
||||
|
||||
FnCallArgs callArgs;
|
||||
|
||||
// 空参数列表
|
||||
if (currentToken().type == TokenType::RightParen)
|
||||
{
|
||||
consumeToken(); // consume `)`
|
||||
return arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
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 arena.Allocate<CallExpr>(callee, callArgs, location);
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseNewExpr()
|
||||
{
|
||||
// new type{...}
|
||||
StateProtector p(this, {State::ParsingNewExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `new`
|
||||
|
||||
SET_STOP_AT(TokenType::LeftBrace); // {
|
||||
auto type_result = parseTypeExpr();
|
||||
if (!type_result)
|
||||
{
|
||||
return std::unexpected(type_result.error());
|
||||
}
|
||||
Expr *type = *type_result;
|
||||
|
||||
if (!match(TokenType::LeftBrace))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("NewExpr", "lbrace {", currentToken()));
|
||||
}
|
||||
|
||||
const Token &lb_token = prevToken();
|
||||
|
||||
/*
|
||||
Positional:
|
||||
new Point{1, 2}
|
||||
Named:
|
||||
new Point{x = 1, y = 2}
|
||||
Shorthand:
|
||||
new Point{y, x}
|
||||
*/
|
||||
|
||||
DynArray<NewExpr::Arg> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `{` in new expr",
|
||||
"insert '}'",
|
||||
makeSourceLocation(lb_token)
|
||||
));
|
||||
}
|
||||
if (args.empty() && match(TokenType::RightBrace)) // 空参
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// named arg
|
||||
if (currentToken().isIdentifier() && peekToken().type == TokenType::Colon)
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
consumeToken(); // consume `:`
|
||||
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
name,
|
||||
*result
|
||||
});
|
||||
}
|
||||
// shorthand
|
||||
else if (currentToken().isIdentifier()
|
||||
&& (peekToken().type == TokenType::Comma || peekToken().type == TokenType::RightBrace))
|
||||
{
|
||||
const Token &name_token = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_token.index, name_token.length);
|
||||
|
||||
|
||||
|
||||
IdentiExpr *ident =
|
||||
arena.Allocate<IdentiExpr>(name, makeSourceLocation(name_token));
|
||||
args.push_back(NewExpr::Arg{name, ident});
|
||||
}
|
||||
else
|
||||
{
|
||||
SET_STOP_AT(TokenType::Comma, TokenType::RightBrace); // , / }
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
args.push_back(NewExpr::Arg{
|
||||
.value = *result
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (match(TokenType::Comma))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (match(TokenType::RightBrace))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NewExpr *newExpr = arena.Allocate<NewExpr>(type, args, location);
|
||||
return newExpr;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseLambdaExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingLambdaExpr});
|
||||
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"lambda expression should not have a name",
|
||||
"remove the name",
|
||||
makeSourceLocation(currentToken())));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Expr *returnType = nullptr;
|
||||
Token rightArrowToken;
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
rightArrowToken = consumeToken();
|
||||
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
if (match(TokenType::DoubleArrow)) // =>
|
||||
{
|
||||
if (returnType)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"use of expr body but specified return type in lambda expr",
|
||||
"remove `-> ...`",
|
||||
makeSourceLocation(rightArrowToken)));
|
||||
}
|
||||
auto result = parseExpression();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
Expr *expr = *result;
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, expr, true, location);
|
||||
return lambda;
|
||||
}
|
||||
else if (currentToken().type == TokenType::LeftBrace)
|
||||
{
|
||||
auto result = parseBlockStmt();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
LambdaExpr *lambda =
|
||||
arena.Allocate<LambdaExpr>(params, returnType, *result, false, location);
|
||||
return lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("LambdaExpr", "darrow => / lbrace {", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
||||
{
|
||||
Expr *lhs = nullptr;
|
||||
Token token = currentToken();
|
||||
|
||||
// NUD
|
||||
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;
|
||||
}
|
||||
else if (token.type == TokenType::Function)
|
||||
{
|
||||
auto result = parseLambdaExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
else if (token.type == TokenType::New)
|
||||
{
|
||||
auto result = parseNewExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
lhs = *result;
|
||||
}
|
||||
|
||||
if (!lhs)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::ExpectedExpression,
|
||||
"expected expression",
|
||||
"insert expressions",
|
||||
makeSourceLocation(prevToken())));
|
||||
}
|
||||
|
||||
// LED
|
||||
while (true)
|
||||
{
|
||||
token = currentToken();
|
||||
if (shouldTerminate())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// is / as
|
||||
if (token.type == TokenType::Is || token.type == TokenType::As)
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `is` or `as`
|
||||
auto typeRes = parseTypeExpr();
|
||||
if (!typeRes)
|
||||
{
|
||||
return std::unexpected(typeRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<InfixExpr>(lhs, op, *typeRes);
|
||||
}
|
||||
// binary
|
||||
else if (IsTokenOp(token.type /* isBinary = true */))
|
||||
{
|
||||
BinaryOperator op = TokenToBinaryOp(token);
|
||||
BindingPower lbp = GetBinaryOpLBp(op);
|
||||
if (rbp >= lbp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto result = parseInfixExpr(lhs);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
lhs = *result;
|
||||
}
|
||||
// [index]
|
||||
else if (token.type == TokenType::LeftBracket)
|
||||
{
|
||||
const auto &expr_result = parseIndexExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
// call
|
||||
else if (token.type == TokenType::LeftParen)
|
||||
{
|
||||
const auto &expr_result = parseCallExpr(lhs);
|
||||
if (!expr_result)
|
||||
{
|
||||
return expr_result;
|
||||
}
|
||||
lhs = *expr_result;
|
||||
}
|
||||
// .member
|
||||
else if (token.type == TokenType::Dot)
|
||||
{
|
||||
consumeToken(); // consume `.`
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("MemberExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &nameToken = consumeToken();
|
||||
const String &name =
|
||||
srcManager.GetSub(nameToken.index, nameToken.length);
|
||||
SourceLocation loc = makeSourceLocation(nameToken);
|
||||
lhs = arena.Allocate<MemberExpr>(lhs, name, loc);
|
||||
}
|
||||
// x++ x--
|
||||
else if (token.type == TokenType::DoublePlus || token.type == TokenType::DoubleMinus)
|
||||
{
|
||||
UnaryOperator op = TokenToUnaryOp(consumeToken());
|
||||
lhs = arena.Allocate<PostfixExpr>(op, lhs);
|
||||
}
|
||||
// ?:
|
||||
else if (token.type == TokenType::Question)
|
||||
{
|
||||
// ?: 最低优先
|
||||
// 赋值 rbp = 101,所以只有当 rbp < 100 时才可能进到三元
|
||||
// 实际上三元是最低优先级的非赋值运算符,我们给一个很小的 lbp
|
||||
constexpr BindingPower TERNARY_LBP = 150;
|
||||
if (rbp >= TERNARY_LBP)
|
||||
{
|
||||
break;
|
||||
}
|
||||
consumeToken(); // consume `?`
|
||||
auto thenRes = parseExpression(0); // 重置绑定力,右结合
|
||||
if (!thenRes)
|
||||
{
|
||||
return std::unexpected(thenRes.error());
|
||||
}
|
||||
if (!match(TokenType::Colon))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TernaryExpr", "`:` for else branch", currentToken()));
|
||||
}
|
||||
auto elseRes = parseExpression(TERNARY_LBP - 1); // 右结合
|
||||
if (!elseRes)
|
||||
{
|
||||
return std::unexpected(elseRes.error());
|
||||
}
|
||||
lhs = arena.Allocate<TernaryExpr>(lhs, *thenRes, *elseRes, lhs->location);
|
||||
}
|
||||
else
|
||||
{
|
||||
return lhs;
|
||||
}
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
}; // namespace Fig
|
||||
@@ -1,37 +0,0 @@
|
||||
/*!
|
||||
@file src/Parser/Parser.cpp
|
||||
@brief 语法分析器实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#include <Parser/Parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<Program *, Error> Parser::Parse()
|
||||
{
|
||||
Program *program = arena.Allocate<Program>();
|
||||
|
||||
while (currentToken().type != TokenType::EndOfFile)
|
||||
{
|
||||
if (lexerError)
|
||||
{
|
||||
return std::unexpected(*lexerError);
|
||||
}
|
||||
auto result = parseStatement();
|
||||
if (!result)
|
||||
{
|
||||
return std::unexpected(result.error());
|
||||
}
|
||||
|
||||
Stmt *stmt = *result;
|
||||
if (stmt)
|
||||
{
|
||||
program->nodes.push_back(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}; // namespace Fig
|
||||
@@ -1,305 +0,0 @@
|
||||
/*!
|
||||
@file src/Parser/Parser.hpp
|
||||
@brief 语法分析器(Pratt + 手动递归下降) 定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <Error/Error.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Token/Token.hpp>
|
||||
#include <Utils/Arena.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Parser
|
||||
{
|
||||
private:
|
||||
Arena arena;
|
||||
Lexer &lexer;
|
||||
SourceManager &srcManager;
|
||||
|
||||
size_t index = 0; // 当前 Token 在 buffer 中的下标
|
||||
DynArray<Token> buffer; // 已从 Lexer 读取的 Token 缓存
|
||||
|
||||
String fileName;
|
||||
bool isEOF = false;
|
||||
|
||||
Diagnostics &diagnostics;
|
||||
std::optional<Error> lexerError; // 词法错误缓存,避免 exit/abort
|
||||
|
||||
// 惰性获取下一个 Token,跳过注释
|
||||
Token nextToken()
|
||||
{
|
||||
if (index + 1 < buffer.size())
|
||||
return buffer[++index];
|
||||
if (isEOF)
|
||||
return buffer[index];
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer[index];
|
||||
}
|
||||
const Token &token = result.value();
|
||||
if (token.type == TokenType::Comments)
|
||||
continue; // 惰性跳过注释
|
||||
|
||||
if (token.type == TokenType::EndOfFile)
|
||||
isEOF = true;
|
||||
buffer.push_back(token);
|
||||
index = buffer.size() - 1;
|
||||
return buffer[index];
|
||||
}
|
||||
}
|
||||
|
||||
inline Token prevToken()
|
||||
{
|
||||
return (index > 0) ? buffer[index - 1] : buffer[0];
|
||||
}
|
||||
inline Token currentToken()
|
||||
{
|
||||
if (buffer.empty())
|
||||
return nextToken();
|
||||
return buffer[index];
|
||||
}
|
||||
|
||||
// 惰性窥视后续 Token
|
||||
Token peekToken(size_t lookahead = 1)
|
||||
{
|
||||
size_t targetIndex = index + lookahead;
|
||||
while (targetIndex >= buffer.size() && !isEOF)
|
||||
{
|
||||
auto result = lexer.NextToken();
|
||||
if (!result)
|
||||
{
|
||||
lexerError = result.error();
|
||||
isEOF = true;
|
||||
Token eof = {0, 0, TokenType::EndOfFile};
|
||||
buffer.push_back(eof);
|
||||
index = buffer.size() - 1;
|
||||
return buffer.back();
|
||||
}
|
||||
if (result->type == TokenType::Comments)
|
||||
continue;
|
||||
if (result->type == TokenType::EndOfFile)
|
||||
isEOF = true;
|
||||
buffer.push_back(*result);
|
||||
}
|
||||
return (targetIndex >= buffer.size()) ? buffer.back() : buffer[targetIndex];
|
||||
}
|
||||
|
||||
inline Token consumeToken()
|
||||
{
|
||||
Token current = currentToken();
|
||||
if (current.type != TokenType::EndOfFile)
|
||||
nextToken();
|
||||
return current;
|
||||
}
|
||||
inline bool match(TokenType type)
|
||||
{
|
||||
if (currentToken().type == type)
|
||||
{
|
||||
consumeToken();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
struct State
|
||||
{
|
||||
enum StateType : std::uint8_t
|
||||
{
|
||||
Standby,
|
||||
|
||||
ParsingLiteralExpr,
|
||||
ParsingIdentiExpr,
|
||||
ParsingInfixExpr,
|
||||
ParsingPrefixExpr,
|
||||
ParsingIndexExpr,
|
||||
ParsingCallExpr,
|
||||
ParsingLambdaExpr,
|
||||
ParsingNewExpr,
|
||||
|
||||
ParsingVarDecl,
|
||||
ParsingIf,
|
||||
ParsingWhile,
|
||||
ParsingFnDefStmt,
|
||||
ParsingReturn,
|
||||
ParsingBreak,
|
||||
ParsingContinue,
|
||||
ParsingStructDef,
|
||||
|
||||
ParsingTypeParameters,
|
||||
ParsingNamedTypeExpr,
|
||||
ParsingFnTypeExpr,
|
||||
} 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;
|
||||
}
|
||||
|
||||
bool shouldTerminate()
|
||||
{
|
||||
const Token &token = currentToken();
|
||||
if (getBaseTerminators().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 ¤tState()
|
||||
{
|
||||
return stateStack.back();
|
||||
}
|
||||
void pushState(State _state)
|
||||
{
|
||||
stateStack.push_back(std::move(_state));
|
||||
}
|
||||
void popState()
|
||||
{
|
||||
if (!stateStack.empty())
|
||||
stateStack.pop_back();
|
||||
}
|
||||
|
||||
struct StateProtector
|
||||
{
|
||||
Parser *p;
|
||||
StateProtector(Parser *_p, State _s) : p(_p)
|
||||
{
|
||||
p->pushState(_s);
|
||||
}
|
||||
~StateProtector()
|
||||
{
|
||||
p->popState();
|
||||
}
|
||||
};
|
||||
|
||||
SourceLocation makeSourceLocation(const Token &tok)
|
||||
{
|
||||
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
||||
// 防止因解析错位导致的异常列号引起终端 OOM
|
||||
if (column > 5000)
|
||||
column = 1;
|
||||
return SourceLocation(
|
||||
SourcePosition(line, column, tok.length),
|
||||
fileName,
|
||||
"[internal parser]",
|
||||
magic_enum::enum_name(currentState().type).data());
|
||||
}
|
||||
|
||||
inline Error makeUnexpectTokenError(
|
||||
const String &stmt,
|
||||
const String &exp,
|
||||
const Token &got,
|
||||
std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
std::format(
|
||||
"expect '{}' in {}, got `{}`", exp, stmt, magic_enum::enum_name(got.type)),
|
||||
"none",
|
||||
makeSourceLocation(got),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
inline Error
|
||||
makeExpectSemicolonError(std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(currentToken()),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
inline Error makeExpectSemicolonError(
|
||||
const Token &token, std::source_location th_loc = std::source_location::current())
|
||||
{
|
||||
return Error(
|
||||
ErrorType::SyntaxError,
|
||||
"expect ';' after statement",
|
||||
"insert ';'",
|
||||
makeSourceLocation(token),
|
||||
th_loc);
|
||||
}
|
||||
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> parseTypeParameters();
|
||||
|
||||
Result<Expr *, Error> parseTypeExpr();
|
||||
Result<Expr *, Error> parseNamedTypeExpr();
|
||||
Result<Expr *, Error> parseFnTypeExpr();
|
||||
|
||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||
Result<Expr *, Error> parseLiteralExpr();
|
||||
Result<Expr *, Error> parseIdentiExpr();
|
||||
Result<Expr *, Error> parsePrefixExpr();
|
||||
Result<Expr *, Error> parseInfixExpr(Expr *);
|
||||
Result<Expr *, Error> parseIndexExpr(Expr *);
|
||||
Result<Expr *, Error> parseCallExpr(Expr *);
|
||||
Result<Expr *, Error> parseNewExpr();
|
||||
Result<Expr *, Error> parseLambdaExpr();
|
||||
|
||||
Result<BlockStmt *, Error> parseBlockStmt();
|
||||
Result<VarDecl *, Error> parseVarDecl(bool);
|
||||
Result<VarDecl *, Error> parseConstDecl(bool);
|
||||
Result<IfStmt *, Error> parseIfStmt();
|
||||
Result<WhileStmt *, Error> parseWhileStmt();
|
||||
Result<DynArray<Param *>, Error> parseFnParams();
|
||||
Result<FnDefStmt *, Error> parseFnDefStmt(bool);
|
||||
Result<ReturnStmt *, Error> parseReturnStmt();
|
||||
|
||||
Result<Stmt *, Error> parseStructDef(bool);
|
||||
Result<Stmt *, Error> parseInterfaceDef(bool);
|
||||
Result<Stmt *, Error> parseImpl();
|
||||
Result<Stmt *, Error> parseForStmt();
|
||||
Result<Stmt *, Error> parseImportStmt();
|
||||
|
||||
Result<Stmt *, Error> parseStatement();
|
||||
|
||||
public:
|
||||
Parser(Lexer &_lexer, SourceManager &_src, String _file, Diagnostics &_diagnostics) :
|
||||
lexer(_lexer), srcManager(_src), fileName(std::move(_file)), diagnostics(_diagnostics)
|
||||
{
|
||||
pushState(State());
|
||||
}
|
||||
|
||||
Result<Program *, Error> Parse();
|
||||
};
|
||||
|
||||
#define SET_STOP_AT(...) currentState().stopAt = {__VA_ARGS__};
|
||||
} // namespace Fig
|
||||
@@ -1,42 +0,0 @@
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
|
||||
String fileName = "[memory]";
|
||||
String filePath =
|
||||
"System" + fileName;
|
||||
|
||||
SourceManager srcManager(filePath);
|
||||
|
||||
String source = R"(
|
||||
var a = 10;
|
||||
var a: Int;
|
||||
var a := 200 * 30 + 2;
|
||||
)";
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
Parser parser(lexer, srcManager, fileName, diagnostics);
|
||||
|
||||
auto result = parser.Parse();
|
||||
if (!result)
|
||||
{
|
||||
ReportError(result.error(), srcManager);
|
||||
return 1;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(srcManager);
|
||||
|
||||
Program *program = *result;
|
||||
std::cout << "Parsed " << program->nodes.size() << " statements\n";
|
||||
for (size_t i = 0; i < program->nodes.size(); ++i)
|
||||
{
|
||||
std::cout << '[' << i << "] " << program->nodes[i]->toString() << '\n';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,201 +0,0 @@
|
||||
/*!
|
||||
@file src/Parser/TypeExprParser.cpp
|
||||
@brief 类型表达式解析器实现 — 类型即值,产生 Expr* 而非 TypeExpr*
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#include <Parser/Parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Result<decltype(StructDefStmt::typeParameters), Error> Parser::parseTypeParameters()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingTypeParameters});
|
||||
decltype(StructDefStmt::typeParameters) tp;
|
||||
|
||||
const Token &lab = consumeToken(); // consume `<`
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (isEOF)
|
||||
{
|
||||
return std::unexpected(Error(
|
||||
ErrorType::SyntaxError,
|
||||
"unclosed `<` in type parameters",
|
||||
"insert '>'",
|
||||
makeSourceLocation(lab)));
|
||||
}
|
||||
if (match(TokenType::Greater)) // >
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeParams", "tp name", currentToken()));
|
||||
}
|
||||
|
||||
const Token &name_tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(name_tok.index, name_tok.length);
|
||||
tp.push_back(name);
|
||||
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError(
|
||||
"TypeParams", "comma or type parameter", currentToken()));
|
||||
}
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseNamedTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingNamedTypeExpr});
|
||||
|
||||
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeExpr", "type name", currentToken()));
|
||||
}
|
||||
|
||||
const Token &firstTok = consumeToken();
|
||||
SourceLocation firstLoc = makeSourceLocation(firstTok);
|
||||
const String &firstName = srcManager.GetSub(firstTok.index, firstTok.length);
|
||||
|
||||
IdentiExpr *ident = arena.Allocate<IdentiExpr>(firstName, firstLoc);
|
||||
Expr *base = ident;
|
||||
|
||||
// a.b.c
|
||||
while (match(TokenType::Dot))
|
||||
{
|
||||
if (!currentToken().isIdentifier())
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeExpr", "identifier after `.`", currentToken()));
|
||||
}
|
||||
const Token &tok = consumeToken();
|
||||
const String &name = srcManager.GetSub(tok.index, tok.length);
|
||||
SourceLocation loc = makeSourceLocation(tok);
|
||||
base = arena.Allocate<MemberExpr>(base, name, loc);
|
||||
}
|
||||
|
||||
// generic args
|
||||
if (match(TokenType::Less))
|
||||
{
|
||||
DynArray<Expr *> args;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
return std::unexpected(result.error());
|
||||
args.push_back(*result);
|
||||
|
||||
if (match(TokenType::Greater))
|
||||
break; // `>`
|
||||
if (!match(TokenType::Comma))
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("TypeArgs", "'>' or ','", currentToken()));
|
||||
}
|
||||
|
||||
base = arena.Allocate<ApplyExpr>(base, std::move(args), firstLoc);
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
Result<Expr *, Error> Parser::parseFnTypeExpr()
|
||||
{
|
||||
StateProtector p(this, {State::ParsingFnTypeExpr});
|
||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `func`
|
||||
if (!match(TokenType::LeftParen)) // `(`
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "lparen (", currentToken()));
|
||||
}
|
||||
|
||||
DynArray<Expr *> paraTypes;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
paraTypes.push_back(*result);
|
||||
|
||||
if (match(TokenType::RightParen))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (isEOF)
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "rparen )", currentToken()));
|
||||
}
|
||||
if (!match(TokenType::Comma))
|
||||
{
|
||||
return std::unexpected(
|
||||
makeUnexpectTokenError("FnTypeExpr", "comma ,", currentToken()));
|
||||
}
|
||||
}
|
||||
|
||||
Expr *returnType = nullptr;
|
||||
|
||||
if (match(TokenType::RightArrow)) // ->
|
||||
{
|
||||
auto result = parseTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
returnType = *result;
|
||||
}
|
||||
|
||||
FnTypeExpr *fnTypeExpr = arena.Allocate<FnTypeExpr>(paraTypes, returnType);
|
||||
return fnTypeExpr;
|
||||
}
|
||||
|
||||
|
||||
Result<Expr *, Error> Parser::parseTypeExpr()
|
||||
{
|
||||
Expr *base = nullptr;
|
||||
|
||||
if (currentToken().isIdentifier())
|
||||
{
|
||||
auto result = parseNamedTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else if (currentToken().type == TokenType::Function)
|
||||
{
|
||||
auto result = parseFnTypeExpr();
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
base = *result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::unexpected(makeUnexpectTokenError("TypeExpr", "name", currentToken()));
|
||||
}
|
||||
|
||||
// nullable
|
||||
if (currentToken().type == TokenType::Question) // ?
|
||||
{
|
||||
base = arena.Allocate<NullableExpr>(
|
||||
base, makeSourceLocation(consumeToken())); // consume `?`
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,311 +0,0 @@
|
||||
/*!
|
||||
@file src/Repl/Repl.hpp
|
||||
@brief Repl定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-11
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <Core/SourceLocations.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
#include <Utils/ConsoleSize.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Repl
|
||||
{
|
||||
private:
|
||||
size_t rline = 1;
|
||||
|
||||
std::istream ∈
|
||||
std::ostream &out;
|
||||
std::ostream &err;
|
||||
|
||||
public:
|
||||
Repl(std::istream &_in, std::ostream &_out, std::ostream &_err) :
|
||||
in(_in), out(_out), err(_err)
|
||||
{
|
||||
}
|
||||
|
||||
void PrintInfo()
|
||||
{
|
||||
out << std::format(
|
||||
"Fig {}, copyright (c) 2025-2026 PuqiAR, under the {} License\n",
|
||||
Core::VERSION,
|
||||
Core::LICENSE);
|
||||
out << std::format(
|
||||
"Build time: {} [{} x{} on {}]\n",
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM);
|
||||
out << "Type '#exit' to exit, '#clear' to clear the the screen, '#license' to see the full license, '#logo' to see a GREAT logo\n";
|
||||
out << '\n';
|
||||
}
|
||||
|
||||
void ClearConsole()
|
||||
{
|
||||
// \033[2J: 清除整个屏幕
|
||||
// \033[H: 将光标移动到左上角
|
||||
out << "\033[2J\033[H" << std::flush;
|
||||
}
|
||||
|
||||
void PrintLicense()
|
||||
{
|
||||
static DynArray<std::string> license_lines{};
|
||||
if (license_lines.empty())
|
||||
{
|
||||
std::string l;
|
||||
bool last_was_r = false;
|
||||
|
||||
for (size_t i = 0; i < Core::LICENSE_TEXT.size(); ++i)
|
||||
{
|
||||
char c = Core::LICENSE_TEXT[i];
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
last_was_r = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
if (!l.empty() || last_was_r)
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
l.clear();
|
||||
}
|
||||
last_was_r = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (last_was_r)
|
||||
{
|
||||
if (!l.empty())
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
l.clear();
|
||||
}
|
||||
last_was_r = false;
|
||||
}
|
||||
|
||||
l += c;
|
||||
}
|
||||
|
||||
if (!l.empty() || last_was_r)
|
||||
{
|
||||
license_lines.push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int lines_per_page = 50;
|
||||
unsigned int page_printed = 0;
|
||||
unsigned int lines_printed = 0;
|
||||
unsigned int total_lines = license_lines.size();
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto _csize = Utils::getConsoleSize();
|
||||
if (_csize)
|
||||
{
|
||||
lines_per_page = static_cast<unsigned int>((*_csize).first * 0.75);
|
||||
if (lines_per_page < 1)
|
||||
lines_per_page = 1;
|
||||
}
|
||||
|
||||
unsigned int lines_this_page =
|
||||
std::min(lines_per_page, total_lines - lines_printed);
|
||||
|
||||
for (unsigned int i = 0; i < lines_this_page; ++i)
|
||||
{
|
||||
out << license_lines[lines_printed + i] << '\n';
|
||||
}
|
||||
|
||||
lines_printed += lines_this_page;
|
||||
page_printed++;
|
||||
|
||||
if (lines_printed >= total_lines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int pages_total = (total_lines + lines_per_page - 1) / lines_per_page;
|
||||
|
||||
out << "\n"
|
||||
<< std::format(
|
||||
"Press any key to continue... ({}/{})", page_printed, pages_total);
|
||||
|
||||
in.get();
|
||||
out << '\n';
|
||||
++rline;
|
||||
}
|
||||
}
|
||||
// void PrintError(const Error &error, const String &source)
|
||||
// {
|
||||
// err << "Oops! An error occurred!";
|
||||
// err << "🔥 " << 'E' << static_cast<int>(error.type) << ": " << error.message << '\n';
|
||||
// err << "Line " << rline << ", `" << source << "`\n";
|
||||
// err << "Suggestion: " << error.suggestion << '\n';
|
||||
// err << std::format(
|
||||
// "Thrower: {} ({}:{}:{})\n",
|
||||
// error.thrower_loc.function_name(),
|
||||
// error.thrower_loc.file_name(),
|
||||
// error.thrower_loc.line(),
|
||||
// error.thrower_loc.column());
|
||||
// }
|
||||
|
||||
void PrintError(const Error &error, const String &source)
|
||||
{
|
||||
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 *const &level_color =
|
||||
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||
|
||||
err << "\n";
|
||||
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';
|
||||
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||
err << source << "\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';
|
||||
}
|
||||
|
||||
unsigned int Start() // exit code: unsigned int
|
||||
{
|
||||
// TODO: 多行输入 Repl
|
||||
|
||||
PrintInfo();
|
||||
|
||||
String fileName = "[REPL]";
|
||||
String filePath = "src/Repl/Repl.hpp";
|
||||
|
||||
SourceManager manager;
|
||||
|
||||
VM vm;
|
||||
|
||||
Value v = Value::GetNullInstance();
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string buf;
|
||||
|
||||
out << ">";
|
||||
|
||||
std::getline(in, buf);
|
||||
|
||||
if (buf.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#exit")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (buf == "#clear")
|
||||
{
|
||||
ClearConsole();
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#license")
|
||||
{
|
||||
PrintLicense();
|
||||
continue;
|
||||
}
|
||||
else if (buf == "#logo")
|
||||
{
|
||||
out << Core::LOGO << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
String source(buf);
|
||||
|
||||
Lexer lexer(buf, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto _program = parser.Parse();
|
||||
if (!_program)
|
||||
{
|
||||
PrintError(_program.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
Program *program = *_program;
|
||||
|
||||
Analyzer analyzer(manager);
|
||||
auto result = analyzer.Analyze(program);
|
||||
|
||||
analyzer.GetDiagnostics().EmitAll(manager);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
PrintError(result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_result = compiler.Compile(program);
|
||||
if (!compile_result)
|
||||
{
|
||||
PrintError(compile_result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
auto exe_result = vm.Execute(compiledModule);
|
||||
if (!exe_result)
|
||||
{
|
||||
PrintError(exe_result.error(), source);
|
||||
continue;
|
||||
}
|
||||
|
||||
v = *exe_result;
|
||||
if (!v.IsNull())
|
||||
{
|
||||
out << v.ToString() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return (v.IsInt() ? v.AsInt() : 0);
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <Core/Core.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace Fig;
|
||||
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
|
||||
|
||||
repl.Start();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*!
|
||||
@file src/Sema/Analyzer.hpp
|
||||
@brief 语义分析
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-06-06
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Ast.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
#include <Error/Diagnostics.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Analyzer
|
||||
{
|
||||
public:
|
||||
Analyzer(SourceManager &) {}
|
||||
|
||||
Result<void, Error> Analyze(Program *)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
Diagnostics &GetDiagnostics() { return diag; }
|
||||
|
||||
private:
|
||||
Diagnostics diag;
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,63 +0,0 @@
|
||||
/*!
|
||||
@file src/Sema/Environment.hpp
|
||||
@brief 符号表
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-07-05
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <Sema/Type.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class SymbolKind : uint8_t
|
||||
{
|
||||
Var,
|
||||
Const,
|
||||
Func,
|
||||
Type,
|
||||
};
|
||||
|
||||
struct Symbol
|
||||
{
|
||||
String name;
|
||||
Type type;
|
||||
SymbolKind kind;
|
||||
int index; // local: register idx, global: global idx, upvalue: upvalue idx
|
||||
|
||||
bool isType() const { return kind == SymbolKind::Type; }
|
||||
};
|
||||
|
||||
struct Scope
|
||||
{
|
||||
Scope *parent = nullptr;
|
||||
bool isFnBoundary = false;
|
||||
HashMap<String, Symbol *> locals;
|
||||
int nextReg = 0;
|
||||
};
|
||||
|
||||
class Environment
|
||||
{
|
||||
public:
|
||||
Scope *current = nullptr;
|
||||
|
||||
void push(bool isFn)
|
||||
{
|
||||
auto *s = new Scope;
|
||||
s->parent = current;
|
||||
s->isFnBoundary = isFn;
|
||||
if (current && !isFn)
|
||||
s->nextReg = current->nextReg;
|
||||
current = s;
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
auto *old = current;
|
||||
current = current->parent;
|
||||
delete old;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,36 +0,0 @@
|
||||
/*!
|
||||
@file src/Sema/Type.hpp
|
||||
@brief 类型系统
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-07-05
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Object/ObjectBase.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
TypeObject *obj = nullptr;
|
||||
bool isNullable = false;
|
||||
|
||||
bool is(TypeTag t) const;
|
||||
bool isAssignableTo(const Type &target) const;
|
||||
};
|
||||
|
||||
inline bool Type::is(TypeTag t) const
|
||||
{
|
||||
return obj && obj->tag == t;
|
||||
}
|
||||
|
||||
inline bool Type::isAssignableTo(const Type &target) const
|
||||
{
|
||||
if (target.is(TypeTag::Any) || is(TypeTag::Any))
|
||||
return true;
|
||||
if (is(TypeTag::Null) && target.isNullable)
|
||||
return true;
|
||||
return obj == target.obj && (!isNullable || target.isNullable);
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,115 +0,0 @@
|
||||
/*!
|
||||
@file src/Utils/Arena.hpp
|
||||
@brief 线性分配内存池,支持非平凡析构对象的自动清理
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-08
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Arena
|
||||
{
|
||||
private:
|
||||
struct DestructorNode
|
||||
{
|
||||
void (*destructor)(void *);
|
||||
void *object;
|
||||
DestructorNode *next;
|
||||
};
|
||||
|
||||
static constexpr std::size_t CHUNK_SIZE = 64 * 1024; // 64KB 块大小
|
||||
|
||||
std::vector<char *> chunks;
|
||||
char *currentPtr = nullptr;
|
||||
std::size_t remaining = 0;
|
||||
DestructorNode *destructorHead = nullptr;
|
||||
|
||||
public:
|
||||
Arena() = default;
|
||||
|
||||
~Arena()
|
||||
{
|
||||
// 逆序调用析构函数
|
||||
DestructorNode *node = destructorHead;
|
||||
while (node)
|
||||
{
|
||||
node->destructor(node->object);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
// 释放所有分配的内存块
|
||||
for (char *chunk : chunks)
|
||||
{
|
||||
delete[] chunk;
|
||||
}
|
||||
}
|
||||
|
||||
// 禁止拷贝和移动,防止内存所有权混乱
|
||||
Arena(const Arena &) = delete;
|
||||
Arena &operator=(const Arena &) = delete;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T *Allocate(Args &&...args)
|
||||
{
|
||||
std::size_t size = sizeof(T);
|
||||
std::size_t alignment = alignof(T);
|
||||
|
||||
// 在当前块中尝试对齐并分配
|
||||
void *ptr = allocateRaw(size, alignment);
|
||||
|
||||
// 在分配的内存上构造对象
|
||||
T *obj = new (ptr) T(std::forward<Args>(args)...);
|
||||
|
||||
// 如果 T 需要析构(如包含 String),注册到销毁链表
|
||||
if constexpr (!std::is_trivially_destructible_v<T>)
|
||||
{
|
||||
// 注意: DestructorNode 本身是 POD,直接在 Arena 里分配,不需要注册析构
|
||||
void *nodeRaw = allocateRaw(sizeof(DestructorNode), alignof(DestructorNode));
|
||||
DestructorNode *node = new (nodeRaw) DestructorNode();
|
||||
|
||||
node->object = obj;
|
||||
node->destructor = [](void *p) { static_cast<T *>(p)->~T(); };
|
||||
node->next = destructorHead;
|
||||
destructorHead = node;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
void *allocateRaw(std::size_t size, std::size_t alignment)
|
||||
{
|
||||
// 对齐计算
|
||||
std::size_t adjustment = 0;
|
||||
std::size_t currentAddr = reinterpret_cast<std::size_t>(currentPtr);
|
||||
if (alignment > 0 && (currentAddr % alignment) != 0)
|
||||
{
|
||||
adjustment = alignment - (currentAddr % alignment);
|
||||
}
|
||||
|
||||
if (remaining < (size + adjustment))
|
||||
{
|
||||
// 当前块空间不足,分配新块
|
||||
std::size_t nextChunkSize = (size > CHUNK_SIZE) ? size : CHUNK_SIZE;
|
||||
currentPtr = new char[nextChunkSize];
|
||||
chunks.push_back(currentPtr);
|
||||
remaining = nextChunkSize;
|
||||
adjustment = 0; // 新分配的块通常是最大对齐的
|
||||
}
|
||||
|
||||
currentPtr += adjustment;
|
||||
void *res = currentPtr;
|
||||
currentPtr += size;
|
||||
remaining -= (size + adjustment);
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,336 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace Fig::ArgParser
|
||||
{
|
||||
|
||||
enum class ParseErrorCode
|
||||
{
|
||||
UnknownOption,
|
||||
MissingValue,
|
||||
MissingRequired,
|
||||
InvalidFormat
|
||||
};
|
||||
|
||||
struct ParseError
|
||||
{
|
||||
ParseErrorCode code;
|
||||
String context;
|
||||
|
||||
String Format() const
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case ParseErrorCode::UnknownOption:
|
||||
return String(std::format("Unknown option: {}", context.toStdString()));
|
||||
case ParseErrorCode::MissingValue:
|
||||
return String(
|
||||
std::format("Missing value for option: {}", context.toStdString()));
|
||||
case ParseErrorCode::MissingRequired:
|
||||
return String(
|
||||
std::format("Missing required option: {}", context.toStdString()));
|
||||
case ParseErrorCode::InvalidFormat:
|
||||
return String(
|
||||
std::format("Invalid argument format: {}", context.toStdString()));
|
||||
default: return String("Unknown parse error");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OptionDef
|
||||
{
|
||||
public:
|
||||
OptionDef(char short_name, String long_name) :
|
||||
short_name_(short_name), long_name_(std::move(long_name))
|
||||
{
|
||||
}
|
||||
|
||||
OptionDef &Help(String help_text)
|
||||
{
|
||||
help_ = std::move(help_text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &DefaultValue(String value)
|
||||
{
|
||||
default_value_ = std::move(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &Required(bool required = true)
|
||||
{
|
||||
required_ = required;
|
||||
return *this;
|
||||
}
|
||||
|
||||
OptionDef &TakesValue(bool takes_value = true)
|
||||
{
|
||||
takes_value_ = takes_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
char short_name_;
|
||||
String long_name_;
|
||||
String help_;
|
||||
std::optional<String> default_value_;
|
||||
bool required_ = false;
|
||||
bool takes_value_ = false;
|
||||
};
|
||||
|
||||
class ParseResult
|
||||
{
|
||||
public:
|
||||
bool HasFlag(const String &long_name) const
|
||||
{
|
||||
auto it = options_.find(long_name);
|
||||
return it != options_.end() && it->second == String("true");
|
||||
}
|
||||
|
||||
std::optional<String> GetOption(const String &long_name) const
|
||||
{
|
||||
if (auto it = options_.find(long_name); it != options_.end())
|
||||
return it->second;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const DynArray<String> &GetPositionals() const
|
||||
{
|
||||
return positionals_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ArgumentParser;
|
||||
HashMap<String, String> options_;
|
||||
DynArray<String> positionals_;
|
||||
};
|
||||
|
||||
class ArgumentParser
|
||||
{
|
||||
public:
|
||||
explicit ArgumentParser(String prog_name, String description = String("")) :
|
||||
prog_name_(std::move(prog_name)), description_(std::move(description))
|
||||
{
|
||||
}
|
||||
|
||||
OptionDef &AddFlag(char short_name, const String &long_name)
|
||||
{
|
||||
return registerOption(short_name, long_name).TakesValue(false);
|
||||
}
|
||||
|
||||
OptionDef &AddFlag(const String &long_name)
|
||||
{
|
||||
return registerOption('\0', long_name).TakesValue(false);
|
||||
}
|
||||
|
||||
OptionDef &AddOption(char short_name, const String &long_name)
|
||||
{
|
||||
return registerOption(short_name, long_name).TakesValue(true);
|
||||
}
|
||||
|
||||
OptionDef &AddOption(const String &long_name)
|
||||
{
|
||||
return registerOption('\0', long_name).TakesValue(true);
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> Parse(int argc, const char *const *argv) const
|
||||
{
|
||||
if (argc <= 0)
|
||||
return ParseResult{};
|
||||
|
||||
return parseInternal(
|
||||
std::span<const char *const>{argv + 1, static_cast<size_t>(argc - 1)})
|
||||
.and_then([this](ParseResult res) { return validateRequired(std::move(res)); })
|
||||
.and_then([this](ParseResult res) { return applyDefaults(std::move(res)); });
|
||||
}
|
||||
|
||||
String FormatHelp() const
|
||||
{
|
||||
std::string help =
|
||||
std::format("Usage: {} [OPTIONS] [ARGS...]\n\n", prog_name_.toStdString());
|
||||
|
||||
if (!description_.empty())
|
||||
help += std::format("{}\n\n", description_.toStdString());
|
||||
|
||||
help += "Options:\n";
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
std::string flags;
|
||||
if (opt.short_name_ != '\0')
|
||||
flags = std::format("-{}, --{}", opt.short_name_, opt.long_name_.toStdString());
|
||||
else
|
||||
flags = std::format(" --{}", opt.long_name_.toStdString());
|
||||
|
||||
if (opt.takes_value_)
|
||||
flags += " <val>";
|
||||
|
||||
std::string desc = opt.help_.toStdString();
|
||||
if (opt.default_value_)
|
||||
desc += std::format(" (default: {})", opt.default_value_->toStdString());
|
||||
if (opt.required_)
|
||||
desc += " [required]";
|
||||
|
||||
help += std::format(" {:<25} {}\n", flags, desc);
|
||||
}
|
||||
|
||||
return String(help);
|
||||
}
|
||||
|
||||
private:
|
||||
OptionDef ®isterOption(char short_name, const String &long_name)
|
||||
{
|
||||
size_t idx = options_.size();
|
||||
options_.emplace_back(short_name, long_name);
|
||||
long_map_[long_name] = idx;
|
||||
if (short_name != '\0')
|
||||
short_map_[short_name] = idx;
|
||||
return options_.back();
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> parseInternal(std::span<const char *const> args) const
|
||||
{
|
||||
ParseResult result;
|
||||
bool only_positionals = false;
|
||||
|
||||
for (size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
std::string_view arg{args[i]};
|
||||
|
||||
if (only_positionals)
|
||||
{
|
||||
result.positionals_.emplace_back(String(std::string(arg)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg == "--")
|
||||
{
|
||||
only_positionals = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg.starts_with("--"))
|
||||
{
|
||||
auto kv = arg.substr(2);
|
||||
if (kv.empty())
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::InvalidFormat, String("--")});
|
||||
|
||||
auto eq_pos = kv.find('=');
|
||||
auto key = kv.substr(0, eq_pos);
|
||||
auto key_s = String(std::string(key));
|
||||
|
||||
auto it = long_map_.find(key_s);
|
||||
if (it == long_map_.end())
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::UnknownOption, String(std::string(arg))});
|
||||
|
||||
const auto &def = options_[it->second];
|
||||
|
||||
if (def.takes_value_)
|
||||
{
|
||||
if (eq_pos != std::string_view::npos)
|
||||
{
|
||||
result.options_[def.long_name_] =
|
||||
String(std::string(kv.substr(eq_pos + 1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i + 1 >= args.size())
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::MissingValue, String(std::string(arg))});
|
||||
result.options_[def.long_name_] = String(std::string(args[++i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (eq_pos != std::string_view::npos)
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::InvalidFormat, String(std::string(arg))});
|
||||
result.options_[def.long_name_] = String("true");
|
||||
}
|
||||
}
|
||||
else if (arg.starts_with('-') && arg.size() > 1)
|
||||
{
|
||||
auto group = arg.substr(1);
|
||||
for (size_t j = 0; j < group.size(); ++j)
|
||||
{
|
||||
char c = group[j];
|
||||
auto it = short_map_.find(c);
|
||||
if (it == short_map_.end())
|
||||
return std::unexpected(ParseError{
|
||||
ParseErrorCode::UnknownOption, String(std::string(1, c))});
|
||||
|
||||
const auto &def = options_[it->second];
|
||||
|
||||
if (def.takes_value_)
|
||||
{
|
||||
if (j + 1 < group.size())
|
||||
{
|
||||
result.options_[def.long_name_] =
|
||||
String(std::string(group.substr(j + 1)));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i + 1 >= args.size())
|
||||
return std::unexpected(ParseError{ParseErrorCode::MissingValue,
|
||||
String(std::format("-{}", c))});
|
||||
result.options_[def.long_name_] = String(std::string(args[++i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.options_[def.long_name_] = String("true");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.positionals_.emplace_back(String(std::string(arg)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> validateRequired(ParseResult res) const
|
||||
{
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
if (opt.required_ && res.options_.find(opt.long_name_) == res.options_.end())
|
||||
{
|
||||
std::string flag =
|
||||
opt.short_name_ != '\0' ?
|
||||
std::format("-{}/--{}", opt.short_name_, opt.long_name_.toStdString()) :
|
||||
std::format("--{}", opt.long_name_.toStdString());
|
||||
return std::unexpected(
|
||||
ParseError{ParseErrorCode::MissingRequired, String(flag)});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Result<ParseResult, ParseError> applyDefaults(ParseResult res) const
|
||||
{
|
||||
for (const auto &opt : options_)
|
||||
{
|
||||
if (opt.default_value_ && res.options_.find(opt.long_name_) == res.options_.end())
|
||||
{
|
||||
res.options_[opt.long_name_] = *opt.default_value_;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
String prog_name_;
|
||||
String description_;
|
||||
DynArray<OptionDef> options_;
|
||||
HashMap<String, size_t> long_map_;
|
||||
HashMap<char, size_t> short_map_;
|
||||
};
|
||||
|
||||
} // namespace Fig::ArgParser
|
||||
@@ -1,88 +0,0 @@
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace Fig::Utils
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取控制台窗口的大小(行数和列数)
|
||||
* @return 如果成功,返回包含 (rows, cols) 的 optional;失败返回 std::nullopt
|
||||
*/
|
||||
|
||||
inline std::optional<std::pair<int, int>> getConsoleSize()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Windows: GetConsoleScreenBufferInfo
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hConsole, &csbi))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 窗口大小 = 右下角坐标 - 左上角坐标 + 1
|
||||
int cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
|
||||
return std::make_pair(rows, cols);
|
||||
|
||||
#else
|
||||
// Linux / macOS / Unix: ioctl
|
||||
struct winsize w;
|
||||
|
||||
// 标准输出被重定向到文件 ?
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
{
|
||||
// 不是终端,获取环境变量
|
||||
char *cols_env = getenv("COLUMNS");
|
||||
char *rows_env = getenv("LINES");
|
||||
if (cols_env && rows_env)
|
||||
{
|
||||
int cols = std::stoi(cols_env);
|
||||
int rows = std::stoi(rows_env);
|
||||
return std::make_pair(rows, cols);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int cols = w.ws_col;
|
||||
int rows = w.ws_row;
|
||||
|
||||
// 如果 ws_col 或 ws_row 为 0,使用环境变量
|
||||
if (cols == 0 || rows == 0)
|
||||
{
|
||||
char *cols_env = getenv("COLUMNS");
|
||||
char *rows_env = getenv("LINES");
|
||||
if (cols_env)
|
||||
cols = std::stoi(cols_env);
|
||||
if (rows_env)
|
||||
rows = std::stoi(rows_env);
|
||||
}
|
||||
|
||||
if (cols > 0 && rows > 0)
|
||||
{
|
||||
return std::make_pair(rows, cols);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Fig::Utils
|
||||
25526
src/Utils/json/json.hpp
25526
src/Utils/json/json.hpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
199
src/VM/Entry.cpp
199
src/VM/Entry.cpp
@@ -1,199 +0,0 @@
|
||||
/*!
|
||||
@file src/VM/Entry.hpp
|
||||
@brief vm入口实现
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-13
|
||||
*/
|
||||
|
||||
#include <VM/Entry.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <SourceManager/SourceManager.hpp>
|
||||
|
||||
#include <Bytecode/Disassembler.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <Lexer/Lexer.hpp>
|
||||
#include <Parser/Parser.hpp>
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <Sema/Analyzer.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
void RunFromPath(const String &path, const Config &conf)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
auto format_print_time = [](std::chrono::nanoseconds nsecs) {
|
||||
auto &out = CoreIO::GetStdOut();
|
||||
auto count = nsecs.count();
|
||||
|
||||
auto old_flags = out.flags();
|
||||
auto old_precision = out.precision();
|
||||
|
||||
if (count < 1'000)
|
||||
{
|
||||
// < 1μs 纳秒
|
||||
out << count << "ns";
|
||||
}
|
||||
else if (count < 1'000'000)
|
||||
{
|
||||
// 1μs ~ 1ms 微秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000.0) << "μs";
|
||||
}
|
||||
else if (count < 1'000'000'000)
|
||||
{
|
||||
// 1ms ~ 1s 毫秒 保留 2 位小数
|
||||
out << std::fixed << std::setprecision(2) << (count / 1'000'000.0) << "ms";
|
||||
}
|
||||
else
|
||||
{
|
||||
// >= 1s 秒 保留 3 位小数
|
||||
out << std::fixed << std::setprecision(3) << (count / 1'000'000'000.0) << "s";
|
||||
}
|
||||
|
||||
out.flags(old_flags);
|
||||
out.precision(old_precision);
|
||||
};
|
||||
|
||||
fs::path _fspath(path.toStdString());
|
||||
|
||||
if (!fs::exists(_fspath))
|
||||
{
|
||||
CoreIO::GetStdErr() << "File not found: " << path << '\n';
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
if (!_fspath.has_extension() || _fspath.extension() != ".fig")
|
||||
{
|
||||
CoreIO::GetStdErr() << "Not a valid Fig-lang source code\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
String fileName(_fspath.filename().string());
|
||||
|
||||
SourceManager manager(path);
|
||||
manager.Read();
|
||||
|
||||
if (!manager.read)
|
||||
{
|
||||
CoreIO::GetStdErr() << "Could not read file: " << path << '\n';
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
const String &source = manager.GetSource();
|
||||
|
||||
Lexer lexer(source, fileName);
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
Parser parser(lexer, manager, fileName, diagnostics);
|
||||
|
||||
auto parse_start = clock::now();
|
||||
auto parse_result = parser.Parse();
|
||||
auto parse_end = clock::now();
|
||||
|
||||
if (!parse_result)
|
||||
{
|
||||
ReportError(parse_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Program *program = *parse_result;
|
||||
|
||||
Analyzer analyer(manager);
|
||||
|
||||
auto analyze_start = clock::now();
|
||||
auto analyze_result = analyer.Analyze(program);
|
||||
auto analyze_end = clock::now();
|
||||
|
||||
if (!analyze_result)
|
||||
{
|
||||
ReportError(analyze_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
Compiler compiler(manager, diagnostics);
|
||||
|
||||
auto compile_start = clock::now();
|
||||
auto compile_result = compiler.Compile(program);
|
||||
auto compile_end = clock::now();
|
||||
|
||||
diagnostics.EmitAll(manager);
|
||||
|
||||
if (!compile_result)
|
||||
{
|
||||
ReportError(compile_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
CompiledModule *compiledModule = *compile_result;
|
||||
|
||||
if (conf.dump)
|
||||
{
|
||||
Disassembler disassembler;
|
||||
disassembler.DisassembleModule(compiledModule);
|
||||
}
|
||||
|
||||
VM vm;
|
||||
|
||||
auto execute_start = clock::now();
|
||||
auto execute_result = vm.Execute(compiledModule);
|
||||
auto execute_end = clock::now();
|
||||
|
||||
if (!execute_result)
|
||||
{
|
||||
ReportError(execute_result.error(), manager);
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
if (conf.pregs)
|
||||
{
|
||||
vm.PrintRegisters();
|
||||
}
|
||||
|
||||
if (conf.time)
|
||||
{
|
||||
auto parse_time = parse_end - parse_start;
|
||||
CoreIO::GetStdOut() << "Parse: ";
|
||||
format_print_time(parse_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto analyze_time = analyze_end - analyze_start;
|
||||
CoreIO::GetStdOut() << "Analyze: ";
|
||||
format_print_time(analyze_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto compile_time = compile_end - compile_start;
|
||||
CoreIO::GetStdOut() << "Compile: ";
|
||||
format_print_time(compile_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto execute_time = execute_end - execute_start;
|
||||
CoreIO::GetStdOut() << "Execute: ";
|
||||
format_print_time(execute_time);
|
||||
CoreIO::GetStdOut() << " | ";
|
||||
|
||||
auto total = parse_time + analyze_time + compile_time + execute_time;
|
||||
CoreIO::GetStdOut() << "Total: ";
|
||||
format_print_time(total);
|
||||
CoreIO::GetStdOut() << '\n';
|
||||
}
|
||||
|
||||
delete compiledModule;
|
||||
}
|
||||
|
||||
std::uint32_t RunRepl()
|
||||
{
|
||||
Repl repl(CoreIO::GetStdCin(), CoreIO::GetStdOut(), CoreIO::GetStdErr());
|
||||
std::uint32_t result = repl.Start();
|
||||
|
||||
CoreIO::GetStdOut() << "Repl exited with code " << result << '\n';
|
||||
return result;
|
||||
}
|
||||
}; // namespace Fig::Entry
|
||||
@@ -1,26 +0,0 @@
|
||||
/*!
|
||||
@file src/VM/Entry.hpp
|
||||
@brief vm入口定义
|
||||
@author PuqiAR (im@puqiar.top)
|
||||
@date 2026-03-13
|
||||
*/
|
||||
|
||||
#include <Deps/Deps.hpp>
|
||||
|
||||
namespace Fig::Entry
|
||||
{
|
||||
struct Config
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Debug,
|
||||
Normal
|
||||
} mode;
|
||||
bool dump;
|
||||
bool pregs;
|
||||
bool time;
|
||||
};
|
||||
|
||||
void RunFromPath(const String &, const Config &conf);
|
||||
std::uint32_t RunRepl();
|
||||
};
|
||||
442
src/VM/VM.cpp
442
src/VM/VM.cpp
@@ -1,442 +0,0 @@
|
||||
/*!
|
||||
@file src/VM/VM.cpp
|
||||
@brief 虚拟机核心执行引擎实现
|
||||
*/
|
||||
|
||||
#include <Core/Core.hpp>
|
||||
#include <VM/VM.hpp>
|
||||
|
||||
#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];
|
||||
(void) pushFrame(entry, registers); // 刚开始执行寄存器不会溢出
|
||||
|
||||
// 对齐 Bytecode.hpp 中的 OpCode 顺序
|
||||
static const void *dispatchTable[] = {&&do_Exit,
|
||||
&&do_Exit_MaxRecursionDepthExceeded,
|
||||
|
||||
&&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_IntFastAdd,
|
||||
&&do_IntFastSub,
|
||||
&&do_IntFastMul,
|
||||
&&do_IntFastDiv,
|
||||
|
||||
&&do_Equal,
|
||||
&&do_NotEqual,
|
||||
&&do_Greater,
|
||||
&&do_Less,
|
||||
&&do_GreaterEqual,
|
||||
&&do_LessEqual,
|
||||
|
||||
&&do_GetGlobal,
|
||||
&&do_SetGlobal,
|
||||
&&do_GetUpval,
|
||||
&&do_SetUpval,
|
||||
&&do_Copy,
|
||||
|
||||
&&do_Count};
|
||||
|
||||
Instruction inst;
|
||||
|
||||
#define DISPATCH() \
|
||||
do \
|
||||
{ \
|
||||
inst = *(currentFrame->ip++); \
|
||||
goto *dispatchTable[inst & 0xFF]; \
|
||||
} while (0)
|
||||
|
||||
// 引擎点火!! :3
|
||||
DISPATCH();
|
||||
|
||||
do_Exit: {
|
||||
return Value::FromInt(decodeSBx(inst));
|
||||
}
|
||||
|
||||
do_Exit_MaxRecursionDepthExceeded: {
|
||||
CoreIO::GetStdErr() << std::format(
|
||||
"Oops! max recursion depth limit {} exceeded in Fn `{}` , exiting...\n",
|
||||
MAX_RECURSION_DEPTH,
|
||||
(currentFrame - 1)->proto->name); // pushFrame失败了,但currentFrame仍然移动,所以
|
||||
// (currentFrame - 1)是 lastFrame
|
||||
std::exit(static_cast<int>(MAX_RECURSION_DEPTH));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
currentFrame->ip = pushFrame(proto, currentFrame->registerBase + baseReg);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Call: {
|
||||
// TODO: FunctionObject 动态解包
|
||||
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t baseReg = decodeB(inst);
|
||||
|
||||
Value callee = currentFrame->registerBase[a];
|
||||
|
||||
FunctionObject *closure = nullptr;
|
||||
|
||||
if (!callee.IsObject())
|
||||
{
|
||||
size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data();
|
||||
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
std::format("Object `{}` is not callable", callee.ToString()),
|
||||
"none",
|
||||
*currentFrame->proto->locations[ipIdx]));
|
||||
}
|
||||
else [[likely]]
|
||||
{
|
||||
|
||||
Object *obj = callee.AsObject();
|
||||
if (!obj->isFunction())
|
||||
{
|
||||
size_t ipIdx = currentFrame->ip - currentFrame->proto->code.data();
|
||||
|
||||
return std::unexpected(Error(ErrorType::TypeError,
|
||||
std::format("Object `{}` is not callable", callee.ToString()),
|
||||
"none",
|
||||
*currentFrame->proto->locations[ipIdx]));
|
||||
}
|
||||
closure = static_cast<FunctionObject *>(obj);
|
||||
}
|
||||
|
||||
currentFrame->ip = pushFrame(closure, currentFrame->registerBase + baseReg);
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Return: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
Value retVal = currentFrame->registerBase[a];
|
||||
|
||||
closeUpvalues(currentFrame->registerBase);
|
||||
|
||||
// 此时 registerBase[0] 指向的是 Caller 的 baseReg 槽位
|
||||
currentFrame->registerBase[0] = retVal;
|
||||
popFrame();
|
||||
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_LoadFn: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
|
||||
Proto *p = compiledModule->protos[bx];
|
||||
|
||||
size_t upValSize = p->upvalues.size();
|
||||
size_t extraSize = upValSize * sizeof(Upvalue *);
|
||||
|
||||
FunctionObject *closure =
|
||||
(FunctionObject *) allocateObject<FunctionObject>(ObjectType::Function, extraSize);
|
||||
|
||||
// CoreIO::GetStdErr() << "DEBUG: p->name = " << p->name << '\n';
|
||||
new (&closure->name) String(p->name); // String非平凡类型,有自己的构造函数
|
||||
closure->proto = p;
|
||||
closure->paraCount = p->numParams;
|
||||
closure->upvalueCount = static_cast<std::uint32_t>(upValSize);
|
||||
|
||||
for (size_t i = 0; i < closure->upvalueCount; ++i)
|
||||
{
|
||||
auto &info = p->upvalues[i];
|
||||
if (info.isLocal)
|
||||
{
|
||||
Value *targetSlot = ¤tFrame->registerBase[info.index];
|
||||
Upvalue *prev = nullptr;
|
||||
Upvalue *curr = openUpvalues;
|
||||
|
||||
while (curr != nullptr && curr->location > targetSlot)
|
||||
{
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
if (curr != nullptr && curr->location == targetSlot)
|
||||
{
|
||||
// 如果别的闭包已经捕获了这个槽位,共享物理指针
|
||||
closure->upvalues[i] = curr;
|
||||
++curr->refCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 首次捕获
|
||||
Upvalue *uv = new Upvalue;
|
||||
|
||||
uv->location = targetSlot;
|
||||
uv->next = curr;
|
||||
++uv->refCount;
|
||||
|
||||
if (prev == nullptr)
|
||||
openUpvalues = uv;
|
||||
else
|
||||
prev->next = uv;
|
||||
closure->upvalues[i] = uv;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
closure->upvalues[i] = currentFrame->closure->upvalues[info.index];
|
||||
}
|
||||
}
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromObject(closure);
|
||||
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();
|
||||
|
||||
do_IntFastAdd: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() + r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastSub: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() - r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastMul: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] = Value::FromInt(l.AsInt() * r.AsInt());
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_IntFastDiv: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
std::uint8_t c = decodeC(inst);
|
||||
|
||||
Value l = currentFrame->registerBase[b];
|
||||
Value r = currentFrame->registerBase[c];
|
||||
|
||||
currentFrame->registerBase[a] =
|
||||
Value::FromDouble(static_cast<double>(l.AsInt()) / r.AsInt());
|
||||
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_GetGlobal: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
currentFrame->registerBase[a] = globals[bx];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_SetGlobal: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint16_t bx = decodeBx(inst);
|
||||
globals[bx] = currentFrame->registerBase[a];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_GetUpval: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
|
||||
currentFrame->registerBase[a] = *(currentFrame->closure->upvalues[b]->location);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_SetUpval: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
*(currentFrame->closure->upvalues[b]->location) = currentFrame->registerBase[a]; // copy
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Copy: {
|
||||
std::uint8_t a = decodeA(inst);
|
||||
std::uint8_t b = decodeB(inst);
|
||||
currentFrame->registerBase[a] = currentFrame->registerBase[b];
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
do_Count: {
|
||||
assert(false && "Hit Count sentinel!");
|
||||
return Value::GetNullInstance();
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
411
src/VM/VM.hpp
411
src/VM/VM.hpp
@@ -1,411 +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 <Core/Core.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream> // debug
|
||||
#include <print>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct CallFrame
|
||||
{
|
||||
FunctionObject *closure; // 动态闭包Context (FastCall时 null)
|
||||
Proto *proto; // 当前执行的原型
|
||||
Instruction *ip; // 当前指令指针
|
||||
Value *registerBase; // 寄存器起点
|
||||
|
||||
inline Value getConstant(std::uint16_t idx)
|
||||
{
|
||||
return proto->constants[idx];
|
||||
}
|
||||
};
|
||||
|
||||
enum class GCPhase : std::uint8_t
|
||||
{
|
||||
Idle,
|
||||
MarkRoots,
|
||||
Marking,
|
||||
Sweeping
|
||||
};
|
||||
|
||||
class VM
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned int MAX_REGISTERS = 1024;
|
||||
static constexpr unsigned int MAX_GLOBALS = 65536;
|
||||
static constexpr unsigned int MAX_RECURSION_DEPTH = 2233;
|
||||
|
||||
Instruction POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
|
||||
// 一次性分配
|
||||
Value registers[MAX_REGISTERS];
|
||||
Value globals[MAX_GLOBALS];
|
||||
|
||||
CallFrame frames[MAX_RECURSION_DEPTH];
|
||||
CallFrame *currentFrame;
|
||||
CallFrame *frameLimit;
|
||||
|
||||
Upvalue *openUpvalues = nullptr;
|
||||
|
||||
// GC
|
||||
Object *objects = nullptr; // 链表头
|
||||
DynArray<Object *> grayStack;
|
||||
|
||||
size_t allocatedBytes = 0;
|
||||
size_t nextGC = 1024 * 1024; // byte, 1MB初始阈值
|
||||
GCPhase gcPhase = GCPhase::Idle;
|
||||
|
||||
private:
|
||||
inline void closeUpvalues(Value *level)
|
||||
{
|
||||
// 函数销毁时,逃逸即将销毁的变量
|
||||
while (openUpvalues && openUpvalues->location >= level)
|
||||
{
|
||||
Upvalue *upval = openUpvalues;
|
||||
upval->closedValue = *upval->location;
|
||||
upval->location = &upval->closedValue;
|
||||
openUpvalues = upval->next;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T *allocateObject(ObjectType type, size_t extraBytes)
|
||||
{
|
||||
|
||||
if (allocatedBytes > nextGC) // 超出阈值
|
||||
{
|
||||
switch (gcPhase)
|
||||
{
|
||||
case GCPhase::Idle: markRoots(); break;
|
||||
case GCPhase::Marking: stepMarking(); break;
|
||||
case GCPhase::Sweeping: sweep(); break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t totalSize = sizeof(T) + extraBytes;
|
||||
|
||||
// 物理分配
|
||||
T *obj = static_cast<T *>(std::malloc(totalSize));
|
||||
if (!obj) [[unlikely]]
|
||||
{
|
||||
// 分配失败
|
||||
CoreIO::GetStdErr() << "Oops! Object allocating failed! Exiting...\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// 构造 Header
|
||||
obj->type = type;
|
||||
obj->color = GCColor::Black;
|
||||
obj->klass = nullptr;
|
||||
obj->next = objects; // 插入全局追踪链表
|
||||
objects = obj;
|
||||
|
||||
allocatedBytes += totalSize;
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline void writeBarrier(Object *parent, Value childVal)
|
||||
{
|
||||
if (!childVal.IsObject())
|
||||
return; // 栈对象无需
|
||||
|
||||
Object *child = childVal.AsObject();
|
||||
// 三色不变式, 黑色对象绝对不能指向白色对象
|
||||
if (parent->color == GCColor::Black && child->color == GCColor::White)
|
||||
{
|
||||
child->color = GCColor::Gray;
|
||||
grayStack.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
inline void markValue(Value value)
|
||||
{
|
||||
if (!value.IsObject())
|
||||
return;
|
||||
|
||||
Object *obj = value.AsObject();
|
||||
if (obj && obj->color == GCColor::White)
|
||||
{
|
||||
obj->color = GCColor::Gray;
|
||||
grayStack.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void markRoots()
|
||||
{
|
||||
// 扫描全局变量
|
||||
for (std::uint32_t i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
markValue(globals[i]);
|
||||
}
|
||||
|
||||
// 扫描vm全部栈 [registers[0], currentFrame]
|
||||
if (currentFrame && currentFrame->proto)
|
||||
{
|
||||
Value *stackTop = currentFrame->registerBase + currentFrame->proto->maxRegisters;
|
||||
for (Value *slot = registers; slot < stackTop; ++slot)
|
||||
{
|
||||
markValue(*slot);
|
||||
}
|
||||
}
|
||||
|
||||
// 扫描逃逸链表 (Open Upvalues)
|
||||
for (Upvalue *uv = openUpvalues; uv != nullptr; uv = uv->next)
|
||||
{
|
||||
markValue(uv->closedValue);
|
||||
}
|
||||
|
||||
gcPhase = GCPhase::Marking;
|
||||
}
|
||||
|
||||
void stepMarking()
|
||||
{
|
||||
// 每次步进处理的对象数量
|
||||
constexpr int WORK_LIMIT = 64;
|
||||
int workCount = 0;
|
||||
|
||||
while (!grayStack.empty() && workCount++ < WORK_LIMIT)
|
||||
{
|
||||
Object *obj = grayStack.back();
|
||||
grayStack.pop_back();
|
||||
|
||||
// 标记为黑色:表示该对象及其子引用已处理完毕
|
||||
obj->color = GCColor::Black;
|
||||
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::Function: {
|
||||
auto *fn = static_cast<FunctionObject *>(obj);
|
||||
for (std::uint32_t i = 0; i < fn->upvalueCount; ++i)
|
||||
{
|
||||
if (fn->upvalues[i])
|
||||
markValue(*(fn->upvalues[i]->location));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::Instance: {
|
||||
auto *inst = static_cast<InstanceObject *>(obj);
|
||||
if (inst->klass)
|
||||
markValue(Value::FromObject(inst->klass));
|
||||
// 扫描所有实例字段
|
||||
std::uint8_t fieldCount = inst->klass ? inst->klass->fieldCount : 0;
|
||||
for (std::uint8_t i = 0; i < fieldCount; ++i)
|
||||
{
|
||||
markValue(inst->fields[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::Struct: {
|
||||
auto *st = static_cast<StructObject *>(obj);
|
||||
for (int i = 0; i < GetOperatorsSize(); ++i)
|
||||
{
|
||||
if (st->operators[i])
|
||||
markValue(Value::FromObject(st->operators[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ObjectType::String: break; // 叶子节点
|
||||
}
|
||||
}
|
||||
|
||||
if (grayStack.empty())
|
||||
gcPhase = GCPhase::Sweeping;
|
||||
}
|
||||
|
||||
void sweep()
|
||||
{
|
||||
Object **curr = &objects;
|
||||
size_t liveBytes = 0;
|
||||
|
||||
while (*curr != nullptr)
|
||||
{
|
||||
Object *obj = *curr;
|
||||
if (obj->color == GCColor::White)
|
||||
{
|
||||
*curr = obj->next;
|
||||
|
||||
// 函数 upvalue需要手动析构
|
||||
if (obj->type == ObjectType::Function)
|
||||
{
|
||||
auto *fn = static_cast<FunctionObject *>(obj);
|
||||
fn->name.~String();
|
||||
for (std::uint32_t i = 0; i < fn->upvalueCount; ++i)
|
||||
{
|
||||
Upvalue *uv = fn->upvalues[i];
|
||||
if (uv)
|
||||
{
|
||||
uv->refCount--; // 减引用
|
||||
if (uv->refCount == 0)
|
||||
{
|
||||
// 只有当所有闭包都死后,才 free 这个 Upvalue 结构体
|
||||
|
||||
std::free(uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 持有 C++ 堆资源的成员手动析构!
|
||||
else if (obj->type == ObjectType::String)
|
||||
{
|
||||
static_cast<StringObject *>(obj)->data.~String();
|
||||
}
|
||||
|
||||
std::free(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 洗白,仍是一条好汉
|
||||
// 以备下次 GC,统计存活大小
|
||||
obj->color = GCColor::White;
|
||||
|
||||
// 计算存活对象的真实物理大小 (Header + 柔性数组/额外数据)
|
||||
size_t objectSize = 0;
|
||||
switch (obj->type)
|
||||
{
|
||||
case ObjectType::String: objectSize = sizeof(StringObject); break;
|
||||
case ObjectType::Instance:
|
||||
objectSize =
|
||||
sizeof(InstanceObject)
|
||||
+ (obj->klass ? obj->klass->fieldCount * sizeof(Value) : 0);
|
||||
break;
|
||||
case ObjectType::Function:
|
||||
objectSize = sizeof(FunctionObject)
|
||||
+ (static_cast<FunctionObject *>(obj)->upvalueCount
|
||||
* sizeof(Upvalue *));
|
||||
break;
|
||||
case ObjectType::Struct: objectSize = sizeof(StructObject); break;
|
||||
}
|
||||
|
||||
liveBytes += objectSize;
|
||||
|
||||
curr = &obj->next;
|
||||
}
|
||||
}
|
||||
|
||||
allocatedBytes = liveBytes;
|
||||
// 阈值调整, 当下一次分配超过存活内存的 2 倍时触发 GC
|
||||
nextGC = (liveBytes < 512 * 1024) ? 1024 * 1024 : liveBytes * 2;
|
||||
|
||||
gcPhase = GCPhase::Idle;
|
||||
}
|
||||
|
||||
public:
|
||||
VM()
|
||||
{
|
||||
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
|
||||
{
|
||||
registers[i] = Value::GetNullInstance();
|
||||
}
|
||||
for (unsigned int i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
globals[i] = Value::GetNullInstance();
|
||||
}
|
||||
|
||||
currentFrame = frames;
|
||||
frameLimit = frames + MAX_RECURSION_DEPTH - 1;
|
||||
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]]
|
||||
inline Instruction *pushFrame(Proto *proto, Value *base) // fastcall
|
||||
{
|
||||
if (++currentFrame >= frameLimit) [[unlikely]] // 达到最大递归层数
|
||||
{
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{nullptr, proto, proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
inline Instruction *pushFrame(FunctionObject *closure, Value *base) // 普通调用
|
||||
{
|
||||
if (++currentFrame >= frameLimit) [[unlikely]]
|
||||
{
|
||||
POISON_MAX_RECURSION_DEPTH_EXCEED_INST =
|
||||
Op::iAsBx(OpCode::Exit_MaxRecursionDepthExceeded, 0, 0);
|
||||
return &POISON_MAX_RECURSION_DEPTH_EXCEED_INST;
|
||||
}
|
||||
[[likely]]
|
||||
*currentFrame = CallFrame{closure, closure->proto, closure->proto->code.data(), base};
|
||||
return currentFrame->ip;
|
||||
}
|
||||
|
||||
inline void popFrame()
|
||||
{
|
||||
--currentFrame;
|
||||
}
|
||||
|
||||
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 *);
|
||||
|
||||
void PrintRegisters(std::ostream &ostream = CoreIO::GetStdOut())
|
||||
{
|
||||
ostream << "=== Registers ===\n";
|
||||
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
|
||||
{
|
||||
Value &v = registers[i];
|
||||
if (!v.IsNull())
|
||||
{
|
||||
ostream << std::format("[{}] {}\n", i, v.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintGlobals(std::ostream &ostream = CoreIO::GetStdOut())
|
||||
{
|
||||
ostream << "== Globals ===\n";
|
||||
for (unsigned int i = 0; i < MAX_GLOBALS; ++i)
|
||||
{
|
||||
Value &v = globals[i];
|
||||
if (!v.IsNull())
|
||||
{
|
||||
ostream << std::format("[{}] {}\n", i, v.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user