Compare commits
17 Commits
91e4eb734e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 68e6552d07 | |||
| 82c7c7178f | |||
| 9f7fe79a2e | |||
| 0f5304001c | |||
| 680197aafe | |||
| 4f87078a87 | |||
| 9338c21449 | |||
| 98de782760 | |||
| fafa2b4946 | |||
| 570a87c3cd | |||
| e1d9812f92 | |||
| 6bcc98bdb3 | |||
| 91b5a0e384 | |||
| c0eacfd236 | |||
| 51a939ac45 | |||
| 0f635ccf2b | |||
| 90448006ff |
@@ -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
@@ -1,213 +0,0 @@
|
|||||||
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
|
|
||||||
Language: Cpp
|
|
||||||
# BasedOnStyle: LLVM
|
|
||||||
|
|
||||||
# 访问说明符(public、private等)的偏移
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
|
|
||||||
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
|
||||||
AlignAfterOpenBracket: DontAlign
|
|
||||||
|
|
||||||
# 连续赋值时,对齐所有等号
|
|
||||||
AlignConsecutiveAssignments: true
|
|
||||||
|
|
||||||
# 连续声明时,对齐所有声明的变量名
|
|
||||||
AlignConsecutiveDeclarations: true
|
|
||||||
|
|
||||||
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
|
||||||
AlignEscapedNewlines: Right
|
|
||||||
|
|
||||||
# 水平对齐二元和三元表达式的操作数
|
|
||||||
AlignOperands: true
|
|
||||||
|
|
||||||
# 对齐连续的尾随的注释
|
|
||||||
AlignTrailingComments: true
|
|
||||||
|
|
||||||
# 允许函数声明的所有参数在放在下一行
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
|
||||||
|
|
||||||
# 允许短的块放在同一行
|
|
||||||
AllowShortBlocksOnASingleLine: true
|
|
||||||
|
|
||||||
# 允许短的case标签放在同一行
|
|
||||||
AllowShortCaseLabelsOnASingleLine: true
|
|
||||||
|
|
||||||
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
|
||||||
AllowShortFunctionsOnASingleLine: Empty
|
|
||||||
|
|
||||||
# 允许短的if语句保持在同一行
|
|
||||||
AllowShortIfStatementsOnASingleLine: false
|
|
||||||
|
|
||||||
# 允许短的循环保持在同一行
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
|
|
||||||
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
|
||||||
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
|
|
||||||
# 总是在多行string字面量前换行
|
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
|
||||||
|
|
||||||
# 总是在template声明后换行
|
|
||||||
AlwaysBreakTemplateDeclarations: true
|
|
||||||
|
|
||||||
# false表示函数实参要么都在同一行,要么都各自一行
|
|
||||||
BinPackArguments: false
|
|
||||||
|
|
||||||
# false表示所有形参要么都在同一行,要么都各自一行
|
|
||||||
BinPackParameters: false
|
|
||||||
|
|
||||||
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
|
|
||||||
BraceWrapping:
|
|
||||||
# class定义后面
|
|
||||||
AfterClass: true
|
|
||||||
# 控制语句后面
|
|
||||||
AfterControlStatement: true
|
|
||||||
# enum定义后面
|
|
||||||
AfterEnum: true
|
|
||||||
# 函数定义后面
|
|
||||||
AfterFunction: true
|
|
||||||
# 命名空间定义后面
|
|
||||||
AfterNamespace: true
|
|
||||||
# struct定义后面
|
|
||||||
AfterStruct: true
|
|
||||||
# union定义后面
|
|
||||||
AfterUnion: true
|
|
||||||
# extern之后
|
|
||||||
AfterExternBlock: false
|
|
||||||
# catch之前
|
|
||||||
BeforeCatch: true
|
|
||||||
# else之前
|
|
||||||
BeforeElse: true
|
|
||||||
# 缩进大括号
|
|
||||||
IndentBraces: false
|
|
||||||
# 分离空函数
|
|
||||||
SplitEmptyFunction: true
|
|
||||||
# 分离空语句
|
|
||||||
SplitEmptyRecord: false
|
|
||||||
# 分离空命名空间
|
|
||||||
SplitEmptyNamespace: false
|
|
||||||
|
|
||||||
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
|
|
||||||
BreakBeforeBinaryOperators: NonAssignment
|
|
||||||
|
|
||||||
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
|
|
||||||
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
|
|
||||||
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
|
|
||||||
# 注:这里认为语句块也属于函数
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
|
|
||||||
# 在三元运算符前换行
|
|
||||||
BreakBeforeTernaryOperators: false
|
|
||||||
|
|
||||||
# 在构造函数的初始化列表的冒号后换行
|
|
||||||
BreakConstructorInitializers: AfterColon
|
|
||||||
|
|
||||||
#BreakInheritanceList: AfterColon
|
|
||||||
|
|
||||||
BreakStringLiterals: false
|
|
||||||
|
|
||||||
# 每行字符的限制,0表示没有限制
|
|
||||||
ColumnLimit: 100
|
|
||||||
CompactNamespaces: true
|
|
||||||
|
|
||||||
# 构造函数的初始化列表要么都在同一行,要么都各自一行
|
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|
||||||
|
|
||||||
# 构造函数的初始化列表的缩进宽度
|
|
||||||
ConstructorInitializerIndentWidth: 4
|
|
||||||
|
|
||||||
# 延续的行的缩进宽度
|
|
||||||
ContinuationIndentWidth: 4
|
|
||||||
|
|
||||||
# 去除C++11的列表初始化的大括号{后和}前的空格
|
|
||||||
Cpp11BracedListStyle: true
|
|
||||||
|
|
||||||
# 继承最常用的指针和引用的对齐方式
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
|
|
||||||
# 固定命名空间注释
|
|
||||||
FixNamespaceComments: true
|
|
||||||
|
|
||||||
# 缩进case标签
|
|
||||||
IndentCaseLabels: true
|
|
||||||
|
|
||||||
IndentPPDirectives: BeforeHash
|
|
||||||
|
|
||||||
# 缩进宽度
|
|
||||||
IndentWidth: 4
|
|
||||||
|
|
||||||
# 函数返回类型换行时,缩进函数声明或函数定义的函数名
|
|
||||||
IndentWrappedFunctionNames: false
|
|
||||||
|
|
||||||
# 保留在块开始处的空行
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
|
|
||||||
# 连续空行的最大数量
|
|
||||||
MaxEmptyLinesToKeep: 1
|
|
||||||
|
|
||||||
# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
|
|
||||||
NamespaceIndentation: All
|
|
||||||
|
|
||||||
# 指针和引用的对齐: Left, Right, Middle
|
|
||||||
PointerAlignment: Right
|
|
||||||
|
|
||||||
# 允许重新排版注释
|
|
||||||
ReflowComments: true
|
|
||||||
|
|
||||||
# 允许排序#include
|
|
||||||
SortIncludes: true
|
|
||||||
|
|
||||||
# 允许排序 using 声明
|
|
||||||
SortUsingDeclarations: false
|
|
||||||
|
|
||||||
# 在C风格类型转换后添加空格
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
# true -> (int) 0.1 false-> (int)0.1
|
|
||||||
|
|
||||||
# 在Template 关键字后面添加空格
|
|
||||||
SpaceAfterTemplateKeyword: true
|
|
||||||
|
|
||||||
# 在赋值运算符之前添加空格
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
|
|
||||||
# SpaceBeforeCpp11BracedList: true
|
|
||||||
|
|
||||||
# SpaceBeforeCtorInitializerColon: true
|
|
||||||
|
|
||||||
# SpaceBeforeInheritanceColon: true
|
|
||||||
|
|
||||||
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
|
|
||||||
# SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
|
|
||||||
# 在空的圆括号中添加空格
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
|
|
||||||
# 在尾随的评论前添加的空格数(只适用于//)
|
|
||||||
SpacesBeforeTrailingComments: 1
|
|
||||||
|
|
||||||
# 在尖括号的<后和>前添加空格
|
|
||||||
SpacesInAngles: false
|
|
||||||
|
|
||||||
# 在C风格类型转换的括号中添加空格
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
|
|
||||||
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
|
|
||||||
SpacesInContainerLiterals: true
|
|
||||||
|
|
||||||
# 在圆括号的(后和)前添加空格
|
|
||||||
SpacesInParentheses: false
|
|
||||||
|
|
||||||
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
|
|
||||||
# 标准: Cpp03, Cpp11, Auto
|
|
||||||
Standard: Auto
|
|
||||||
|
|
||||||
# tab宽度
|
|
||||||
TabWidth: 4
|
|
||||||
|
|
||||||
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
|
|
||||||
UseTab: Never
|
|
||||||
@@ -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版本发布完成!"
|
|
||||||
+5
-1
@@ -8,4 +8,8 @@ build/
|
|||||||
.vscode
|
.vscode
|
||||||
.VSCodeCounter
|
.VSCodeCounter
|
||||||
|
|
||||||
/test.fig
|
/test.fig
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|||||||
Generated
+7
@@ -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"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "fig"
|
||||||
|
version = "0.6.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
function fib(n)
|
||||||
|
if (n <= 1) then
|
||||||
|
return n
|
||||||
|
else
|
||||||
|
return fib(n - 1) + fib(n - 2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local start = os.clock()
|
||||||
|
local result = fib(30)
|
||||||
|
local endt = os.clock()
|
||||||
|
print(result, " cost: ", (endt - start) * 1000, "ms")
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from time import time as tt
|
from time import time as tt
|
||||||
|
|
||||||
def fib(x:int) -> int:
|
def fib(x:int) -> int:
|
||||||
if x <= 1: return x;
|
if x <= 1: return x
|
||||||
return fib(x-1) + fib(x-2)
|
return fib(x-1) + fib(x-2)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
t0 = tt()
|
t0 = tt()
|
||||||
result = fib(30)
|
result = fib(35)
|
||||||
t1 = tt()
|
t1 = tt()
|
||||||
print('cost: ',t1-t0, 'result:', result)
|
print('cost: ',t1-t0, 'result:', result)
|
||||||
|
|||||||
@@ -13,15 +13,6 @@ furnished to do so, subject to the following conditions:
|
|||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
2. This project includes code from the following projects with their respective licenses:
|
2. This project includes code from the following projects with their respective licenses:
|
||||||
- argparse (MIT License)
|
|
||||||
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
- magic_enum (MIT License)
|
- magic_enum (MIT License)
|
||||||
Copyright (c) 2019 - 2024 Daniil Goncharov
|
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,32 @@
|
|||||||
# Fig
|
# Fig
|
||||||
|
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||||
|
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||||
|
</picture>
|
||||||
|
|
||||||
|
> **🔔 Main Repository: https://git.fig-lang.cn/PuqiAR/Fig**
|
||||||
|
> *GitHub is only a mirror. Please submit issues and PRs to the main repository.*
|
||||||
|
|
||||||
|
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
**Fig** is a programming language that blends dynamic typing with optional static type annotations, built on reference-based value semantics.
|
||||||
|
|
||||||
|
> **v0.6.0 — Rust rewrite in progress.**
|
||||||
|
|
||||||
|
## Progress
|
||||||
|
|
||||||
|
```
|
||||||
|
Lexer ████████████████████ 100% (43 tests, all pass)
|
||||||
|
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
VM ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Fig
|
||||||
|
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="./Logo/LogoDark.svg">
|
||||||
|
<img src="./Logo/Logo.svg" alt="Fig Logo" width="200">
|
||||||
|
</picture>
|
||||||
|
|
||||||
|
> **🔔 主仓库:https://git.fig-lang.cn/PuqiAR/Fig**
|
||||||
|
> *GitHub 仅为镜像,Issue 与 PR 请提交至主仓库*
|
||||||
|
|
||||||
|
[English](./README.md) | [中文](./README_zh-CN.md)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。
|
||||||
|
|
||||||
|
> **v0.6.0 — Rust 重写进行中。**
|
||||||
|
|
||||||
|
## 进度
|
||||||
|
|
||||||
|
```
|
||||||
|
Lexer ████████████████████ 100% (43 测试, 全部通过)
|
||||||
|
Parser ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
Analyzer ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
Compiler ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
VM ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
LSP ░░░░░░░░░░░░░░░░░░░░ 0%
|
||||||
|
```
|
||||||
@@ -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,23 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Ast.hpp
|
|
||||||
@brief Ast总链接
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Expr/CallExpr.hpp>
|
|
||||||
#include <Ast/Expr/IdentiExpr.hpp>
|
|
||||||
#include <Ast/Expr/IndexExpr.hpp>
|
|
||||||
#include <Ast/Expr/InfixExpr.hpp>
|
|
||||||
#include <Ast/Expr/LiteralExpr.hpp>
|
|
||||||
#include <Ast/Expr/PrefixExpr.hpp>
|
|
||||||
|
|
||||||
#include <Ast/Stmt/ControlFlowStmts.hpp>
|
|
||||||
#include <Ast/Stmt/ExprStmt.hpp>
|
|
||||||
#include <Ast/Stmt/FnDefStmt.hpp>
|
|
||||||
#include <Ast/Stmt/IfStmt.hpp>
|
|
||||||
#include <Ast/Stmt/VarDecl.hpp>
|
|
||||||
#include <Ast/Stmt/WhileStmt.hpp>
|
|
||||||
#include <Ast/TypeExpr.hpp>
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Base.hpp
|
|
||||||
@brief AstNode基类定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <Core/SourceLocations.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
#include <Sema/Type.hpp>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
enum class AstType : std::uint8_t
|
|
||||||
{
|
|
||||||
AstNode, // 基类
|
|
||||||
Program, // 程序
|
|
||||||
Expr, // 表达式
|
|
||||||
Stmt, // 语句
|
|
||||||
BlockStmt, // 块语句
|
|
||||||
|
|
||||||
/* Expressions */
|
|
||||||
IdentiExpr, // 标识符表达式
|
|
||||||
LiteralExpr, // 字面量表达式
|
|
||||||
PrefixExpr, // 一元 前缀表达式
|
|
||||||
InfixExpr, // 二元 中缀表达式
|
|
||||||
|
|
||||||
IndexExpr, // 后缀表达式,索引
|
|
||||||
CallExpr, // 后缀表达式,函数调用
|
|
||||||
|
|
||||||
/* Statements */
|
|
||||||
ExprStmt, // 表达式语句,如 println(1)
|
|
||||||
VarDecl, // 变量声明
|
|
||||||
IfStmt, // If语句
|
|
||||||
ElseIfStmt, // ElseIf语句,不准悬空,平铺式else if
|
|
||||||
WhileStmt, // while语句
|
|
||||||
FnDefStmt, // func函数定义语句
|
|
||||||
ReturnStmt, // 返回语句
|
|
||||||
BreakStmt, // break语句
|
|
||||||
ContinueStmt, // continue语句
|
|
||||||
|
|
||||||
/* Type Expressions */
|
|
||||||
TypeExpr, // 基类
|
|
||||||
NamedTypeExpr, // 命名类型,支持namespace, std.file这样的
|
|
||||||
// 泛型等...
|
|
||||||
};
|
|
||||||
struct AstNode
|
|
||||||
{
|
|
||||||
AstType type = AstType::AstNode;
|
|
||||||
SourceLocation location;
|
|
||||||
|
|
||||||
virtual String toString() const = 0;
|
|
||||||
virtual ~AstNode() {};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TypeExpr : public AstNode
|
|
||||||
{
|
|
||||||
TypeExpr()
|
|
||||||
{
|
|
||||||
type = AstType::TypeExpr;
|
|
||||||
}
|
|
||||||
virtual ~TypeExpr() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Program;
|
|
||||||
|
|
||||||
struct Expr : public AstNode
|
|
||||||
{
|
|
||||||
TypeInfo *resolvedType = nullptr;
|
|
||||||
// TODO: 可选的常量折叠
|
|
||||||
// 拓展 isConstExpr和 constValue槽位
|
|
||||||
|
|
||||||
Expr()
|
|
||||||
{
|
|
||||||
type = AstType::Expr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Stmt : public AstNode
|
|
||||||
{
|
|
||||||
bool isPublic;
|
|
||||||
Stmt()
|
|
||||||
{
|
|
||||||
type = AstType::Stmt;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Program final : public AstNode
|
|
||||||
{
|
|
||||||
DynArray<Stmt *> nodes;
|
|
||||||
|
|
||||||
Program()
|
|
||||||
{
|
|
||||||
type = AstType::Program;
|
|
||||||
}
|
|
||||||
|
|
||||||
Program(DynArray<Stmt *> _nodes)
|
|
||||||
{
|
|
||||||
type = AstType::Program;
|
|
||||||
nodes = std::move(_nodes);
|
|
||||||
if (!_nodes.empty())
|
|
||||||
{
|
|
||||||
location = std::move(_nodes.back()->location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return "<Program>";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BlockStmt final : public Stmt
|
|
||||||
{
|
|
||||||
DynArray<Stmt *> nodes;
|
|
||||||
BlockStmt()
|
|
||||||
{
|
|
||||||
type = AstType::BlockStmt;
|
|
||||||
}
|
|
||||||
BlockStmt(DynArray<Stmt *> _nodes)
|
|
||||||
{
|
|
||||||
type = AstType::BlockStmt;
|
|
||||||
nodes = std::move(_nodes);
|
|
||||||
if (!_nodes.empty())
|
|
||||||
{
|
|
||||||
location = std::move(_nodes.back()->location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return "<BlockStmt>";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
|
|
||||||
namespace std
|
|
||||||
{
|
|
||||||
template <>
|
|
||||||
struct std::formatter<Fig::AstNode *, char>
|
|
||||||
{
|
|
||||||
constexpr auto parse(std::format_parse_context &ctx)
|
|
||||||
{
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const Fig::AstNode *_node, FormatContext &ctx) const
|
|
||||||
{
|
|
||||||
return std::format_to(ctx.out(), "{}", _node->toString().toStdString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace std
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Expr/CallExpr.hpp
|
|
||||||
@brief CallExpr等定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct FnCallArgs
|
|
||||||
{
|
|
||||||
DynArray<Expr *> args;
|
|
||||||
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
return args.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
String toString() const
|
|
||||||
{
|
|
||||||
String str = "(";
|
|
||||||
for (const Expr *expr : args)
|
|
||||||
{
|
|
||||||
if (expr != args.front())
|
|
||||||
{
|
|
||||||
str += ", ";
|
|
||||||
}
|
|
||||||
str += expr->toString();
|
|
||||||
}
|
|
||||||
str += ")";
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CallExpr final : public Expr
|
|
||||||
{
|
|
||||||
Expr *callee;
|
|
||||||
FnCallArgs args;
|
|
||||||
|
|
||||||
CallExpr()
|
|
||||||
{
|
|
||||||
type = AstType::CallExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CallExpr(Expr *_callee, FnCallArgs _args) : callee(_callee), args(std::move(_args))
|
|
||||||
{
|
|
||||||
type = AstType::CallExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return std::format("<CallExpr: '{}{}'>", callee->toString(), args.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Expr/IdentiExpr.hpp
|
|
||||||
@brief IdentiExpr定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct IdentiExpr final : Expr
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
|
|
||||||
// Analyzer槽位
|
|
||||||
|
|
||||||
// 寻址空间
|
|
||||||
bool isGlobal = false; // 是否全局, 全局变量存哈希/堆
|
|
||||||
|
|
||||||
// 仅 isGlobal = false 有效
|
|
||||||
int resolvedDepth = -1; // 用于获取闭包上值, -1 代表未解析
|
|
||||||
|
|
||||||
// 栈内槽位
|
|
||||||
int localId = -1; // 局部变量id, -1 未解析
|
|
||||||
|
|
||||||
|
|
||||||
IdentiExpr()
|
|
||||||
{
|
|
||||||
type = AstType::IdentiExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentiExpr(String _name, SourceLocation _loc)
|
|
||||||
{
|
|
||||||
type = AstType::IdentiExpr;
|
|
||||||
name = std::move(_name);
|
|
||||||
location = std::move(_loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return std::format("<IdentiExpr: {}>", name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -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,36 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Expr/LiteralExpr.hpp
|
|
||||||
@brief 字面量表达式定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
#include <Token/Token.hpp>
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct LiteralExpr final : Expr
|
|
||||||
{
|
|
||||||
Token token;
|
|
||||||
|
|
||||||
LiteralExpr()
|
|
||||||
{
|
|
||||||
type = AstType::LiteralExpr;
|
|
||||||
}
|
|
||||||
LiteralExpr(const Token& token, SourceLocation _location) : token(token)
|
|
||||||
{
|
|
||||||
type = AstType::LiteralExpr;
|
|
||||||
location = std::move(_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(token.type));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,184 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Operator.cpp
|
|
||||||
@brief 运算符定义内函数实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Ast/Operator.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap()
|
|
||||||
{
|
|
||||||
static HashMap<TokenType, UnaryOperator> unaryOpMap{
|
|
||||||
{TokenType::Tilde, UnaryOperator::BitNot},
|
|
||||||
{TokenType::Minus, UnaryOperator::Negate},
|
|
||||||
{TokenType::Not, UnaryOperator::Not},
|
|
||||||
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
|
||||||
};
|
|
||||||
return unaryOpMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap()
|
|
||||||
{
|
|
||||||
static HashMap<TokenType, BinaryOperator> binaryOpMap{
|
|
||||||
{TokenType::Plus, BinaryOperator::Add},
|
|
||||||
{TokenType::Minus, BinaryOperator::Subtract},
|
|
||||||
{TokenType::Asterisk, BinaryOperator::Multiply},
|
|
||||||
{TokenType::Slash, BinaryOperator::Divide},
|
|
||||||
{TokenType::Percent, BinaryOperator::Modulo},
|
|
||||||
|
|
||||||
{TokenType::Equal, BinaryOperator::Equal},
|
|
||||||
{TokenType::NotEqual, BinaryOperator::NotEqual},
|
|
||||||
{TokenType::Less, BinaryOperator::Less},
|
|
||||||
{TokenType::Greater, BinaryOperator::Greater},
|
|
||||||
{TokenType::LessEqual, BinaryOperator::LessEqual},
|
|
||||||
{TokenType::GreaterEqual, BinaryOperator::GreaterEqual},
|
|
||||||
|
|
||||||
{TokenType::Is, BinaryOperator::Is},
|
|
||||||
|
|
||||||
{TokenType::And, BinaryOperator::LogicalAnd},
|
|
||||||
{TokenType::Or, BinaryOperator::LogicalOr},
|
|
||||||
|
|
||||||
{TokenType::Power, BinaryOperator::Power},
|
|
||||||
|
|
||||||
{TokenType::Assign, BinaryOperator::Assign},
|
|
||||||
{TokenType::PlusEqual, BinaryOperator::AddAssign},
|
|
||||||
{TokenType::MinusEqual, BinaryOperator::SubAssign},
|
|
||||||
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
|
||||||
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
|
||||||
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
|
||||||
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
|
||||||
|
|
||||||
{TokenType::Pipe, BinaryOperator::BitOr},
|
|
||||||
{TokenType::Ampersand, BinaryOperator::BitAnd},
|
|
||||||
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
|
||||||
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
|
||||||
|
|
||||||
{TokenType::Dot, BinaryOperator::MemberAccess},
|
|
||||||
};
|
|
||||||
return binaryOpMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 赋值 < 三元 < 逻辑或 < 逻辑与 < 位运算 < 比较 < 位移 < 加减 < 乘除 < 幂 < 一元 < 成员访问 < (后缀)
|
|
||||||
|
|
||||||
/*
|
|
||||||
暂划分:
|
|
||||||
二元运算符:0 - 20000
|
|
||||||
一元运算符:20001 - 40000
|
|
||||||
后缀/成员/其他:40001 - 60001
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap()
|
|
||||||
{
|
|
||||||
static HashMap<UnaryOperator, BindingPower> unbpm{
|
|
||||||
{UnaryOperator::BitNot, 20001},
|
|
||||||
{UnaryOperator::Negate, 20001},
|
|
||||||
{UnaryOperator::Not, 20001},
|
|
||||||
{UnaryOperator::AddressOf, 20001},
|
|
||||||
};
|
|
||||||
return unbpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap()
|
|
||||||
{
|
|
||||||
static HashMap<BinaryOperator, BindingPower> bnbpm{
|
|
||||||
{BinaryOperator::Assign, 100},
|
|
||||||
{BinaryOperator::AddAssign, 100},
|
|
||||||
{BinaryOperator::SubAssign, 100},
|
|
||||||
{BinaryOperator::MultiplyAssign, 100},
|
|
||||||
{BinaryOperator::DivideAssign, 100},
|
|
||||||
{BinaryOperator::ModuloAssign, 100},
|
|
||||||
{BinaryOperator::BitXorAssign, 100},
|
|
||||||
|
|
||||||
{BinaryOperator::LogicalOr, 500},
|
|
||||||
{BinaryOperator::LogicalAnd, 550},
|
|
||||||
|
|
||||||
{BinaryOperator::BitOr, 1000},
|
|
||||||
{BinaryOperator::BitXor, 1100},
|
|
||||||
{BinaryOperator::BitAnd, 1200},
|
|
||||||
|
|
||||||
{BinaryOperator::Equal, 2000},
|
|
||||||
{BinaryOperator::NotEqual, 2000},
|
|
||||||
|
|
||||||
{BinaryOperator::Less, 2100},
|
|
||||||
{BinaryOperator::LessEqual, 2100},
|
|
||||||
{BinaryOperator::Greater, 2100},
|
|
||||||
{BinaryOperator::GreaterEqual, 2100},
|
|
||||||
|
|
||||||
{BinaryOperator::Is, 2100},
|
|
||||||
|
|
||||||
{BinaryOperator::ShiftLeft, 3000},
|
|
||||||
{BinaryOperator::ShiftRight, 3000},
|
|
||||||
|
|
||||||
{BinaryOperator::Add, 4000},
|
|
||||||
{BinaryOperator::Subtract, 4000},
|
|
||||||
{BinaryOperator::Multiply, 4500},
|
|
||||||
{BinaryOperator::Divide, 4500},
|
|
||||||
|
|
||||||
{BinaryOperator::Power, 5000},
|
|
||||||
|
|
||||||
{BinaryOperator::MemberAccess, 40001},
|
|
||||||
};
|
|
||||||
return bnbpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingPower GetUnaryOpRBp(UnaryOperator op)
|
|
||||||
{
|
|
||||||
return GetUnaryOpBindingPowerMap().at(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingPower GetBinaryOpLBp(BinaryOperator op)
|
|
||||||
{
|
|
||||||
return GetBinaryOpBindingPowerMap().at(op);
|
|
||||||
}
|
|
||||||
|
|
||||||
BindingPower GetBinaryOpRBp(BinaryOperator op)
|
|
||||||
{
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
右结合,左绑定力 >= 右
|
|
||||||
a = b = c
|
|
||||||
a = (b = c)
|
|
||||||
*/
|
|
||||||
case BinaryOperator::Assign:
|
|
||||||
case BinaryOperator::AddAssign:
|
|
||||||
case BinaryOperator::SubAssign:
|
|
||||||
case BinaryOperator::MultiplyAssign:
|
|
||||||
case BinaryOperator::DivideAssign:
|
|
||||||
case BinaryOperator::ModuloAssign:
|
|
||||||
case BinaryOperator::BitXorAssign:
|
|
||||||
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/*
|
|
||||||
左结合, 左绑定力 < 右
|
|
||||||
a * b * c
|
|
||||||
(a * b) * c
|
|
||||||
*/
|
|
||||||
return GetBinaryOpLBp(op) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsTokenOp(TokenType type, bool binary /* = true*/)
|
|
||||||
{
|
|
||||||
if (binary)
|
|
||||||
{
|
|
||||||
return GetBinaryOpMap().contains(type);
|
|
||||||
}
|
|
||||||
return GetUnaryOpMap().contains(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnaryOperator TokenToUnaryOp(const Token &token)
|
|
||||||
{
|
|
||||||
return GetUnaryOpMap().at(token.type);
|
|
||||||
}
|
|
||||||
BinaryOperator TokenToBinaryOp(const Token &token)
|
|
||||||
{
|
|
||||||
return GetBinaryOpMap().at(token.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Operator.hpp
|
|
||||||
@brief 运算符定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Token/Token.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
enum class UnaryOperator : std::uint8_t
|
|
||||||
{
|
|
||||||
BitNot, // 位运算取反 ~
|
|
||||||
Negate, // 取反 -
|
|
||||||
Not, // 逻辑非 ! / not
|
|
||||||
AddressOf, // 取引用 &
|
|
||||||
|
|
||||||
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
|
||||||
};
|
|
||||||
enum class BinaryOperator : std::uint8_t
|
|
||||||
{
|
|
||||||
Add, // 加 +
|
|
||||||
Subtract, // 减 -
|
|
||||||
Multiply, // 乘 *
|
|
||||||
Divide, // 除 /
|
|
||||||
Modulo, // 取模 %
|
|
||||||
|
|
||||||
Equal, // 等于 ==
|
|
||||||
NotEqual, // 不等于 !=
|
|
||||||
Less, // 小于 <
|
|
||||||
Greater, // 大于 >
|
|
||||||
LessEqual, // 小于等于 <=
|
|
||||||
GreaterEqual, // 大于等于 >=
|
|
||||||
|
|
||||||
Is, // is操作符
|
|
||||||
|
|
||||||
LogicalAnd, // 逻辑与 && / and
|
|
||||||
LogicalOr, // 逻辑或 || / or
|
|
||||||
|
|
||||||
Power, // 幂运算 **
|
|
||||||
|
|
||||||
Assign, // 赋值(修改) =
|
|
||||||
AddAssign, // +=
|
|
||||||
SubAssign, // -=
|
|
||||||
MultiplyAssign, // *=
|
|
||||||
DivideAssign, // /=
|
|
||||||
ModuloAssign, // %=
|
|
||||||
BitXorAssign, // ^=
|
|
||||||
|
|
||||||
// 位运算
|
|
||||||
BitAnd, // 按位与 &
|
|
||||||
BitOr, // 按位或 |
|
|
||||||
BitXor, // 异或 ^
|
|
||||||
ShiftLeft, // 左移
|
|
||||||
ShiftRight, // 右移
|
|
||||||
|
|
||||||
// 成员访问
|
|
||||||
MemberAccess, // .
|
|
||||||
|
|
||||||
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr unsigned int GetOperatorsSize()
|
|
||||||
{
|
|
||||||
// 获取全部运算符的数量
|
|
||||||
return static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(BinaryOperator::Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
using BindingPower = unsigned int;
|
|
||||||
|
|
||||||
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap();
|
|
||||||
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap();
|
|
||||||
|
|
||||||
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap();
|
|
||||||
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap();
|
|
||||||
|
|
||||||
BindingPower GetUnaryOpRBp(UnaryOperator);
|
|
||||||
|
|
||||||
BindingPower GetBinaryOpLBp(BinaryOperator);
|
|
||||||
BindingPower GetBinaryOpRBp(BinaryOperator);
|
|
||||||
|
|
||||||
bool IsTokenOp(TokenType type, bool binary = true);
|
|
||||||
|
|
||||||
UnaryOperator TokenToUnaryOp(const Token &);
|
|
||||||
BinaryOperator TokenToBinaryOp(const Token &);
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,102 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Stmt/FnDefStmt.hpp
|
|
||||||
@brief FnDefStmt定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-25
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct Param
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
SourceLocation location;
|
|
||||||
|
|
||||||
TypeInfo *resolvedType = nullptr;
|
|
||||||
int localId = -1;
|
|
||||||
|
|
||||||
virtual String toString() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PosParam final : public Param
|
|
||||||
{
|
|
||||||
TypeExpr *type;
|
|
||||||
Expr *defaultValue;
|
|
||||||
|
|
||||||
PosParam() {}
|
|
||||||
PosParam(String _name, TypeExpr *_type, Expr *_defaultValue, SourceLocation _location) :
|
|
||||||
type(_type), defaultValue(_defaultValue)
|
|
||||||
{
|
|
||||||
name = std::move(_name);
|
|
||||||
location = std::move(_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return std::format("<Pos {}: {}{}>",
|
|
||||||
name,
|
|
||||||
(type ? type->toString() : "Any"),
|
|
||||||
(defaultValue ? " =" + defaultValue->toString() : ""));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
(public) func foo([name: (type) (= default value)]) (-> return type)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct FnDefStmt final : public Stmt
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
DynArray<Param *> params;
|
|
||||||
TypeExpr *returnType;
|
|
||||||
BlockStmt *body;
|
|
||||||
|
|
||||||
TypeInfo *resolvedReturnType = nullptr;
|
|
||||||
int localId = -1;
|
|
||||||
|
|
||||||
FnDefStmt()
|
|
||||||
{
|
|
||||||
type = AstType::FnDefStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
FnDefStmt(bool _isPublic,
|
|
||||||
String _name,
|
|
||||||
DynArray<Param *> _params,
|
|
||||||
TypeExpr *_returnType,
|
|
||||||
BlockStmt *_body,
|
|
||||||
SourceLocation _location) :
|
|
||||||
name(std::move(_name)), params(std::move(_params)), returnType(_returnType), body(_body)
|
|
||||||
{
|
|
||||||
type = AstType::FnDefStmt;
|
|
||||||
isPublic = _isPublic;
|
|
||||||
location = std::move(_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
String pStr;
|
|
||||||
for (const Param *p : params)
|
|
||||||
{
|
|
||||||
if (p != *params.begin())
|
|
||||||
{
|
|
||||||
pStr += ", ";
|
|
||||||
}
|
|
||||||
pStr += p->toString();
|
|
||||||
}
|
|
||||||
return std::format("<FnDefStmt {}{}({}) -> {} {{{}}}>",
|
|
||||||
(isPublic ? "public " : ""),
|
|
||||||
name,
|
|
||||||
pStr,
|
|
||||||
(returnType ? returnType->toString() : "Any"),
|
|
||||||
body->toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,47 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/Stmt/VarDecl.hpp
|
|
||||||
@brief VarDecl定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct VarDecl final : public Stmt
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
TypeExpr *typeSpecifier;
|
|
||||||
bool isInfer; // 是否用了 := 类型推断
|
|
||||||
Expr *initExpr;
|
|
||||||
|
|
||||||
int localId = -1;
|
|
||||||
|
|
||||||
VarDecl()
|
|
||||||
{
|
|
||||||
type = AstType::VarDecl;
|
|
||||||
}
|
|
||||||
|
|
||||||
VarDecl(bool _isPublic, String _name, TypeExpr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
|
||||||
name(std::move(_name)),
|
|
||||||
typeSpecifier(_typeSpecifier),
|
|
||||||
isInfer(_isInfer),
|
|
||||||
initExpr(_initExpr) // location 指向关键字 var/const位置
|
|
||||||
{
|
|
||||||
type = AstType::VarDecl;
|
|
||||||
isPublic = _isPublic;
|
|
||||||
location = std::move(_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
const String &typeSpecifierString = (typeSpecifier ? typeSpecifier->toString() : "Any");
|
|
||||||
const String &initExprString = (initExpr ? initExpr->toString() : "(disprovided)");
|
|
||||||
return std::format("<VarDecl {}: {} = {}>", name, typeSpecifierString, initExprString);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,36 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Ast/TypeExpr.hpp
|
|
||||||
@brief TypeExpr定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-25
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Base.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
|
|
||||||
struct NamedTypeExpr final : public TypeExpr
|
|
||||||
{
|
|
||||||
DynArray<String> path; // {"std", "file"} etc.
|
|
||||||
|
|
||||||
NamedTypeExpr()
|
|
||||||
{
|
|
||||||
type = AstType::NamedTypeExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
NamedTypeExpr(DynArray<String> _path, SourceLocation _location) :
|
|
||||||
path(std::move(_path))
|
|
||||||
{
|
|
||||||
type = AstType::NamedTypeExpr;
|
|
||||||
location = std::move(_location);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual String toString() const override
|
|
||||||
{
|
|
||||||
return std::format("<NamedTypeExpr '{}'>", path);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Bytecode/Bytecode.hpp
|
|
||||||
@brief 字节码Bytecode定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-18
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
|
|
||||||
// 定长 32-bit
|
|
||||||
using Instruction = std::uint32_t;
|
|
||||||
|
|
||||||
enum class OpCode : std::uint8_t
|
|
||||||
{
|
|
||||||
Exit, // 结束运行
|
|
||||||
LoadK, // iABx 模式: R[A] = Constants[Bx]
|
|
||||||
LoadTrue, // iABC: R[A] = true
|
|
||||||
LoadFalse, // iABC: R[A] = false
|
|
||||||
LoadNull, // iABC: R[A] = null
|
|
||||||
|
|
||||||
FastCall, // iABC: A: ProtoIdx, B: 函数起始寄存器
|
|
||||||
Call, // 动态派发 iABC: A: 函数体对象寄存器 B: 函数起始寄存器
|
|
||||||
Return, // iABC 模式: 返回 R[A] 的值
|
|
||||||
|
|
||||||
LoadFn, // 惰性装修, iABx: R[A] = new FunctionObject...
|
|
||||||
|
|
||||||
Jmp, // iAsBx: ip += sBx 无条件跳转
|
|
||||||
JmpIfFalse, // iAsBx: 如果 R[A] 为假, ip += sBx
|
|
||||||
|
|
||||||
Mov, // iABx: R[A] = R[Bx]
|
|
||||||
Add, // iABC: R[A] = R[B] + R[C]
|
|
||||||
Sub, // iABC: R[A] = R[B] - R[C]
|
|
||||||
Mul, // iABC: R[A] = R[B] * R[C]
|
|
||||||
Div, // iABC: R[A] = R[B] / R[C]
|
|
||||||
Mod, // iABC: R[A] = R[B] % R[C]
|
|
||||||
BitXor, // iABC: R[A] = R[B] ^ R[C]
|
|
||||||
|
|
||||||
Equal, // iABC: R[A] = R[B] == R[C]
|
|
||||||
NotEqual, // iABC: R[A] = R[B] != R[C]
|
|
||||||
Greater, // iABC: R[A] = R[B] > R[C]
|
|
||||||
Less, // iABC: R[A] = R[B] < R[C]
|
|
||||||
GreaterEqual, // iABC: R[A] = R[B] >= R[C]
|
|
||||||
LessEqual, // iABC: R[A] = R[B] <= R[C]
|
|
||||||
|
|
||||||
Count, // 哨兵
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Op
|
|
||||||
{
|
|
||||||
// [OpCode: 8] [A: 8] [Bx: 16]
|
|
||||||
[[nodiscard]] inline constexpr Instruction iABx(OpCode op, std::uint8_t a, std::uint16_t bx)
|
|
||||||
{
|
|
||||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
|
||||||
| (static_cast<std::uint32_t>(bx) << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [OpCode: 8] [A: 8] [B: 8] [C: 8]
|
|
||||||
[[nodiscard]] inline constexpr Instruction iABC(
|
|
||||||
OpCode op, std::uint8_t a, std::uint8_t b, std::uint8_t c)
|
|
||||||
{
|
|
||||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
|
||||||
| (static_cast<std::uint32_t>(b) << 16) | (static_cast<std::uint32_t>(c) << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
inline constexpr Instruction iAsBx(OpCode op, std::uint8_t a, std::int16_t sbx)
|
|
||||||
{
|
|
||||||
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
|
||||||
| (static_cast<std::uint32_t>(static_cast<std::uint16_t>(sbx)) << 16);
|
|
||||||
}
|
|
||||||
} // namespace Op
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
#include <Compiler/Compiler.hpp>
|
|
||||||
#include <Core/Core.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Lexer/Lexer.hpp>
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
#include <SourceManager/SourceManager.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <print>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
using namespace Fig;
|
|
||||||
|
|
||||||
String fileName = "test.fig";
|
|
||||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
|
||||||
|
|
||||||
SourceManager manager(filePath);
|
|
||||||
manager.Read();
|
|
||||||
|
|
||||||
if (!manager.read)
|
|
||||||
{
|
|
||||||
std::cerr << "Couldn't read file";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lexer lexer(manager.GetSource(), fileName);
|
|
||||||
Parser parser(lexer, manager, fileName);
|
|
||||||
|
|
||||||
const auto &program_result = parser.Parse();
|
|
||||||
if (!program_result)
|
|
||||||
{
|
|
||||||
ReportError(program_result.error(), manager);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
Program *program = *program_result;
|
|
||||||
|
|
||||||
Compiler compiler(fileName, manager);
|
|
||||||
const auto &comp_result = compiler.Compile(program);
|
|
||||||
if (!comp_result)
|
|
||||||
{
|
|
||||||
ReportError(comp_result.error(), manager);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompiledModule *compiledModule = *comp_result;
|
|
||||||
|
|
||||||
size_t cnt = 0;
|
|
||||||
for (Proto *proto : compiledModule->protos)
|
|
||||||
{
|
|
||||||
std::cout << "=====================\n"
|
|
||||||
<< "Proto: " << cnt++ << '\n';
|
|
||||||
std::cout << "=== Constant Pool ===" << '\n';
|
|
||||||
for (size_t i = 0; i < proto->constants.size(); ++i)
|
|
||||||
{
|
|
||||||
std::print("[{}] {}\n", i, proto->constants[i].ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
DumpCode(proto->code);
|
|
||||||
|
|
||||||
std::cout << "\nMax Stack Size: " << (int) proto->maxStack << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Compiler/Compiler.cpp
|
|
||||||
@brief 编译器实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-18
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Compiler/Compiler.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
|
|
||||||
{
|
|
||||||
current->freeReg = 0;
|
|
||||||
|
|
||||||
for (Stmt *stmt : program->nodes)
|
|
||||||
{
|
|
||||||
auto result = compileStmt(static_cast<Stmt *>(stmt));
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainFuncIndex != -1)
|
|
||||||
{
|
|
||||||
std::uint8_t baseReg = AllocReg();
|
|
||||||
Emit(Op::iABC(OpCode::FastCall, mainFuncIndex, baseReg, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit(Op::iABC(OpCode::Exit, 0, 0, 0)); // 一定要退出,这是虚拟机退出信号,否则ub
|
|
||||||
|
|
||||||
CompiledModule *compiledModule = new CompiledModule(fileName, allProtos);
|
|
||||||
return compiledModule;
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Compiler/Compiler.hpp
|
|
||||||
@brief 编译器定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Ast.hpp>
|
|
||||||
#include <Bytecode/Bytecode.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
#include <Object/Object.hpp>
|
|
||||||
#include <SourceManager/SourceManager.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
// 编译产物-函数
|
|
||||||
struct Proto
|
|
||||||
{
|
|
||||||
DynArray<Instruction> code;
|
|
||||||
DynArray<Value> constants;
|
|
||||||
std::uint8_t maxStack = 0; // 函数运行所需寄存器数量
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LocalVar
|
|
||||||
{
|
|
||||||
int localId; // AST 传来的纯数字 ID
|
|
||||||
std::uint8_t reg; // 分配到的物理寄存器 ID
|
|
||||||
int depth; // 物理作用域深度(用于 EndScope 释放寄存器)
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr int MAX_LOCALS = 250;
|
|
||||||
inline constexpr int MAX_CONSTANTS = UINT16_MAX + 1;
|
|
||||||
|
|
||||||
// 任何跨函数、跨模块的编译,都压入弹出这个 State
|
|
||||||
struct FuncState
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
FuncState *enclosing = nullptr; // 指向外层状态 (支持闭包)
|
|
||||||
Proto *proto = nullptr;
|
|
||||||
|
|
||||||
std::uint8_t freeReg = 0;
|
|
||||||
int scopeDepth = 0;
|
|
||||||
DynArray<LocalVar> locals;
|
|
||||||
|
|
||||||
std::uint8_t fastRegMap[UINT8_MAX + 1]; // 256, 索引 = localId, 值 = 寄存器 id
|
|
||||||
|
|
||||||
FuncState(String _name, FuncState *enc = nullptr) : name(std::move(_name)), enclosing(enc)
|
|
||||||
{
|
|
||||||
proto = new Proto();
|
|
||||||
std::fill_n(fastRegMap, 256, UINT8_MAX); // 255代表未分配
|
|
||||||
}
|
|
||||||
// 注意:这里不 delete proto,因为 proto 是要作为编译产物吐出去的
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompiledModule
|
|
||||||
{
|
|
||||||
String name; // 供调试/打印
|
|
||||||
DynArray<Proto *> protos; // 扁平化函数原型
|
|
||||||
|
|
||||||
CompiledModule(String _name, DynArray<Proto *> _protos) :
|
|
||||||
name(std::move(_name)), protos(std::move(_protos))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~CompiledModule()
|
|
||||||
{
|
|
||||||
for (auto *p : protos)
|
|
||||||
{
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Compiler
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
String fileName;
|
|
||||||
|
|
||||||
SourceManager &manager;
|
|
||||||
FuncState *current = nullptr; // 永远指向当前正在编译的上下文
|
|
||||||
|
|
||||||
int mainFuncIndex = -1;
|
|
||||||
HashMap<int, int> globalFuncMap; // localid -> ProtoIdx
|
|
||||||
|
|
||||||
public:
|
|
||||||
DynArray<Proto *> allProtos;
|
|
||||||
|
|
||||||
struct FuncStateProtector
|
|
||||||
{
|
|
||||||
Compiler *compiler;
|
|
||||||
FuncState *prevState;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
FuncStateProtector(Compiler *comp, FuncState *newState) :
|
|
||||||
compiler(comp), prevState(comp->current)
|
|
||||||
{
|
|
||||||
compiler->current = newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
~FuncStateProtector()
|
|
||||||
{
|
|
||||||
compiler->current = prevState;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Compiler(String _fileName, SourceManager &_manager) :
|
|
||||||
fileName(std::move(_fileName)), manager(_manager)
|
|
||||||
{
|
|
||||||
// 初始化顶级作用域
|
|
||||||
current = new FuncState("global", nullptr);
|
|
||||||
allProtos.push_back(current->proto); // 最顶层, bootstrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
~Compiler()
|
|
||||||
{
|
|
||||||
// 内存清理 (如果有异常中断)
|
|
||||||
while (current != nullptr)
|
|
||||||
{
|
|
||||||
FuncState *prev = current->enclosing;
|
|
||||||
delete current;
|
|
||||||
current = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<CompiledModule *, Error> Compile(Program *program);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void PushState(String _name)
|
|
||||||
{
|
|
||||||
current = new FuncState(std::move(_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
Proto *PopState()
|
|
||||||
{
|
|
||||||
FuncState *oldState = current;
|
|
||||||
Proto *finishedProto = oldState->proto;
|
|
||||||
|
|
||||||
current = oldState->enclosing;
|
|
||||||
delete oldState;
|
|
||||||
|
|
||||||
return finishedProto;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t AllocReg()
|
|
||||||
{
|
|
||||||
if (current->freeReg >= 255)
|
|
||||||
{
|
|
||||||
assert(false && "Register overflow!");
|
|
||||||
}
|
|
||||||
std::uint8_t reg = current->freeReg++;
|
|
||||||
if (current->freeReg > current->proto->maxStack)
|
|
||||||
{
|
|
||||||
current->proto->maxStack = current->freeReg;
|
|
||||||
}
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeReg(std::uint8_t reg)
|
|
||||||
{
|
|
||||||
// 如果这个寄存器被局部变量使用,不释放直接 Return
|
|
||||||
for (const auto &local : current->locals)
|
|
||||||
{
|
|
||||||
if (local.reg == reg)
|
|
||||||
{
|
|
||||||
return; // 拒绝释放,保护局部变量生命周期
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果它是纯粹的临时计算结果),释放
|
|
||||||
if (reg == current->freeReg - 1)
|
|
||||||
{
|
|
||||||
current->freeReg--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void Emit(Instruction inst)
|
|
||||||
{
|
|
||||||
current->proto->code.push_back(inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t AddConstant(Value v)
|
|
||||||
{
|
|
||||||
auto it =
|
|
||||||
std::find(current->proto->constants.begin(), current->proto->constants.end(), v);
|
|
||||||
if (it != current->proto->constants.end())
|
|
||||||
{
|
|
||||||
return std::distance(current->proto->constants.begin(), it);
|
|
||||||
}
|
|
||||||
|
|
||||||
current->proto->constants.push_back(v);
|
|
||||||
return static_cast<std::uint16_t>(current->proto->constants.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginScope()
|
|
||||||
{
|
|
||||||
current->scopeDepth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EndScope()
|
|
||||||
{
|
|
||||||
current->scopeDepth--;
|
|
||||||
while (!current->locals.empty() && current->locals.back().depth > current->scopeDepth)
|
|
||||||
{
|
|
||||||
FreeReg(current->locals.back().reg);
|
|
||||||
current->locals.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t DeclareLocal(int localId)
|
|
||||||
{
|
|
||||||
std::uint8_t reg = AllocReg();
|
|
||||||
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
|
|
||||||
current->fastRegMap[localId] = reg;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t DeclareLocal(int localId, std::uint8_t reg) // 表示复用哪个寄存器
|
|
||||||
{
|
|
||||||
current->locals.push_back(LocalVar{localId, reg, current->scopeDepth});
|
|
||||||
current->fastRegMap[localId] = reg;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发射一条跳转指令,并返回它在代码数组里的绝对索引 (Index)
|
|
||||||
int EmitJump(OpCode op, std::uint8_t aReg = 0)
|
|
||||||
{
|
|
||||||
// 预填 0
|
|
||||||
Emit(Op::iAsBx(op, aReg, 0));
|
|
||||||
return current->proto->code.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 填真实偏移量到那条指令里
|
|
||||||
void PatchJump(int instructionIndex)
|
|
||||||
{
|
|
||||||
// 目标地址就是当前代码数组的末尾
|
|
||||||
int target = current->proto->code.size();
|
|
||||||
|
|
||||||
// 相对偏移量 = 目标地址 - 指令自身所在的地址 - 1
|
|
||||||
// (因为 VM 里的 ip 在取指后会自动 +1,所以偏移要减去 1)
|
|
||||||
int offset = target - instructionIndex - 1;
|
|
||||||
|
|
||||||
if (offset < INT16_MIN || offset > INT16_MAX)
|
|
||||||
{
|
|
||||||
assert(false && "PatchJump: Jump offset exceeds 16-bit signed limit!");
|
|
||||||
}
|
|
||||||
Instruction &inst = current->proto->code[instructionIndex];
|
|
||||||
inst = (inst & 0x0000FFFF)
|
|
||||||
| (static_cast<Instruction>(static_cast<std::uint16_t>(offset)) << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
SourceLocation makeSourceLocation(AstNode *node)
|
|
||||||
{
|
|
||||||
SourceLocation location = node->location; // copy
|
|
||||||
location.functionName = current->name;
|
|
||||||
location.fileName = fileName;
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> compileIdentiExpr(IdentiExpr *);
|
|
||||||
Result<std::uint8_t, Error> compileLiteral(LiteralExpr *);
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> compileAssignment(
|
|
||||||
InfixExpr *); // 编译赋值,由 CompileInfixExpr调用
|
|
||||||
Result<std::uint8_t, Error> compileInfixExpr(InfixExpr *);
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> compileLeftValue(
|
|
||||||
Expr *); // 左值对象,可以是变量、结构体字段或模块对象
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> compileCallExpr(CallExpr *);
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> compileExpr(Expr *);
|
|
||||||
|
|
||||||
/* Statements */
|
|
||||||
Result<void, Error> compileVarDecl(VarDecl *);
|
|
||||||
Result<void, Error> compileBlockStmt(BlockStmt *);
|
|
||||||
Result<void, Error> compileIfStmt(IfStmt *);
|
|
||||||
Result<void, Error> compileWhileStmt(WhileStmt *);
|
|
||||||
Result<void, Error> compileFnDefStmt(FnDefStmt *);
|
|
||||||
Result<void, Error> compileReturnStmt(ReturnStmt *);
|
|
||||||
|
|
||||||
Result<void, Error> compileStmt(Stmt *);
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void DisassembleInstruction(Instruction inst, std::size_t index)
|
|
||||||
{
|
|
||||||
// 提取OpCode (低 8 位)
|
|
||||||
auto op = static_cast<OpCode>(inst & 0xFF);
|
|
||||||
|
|
||||||
std::string_view opName = magic_enum::enum_name(op);
|
|
||||||
|
|
||||||
// 所有指令至少都有 A 操作数 (8~15 位)
|
|
||||||
std::uint8_t a = (inst >> 8) & 0xFF;
|
|
||||||
|
|
||||||
// 地址补零,指令名左对齐占 10 字符
|
|
||||||
std::cout << std::format("{:04d} {:<10} ", index, opName);
|
|
||||||
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case OpCode::Exit: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode::Mov: {
|
|
||||||
// iABx 模式
|
|
||||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
|
||||||
std::cout << std::format("R{:<3} R[{}]", a, bx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OpCode::LoadK: {
|
|
||||||
// iABx 模式:解析 Bx (16~31 位)
|
|
||||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
|
||||||
std::cout << std::format("R{:<3} K[{}]", a, bx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode::Jmp:
|
|
||||||
case OpCode::JmpIfFalse: {
|
|
||||||
// iAsBx
|
|
||||||
std::int16_t sbx = static_cast<std::uint16_t>(inst >> 16);
|
|
||||||
std::cout << std::format("R{:<3} [{}]", a, sbx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode::FastCall:
|
|
||||||
{
|
|
||||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
|
||||||
std::cout << std::format("Proto{:<3} R[{}]+", a, b);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OpCode::Call:
|
|
||||||
{
|
|
||||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
|
||||||
std::cout << std::format("R{:<3} R[{}]+", a, b);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode::LoadTrue:
|
|
||||||
case OpCode::LoadFalse:
|
|
||||||
case OpCode::LoadNull:
|
|
||||||
case OpCode::Add:
|
|
||||||
case OpCode::Sub:
|
|
||||||
case OpCode::Mul:
|
|
||||||
case OpCode::Div:
|
|
||||||
case OpCode::Mod: {
|
|
||||||
// iABC 模式:解析 B (16~23 位) 和 C (24~31 位)
|
|
||||||
std::uint8_t b = (inst >> 16) & 0xFF;
|
|
||||||
std::uint8_t c = (inst >> 24) & 0xFF;
|
|
||||||
std::cout << std::format("R{:<3} R{:<3} R{}", a, b, c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OpCode::Return: {
|
|
||||||
// iA 模式:只用到了 A
|
|
||||||
std::cout << std::format("R{}", a);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OpCode::LoadFn: {
|
|
||||||
std::uint16_t bx = (inst >> 16) & 0xFFFF;
|
|
||||||
std::cout << std::format("R{:<3} Proto[{}]", a, bx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// default: {
|
|
||||||
// std::cout << "?";
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
std::cout << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void DumpCode(const DynArray<Instruction> &code)
|
|
||||||
{
|
|
||||||
std::cout << " Bytecode\n";
|
|
||||||
for (std::size_t i = 0; i < code.size(); ++i)
|
|
||||||
{
|
|
||||||
DisassembleInstruction(code[i], i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Compiler/ExprCompiler.cpp
|
|
||||||
@brief 编译器实现(表达式部分)
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Compiler/Compiler.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileIdentiExpr(IdentiExpr *ie)
|
|
||||||
{
|
|
||||||
// TODO: 处理全局变量和闭包 Upvalue
|
|
||||||
std::uint8_t targetReg = current->fastRegMap[ie->localId];
|
|
||||||
|
|
||||||
if (targetReg == UINT8_MAX)
|
|
||||||
{
|
|
||||||
assert(false && "Compiler Bug: Encountered unmapped localId in fastRegMap!");
|
|
||||||
}
|
|
||||||
return targetReg;
|
|
||||||
}
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileLiteral(
|
|
||||||
LiteralExpr *lit) // 编译字面量, 负责转换 token -> Value
|
|
||||||
{
|
|
||||||
const Token &token = lit->token;
|
|
||||||
String lexeme = manager.GetSub(token.index, token.length);
|
|
||||||
|
|
||||||
if (!token.isLiteral())
|
|
||||||
{
|
|
||||||
assert(false && "CompileLiteral: token is not literal");
|
|
||||||
}
|
|
||||||
|
|
||||||
Value v;
|
|
||||||
|
|
||||||
if (token.type == TokenType::LiteralNull)
|
|
||||||
{
|
|
||||||
v = Value::GetNullInstance();
|
|
||||||
}
|
|
||||||
else if (token.type == TokenType::LiteralTrue)
|
|
||||||
{
|
|
||||||
v = Value::GetTrueInstance();
|
|
||||||
}
|
|
||||||
else if (token.type == TokenType::LiteralFalse)
|
|
||||||
{
|
|
||||||
v = Value::GetFalseInstance();
|
|
||||||
}
|
|
||||||
else if (token.type == TokenType::LiteralNumber)
|
|
||||||
{
|
|
||||||
// TODO: 更换为无异常手写数字解析版本 (charconv也可)
|
|
||||||
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
|
|
||||||
{
|
|
||||||
// 非整数
|
|
||||||
double d = std::stod(lexeme.toStdString());
|
|
||||||
v = Value::FromDouble(d);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::int32_t i = std::stoi(lexeme.toStdString());
|
|
||||||
v = Value::FromInt(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert("false" && "CompileLiteral: unsupport literal");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t targetReg = AllocReg();
|
|
||||||
|
|
||||||
if (current->proto->constants.size() >= MAX_CONSTANTS)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TooManyConstants,
|
|
||||||
std::format("constant limit exceeded: {}", MAX_CONSTANTS),
|
|
||||||
"How did you write such code? try global variable or split file",
|
|
||||||
makeSourceLocation(lit)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t kIndex = AddConstant(v);
|
|
||||||
|
|
||||||
Emit(Op::iABx(OpCode::LoadK, targetReg, kIndex));
|
|
||||||
return targetReg;
|
|
||||||
}
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileAssignment(
|
|
||||||
InfixExpr *infix) // 编译赋值,由 CompileInfixExpr调用
|
|
||||||
{
|
|
||||||
// op必须为 =
|
|
||||||
const auto &_lhsReg = compileLeftValue(infix->left); // 必须为左值对象
|
|
||||||
if (!_lhsReg)
|
|
||||||
{
|
|
||||||
return _lhsReg;
|
|
||||||
}
|
|
||||||
std::uint8_t lhsReg = *_lhsReg;
|
|
||||||
|
|
||||||
const auto &_rhsReg = compileExpr(infix->right);
|
|
||||||
std::uint8_t rhsReg = *_rhsReg;
|
|
||||||
|
|
||||||
FreeReg(rhsReg);
|
|
||||||
switch (infix->op)
|
|
||||||
{
|
|
||||||
case BinaryOperator::Assign: {
|
|
||||||
Emit(Op::iABx(OpCode::Mov, lhsReg, rhsReg)); // lhsReg = rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::AddAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::Add, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg + rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::SubAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::Sub, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg - rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::MultiplyAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::Mul, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg * rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::DivideAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::Div, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg / rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::ModuloAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::Mod, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg % rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BinaryOperator::BitXorAssign: {
|
|
||||||
Emit(Op::iABC(OpCode::BitXor, lhsReg, lhsReg, rhsReg)); // lhsReg = lhsReg ^ rhsReg
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
assert(false && "CompileAssignment: op unsupported yet");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lhsReg; // 返回赋值的结果,支持连续赋值
|
|
||||||
}
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileInfixExpr(
|
|
||||||
InfixExpr *infix) // 编译中缀表达式,返回一个存放结果的寄存器 ID
|
|
||||||
{
|
|
||||||
if (infix->op >= BinaryOperator::Assign && infix->op <= BinaryOperator::BitXorAssign)
|
|
||||||
{
|
|
||||||
return compileAssignment(infix);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &_lhsReg = compileExpr(infix->left);
|
|
||||||
if (!_lhsReg)
|
|
||||||
{
|
|
||||||
return _lhsReg;
|
|
||||||
}
|
|
||||||
std::uint8_t lhsReg = *_lhsReg;
|
|
||||||
const auto &_rhsReg = compileExpr(infix->right);
|
|
||||||
if (!_rhsReg)
|
|
||||||
{
|
|
||||||
return _rhsReg;
|
|
||||||
}
|
|
||||||
std::uint8_t rhsReg = *_rhsReg;
|
|
||||||
|
|
||||||
FreeReg(rhsReg);
|
|
||||||
FreeReg(lhsReg);
|
|
||||||
|
|
||||||
std::uint8_t resultReg = AllocReg();
|
|
||||||
switch (infix->op)
|
|
||||||
{
|
|
||||||
case BinaryOperator::Add: {
|
|
||||||
Emit(Op::iABC(OpCode::Add, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Subtract: {
|
|
||||||
Emit(Op::iABC(OpCode::Sub, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Multiply: {
|
|
||||||
Emit(Op::iABC(OpCode::Mul, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Divide: {
|
|
||||||
Emit(Op::iABC(OpCode::Div, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Modulo: {
|
|
||||||
Emit(Op::iABC(OpCode::Mod, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Greater: {
|
|
||||||
Emit(Op::iABC(OpCode::Greater, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::GreaterEqual: {
|
|
||||||
Emit(Op::iABC(OpCode::GreaterEqual, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Less: {
|
|
||||||
Emit(Op::iABC(OpCode::Less, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::LessEqual: {
|
|
||||||
Emit(Op::iABC(OpCode::LessEqual, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BinaryOperator::Equal: {
|
|
||||||
Emit(Op::iABC(OpCode::Equal, resultReg, lhsReg, rhsReg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: assert(false && "CompileInfixExpr: op unsupported yet");
|
|
||||||
}
|
|
||||||
return resultReg;
|
|
||||||
}
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileLeftValue(
|
|
||||||
Expr *expr) // 左值对象,可以是变量、结构体字段或模块对象
|
|
||||||
{
|
|
||||||
switch (expr->type)
|
|
||||||
{
|
|
||||||
case AstType::IdentiExpr:
|
|
||||||
return compileIdentiExpr(static_cast<IdentiExpr *>(expr));
|
|
||||||
// TODO: 数组切片(a[0])或对象属性(a.b)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Analyzer 有漏洞(编译器内部
|
|
||||||
// 直接崩溃
|
|
||||||
assert(false && "Compiler Bug: Invalid L-value bypassed Analyzer!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileCallExpr(CallExpr *expr)
|
|
||||||
{
|
|
||||||
bool isStatic = false; // 是否为单纯的 fn(...) 静态函数调用
|
|
||||||
int protoIdx = -1;
|
|
||||||
|
|
||||||
if (expr->callee->type == AstType::IdentiExpr)
|
|
||||||
{
|
|
||||||
IdentiExpr *id = static_cast<IdentiExpr *>(expr->callee);
|
|
||||||
// 如果是函数名且深度为 0 (全局/扁平函数池)
|
|
||||||
if (id->resolvedType->tag == TypeTag::Function && id->resolvedDepth == 0)
|
|
||||||
{
|
|
||||||
if (globalFuncMap.contains(id->localId))
|
|
||||||
{
|
|
||||||
isStatic = true;
|
|
||||||
protoIdx = globalFuncMap[id->localId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t baseReg = AllocReg();
|
|
||||||
|
|
||||||
if (!isStatic)
|
|
||||||
{
|
|
||||||
auto calleeRes = compileExpr(expr->callee);
|
|
||||||
if (!calleeRes)
|
|
||||||
{
|
|
||||||
return calleeRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*calleeRes != baseReg)
|
|
||||||
{
|
|
||||||
Emit(Op::iABx(OpCode::Mov, baseReg, *calleeRes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < expr->args.size(); ++i)
|
|
||||||
{
|
|
||||||
std::uint8_t argTarget = AllocReg();
|
|
||||||
auto argRes = compileExpr(expr->args.args[i]);
|
|
||||||
if (!argRes)
|
|
||||||
{
|
|
||||||
return argRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*argRes != argTarget)
|
|
||||||
{
|
|
||||||
Emit(Op::iABx(OpCode::Mov, argTarget, *argRes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t expectRet = 1;
|
|
||||||
|
|
||||||
if (isStatic)
|
|
||||||
{
|
|
||||||
Emit(Op::iABC(OpCode::FastCall, (std::uint8_t) protoIdx, baseReg, expectRet));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Emit(Op::iABC(OpCode::Call, baseReg, baseReg, expectRet));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < expr->args.args.size(); ++i)
|
|
||||||
{
|
|
||||||
current->freeReg--;
|
|
||||||
}
|
|
||||||
return baseReg; // 返回值起点
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<std::uint8_t, Error> Compiler::compileExpr(
|
|
||||||
Expr *expr) // 编译表达式,必定返回一个存放结果的寄存器 ID
|
|
||||||
{
|
|
||||||
switch (expr->type)
|
|
||||||
{
|
|
||||||
case AstType::Stmt:
|
|
||||||
case AstType::Expr:
|
|
||||||
case AstType::AstNode: assert(false && "CompileExpr: bad node type"); break;
|
|
||||||
|
|
||||||
case AstType::IdentiExpr: {
|
|
||||||
return compileLeftValue(expr); // 左值直接转换成右值
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::LiteralExpr: {
|
|
||||||
LiteralExpr *lit = static_cast<LiteralExpr *>(expr);
|
|
||||||
|
|
||||||
auto result = compileLiteral(lit);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
std::uint8_t targetReg = *result;
|
|
||||||
return targetReg;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::InfixExpr: {
|
|
||||||
return compileInfixExpr(static_cast<InfixExpr *>(expr));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::CallExpr: {
|
|
||||||
return compileCallExpr(static_cast<CallExpr *>(expr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Compiler/StmtCompiler.cpp
|
|
||||||
@brief 编译器实现(语句部分)
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Compiler/Compiler.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<void, Error> Compiler::compileVarDecl(VarDecl *varDecl)
|
|
||||||
{
|
|
||||||
if (current->freeReg > MAX_LOCALS)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TooManyLocals,
|
|
||||||
std::format("local limit exceeded: {}", MAX_LOCALS),
|
|
||||||
"try split function or use arrays/structs...",
|
|
||||||
makeSourceLocation(varDecl)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t varReg;
|
|
||||||
if (varDecl->initExpr)
|
|
||||||
{
|
|
||||||
auto result = compileExpr(varDecl->initExpr);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
std::uint8_t resultReg = *result;
|
|
||||||
varReg = DeclareLocal(varDecl->localId, resultReg); // 复用临时计算结果寄存器
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: 未初始化提供初始值
|
|
||||||
varReg = DeclareLocal(varDecl->localId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result<void, Error>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileBlockStmt(BlockStmt *blockStmt)
|
|
||||||
{
|
|
||||||
for (Stmt *stmt : blockStmt->nodes)
|
|
||||||
{
|
|
||||||
auto result = compileStmt(stmt);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileIfStmt(IfStmt *stmt)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if cond1
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if cond2 #1
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if cond3 #2
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else #3
|
|
||||||
{
|
|
||||||
}
|
|
||||||
quit #4
|
|
||||||
|
|
||||||
Bytecode:
|
|
||||||
JmpIfFalse cond1 #1
|
|
||||||
; consequent内容
|
|
||||||
; ...
|
|
||||||
; if条件为true, 跳过所有 else/elseif
|
|
||||||
|
|
||||||
Jmp #4
|
|
||||||
|
|
||||||
; #1
|
|
||||||
JmpIfFalse cond2 #2
|
|
||||||
; consequent
|
|
||||||
|
|
||||||
Jmp #4
|
|
||||||
|
|
||||||
; #2
|
|
||||||
JmpIfFalse cond3 #3
|
|
||||||
; consequent
|
|
||||||
|
|
||||||
Jmp #4
|
|
||||||
|
|
||||||
; #3
|
|
||||||
; 没有一次执行分支
|
|
||||||
; else部分
|
|
||||||
; ...
|
|
||||||
|
|
||||||
#4
|
|
||||||
|
|
||||||
*/
|
|
||||||
std::vector<int> exitJumps; // 所有分支都要跳到最后,收集所有jump最后回填
|
|
||||||
const auto &condResult = compileExpr(stmt->cond);
|
|
||||||
if (!condResult)
|
|
||||||
{
|
|
||||||
return std::unexpected(condResult.error());
|
|
||||||
}
|
|
||||||
std::uint8_t condReg = *condResult;
|
|
||||||
int jumpToNext = EmitJump(OpCode::JmpIfFalse, condReg);
|
|
||||||
FreeReg(condReg);
|
|
||||||
|
|
||||||
const auto &blockResult = compileStmt(stmt->consequent);
|
|
||||||
if (!blockResult)
|
|
||||||
{
|
|
||||||
return blockResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完if直接跳到出口
|
|
||||||
PatchJump(jumpToNext); // 回填,跳到下一个else/elseif
|
|
||||||
|
|
||||||
for (auto *elif : stmt->elifs)
|
|
||||||
{
|
|
||||||
const auto &elifCondResult = compileExpr(elif->cond);
|
|
||||||
if (!elifCondResult)
|
|
||||||
return std::unexpected(elifCondResult.error());
|
|
||||||
std::uint8_t elifCondReg = *elifCondResult;
|
|
||||||
|
|
||||||
jumpToNext = EmitJump(OpCode::JmpIfFalse, elifCondReg);
|
|
||||||
FreeReg(elifCondReg);
|
|
||||||
|
|
||||||
const auto &blockResult = compileStmt(elif->consequent);
|
|
||||||
if (!blockResult)
|
|
||||||
{
|
|
||||||
return blockResult;
|
|
||||||
}
|
|
||||||
exitJumps.push_back(EmitJump(OpCode::Jmp)); // 执行完else if,跳到出口
|
|
||||||
PatchJump(jumpToNext); // 跳到下一个分支
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stmt->alternate)
|
|
||||||
{
|
|
||||||
auto result = compileStmt(stmt->alternate);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int exitIndex : exitJumps)
|
|
||||||
{
|
|
||||||
PatchJump(exitIndex); // 回填所有跳转出口的指令
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileWhileStmt(WhileStmt *stmt)
|
|
||||||
{
|
|
||||||
int beginIns = current->proto->code.size() - 1;
|
|
||||||
|
|
||||||
auto condRegResult = compileExpr(stmt->cond);
|
|
||||||
if (!condRegResult)
|
|
||||||
{
|
|
||||||
return std::unexpected(condRegResult.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint8_t condReg = *condRegResult;
|
|
||||||
|
|
||||||
int exitJump = EmitJump(OpCode::JmpIfFalse, condReg);
|
|
||||||
auto bodyResult = compileBlockStmt(stmt->body);
|
|
||||||
|
|
||||||
if (!bodyResult)
|
|
||||||
{
|
|
||||||
return bodyResult;
|
|
||||||
}
|
|
||||||
Emit(Op::iAsBx(
|
|
||||||
OpCode::Jmp, 0, beginIns - current->proto->code.size())); // 回到开头对condition求值
|
|
||||||
PatchJump(exitJump);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileFnDefStmt(FnDefStmt *stmt)
|
|
||||||
{
|
|
||||||
std::uint8_t funcReg = DeclareLocal(stmt->localId);
|
|
||||||
|
|
||||||
// 创建子函数编译状态
|
|
||||||
// 传入 current 作为 enclosing,用于后续支持闭包 Upvalue 查找
|
|
||||||
FuncState childState(stmt->name, current);
|
|
||||||
|
|
||||||
allProtos.push_back(childState.proto);
|
|
||||||
std::uint16_t protoIdx = static_cast<std::uint16_t>(allProtos.size() - 1);
|
|
||||||
|
|
||||||
globalFuncMap[stmt->localId] = protoIdx; // 把函数的local id映射到protoIdx
|
|
||||||
|
|
||||||
{
|
|
||||||
FuncStateProtector stateGuard(this, &childState);
|
|
||||||
|
|
||||||
AllocReg();
|
|
||||||
|
|
||||||
// 将参数映射为子函数的初始局部变量 (R0, R1...)
|
|
||||||
for (Param *p : stmt->params)
|
|
||||||
{
|
|
||||||
PosParam *posParam = static_cast<PosParam *>(p); // TODO: 其他参数支持...
|
|
||||||
// 按顺序分配寄存器
|
|
||||||
DeclareLocal(posParam->localId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// B编译函数体语句
|
|
||||||
auto bodyRes = compileStmt(stmt->body);
|
|
||||||
if (!bodyRes)
|
|
||||||
return bodyRes;
|
|
||||||
|
|
||||||
// 隐式返回 null
|
|
||||||
std::uint8_t resReg = AllocReg();
|
|
||||||
Emit(Op::iABC(OpCode::LoadNull, resReg, 0, 0));
|
|
||||||
Emit(Op::iABC(OpCode::Return, resReg, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 5. 检查是否是 main 函数
|
|
||||||
if (stmt->name == U"main")
|
|
||||||
{
|
|
||||||
this->mainFuncIndex = protoIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit(Op::iABx(OpCode::LoadFn, funcReg, protoIdx));
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileReturnStmt(ReturnStmt *stmt)
|
|
||||||
{
|
|
||||||
auto res = compileExpr(stmt->value);
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
return std::unexpected(res.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit(Op::iABC(OpCode::Return, *res, 0, 0));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Compiler::compileStmt(Stmt *stmt) // 编译语句
|
|
||||||
{
|
|
||||||
switch (stmt->type)
|
|
||||||
{
|
|
||||||
case AstType::ExprStmt: {
|
|
||||||
ExprStmt *exprStmt = static_cast<ExprStmt *>(stmt);
|
|
||||||
Expr *expr = exprStmt->expr;
|
|
||||||
auto result = compileExpr(expr);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
FreeReg(*result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::VarDecl: {
|
|
||||||
return compileVarDecl(static_cast<VarDecl *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::BlockStmt: {
|
|
||||||
return compileBlockStmt(static_cast<BlockStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::IfStmt: {
|
|
||||||
return compileIfStmt(static_cast<IfStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::WhileStmt: {
|
|
||||||
return compileWhileStmt(static_cast<WhileStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::FnDefStmt: {
|
|
||||||
return compileFnDefStmt(static_cast<FnDefStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::ReturnStmt: {
|
|
||||||
return compileReturnStmt(static_cast<ReturnStmt *>(stmt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result<void, Error>();
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,47 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Core/CoreIO.cpp
|
|
||||||
@brief 标准输入输出链接
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Core/CoreIO.hpp>
|
|
||||||
#include <Core/CoreInfos.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Fig::CoreIO
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) || defined(__APPLE__) || defined (__linux__) || defined (__unix__)
|
|
||||||
std::ostream &GetStdOut()
|
|
||||||
{
|
|
||||||
static std::ostream &out = std::cout;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
std::ostream &GetStdErr()
|
|
||||||
{
|
|
||||||
static std::ostream &err = std::cerr;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
std::ostream &GetStdLog()
|
|
||||||
{
|
|
||||||
static std::ostream &log = std::clog;
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
std::istream &GetStdCin()
|
|
||||||
{
|
|
||||||
static std::istream &cin = std::cin;
|
|
||||||
return cin;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// link
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void InitConsoleIO()
|
|
||||||
{
|
|
||||||
SetConsoleCP(CP_UTF8);
|
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -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,67 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Core/CoreInfos.hpp
|
|
||||||
@brief 核心系统信息
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Deps/String/String.hpp>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#define __FCORE_VERSION "0.5.0-alpha"
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define __FCORE_PLATFORM "Windows"
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
#define __FCORE_PLATFORM "Apple"
|
|
||||||
#elif defined(__linux__)
|
|
||||||
#define __FCORE_PLATFORM "Linux"
|
|
||||||
#elif defined(__unix__)
|
|
||||||
#define __FCORE_PLATFORM "Unix"
|
|
||||||
#else
|
|
||||||
#define __FCORE_PLATFORM "Unknown"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#if defined(__clang__)
|
|
||||||
#define __FCORE_COMPILER "llvm-mingw"
|
|
||||||
#else
|
|
||||||
#define __FCORE_COMPILER "MinGW"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define __FCORE_COMPILER "GCC"
|
|
||||||
#endif
|
|
||||||
#elif defined(__clang__)
|
|
||||||
#define __FCORE_COMPILER "Clang"
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define __FCORE_COMPILER "MSVC"
|
|
||||||
#else
|
|
||||||
#define __FCORE_COMPILER "Unknown"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if SIZE_MAX == 18446744073709551615ull
|
|
||||||
#define __FCORE_ARCH "64"
|
|
||||||
#else
|
|
||||||
#define __FCORE_ARCH "86"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define __FCORE_LINK_DEPS
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
namespace Core
|
|
||||||
{
|
|
||||||
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
|
||||||
inline constexpr std::string_view LICENSE = "MIT";
|
|
||||||
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
|
||||||
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
|
||||||
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
|
||||||
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
|
||||||
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
|
||||||
}; // namespace Core
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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,173 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Error/Error.cpp
|
|
||||||
@brief 错误报告实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Core/Core.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
void ColoredPrint(const char *color, const char *msg, std::ostream &ost = CoreIO::GetStdErr())
|
|
||||||
{
|
|
||||||
ost << color << msg << TerminalColors::Reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
|
||||||
{
|
|
||||||
ost << color << msg << TerminalColors::Reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColoredPrint(const char *color, const String &msg, std::ostream &ost = CoreIO::GetStdErr())
|
|
||||||
{
|
|
||||||
ost << color << msg << TerminalColors::Reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MultipleStr(const char *c, size_t n)
|
|
||||||
{
|
|
||||||
std::string buf;
|
|
||||||
for (size_t i = 0; i < n; ++i) { buf += c; }
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ErrorTypeToString(ErrorType type)
|
|
||||||
{
|
|
||||||
using enum ErrorType;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case UnusedSymbol: return "UnusedSymbol";
|
|
||||||
|
|
||||||
case MayBeNull: return "MaybeNull";
|
|
||||||
|
|
||||||
case UnterminatedString: return "UnterminatedString";
|
|
||||||
case UnterminatedComments: return "UnterminatedComments";
|
|
||||||
case InvalidNumberLiteral: return "InvalidNumberLiteral";
|
|
||||||
case InvalidCharacter: return "InvalidCharacter";
|
|
||||||
case InvalidSymbol: return "InvalidSymbol";
|
|
||||||
|
|
||||||
case ExpectedExpression: return "ExpectedExpression";
|
|
||||||
case SyntaxError: return "SyntaxError";
|
|
||||||
|
|
||||||
case RedeclarationError: return "RedeclarationError";
|
|
||||||
case UseUndeclaredIdentifier: return "UseUndeclaredIdentifier";
|
|
||||||
case NotAnLvalue: return "NotAnLvalue";
|
|
||||||
case TypeError: return "TypeError";
|
|
||||||
|
|
||||||
case TooManyLocals: return "TooManyLocals";
|
|
||||||
case TooManyConstants: return "TooManyConstants";
|
|
||||||
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintSystemInfos()
|
|
||||||
{
|
|
||||||
std::ostream &err = CoreIO::GetStdErr();
|
|
||||||
std::stringstream build_info;
|
|
||||||
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '['
|
|
||||||
<< Core::COMPILER << ']' << '\n'
|
|
||||||
<< " Build Time: " << Core::COMPILE_TIME;
|
|
||||||
|
|
||||||
const std::string &build_info_str = build_info.str();
|
|
||||||
err << MultipleStr("─", build_info_str.size()) << '\n';
|
|
||||||
err << build_info_str << '\n';
|
|
||||||
err << MultipleStr("─", build_info_str.size()) << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
|
|
||||||
{
|
|
||||||
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
|
||||||
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
|
||||||
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
|
||||||
|
|
||||||
namespace TC = TerminalColors;
|
|
||||||
std::ostream &err = CoreIO::GetStdErr();
|
|
||||||
|
|
||||||
uint8_t level = ErrorLevel(error.type);
|
|
||||||
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
|
||||||
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
|
||||||
|
|
||||||
err << "🔥 "
|
|
||||||
<< level_color
|
|
||||||
//<< '(' << level_name << ')'
|
|
||||||
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
|
|
||||||
<< TC::Reset << '\n';
|
|
||||||
|
|
||||||
const SourceLocation &location = error.location;
|
|
||||||
|
|
||||||
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
|
|
||||||
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
|
|
||||||
<< TC::Cyan << ')' << TC::Reset << '\n';
|
|
||||||
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
|
||||||
|
|
||||||
// 尝试打印上3行 下2行
|
|
||||||
|
|
||||||
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
|
||||||
while (!srcManager.HasLine(line_end)) { --line_end; }
|
|
||||||
while (!srcManager.HasLine(line_start)) { ++line_start; }
|
|
||||||
|
|
||||||
const auto &getLineNumWidth = [](size_t l) {
|
|
||||||
unsigned int cnt = 0;
|
|
||||||
while (l != 0)
|
|
||||||
{
|
|
||||||
l = l / 10;
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
return cnt;
|
|
||||||
};
|
|
||||||
unsigned int max_line_number_width = getLineNumWidth(line_end);
|
|
||||||
for (size_t i = line_start; i <= line_end; ++i)
|
|
||||||
{
|
|
||||||
unsigned int offset = 2 + 2 + 1;
|
|
||||||
// ' └─ '
|
|
||||||
if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; }
|
|
||||||
else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; }
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err << MultipleStr(" ", offset);
|
|
||||||
}
|
|
||||||
unsigned int cur_line_number_width = getLineNumWidth(i);
|
|
||||||
|
|
||||||
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset;
|
|
||||||
err << " │ " << srcManager.GetLine(i) << '\n';
|
|
||||||
if (i == location.sp.line)
|
|
||||||
{
|
|
||||||
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
|
||||||
err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
|
||||||
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
|
||||||
|
|
||||||
err << MultipleStr(" ", error_col_offset)
|
|
||||||
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color
|
|
||||||
<< error.message << TC::Reset << "\n\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err << "\n";
|
|
||||||
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
|
||||||
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n";
|
|
||||||
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
|
||||||
err << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportError(const Error &error, const SourceManager &srcManager)
|
|
||||||
{
|
|
||||||
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
|
||||||
assert(srcManager.HasLine(error.location.sp.line));
|
|
||||||
|
|
||||||
PrintSystemInfos();
|
|
||||||
PrintErrorInfo(error, srcManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportErrors(const std::vector<Error> &errors, const SourceManager &srcManager)
|
|
||||||
{
|
|
||||||
std::ostream &ost = CoreIO::GetStdErr();
|
|
||||||
PrintSystemInfos();
|
|
||||||
for (const auto &err : errors)
|
|
||||||
{
|
|
||||||
PrintErrorInfo(err, srcManager);
|
|
||||||
ost << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Error/Error.hpp
|
|
||||||
@brief Error定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-13
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Core/SourceLocations.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <SourceManager/SourceManager.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
#include <source_location>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
0-1000 Minor
|
|
||||||
1001-2000 Medium
|
|
||||||
2001-3000 Critical
|
|
||||||
*/
|
|
||||||
enum class ErrorType : unsigned int
|
|
||||||
{
|
|
||||||
/* Minor */
|
|
||||||
UnusedSymbol = 0,
|
|
||||||
|
|
||||||
/* Medium */
|
|
||||||
MayBeNull = 1001,
|
|
||||||
|
|
||||||
/* Critical */
|
|
||||||
|
|
||||||
// lexer errors
|
|
||||||
UnterminatedString = 2001,
|
|
||||||
UnterminatedComments,
|
|
||||||
InvalidNumberLiteral,
|
|
||||||
InvalidCharacter,
|
|
||||||
InvalidSymbol,
|
|
||||||
|
|
||||||
// parser errors
|
|
||||||
ExpectedExpression,
|
|
||||||
SyntaxError,
|
|
||||||
|
|
||||||
// analyzer errors
|
|
||||||
RedeclarationError,
|
|
||||||
UseUndeclaredIdentifier,
|
|
||||||
NotAnLvalue,
|
|
||||||
TypeError,
|
|
||||||
|
|
||||||
// compile errors
|
|
||||||
TooManyLocals,
|
|
||||||
TooManyConstants,
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *ErrorTypeToString(ErrorType type);
|
|
||||||
|
|
||||||
struct Error
|
|
||||||
{
|
|
||||||
ErrorType type;
|
|
||||||
String message;
|
|
||||||
String suggestion;
|
|
||||||
|
|
||||||
SourceLocation location;
|
|
||||||
std::source_location thrower_loc;
|
|
||||||
|
|
||||||
Error() {}
|
|
||||||
Error(ErrorType _type,
|
|
||||||
const String &_message,
|
|
||||||
const String &_suggestion,
|
|
||||||
const SourceLocation &_location,
|
|
||||||
const std::source_location &_throwerloc = std::source_location::current())
|
|
||||||
{
|
|
||||||
type = _type;
|
|
||||||
message = _message;
|
|
||||||
suggestion = _suggestion;
|
|
||||||
location = _location;
|
|
||||||
thrower_loc = _throwerloc;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace TerminalColors
|
|
||||||
{
|
|
||||||
constexpr const char *Reset = "\033[0m";
|
|
||||||
constexpr const char *Bold = "\033[1m";
|
|
||||||
constexpr const char *Dim = "\033[2m";
|
|
||||||
constexpr const char *Italic = "\033[3m";
|
|
||||||
constexpr const char *Underline = "\033[4m";
|
|
||||||
constexpr const char *Blink = "\033[5m";
|
|
||||||
constexpr const char *Reverse = "\033[7m"; // 前背景反色
|
|
||||||
constexpr const char *Hidden = "\033[8m"; // 隐藏文本
|
|
||||||
constexpr const char *Strike = "\033[9m"; // 删除线
|
|
||||||
|
|
||||||
constexpr const char *Black = "\033[30m";
|
|
||||||
constexpr const char *Red = "\033[31m";
|
|
||||||
constexpr const char *Green = "\033[32m";
|
|
||||||
constexpr const char *Yellow = "\033[33m";
|
|
||||||
constexpr const char *Blue = "\033[34m";
|
|
||||||
constexpr const char *Magenta = "\033[35m";
|
|
||||||
constexpr const char *Cyan = "\033[36m";
|
|
||||||
constexpr const char *White = "\033[37m";
|
|
||||||
|
|
||||||
constexpr const char *LightBlack = "\033[90m";
|
|
||||||
constexpr const char *LightRed = "\033[91m";
|
|
||||||
constexpr const char *LightGreen = "\033[92m";
|
|
||||||
constexpr const char *LightYellow = "\033[93m";
|
|
||||||
constexpr const char *LightBlue = "\033[94m";
|
|
||||||
constexpr const char *LightMagenta = "\033[95m";
|
|
||||||
constexpr const char *LightCyan = "\033[96m";
|
|
||||||
constexpr const char *LightWhite = "\033[97m";
|
|
||||||
|
|
||||||
constexpr const char *DarkRed = "\033[38;2;128;0;0m";
|
|
||||||
constexpr const char *DarkGreen = "\033[38;2;0;100;0m";
|
|
||||||
constexpr const char *DarkYellow = "\033[38;2;128;128;0m";
|
|
||||||
constexpr const char *DarkBlue = "\033[38;2;0;0;128m";
|
|
||||||
constexpr const char *DarkMagenta = "\033[38;2;100;0;100m";
|
|
||||||
constexpr const char *DarkCyan = "\033[38;2;0;128;128m";
|
|
||||||
constexpr const char *DarkGray = "\033[38;2;64;64;64m";
|
|
||||||
constexpr const char *Gray = "\033[38;2;128;128;128m";
|
|
||||||
constexpr const char *Silver = "\033[38;2;192;192;192m";
|
|
||||||
|
|
||||||
constexpr const char *Navy = "\033[38;2;0;0;128m";
|
|
||||||
constexpr const char *RoyalBlue = "\033[38;2;65;105;225m";
|
|
||||||
constexpr const char *ForestGreen = "\033[38;2;34;139;34m";
|
|
||||||
constexpr const char *Olive = "\033[38;2;128;128;0m";
|
|
||||||
constexpr const char *Teal = "\033[38;2;0;128;128m";
|
|
||||||
constexpr const char *Maroon = "\033[38;2;128;0;0m";
|
|
||||||
constexpr const char *Purple = "\033[38;2;128;0;128m";
|
|
||||||
constexpr const char *Orange = "\033[38;2;255;165;0m";
|
|
||||||
constexpr const char *Gold = "\033[38;2;255;215;0m";
|
|
||||||
constexpr const char *Pink = "\033[38;2;255;192;203m";
|
|
||||||
constexpr const char *Crimson = "\033[38;2;220;20;60m";
|
|
||||||
|
|
||||||
constexpr const char *OnBlack = "\033[40m";
|
|
||||||
constexpr const char *OnRed = "\033[41m";
|
|
||||||
constexpr const char *OnGreen = "\033[42m";
|
|
||||||
constexpr const char *OnYellow = "\033[43m";
|
|
||||||
constexpr const char *OnBlue = "\033[44m";
|
|
||||||
constexpr const char *OnMagenta = "\033[45m";
|
|
||||||
constexpr const char *OnCyan = "\033[46m";
|
|
||||||
constexpr const char *OnWhite = "\033[47m";
|
|
||||||
|
|
||||||
constexpr const char *OnLightBlack = "\033[100m";
|
|
||||||
constexpr const char *OnLightRed = "\033[101m";
|
|
||||||
constexpr const char *OnLightGreen = "\033[102m";
|
|
||||||
constexpr const char *OnLightYellow = "\033[103m";
|
|
||||||
constexpr const char *OnLightBlue = "\033[104m";
|
|
||||||
constexpr const char *OnLightMagenta = "\033[105m";
|
|
||||||
constexpr const char *OnLightCyan = "\033[106m";
|
|
||||||
constexpr const char *OnLightWhite = "\033[107m";
|
|
||||||
|
|
||||||
constexpr const char *OnDarkBlue = "\033[48;2;0;0;128m";
|
|
||||||
constexpr const char *OnGreenYellow = "\033[48;2;173;255;47m";
|
|
||||||
constexpr const char *OnOrange = "\033[48;2;255;165;0m";
|
|
||||||
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
|
||||||
}; // namespace TerminalColors
|
|
||||||
|
|
||||||
inline uint8_t ErrorLevel(ErrorType t)
|
|
||||||
{
|
|
||||||
unsigned int id = static_cast<int>(t);
|
|
||||||
if (id <= 1000)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (id > 1000 && id <= 2000)
|
|
||||||
{
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if (id > 2000)
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportError(const Error &error, const SourceManager &srcManager);
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#include <LSP/LSPServer.hpp>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <io.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
_setmode(_fileno(stdin), _O_BINARY);
|
|
||||||
_setmode(_fileno(stdout), _O_BINARY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Fig::LspServer server;
|
|
||||||
|
|
||||||
server.Run();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Ast.hpp>
|
|
||||||
#include <Lexer/Lexer.hpp>
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
#include <Sema/Analyzer.hpp>
|
|
||||||
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
#include <SourceManager/SourceManager.hpp>
|
|
||||||
|
|
||||||
#include <charconv> // C++17/20 字符串转数字
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <Utils/json/json.hpp>
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
|
|
||||||
class LspServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Run()
|
|
||||||
{
|
|
||||||
std::ios_base::sync_with_stdio(false);
|
|
||||||
std::cin.tie(NULL);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
if (!std::getline(std::cin, line))
|
|
||||||
break; // 退出循环
|
|
||||||
|
|
||||||
// 解析 HTTP 风格的 Header: "Content-Length: 123\r"
|
|
||||||
if (line.starts_with("Content-Length: "))
|
|
||||||
{
|
|
||||||
std::size_t length = 0;
|
|
||||||
// 使用 C++ 极速的 from_chars 替代 stoi
|
|
||||||
auto res = std::from_chars(line.data() + 16, line.data() + line.size(), length);
|
|
||||||
if (res.ec != std::errc())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// 消费 Header 和 Body 之间那行空的 "\r\n"
|
|
||||||
std::string emptyLine;
|
|
||||||
std::getline(std::cin, emptyLine);
|
|
||||||
|
|
||||||
// 读取对应字节的 JSON Body
|
|
||||||
std::string jsonBody(length, '\0');
|
|
||||||
std::cin.read(jsonBody.data(), length);
|
|
||||||
|
|
||||||
// 派发给路由
|
|
||||||
HandleMessage(jsonBody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void HandleMessage(const std::string &rawJson)
|
|
||||||
{
|
|
||||||
json req = json::parse(rawJson, nullptr, false);
|
|
||||||
if (req.is_discarded())
|
|
||||||
return; // 忽略解析失败的报文
|
|
||||||
|
|
||||||
std::string method = req.value("method", "");
|
|
||||||
|
|
||||||
if (method == "initialize")
|
|
||||||
{
|
|
||||||
Respond(req["id"], R"({
|
|
||||||
"capabilities": {
|
|
||||||
"textDocumentSync": 1,
|
|
||||||
"hoverProvider": true
|
|
||||||
}
|
|
||||||
})");
|
|
||||||
}
|
|
||||||
else if (method == "textDocument/didOpen")
|
|
||||||
{
|
|
||||||
std::string uri = req["params"]["textDocument"]["uri"];
|
|
||||||
std::string text = req["params"]["textDocument"]["text"];
|
|
||||||
PublishDiagnostics(uri, text);
|
|
||||||
}
|
|
||||||
else if (method == "textDocument/didChange")
|
|
||||||
{
|
|
||||||
std::string uri = req["params"]["textDocument"]["uri"];
|
|
||||||
std::string text = req["params"]["contentChanges"][0]["text"];
|
|
||||||
PublishDiagnostics(uri, text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Respond(int id, const std::string &resultJsonString)
|
|
||||||
{
|
|
||||||
std::string response = "{\"jsonrpc\":\"2.0\",\"id\":" + std::to_string(id)
|
|
||||||
+ ",\"result\":" + resultJsonString + "}";
|
|
||||||
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
|
||||||
<< response << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendDiagnostics(const std::string &uri, const Error *err = nullptr)
|
|
||||||
{
|
|
||||||
json diagnostics = json::array(); // 默认空数组,代码无错时擦除红线
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
// LSP 规定行列号必须从 0 开始算
|
|
||||||
int startLine = err->location.sp.line - 1;
|
|
||||||
int startChar = err->location.sp.column - 1;
|
|
||||||
int endLine = startLine;
|
|
||||||
int endChar = startChar + err->location.sp.tok_length;
|
|
||||||
|
|
||||||
std::string fullMessage = err->message.toStdString();
|
|
||||||
if (!err->suggestion.empty())
|
|
||||||
{
|
|
||||||
fullMessage += " 💡suggestion: " + err->suggestion.toStdString();
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics.push_back(
|
|
||||||
{{"range",
|
|
||||||
{{"start", {{"line", startLine}, {"character", startChar}}},
|
|
||||||
{"end", {{"line", endLine}, {"character", endChar}}}}},
|
|
||||||
{"severity", 1}, // 1 = 致命错误红线
|
|
||||||
{"source", "Fig LSP Server"},
|
|
||||||
{"message", fullMessage}});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组装 Notification
|
|
||||||
json notification = {{"jsonrpc", "2.0"},
|
|
||||||
{"method", "textDocument/publishDiagnostics"},
|
|
||||||
{"params", {{"uri", uri}, {"diagnostics", diagnostics}}}};
|
|
||||||
|
|
||||||
std::string response = notification.dump();
|
|
||||||
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
|
||||||
<< response << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PublishDiagnostics(const std::string &uri, const std::string &sourceCode)
|
|
||||||
{
|
|
||||||
SourceManager manager;
|
|
||||||
manager.LoadFromMemory(sourceCode);
|
|
||||||
|
|
||||||
Lexer lexer(sourceCode, "");
|
|
||||||
Parser parser(lexer, manager, "");
|
|
||||||
|
|
||||||
// 1. 语法检查拦截
|
|
||||||
auto parserResult = parser.Parse();
|
|
||||||
if (!parserResult)
|
|
||||||
{
|
|
||||||
SendDiagnostics(uri, &parserResult.error());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Program *program = *parserResult;
|
|
||||||
|
|
||||||
Analyzer analyzer(manager);
|
|
||||||
|
|
||||||
// 语义检查拦截
|
|
||||||
auto analyzerResult = analyzer.Analyze(program);
|
|
||||||
if (!analyzerResult)
|
|
||||||
{
|
|
||||||
SendDiagnostics(uri, &analyzerResult.error());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 一切完美,发射空数组清空过去的错误红线
|
|
||||||
SendDiagnostics(uri, nullptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -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,25 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Object/FunctionObject.hpp
|
|
||||||
@brief 函数对象定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-28
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Object/ObjectBase.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
// 运行时闭包对象 (24字节 Base + 8字节 Proto指针 = 32 bytes)
|
|
||||||
|
|
||||||
struct Proto;
|
|
||||||
struct FunctionObject final : public Object
|
|
||||||
{
|
|
||||||
Proto *proto; // 指向编译器生成的只读字节码与常量池
|
|
||||||
|
|
||||||
// TODO: 实现闭包时 加一个 Upvalue 指针数组
|
|
||||||
// Value* upvalues;
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Object/Object.hpp
|
|
||||||
@brief 值表示实现 (NaN Boxing) 和 堆对象函数的实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Object/ObjectBase.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
String Value::ToString() const
|
|
||||||
{
|
|
||||||
if (IsNull())
|
|
||||||
{
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
else if (IsInt())
|
|
||||||
{
|
|
||||||
return std::to_string(AsInt());
|
|
||||||
}
|
|
||||||
else if (IsDouble())
|
|
||||||
{
|
|
||||||
return std::format("{}", AsDouble());
|
|
||||||
}
|
|
||||||
else if (IsBool())
|
|
||||||
{
|
|
||||||
return (AsBool() ? "true" : "false");
|
|
||||||
}
|
|
||||||
else if (IsObject())
|
|
||||||
{
|
|
||||||
return "Object"; // TODO: 分派
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return "Unknow";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Object/Object.hpp
|
|
||||||
@brief 值系统总文件
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Object/ObjectBase.hpp>
|
|
||||||
#include <Object/StringObject.hpp>
|
|
||||||
#include <Object/StructObject.hpp>
|
|
||||||
#include <Object/FunctionObject.hpp>
|
|
||||||
@@ -1,231 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Object/ObjectBase.hpp
|
|
||||||
@brief 值表示定义 (NaN Boxing) uint64
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <bit>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Object; // 前置声明
|
|
||||||
|
|
||||||
/*
|
|
||||||
正常来说直接 Value = std::uint64_t会更快
|
|
||||||
但是这样会带来隐式转换的问题
|
|
||||||
|
|
||||||
因此我们封装成一个类,这样速度不会损失很多。
|
|
||||||
(release模式编译器会直接优化, 速度和uint64_t直接表示一样快)
|
|
||||||
*/
|
|
||||||
class Value
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::uint64_t v_; // 唯一的物理成员 sizeof(Value) 永远是 8 字节。
|
|
||||||
|
|
||||||
// --- 私有掩码常量 ---
|
|
||||||
static constexpr std::uint64_t QNAN_MASK = 0x7ffc000000000000;
|
|
||||||
static constexpr std::uint64_t SIGN_BIT = 0x8000000000000000;
|
|
||||||
|
|
||||||
// 专门给 Int32 预留的高位 Tag
|
|
||||||
static constexpr std::uint32_t INT_TAG_HIGH = 0x7FFD0000;
|
|
||||||
|
|
||||||
// 基础原语 Tag
|
|
||||||
static constexpr std::uint64_t TAG_NULL = 1;
|
|
||||||
static constexpr std::uint64_t TAG_FALSE = 2;
|
|
||||||
static constexpr std::uint64_t TAG_TRUE = 3;
|
|
||||||
|
|
||||||
// 私有底层构造:仅供内部组装使用
|
|
||||||
constexpr explicit Value(uint64_t raw) : v_(raw) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// 默认构造为 Null,保证未初始化变量也是安全的
|
|
||||||
constexpr Value()
|
|
||||||
{
|
|
||||||
*this = GetNullInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Value FromDouble(double d)
|
|
||||||
{
|
|
||||||
uint64_t raw = std::bit_cast<uint64_t>(d);
|
|
||||||
// 清洗非法的 NaN
|
|
||||||
if ((raw & QNAN_MASK) == QNAN_MASK)
|
|
||||||
return Value(QNAN_MASK);
|
|
||||||
return Value(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Value FromInt(std::int32_t i)
|
|
||||||
{
|
|
||||||
// 移位构造,彻底阻断符号扩展漏洞
|
|
||||||
return Value((static_cast<uint64_t>(INT_TAG_HIGH) << 32) | static_cast<uint32_t>(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Value &GetTrueInstance()
|
|
||||||
{
|
|
||||||
static Value trueInstance(QNAN_MASK | TAG_TRUE);
|
|
||||||
return trueInstance;
|
|
||||||
}
|
|
||||||
[[nodiscard]] static constexpr Value &GetFalseInstance()
|
|
||||||
{
|
|
||||||
static Value falseInstance(QNAN_MASK | TAG_FALSE);
|
|
||||||
return falseInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Value &FromBool(bool b)
|
|
||||||
{
|
|
||||||
return (b ? GetTrueInstance() : GetFalseInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static constexpr Value &GetNullInstance()
|
|
||||||
{
|
|
||||||
static Value nullInstance(QNAN_MASK | TAG_NULL);
|
|
||||||
return nullInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static Value FromObject(Object *ptr)
|
|
||||||
{
|
|
||||||
return Value(reinterpret_cast<uint64_t>(ptr) | SIGN_BIT | QNAN_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 类型检查 (Is)
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsDouble() const
|
|
||||||
{
|
|
||||||
return (v_ & QNAN_MASK) != QNAN_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsInt() const
|
|
||||||
{
|
|
||||||
// 安全的高 32 位移位判定
|
|
||||||
return static_cast<uint32_t>(v_ >> 32) == INT_TAG_HIGH;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsNumber() const
|
|
||||||
{
|
|
||||||
return IsDouble() || IsInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsNull() const
|
|
||||||
{
|
|
||||||
return v_ == (QNAN_MASK | TAG_NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsBool() const
|
|
||||||
{
|
|
||||||
return (v_ | 1) == (QNAN_MASK | TAG_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool IsObject() const
|
|
||||||
{
|
|
||||||
return (v_ & (SIGN_BIT | QNAN_MASK)) == (SIGN_BIT | QNAN_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提取数据 (Unbox / As)
|
|
||||||
[[nodiscard]] constexpr double AsDouble() const
|
|
||||||
{
|
|
||||||
return std::bit_cast<double>(v_);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr int32_t AsInt() const
|
|
||||||
{
|
|
||||||
return static_cast<int32_t>(v_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 核心辅助:泛型数字提取。算术指令可以直接用这个,免去手写 if 分支
|
|
||||||
// 若不是 int/double 会导致非常恐怖的问题
|
|
||||||
[[nodiscard]] constexpr double CastToDouble() const
|
|
||||||
{
|
|
||||||
return IsInt() ? static_cast<double>(AsInt()) : AsDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool AsBool() const
|
|
||||||
{
|
|
||||||
return v_ == (QNAN_MASK | TAG_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] struct Object *AsObject() const
|
|
||||||
{
|
|
||||||
return reinterpret_cast<struct Object *>(v_ & ~(SIGN_BIT | QNAN_MASK));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重载
|
|
||||||
|
|
||||||
// 暴露原生值用于硬核位运算或 Hash 计算
|
|
||||||
[[nodiscard]] constexpr uint64_t Raw() const
|
|
||||||
{
|
|
||||||
return v_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 让 VM 的 OP_EQ 指令极简:`if (RA == RB)`
|
|
||||||
[[nodiscard]] constexpr bool operator==(const Value &other) const
|
|
||||||
{
|
|
||||||
// IEEE 754 规定浮点数有 +0.0 == -0.0 的特殊规则,所以不直接比对raw bits而是转换成 double进行C++比对
|
|
||||||
if (IsDouble() && other.IsDouble())
|
|
||||||
{
|
|
||||||
return AsDouble() == other.AsDouble();
|
|
||||||
}
|
|
||||||
// 直接比较 64 位整数内存,所以堆对象比较为地址(运算符重载由Compiler处理)
|
|
||||||
return v_ == other.v_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool operator!=(const Value &other) const
|
|
||||||
{
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 类函数
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
String ToString() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
C风格继承 + 手动分发
|
|
||||||
禁止任何 virtual 达到最高效率
|
|
||||||
*/
|
|
||||||
enum class ObjectType : uint8_t
|
|
||||||
{
|
|
||||||
String,
|
|
||||||
Function,
|
|
||||||
Struct,
|
|
||||||
Instance,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StructObject /* : public Object */; // 结构体基类的定义,前向声明
|
|
||||||
|
|
||||||
// Total 24 bytes size
|
|
||||||
struct Object
|
|
||||||
{
|
|
||||||
Object *next; // 8 bytes: gc链表
|
|
||||||
StructObject *klass; // 8 bytes: 一切皆对象,父类指针
|
|
||||||
ObjectType type; // 1 byte : 类型
|
|
||||||
bool isMarked = false; // 1 byte : gc标记
|
|
||||||
// + 6 bytes padding
|
|
||||||
|
|
||||||
constexpr bool isString() const
|
|
||||||
{
|
|
||||||
return type == ObjectType::String;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isFunction() const
|
|
||||||
{
|
|
||||||
return type == ObjectType::Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isStruct() const
|
|
||||||
{
|
|
||||||
return type == ObjectType::Struct;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool isInstance() const
|
|
||||||
{
|
|
||||||
return type == ObjectType::Instance;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -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,54 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Object/StructObject.hpp
|
|
||||||
@brief 结构体类型 StructObject 定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Operator.hpp>
|
|
||||||
#include <Object/ObjectBase.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
// Total 24 bytes size
|
|
||||||
struct Object
|
|
||||||
{
|
|
||||||
Object *next; // 8 bytes: gc链表
|
|
||||||
Struct *klass; // 8 bytes: 一切皆对象,父类指针
|
|
||||||
ObjectType type; // 1 byte : 类型
|
|
||||||
bool isMarked = false; // 1 byte : gc标记
|
|
||||||
// + 6 bytes padding
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
struct StructObject final : public Object
|
|
||||||
{
|
|
||||||
String name; // 元信息(仅供调试/打印/反射)
|
|
||||||
|
|
||||||
// 内存布局信息
|
|
||||||
std::uint8_t fieldCount;
|
|
||||||
Object *operators[GetOperatorsSize()];
|
|
||||||
/*
|
|
||||||
运算符重载,nullptr代表无重载
|
|
||||||
一般为 NativeFunction / Function
|
|
||||||
|
|
||||||
排列:
|
|
||||||
[unary operators ]( binary operators]
|
|
||||||
0 - UnaryOperators::Count BinaryOperators::Count
|
|
||||||
*/
|
|
||||||
|
|
||||||
Object *GetUnaryOperator(UnaryOperator _op)
|
|
||||||
{
|
|
||||||
std::uint8_t idx = static_cast<std::uint8_t>(_op);
|
|
||||||
return operators[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *GetBinaryOperator(BinaryOperator _op)
|
|
||||||
{
|
|
||||||
std::uint16_t idx = static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(_op);
|
|
||||||
return operators[idx];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Parser/ExprParser.hpp
|
|
||||||
@brief 语法分析器(Pratt + 手动递归下降) 表达式解析实现 (pratt)
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<LiteralExpr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingLiteralExpr});
|
|
||||||
|
|
||||||
const Token &literal_token = consumeToken();
|
|
||||||
LiteralExpr *node = new LiteralExpr(literal_token, makeSourceLocation(literal_token));
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
Result<IdentiExpr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingIdentiExpr});
|
|
||||||
|
|
||||||
const Token &identifier = consumeToken();
|
|
||||||
IdentiExpr *node = new IdentiExpr(
|
|
||||||
srcManager.GetSub(identifier.index, identifier.length), makeSourceLocation(identifier));
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<InfixExpr *, Error> Parser::parseInfixExpr(Expr *lhs) // 当前token为 op
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingInfixExpr});
|
|
||||||
|
|
||||||
const Token &op_token = consumeToken();
|
|
||||||
BinaryOperator op = TokenToBinaryOp(op_token);
|
|
||||||
BindingPower rbp = GetBinaryOpRBp(op);
|
|
||||||
|
|
||||||
const auto &rhs_result = parseExpression(rbp);
|
|
||||||
if (!rhs_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(rhs_result.error());
|
|
||||||
}
|
|
||||||
Expr *rhs = *rhs_result;
|
|
||||||
|
|
||||||
InfixExpr *node = new InfixExpr(lhs, op, rhs);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<PrefixExpr *, Error> Parser::parsePrefixExpr() // 当前token为op
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingPrefixExpr});
|
|
||||||
|
|
||||||
const Token &op_token = consumeToken();
|
|
||||||
UnaryOperator op = TokenToUnaryOp(op_token);
|
|
||||||
|
|
||||||
BindingPower rbp = GetUnaryOpRBp(op);
|
|
||||||
const auto &rhs_result = parseExpression(rbp);
|
|
||||||
if (!rhs_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(rhs_result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *rhs = *rhs_result;
|
|
||||||
PrefixExpr *node = new PrefixExpr(op, rhs);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<IndexExpr *, Error> Parser::parseIndexExpr(
|
|
||||||
Expr *base) // 由 parseExpression调用, 当前token为 `[`
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingIndexExpr});
|
|
||||||
|
|
||||||
const Token &lbracket_token = consumeToken(); // consume `[`
|
|
||||||
const auto &index_result = parseExpression();
|
|
||||||
|
|
||||||
if (!index_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(index_result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::RightBracket) // `]`
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed brackets",
|
|
||||||
"insert `]`",
|
|
||||||
makeSourceLocation(lbracket_token)));
|
|
||||||
}
|
|
||||||
consumeToken(); // consume `]`
|
|
||||||
|
|
||||||
IndexExpr *indexExpr = new IndexExpr(base, *index_result);
|
|
||||||
return indexExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<CallExpr *, Error> Parser::parseCallExpr(
|
|
||||||
Expr *callee) // 由 parseExpression调用, 当前token为 `(`
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingCallExpr});
|
|
||||||
|
|
||||||
const Token &lparen_token = consumeToken(); // consume `(`
|
|
||||||
|
|
||||||
FnCallArgs callArgs;
|
|
||||||
|
|
||||||
// 空参数列表
|
|
||||||
if (currentToken().type == TokenType::RightParen)
|
|
||||||
{
|
|
||||||
consumeToken(); // consume `)`
|
|
||||||
return new CallExpr(callee, callArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (currentToken().type == TokenType::EndOfFile)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"fn call has unclosed parenthese",
|
|
||||||
"insert `)`",
|
|
||||||
makeSourceLocation(lparen_token)));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &arg_result = parseExpression();
|
|
||||||
if (!arg_result)
|
|
||||||
return std::unexpected(arg_result.error());
|
|
||||||
|
|
||||||
callArgs.args.push_back(*arg_result);
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::RightParen)
|
|
||||||
{
|
|
||||||
consumeToken(); // consume `)`
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::Comma)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"expected `,` or `)` in argument list",
|
|
||||||
"insert `,`",
|
|
||||||
makeSourceLocation(currentToken())));
|
|
||||||
}
|
|
||||||
|
|
||||||
consumeToken(); // consume `,`
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CallExpr(callee, callArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
|
||||||
{
|
|
||||||
Expr *lhs = nullptr;
|
|
||||||
Token token = currentToken();
|
|
||||||
|
|
||||||
if (token.isIdentifier())
|
|
||||||
{
|
|
||||||
const auto &lhs_result = parseIdentiExpr();
|
|
||||||
if (!lhs_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(lhs_result.error());
|
|
||||||
}
|
|
||||||
lhs = *lhs_result;
|
|
||||||
}
|
|
||||||
else if (token.isLiteral())
|
|
||||||
{
|
|
||||||
const auto &lhs_result = parseLiteralExpr();
|
|
||||||
if (!lhs_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(lhs_result.error());
|
|
||||||
}
|
|
||||||
lhs = *lhs_result;
|
|
||||||
}
|
|
||||||
else if (IsTokenOp(token.type, false)) // 是否是一元运算符
|
|
||||||
{
|
|
||||||
const auto &lhs_result = parsePrefixExpr();
|
|
||||||
if (!lhs_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(lhs_result.error());
|
|
||||||
}
|
|
||||||
lhs = *lhs_result;
|
|
||||||
}
|
|
||||||
else if (token.type == TokenType::LeftParen)
|
|
||||||
{
|
|
||||||
const Token &lparen_token = consumeToken(); // consume `(`
|
|
||||||
const auto &expr_result = parseExpression(0);
|
|
||||||
if (!expr_result)
|
|
||||||
{
|
|
||||||
return expr_result;
|
|
||||||
}
|
|
||||||
const Token &rparen_token = consumeToken(); // consume `)`
|
|
||||||
if (rparen_token.type != TokenType::RightParen)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed parenthese",
|
|
||||||
"insert `)`",
|
|
||||||
makeSourceLocation(lparen_token)));
|
|
||||||
}
|
|
||||||
lhs = *expr_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lhs)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
|
||||||
"expected expression",
|
|
||||||
"insert expressions",
|
|
||||||
makeSourceLocation(prevToken())));
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
token = currentToken();
|
|
||||||
if (shouldTerminate())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
|
||||||
{
|
|
||||||
BinaryOperator op = TokenToBinaryOp(token);
|
|
||||||
BindingPower lbp = GetBinaryOpLBp(op);
|
|
||||||
if (rbp >= lbp)
|
|
||||||
{
|
|
||||||
// 前操作数的右绑定力比当前操作数的左绑定力大
|
|
||||||
// lhs被吸走
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = parseInfixExpr(lhs);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
resetTermintors();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
lhs = *result;
|
|
||||||
}
|
|
||||||
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
|
||||||
// 而不用走正常路径
|
|
||||||
else if (token.type == TokenType::LeftBracket) // `[`
|
|
||||||
{
|
|
||||||
const auto &expr_result = parseIndexExpr(lhs);
|
|
||||||
if (!expr_result)
|
|
||||||
{
|
|
||||||
resetTermintors();
|
|
||||||
return expr_result;
|
|
||||||
}
|
|
||||||
lhs = *expr_result;
|
|
||||||
}
|
|
||||||
else if (token.type == TokenType::LeftParen) // `(`
|
|
||||||
{
|
|
||||||
const auto &expr_result = parseCallExpr(lhs);
|
|
||||||
if (!expr_result)
|
|
||||||
{
|
|
||||||
resetTermintors();
|
|
||||||
return expr_result;
|
|
||||||
}
|
|
||||||
lhs = *expr_result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
|
||||||
"expression unexpectedly ended",
|
|
||||||
"insert expressions",
|
|
||||||
makeSourceLocation(token)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Parser/Parser.cpp
|
|
||||||
@brief 语法分析器(Pratt + 手动递归下降) 实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<Program *, Error> Parser::Parse()
|
|
||||||
{
|
|
||||||
Program *program = new Program;
|
|
||||||
while (!isEOF)
|
|
||||||
{
|
|
||||||
auto result = parseStatement();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
Stmt *stmt = *result;
|
|
||||||
if (!stmt)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
program->nodes.push_back(stmt);
|
|
||||||
}
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,321 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Parser/Parser.hpp
|
|
||||||
@brief 语法分析器(Pratt + 手动递归下降) 定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-14
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Ast/Ast.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
#include <Lexer/Lexer.hpp>
|
|
||||||
#include <Token/Token.hpp>
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Lexer &lexer;
|
|
||||||
SourceManager &srcManager;
|
|
||||||
|
|
||||||
size_t index = 0; // token在buffer下标
|
|
||||||
DynArray<Token> buffer;
|
|
||||||
|
|
||||||
String fileName;
|
|
||||||
|
|
||||||
bool isEOF = false;
|
|
||||||
|
|
||||||
Token nextToken()
|
|
||||||
{
|
|
||||||
assert(!isEOF && "nextToken: eof but called nextToken");
|
|
||||||
if (index + 1 < buffer.size())
|
|
||||||
{
|
|
||||||
return buffer[++index];
|
|
||||||
}
|
|
||||||
auto result = lexer.NextToken();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
ReportError(result.error(), srcManager);
|
|
||||||
std::exit(-1);
|
|
||||||
}
|
|
||||||
const Token &token = result.value();
|
|
||||||
if (token.type == TokenType::EndOfFile)
|
|
||||||
{
|
|
||||||
isEOF = true;
|
|
||||||
}
|
|
||||||
buffer.push_back(token);
|
|
||||||
index++;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Token prevToken()
|
|
||||||
{
|
|
||||||
if (buffer.size() < 2)
|
|
||||||
{
|
|
||||||
return currentToken();
|
|
||||||
}
|
|
||||||
return buffer[buffer.size() - 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Token currentToken()
|
|
||||||
{
|
|
||||||
if (buffer.empty())
|
|
||||||
{
|
|
||||||
return nextToken();
|
|
||||||
}
|
|
||||||
return buffer.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
Token peekToken(size_t lookahead = 1)
|
|
||||||
{
|
|
||||||
assert(!isEOF && "peekToken: eof but called peekToken");
|
|
||||||
|
|
||||||
size_t peekIndex = index + lookahead;
|
|
||||||
while (peekIndex >= buffer.size() && !isEOF)
|
|
||||||
{
|
|
||||||
auto result = lexer.NextToken();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
ReportError(result.error(), srcManager);
|
|
||||||
std::abort();
|
|
||||||
}
|
|
||||||
const Token &token = result.value();
|
|
||||||
if (token.type == TokenType::EndOfFile)
|
|
||||||
{
|
|
||||||
isEOF = true;
|
|
||||||
}
|
|
||||||
buffer.push_back(token);
|
|
||||||
}
|
|
||||||
if (peekIndex >= buffer.size()) // 没有那么多token
|
|
||||||
{
|
|
||||||
return buffer.back(); // back是EOF Token
|
|
||||||
}
|
|
||||||
return buffer[peekIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Token consumeToken()
|
|
||||||
{
|
|
||||||
if (isEOF)
|
|
||||||
return buffer.back();
|
|
||||||
Token current = currentToken();
|
|
||||||
nextToken();
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool match(TokenType type)
|
|
||||||
{
|
|
||||||
if (currentToken().type == type)
|
|
||||||
{
|
|
||||||
consumeToken();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Error makeUnexpectTokenError(const String &stmtType,
|
|
||||||
const String &expect,
|
|
||||||
const Token &tokenGot,
|
|
||||||
std::source_location loc = std::source_location::current())
|
|
||||||
{
|
|
||||||
return Error(ErrorType::SyntaxError,
|
|
||||||
std::format("expect '{}' in {}, got `{}`",
|
|
||||||
expect,
|
|
||||||
stmtType,
|
|
||||||
magic_enum::enum_name(tokenGot.type)),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(tokenGot),
|
|
||||||
loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Error makeExpectSemicolonError(
|
|
||||||
std::source_location loc = std::source_location::current())
|
|
||||||
{
|
|
||||||
return Error(ErrorType::SyntaxError,
|
|
||||||
"expect ';' after statement",
|
|
||||||
"insert ';'",
|
|
||||||
makeSourceLocation(currentToken()),
|
|
||||||
loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct State
|
|
||||||
{
|
|
||||||
enum StateType : std::uint8_t
|
|
||||||
{
|
|
||||||
Standby,
|
|
||||||
|
|
||||||
ParsingLiteralExpr,
|
|
||||||
ParsingIdentiExpr,
|
|
||||||
|
|
||||||
ParsingInfixExpr,
|
|
||||||
ParsingPrefixExpr,
|
|
||||||
|
|
||||||
ParsingIndexExpr,
|
|
||||||
ParsingCallExpr,
|
|
||||||
|
|
||||||
ParsingVarDecl,
|
|
||||||
ParsingIf,
|
|
||||||
ParsingWhile,
|
|
||||||
ParsingFnDefStmt,
|
|
||||||
ParsingReturn,
|
|
||||||
ParsingBreak,
|
|
||||||
ParsingContinue,
|
|
||||||
|
|
||||||
ParsingNamedTypeExpr,
|
|
||||||
|
|
||||||
} type = StateType::Standby;
|
|
||||||
std::unordered_set<TokenType> stopAt = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
const std::unordered_set<TokenType> &getBaseTerminators()
|
|
||||||
{
|
|
||||||
static const std::unordered_set<TokenType> baseTerminators = {TokenType::Semicolon,
|
|
||||||
TokenType::RightParen,
|
|
||||||
TokenType::RightBracket,
|
|
||||||
TokenType::RightBrace,
|
|
||||||
TokenType::Comma,
|
|
||||||
TokenType::EndOfFile};
|
|
||||||
return baseTerminators;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<TokenType> &getTerminators() // 返回固定的终止符
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Syntax terminators:
|
|
||||||
; ) ] } , EOF
|
|
||||||
*/
|
|
||||||
|
|
||||||
static std::unordered_set<TokenType> terminators(getBaseTerminators());
|
|
||||||
return terminators;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetTermintors()
|
|
||||||
{
|
|
||||||
getTerminators() = getBaseTerminators();
|
|
||||||
}
|
|
||||||
bool shouldTerminate() // 判断是否终结
|
|
||||||
{
|
|
||||||
const Token &token = currentToken();
|
|
||||||
const auto &terminators = getTerminators();
|
|
||||||
|
|
||||||
if (terminators.contains(token.type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (auto it = stateStack.rbegin(); it < stateStack.rend(); ++it)
|
|
||||||
{
|
|
||||||
if (it->stopAt.contains(token.type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DynArray<State> stateStack;
|
|
||||||
|
|
||||||
State ¤tState()
|
|
||||||
{
|
|
||||||
return stateStack.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushState(State _state)
|
|
||||||
{
|
|
||||||
stateStack.push_back(std::move(_state));
|
|
||||||
}
|
|
||||||
|
|
||||||
void popState()
|
|
||||||
{
|
|
||||||
if (!stateStack.empty())
|
|
||||||
{
|
|
||||||
stateStack.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StateProtector
|
|
||||||
{
|
|
||||||
Parser *parser;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StateProtector(Parser *p, const State &newState) : parser(p)
|
|
||||||
{
|
|
||||||
parser->pushState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
~StateProtector()
|
|
||||||
{
|
|
||||||
parser->popState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁止拷贝
|
|
||||||
StateProtector(const StateProtector &) = delete;
|
|
||||||
StateProtector &operator=(const StateProtector &) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
|
|
||||||
lexer(_lexer), srcManager(_srcManager), fileName(std::move(_fileName))
|
|
||||||
{
|
|
||||||
pushState(State());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
SourceLocation makeSourceLocation(const Token &tok)
|
|
||||||
{
|
|
||||||
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
|
||||||
return SourceLocation(SourcePosition(line, column, tok.length),
|
|
||||||
fileName,
|
|
||||||
"[internal parser]",
|
|
||||||
magic_enum::enum_name(currentState().type).data());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TypeExpressions */
|
|
||||||
|
|
||||||
Result<NamedTypeExpr *, Error> parseNamedTypeExpr(); // 当前token为identifier
|
|
||||||
|
|
||||||
Result<TypeExpr *, Error> parseTypeExpr();
|
|
||||||
|
|
||||||
/* Expressions */
|
|
||||||
Result<LiteralExpr *, Error> parseLiteralExpr(); // 当前token为literal时调用
|
|
||||||
Result<IdentiExpr *, Error> parseIdentiExpr(); // 当前token为Identifier调用
|
|
||||||
|
|
||||||
Result<InfixExpr *, Error> parseInfixExpr(
|
|
||||||
Expr *); // 由 parseExpression递归调用, 当前token为op
|
|
||||||
Result<PrefixExpr *, Error> parsePrefixExpr(); // 由 parseExpression递归调用, 当前token为op
|
|
||||||
|
|
||||||
Result<IndexExpr *, Error> parseIndexExpr(
|
|
||||||
Expr *); // 由 parseExpression调用, 当前token为 `[`
|
|
||||||
Result<CallExpr *, Error> parseCallExpr(Expr *); // 由 parseExpression调用, 当前token为 `(`
|
|
||||||
|
|
||||||
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
|
||||||
|
|
||||||
/* Statements */
|
|
||||||
Result<BlockStmt *, Error> parseBlockStmt(); // 当前token为 {
|
|
||||||
Result<VarDecl *, Error> parseVarDecl(bool); // 由 parseStatement调用, 当前token为 var
|
|
||||||
Result<IfStmt *, Error> parseIfStmt(); // 由 parseStatement调用, 当前token为 if
|
|
||||||
Result<WhileStmt *, Error> parseWhileStmt(); // 由 parseStatement调用, 当前token为 while
|
|
||||||
|
|
||||||
Result<DynArray<Param *>, Error> parseFnParams(); // 由 parseFnDefStmt或lambda调用
|
|
||||||
Result<FnDefStmt *, Error> parseFnDefStmt(bool); // 由 parseStatement调用, 当前token为 func
|
|
||||||
|
|
||||||
Result<ReturnStmt *, Error> parseReturnStmt(); // 由 parseStatement调用, 当前token为 return
|
|
||||||
// continue break直接由parseStatement一步解析
|
|
||||||
|
|
||||||
Result<Stmt *, Error> parseStatement();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Result<Program *, Error> Parse();
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SET_STOP_AT(...) currentState().stopAt = {__VA_ARGS__};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include <Parser/Parser.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
using namespace Fig;
|
|
||||||
|
|
||||||
String fileName = "test.fig";
|
|
||||||
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
|
||||||
SourceManager srcManager(filePath);
|
|
||||||
|
|
||||||
String source = srcManager.Read();
|
|
||||||
if (!srcManager.read)
|
|
||||||
{
|
|
||||||
std::cerr << "Couldn't read file";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lexer lexer(source, fileName);
|
|
||||||
Parser parser(lexer, srcManager, fileName);
|
|
||||||
auto result = parser.Parse();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
ReportError(result.error(), srcManager);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
Program *program = *result;
|
|
||||||
for (Stmt *stmt : program->nodes)
|
|
||||||
{
|
|
||||||
std::cout << stmt->toString() << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,542 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Parser/StmtParser.hpp
|
|
||||||
@brief 语法分析器(Pratt + 手动递归下降) 语句解析实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<BlockStmt *, Error> Parser::parseBlockStmt() // 当前token为 {
|
|
||||||
{
|
|
||||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `{`
|
|
||||||
BlockStmt *stmt = new BlockStmt();
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (isEOF)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed braces in block stmt",
|
|
||||||
"insert '}'",
|
|
||||||
location));
|
|
||||||
}
|
|
||||||
if (match(TokenType::RightBrace))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto result = parseStatement();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
stmt->nodes.push_back(*result);
|
|
||||||
}
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
Result<VarDecl *, Error> Parser::parseVarDecl(
|
|
||||||
bool isPublic) // 由 parseStatement调用, 当前token为 var
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingVarDecl});
|
|
||||||
|
|
||||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `var`
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::Identifier)
|
|
||||||
{
|
|
||||||
return std::unexpected(makeUnexpectTokenError("VarDecl", "var name", currentToken()));
|
|
||||||
}
|
|
||||||
const String &name = srcManager.GetSub(currentToken().index, currentToken().length);
|
|
||||||
consumeToken(); // consume name
|
|
||||||
|
|
||||||
TypeExpr *typeSpeicifer = nullptr;
|
|
||||||
if (match(TokenType::Colon)) // `:`
|
|
||||||
{
|
|
||||||
// SET_STOP_AT(TokenType::Walrus, TokenType::Assign);
|
|
||||||
auto result = parseTypeExpr();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
typeSpeicifer = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *initExpr = nullptr;
|
|
||||||
bool isInfer = false;
|
|
||||||
if (match(TokenType::Assign))
|
|
||||||
{
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
initExpr = *result;
|
|
||||||
}
|
|
||||||
else if (match(TokenType::Walrus)) // :=
|
|
||||||
{
|
|
||||||
if (typeSpeicifer) // 指定了类型同时使用 :=
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"used type infer but specifying the type",
|
|
||||||
"change `:=` to '='",
|
|
||||||
makeSourceLocation(prevToken()) // :=
|
|
||||||
));
|
|
||||||
}
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
initExpr = *result;
|
|
||||||
isInfer = true; // 使用类型自动推断 :=
|
|
||||||
}
|
|
||||||
if (!match(TokenType::Semicolon))
|
|
||||||
{
|
|
||||||
return std::unexpected(makeExpectSemicolonError());
|
|
||||||
}
|
|
||||||
VarDecl *varDecl = new VarDecl(isPublic, name, typeSpeicifer, isInfer, initExpr, location);
|
|
||||||
return varDecl;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<IfStmt *, Error> Parser::parseIfStmt() // 由 parseStatement调用, 当前token is if
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingIf});
|
|
||||||
|
|
||||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `if`
|
|
||||||
|
|
||||||
Expr *cond = nullptr;
|
|
||||||
if (match(TokenType::LeftParen)) // match and consume `(`
|
|
||||||
{
|
|
||||||
const Token &lpToken = prevToken();
|
|
||||||
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
|
|
||||||
const auto &result = parseExpression(0);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
if (!match(TokenType::RightParen))
|
|
||||||
{
|
|
||||||
delete *result;
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed parenthese in if condition",
|
|
||||||
"insert `)`",
|
|
||||||
makeSourceLocation(lpToken)));
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SET_STOP_AT(TokenType::LeftBrace);
|
|
||||||
auto result = parseExpression(0);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("IfStmt", "LeftBrace `{`", currentToken()));
|
|
||||||
}
|
|
||||||
auto result = parseBlockStmt();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
BlockStmt *consequent = *result;
|
|
||||||
|
|
||||||
DynArray<ElseIfStmt *> elifs;
|
|
||||||
BlockStmt *alternate = nullptr;
|
|
||||||
|
|
||||||
while (match(TokenType::Else))
|
|
||||||
{
|
|
||||||
SourceLocation elseLocation = makeSourceLocation(prevToken());
|
|
||||||
if (match(TokenType::If))
|
|
||||||
{
|
|
||||||
// else if
|
|
||||||
if (alternate)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"else if after else",
|
|
||||||
"remove else if",
|
|
||||||
elseLocation));
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *cond = nullptr;
|
|
||||||
|
|
||||||
if (match(TokenType::LeftParen)) // `(`
|
|
||||||
{
|
|
||||||
const Token &lpToken = prevToken();
|
|
||||||
|
|
||||||
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
|
|
||||||
const auto &result = parseExpression(0);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
if (!match(TokenType::RightParen))
|
|
||||||
{
|
|
||||||
delete *result;
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed parenthese in if condition",
|
|
||||||
"insert `)`",
|
|
||||||
makeSourceLocation(lpToken)));
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SET_STOP_AT(TokenType::LeftBrace);
|
|
||||||
auto result = parseExpression(0);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
if (currentToken().type != TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("ElseIfStmt", "LeftBrace `{`", currentToken()));
|
|
||||||
}
|
|
||||||
auto result = parseBlockStmt();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
BlockStmt *consequent = *result;
|
|
||||||
ElseIfStmt *elif = new ElseIfStmt(cond, consequent, elseLocation);
|
|
||||||
elifs.push_back(elif);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// else
|
|
||||||
if (alternate)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"duplicate else in if stmt",
|
|
||||||
"remove it",
|
|
||||||
elseLocation));
|
|
||||||
}
|
|
||||||
if (currentToken().type != TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("ElseStmt", "LeftBrace `{`", currentToken()));
|
|
||||||
}
|
|
||||||
auto result = parseBlockStmt();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
alternate = *result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IfStmt *ifStmt = new IfStmt(cond, consequent, elifs, alternate, location);
|
|
||||||
return ifStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<WhileStmt *, Error> Parser::parseWhileStmt() // 由 parseStatement调用, 当前token为 while
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingWhile});
|
|
||||||
|
|
||||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `while`
|
|
||||||
|
|
||||||
Expr *cond = nullptr;
|
|
||||||
if (match(TokenType::LeftParen))
|
|
||||||
{
|
|
||||||
const Token &lpToken = prevToken();
|
|
||||||
SET_STOP_AT(TokenType::RightParen, TokenType::LeftBrace);
|
|
||||||
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match(TokenType::RightParen))
|
|
||||||
{
|
|
||||||
delete *result;
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed parenthese in while condition",
|
|
||||||
"insert ')'",
|
|
||||||
makeSourceLocation(lpToken)));
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SET_STOP_AT(TokenType::LeftBrace);
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
cond = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
delete cond;
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("while stmt", "left brace '{'", currentToken()));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = parseBlockStmt();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
delete cond;
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
BlockStmt *body = *result;
|
|
||||||
|
|
||||||
WhileStmt *whileStmt = new WhileStmt(cond, body, location);
|
|
||||||
return whileStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<DynArray<Param *>, Error> Parser::parseFnParams() // 由 parseFnDefStmt或lambda调用
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingFnDefStmt});
|
|
||||||
|
|
||||||
const Token &lpToken = consumeToken(); // consume `(`
|
|
||||||
DynArray<Param *> params;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (isEOF)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"unclosed parenthese in function parameters",
|
|
||||||
"insert ')'",
|
|
||||||
makeSourceLocation(lpToken)));
|
|
||||||
}
|
|
||||||
if (match(TokenType::RightParen))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Token &nToken = consumeToken();
|
|
||||||
SourceLocation location = makeSourceLocation(nToken);
|
|
||||||
const String &name = srcManager.GetSub(nToken.index, nToken.length);
|
|
||||||
|
|
||||||
// TODO: 支持剩余参数解析...
|
|
||||||
|
|
||||||
TypeExpr *type = nullptr;
|
|
||||||
if (match(TokenType::Colon)) // :
|
|
||||||
{
|
|
||||||
auto result = parseTypeExpr();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
type = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *defaultValue = nullptr;
|
|
||||||
|
|
||||||
if (match(TokenType::Assign)) // =
|
|
||||||
{
|
|
||||||
SET_STOP_AT(TokenType::Comma, TokenType::RightParen, TokenType::LeftBrace); // , ) {
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
if (type)
|
|
||||||
{
|
|
||||||
delete type;
|
|
||||||
}
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
defaultValue = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
PosParam *posParam = new PosParam(name, type, defaultValue, location);
|
|
||||||
params.push_back(posParam);
|
|
||||||
|
|
||||||
if (match(TokenType::Comma))
|
|
||||||
{
|
|
||||||
if (!currentToken().isIdentifier())
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("fn params", "param name", currentToken()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<FnDefStmt *, Error> Parser::parseFnDefStmt(
|
|
||||||
bool isPublic) // 由 parseStatement调用, 当前token为 func
|
|
||||||
{
|
|
||||||
SourceLocation location = makeSourceLocation(
|
|
||||||
consumeToken()); // 无论是否加了public, location都设置为 func token (我懒 :D)
|
|
||||||
|
|
||||||
if (!currentToken().isIdentifier())
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("fn def stmt", "function name", currentToken()));
|
|
||||||
}
|
|
||||||
const Token &nameToken = consumeToken(); // consume name
|
|
||||||
const String &name = srcManager.GetSub(nameToken.index, nameToken.length);
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::LeftParen)
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("fn def stmt", "lparen '('", currentToken()));
|
|
||||||
}
|
|
||||||
|
|
||||||
DynArray<Param *> params;
|
|
||||||
|
|
||||||
auto paraResult = parseFnParams();
|
|
||||||
if (!paraResult)
|
|
||||||
{
|
|
||||||
return std::unexpected(paraResult.error());
|
|
||||||
}
|
|
||||||
params = *paraResult;
|
|
||||||
|
|
||||||
TypeExpr *returnType = nullptr;
|
|
||||||
if (match(TokenType::RightArrow)) // ->
|
|
||||||
{
|
|
||||||
auto result = parseTypeExpr();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
returnType = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type != TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("fn def stmt", "function body '{'", currentToken()));
|
|
||||||
}
|
|
||||||
BlockStmt *body = nullptr;
|
|
||||||
auto bodyResult = parseBlockStmt();
|
|
||||||
|
|
||||||
if (!bodyResult)
|
|
||||||
{
|
|
||||||
if (returnType)
|
|
||||||
{
|
|
||||||
delete returnType;
|
|
||||||
}
|
|
||||||
return std::unexpected(bodyResult.error());
|
|
||||||
}
|
|
||||||
body = *bodyResult;
|
|
||||||
|
|
||||||
FnDefStmt *fnDef = new FnDefStmt(isPublic, name, params, returnType, body, location);
|
|
||||||
return fnDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<ReturnStmt *, Error>
|
|
||||||
Parser::parseReturnStmt() // 由 parseStatement调用, 当前token为 return
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingReturn});
|
|
||||||
|
|
||||||
SourceLocation location = makeSourceLocation(consumeToken()); // consume `return`
|
|
||||||
auto result = parseExpression();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr *value = *result;
|
|
||||||
ReturnStmt *returnStmt = new ReturnStmt(value, location);
|
|
||||||
|
|
||||||
if (!match(TokenType::Semicolon))
|
|
||||||
{
|
|
||||||
return std::unexpected(makeExpectSemicolonError());
|
|
||||||
}
|
|
||||||
return returnStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Stmt *, Error> Parser::parseStatement()
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::Standby});
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::Public)
|
|
||||||
{
|
|
||||||
consumeToken(); // consume `public`
|
|
||||||
if (currentToken().type == TokenType::Variable)
|
|
||||||
{
|
|
||||||
return parseVarDecl(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::Function)
|
|
||||||
{
|
|
||||||
return parseFnDefStmt(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("public", "var/const/func/struct", currentToken()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::LeftBrace)
|
|
||||||
{
|
|
||||||
return parseBlockStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::Variable)
|
|
||||||
{
|
|
||||||
return parseVarDecl(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::If)
|
|
||||||
{
|
|
||||||
return parseIfStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::While)
|
|
||||||
{
|
|
||||||
return parseWhileStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::Function)
|
|
||||||
{
|
|
||||||
return parseFnDefStmt(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentToken().type == TokenType::Return)
|
|
||||||
{
|
|
||||||
return parseReturnStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::Break))
|
|
||||||
{
|
|
||||||
SourceLocation location = makeSourceLocation(prevToken());
|
|
||||||
if (!match(TokenType::Semicolon))
|
|
||||||
{
|
|
||||||
return std::unexpected(makeExpectSemicolonError());
|
|
||||||
}
|
|
||||||
BreakStmt *breakStmt = new BreakStmt(location);
|
|
||||||
return breakStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match(TokenType::Continue))
|
|
||||||
{
|
|
||||||
SourceLocation location = makeSourceLocation(prevToken());
|
|
||||||
if (!match(TokenType::Semicolon))
|
|
||||||
{
|
|
||||||
return std::unexpected(makeExpectSemicolonError());
|
|
||||||
}
|
|
||||||
ContinueStmt *continueStmt = new ContinueStmt(location);
|
|
||||||
return continueStmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEOF)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &expr_result = parseExpression();
|
|
||||||
if (!expr_result)
|
|
||||||
{
|
|
||||||
return std::unexpected(expr_result.error());
|
|
||||||
}
|
|
||||||
ExprStmt *exprStmt = new ExprStmt(*expr_result);
|
|
||||||
if (!match(TokenType::Semicolon))
|
|
||||||
{
|
|
||||||
return std::unexpected(makeExpectSemicolonError());
|
|
||||||
}
|
|
||||||
return exprStmt;
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Parser/TypeExprParser.cpp
|
|
||||||
@brief 类型表达式解析器实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-25
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<NamedTypeExpr *, Error> Parser::parseNamedTypeExpr() // 当前token为identifier
|
|
||||||
{
|
|
||||||
StateProtector p(this, {State::ParsingNamedTypeExpr});
|
|
||||||
|
|
||||||
SourceLocation location = makeSourceLocation(currentToken());
|
|
||||||
|
|
||||||
DynArray<String> path;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const Token &subPathTok = consumeToken();
|
|
||||||
const String &subPath = srcManager.GetSub(subPathTok.index, subPathTok.length);
|
|
||||||
path.push_back(subPath);
|
|
||||||
|
|
||||||
if (match(TokenType::Dot))
|
|
||||||
{
|
|
||||||
if (!currentToken().isIdentifier())
|
|
||||||
{
|
|
||||||
return std::unexpected(
|
|
||||||
makeUnexpectTokenError("named type expr", "identifier", currentToken()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NamedTypeExpr *namedTypeExpr = new NamedTypeExpr(path, location);
|
|
||||||
return namedTypeExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<TypeExpr *, Error> Parser::parseTypeExpr()
|
|
||||||
{
|
|
||||||
// TODO: 泛型表达式解析
|
|
||||||
if (currentToken().isIdentifier())
|
|
||||||
{
|
|
||||||
return parseNamedTypeExpr();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::unexpected(makeUnexpectTokenError("type expr", "name/...", currentToken()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,662 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Sema/Analyzer.hpp
|
|
||||||
@brief 前端类型检查器实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Sema/Analyzer.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<TypeInfo *, Error> Analyzer::resolveType(TypeExpr *typeExpr)
|
|
||||||
{
|
|
||||||
NamedTypeExpr *nte = dynamic_cast<NamedTypeExpr *>(typeExpr);
|
|
||||||
TypeInfo *type = nullptr;
|
|
||||||
if (nte)
|
|
||||||
{
|
|
||||||
type = typeCtx.ResolveTypePath(nte->path);
|
|
||||||
if (!type)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format("no such type `{}` exists", nte->toString()),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(typeExpr)));
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeVarDecl(VarDecl *stmt)
|
|
||||||
{
|
|
||||||
auto sym = env.Resolve(stmt->name);
|
|
||||||
if (sym != std::nullopt && sym->depth == env.GetDepth())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::RedeclarationError,
|
|
||||||
std::format("variable `{}` has already defined in this scope", stmt->name),
|
|
||||||
"change its name",
|
|
||||||
makeSourceLocation(stmt)));
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeInfo *initType = typeCtx.GetNull();
|
|
||||||
if (stmt->initExpr)
|
|
||||||
{
|
|
||||||
const auto &res = analyzeExpr(stmt->initExpr);
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
initType = stmt->initExpr->resolvedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeInfo *declaredType = typeCtx.GetAny();
|
|
||||||
if (stmt->typeSpecifier)
|
|
||||||
{
|
|
||||||
auto result = resolveType(stmt->typeSpecifier);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
declaredType = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stmt->isInfer)
|
|
||||||
{
|
|
||||||
declaredType = initType;
|
|
||||||
}
|
|
||||||
else if (stmt->initExpr && declaredType != typeCtx.GetAny() && declaredType != initType)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format("cannot assign type `{}` to variable {} which speicifer type is '{}'",
|
|
||||||
initType->name,
|
|
||||||
stmt->name,
|
|
||||||
declaredType->name),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(stmt->initExpr)));
|
|
||||||
}
|
|
||||||
stmt->localId = env.Define(stmt->name, declaredType, stmt->isPublic, false);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeIfStmt(IfStmt *stmt)
|
|
||||||
{
|
|
||||||
auto condRes = analyzeExpr(stmt->cond);
|
|
||||||
if (!condRes)
|
|
||||||
{
|
|
||||||
return condRes;
|
|
||||||
}
|
|
||||||
if (stmt->cond->resolvedType != typeCtx.GetAny()
|
|
||||||
&& stmt->cond->resolvedType != typeCtx.GetBool())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format(
|
|
||||||
"if condition must be boolean, got `{}`", stmt->cond->resolvedType->name),
|
|
||||||
"ensure condition is boolean",
|
|
||||||
makeSourceLocation(stmt->cond)));
|
|
||||||
}
|
|
||||||
auto consequentRes = analyzeStmt(stmt->consequent);
|
|
||||||
if (!consequentRes)
|
|
||||||
{
|
|
||||||
return consequentRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ElseIfStmt *elif : stmt->elifs)
|
|
||||||
{
|
|
||||||
auto condRes = analyzeExpr(elif->cond);
|
|
||||||
if (elif->cond->resolvedType != typeCtx.GetAny()
|
|
||||||
&& elif->cond->resolvedType != typeCtx.GetBool())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format("else if condition must be boolean, got `{}`",
|
|
||||||
elif->cond->resolvedType->name),
|
|
||||||
"ensure condition is boolean",
|
|
||||||
makeSourceLocation(elif->cond)));
|
|
||||||
}
|
|
||||||
auto consequentRes = analyzeStmt(elif->consequent);
|
|
||||||
if (!consequentRes)
|
|
||||||
{
|
|
||||||
return consequentRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stmt->alternate)
|
|
||||||
{
|
|
||||||
auto alternateRes = analyzeStmt(stmt->alternate);
|
|
||||||
if (!alternateRes)
|
|
||||||
{
|
|
||||||
return alternateRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeWhileStmt(WhileStmt *stmt)
|
|
||||||
{
|
|
||||||
auto condRes = analyzeExpr(stmt->cond);
|
|
||||||
if (!condRes)
|
|
||||||
{
|
|
||||||
return condRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stmt->cond->resolvedType != typeCtx.GetAny()
|
|
||||||
&& stmt->cond->resolvedType != typeCtx.GetBool())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format(
|
|
||||||
"while condition must be boolean, got `{}`", stmt->cond->resolvedType->name),
|
|
||||||
"ensure condition is boolean",
|
|
||||||
makeSourceLocation(stmt->cond)));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto bodyRes = analyzeStmt(stmt->body);
|
|
||||||
if (!bodyRes)
|
|
||||||
{
|
|
||||||
return bodyRes;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeFnDefStmt(FnDefStmt *stmt)
|
|
||||||
{
|
|
||||||
auto sym = env.Resolve(stmt->name);
|
|
||||||
if (sym != std::nullopt && sym->depth == env.GetDepth())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::RedeclarationError,
|
|
||||||
std::format("function `{}` has already defined in this scope", stmt->name),
|
|
||||||
"change its name",
|
|
||||||
makeSourceLocation(stmt)));
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt->resolvedReturnType = typeCtx.GetAny(); // 默认Any
|
|
||||||
if (stmt->returnType)
|
|
||||||
{
|
|
||||||
auto result = resolveType(stmt->returnType);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
stmt->resolvedReturnType = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt->localId = env.Define(stmt->name,
|
|
||||||
typeCtx.GetFunction(),
|
|
||||||
stmt->isPublic,
|
|
||||||
true); // 函数定义语句定义的函数为常量
|
|
||||||
|
|
||||||
env.EnterFunction();
|
|
||||||
env.EnterScope();
|
|
||||||
|
|
||||||
PosParam *lastDefaultValueP = nullptr;
|
|
||||||
for (Param *p : stmt->params)
|
|
||||||
{
|
|
||||||
PosParam *posParam = dynamic_cast<PosParam *>(p);
|
|
||||||
if (posParam)
|
|
||||||
{
|
|
||||||
posParam->resolvedType = typeCtx.GetAny();
|
|
||||||
if (posParam->type)
|
|
||||||
{
|
|
||||||
auto result = resolveType(posParam->type);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
}
|
|
||||||
posParam->resolvedType = *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!posParam->defaultValue && lastDefaultValueP)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
std::format("no-default parameter `{}` follows default parameter '{}'",
|
|
||||||
posParam->name,
|
|
||||||
lastDefaultValueP->name),
|
|
||||||
"reorder parameters",
|
|
||||||
posParam->location));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (posParam->defaultValue)
|
|
||||||
{
|
|
||||||
lastDefaultValueP = posParam;
|
|
||||||
|
|
||||||
auto result = analyzeExpr(posParam->defaultValue);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (posParam->resolvedType != typeCtx.GetAny()
|
|
||||||
&& posParam->defaultValue->resolvedType != posParam->resolvedType)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format(
|
|
||||||
"in function '{}', parameter '{}' expects type '{}', but got default value type `{}`",
|
|
||||||
stmt->name,
|
|
||||||
posParam->name,
|
|
||||||
posParam->resolvedType->name,
|
|
||||||
posParam->defaultValue->resolvedType->name),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(posParam->defaultValue)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
posParam->localId =
|
|
||||||
env.Define(posParam->name, posParam->resolvedType, false, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// ... 其他参数解析
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnTypeProtector p(this, stmt->resolvedReturnType);
|
|
||||||
|
|
||||||
auto bodyRes = analyzeStmt(stmt->body);
|
|
||||||
|
|
||||||
env.LeaveScope();
|
|
||||||
env.LeaveFunction();
|
|
||||||
|
|
||||||
if (!bodyRes)
|
|
||||||
{
|
|
||||||
return bodyRes;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeReturnStmt(ReturnStmt *stmt)
|
|
||||||
{
|
|
||||||
if (!currentReturnType)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
"return outside function",
|
|
||||||
"remove `return ...`",
|
|
||||||
makeSourceLocation(stmt)));
|
|
||||||
}
|
|
||||||
auto result = analyzeExpr(stmt->value);
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeInfo *valueType = stmt->value->resolvedType;
|
|
||||||
|
|
||||||
if (!currentReturnType->isAny() && !valueType->isAny() && currentReturnType != valueType)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format("return type mismatch: expects '{}', got `{}`",
|
|
||||||
currentReturnType->name,
|
|
||||||
stmt->value->resolvedType->name),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(stmt->value)));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeIdentiExpr(IdentiExpr *expr)
|
|
||||||
{
|
|
||||||
auto sym = env.Resolve(expr->name);
|
|
||||||
if (sym == std::nullopt)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::UseUndeclaredIdentifier,
|
|
||||||
std::format("`{}` has not been defined", expr->name),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(expr)));
|
|
||||||
}
|
|
||||||
// TODO: 引入 Module 跨文件 import,检查 isPublic
|
|
||||||
expr->localId = sym->localId;
|
|
||||||
|
|
||||||
expr->resolvedType = sym->type;
|
|
||||||
expr->resolvedDepth = sym->depth;
|
|
||||||
expr->isGlobal = (sym->depth == 0);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeInfixExpr(InfixExpr *expr)
|
|
||||||
{
|
|
||||||
auto resL = analyzeExpr(expr->left);
|
|
||||||
if (!resL)
|
|
||||||
return std::unexpected(resL.error());
|
|
||||||
|
|
||||||
auto resR = analyzeExpr(expr->right);
|
|
||||||
if (!resR)
|
|
||||||
return std::unexpected(resR.error());
|
|
||||||
|
|
||||||
TypeInfo *lType = expr->left->resolvedType;
|
|
||||||
TypeInfo *rType = expr->right->resolvedType;
|
|
||||||
|
|
||||||
switch (expr->op)
|
|
||||||
{
|
|
||||||
// 算术族 (+, -, *, /, **)
|
|
||||||
case BinaryOperator::Add:
|
|
||||||
if (lType == typeCtx.GetString() && rType == typeCtx.GetString())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[[fallthrough]];
|
|
||||||
case BinaryOperator::Subtract:
|
|
||||||
case BinaryOperator::Multiply:
|
|
||||||
case BinaryOperator::Divide:
|
|
||||||
case BinaryOperator::Power:
|
|
||||||
if (lType == typeCtx.GetInt() && rType == typeCtx.GetInt())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetInt();
|
|
||||||
}
|
|
||||||
else if ((lType == typeCtx.GetInt() || lType == typeCtx.GetDouble())
|
|
||||||
&& (rType == typeCtx.GetInt() || rType == typeCtx.GetDouble()))
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetDouble();
|
|
||||||
}
|
|
||||||
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetAny();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"invalid operands for arithmetic operation",
|
|
||||||
"ensure both sides are numbers (Int or Double)",
|
|
||||||
makeSourceLocation(expr->right)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 整数特化族 (%, &, |, ^, <<, >>)
|
|
||||||
case BinaryOperator::Modulo:
|
|
||||||
case BinaryOperator::BitAnd:
|
|
||||||
case BinaryOperator::BitOr:
|
|
||||||
case BinaryOperator::BitXor:
|
|
||||||
case BinaryOperator::ShiftLeft:
|
|
||||||
case BinaryOperator::ShiftRight:
|
|
||||||
if (lType == typeCtx.GetInt() && rType == typeCtx.GetInt())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetInt();
|
|
||||||
}
|
|
||||||
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetAny();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"bitwise and modulo operations require Int operands",
|
|
||||||
"cast operands to Int before operation",
|
|
||||||
makeSourceLocation(expr->right)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 比较族 (==, !=, <, >, <=, >=)
|
|
||||||
|
|
||||||
case BinaryOperator::Equal:
|
|
||||||
case BinaryOperator::NotEqual:
|
|
||||||
case BinaryOperator::Less:
|
|
||||||
case BinaryOperator::Greater:
|
|
||||||
case BinaryOperator::LessEqual:
|
|
||||||
case BinaryOperator::GreaterEqual:
|
|
||||||
case BinaryOperator::Is:
|
|
||||||
if (lType != typeCtx.GetAny() && rType != typeCtx.GetAny()
|
|
||||||
&& lType != rType) // lType == rType放行
|
|
||||||
{
|
|
||||||
if (!((lType == typeCtx.GetInt() && rType == typeCtx.GetDouble())
|
|
||||||
|| (lType == typeCtx.GetDouble() && rType == typeCtx.GetInt())))
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"cannot compare different types",
|
|
||||||
"ensure both sides of the comparison are of the same type",
|
|
||||||
makeSourceLocation(expr)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 支持Struct后进行检查,右操作数是 Struct才合理
|
|
||||||
// 如 1.2 is Int --> false
|
|
||||||
// 1 is Int --> true
|
|
||||||
|
|
||||||
expr->resolvedType = typeCtx.GetBool();
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 逻辑族 (&&, ||)
|
|
||||||
case BinaryOperator::LogicalAnd:
|
|
||||||
case BinaryOperator::LogicalOr:
|
|
||||||
if (lType == typeCtx.GetBool() && rType == typeCtx.GetBool())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetBool();
|
|
||||||
}
|
|
||||||
else if (lType == typeCtx.GetAny() || rType == typeCtx.GetAny())
|
|
||||||
{
|
|
||||||
expr->resolvedType = typeCtx.GetBool();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"logical operators require Bool operands",
|
|
||||||
"use boolean expressions",
|
|
||||||
makeSourceLocation(expr)));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 纯赋值与复合赋值族 (=, +=, -=, ...)
|
|
||||||
case BinaryOperator::Assign:
|
|
||||||
case BinaryOperator::AddAssign:
|
|
||||||
case BinaryOperator::SubAssign:
|
|
||||||
case BinaryOperator::MultiplyAssign:
|
|
||||||
case BinaryOperator::DivideAssign:
|
|
||||||
case BinaryOperator::ModuloAssign:
|
|
||||||
case BinaryOperator::BitXorAssign:
|
|
||||||
// 左侧必须是合法的 L-Value
|
|
||||||
if (!isValidLvalue(expr->left))
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::NotAnLvalue,
|
|
||||||
"invalid assignment target",
|
|
||||||
"left side must be a variable, property, or indexable target",
|
|
||||||
makeSourceLocation(expr->left) // 错误精准定位到左侧节点
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 类型匹配拦截 (纯赋值)
|
|
||||||
if (expr->op == BinaryOperator::Assign)
|
|
||||||
{
|
|
||||||
if (lType != typeCtx.GetAny() && rType != typeCtx.GetAny() && lType != rType)
|
|
||||||
{
|
|
||||||
if (!(lType == typeCtx.GetDouble() && rType == typeCtx.GetInt()))
|
|
||||||
{ // 允许 Int 赋给 Double
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"cannot assign value to variable of different type",
|
|
||||||
"ensure the assigned value matches the declared type",
|
|
||||||
makeSourceLocation(expr->right)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expr->resolvedType = lType;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// 成员访问 (.)
|
|
||||||
case BinaryOperator::MemberAccess:
|
|
||||||
if (lType != typeCtx.GetStruct() && lType != typeCtx.GetAny())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"member access requires a Struct object",
|
|
||||||
"check if the left side evaluates to an object",
|
|
||||||
makeSourceLocation(expr->left)));
|
|
||||||
}
|
|
||||||
if (expr->right->type != AstType::IdentiExpr)
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::SyntaxError,
|
|
||||||
std::format("expect field name after member access '.', got {}",
|
|
||||||
expr->right->toString()),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(expr->right)));
|
|
||||||
}
|
|
||||||
expr->resolvedType = typeCtx.GetAny();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
"unknown binary operator in static analysis",
|
|
||||||
"this is likely an internal compiler error",
|
|
||||||
makeSourceLocation(expr)));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeCallExpr(CallExpr *expr)
|
|
||||||
{
|
|
||||||
auto calleeRes = analyzeExpr(expr->callee);
|
|
||||||
if (!calleeRes)
|
|
||||||
{
|
|
||||||
return calleeRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr->callee->resolvedType != typeCtx.GetAny()
|
|
||||||
&& expr->callee->resolvedType != typeCtx.GetFunction())
|
|
||||||
{
|
|
||||||
return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
std::format("object `{}` is not callable", expr->callee->toString()),
|
|
||||||
"none",
|
|
||||||
makeSourceLocation(expr->callee)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto *arg : expr->args.args)
|
|
||||||
{
|
|
||||||
auto argRes = analyzeExpr(arg);
|
|
||||||
if (!argRes)
|
|
||||||
{
|
|
||||||
return argRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expr->resolvedType = typeCtx.GetAny();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeStmt(Stmt *stmt)
|
|
||||||
{
|
|
||||||
if (!stmt)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
switch (stmt->type)
|
|
||||||
{
|
|
||||||
case AstType::VarDecl: return analyzeVarDecl(static_cast<VarDecl *>(stmt));
|
|
||||||
|
|
||||||
case AstType::ExprStmt: {
|
|
||||||
auto *exprStmt = static_cast<ExprStmt *>(stmt);
|
|
||||||
return analyzeExpr(exprStmt->expr); // 表达式语句只需要推导内部表达式即可
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::BlockStmt: {
|
|
||||||
auto *block = static_cast<BlockStmt *>(stmt);
|
|
||||||
env.EnterScope(); // 进入新大括号,作用域深度 +1
|
|
||||||
for (auto *s : block->nodes)
|
|
||||||
{
|
|
||||||
auto res = analyzeStmt(s);
|
|
||||||
if (!res)
|
|
||||||
return std::unexpected(res.error());
|
|
||||||
}
|
|
||||||
env.LeaveScope(); // 离开大括号,自动销毁局部类型记录
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::IfStmt: {
|
|
||||||
return analyzeIfStmt(static_cast<IfStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::WhileStmt: {
|
|
||||||
return analyzeWhileStmt(static_cast<WhileStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::FnDefStmt: {
|
|
||||||
return analyzeFnDefStmt(static_cast<FnDefStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::ReturnStmt: {
|
|
||||||
return analyzeReturnStmt(static_cast<ReturnStmt *>(stmt));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 其他语句分析
|
|
||||||
|
|
||||||
// default:
|
|
||||||
// return std::unexpected(Error(ErrorType::TypeError,
|
|
||||||
// "unsupported statement type in analyzer",
|
|
||||||
// "internal compiler error",
|
|
||||||
// makeSourceLocation(stmt)));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::analyzeExpr(Expr *expr)
|
|
||||||
{
|
|
||||||
if (!expr)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
switch (expr->type)
|
|
||||||
{
|
|
||||||
case AstType::LiteralExpr: {
|
|
||||||
auto *lit = static_cast<LiteralExpr *>(expr);
|
|
||||||
switch (lit->token.type)
|
|
||||||
{
|
|
||||||
case TokenType::LiteralTrue:
|
|
||||||
case TokenType::LiteralFalse: lit->resolvedType = typeCtx.GetBool(); break;
|
|
||||||
|
|
||||||
case TokenType::LiteralNull: lit->resolvedType = typeCtx.GetNull(); break;
|
|
||||||
|
|
||||||
case TokenType::LiteralNumber: {
|
|
||||||
const String &lexeme = manager.GetSub(lit->token.index, lit->token.length);
|
|
||||||
if (lexeme.contains(U'.') || lexeme.contains(U'e'))
|
|
||||||
{
|
|
||||||
lit->resolvedType = typeCtx.GetDouble();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lit->resolvedType = typeCtx.GetInt();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TokenType::LiteralString: {
|
|
||||||
lit->resolvedType = typeCtx.GetString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
lit->resolvedType = typeCtx.GetAny();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::IdentiExpr: {
|
|
||||||
return analyzeIdentiExpr(static_cast<IdentiExpr *>(expr));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::InfixExpr: {
|
|
||||||
return analyzeInfixExpr(static_cast<InfixExpr *>(expr));
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::CallExpr: {
|
|
||||||
return analyzeCallExpr(static_cast<CallExpr *>(expr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: PrefixExpr (前缀), CallExpr (函数调用), MemberExpr (属性访问)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// 对于还没实现的表达式,默认降级为 Any 防止崩溃
|
|
||||||
expr->resolvedType = typeCtx.GetAny();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void, Error> Analyzer::Analyze(Program *program)
|
|
||||||
{
|
|
||||||
for (auto *stmt : program->nodes)
|
|
||||||
{
|
|
||||||
auto res = analyzeStmt(stmt);
|
|
||||||
if (!res)
|
|
||||||
return std::unexpected(res.error()); // 遇到任何错误,立刻中断并向上传递
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Sema/Analyzer.hpp
|
|
||||||
@brief 前端类型检查器定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Sema/Environment.hpp>
|
|
||||||
#include <Sema/Type.hpp>
|
|
||||||
|
|
||||||
#include <Ast/Ast.hpp>
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
class Analyzer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Environment env;
|
|
||||||
SourceManager &manager;
|
|
||||||
|
|
||||||
TypeContext typeCtx;
|
|
||||||
|
|
||||||
TypeInfo *currentReturnType = nullptr; // 正在分析的函数,预期返回类型
|
|
||||||
|
|
||||||
struct ReturnTypeProtector
|
|
||||||
{
|
|
||||||
Analyzer *analyzer;
|
|
||||||
TypeInfo *prevCurrentReturnType;
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
ReturnTypeProtector(Analyzer *_analyzer, TypeInfo *current) :
|
|
||||||
analyzer(_analyzer), prevCurrentReturnType(_analyzer->currentReturnType)
|
|
||||||
{
|
|
||||||
analyzer->currentReturnType = current;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ReturnTypeProtector()
|
|
||||||
{
|
|
||||||
analyzer->currentReturnType = prevCurrentReturnType;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnTypeProtector(const ReturnTypeProtector &) = delete;
|
|
||||||
ReturnTypeProtector &operator=(const ReturnTypeProtector &) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
SourceLocation makeSourceLocation(
|
|
||||||
AstNode *ast, std::source_location loc = std::source_location::current())
|
|
||||||
{
|
|
||||||
return SourceLocation(ast->location.sp,
|
|
||||||
ast->location.fileName,
|
|
||||||
"[internal analyzer]",
|
|
||||||
loc.function_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValidLvalue(Expr *expr)
|
|
||||||
{
|
|
||||||
if (expr->type == AstType::IdentiExpr)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (expr->type == AstType::InfixExpr)
|
|
||||||
{
|
|
||||||
InfixExpr *infix = static_cast<InfixExpr *>(expr);
|
|
||||||
if (infix->op == BinaryOperator::MemberAccess)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (expr->type == AstType::IndexExpr)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<TypeInfo *, Error> resolveType(TypeExpr *);
|
|
||||||
|
|
||||||
Result<void, Error> analyzeVarDecl(VarDecl *);
|
|
||||||
Result<void, Error> analyzeIfStmt(IfStmt *);
|
|
||||||
Result<void, Error> analyzeWhileStmt(WhileStmt *);
|
|
||||||
Result<void, Error> analyzeFnDefStmt(FnDefStmt *);
|
|
||||||
Result<void, Error> analyzeReturnStmt(ReturnStmt *);
|
|
||||||
|
|
||||||
Result<void, Error> analyzeIdentiExpr(IdentiExpr *);
|
|
||||||
Result<void, Error> analyzeInfixExpr(InfixExpr *);
|
|
||||||
Result<void, Error> analyzeCallExpr(CallExpr *);
|
|
||||||
|
|
||||||
Result<void, Error> analyzeStmt(Stmt *);
|
|
||||||
Result<void, Error> analyzeExpr(Expr *);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Result<void, Error> Analyze(Program *);
|
|
||||||
|
|
||||||
Analyzer(SourceManager &_manager) : manager(_manager), typeCtx() {}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#include <Sema/Analyzer.hpp>
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
#include <Lexer/Lexer.hpp>
|
|
||||||
#include <Parser/Parser.hpp>
|
|
||||||
#include <SourceManager/SourceManager.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
using namespace Fig;
|
|
||||||
|
|
||||||
const String &fileName = "test.fig";
|
|
||||||
const String &filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
|
||||||
|
|
||||||
SourceManager manager(filePath);
|
|
||||||
manager.Read();
|
|
||||||
|
|
||||||
if (!manager.read)
|
|
||||||
{
|
|
||||||
std::cerr << "Read file failed \n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lexer lexer(manager.GetSource(), fileName);
|
|
||||||
Parser parser(lexer, manager, fileName);
|
|
||||||
|
|
||||||
auto result = parser.Parse();
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
ReportError(result.error(), manager);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Program *program = *result;
|
|
||||||
|
|
||||||
Analyzer analyzer(manager);
|
|
||||||
|
|
||||||
const auto &analyzeResult = analyzer.Analyze(program);
|
|
||||||
if (!analyzeResult)
|
|
||||||
{
|
|
||||||
ReportError(analyzeResult.error(), manager);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Analyze successfully, PROGRAM OK\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Sema/Environment.hpp
|
|
||||||
@brief 符号和作用域环境定义
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <Error/Error.hpp>
|
|
||||||
#include <Sema/Type.hpp>
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
// 记录在 Analyzer 中的符号元数据
|
|
||||||
struct Symbol
|
|
||||||
{
|
|
||||||
String name;
|
|
||||||
TypeInfo *type;
|
|
||||||
bool isPublic;
|
|
||||||
int depth; // 词法作用域深度
|
|
||||||
bool isConstant; // 是否是 const 声明的不可变常量 (用于报错: 尝试修改常量)
|
|
||||||
|
|
||||||
int localId = -1; // Analyzer 虚拟槽位分配
|
|
||||||
};
|
|
||||||
|
|
||||||
// 作用域回档水位线
|
|
||||||
struct ScopeWatermark
|
|
||||||
{
|
|
||||||
std::size_t symbolCount;
|
|
||||||
int savedLocalId;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 语义分析函数上下文 (隔离局部变量 ID 空间)
|
|
||||||
struct SemaFuncState
|
|
||||||
{
|
|
||||||
SemaFuncState *enclosing = nullptr;
|
|
||||||
int currentDepth = 0;
|
|
||||||
int nextLocalId = 0;
|
|
||||||
DynArray<ScopeWatermark> scopeStack;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Environment
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
DynArray<Symbol> symbols;
|
|
||||||
SemaFuncState *current = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Environment()
|
|
||||||
{
|
|
||||||
current = new SemaFuncState();
|
|
||||||
}
|
|
||||||
|
|
||||||
~Environment()
|
|
||||||
{
|
|
||||||
while (current)
|
|
||||||
{
|
|
||||||
SemaFuncState *prev = current->enclosing;
|
|
||||||
delete current;
|
|
||||||
current = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 函数边界控
|
|
||||||
|
|
||||||
void EnterFunction()
|
|
||||||
{
|
|
||||||
SemaFuncState *newState = new SemaFuncState();
|
|
||||||
newState->enclosing = current;
|
|
||||||
current = newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaveFunction()
|
|
||||||
{
|
|
||||||
assert(current && "Environment: Unmatched LeaveFunction");
|
|
||||||
SemaFuncState *oldState = current;
|
|
||||||
current = oldState->enclosing;
|
|
||||||
delete oldState;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 词法作用域控制
|
|
||||||
|
|
||||||
void EnterScope()
|
|
||||||
{
|
|
||||||
current->currentDepth++;
|
|
||||||
current->scopeStack.push_back({symbols.size(), current->nextLocalId});
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeaveScope()
|
|
||||||
{
|
|
||||||
assert(current->currentDepth > 0 && "Environment: Unmatched LeaveScope");
|
|
||||||
current->currentDepth--;
|
|
||||||
|
|
||||||
assert(!current->scopeStack.empty());
|
|
||||||
ScopeWatermark archive = current->scopeStack.back();
|
|
||||||
current->scopeStack.pop_back();
|
|
||||||
|
|
||||||
// 物理截断符号表,回滚槽位发号器以复用物理寄存器
|
|
||||||
while (symbols.size() > archive.symbolCount)
|
|
||||||
{
|
|
||||||
symbols.pop_back();
|
|
||||||
}
|
|
||||||
current->nextLocalId = archive.savedLocalId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 符号操作
|
|
||||||
|
|
||||||
// 注册符号, 返回分配的 localId。调用前内部执行同级作用域重定义断言
|
|
||||||
int Define(const String &name, TypeInfo *type, bool isPublic, bool isConst)
|
|
||||||
{
|
|
||||||
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
|
|
||||||
{
|
|
||||||
if (it->depth < current->currentDepth)
|
|
||||||
break;
|
|
||||||
if (it->name == name)
|
|
||||||
{
|
|
||||||
assert(false && "Environment.Define: redefinition");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int allocatedId = current->nextLocalId++;
|
|
||||||
symbols.push_back({name, type, isPublic, current->currentDepth, isConst, allocatedId});
|
|
||||||
return allocatedId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析符号。找不到返回 nullopt
|
|
||||||
std::optional<Symbol> Resolve(const String &name)
|
|
||||||
{
|
|
||||||
for (auto it = symbols.rbegin(); it != symbols.rend(); ++it)
|
|
||||||
{
|
|
||||||
if (it->name == name)
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetDepth() const
|
|
||||||
{
|
|
||||||
return current->currentDepth;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/Sema/Type.hpp
|
|
||||||
@brief 前端类型检查的类型定义和类型驻留池
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-23
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Deps/Deps.hpp>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
enum class TypeTag : std::uint8_t
|
|
||||||
{
|
|
||||||
Any, // 动态类型底线
|
|
||||||
Null, // 空值
|
|
||||||
Int,
|
|
||||||
Double,
|
|
||||||
Bool,
|
|
||||||
String,
|
|
||||||
Function,
|
|
||||||
Struct,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TypeInfo
|
|
||||||
{
|
|
||||||
TypeTag tag;
|
|
||||||
String name; // 完整路径序列化, 如 Int, std.file.File
|
|
||||||
|
|
||||||
bool isAny() const
|
|
||||||
{
|
|
||||||
return tag == TypeTag::Any;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 全局唯一类型驻留池
|
|
||||||
class TypeContext
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
DynArray<TypeInfo *> allTypes;
|
|
||||||
|
|
||||||
// 缓存
|
|
||||||
TypeInfo *typeAny;
|
|
||||||
TypeInfo *typeNull;
|
|
||||||
TypeInfo *typeInt;
|
|
||||||
TypeInfo *typeDouble;
|
|
||||||
TypeInfo *typeBool;
|
|
||||||
TypeInfo *typeString;
|
|
||||||
TypeInfo *typeFunction;
|
|
||||||
TypeInfo *typeStruct;
|
|
||||||
|
|
||||||
public:
|
|
||||||
TypeInfo *GetAny()
|
|
||||||
{
|
|
||||||
return typeAny;
|
|
||||||
}
|
|
||||||
TypeInfo *GetNull()
|
|
||||||
{
|
|
||||||
return typeNull;
|
|
||||||
}
|
|
||||||
TypeInfo *GetInt()
|
|
||||||
{
|
|
||||||
return typeInt;
|
|
||||||
}
|
|
||||||
TypeInfo *GetDouble()
|
|
||||||
{
|
|
||||||
return typeDouble;
|
|
||||||
}
|
|
||||||
TypeInfo *GetBool()
|
|
||||||
{
|
|
||||||
return typeBool;
|
|
||||||
}
|
|
||||||
TypeInfo *GetString()
|
|
||||||
{
|
|
||||||
return typeString;
|
|
||||||
}
|
|
||||||
TypeInfo *GetFunction()
|
|
||||||
{
|
|
||||||
return typeFunction;
|
|
||||||
}
|
|
||||||
TypeInfo *GetStruct()
|
|
||||||
{
|
|
||||||
return typeStruct;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeInfo *ResolveTypePath(const String &fullName)
|
|
||||||
{
|
|
||||||
for (auto *t : allTypes)
|
|
||||||
{
|
|
||||||
if (t->name == fullName)
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
return nullptr; // 没找到该类型
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeInfo *ResolveTypePath(const DynArray<String> &path)
|
|
||||||
{
|
|
||||||
// TODO: 支持Module 系统, 查 Module 的导出表
|
|
||||||
String fullName = path.empty() ? "" : path[0];
|
|
||||||
for (size_t i = 1; i < path.size(); ++i)
|
|
||||||
{
|
|
||||||
fullName += "." + path[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResolveTypePath(fullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
~TypeContext()
|
|
||||||
{
|
|
||||||
for (TypeInfo *t : allTypes)
|
|
||||||
delete t;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeContext()
|
|
||||||
{
|
|
||||||
typeAny = createBuiltin(TypeTag::Any, "Any");
|
|
||||||
typeNull = createBuiltin(TypeTag::Null, "Null");
|
|
||||||
typeInt = createBuiltin(TypeTag::Int, "Int");
|
|
||||||
typeDouble = createBuiltin(TypeTag::Double, "Double");
|
|
||||||
typeBool = createBuiltin(TypeTag::Bool, "Bool");
|
|
||||||
typeString = createBuiltin(TypeTag::String, "String");
|
|
||||||
typeFunction = createBuiltin(TypeTag::Function, "Function");
|
|
||||||
typeStruct = createBuiltin(TypeTag::Struct, "Struct");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
TypeInfo *createBuiltin(TypeTag tag, String name)
|
|
||||||
{
|
|
||||||
TypeInfo *t = new TypeInfo{tag, std::move(name)};
|
|
||||||
allTypes.push_back(t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}; // namespace Fig
|
|
||||||
@@ -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
|
|
||||||
File diff suppressed because it is too large
Load Diff
-25526
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
|
|
||||||
-230
@@ -1,230 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/VM/VM.cpp
|
|
||||||
@brief 虚拟机核心执行引擎实现
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <VM/VM.hpp>
|
|
||||||
|
|
||||||
// Computed GOTO!!!
|
|
||||||
#define BINARY_ARITHMETIC_OP(opName, op) \
|
|
||||||
do_##opName: \
|
|
||||||
{ \
|
|
||||||
std::uint8_t a = decodeA(inst); \
|
|
||||||
std::uint8_t b = decodeB(inst); \
|
|
||||||
std::uint8_t c = decodeC(inst); \
|
|
||||||
Value lhs = currentFrame->registerBase[b]; \
|
|
||||||
Value rhs = currentFrame->registerBase[c]; \
|
|
||||||
if (lhs.IsInt() && rhs.IsInt()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = Value::FromInt(lhs.AsInt() op rhs.AsInt()); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsDouble() && rhs.IsDouble()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsDouble() op rhs.AsDouble()); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsInt() && rhs.IsDouble()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsInt() op rhs.AsDouble()); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsDouble() && rhs.IsInt()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = Value::FromDouble(lhs.AsDouble() op rhs.AsInt()); \
|
|
||||||
} \
|
|
||||||
else \
|
|
||||||
{ \
|
|
||||||
assert(false && "VM Runtime Error: Unsupported types for arithmetic operation"); \
|
|
||||||
} \
|
|
||||||
DISPATCH(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BINARY_COMPARE_OP(opName, op) \
|
|
||||||
do_##opName: \
|
|
||||||
{ \
|
|
||||||
std::uint8_t a = decodeA(inst); \
|
|
||||||
std::uint8_t b = decodeB(inst); \
|
|
||||||
std::uint8_t c = decodeC(inst); \
|
|
||||||
Value lhs = currentFrame->registerBase[b]; \
|
|
||||||
Value rhs = currentFrame->registerBase[c]; \
|
|
||||||
if (lhs.IsInt() && rhs.IsInt()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = (lhs.AsInt() op rhs.AsInt()) ? \
|
|
||||||
Value::GetTrueInstance() : \
|
|
||||||
Value::GetFalseInstance(); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsDouble() && rhs.IsDouble()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = (lhs.AsDouble() op rhs.AsDouble()) ? \
|
|
||||||
Value::GetTrueInstance() : \
|
|
||||||
Value::GetFalseInstance(); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsInt() && rhs.IsDouble()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = (lhs.AsInt() op rhs.AsDouble()) ? \
|
|
||||||
Value::GetTrueInstance() : \
|
|
||||||
Value::GetFalseInstance(); \
|
|
||||||
} \
|
|
||||||
else if (lhs.IsDouble() && rhs.IsInt()) [[likely]] \
|
|
||||||
{ \
|
|
||||||
currentFrame->registerBase[a] = (lhs.AsDouble() op rhs.AsInt()) ? \
|
|
||||||
Value::GetTrueInstance() : \
|
|
||||||
Value::GetFalseInstance(); \
|
|
||||||
} \
|
|
||||||
else \
|
|
||||||
{ \
|
|
||||||
assert(false && "VM Runtime Error: Unsupported types for comparison"); \
|
|
||||||
} \
|
|
||||||
DISPATCH(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
Result<Value, Error> VM::Execute(CompiledModule *compiledModule)
|
|
||||||
{
|
|
||||||
Proto *entry = compiledModule->protos[0];
|
|
||||||
pushFrame(entry, registers);
|
|
||||||
|
|
||||||
// 🔥 必须与 Bytecode.hpp 中的 OpCode 枚举严格一一对应!
|
|
||||||
static const void *dispatchTable[] = {&&do_Exit,
|
|
||||||
&&do_LoadK,
|
|
||||||
&&do_LoadTrue,
|
|
||||||
&&do_LoadFalse,
|
|
||||||
&&do_LoadNull,
|
|
||||||
&&do_FastCall,
|
|
||||||
&&do_Call,
|
|
||||||
&&do_Return,
|
|
||||||
&&do_LoadFn,
|
|
||||||
&&do_Jmp,
|
|
||||||
&&do_JmpIfFalse,
|
|
||||||
&&do_Mov,
|
|
||||||
&&do_Add,
|
|
||||||
&&do_Sub,
|
|
||||||
&&do_Mul,
|
|
||||||
&&do_Div,
|
|
||||||
&&do_Mod,
|
|
||||||
&&do_BitXor,
|
|
||||||
&&do_Equal,
|
|
||||||
&&do_NotEqual,
|
|
||||||
&&do_Greater,
|
|
||||||
&&do_Less,
|
|
||||||
&&do_GreaterEqual,
|
|
||||||
&&do_LessEqual,
|
|
||||||
&&do_Count};
|
|
||||||
|
|
||||||
Instruction inst;
|
|
||||||
|
|
||||||
// 🔥 核心分发引擎:取指 -> 直接查表并 Jump
|
|
||||||
#define DISPATCH() \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
inst = *(currentFrame->ip++); \
|
|
||||||
goto *dispatchTable[inst & 0xFF]; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// 引擎点火!
|
|
||||||
DISPATCH();
|
|
||||||
|
|
||||||
do_Exit: {
|
|
||||||
[[unlikely]] return Value::GetNullInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_LoadK: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
std::uint16_t bx = decodeBx(inst);
|
|
||||||
currentFrame->registerBase[a] = currentFrame->getConstant(bx);
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_LoadTrue: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
currentFrame->registerBase[a] = Value::GetTrueInstance();
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_LoadFalse: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
currentFrame->registerBase[a] = Value::GetFalseInstance();
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_LoadNull: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
currentFrame->registerBase[a] = Value::GetNullInstance();
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_FastCall: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
Proto *proto = compiledModule->protos[a];
|
|
||||||
std::uint8_t baseReg = decodeB(inst);
|
|
||||||
pushFrame(proto, currentFrame->registerBase + baseReg);
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_Call: {
|
|
||||||
// TODO: FunctionObject 动态解包
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_Return: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
*currentFrame->registerBase = currentFrame->registerBase[a];
|
|
||||||
popFrame();
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_LoadFn: {
|
|
||||||
// std::uint8_t a = decodeA(inst);
|
|
||||||
// std::uint16_t bx = decodeBx(inst);
|
|
||||||
// TODO: R[a] = new FunctionObject(compiledModule->protos[bx])
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_Jmp: {
|
|
||||||
std::int16_t sbx = decodeSBx(inst);
|
|
||||||
currentFrame->ip += sbx;
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_JmpIfFalse: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
Value &v = currentFrame->registerBase[a];
|
|
||||||
if (!v.AsBool())
|
|
||||||
{
|
|
||||||
std::int16_t sbx = decodeSBx(inst);
|
|
||||||
currentFrame->ip += sbx;
|
|
||||||
}
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
do_Mov: {
|
|
||||||
std::uint8_t a = decodeA(inst);
|
|
||||||
std::uint16_t bx = decodeBx(inst);
|
|
||||||
currentFrame->registerBase[a] = currentFrame->registerBase[bx];
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
BINARY_ARITHMETIC_OP(Add, +);
|
|
||||||
BINARY_ARITHMETIC_OP(Sub, -);
|
|
||||||
BINARY_ARITHMETIC_OP(Mul, *);
|
|
||||||
BINARY_ARITHMETIC_OP(Div, /);
|
|
||||||
|
|
||||||
do_Mod:
|
|
||||||
do_BitXor:
|
|
||||||
assert(false && "VM: Mod and BitXor not fully implemented yet!");
|
|
||||||
DISPATCH();
|
|
||||||
|
|
||||||
BINARY_COMPARE_OP(Equal, ==);
|
|
||||||
BINARY_COMPARE_OP(NotEqual, !=);
|
|
||||||
BINARY_COMPARE_OP(Greater, >);
|
|
||||||
BINARY_COMPARE_OP(Less, <);
|
|
||||||
BINARY_COMPARE_OP(GreaterEqual, >=);
|
|
||||||
BINARY_COMPARE_OP(LessEqual, <=);
|
|
||||||
|
|
||||||
do_Count: {
|
|
||||||
assert(false && "Hit Count sentinel!");
|
|
||||||
return Value::GetNullInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}; // namespace Fig
|
|
||||||
-113
@@ -1,113 +0,0 @@
|
|||||||
/*!
|
|
||||||
@file src/VM/VM.hpp
|
|
||||||
@brief 虚拟机核心执行引擎
|
|
||||||
@author PuqiAR (im@puqiar.top)
|
|
||||||
@date 2026-02-19
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Compiler/Compiler.hpp>
|
|
||||||
#include <Object/Object.hpp>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <iostream> // debug
|
|
||||||
#include <print>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
struct CallFrame
|
|
||||||
{
|
|
||||||
Proto *proto; // 当前执行的原型
|
|
||||||
Instruction *ip; // 当前指令指针
|
|
||||||
Value *registerBase; // 寄存器起点
|
|
||||||
|
|
||||||
inline Value getConstant(std::uint16_t idx)
|
|
||||||
{
|
|
||||||
return proto->constants[idx];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class VM
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
static constexpr unsigned int MAX_REGISTERS = 1024;
|
|
||||||
|
|
||||||
// 一次性分配
|
|
||||||
Value registers[MAX_REGISTERS];
|
|
||||||
|
|
||||||
DynArray<CallFrame> frames;
|
|
||||||
CallFrame *currentFrame;
|
|
||||||
public:
|
|
||||||
VM()
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
|
|
||||||
{
|
|
||||||
registers[i] = Value::GetNullInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void pushFrame(Proto *proto, Value *base)
|
|
||||||
{
|
|
||||||
frames.push_back({
|
|
||||||
proto,
|
|
||||||
proto->code.data(),
|
|
||||||
base
|
|
||||||
});
|
|
||||||
currentFrame = &frames.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void popFrame()
|
|
||||||
{
|
|
||||||
frames.pop_back();
|
|
||||||
if (!frames.empty())
|
|
||||||
{
|
|
||||||
currentFrame = &frames.back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline OpCode decodeOpCode(Instruction inst)
|
|
||||||
{
|
|
||||||
return static_cast<OpCode>(inst & 0xFF);
|
|
||||||
}
|
|
||||||
inline std::uint8_t decodeA(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst >> 8) & 0xFF;
|
|
||||||
}
|
|
||||||
inline std::uint16_t decodeBx(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst >> 16) & 0xFFFF;
|
|
||||||
}
|
|
||||||
inline std::uint8_t decodeB(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst >> 16) & 0xFF;
|
|
||||||
}
|
|
||||||
inline std::uint8_t decodeC(Instruction inst)
|
|
||||||
{
|
|
||||||
return (inst >> 24) & 0xFF;
|
|
||||||
}
|
|
||||||
inline std::int16_t decodeSBx(Instruction inst)
|
|
||||||
{
|
|
||||||
return static_cast<std::int16_t>(inst >> 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// 执行入口:接收 Proto
|
|
||||||
Result<Value, Error> Execute(CompiledModule *);
|
|
||||||
|
|
||||||
inline void PrintRegisters()
|
|
||||||
{
|
|
||||||
std::cout << "=== Registers ===" << '\n';
|
|
||||||
for (unsigned int i = 0; i < MAX_REGISTERS; ++i)
|
|
||||||
{
|
|
||||||
Value &v = registers[i];
|
|
||||||
if (!v.IsNull())
|
|
||||||
{
|
|
||||||
std::println("[{}] {}", i, v.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
/*
|
|
||||||
哈哈!
|
|
||||||
VM的测试?
|
|
||||||
就是main.cpp啦!全流程执行!
|
|
||||||
*/
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
src/core/build_info.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::core::colors;
|
||||||
|
|
||||||
|
pub struct BuildInfo
|
||||||
|
{
|
||||||
|
pub version: &'static str,
|
||||||
|
pub git_hash: &'static str,
|
||||||
|
pub build_time: &'static str,
|
||||||
|
pub platform: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get() -> BuildInfo
|
||||||
|
{
|
||||||
|
BuildInfo
|
||||||
|
{
|
||||||
|
version: env!("CARGO_PKG_VERSION"),
|
||||||
|
git_hash: env!("GIT_HASH"),
|
||||||
|
build_time: env!("BUILD_TIME"),
|
||||||
|
platform: format!(
|
||||||
|
"{} [{} | {}]",
|
||||||
|
std::env::consts::OS,
|
||||||
|
env!("FIG_COMPILER_ID"),
|
||||||
|
std::env::consts::ARCH
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_header(sub_system: &str)
|
||||||
|
{
|
||||||
|
let info = get();
|
||||||
|
print!(
|
||||||
|
"{}{} v{} {}(Build {} {} {}){}\n\n",
|
||||||
|
colors::BOLD,
|
||||||
|
sub_system,
|
||||||
|
info.version,
|
||||||
|
colors::DIM,
|
||||||
|
info.git_hash,
|
||||||
|
info.build_time,
|
||||||
|
info.platform,
|
||||||
|
colors::RESET,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
src/core/colors.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub const RESET: &str = "\x1b[0m";
|
||||||
|
pub const BOLD: &str = "\x1b[1m";
|
||||||
|
pub const DIM: &str = "\x1b[2m";
|
||||||
|
|
||||||
|
pub const RED: &str = "\x1b[31m";
|
||||||
|
pub const GREEN: &str = "\x1b[32m";
|
||||||
|
pub const YELLOW: &str = "\x1b[33m";
|
||||||
|
pub const CYAN: &str = "\x1b[36m";
|
||||||
|
|
||||||
|
pub const BRIGHT_RED: &str = "\x1b[91m";
|
||||||
|
pub const BRIGHT_CYAN: &str = "\x1b[96m";
|
||||||
|
|
||||||
|
pub const GRAY: &str = "\x1b[90m";
|
||||||
|
pub const LIGHT_GRAY: &str = "\x1b[37m";
|
||||||
|
|
||||||
|
pub const ORANGE: &str = "\x1b[38;2;217;119;6m";
|
||||||
|
pub const PURPLE: &str = "\x1b[38;2;168;85;247m"; // 亮紫色 error 主色
|
||||||
|
pub const CRITICAL_RED: &str = "\x1b[38;2;239;68;68m"; // critical
|
||||||
|
pub const ACCENT_BLUE: &str = "\x1b[38;2;59;130;246m";
|
||||||
|
|
||||||
|
// 源码行号
|
||||||
|
pub const LINE_NO: &str = "\x1b[38;2;59;130;246m"; // 蓝,和 error 区分
|
||||||
|
// 源码文本
|
||||||
|
pub const LIGHT_BLUE: &str = "\x1b[38;2;96;165;250m";
|
||||||
|
pub const SOURCE_TEXT: &str = "\x1b[38;2;180;180;180m"; // 浅灰,比 DIM 亮
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
src/core/mod.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub mod source;
|
||||||
|
pub mod colors;
|
||||||
|
pub mod build_info;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum Lang
|
||||||
|
{
|
||||||
|
en_US,
|
||||||
|
zh_CN,
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
src/core/source.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SourcePosition
|
||||||
|
{
|
||||||
|
pub file_id: usize,
|
||||||
|
pub line: usize,
|
||||||
|
pub column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct SourceRange
|
||||||
|
{
|
||||||
|
pub file_id: usize,
|
||||||
|
pub start_line: usize,
|
||||||
|
pub start_column: usize,
|
||||||
|
pub end_line: usize,
|
||||||
|
pub end_column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceFile / SourceManager
|
||||||
|
|
||||||
|
pub struct SourceFile
|
||||||
|
{
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub source: String,
|
||||||
|
line_offsets: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceFile
|
||||||
|
{
|
||||||
|
pub fn from_string(name: &str, source: String) -> Self
|
||||||
|
{
|
||||||
|
let line_offsets = Self::build_line_offsets(&source);
|
||||||
|
Self { path: PathBuf::from(name), source, line_offsets }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_path(path: PathBuf) -> Result<Self, std::io::Error>
|
||||||
|
{
|
||||||
|
let source = std::fs::read_to_string(&path)?;
|
||||||
|
let line_offsets = Self::build_line_offsets(&source);
|
||||||
|
Ok(Self { path, source, line_offsets })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(path: PathBuf) -> Result<Self, std::io::Error>
|
||||||
|
{
|
||||||
|
let source = std::fs::read_to_string(&path)?;
|
||||||
|
let line_offsets = Self::build_line_offsets(&source);
|
||||||
|
Ok(Self { path, source, line_offsets })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_line_offsets(source: &str) -> Vec<usize>
|
||||||
|
{
|
||||||
|
std::iter::once(0)
|
||||||
|
.chain(
|
||||||
|
source
|
||||||
|
.bytes()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, b)| *b == b'\n')
|
||||||
|
.map(|(i, _)| i + 1),
|
||||||
|
)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line_offsets(&self) -> &[usize] { &self.line_offsets }
|
||||||
|
|
||||||
|
pub fn offset_to_position(&self, offset: usize) -> (usize, usize)
|
||||||
|
{
|
||||||
|
let line = match self.line_offsets.binary_search(&offset) {
|
||||||
|
Ok(line) => line,
|
||||||
|
Err(line) => line.saturating_sub(1),
|
||||||
|
};
|
||||||
|
let col = offset - self.line_offsets[line] + 1;
|
||||||
|
(line + 1, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_line(&self, offset: usize) -> &str
|
||||||
|
{
|
||||||
|
let (line, _) = self.offset_to_position(offset);
|
||||||
|
let start = self.line_offsets[line - 1];
|
||||||
|
let end = self.source[start..]
|
||||||
|
.find('\n')
|
||||||
|
.map(|i| start + i)
|
||||||
|
.unwrap_or(self.source.len());
|
||||||
|
&self.source[start..end]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SourceManager
|
||||||
|
{
|
||||||
|
files: Vec<SourceFile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceManager
|
||||||
|
{
|
||||||
|
pub fn new() -> Self
|
||||||
|
{
|
||||||
|
Self { files: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file(&mut self, file: SourceFile) -> usize
|
||||||
|
{
|
||||||
|
let id = self.files.len();
|
||||||
|
self.files.push(file);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, id: usize) -> Option<&SourceFile>
|
||||||
|
{
|
||||||
|
self.files.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self, id: usize) -> Option<&str>
|
||||||
|
{
|
||||||
|
self.files.get(id).map(|f| f.source.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
src/error/definitions/invalid_escape_sequence.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidEscapeSequenceError {
|
||||||
|
file_id: usize,
|
||||||
|
start_line: usize,
|
||||||
|
start_column: usize,
|
||||||
|
escape_sequence: String,
|
||||||
|
|
||||||
|
thrower: Option<Thrower>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InvalidEscapeSequenceError {
|
||||||
|
pub fn new(file_id: usize, start_line: usize, start_column: usize, escape_sequence: String) -> Self {
|
||||||
|
InvalidEscapeSequenceError {
|
||||||
|
file_id,
|
||||||
|
start_line,
|
||||||
|
start_column,
|
||||||
|
escape_sequence,
|
||||||
|
thrower: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_thrower(mut self, thrower: Thrower) -> Self {
|
||||||
|
self.thrower = Some(thrower);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for InvalidEscapeSequenceError {
|
||||||
|
fn error_type(&self) -> ErrorType {
|
||||||
|
ErrorType::InvalidEscapeSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self, lang: crate::core::Lang) -> String {
|
||||||
|
match lang {
|
||||||
|
crate::core::Lang::en_US => format!(
|
||||||
|
"Invalid escape sequence: {} at line {}, column {}",
|
||||||
|
self.escape_sequence, self.start_line, self.start_column
|
||||||
|
),
|
||||||
|
crate::core::Lang::zh_CN => format!(
|
||||||
|
"无效的转义序列: {} 在第 {} 行,第 {} 列",
|
||||||
|
self.escape_sequence, self.start_line, self.start_column
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> crate::core::source::SourceRange {
|
||||||
|
crate::core::source::SourceRange {
|
||||||
|
file_id: self.file_id,
|
||||||
|
start_line: self.start_line,
|
||||||
|
start_column: self.start_column,
|
||||||
|
end_line: self.start_line,
|
||||||
|
end_column: self.start_column + self.escape_sequence.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
src/error/definitions/invalid_number_literal.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::core;
|
||||||
|
use crate::core::source::SourceRange;
|
||||||
|
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidNumberLiteralError {
|
||||||
|
file_id: usize,
|
||||||
|
start_line: usize,
|
||||||
|
start_column: usize,
|
||||||
|
end_column: usize,
|
||||||
|
thrower: Option<Thrower>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InvalidNumberLiteralError {
|
||||||
|
pub fn new(file_id: usize, start_line: usize, start_column: usize, end_column: usize) -> Self {
|
||||||
|
InvalidNumberLiteralError {
|
||||||
|
file_id,
|
||||||
|
start_line,
|
||||||
|
start_column,
|
||||||
|
end_column,
|
||||||
|
thrower: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_thrower(mut self, thrower: Thrower) -> Self {
|
||||||
|
self.thrower = Some(thrower);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for InvalidNumberLiteralError {
|
||||||
|
fn error_type(&self) -> ErrorType {
|
||||||
|
ErrorType::InvalidNumberLiteral
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self, lang: core::Lang) -> String {
|
||||||
|
match lang {
|
||||||
|
core::Lang::en_US => {
|
||||||
|
format!(
|
||||||
|
"Invalid number literal at line {}, column {}",
|
||||||
|
self.start_line, self.start_column,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
core::Lang::zh_CN => {
|
||||||
|
format!(
|
||||||
|
"无效的数字字面量,位于第 {} 行,第 {} 列",
|
||||||
|
self.start_line, self.start_column,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> SourceRange {
|
||||||
|
SourceRange {
|
||||||
|
file_id: self.file_id,
|
||||||
|
start_line: self.start_line,
|
||||||
|
start_column: self.start_column,
|
||||||
|
end_line: self.start_line,
|
||||||
|
end_column: self.end_column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thrower(&self) -> Option<Thrower> {
|
||||||
|
self.thrower
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
src/error/definitions/mod.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub mod unterminated_string_literal;
|
||||||
|
pub mod unexpected_character;
|
||||||
|
pub mod invalid_number_literal;
|
||||||
|
pub mod invalid_escape_sequence;
|
||||||
|
pub mod unterminated_comment;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
src/error/definitions/unexpected_character.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::core;
|
||||||
|
use crate::core::source::SourceRange;
|
||||||
|
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnexpectedCharacterError
|
||||||
|
{
|
||||||
|
file_id: usize,
|
||||||
|
character: char,
|
||||||
|
line: usize,
|
||||||
|
column: usize,
|
||||||
|
thrower: Option<Thrower>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnexpectedCharacterError
|
||||||
|
{
|
||||||
|
pub fn new(file_id: usize, character: char, line: usize, column: usize) -> Self
|
||||||
|
{
|
||||||
|
UnexpectedCharacterError
|
||||||
|
{
|
||||||
|
file_id,
|
||||||
|
character,
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
thrower: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_thrower(mut self, t: Thrower) -> Self
|
||||||
|
{
|
||||||
|
self.thrower = Some(t);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for UnexpectedCharacterError
|
||||||
|
{
|
||||||
|
fn error_type(&self) -> ErrorType
|
||||||
|
{
|
||||||
|
ErrorType::UnexpectedCharacter
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self, lang: crate::core::Lang) -> String
|
||||||
|
{
|
||||||
|
let ch = match (self.character, lang) {
|
||||||
|
(' ', crate::core::Lang::zh_CN) => "<空格>",
|
||||||
|
(' ', _) => "<space>",
|
||||||
|
('\n', crate::core::Lang::zh_CN) => "<换行>",
|
||||||
|
('\n', _) => "<newline>",
|
||||||
|
('\t', crate::core::Lang::zh_CN) => "<制表符>",
|
||||||
|
('\t', _) => "<tab>",
|
||||||
|
('\r', crate::core::Lang::zh_CN) => "<回车>",
|
||||||
|
('\r', _) => "<carriage return>",
|
||||||
|
(other, crate::core::Lang::zh_CN) =>
|
||||||
|
return format!("意外的字符 '{}',位于第 {} 行,第 {} 列", other, self.line, self.column),
|
||||||
|
(other, _) =>
|
||||||
|
return format!("Unexpected character '{}' at line {}, column {}", other, self.line, self.column),
|
||||||
|
};
|
||||||
|
match lang {
|
||||||
|
crate::core::Lang::zh_CN =>
|
||||||
|
format!("意外的字符 {},位于第 {} 行,第 {} 列", ch, self.line, self.column),
|
||||||
|
_ =>
|
||||||
|
format!("Unexpected character {} at line {}, column {}", ch, self.line, self.column),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> SourceRange
|
||||||
|
{
|
||||||
|
SourceRange
|
||||||
|
{
|
||||||
|
file_id: self.file_id,
|
||||||
|
start_line: self.line,
|
||||||
|
start_column: self.column,
|
||||||
|
end_line: self.line,
|
||||||
|
end_column: self.column + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thrower(&self) -> Option<Thrower>
|
||||||
|
{
|
||||||
|
self.thrower
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
src/error/definitions/unterminated_comment.rs
|
||||||
|
Part of The Fig Project, under the MIT License.
|
||||||
|
Copyright (c) 2026, PuqiAR (im@puqiar.top) All rights reserved.
|
||||||
|
See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::error::diagnostic::{Diagnostic, ErrorType, Thrower};
|
||||||
|
use crate::core;
|
||||||
|
use crate::core::source::SourceRange;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnterminatedCommentError
|
||||||
|
{
|
||||||
|
file_id: usize,
|
||||||
|
start_line: usize,
|
||||||
|
start_column: usize,
|
||||||
|
end_line: usize,
|
||||||
|
end_column: usize,
|
||||||
|
thrower: Option<Thrower>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnterminatedCommentError
|
||||||
|
{
|
||||||
|
pub fn new(file_id: usize, start_line: usize, start_column: usize, end_line: usize, end_column: usize) -> Self
|
||||||
|
{
|
||||||
|
UnterminatedCommentError
|
||||||
|
{
|
||||||
|
file_id,
|
||||||
|
start_line,
|
||||||
|
start_column,
|
||||||
|
end_line,
|
||||||
|
end_column,
|
||||||
|
thrower: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_thrower(mut self, t: Thrower) -> Self
|
||||||
|
{
|
||||||
|
self.thrower = Some(t);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for UnterminatedCommentError
|
||||||
|
{
|
||||||
|
fn error_type(&self) -> ErrorType
|
||||||
|
{
|
||||||
|
ErrorType::UnterminatedComment
|
||||||
|
}
|
||||||
|
|
||||||
|
fn message(&self, lang: core::Lang) -> String
|
||||||
|
{
|
||||||
|
match lang
|
||||||
|
{
|
||||||
|
core::Lang::en_US =>
|
||||||
|
{
|
||||||
|
format!("Unterminated block comment starting at line {}, column {}", self.start_line, self.start_column)
|
||||||
|
}
|
||||||
|
core::Lang::zh_CN =>
|
||||||
|
{
|
||||||
|
format!("未终止的多行注释,起始于第 {} 行,第 {} 列", self.start_line, self.start_column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span(&self) -> SourceRange
|
||||||
|
{
|
||||||
|
SourceRange
|
||||||
|
{
|
||||||
|
file_id: self.file_id,
|
||||||
|
start_line: self.start_line,
|
||||||
|
start_column: self.start_column,
|
||||||
|
end_line: self.end_line,
|
||||||
|
end_column: self.end_column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thrower(&self) -> Option<Thrower>
|
||||||
|
{
|
||||||
|
self.thrower
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user