Compare commits
37 Commits
dd0b2904ba
...
cpp_editio
| Author | SHA1 | Date | |
|---|---|---|---|
| 680197aafe | |||
| 4f87078a87 | |||
| 9338c21449 | |||
| 98de782760 | |||
| fafa2b4946 | |||
| 570a87c3cd | |||
| e1d9812f92 | |||
| 6bcc98bdb3 | |||
| 91b5a0e384 | |||
| c0eacfd236 | |||
| 51a939ac45 | |||
| 0f635ccf2b | |||
| 90448006ff | |||
| 91e4eb734e | |||
| 6dbecbbdc0 | |||
| 1fe9ccf7ea | |||
| bb23ddf9fa | |||
| 12dc31a6c0 | |||
| a0fb8cdffb | |||
| b7bb889676 | |||
| 852dd27836 | |||
| abdb1d2fb0 | |||
| eb20993e27 | |||
| 2631f76da1 | |||
| f2e899c7a7 | |||
| c81da16dfb | |||
| 663fe39070 | |||
| 6b75e028ff | |||
| 878157c2fc | |||
| 35e479fd05 | |||
| 51e831cc6a | |||
| 35b98c4d7f | |||
| 877253cbbc | |||
| cfcdfde170 | |||
| 5e75402b43 | |||
| 642ce66f75 | |||
| 58212a3715 |
33
.ci/Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
# 1. 设置镜像源(可选,用于加速)
|
||||||
|
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
|
||||||
|
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
|
||||||
|
|
||||||
|
# 2. 一次性安装所有依赖(工具链、库、CI工具)
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
wget gnupg ca-certificates \
|
||||||
|
clang-19 lld-19 libc++-19-dev libc++abi-19-dev \
|
||||||
|
mingw-w64 g++-mingw-w64 \
|
||||||
|
git tar curl \
|
||||||
|
python3 python3-pip python3.12-venv libpython3.12 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* && \
|
||||||
|
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 && \
|
||||||
|
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 && \
|
||||||
|
ln -sf /usr/bin/ld.lld-19 /usr/bin/ld.lld
|
||||||
|
|
||||||
|
# 3. 安装xmake
|
||||||
|
RUN wget -O /usr/local/bin/xmake \
|
||||||
|
https://git.fig-lang.cn/PuqiAR/xmake-binary-copy/raw/commit/989d1f2dabb0bc8d5981a5f900c2cf7c2ac78ee4/xmake-bundle-v3.0.5.linux.x86_64 && \
|
||||||
|
chmod +x /usr/local/bin/xmake
|
||||||
|
|
||||||
|
# 4. 创建非root用户
|
||||||
|
RUN useradd -m -s /bin/bash builder
|
||||||
|
USER builder
|
||||||
|
WORKDIR /home/builder
|
||||||
|
|
||||||
|
RUN xmake --version | head -1 && \
|
||||||
|
clang++ --version | head -1 && \
|
||||||
|
git --version && \
|
||||||
|
echo "✅ 核心工具就绪"
|
||||||
213
.clang-format
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
|
||||||
|
# 访问说明符(public、private等)的偏移
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
|
||||||
|
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||||
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
|
|
||||||
|
# 连续赋值时,对齐所有等号
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
|
||||||
|
# 连续声明时,对齐所有声明的变量名
|
||||||
|
AlignConsecutiveDeclarations: true
|
||||||
|
|
||||||
|
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
|
||||||
|
# 水平对齐二元和三元表达式的操作数
|
||||||
|
AlignOperands: true
|
||||||
|
|
||||||
|
# 对齐连续的尾随的注释
|
||||||
|
AlignTrailingComments: true
|
||||||
|
|
||||||
|
# 允许函数声明的所有参数在放在下一行
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
|
||||||
|
# 允许短的块放在同一行
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的case标签放在同一行
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
|
||||||
|
# 允许短的if语句保持在同一行
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
|
||||||
|
# 允许短的循环保持在同一行
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
|
||||||
|
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
||||||
|
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
|
||||||
|
# 总是在多行string字面量前换行
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
|
||||||
|
# 总是在template声明后换行
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
|
||||||
|
# false表示函数实参要么都在同一行,要么都各自一行
|
||||||
|
BinPackArguments: false
|
||||||
|
|
||||||
|
# false表示所有形参要么都在同一行,要么都各自一行
|
||||||
|
BinPackParameters: false
|
||||||
|
|
||||||
|
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
|
||||||
|
BraceWrapping:
|
||||||
|
# class定义后面
|
||||||
|
AfterClass: true
|
||||||
|
# 控制语句后面
|
||||||
|
AfterControlStatement: true
|
||||||
|
# enum定义后面
|
||||||
|
AfterEnum: true
|
||||||
|
# 函数定义后面
|
||||||
|
AfterFunction: true
|
||||||
|
# 命名空间定义后面
|
||||||
|
AfterNamespace: true
|
||||||
|
# struct定义后面
|
||||||
|
AfterStruct: true
|
||||||
|
# union定义后面
|
||||||
|
AfterUnion: true
|
||||||
|
# extern之后
|
||||||
|
AfterExternBlock: false
|
||||||
|
# catch之前
|
||||||
|
BeforeCatch: true
|
||||||
|
# else之前
|
||||||
|
BeforeElse: true
|
||||||
|
# 缩进大括号
|
||||||
|
IndentBraces: false
|
||||||
|
# 分离空函数
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
# 分离空语句
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
# 分离空命名空间
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
|
||||||
|
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
|
||||||
|
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
|
||||||
|
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
|
||||||
|
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
|
||||||
|
# 注:这里认为语句块也属于函数
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
|
||||||
|
# 在三元运算符前换行
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
|
||||||
|
# 在构造函数的初始化列表的冒号后换行
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
|
||||||
|
#BreakInheritanceList: AfterColon
|
||||||
|
|
||||||
|
BreakStringLiterals: false
|
||||||
|
|
||||||
|
# 每行字符的限制,0表示没有限制
|
||||||
|
ColumnLimit: 100
|
||||||
|
CompactNamespaces: true
|
||||||
|
|
||||||
|
# 构造函数的初始化列表要么都在同一行,要么都各自一行
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
|
||||||
|
# 构造函数的初始化列表的缩进宽度
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
|
||||||
|
# 延续的行的缩进宽度
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
|
||||||
|
# 去除C++11的列表初始化的大括号{后和}前的空格
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
|
||||||
|
# 继承最常用的指针和引用的对齐方式
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
|
||||||
|
# 固定命名空间注释
|
||||||
|
FixNamespaceComments: true
|
||||||
|
|
||||||
|
# 缩进case标签
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
|
||||||
|
# 缩进宽度
|
||||||
|
IndentWidth: 4
|
||||||
|
|
||||||
|
# 函数返回类型换行时,缩进函数声明或函数定义的函数名
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
|
||||||
|
# 保留在块开始处的空行
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
|
||||||
|
# 连续空行的最大数量
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
|
||||||
|
# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
|
||||||
|
NamespaceIndentation: All
|
||||||
|
|
||||||
|
# 指针和引用的对齐: Left, Right, Middle
|
||||||
|
PointerAlignment: Right
|
||||||
|
|
||||||
|
# 允许重新排版注释
|
||||||
|
ReflowComments: true
|
||||||
|
|
||||||
|
# 允许排序#include
|
||||||
|
SortIncludes: true
|
||||||
|
|
||||||
|
# 允许排序 using 声明
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
|
||||||
|
# 在C风格类型转换后添加空格
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
# true -> (int) 0.1 false-> (int)0.1
|
||||||
|
|
||||||
|
# 在Template 关键字后面添加空格
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
|
||||||
|
# 在赋值运算符之前添加空格
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
|
||||||
|
# SpaceBeforeCpp11BracedList: true
|
||||||
|
|
||||||
|
# SpaceBeforeCtorInitializerColon: true
|
||||||
|
|
||||||
|
# SpaceBeforeInheritanceColon: true
|
||||||
|
|
||||||
|
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
|
||||||
|
# SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
|
||||||
|
# 在空的圆括号中添加空格
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
|
||||||
|
# 在尾随的评论前添加的空格数(只适用于//)
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
|
||||||
|
# 在尖括号的<后和>前添加空格
|
||||||
|
SpacesInAngles: false
|
||||||
|
|
||||||
|
# 在C风格类型转换的括号中添加空格
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
|
||||||
|
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
|
||||||
|
# 在圆括号的(后和)前添加空格
|
||||||
|
SpacesInParentheses: false
|
||||||
|
|
||||||
|
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
|
||||||
|
# 标准: Cpp03, Cpp11, Auto
|
||||||
|
Standard: Auto
|
||||||
|
|
||||||
|
# tab宽度
|
||||||
|
TabWidth: 4
|
||||||
|
|
||||||
|
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
|
||||||
|
UseTab: Never
|
||||||
365
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
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版本发布完成!"
|
||||||
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Xmake cache
|
||||||
|
.xmake/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# MacOS Cache
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.VSCodeCounter
|
||||||
|
|
||||||
|
/test.fig
|
||||||
79
ExampleCodes/1-Variables.fig
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
var any_var; // type is Any
|
||||||
|
var number = 10; // type is Int
|
||||||
|
number = "123"; // valid
|
||||||
|
|
||||||
|
var number2 := 10; // specific type is Int
|
||||||
|
var number3: Int = 10; // both is ok
|
||||||
|
/*
|
||||||
|
number2 = 3.14;
|
||||||
|
invalid!
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Pi := 3.14; // recommended, auto detect type
|
||||||
|
// equal -> const Pi: Double = 3.14;
|
||||||
|
|
||||||
|
/*
|
||||||
|
In fig, we have 13 builtin-type
|
||||||
|
|
||||||
|
01 Any
|
||||||
|
02 Null
|
||||||
|
03 Int
|
||||||
|
04 String
|
||||||
|
05 Bool
|
||||||
|
06 Double
|
||||||
|
07 Function
|
||||||
|
08 StructType
|
||||||
|
09 StructInstance
|
||||||
|
10 List
|
||||||
|
11 Map
|
||||||
|
12 Module
|
||||||
|
13 InterfaceType
|
||||||
|
|
||||||
|
3, 4, 5, 6, 10, 11 are initable
|
||||||
|
|
||||||
|
value system:
|
||||||
|
object is immutable
|
||||||
|
(included basic types: Int, String...)
|
||||||
|
|
||||||
|
`variable` is a name, refers to an object
|
||||||
|
assignment is to bind name to value
|
||||||
|
|
||||||
|
Example: var a := 10;
|
||||||
|
|
||||||
|
[name] 'a' ---> variable slot (name, declared type, access modifier, [value) ---> ObjectPtr ---> raw Object class
|
||||||
|
bind bind (shared_ptr)
|
||||||
|
|
||||||
|
For example:
|
||||||
|
var a := 10;
|
||||||
|
var b := 10;
|
||||||
|
|
||||||
|
`a` and `b` reference to the same object in memory
|
||||||
|
|
||||||
|
a = 20;
|
||||||
|
|
||||||
|
now a refers to a new object (20, Int)
|
||||||
|
|
||||||
|
what about complex types?
|
||||||
|
they actually have same behaviors with basic types
|
||||||
|
|
||||||
|
var a := [1, 2, 3, 4];
|
||||||
|
var b := a;
|
||||||
|
|
||||||
|
> a
|
||||||
|
[1, 2, 3, 4]
|
||||||
|
> b
|
||||||
|
[1, 2, 3, 4]
|
||||||
|
|
||||||
|
set a[0] to 5
|
||||||
|
|
||||||
|
> a
|
||||||
|
[5, 2, 3, 4]
|
||||||
|
> b
|
||||||
|
[5, 2, 3, 4]
|
||||||
|
|
||||||
|
Why did such a result occur?
|
||||||
|
|
||||||
|
" `a` and `b` reference to the same object in memory "
|
||||||
|
|
||||||
|
If you wish to obtain a copy, use List {a} to deeply copy it
|
||||||
|
*/
|
||||||
16
ExampleCodes/2-Function.fig
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import std.io;
|
||||||
|
|
||||||
|
func greeting(name:String) -> String
|
||||||
|
{
|
||||||
|
return "Hello " + name + "!";
|
||||||
|
}
|
||||||
|
|
||||||
|
io.println(greeting("Fig"));
|
||||||
|
|
||||||
|
func adder(x)
|
||||||
|
{
|
||||||
|
return func (n) => x + n; // closure
|
||||||
|
}
|
||||||
|
|
||||||
|
const add2 = adder(2);
|
||||||
|
io.println(add2(3));
|
||||||
30
ExampleCodes/3-Structure.fig
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import std.io;
|
||||||
|
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
x: Int; // type specifiers are optional
|
||||||
|
y: Int; // type specifiers are optional
|
||||||
|
|
||||||
|
// x and y are private fields, can only reached by internal context
|
||||||
|
|
||||||
|
public func toString() -> String
|
||||||
|
{
|
||||||
|
return "(" + (x as String) + "," + (y as String) + ")";
|
||||||
|
}
|
||||||
|
// public func toString() {} is ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// make points
|
||||||
|
|
||||||
|
var p1 := new Point{1, 2};
|
||||||
|
io.println(p1.toString()); // (1,2)
|
||||||
|
|
||||||
|
var p2 := new Point{x: 2, y: 3};
|
||||||
|
io.println(p2.toString()); // (2,3)
|
||||||
|
|
||||||
|
var x := 114;
|
||||||
|
var y := 514;
|
||||||
|
|
||||||
|
var p3 := new Point{y, x}; // shorthand mode, can be unordered, auto match field and variable!
|
||||||
|
// = Point{x: x, y: y}
|
||||||
|
io.println(p3.toString()); // (114,514)
|
||||||
94
ExampleCodes/4-Interface.fig
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import std.io;
|
||||||
|
|
||||||
|
interface Document
|
||||||
|
{
|
||||||
|
getDepth() -> Int; // return type is necessary
|
||||||
|
getName() -> String;
|
||||||
|
|
||||||
|
/* toString() -> String
|
||||||
|
{
|
||||||
|
// default implementation
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
struct File
|
||||||
|
{
|
||||||
|
public depth: Int;
|
||||||
|
public name: String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Document for File
|
||||||
|
{
|
||||||
|
getDepth()
|
||||||
|
{
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Folder
|
||||||
|
{
|
||||||
|
public depth: Int;
|
||||||
|
public name: String;
|
||||||
|
public childs: List = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Document for Folder
|
||||||
|
{
|
||||||
|
getDepth()
|
||||||
|
{
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root_folder := new Folder{
|
||||||
|
0,
|
||||||
|
"root",
|
||||||
|
[
|
||||||
|
new File{
|
||||||
|
1,
|
||||||
|
"joyo.txt"
|
||||||
|
},
|
||||||
|
new Folder{
|
||||||
|
2,
|
||||||
|
"joyoyo",
|
||||||
|
[
|
||||||
|
new File{
|
||||||
|
3,
|
||||||
|
"JOYO2.txt"
|
||||||
|
},
|
||||||
|
new Folder{
|
||||||
|
3,
|
||||||
|
"joyoyoyo"
|
||||||
|
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
func print_directory(root: Document)
|
||||||
|
{
|
||||||
|
io.print(" " * root.getDepth());
|
||||||
|
io.println(root.getDepth(), root.getName());
|
||||||
|
if root is Folder
|
||||||
|
{
|
||||||
|
for var i := 0; i < root.childs.length(); i += 1
|
||||||
|
{
|
||||||
|
var child := root.childs[i];
|
||||||
|
print_directory(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print_directory(root_folder);
|
||||||
15
ExampleCodes/FigFig/main.fig
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import std.io;
|
||||||
|
|
||||||
|
import token {Token, TokenType};
|
||||||
|
import tokenizer {Tokenizer};
|
||||||
|
|
||||||
|
const src := "abc egaD";
|
||||||
|
const tokenizer := new Tokenizer{src};
|
||||||
|
|
||||||
|
const result := tokenizer.TokenizeAll();
|
||||||
|
|
||||||
|
for var i := 0; i < result.length(); i += 1
|
||||||
|
{
|
||||||
|
const tok := result[i];
|
||||||
|
io.printf("{}: {}\n", tok.literal, tok.type);
|
||||||
|
}
|
||||||
31
ExampleCodes/FigFig/token.fig
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Example code: FigFig
|
||||||
|
|
||||||
|
Token.fig
|
||||||
|
Copyright (C) 2020-2026 PuqiAR
|
||||||
|
|
||||||
|
*/
|
||||||
|
struct _TokenTypes
|
||||||
|
{
|
||||||
|
public EOF = -1;
|
||||||
|
public Identifier = 0;
|
||||||
|
|
||||||
|
public StringLiteral = 1;
|
||||||
|
public NumberLiteral = 2;
|
||||||
|
public True = 3;
|
||||||
|
public False = 4;
|
||||||
|
public Null = 5;
|
||||||
|
|
||||||
|
public Plus = 6;
|
||||||
|
public Minus = 7;
|
||||||
|
public Asterisk = 8;
|
||||||
|
public Slash = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
public const TokenType := new _TokenTypes{};
|
||||||
|
|
||||||
|
public struct Token
|
||||||
|
{
|
||||||
|
public literal: String = "";
|
||||||
|
public type: Int = TokenType.EOF;
|
||||||
|
}
|
||||||
102
ExampleCodes/FigFig/tokenizer.fig
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
Example code: FigFig
|
||||||
|
|
||||||
|
Tokenizer.fig
|
||||||
|
Copyright (C) 2020-2026 PuqiAR
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import token {Token, TokenType};
|
||||||
|
|
||||||
|
func list_contains(lst: List, value: Any) -> Bool
|
||||||
|
{
|
||||||
|
for var i := 0; i < lst.length(); i += 1
|
||||||
|
{
|
||||||
|
if lst[i] == value
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func isspace(c: String) -> Bool
|
||||||
|
{
|
||||||
|
return c == " " || c == "\n" || c == "\t";
|
||||||
|
}
|
||||||
|
|
||||||
|
func isalpha(c: String) -> Bool
|
||||||
|
{
|
||||||
|
const alb := [
|
||||||
|
"a", "b", "c", "d",
|
||||||
|
"e", "f", "g", "h",
|
||||||
|
"i", "j", "k", "l",
|
||||||
|
"m", "n", "o", "p",
|
||||||
|
"q", "r", "s", "t",
|
||||||
|
"u", "v", "w", "x",
|
||||||
|
"y", "z",
|
||||||
|
"A", "B", "C", "D",
|
||||||
|
"E", "F", "G", "H",
|
||||||
|
"I", "J", "K", "L",
|
||||||
|
"M", "N", "O", "P",
|
||||||
|
"Q", "R", "S", "T",
|
||||||
|
"U", "V", "W", "X",
|
||||||
|
"Y", "Z"
|
||||||
|
];
|
||||||
|
return list_contains(alb, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public struct Tokenizer
|
||||||
|
{
|
||||||
|
src: String = "";
|
||||||
|
idx: Int = 0;
|
||||||
|
|
||||||
|
func next() -> Null
|
||||||
|
{
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasNext() -> Bool
|
||||||
|
{
|
||||||
|
return idx < src.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
func produce() -> String
|
||||||
|
{
|
||||||
|
const tmp := src[idx];
|
||||||
|
idx += 1;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
func current() -> String
|
||||||
|
{
|
||||||
|
return src[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
public func TokenizeAll() -> List
|
||||||
|
{
|
||||||
|
var output := [];
|
||||||
|
|
||||||
|
const push := func (tok: Token) => output.push(tok);
|
||||||
|
|
||||||
|
while hasNext()
|
||||||
|
{
|
||||||
|
while hasNext() && isspace(current())
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
if isalpha(current())
|
||||||
|
{
|
||||||
|
var identi := "";
|
||||||
|
while hasNext() && isalpha(current())
|
||||||
|
{
|
||||||
|
identi += produce();
|
||||||
|
}
|
||||||
|
push(new Token{identi, TokenType.Identifier});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
ExampleCodes/SpeedTest/fib.fig
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import std.io;
|
||||||
|
|
||||||
|
func fib(x:Int) -> Int
|
||||||
|
{
|
||||||
|
if (x <= 1)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result := fib(25);
|
||||||
|
io.println("result: ", result);
|
||||||
11
ExampleCodes/SpeedTest/fib.lua
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
function fib(n)
|
||||||
|
if (n <= 1) then
|
||||||
|
return n
|
||||||
|
else
|
||||||
|
return fib(n - 1) + fib(n - 2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local start = os.clock()
|
||||||
|
local result = fib(30)
|
||||||
|
local endt = os.clock()
|
||||||
|
print(result, " cost: ", (endt - start) * 1000, "ms")
|
||||||
11
ExampleCodes/SpeedTest/fib.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from time import time as tt
|
||||||
|
|
||||||
|
def fib(x:int) -> int:
|
||||||
|
if x <= 1: return x
|
||||||
|
return fib(x-1) + fib(x-2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
t0 = tt()
|
||||||
|
result = fib(35)
|
||||||
|
t1 = tt()
|
||||||
|
print('cost: ',t1-t0, 'result:', result)
|
||||||
84
ExampleCodes/SpeedTest/fibBenchmark.fig
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import std.io;
|
||||||
|
import std.time;
|
||||||
|
import std.value;
|
||||||
|
|
||||||
|
func benchmark(fn: Function, arg: Any) -> Null
|
||||||
|
{
|
||||||
|
io.println("Testing fn:", fn, "with arg:", arg);
|
||||||
|
const start := time.now();
|
||||||
|
|
||||||
|
const result := fn(arg);
|
||||||
|
|
||||||
|
const end := time.now();
|
||||||
|
const duration := new time.Time{
|
||||||
|
end.since(start)
|
||||||
|
};
|
||||||
|
io.println("=" * 50);
|
||||||
|
io.println("fn returns:", result, "\n");
|
||||||
|
io.println("Cost:", duration.toSeconds(), "s");
|
||||||
|
io.println(" ", duration.toMillis(), "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
var memo := {};
|
||||||
|
|
||||||
|
func fib_memo(x)
|
||||||
|
{
|
||||||
|
if memo.contains(x)
|
||||||
|
{
|
||||||
|
return memo.get(x);
|
||||||
|
}
|
||||||
|
if x <= 1
|
||||||
|
{
|
||||||
|
memo[x] = x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
var result := fib_memo(x - 1) + fib_memo(x - 2);
|
||||||
|
memo[x] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
func fib_iter(n)
|
||||||
|
{
|
||||||
|
var a := 0;
|
||||||
|
var b := 1;
|
||||||
|
for var i := 0; i < n; i = i + 1
|
||||||
|
{
|
||||||
|
var temp := a + b;
|
||||||
|
a = b;
|
||||||
|
b = temp;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
func fib(x)
|
||||||
|
{
|
||||||
|
if x <= 1
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return fib(x - 1) + fib(x - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
func fib_tail(n, a=0, b=1) {
|
||||||
|
if n == 0 { return a; }
|
||||||
|
if n == 1 { return b; }
|
||||||
|
return fib_tail(n-1, b, a+b);
|
||||||
|
}
|
||||||
|
|
||||||
|
const n := 30;
|
||||||
|
|
||||||
|
io.println("! fib(" + value.string_from(n) + "):");
|
||||||
|
benchmark(fib, n);
|
||||||
|
io.print("\n\n");
|
||||||
|
|
||||||
|
io.println("! fib_memo(" + value.string_from(n) + "):");
|
||||||
|
benchmark(fib_memo, n);
|
||||||
|
io.print("\n\n");
|
||||||
|
|
||||||
|
io.println("! fib_iter(" + value.string_from(n) + "):");
|
||||||
|
benchmark(fib_iter, n);
|
||||||
|
io.print("\n\n");
|
||||||
|
|
||||||
|
io.println("! fib_tail(" + value.string_from(n) + "):");
|
||||||
|
benchmark(fib_tail, n);
|
||||||
|
io.print("\n\n");
|
||||||
27
ExampleCodes/SpeedTest/fibLoopTest.fig
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import std.io;
|
||||||
|
import std.value;
|
||||||
|
|
||||||
|
var callCnt:Int = 0;
|
||||||
|
func fib(x:Int) -> Int
|
||||||
|
{
|
||||||
|
callCnt = callCnt + 1;
|
||||||
|
if (x <= 1)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fibx:Int;
|
||||||
|
io.print("input an index of fib ");
|
||||||
|
fibx = value.int_parse(io.read());
|
||||||
|
|
||||||
|
var cnt:Int = 0;
|
||||||
|
io.println("test forever");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cnt = cnt + 1;
|
||||||
|
io.println("test ", cnt,",result: ", fib(fibx));
|
||||||
|
io.println("func `fib` called ", callCnt);
|
||||||
|
callCnt = 0;
|
||||||
|
}
|
||||||
29
ExampleCodes/use_std_test.fig
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import std.io;
|
||||||
|
import std.test;
|
||||||
|
|
||||||
|
var ascii_string_test := new test.Test{
|
||||||
|
"ascii_string_test",
|
||||||
|
func () => io.println("Hello," + " world!"),
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
var unicode_string_test := new test.Test{
|
||||||
|
"unicode_string_test",
|
||||||
|
func () => io.println("你好," + " 世界!"),
|
||||||
|
2
|
||||||
|
};
|
||||||
|
|
||||||
|
var unicode_string_inserting_test := new test.Test{
|
||||||
|
"unicode_string_inserting_test",
|
||||||
|
func (){
|
||||||
|
var str := "我是你的粑粑";
|
||||||
|
str.insert(1, "不");
|
||||||
|
return str;
|
||||||
|
},
|
||||||
|
"我不是你的粑粑"
|
||||||
|
};
|
||||||
|
|
||||||
|
var tests := [ascii_string_test, unicode_string_test, unicode_string_inserting_test];
|
||||||
|
|
||||||
|
var tester := new test.Tester{tests};
|
||||||
|
tester.TestAll();
|
||||||
5
Fig-VSCode/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
out
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
.vscode-test/
|
||||||
|
*.vsix
|
||||||
5
Fig-VSCode/.vscode-test.mjs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { defineConfig } from '@vscode/test-cli';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
files: 'out/test/**/*.test.js',
|
||||||
|
});
|
||||||
11
Fig-VSCode/.vscodeignore
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.vscode/**
|
||||||
|
.vscode-test/**
|
||||||
|
src/**
|
||||||
|
.gitignore
|
||||||
|
.yarnrc
|
||||||
|
vsc-extension-quickstart.md
|
||||||
|
**/tsconfig.json
|
||||||
|
**/eslint.config.mjs
|
||||||
|
**/*.map
|
||||||
|
**/*.ts
|
||||||
|
**/.vscode-test.*
|
||||||
9
Fig-VSCode/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to the "fig-vscode" extension will be documented in this file.
|
||||||
|
|
||||||
|
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Initial release
|
||||||
46
Fig-VSCode/LICENSE
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
MIT License (Fig)
|
||||||
|
|
||||||
|
Copyright (c) 2025 PuqiAR <im@puqiar.top>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
1. The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
2. This project includes code from the following projects with their respective licenses:
|
||||||
|
|
||||||
|
- argparse (MIT License)
|
||||||
|
Copyright (c) 2018 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
- magic_enum (MIT License)
|
||||||
|
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||||
|
|
||||||
|
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.
|
||||||
3
Fig-VSCode/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# fig-vscode README
|
||||||
|
|
||||||
|
Fuck.
|
||||||
BIN
Fig-VSCode/bin/Fig-LSP.exe
Normal file
27
Fig-VSCode/eslint.config.mjs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import typescriptEslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default [{
|
||||||
|
files: ["**/*.ts"],
|
||||||
|
}, {
|
||||||
|
plugins: {
|
||||||
|
"@typescript-eslint": typescriptEslint.plugin,
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
parser: typescriptEslint.parser,
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/naming-convention": ["warn", {
|
||||||
|
selector: "import",
|
||||||
|
format: ["camelCase", "PascalCase"],
|
||||||
|
}],
|
||||||
|
|
||||||
|
curly: "warn",
|
||||||
|
eqeqeq: "warn",
|
||||||
|
"no-throw-literal": "warn",
|
||||||
|
semi: "warn",
|
||||||
|
},
|
||||||
|
}];
|
||||||
4
Fig-VSCode/images/Logo.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="512" height="512" fill="#F28C28"/>
|
||||||
|
<path d="M135 100 H395 V195 H230 V265 H370 V360 H230 V430 H135 V100 Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 244 B |
3
Fig-VSCode/images/LogoDark.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M135 100 H395 V195 H230 V265 H370 V360 H230 V430 H135 V100 Z" fill="#F28C28"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 196 B |
147
Fig-VSCode/package-lock.json
generated
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"name": "fig-vscode",
|
||||||
|
"version": "0.5.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "fig-vscode",
|
||||||
|
"version": "0.5.0",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-languageclient": "^9.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "^20.6.0",
|
||||||
|
"@types/vscode": "^1.109.0",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.108.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/mocha": {
|
||||||
|
"version": "10.0.10",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/mocha/-/mocha-10.0.10.tgz",
|
||||||
|
"integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.19.33",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.33.tgz",
|
||||||
|
"integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/vscode": {
|
||||||
|
"version": "1.109.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.109.0.tgz",
|
||||||
|
"integrity": "sha512-0Pf95rnwEIwDbmXGC08r0B4TQhAbsHQ5UyTIgVgoieDe4cOnf92usuR5dEczb6bTKEp7ziZH4TV1TRGPPCExtw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.7.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
|
||||||
|
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
|
||||||
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/vscode-jsonrpc": {
|
||||||
|
"version": "8.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
||||||
|
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageclient": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^5.1.0",
|
||||||
|
"semver": "^7.3.7",
|
||||||
|
"vscode-languageserver-protocol": "3.17.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.82.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageclient/node_modules/minimatch": {
|
||||||
|
"version": "5.1.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.7.tgz",
|
||||||
|
"integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-protocol": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-jsonrpc": "8.2.0",
|
||||||
|
"vscode-languageserver-types": "3.17.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vscode-languageserver-types": {
|
||||||
|
"version": "3.17.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
|
||||||
|
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Fig-VSCode/package.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"name": "fig-vscode",
|
||||||
|
"displayName": "Fig Language",
|
||||||
|
"description": "VSCode extension for Fig language with syntax highlighting and lsp support",
|
||||||
|
"version": "0.5.0",
|
||||||
|
"publisher": "PuqiAR",
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.108.0"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
"Programming Languages"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "https://github.com/PuqiAR/Fig"
|
||||||
|
},
|
||||||
|
"activationEvents": [
|
||||||
|
"onLanguage:fig"
|
||||||
|
],
|
||||||
|
"main": "./out/extension.js",
|
||||||
|
"contributes": {
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "fig",
|
||||||
|
"aliases": [
|
||||||
|
"Fig",
|
||||||
|
"fig"
|
||||||
|
],
|
||||||
|
"extensions": [
|
||||||
|
".fig"
|
||||||
|
],
|
||||||
|
"configuration": "./language-configuration.json",
|
||||||
|
"icon": {
|
||||||
|
"light": "./images/Logo.svg",
|
||||||
|
"dark": "./images/LogoDark.svg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grammars": [
|
||||||
|
{
|
||||||
|
"language": "fig",
|
||||||
|
"scopeName": "source.fig",
|
||||||
|
"path": "./syntaxes/fig.tmLanguage.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"vscode:prepublish": "npm run compile",
|
||||||
|
"compile": "tsc -p ./",
|
||||||
|
"watch": "tsc -watch -p ./",
|
||||||
|
"pretest": "npm run compile",
|
||||||
|
"test": "node ./out/test/runTest.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vscode-languageclient": "^9.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mocha": "^10.0.10",
|
||||||
|
"@types/node": "^20.6.0",
|
||||||
|
"@types/vscode": "^1.108.0",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Fig-VSCode/src/extension.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import { ExtensionContext, workspace } from 'vscode';
|
||||||
|
import {
|
||||||
|
LanguageClient,
|
||||||
|
LanguageClientOptions,
|
||||||
|
ServerOptions
|
||||||
|
} from 'vscode-languageclient/node';
|
||||||
|
|
||||||
|
let client: LanguageClient;
|
||||||
|
|
||||||
|
export function activate(context: ExtensionContext) {
|
||||||
|
const exeName = process.platform === 'win32' ? 'Fig-LSP.exe' : 'Fig-LSP';
|
||||||
|
|
||||||
|
// 获取插件安装后的绝对沙箱路径
|
||||||
|
const serverCommand = context.asAbsolutePath(path.join('bin', exeName));
|
||||||
|
|
||||||
|
const serverOptions: ServerOptions = {
|
||||||
|
run: { command: serverCommand, args: [] },
|
||||||
|
debug: { command: serverCommand, args: [] }
|
||||||
|
};
|
||||||
|
|
||||||
|
const clientOptions: LanguageClientOptions = {
|
||||||
|
documentSelector: [{ scheme: 'file', language: 'fig' }],
|
||||||
|
synchronize: {
|
||||||
|
fileEvents: workspace.createFileSystemWatcher('**/*.fig')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
client = new LanguageClient(
|
||||||
|
'figLanguageServer',
|
||||||
|
'Fig Language Server',
|
||||||
|
serverOptions,
|
||||||
|
clientOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
client.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deactivate(): Thenable<void> | undefined {
|
||||||
|
if (!client) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return client.stop();
|
||||||
|
}
|
||||||
25
Fig-VSCode/src/language-configuration.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"comments": {
|
||||||
|
"lineComment": "//",
|
||||||
|
"blockComment": ["/*", "*/"]
|
||||||
|
},
|
||||||
|
"brackets": [
|
||||||
|
["{", "}"],
|
||||||
|
["[", "]"],
|
||||||
|
["(", ")"]
|
||||||
|
],
|
||||||
|
"autoClosingPairs": [
|
||||||
|
{ "open": "{", "close": "}" },
|
||||||
|
{ "open": "[", "close": "]" },
|
||||||
|
{ "open": "(", "close": ")" },
|
||||||
|
{ "open": "\"", "close": "\"" },
|
||||||
|
{ "open": "'", "close": "'" }
|
||||||
|
],
|
||||||
|
"surroundingPairs": [
|
||||||
|
{ "open": "{", "close": "}" },
|
||||||
|
{ "open": "[", "close": "]" },
|
||||||
|
{ "open": "(", "close": ")" },
|
||||||
|
{ "open": "\"", "close": "\"" },
|
||||||
|
{ "open": "'", "close": "'" }
|
||||||
|
]
|
||||||
|
}
|
||||||
15
Fig-VSCode/src/test/extension.test.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import * as assert from 'assert';
|
||||||
|
|
||||||
|
// You can import and use all API from the 'vscode' module
|
||||||
|
// as well as import your extension to test it
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
// import * as myExtension from '../../extension';
|
||||||
|
|
||||||
|
suite('Extension Test Suite', () => {
|
||||||
|
vscode.window.showInformationMessage('Start all tests.');
|
||||||
|
|
||||||
|
test('Sample test', () => {
|
||||||
|
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||||
|
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||||
|
});
|
||||||
|
});
|
||||||
114
Fig-VSCode/syntaxes/fig.tmLanguage.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"name": "Fig",
|
||||||
|
"scopeName": "source.fig",
|
||||||
|
"patterns": [
|
||||||
|
{ "include": "#comments" },
|
||||||
|
{ "include": "#strings" },
|
||||||
|
{ "include": "#numbers" },
|
||||||
|
{ "include": "#keywords" },
|
||||||
|
{ "include": "#operators" },
|
||||||
|
{ "include": "#functions" },
|
||||||
|
{ "include": "#identifiers" }
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"comments": {
|
||||||
|
"patterns": [
|
||||||
|
{ "name": "comment.line.double-slash.fig", "match": "//.*$" },
|
||||||
|
{ "name": "comment.block.fig", "begin": "/\\*", "end": "\\*/" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"strings": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "string.quoted.double.fig",
|
||||||
|
"begin": "\"\"\"",
|
||||||
|
"end": "\"\"\"",
|
||||||
|
"patterns": [{ "match": ".", "name": "string.content.fig" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.quoted.double.fig",
|
||||||
|
"begin": "\"",
|
||||||
|
"end": "\"",
|
||||||
|
"patterns": [
|
||||||
|
{ "match": "\\\\.", "name": "constant.character.escape.fig" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.quoted.raw.fig",
|
||||||
|
"begin": "r\"",
|
||||||
|
"end": "\"",
|
||||||
|
"patterns": [{ "match": ".", "name": "string.content.fig" }]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"numbers": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "constant.numeric.float.fig",
|
||||||
|
"match": "\\d*\\.\\d+([eE][+-]?\\d+)?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "constant.numeric.integer.fig",
|
||||||
|
"match": "\\d+([eE][+-]?\\d+)?"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "keyword.control.fig",
|
||||||
|
"match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|new|struct|interface|impl|public|return|break|continue|try|catch|throw|is|as)\\b"
|
||||||
|
},
|
||||||
|
{ "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"operators": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.arithmetic.fig",
|
||||||
|
"match": "(\\+|\\-|\\*|/|%|\\*\\*)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.assignment.fig",
|
||||||
|
"match": "(=|\\+=|\\-=|\\*=|/=|%=|\\^=|:=)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.logical.fig",
|
||||||
|
"match": "(&&|\\|\\||\\b(and|or|not)\\b)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.comparison.fig",
|
||||||
|
"match": "(==|!=|<=|>=|<|>)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "punctuation.separator.fig",
|
||||||
|
"match": "[\\(\\)\\[\\]\\{\\},;:.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.other.fig",
|
||||||
|
"match": "(\\+\\+|--|->|=>|<<|>>|\\^|&|\\||~)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functions": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "entity.name.function.fig",
|
||||||
|
"begin": "\\bfunc\\s+([a-zA-Z_][a-zA-Z0-9_]*)",
|
||||||
|
"beginCaptures": {
|
||||||
|
"1": { "name": "entity.name.function.fig" }
|
||||||
|
},
|
||||||
|
"end": "(?=;)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"identifiers": {
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "variable.other.fig",
|
||||||
|
"match": "(?!\\bfunc\\b)[a-zA-Z_][a-zA-Z0-9_]*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Fig-VSCode/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "Node16",
|
||||||
|
"target": "ES2022",
|
||||||
|
"outDir": "out",
|
||||||
|
"lib": [
|
||||||
|
"ES2022"
|
||||||
|
],
|
||||||
|
"sourceMap": true,
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true, /* enable all strict type-checking options */
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Fig-VSCode/vsc-extension-quickstart.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Welcome to your VS Code Extension
|
||||||
|
|
||||||
|
## What's in the folder
|
||||||
|
|
||||||
|
* This folder contains all of the files necessary for your extension.
|
||||||
|
* `package.json` - this is the manifest file in which you declare your extension and command.
|
||||||
|
* The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
|
||||||
|
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
|
||||||
|
* The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
|
||||||
|
* We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
|
||||||
|
|
||||||
|
## Get up and running straight away
|
||||||
|
|
||||||
|
* Press `F5` to open a new window with your extension loaded.
|
||||||
|
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
|
||||||
|
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
|
||||||
|
* Find output from your extension in the debug console.
|
||||||
|
|
||||||
|
## Make changes
|
||||||
|
|
||||||
|
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
|
||||||
|
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||||
|
|
||||||
|
## Explore the API
|
||||||
|
|
||||||
|
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
|
||||||
|
|
||||||
|
## Run tests
|
||||||
|
|
||||||
|
* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
|
||||||
|
* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
|
||||||
|
* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
|
||||||
|
* See the output of the test result in the Test Results view.
|
||||||
|
* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
|
||||||
|
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
|
||||||
|
* You can create folders inside the `test` folder to structure your tests any way you want.
|
||||||
|
|
||||||
|
## Go further
|
||||||
|
|
||||||
|
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
|
||||||
|
* Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
|
||||||
|
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
|
||||||
|
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
|
||||||
|
* Integrate to the [report issue](https://code.visualstudio.com/api/get-started/wrapping-up#issue-reporting) flow to get issue and feature requests reported by users.
|
||||||
66
LICENSE
@@ -1,18 +1,56 @@
|
|||||||
MIT License
|
MIT License (Fig)
|
||||||
|
|
||||||
Copyright (c) 2026 PuqiAR
|
Copyright (c) 2026 PuqiAR <im@puqiar.top>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
in the Software without restriction, including without limitation the rights
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
following conditions:
|
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
|
1. The above copyright notice and this permission notice shall be included in all
|
||||||
portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
2. This project includes code from the following projects with their respective licenses:
|
||||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
- magic_enum (MIT License)
|
||||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||||
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.
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
- json (MIT LICENSE) (for LSP Server JSON-RPC)
|
||||||
|
Copyright (c) 2013-2026 Niels Lohmann
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|||||||
BIN
Logo/Logo.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
4
Logo/Logo.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="512" height="512" fill="#F28C28"/>
|
||||||
|
<path d="M135 100 H395 V195 H230 V265 H370 V360 H230 V430 H135 V100 Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 244 B |
BIN
Logo/LogoDark.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
3
Logo/LogoDark.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M135 100 H395 V195 H230 V265 H370 V360 H230 V430 H135 V100 Z" fill="#F28C28"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 196 B |
193
README.md
@@ -1,2 +1,195 @@
|
|||||||
# Fig
|
# Fig
|
||||||
|
|
||||||
|
## tmd赶工代码质量太差了暑假我要重构
|
||||||
|
|
||||||
|
<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)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

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

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
-~28ms%20(i5--13490f)-brightgreen)
|
||||||
|
|
||||||
|
**Fig** 是一门动态类型与静态类型注解混合的编程语言,采用基于引用的值语义。它既保留了脚本语言的灵活性,又提供了可选的类型约束,让代码更健壮。
|
||||||
|
|
||||||
|
> ⚠️ **Fig 当前为 0.5.0-alpha 版本,语法和 API 仍可能发生变动。** 欢迎试用和反馈,但请勿用于生产环境。
|
||||||
|
|
||||||
|
```fig
|
||||||
|
// 试试 Fig 的感觉
|
||||||
|
var flexible = 10 // 动态类型,可以是任意值
|
||||||
|
flexible = "hello" // 没问题
|
||||||
|
|
||||||
|
var fixed := 20 // 固定为 Int 类型
|
||||||
|
// fixed = 3.14 // 错误!类型不匹配
|
||||||
|
|
||||||
|
func greet(name) => "Hello, " + name + "!"
|
||||||
|
println(greet("Fig")) // 输出: Hello, Fig!
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✨ 核心特性
|
||||||
|
|
||||||
|
### 🎭 动态与静态的融合
|
||||||
|
变量默认是动态的(`Any` 类型),但你可以随时用 `:=` 或 `: Type` 锁定类型,享受静态检查的安全感。
|
||||||
|
|
||||||
|
```fig
|
||||||
|
var any; // 类型为 Any
|
||||||
|
any = 42; // 现在指向 Int
|
||||||
|
any = [1,2,3]; // 现在指向 List
|
||||||
|
|
||||||
|
var safe := 100; // 推断为 Int,之后只能赋 Int 值
|
||||||
|
safe = 200; // ✅
|
||||||
|
// safe = "oops" // ❌ 编译错误
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔗 基于引用的值语义
|
||||||
|
所有对象都是不可变的,变量只是指向对象的“名字”。多个变量可以共享同一个对象,修改操作会创建新对象,但复杂类型(如列表)的修改会反映在所有引用上——这正是引用的含义。
|
||||||
|
|
||||||
|
```fig
|
||||||
|
var a := [1, 2, 3];
|
||||||
|
var b := a; // b 和 a 指向同一个列表
|
||||||
|
a[0] = 99; // 修改列表内容
|
||||||
|
println(b) // 输出: [99, 2, 3] —— 因为 b 也看到了变化
|
||||||
|
|
||||||
|
// 需要真正的副本?用深拷贝 (语法可能变动)
|
||||||
|
var c := new List{a}; // 深拷贝 a
|
||||||
|
c[0] = 0;
|
||||||
|
println(a) // 仍是 [99, 2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🏗 面向对象特性 (OOP)
|
||||||
|
Fig 提供基于结构体的轻量面向对象支持,通过接口实现多态。
|
||||||
|
|
||||||
|
- **结构体即类**:用 `struct` 定义,可包含字段和方法
|
||||||
|
- **访问控制**:`public` 关键字,默认为私有
|
||||||
|
- **简洁构造**:
|
||||||
|
```fig
|
||||||
|
var p1 := new Point{1, 2}; // 位置参数
|
||||||
|
var p2 := new Point{x: 2, y: 3}; // 命名参数
|
||||||
|
var x := 5, y := 10;
|
||||||
|
var p3 := new Point{y, x}; // 变量名简写,自动匹配字段
|
||||||
|
```
|
||||||
|
- **接口与实现**:
|
||||||
|
```fig
|
||||||
|
interface Drawable { draw() -> String; }
|
||||||
|
struct Circle { radius: Double }
|
||||||
|
impl Drawable for Circle {
|
||||||
|
draw() => "⚪ 半径: " + radius;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **无继承,用接口实现多态**(当前为隐式 `this`,未来可能改为显式 `self.xxx`)
|
||||||
|
|
||||||
|
### 🧩 函数式特性
|
||||||
|
函数是一等公民,支持闭包和简洁的箭头语法。
|
||||||
|
|
||||||
|
```fig
|
||||||
|
func multiplier(factor) {
|
||||||
|
return func (n) => n * factor; // 闭包(upvalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
var double = multiplier(2);
|
||||||
|
println(double(5)) // 输出: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### ⚡ 高性能实现
|
||||||
|
**递归 fib(30) 仅需 28ms** (i5-13490f, Windows 11, DDR4 2667, clang 21.1.8, libc++, C++23) —— 在动态语言中表现优异。
|
||||||
|
|
||||||
|
Fig 通过一系列底层优化实现高性能:
|
||||||
|
- **寄存器虚拟机**:替代树遍历解释器,执行效率大幅提升。
|
||||||
|
- **FastCall 优化**:全局普通函数直接调用原型(proto),避免运行时解包开销(传统调用需检查是否为函数对象并解包)。
|
||||||
|
- **Window Slicing**(源自 Lua):栈滑动窗口技术,高效管理函数调用和闭包(upvalue),减少内存分配。
|
||||||
|
- **Upvalue 机制**:轻量闭包实现,让函数式编程无性能负担。
|
||||||
|
- **Computed Goto**:利用 GCC/Clang 扩展,加速字节码分发。
|
||||||
|
- **NaN-Boxing**:高效存储和类型判断,提升动态类型性能。
|
||||||
|
|
||||||
|
#### 性能对比
|
||||||
|
以下为递归计算 fib(30) 的粗略对比(不同环境可能存在差异,仅供参考):
|
||||||
|
|
||||||
|
| 语言类型 | 语言 | 时间 (ms) | 备注 |
|
||||||
|
|--------|------|----------|------|
|
||||||
|
| AOT 编译 | C (clang -O2) | ~0.05 | 纳秒级,作为速度参照 |
|
||||||
|
| AOT 编译 | Rust (--release) | ~0.06 | 同上 |
|
||||||
|
| **动态语言** | **Fig** | **~28** | **寄存器 VM + 上述优化** |
|
||||||
|
| 动态语言 | Lua 5.4 | ~35 | 经典的动态语言 |
|
||||||
|
| 动态语言 | Python 3.11 | ~450 | CPython |
|
||||||
|
| 动态语言 | Node.js 20 | ~60 | V8 引擎 |
|
||||||
|
| 动态语言 | Ruby 3.2 | ~800 | CRuby |
|
||||||
|
|
||||||
|
*Fig 在动态语言阵营中表现出色,接近 Lua 和 Node.js 的水平,远超 Python 和 Ruby。*
|
||||||
|
|
||||||
|
## 🚀 快速上手
|
||||||
|
|
||||||
|
### 安装
|
||||||
|
|
||||||
|
#### 方法一:下载预编译二进制
|
||||||
|
从 [主仓库 Releases](https://git.fig-lang.cn/PuqiAR/Fig/releases) 或 [GitHub 镜像](https://github.com/PuqiAR/Fig/releases) 下载对应平台的二进制,解压后加入 PATH。
|
||||||
|
|
||||||
|
#### 方法二:使用 xmake 从源码编译
|
||||||
|
```bash
|
||||||
|
# 克隆主仓库(GitHub 为镜像)
|
||||||
|
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||||
|
cd Fig
|
||||||
|
|
||||||
|
# 安装 xmake(如未安装):https://xmake.io
|
||||||
|
|
||||||
|
# 编译(必须用 clang,因为 computed goto 需要编译器支持)
|
||||||
|
xmake f --toolchain=clang
|
||||||
|
xmake
|
||||||
|
|
||||||
|
# 编译完成后,二进制位于 build/ 目录下
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行第一个 Fig 程序
|
||||||
|
创建文件 `hello.fig`:
|
||||||
|
```fig
|
||||||
|
import std.io; // io 模块必须导入,println 等函数位于其中
|
||||||
|
println("Hello, Fig!");
|
||||||
|
```
|
||||||
|
运行:
|
||||||
|
```bash
|
||||||
|
./build/fig hello.fig
|
||||||
|
```
|
||||||
|
或已加入 PATH:
|
||||||
|
```bash
|
||||||
|
fig hello.fig
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 当前状态与路线图
|
||||||
|
|
||||||
|
| 状态 | 模块 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| ✅ | 前端 | 词法/语法分析、AST、语义分析 |
|
||||||
|
| ✅ | 核心语义 | 变量、函数、闭包、结构体、接口、类型系统 |
|
||||||
|
| ✅ | 内置类型 | 13 种内置类型(Any, Null, Int, String, Bool, Double, Function, StructType, StructInstance, List, Map, Module, InterfaceType) |
|
||||||
|
| 🚧 | 寄存器虚拟机 | 原树遍历解释器已废弃,VM 开发中 |
|
||||||
|
| 🚧 | 垃圾回收 | 基础 GC 已实现,正在优化 |
|
||||||
|
| 📝 | 标准库 | IO 已初步可用,更多库待完善 |
|
||||||
|
| 📝 | FFI | 外部函数接口(C ABI) |
|
||||||
|
| 📝 | LSP | 语言服务器协议支持 |
|
||||||
|
|
||||||
|
## 📚 文档与社区
|
||||||
|
|
||||||
|
- **文档**:查看 [`/docs`](./docs) 目录(持续更新)
|
||||||
|
- **示例代码**:[`/ExampleCodes`](./ExampleCodes) 包含各种用法演示
|
||||||
|
- **贡献**:欢迎提交 Issue 或 PR(主仓库)
|
||||||
|
- 报告 bug
|
||||||
|
- 讨论特性
|
||||||
|
- 改进文档
|
||||||
|
- **许可证**:MIT © PuqiAR
|
||||||
|
- **作者**:PuqiAR · [im@puqiar.top](mailto:im@puqiar.top)
|
||||||
|
- **相关链接**:
|
||||||
|
- [主仓库](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||||
|
- [GitHub 镜像](https://github.com/PuqiAR/Fig)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Fig** 还在快速迭代中,如果你对它感兴趣,不妨试一试,或者加入我们一起塑造它的未来!
|
||||||
3
compile_flags.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-std=c++2b
|
||||||
|
-static
|
||||||
|
-stdlib=libc++
|
||||||
BIN
docs/FigRuntimeValueSystem.png
Normal file
|
After Width: | Height: | Size: 633 KiB |
24
docs/zh_CN/01-简介.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Fig 语言简介
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
Fig 是一门动态类型、解释执行的编程语言,专注于简洁语法和实用的语言特性。它采用树遍历解释器架构,支持多种编程范式。
|
||||||
|
|
||||||
|
## 实际观察到的特性
|
||||||
|
1. **解释执行**:基于 AST 的树遍历解释器,无编译步骤
|
||||||
|
2. **动态类型系统**:运行时类型检查,支持类型注解但不强制
|
||||||
|
3. **混合范式**:支持函数式、面向对象和命令式风格
|
||||||
|
4. **模块系统**:支持代码组织和复用
|
||||||
|
5. **内置类型**:整数、浮点数、字符串、列表、映射等
|
||||||
|
6. **垃圾回收**:基于引用计数的自动内存管理
|
||||||
|
|
||||||
|
## 语言设计特点
|
||||||
|
- **渐进类型**:支持类型注解但不强制,兼顾灵活性和可读性
|
||||||
|
- **一等公民函数**:函数可以作为参数传递和返回
|
||||||
|
- **闭包支持**:完整的词法作用域和闭包
|
||||||
|
- **错误处理**:异常机制和 try-catch 结构
|
||||||
|
- **可变与不可变**:const/var 区分,平衡安全与灵活
|
||||||
|
|
||||||
|
## 目标应用场景
|
||||||
|
- 脚本编写和自动化任务
|
||||||
|
- 教育用途和学习编程
|
||||||
|
- 配置语言和DSL
|
||||||
82
docs/zh_CN/02-快速开始.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# 快速开始
|
||||||
|
|
||||||
|
## 运行 Fig 程序
|
||||||
|
|
||||||
|
Fig 语言通过解释器直接执行源代码文件。基本执行命令如下:
|
||||||
|
|
||||||
|
`./Fig 你的脚本.fig`
|
||||||
|
|
||||||
|
### 查看帮助和版本
|
||||||
|
|
||||||
|
显示帮助信息:
|
||||||
|
`./Fig -h` 或 `./Fig --help`
|
||||||
|
|
||||||
|
显示版本信息:
|
||||||
|
`./Fig -v` 或 `./Fig --version`
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
创建一个名为 `hello.fig` 的文件,内容为:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import std.io;
|
||||||
|
io.println("Hello, Fig!");
|
||||||
|
```
|
||||||
|
|
||||||
|
在终端中运行:
|
||||||
|
`./Fig hello.fig`
|
||||||
|
|
||||||
|
你会看到输出:`Hello, Fig!`
|
||||||
|
|
||||||
|
## 程序示例
|
||||||
|
|
||||||
|
### 简单表达式程序
|
||||||
|
|
||||||
|
Fig 程序可以只包含表达式:
|
||||||
|
|
||||||
|
```go
|
||||||
|
1 + 2 * 3
|
||||||
|
```
|
||||||
|
|
||||||
|
运行此程序会输出计算结果 `7`。
|
||||||
|
|
||||||
|
### 带变量的程序
|
||||||
|
|
||||||
|
```go
|
||||||
|
var x = 10;
|
||||||
|
var y = x * 2;
|
||||||
|
y + 5
|
||||||
|
```
|
||||||
|
|
||||||
|
运行输出 `25`。
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
当源代码有语法或类型错误时,解释器会显示详细的错误信息。例如:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
An error occurred! Fig 0.4.2-alpha (2026-01-23 01:30:46)[llvm-mingw 64 bit on `Windows`]
|
||||||
|
✖ TypeError: Variable `x` expects init-value type `Int`, but got 'Double'
|
||||||
|
at 1:14 in file 'your_file.fig'
|
||||||
|
var x: Int = 3.14;
|
||||||
|
^
|
||||||
|
```
|
||||||
|
|
||||||
|
错误信息包括:
|
||||||
|
- 错误类型和描述
|
||||||
|
- 发生错误的文件和位置
|
||||||
|
- 相关的堆栈跟踪信息
|
||||||
|
|
||||||
|
## 运行流程
|
||||||
|
|
||||||
|
1. **编写代码**:使用任何文本编辑器创建 `.fig` 文件
|
||||||
|
2. **保存文件**:确保文件扩展名为 `.fig`
|
||||||
|
3. **执行程序**:在终端中运行 `./Fig 文件名.fig`
|
||||||
|
4. **查看结果**:程序输出显示在终端中
|
||||||
|
5. **调试错误**:根据错误信息修改代码
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- Fig 源文件必须使用 UTF-8 编码
|
||||||
|
- 语句通常以分号结束,但程序最后一条表达式可以省略分号
|
||||||
|
- 文件路径可以包含空格,但建议使用引号包裹:`./Fig "my script.fig"`
|
||||||
213
docs/zh_CN/03-基础语法.md
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
# 基础语法
|
||||||
|
|
||||||
|
## 注释
|
||||||
|
|
||||||
|
Fig 支持两种注释格式:
|
||||||
|
|
||||||
|
单行注释:
|
||||||
|
```go
|
||||||
|
// 这是一个单行注释
|
||||||
|
var x = 10; // 注释可以在语句后面
|
||||||
|
```
|
||||||
|
|
||||||
|
多行注释:
|
||||||
|
```go
|
||||||
|
/* 这是一个
|
||||||
|
多行注释 */
|
||||||
|
/* 注释内可以有 // 嵌套的单行注释 */
|
||||||
|
```
|
||||||
|
|
||||||
|
注释不能嵌套多个多行注释:`/* /* 嵌套 */ */` 会导致错误。
|
||||||
|
|
||||||
|
## 变量声明
|
||||||
|
|
||||||
|
### 可变变量
|
||||||
|
```go
|
||||||
|
var name = "Fig";
|
||||||
|
var count = 0;
|
||||||
|
count = count + 1; // 可以重新赋值
|
||||||
|
```
|
||||||
|
|
||||||
|
### 常量
|
||||||
|
```go
|
||||||
|
const PI = 3.14159;
|
||||||
|
const MAX_SIZE = 100;
|
||||||
|
// PI = 3.14; // 错误:常量不能重新赋值
|
||||||
|
```
|
||||||
|
|
||||||
|
常量必须在声明时初始化。
|
||||||
|
|
||||||
|
### 类型注解(可选)
|
||||||
|
```go
|
||||||
|
var name: String = "Fig";
|
||||||
|
var age: Int = 14;
|
||||||
|
const VERSION: Double = 0.4;
|
||||||
|
```
|
||||||
|
|
||||||
|
类型注解提供额外的类型信息,但解释器会在运行时进行类型检查。
|
||||||
|
|
||||||
|
## 标识符命名
|
||||||
|
|
||||||
|
### 规则
|
||||||
|
- 可以包含字母、数字和下划线
|
||||||
|
- 不能以数字开头
|
||||||
|
- 区分大小写
|
||||||
|
- 支持 Unicode 字符
|
||||||
|
|
||||||
|
### 有效示例
|
||||||
|
```go
|
||||||
|
var count = 0;
|
||||||
|
var user_name = "Alice";
|
||||||
|
var 计数器 = 0; // 中文标识符
|
||||||
|
var π = 3.14159; // Unicode 符号
|
||||||
|
var _private = true; // 以下划线开头
|
||||||
|
```
|
||||||
|
|
||||||
|
### 无效示例
|
||||||
|
```
|
||||||
|
var 123abc = 0; // 不能以数字开头
|
||||||
|
var my-var = 0; // 不能包含连字符
|
||||||
|
```
|
||||||
|
|
||||||
|
## 基本字面量
|
||||||
|
|
||||||
|
### 数字
|
||||||
|
```go
|
||||||
|
// 整数
|
||||||
|
var a = 42; // 十进制
|
||||||
|
var b = -100; // 负数
|
||||||
|
var c = 0; // 零
|
||||||
|
|
||||||
|
// 浮点数
|
||||||
|
var d = 3.14;
|
||||||
|
var e = 1.0;
|
||||||
|
var f = -0.5;
|
||||||
|
var g = 1.23e-10; // 科学计数法
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字符串
|
||||||
|
```go
|
||||||
|
var s1 = "Hello";
|
||||||
|
var s2 = "World";
|
||||||
|
var s3 = "包含\"引号\"的字符串"; // 转义引号
|
||||||
|
var s4 = "第一行\n第二行"; // 转义换行符
|
||||||
|
```
|
||||||
|
|
||||||
|
多行字符串直接跨行书写:
|
||||||
|
```rust
|
||||||
|
var message = "这是一个
|
||||||
|
多行字符串
|
||||||
|
可以包含多行内容";
|
||||||
|
```
|
||||||
|
|
||||||
|
### 布尔值
|
||||||
|
```go
|
||||||
|
var yes = true;
|
||||||
|
var no = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 空值
|
||||||
|
```dart
|
||||||
|
var nothing = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 分号使用
|
||||||
|
|
||||||
|
所有语句都必须以分号结束:
|
||||||
|
|
||||||
|
```go
|
||||||
|
var x = 10; // 正确
|
||||||
|
var y = 20 // 错误:缺少分号
|
||||||
|
|
||||||
|
func add(a, b) {
|
||||||
|
return a + b; // return 语句需要分号
|
||||||
|
} // 函数体右花括号后不需要分号
|
||||||
|
```
|
||||||
|
|
||||||
|
表达式作为独立语句时也需要分号:
|
||||||
|
```go
|
||||||
|
1 + 2 * 3; // 表达式语句需要分号
|
||||||
|
io.println("test"); // 函数调用语句需要分号
|
||||||
|
```
|
||||||
|
|
||||||
|
## 表达式与语句
|
||||||
|
|
||||||
|
### 表达式
|
||||||
|
表达式会产生一个值(包括 `null`):
|
||||||
|
```go
|
||||||
|
1 + 2 // 值为 3
|
||||||
|
x * y // 值为乘积
|
||||||
|
funcCall(arg) // 值为函数返回值
|
||||||
|
```
|
||||||
|
|
||||||
|
### 语句
|
||||||
|
语句执行操作但不产生值:
|
||||||
|
```go
|
||||||
|
var x = 10; // 声明语句
|
||||||
|
if condition {} // 条件语句
|
||||||
|
return value; // 返回语句
|
||||||
|
```
|
||||||
|
|
||||||
|
表达式可以作为语句使用(表达式语句):
|
||||||
|
```go
|
||||||
|
1 + 2; // 计算但丢弃结果
|
||||||
|
io.println("hi"); // 函数调用作为语句
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关键字
|
||||||
|
|
||||||
|
Fig 语言的关键字包括:
|
||||||
|
|
||||||
|
| 类别 | 关键字 |
|
||||||
|
| -------- | ----------------------------------------------------------- |
|
||||||
|
| 声明 | `func`, `var`, `const`, `struct`, `interface`, `import` |
|
||||||
|
| 控制流 | `if`, `else`, `while`, `for`, `return`, `break`, `continue` |
|
||||||
|
| 错误处理 | `try`, `catch`, `throw`, `finally` |
|
||||||
|
| 逻辑运算 | `and`, `or`, `not` |
|
||||||
|
| 类型相关 | `is`, `as`, `impl`, `new`, `public` |
|
||||||
|
|
||||||
|
这些关键字不能用作标识符名称。
|
||||||
|
|
||||||
|
## 操作符
|
||||||
|
|
||||||
|
### 算术运算符
|
||||||
|
`+`, `-`, `*`, `/`, `%`, `**`(幂运算)
|
||||||
|
|
||||||
|
### 比较运算符
|
||||||
|
`==`, `!=`, `<`, `>`, `<=`, `>=`
|
||||||
|
|
||||||
|
### 逻辑运算符
|
||||||
|
`and`, `or`, `not`, `&&`, `||`, `!`
|
||||||
|
|
||||||
|
### 位运算符
|
||||||
|
`&`, `|`, `^`, `~`, `<<`, `>>`
|
||||||
|
|
||||||
|
### 赋值运算符
|
||||||
|
`=`, `:=`, `+=`, `-=`, `*=`, `/=`, `%=`, `^=`
|
||||||
|
|
||||||
|
### 其他运算符
|
||||||
|
`.`(成员访问), `?`(三元运算), `...`(可变参数), `->`(箭头), `=>`(双箭头)
|
||||||
|
|
||||||
|
## 空白字符
|
||||||
|
|
||||||
|
Fig 对空白字符没有特殊要求:
|
||||||
|
- 空格和制表符可以互换使用
|
||||||
|
- 缩进不影响程序语义
|
||||||
|
- 操作符周围的空格是可选的(但建议添加以提高可读性)
|
||||||
|
|
||||||
|
## 代码示例
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 完整的程序示例
|
||||||
|
import std.io;
|
||||||
|
|
||||||
|
func calculate(a: Int, b: Int) -> Int {
|
||||||
|
var result = a * b;
|
||||||
|
return result + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = 5;
|
||||||
|
const y = 3;
|
||||||
|
var answer = calculate(x, y);
|
||||||
|
io.println("结果是:" + answer.toString());
|
||||||
|
```
|
||||||
156
docs/zh_CN/04-数据类型.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# 数据类型
|
||||||
|
|
||||||
|
## 类型系统
|
||||||
|
|
||||||
|
Fig 语言是动态类型语言,运行时进行类型检查。
|
||||||
|
|
||||||
|
## 基本类型
|
||||||
|
|
||||||
|
### Null 类型
|
||||||
|
表示空值,只有一个值 `null`。
|
||||||
|
```dart
|
||||||
|
var a = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Int 类型
|
||||||
|
64位有符号整数。
|
||||||
|
```go
|
||||||
|
var x = 42;
|
||||||
|
var y = -100;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Double 类型
|
||||||
|
双精度浮点数。
|
||||||
|
```go
|
||||||
|
var pi = 3.14;
|
||||||
|
var speed = 2.998e8;
|
||||||
|
```
|
||||||
|
|
||||||
|
### String 类型
|
||||||
|
字符串。
|
||||||
|
```go
|
||||||
|
var s1 = "Hello";
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bool 类型
|
||||||
|
布尔值,`true` 或 `false`。
|
||||||
|
```go
|
||||||
|
var yes = true;
|
||||||
|
var no = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 复合类型
|
||||||
|
|
||||||
|
### List 类型
|
||||||
|
有序集合。
|
||||||
|
```go
|
||||||
|
var list = [1, 2, 3];
|
||||||
|
var mixed = [1, "text", true];
|
||||||
|
```
|
||||||
|
可以通过 **length()** 方法获取长度,返回Int
|
||||||
|
通过 **get()** 方法获取指定下标,不存在的下标返回null
|
||||||
|
|
||||||
|
### Map 类型
|
||||||
|
键值对集合。
|
||||||
|
```go
|
||||||
|
var map = {"key": "value", "num": 42};
|
||||||
|
```
|
||||||
|
|
||||||
|
通过 **get()** 方法获取指定下标,不存在的下标返回 `null`
|
||||||
|
|
||||||
|
### Function 类型
|
||||||
|
函数。
|
||||||
|
```go
|
||||||
|
var f = func(x, y) { return x + y; };
|
||||||
|
```
|
||||||
|
|
||||||
|
## 自定义类型
|
||||||
|
|
||||||
|
### 结构体定义
|
||||||
|
```rust
|
||||||
|
struct Person {
|
||||||
|
name: String;
|
||||||
|
age: Int;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
默认字段为私有,如需声明为外部可见的字段,使用 public关键字
|
||||||
|
如
|
||||||
|
```go
|
||||||
|
struct Person {
|
||||||
|
public name: String;
|
||||||
|
public age: Int;
|
||||||
|
|
||||||
|
public func greeting()
|
||||||
|
{
|
||||||
|
io.println("hello!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 创建实例
|
||||||
|
使用 `new` 关键字:
|
||||||
|
```go
|
||||||
|
var p = new Person {name: "Alice", age: 20};
|
||||||
|
```
|
||||||
|
|
||||||
|
支持三种初始化方式:
|
||||||
|
1. 命名参数:`new Person {name: "Alice", age: 20}`
|
||||||
|
2. 位置参数:`new Person {"Alice", 20}`
|
||||||
|
3. 简写方式:`new Person {name, age}`(使用同名变量)
|
||||||
|
|
||||||
|
## 类型操作
|
||||||
|
|
||||||
|
### 类型检查
|
||||||
|
使用 `is` 运算符:
|
||||||
|
```go
|
||||||
|
var x = "hello";
|
||||||
|
io.println(x is String); // true
|
||||||
|
|
||||||
|
var p = new Person {name: "Alice", age: 20};
|
||||||
|
io.println(p is Person); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取类型
|
||||||
|
使用 `value.type()` 函数(获取内部类型):
|
||||||
|
```go
|
||||||
|
import std.value;
|
||||||
|
|
||||||
|
var num = 42;
|
||||||
|
var str = "hello";
|
||||||
|
var lst = [1, 2, 3];
|
||||||
|
|
||||||
|
io.println(value.type(num)); // 获取 num 的类型
|
||||||
|
io.println(value.type(str)); // 获取 str 的类型
|
||||||
|
io.println(value.type(lst)); // 获取 lst 的类型
|
||||||
|
```
|
||||||
|
|
||||||
|
## 类型转换
|
||||||
|
|
||||||
|
### 转换函数
|
||||||
|
通过 `std.value` 模块:
|
||||||
|
```go
|
||||||
|
import std.value;
|
||||||
|
|
||||||
|
var str = value.string_from(42); // "42"
|
||||||
|
var num = value.int_parse("123"); // 123
|
||||||
|
var dbl = value.double_parse("3.14"); // 3.14
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
### try-catch 语法
|
||||||
|
```rust
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 可能抛出异常的代码
|
||||||
|
}
|
||||||
|
catch(e: ErrorType)
|
||||||
|
{
|
||||||
|
// 处理特定类型的错误
|
||||||
|
}
|
||||||
|
catch(e: AnotherError)
|
||||||
|
{
|
||||||
|
// 处理另一种错误
|
||||||
|
}
|
||||||
|
```
|
||||||
289
docs/zh_CN/05-函数.md
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
# 函数
|
||||||
|
|
||||||
|
## 函数定义
|
||||||
|
|
||||||
|
### 基本语法
|
||||||
|
```go
|
||||||
|
func 函数名(参数列表) -> 返回类型 {
|
||||||
|
函数体
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
```go
|
||||||
|
// 简单函数
|
||||||
|
func greet() -> Null {
|
||||||
|
io.println("Hello!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带参数的函数
|
||||||
|
func add(a: Int, b: Int) -> Int {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 省略返回类型(默认为 Any)
|
||||||
|
func say(message: String) {
|
||||||
|
io.println(message);
|
||||||
|
// 没有 return,返回 null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参数
|
||||||
|
|
||||||
|
### 必需参数
|
||||||
|
```go
|
||||||
|
func power(base: Double, exponent: Double) -> Double {
|
||||||
|
return base ** exponent;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 默认参数
|
||||||
|
参数可以指定默认值:
|
||||||
|
```go
|
||||||
|
func createPerson(name: String, age: Int = 18) -> Null {
|
||||||
|
io.println(name + " is " + age + " years old");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
createPerson("Alice"); // 使用默认 age=18
|
||||||
|
createPerson("Bob", 25); // 指定 age=25
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可变参数
|
||||||
|
使用 `...` 表示可变参数,接收一个 List:
|
||||||
|
```go
|
||||||
|
func sum(numbers...) -> Int {
|
||||||
|
var total = 0;
|
||||||
|
var i = 0;
|
||||||
|
for i = 0; i < numbers.length(); i = i + 1 {
|
||||||
|
total = total + numbers.get(i);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
sum(1, 2, 3); // 返回 6
|
||||||
|
sum(1, 2, 3, 4, 5); // 返回 15
|
||||||
|
```
|
||||||
|
|
||||||
|
可变参数不支持类型限制,获取到的总是 List 类型。
|
||||||
|
|
||||||
|
## 返回值
|
||||||
|
|
||||||
|
### 显式返回
|
||||||
|
使用 `return` 语句返回值:
|
||||||
|
```go
|
||||||
|
func max(a: Int, b: Int) -> Int {
|
||||||
|
if a > b {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 无返回值
|
||||||
|
函数如果没有 return 语句,返回 null:
|
||||||
|
```go
|
||||||
|
func log(message: String) -> Null {
|
||||||
|
io.println("[LOG] " + message);
|
||||||
|
// 函数结束,返回 null
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(data: List) {
|
||||||
|
if data.length() == 0 {
|
||||||
|
return; // 提前返回 null
|
||||||
|
}
|
||||||
|
// 处理数据...
|
||||||
|
// 函数结束,返回 null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 函数作为值
|
||||||
|
|
||||||
|
### 函数赋值
|
||||||
|
函数是一等公民,可以赋值给变量:
|
||||||
|
```go
|
||||||
|
var addFunc = func(a: Int, b: Int) -> Int {
|
||||||
|
return a + b;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
var result = addFunc(3, 4); // result = 7
|
||||||
|
```
|
||||||
|
|
||||||
|
### 函数作为参数
|
||||||
|
```go
|
||||||
|
func applyOperation(x: Int, y: Int, op: Function) -> Int {
|
||||||
|
return op(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiply(a: Int, b: Int) -> Int {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
var product = applyOperation(3, 4, multiply); // product = 12
|
||||||
|
```
|
||||||
|
|
||||||
|
### 函数作为返回值
|
||||||
|
```go
|
||||||
|
func makeMultiplier(factor: Int) -> Function {
|
||||||
|
return func(x: Int) -> Int {
|
||||||
|
return x * factor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
var double = makeMultiplier(2);
|
||||||
|
var triple = makeMultiplier(3);
|
||||||
|
|
||||||
|
io.println(double(5)); // 输出 10
|
||||||
|
io.println(triple(5)); // 输出 15
|
||||||
|
```
|
||||||
|
|
||||||
|
## 匿名函数
|
||||||
|
|
||||||
|
### Lambda 表达式
|
||||||
|
```go
|
||||||
|
// 完整形式
|
||||||
|
var square = func(x: Int) {
|
||||||
|
return x * x;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 简写形式(单表达式)
|
||||||
|
var squareShort = func(x: Int) => x * x;
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
io.println(square(5)); // 输出 25
|
||||||
|
io.println(squareShort(5)); // 输出 25
|
||||||
|
```
|
||||||
|
|
||||||
|
### 立即调用函数表达式
|
||||||
|
```go
|
||||||
|
var result = func(x: Int, y: Int){
|
||||||
|
return x + y;
|
||||||
|
}(3, 4); // result = 7
|
||||||
|
```
|
||||||
|
|
||||||
|
## 闭包
|
||||||
|
|
||||||
|
### 捕获外部变量
|
||||||
|
函数可以捕获其定义作用域中的变量:
|
||||||
|
```go
|
||||||
|
func makeCounter() -> Function {
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
return func(){
|
||||||
|
count = count + 1;
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var counter = makeCounter();
|
||||||
|
io.println(counter()); // 输出 1
|
||||||
|
io.println(counter()); // 输出 2
|
||||||
|
io.println(counter()); // 输出 3
|
||||||
|
```
|
||||||
|
|
||||||
|
每个闭包有自己独立的捕获变量:
|
||||||
|
```go
|
||||||
|
var c1 = makeCounter();
|
||||||
|
var c2 = makeCounter();
|
||||||
|
|
||||||
|
io.println(c1()); // 输出 1
|
||||||
|
io.println(c1()); // 输出 2
|
||||||
|
io.println(c2()); // 输出 1(独立的计数)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 递归函数
|
||||||
|
|
||||||
|
函数可以调用自身:
|
||||||
|
```go
|
||||||
|
func factorial(n: Int) -> Int {
|
||||||
|
if n <= 1 {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return n * factorial(n - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
io.println(factorial(5)); // 输出 120
|
||||||
|
```
|
||||||
|
|
||||||
|
### 嵌套函数定义
|
||||||
|
```go
|
||||||
|
func outer(x: Int) -> Int {
|
||||||
|
func inner(y: Int) -> Int {
|
||||||
|
return y * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inner(x) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
io.println(outer(10)); // 输出 21
|
||||||
|
```
|
||||||
|
|
||||||
|
## 函数调用
|
||||||
|
|
||||||
|
### 普通调用
|
||||||
|
```go
|
||||||
|
func calculate(a: Int, b: Int, c: Int) -> Int {
|
||||||
|
return a + b * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 位置参数调用
|
||||||
|
var result = calculate(1, 2, 3); // 1 + 2*3 = 7
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法调用语法
|
||||||
|
对象的方法调用:
|
||||||
|
```go
|
||||||
|
var list = [1, 2, 3];
|
||||||
|
var length = list.length(); // 方法调用
|
||||||
|
```
|
||||||
|
|
||||||
|
## 函数示例
|
||||||
|
|
||||||
|
### 实用函数组合
|
||||||
|
```go
|
||||||
|
import std.io;
|
||||||
|
|
||||||
|
// 高阶函数示例
|
||||||
|
func compose(f: Function, g: Function) -> Function {
|
||||||
|
return func(x: Any) -> Any {
|
||||||
|
return f(g(x));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
func addOne(x: Int) -> Int {
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
func double(x: Int) -> Int {
|
||||||
|
return x * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var addThenDouble = compose(double, addOne);
|
||||||
|
io.println(addThenDouble(5)); // (5+1)*2 = 12
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回调函数模式
|
||||||
|
```go
|
||||||
|
func processData(data: List, callback: Function) -> Null {
|
||||||
|
var i = 0;
|
||||||
|
for i = 0; i < data.length(); i = i + 1 {
|
||||||
|
var result = callback(data.get(i));
|
||||||
|
io.println("处理结果: " + result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用
|
||||||
|
var numbers = [1, 2, 3, 4, 5];
|
||||||
|
processData(numbers, func(x){
|
||||||
|
return x * x;
|
||||||
|
});
|
||||||
|
```
|
||||||
126
docs/zh_CN/06-面对对象.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# 面对对象
|
||||||
|
|
||||||
|
> Fig中只有结构体(`struct`) 遵循go圣经 组合由于继承 (struct组合尚未推出)
|
||||||
|
|
||||||
|
## 结构体定义
|
||||||
|
完整语法:
|
||||||
|
```cpp
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
public x: Int; // 公开字段
|
||||||
|
public y: Int; // 公开字段
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Person
|
||||||
|
{
|
||||||
|
name: String; // 私有字段,无法被外部访问
|
||||||
|
age: Int; // 私有字段,无法被外部访问
|
||||||
|
sex: String = "Airplane"; // 私有字段,无法被外部访问
|
||||||
|
|
||||||
|
public func getName() -> String // 公开类函数
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 结构体初始化
|
||||||
|
语法:
|
||||||
|
```go
|
||||||
|
|
||||||
|
// 位置参数构造
|
||||||
|
var person := new Person{
|
||||||
|
"Fig", // name
|
||||||
|
1, // age
|
||||||
|
"Language" // sex
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 命名参数构造模式,可以无序
|
||||||
|
var person := new Person{
|
||||||
|
name: "Fig",
|
||||||
|
age: 1,
|
||||||
|
sex: "Language"
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 语法糖:同名变量构造
|
||||||
|
const name := "Fig";
|
||||||
|
const age := 1;
|
||||||
|
const sex := "Language";
|
||||||
|
|
||||||
|
var person := new Person{sex, name, age}; // 可以无序,自动匹配
|
||||||
|
```
|
||||||
|
|
||||||
|
请注意,同名变量构造(shorthand)模式请规范使用:
|
||||||
|
|
||||||
|
示例:定义
|
||||||
|
```cpp
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
public x;
|
||||||
|
public y;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
使用:
|
||||||
|
```go
|
||||||
|
var a := Point{1,2};
|
||||||
|
io.println(a.x, a.y); // 1 2
|
||||||
|
|
||||||
|
var x := 7;
|
||||||
|
var y := 6;
|
||||||
|
var b := Point{y, x};
|
||||||
|
io.println(b.x, b.y); // 7 6
|
||||||
|
// ??
|
||||||
|
```
|
||||||
|
|
||||||
|
使用该模式最好有序构造,如果你清楚你在做什么,完全可以利用它
|
||||||
|
|
||||||
|
## 结构体运算符重载
|
||||||
|
|
||||||
|
**目前不支持**
|
||||||
|
|
||||||
|
## 接口与实现
|
||||||
|
`interface` 与 `implement`
|
||||||
|
|
||||||
|
### 定义接口
|
||||||
|
```go
|
||||||
|
interface Printable
|
||||||
|
{
|
||||||
|
toString() -> String;
|
||||||
|
getName() -> String
|
||||||
|
{
|
||||||
|
return "Printable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
使用 `interface` + 名字 {内容}定义结构体
|
||||||
|
方法签名为 `method() -> type`,其中必须提供返回类型,这是约定。
|
||||||
|
提供默认方法将在子类实现为实现情况下自动替补
|
||||||
|
|
||||||
|
### 实现
|
||||||
|
```rust
|
||||||
|
struct Person
|
||||||
|
{
|
||||||
|
name: String;
|
||||||
|
age: Int;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printable for Person
|
||||||
|
{
|
||||||
|
toString()
|
||||||
|
{
|
||||||
|
return name + ":" + age;
|
||||||
|
}
|
||||||
|
getName()
|
||||||
|
{
|
||||||
|
return "Person";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
实现时不需要也不允许提供返回类型,必须与`interface`约定一致
|
||||||
|
内部通过动态派发 vtable实现
|
||||||
33
src/Ast/Ast.hpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Ast.hpp
|
||||||
|
@brief Ast总链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Expr/CallExpr.hpp>
|
||||||
|
#include <Ast/Expr/IdentiExpr.hpp>
|
||||||
|
#include <Ast/Expr/IndexExpr.hpp>
|
||||||
|
#include <Ast/Expr/InfixExpr.hpp>
|
||||||
|
#include <Ast/Expr/LambdaExpr.hpp>
|
||||||
|
#include <Ast/Expr/LiteralExpr.hpp>
|
||||||
|
#include <Ast/Expr/MemberExpr.hpp>
|
||||||
|
#include <Ast/Expr/NewExpr.hpp>
|
||||||
|
#include <Ast/Expr/PrefixExpr.hpp>
|
||||||
|
#include <Ast/Expr/TernaryExpr.hpp>
|
||||||
|
#include <Ast/Expr/PostfixExpr.hpp>
|
||||||
|
|
||||||
|
#include <Ast/Stmt/ControlFlowStmts.hpp>
|
||||||
|
#include <Ast/Stmt/ExprStmt.hpp>
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
#include <Ast/Stmt/IfStmt.hpp>
|
||||||
|
#include <Ast/Stmt/ImplStmt.hpp>
|
||||||
|
#include <Ast/Stmt/InterfaceDefStmt.hpp>
|
||||||
|
#include <Ast/Stmt/StructDefStmt.hpp>
|
||||||
|
#include <Ast/Stmt/ForStmt.hpp>
|
||||||
|
#include <Ast/Stmt/ImportStmt.hpp>
|
||||||
|
#include <Ast/Stmt/VarDecl.hpp>
|
||||||
|
#include <Ast/Stmt/WhileStmt.hpp>
|
||||||
|
#include <Ast/TypeExpr.hpp>
|
||||||
167
src/Ast/Base.hpp
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Base.hpp
|
||||||
|
@brief AstNode基类定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Sema/Type.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class AstType : std::uint8_t
|
||||||
|
{
|
||||||
|
AstNode,
|
||||||
|
Program,
|
||||||
|
Expr,
|
||||||
|
Stmt,
|
||||||
|
BlockStmt,
|
||||||
|
|
||||||
|
/* Expressions */
|
||||||
|
IdentiExpr,
|
||||||
|
LiteralExpr,
|
||||||
|
PrefixExpr,
|
||||||
|
InfixExpr,
|
||||||
|
IndexExpr,
|
||||||
|
CallExpr,
|
||||||
|
MemberExpr, // obj.prop
|
||||||
|
NewExpr, // new Point{}
|
||||||
|
LambdaExpr,
|
||||||
|
TernaryExpr, // cond ? then : else
|
||||||
|
PostfixExpr, // expr++ / expr--
|
||||||
|
|
||||||
|
/* Statements */
|
||||||
|
ExprStmt,
|
||||||
|
VarDecl,
|
||||||
|
IfStmt,
|
||||||
|
ElseIfStmt,
|
||||||
|
WhileStmt,
|
||||||
|
FnDefStmt,
|
||||||
|
StructDefStmt,
|
||||||
|
InterfaceDefStmt,
|
||||||
|
ImplStmt, // impl Document for File {}
|
||||||
|
ReturnStmt,
|
||||||
|
BreakStmt,
|
||||||
|
ContinueStmt,
|
||||||
|
ForStmt, // for loop
|
||||||
|
ImportStmt, // import
|
||||||
|
|
||||||
|
/* Type Expressions */
|
||||||
|
TypeExpr,
|
||||||
|
NamedTypeExpr, // 废弃,用 IdentiExpr/MemberExpr/ApplyExpr 替代
|
||||||
|
NullableTypeExpr, // 废弃,用 NullableExpr 替代
|
||||||
|
FnTypeExpr,
|
||||||
|
ApplyExpr, // 泛型实例化: List<Int>
|
||||||
|
NullableExpr, // 可空后缀: Int?
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstNode
|
||||||
|
{
|
||||||
|
AstType type = AstType::AstNode;
|
||||||
|
SourceLocation location;
|
||||||
|
|
||||||
|
virtual String toString() const = 0;
|
||||||
|
virtual ~AstNode() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr : public AstNode
|
||||||
|
{
|
||||||
|
// 语义分析后填充
|
||||||
|
Type resolvedType;
|
||||||
|
|
||||||
|
Expr()
|
||||||
|
{
|
||||||
|
type = AstType::Expr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stmt : public AstNode
|
||||||
|
{
|
||||||
|
bool isPublic = false;
|
||||||
|
Stmt()
|
||||||
|
{
|
||||||
|
type = AstType::Stmt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Program final : public AstNode
|
||||||
|
{
|
||||||
|
DynArray<Stmt *> nodes;
|
||||||
|
Program()
|
||||||
|
{
|
||||||
|
type = AstType::Program;
|
||||||
|
}
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return "<Program>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockStmt final : public Stmt
|
||||||
|
{
|
||||||
|
DynArray<Stmt *> nodes;
|
||||||
|
BlockStmt()
|
||||||
|
{
|
||||||
|
type = AstType::BlockStmt;
|
||||||
|
}
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return "<BlockStmt>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Type Expressions (inherit Expr — 类型即值) ---
|
||||||
|
|
||||||
|
struct TypeExpr : public Expr
|
||||||
|
{
|
||||||
|
TypeExpr() { type = AstType::TypeExpr; }
|
||||||
|
virtual ~TypeExpr() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ApplyExpr: 泛型实例化,List<Int> → ApplyExpr(base, [Int])
|
||||||
|
struct ApplyExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *base; // 基础类型表达式
|
||||||
|
DynArray<Expr *> args; // 泛型参数
|
||||||
|
|
||||||
|
ApplyExpr() { type = AstType::ApplyExpr; }
|
||||||
|
ApplyExpr(Expr *_base, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||||
|
base(_base), args(std::move(_args))
|
||||||
|
{
|
||||||
|
type = AstType::ApplyExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String s = base->toString() + "<";
|
||||||
|
for (size_t i = 0; i < args.size(); ++i)
|
||||||
|
{
|
||||||
|
if (i) s += ", ";
|
||||||
|
s += args[i]->toString();
|
||||||
|
}
|
||||||
|
s += ">";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// NullableExpr: 可空后缀 Int? → NullableExpr(Int)
|
||||||
|
struct NullableExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *inner;
|
||||||
|
|
||||||
|
NullableExpr() { type = AstType::NullableExpr; }
|
||||||
|
NullableExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||||
|
{
|
||||||
|
type = AstType::NullableExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return inner->toString() + "?";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
61
src/Ast/Expr/CallExpr.hpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/CallExpr.hpp
|
||||||
|
@brief CallExpr等定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct FnCallArgs
|
||||||
|
{
|
||||||
|
DynArray<Expr *> args;
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return args.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
String toString() const
|
||||||
|
{
|
||||||
|
String str = "(";
|
||||||
|
for (const Expr *expr : args)
|
||||||
|
{
|
||||||
|
if (expr != args.front())
|
||||||
|
{
|
||||||
|
str += ", ";
|
||||||
|
}
|
||||||
|
str += expr->toString();
|
||||||
|
}
|
||||||
|
str += ")";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *callee;
|
||||||
|
FnCallArgs args;
|
||||||
|
|
||||||
|
CallExpr()
|
||||||
|
{
|
||||||
|
type = AstType::CallExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallExpr(Expr *_callee, FnCallArgs _args, SourceLocation _location) : callee(_callee), args(std::move(_args))
|
||||||
|
{
|
||||||
|
type = AstType::CallExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<CallExpr: '{}{}'>", callee->toString(), args.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
32
src/Ast/Expr/IdentiExpr.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/IdentiExpr.hpp
|
||||||
|
@brief 标识符表达式定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Sema/Environment.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct IdentiExpr final : public Expr
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
Symbol *resolvedSymbol = nullptr; // 语义分析后填充,Compiler 唯一的依赖
|
||||||
|
|
||||||
|
IdentiExpr()
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
}
|
||||||
|
IdentiExpr(String _name, SourceLocation _location) : name(std::move(_name))
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<IdentiExpr '{}'>", name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
35
src/Ast/Expr/IndexExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/IndexExpr.hpp
|
||||||
|
@brief IndexExpr定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct IndexExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *base;
|
||||||
|
Expr *index;
|
||||||
|
|
||||||
|
IndexExpr()
|
||||||
|
{
|
||||||
|
type = AstType::IndexExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexExpr(Expr *_base, Expr *_index) : base(_base), index(_index)
|
||||||
|
{
|
||||||
|
type = AstType::IndexExpr;
|
||||||
|
location = base->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<IndexExpr: '{}[{}]'>", base->toString(), index->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
39
src/Ast/Expr/InfixExpr.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/InfixExpr.hpp
|
||||||
|
@brief 中缀表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct InfixExpr final : Expr
|
||||||
|
{
|
||||||
|
Expr *left;
|
||||||
|
BinaryOperator op;
|
||||||
|
Expr *right;
|
||||||
|
|
||||||
|
InfixExpr()
|
||||||
|
{
|
||||||
|
type = AstType::InfixExpr;
|
||||||
|
}
|
||||||
|
InfixExpr(Expr *_left, BinaryOperator _op, Expr *_right) :
|
||||||
|
left(_left), op(_op), right(_right)
|
||||||
|
{
|
||||||
|
type = AstType::InfixExpr;
|
||||||
|
location = _left->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<InfixExpr: '{}' {} '{}'>", left->toString(), magic_enum::enum_name(op), right->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
70
src/Ast/Expr/LambdaExpr.hpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/LambdaExpr.hpp
|
||||||
|
@brief Lambda表达式定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct LambdaExpr final : public Expr
|
||||||
|
{
|
||||||
|
// func (params) [-> return type] ([=> expr] / [ {stmt} ])
|
||||||
|
|
||||||
|
DynArray<Param *> params;
|
||||||
|
Expr *returnType;
|
||||||
|
AstNode *body; // expr/blockstmt
|
||||||
|
bool isExprBody;
|
||||||
|
|
||||||
|
DynArray<UpvalueInfo> upvalues;
|
||||||
|
|
||||||
|
LambdaExpr()
|
||||||
|
{
|
||||||
|
type = AstType::LambdaExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
LambdaExpr(
|
||||||
|
DynArray<Param *> _params,
|
||||||
|
Expr *_returnType,
|
||||||
|
AstNode *_body,
|
||||||
|
bool _isExprBody,
|
||||||
|
SourceLocation _location) :
|
||||||
|
params(std::move(_params)),
|
||||||
|
returnType(_returnType),
|
||||||
|
body(_body),
|
||||||
|
isExprBody(_isExprBody)
|
||||||
|
{
|
||||||
|
type = AstType::LambdaExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String specifying = "<LambdaExpr 'func (";
|
||||||
|
for (auto &p : params)
|
||||||
|
{
|
||||||
|
if (p != params.front())
|
||||||
|
{
|
||||||
|
specifying += ", ";
|
||||||
|
}
|
||||||
|
specifying += p->toString();
|
||||||
|
}
|
||||||
|
if (isExprBody)
|
||||||
|
{
|
||||||
|
specifying += ") => ";
|
||||||
|
specifying += body->toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
specifying += ") {";
|
||||||
|
specifying += body->toString();
|
||||||
|
specifying.push_back(U'}');
|
||||||
|
}
|
||||||
|
specifying += "'>";
|
||||||
|
return specifying;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
27
src/Ast/Expr/LiteralExpr.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/LiteralExpr.hpp
|
||||||
|
@brief 字面量表达式定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct LiteralExpr final : public Expr
|
||||||
|
{
|
||||||
|
Token literal;
|
||||||
|
|
||||||
|
LiteralExpr() { type = AstType::LiteralExpr; }
|
||||||
|
LiteralExpr(const Token &_literal, SourceLocation _location) : literal(_literal)
|
||||||
|
{
|
||||||
|
type = AstType::LiteralExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override {
|
||||||
|
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(literal.type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
35
src/Ast/Expr/MemberExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/MemberExpr.hpp
|
||||||
|
@brief 成员访问表达式定义:obj.member
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct MemberExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *target; // 访问对象
|
||||||
|
String name; // 成员名字
|
||||||
|
|
||||||
|
MemberExpr()
|
||||||
|
{
|
||||||
|
type = AstType::MemberExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberExpr(Expr *_t, String _n, SourceLocation _loc) : target(_t), name(std::move(_n))
|
||||||
|
{
|
||||||
|
type = AstType::MemberExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<MemberExpr {}.{}>", target->toString(), name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
50
src/Ast/Expr/NewExpr.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/ObjectInitExpr.hpp
|
||||||
|
@brief 对象初始化表达式 AST 定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct NewExpr final : public Expr
|
||||||
|
{
|
||||||
|
struct Arg
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
Expr *value;
|
||||||
|
};
|
||||||
|
Expr *typeExpr;
|
||||||
|
DynArray<Arg> args;
|
||||||
|
|
||||||
|
NewExpr()
|
||||||
|
{
|
||||||
|
type = AstType::NewExpr;
|
||||||
|
}
|
||||||
|
NewExpr(Expr *_te, DynArray<Arg> _args, SourceLocation _loc) :
|
||||||
|
typeExpr(_te), args(std::move(_args))
|
||||||
|
{
|
||||||
|
type = AstType::NewExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String res = "<NewExpr 'new " + typeExpr->toString() + "{";
|
||||||
|
for (size_t i = 0; i < args.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!args[i].name.empty())
|
||||||
|
res += args[i].name + ": ";
|
||||||
|
res += args[i].value->toString();
|
||||||
|
if (i < args.size() - 1)
|
||||||
|
res += ", ";
|
||||||
|
}
|
||||||
|
res += "}'>";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
35
src/Ast/Expr/PostfixExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/PostfixExpr.hpp
|
||||||
|
@brief expr++ / expr--
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-06-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct PostfixExpr final : public Expr
|
||||||
|
{
|
||||||
|
UnaryOperator op;
|
||||||
|
Expr *operand;
|
||||||
|
|
||||||
|
PostfixExpr() { type = AstType::PostfixExpr; }
|
||||||
|
|
||||||
|
PostfixExpr(UnaryOperator _op, Expr *_operand) :
|
||||||
|
op(_op), operand(_operand)
|
||||||
|
{
|
||||||
|
type = AstType::PostfixExpr;
|
||||||
|
location = _operand->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<PostfixExpr: {} '{}'>",
|
||||||
|
operand->toString(), magic_enum::enum_name(op));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
39
src/Ast/Expr/PrefixExpr.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/PrefixExpr.hpp
|
||||||
|
@brief 前缀表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct PrefixExpr final : Expr
|
||||||
|
{
|
||||||
|
UnaryOperator op;
|
||||||
|
Expr *operand;
|
||||||
|
|
||||||
|
PrefixExpr()
|
||||||
|
{
|
||||||
|
type = AstType::PrefixExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrefixExpr(UnaryOperator _op, Expr *_operand) :
|
||||||
|
op(_op), operand(_operand)
|
||||||
|
{
|
||||||
|
type = AstType::PrefixExpr;
|
||||||
|
location = _operand->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<PrefixExpr: {} '{}'>", magic_enum::enum_name(op), operand->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
35
src/Ast/Expr/TernaryExpr.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/TernaryExpr.hpp
|
||||||
|
@brief cond ? then : else
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-06-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct TernaryExpr final : public Expr
|
||||||
|
{
|
||||||
|
Expr *cond;
|
||||||
|
Expr *thenExpr;
|
||||||
|
Expr *elseExpr;
|
||||||
|
|
||||||
|
TernaryExpr() { type = AstType::TernaryExpr; }
|
||||||
|
|
||||||
|
TernaryExpr(Expr *_cond, Expr *_then, Expr *_else, SourceLocation _loc) :
|
||||||
|
cond(_cond), thenExpr(_then), elseExpr(_else)
|
||||||
|
{
|
||||||
|
type = AstType::TernaryExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<TernaryExpr: {} ? {} : {}>",
|
||||||
|
cond->toString(), thenExpr->toString(), elseExpr->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
193
src/Ast/Operator.cpp
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.cpp
|
||||||
|
@brief 运算符定义内函数实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, UnaryOperator> unaryOpMap{
|
||||||
|
{TokenType::Tilde, UnaryOperator::BitNot},
|
||||||
|
{TokenType::Minus, UnaryOperator::Negate},
|
||||||
|
{TokenType::Not, UnaryOperator::Not},
|
||||||
|
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
||||||
|
{TokenType::DoublePlus, UnaryOperator::Increment},
|
||||||
|
{TokenType::DoubleMinus, UnaryOperator::Decrement},
|
||||||
|
};
|
||||||
|
return unaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, BinaryOperator> binaryOpMap{
|
||||||
|
{TokenType::Plus, BinaryOperator::Add},
|
||||||
|
{TokenType::Minus, BinaryOperator::Subtract},
|
||||||
|
{TokenType::Asterisk, BinaryOperator::Multiply},
|
||||||
|
{TokenType::Slash, BinaryOperator::Divide},
|
||||||
|
{TokenType::Percent, BinaryOperator::Modulo},
|
||||||
|
|
||||||
|
{TokenType::Equal, BinaryOperator::Equal},
|
||||||
|
{TokenType::NotEqual, BinaryOperator::NotEqual},
|
||||||
|
{TokenType::Less, BinaryOperator::Less},
|
||||||
|
{TokenType::Greater, BinaryOperator::Greater},
|
||||||
|
{TokenType::LessEqual, BinaryOperator::LessEqual},
|
||||||
|
{TokenType::GreaterEqual, BinaryOperator::GreaterEqual},
|
||||||
|
|
||||||
|
{TokenType::Is, BinaryOperator::Is},
|
||||||
|
|
||||||
|
{TokenType::And, BinaryOperator::LogicalAnd},
|
||||||
|
{TokenType::Or, BinaryOperator::LogicalOr},
|
||||||
|
{TokenType::DoubleAmpersand, BinaryOperator::LogicalAnd},
|
||||||
|
{TokenType::DoublePipe, BinaryOperator::LogicalOr},
|
||||||
|
|
||||||
|
{TokenType::Power, BinaryOperator::Power},
|
||||||
|
|
||||||
|
{TokenType::Assign, BinaryOperator::Assign},
|
||||||
|
{TokenType::PlusEqual, BinaryOperator::AddAssign},
|
||||||
|
{TokenType::MinusEqual, BinaryOperator::SubAssign},
|
||||||
|
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
||||||
|
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
||||||
|
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
||||||
|
{TokenType::Caret, BinaryOperator::BitXor},
|
||||||
|
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||||
|
|
||||||
|
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||||
|
{TokenType::Ampersand, BinaryOperator::BitAnd},
|
||||||
|
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||||
|
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||||
|
|
||||||
|
{TokenType::As, BinaryOperator::As},
|
||||||
|
};
|
||||||
|
return binaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赋值 < 三元 < 逻辑或 < 逻辑与 < 位运算 < 比较 < 位移 < 加减 < 乘除 < 幂 < 一元 < 成员访问 < (后缀)
|
||||||
|
|
||||||
|
/*
|
||||||
|
暂划分:
|
||||||
|
二元运算符:0 - 20000
|
||||||
|
一元运算符:20001 - 40000
|
||||||
|
后缀/成员/其他:40001 - 60001
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<UnaryOperator, BindingPower> unbpm{
|
||||||
|
{UnaryOperator::BitNot, 20001},
|
||||||
|
{UnaryOperator::Negate, 20001},
|
||||||
|
{UnaryOperator::Not, 20001},
|
||||||
|
{UnaryOperator::AddressOf, 20001},
|
||||||
|
{UnaryOperator::Increment, 20001},
|
||||||
|
{UnaryOperator::Decrement, 20001},
|
||||||
|
};
|
||||||
|
return unbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<BinaryOperator, BindingPower> bnbpm{
|
||||||
|
{BinaryOperator::Assign, 100},
|
||||||
|
{BinaryOperator::AddAssign, 100},
|
||||||
|
{BinaryOperator::SubAssign, 100},
|
||||||
|
{BinaryOperator::MultiplyAssign, 100},
|
||||||
|
{BinaryOperator::DivideAssign, 100},
|
||||||
|
{BinaryOperator::ModuloAssign, 100},
|
||||||
|
{BinaryOperator::BitXorAssign, 100},
|
||||||
|
|
||||||
|
{BinaryOperator::LogicalOr, 500},
|
||||||
|
{BinaryOperator::LogicalAnd, 550},
|
||||||
|
|
||||||
|
{BinaryOperator::BitOr, 1000},
|
||||||
|
{BinaryOperator::BitXor, 1100},
|
||||||
|
{BinaryOperator::BitAnd, 1200},
|
||||||
|
|
||||||
|
{BinaryOperator::Equal, 2000},
|
||||||
|
{BinaryOperator::NotEqual, 2000},
|
||||||
|
|
||||||
|
{BinaryOperator::Less, 2100},
|
||||||
|
{BinaryOperator::LessEqual, 2100},
|
||||||
|
{BinaryOperator::Greater, 2100},
|
||||||
|
{BinaryOperator::GreaterEqual, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::Is, 2100},
|
||||||
|
{BinaryOperator::As, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::ShiftLeft, 3000},
|
||||||
|
{BinaryOperator::ShiftRight, 3000},
|
||||||
|
|
||||||
|
{BinaryOperator::Add, 4000},
|
||||||
|
{BinaryOperator::Subtract, 4000},
|
||||||
|
{BinaryOperator::Multiply, 4500},
|
||||||
|
{BinaryOperator::Divide, 4500},
|
||||||
|
{BinaryOperator::Modulo, 4500},
|
||||||
|
|
||||||
|
{BinaryOperator::Power, 5000},
|
||||||
|
};
|
||||||
|
return bnbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator op)
|
||||||
|
{
|
||||||
|
return GetUnaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return GetBinaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
右结合,左绑定力 >= 右
|
||||||
|
a = b = c
|
||||||
|
a = (b = c)
|
||||||
|
*/
|
||||||
|
case BinaryOperator::Assign:
|
||||||
|
case BinaryOperator::AddAssign:
|
||||||
|
case BinaryOperator::SubAssign:
|
||||||
|
case BinaryOperator::MultiplyAssign:
|
||||||
|
case BinaryOperator::DivideAssign:
|
||||||
|
case BinaryOperator::ModuloAssign:
|
||||||
|
case BinaryOperator::BitXorAssign:
|
||||||
|
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
||||||
|
|
||||||
|
case BinaryOperator::As: return GetBinaryOpLBp(op) + 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
左结合, 左绑定力 < 右
|
||||||
|
a * b * c
|
||||||
|
(a * b) * c
|
||||||
|
*/
|
||||||
|
return GetBinaryOpLBp(op) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary /* = true*/)
|
||||||
|
{
|
||||||
|
if (binary)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
return GetUnaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetUnaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
96
src/Ast/Operator.hpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.hpp
|
||||||
|
@brief 运算符定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class UnaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
BitNot, // 位运算取反 ~
|
||||||
|
Negate, // 取反 -
|
||||||
|
Not, // 逻辑非 ! / not
|
||||||
|
AddressOf, // 取引用 &
|
||||||
|
Increment, // ++
|
||||||
|
Decrement, // --
|
||||||
|
|
||||||
|
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
||||||
|
};
|
||||||
|
enum class BinaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
Add, // 加 +
|
||||||
|
Subtract, // 减 -
|
||||||
|
Multiply, // 乘 *
|
||||||
|
Divide, // 除 /
|
||||||
|
Modulo, // 取模 %
|
||||||
|
|
||||||
|
Equal, // 等于 ==
|
||||||
|
NotEqual, // 不等于 !=
|
||||||
|
Less, // 小于 <
|
||||||
|
Greater, // 大于 >
|
||||||
|
LessEqual, // 小于等于 <=
|
||||||
|
GreaterEqual, // 大于等于 >=
|
||||||
|
|
||||||
|
Is, // is操作符
|
||||||
|
|
||||||
|
LogicalAnd, // 逻辑与 && / and
|
||||||
|
LogicalOr, // 逻辑或 || / or
|
||||||
|
|
||||||
|
Power, // 幂运算 **
|
||||||
|
|
||||||
|
Assign, // 赋值(修改) =
|
||||||
|
AddAssign, // +=
|
||||||
|
SubAssign, // -=
|
||||||
|
MultiplyAssign, // *=
|
||||||
|
DivideAssign, // /=
|
||||||
|
ModuloAssign, // %=
|
||||||
|
BitXorAssign, // ^=
|
||||||
|
|
||||||
|
// 位运算
|
||||||
|
BitAnd, // 按位与 &
|
||||||
|
BitOr, // 按位或 |
|
||||||
|
BitXor, // 异或 ^
|
||||||
|
ShiftLeft, // 左移
|
||||||
|
ShiftRight, // 右移
|
||||||
|
|
||||||
|
As, // as
|
||||||
|
|
||||||
|
// 成员访问
|
||||||
|
MemberAccess, // .
|
||||||
|
|
||||||
|
Count // 哨兵,(int) Count 获得运算符数量(注意,enum必须从 0 开始且不中断)
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr unsigned int GetOperatorsSize()
|
||||||
|
{
|
||||||
|
// 获取全部运算符的数量
|
||||||
|
return static_cast<std::uint8_t>(UnaryOperator::Count) + static_cast<std::uint8_t>(BinaryOperator::Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
using BindingPower = unsigned int;
|
||||||
|
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap();
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap();
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap();
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap();
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator);
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator);
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator);
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary = true);
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &);
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &);
|
||||||
|
}; // namespace Fig
|
||||||
72
src/Ast/Stmt/ControlFlowStmts.hpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/ControlFlowStmts
|
||||||
|
@brief 控制流语句 return, break, continue 定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-27
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct ReturnStmt final : public Stmt
|
||||||
|
{
|
||||||
|
Expr *value;
|
||||||
|
|
||||||
|
ReturnStmt()
|
||||||
|
{
|
||||||
|
type = AstType::ReturnStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnStmt(Expr *_value, SourceLocation _location) : value(_value)
|
||||||
|
{
|
||||||
|
type = AstType::ReturnStmt;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<ReturnStmt '{}'>", value->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BreakStmt final : public Stmt
|
||||||
|
{
|
||||||
|
BreakStmt()
|
||||||
|
{
|
||||||
|
type = AstType::BreakStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
BreakStmt(SourceLocation _location)
|
||||||
|
{
|
||||||
|
type = AstType::BreakStmt;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return "<BreakStmt>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ContinueStmt final : public Stmt
|
||||||
|
{
|
||||||
|
ContinueStmt()
|
||||||
|
{
|
||||||
|
type = AstType::ContinueStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContinueStmt(SourceLocation _location)
|
||||||
|
{
|
||||||
|
type = AstType::ContinueStmt;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return "<ContinueStmt>";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
35
src/Ast/Stmt/ExprStmt.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
51
src/Ast/Stmt/FnDefStmt.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/FnDefStmt.hpp
|
||||||
|
@brief 函数定义 AST 节点
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Sema/Environment.hpp>
|
||||||
|
#include <Bytecode/Bytecode.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct Param : public AstNode {
|
||||||
|
String name;
|
||||||
|
Expr *typeSpecifier;
|
||||||
|
Expr *defaultValue;
|
||||||
|
Type resolvedType;
|
||||||
|
Param() { type = AstType::AstNode; }
|
||||||
|
virtual ~Param() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PosParam final : public Param {
|
||||||
|
PosParam(String _n, Expr *_ts, Expr *_dv, SourceLocation _loc) {
|
||||||
|
name = std::move(_n); typeSpecifier = _ts; defaultValue = _dv; location = std::move(_loc);
|
||||||
|
}
|
||||||
|
virtual String toString() const override { return name; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FnDefStmt final : public Stmt {
|
||||||
|
String name;
|
||||||
|
DynArray<Param *> params;
|
||||||
|
Expr *returnTypeSpecifier;
|
||||||
|
BlockStmt *body;
|
||||||
|
Type resolvedReturnType;
|
||||||
|
Symbol *resolvedSymbol = nullptr; // 连接物理符号
|
||||||
|
|
||||||
|
int protoIndex = -1; // 在CompiledModule扁平化protos的下标
|
||||||
|
DynArray<UpvalueInfo> upvalues;
|
||||||
|
|
||||||
|
FnDefStmt() { type = AstType::FnDefStmt; }
|
||||||
|
FnDefStmt(bool _p, String _n, DynArray<Param *> _pa, Expr *_rt, BlockStmt *_b, SourceLocation _loc)
|
||||||
|
: name(std::move(_n)), params(std::move(_pa)), returnTypeSpecifier(_rt), body(_b)
|
||||||
|
{
|
||||||
|
type = AstType::FnDefStmt; isPublic = _p; location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override {
|
||||||
|
return std::format("<FnDefStmt '{}'>", name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
32
src/Ast/Stmt/ForStmt.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/ForStmt.hpp
|
||||||
|
@brief for loop
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-06-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct ForStmt final : public Stmt
|
||||||
|
{
|
||||||
|
Stmt *init;
|
||||||
|
Expr *cond;
|
||||||
|
Expr *step;
|
||||||
|
BlockStmt *body;
|
||||||
|
|
||||||
|
ForStmt() { type = AstType::ForStmt; }
|
||||||
|
|
||||||
|
ForStmt(Stmt *_init, Expr *_cond, Expr *_step, BlockStmt *_body, SourceLocation _loc) :
|
||||||
|
init(_init), cond(_cond), step(_step), body(_body)
|
||||||
|
{
|
||||||
|
type = AstType::ForStmt;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override { return "<ForStmt>"; }
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
69
src/Ast/Stmt/IfStmt.hpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*!
|
||||||
|
@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
|
||||||
32
src/Ast/Stmt/ImplStmt.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/ImplStmt.hpp
|
||||||
|
@brief 实现块 AST 节点
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct ImplStmt final : public Stmt
|
||||||
|
{
|
||||||
|
Expr *interfaceType;
|
||||||
|
Expr *structType;
|
||||||
|
DynArray<FnDefStmt *> methods;
|
||||||
|
|
||||||
|
ImplStmt(Expr *_it, Expr *_st, DynArray<FnDefStmt *> _m, SourceLocation _loc) :
|
||||||
|
interfaceType(_it), structType(_st), methods(std::move(_m))
|
||||||
|
{
|
||||||
|
type = AstType::ImplStmt;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String detail =
|
||||||
|
(interfaceType ? interfaceType->toString() + " for " : "") + structType->toString();
|
||||||
|
return std::format("<ImplStmt '{}'>", detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
33
src/Ast/Stmt/ImportStmt.hpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/ImportStmt.hpp
|
||||||
|
@brief import
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-06-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct ImportStmt final : public Stmt
|
||||||
|
{
|
||||||
|
String path;
|
||||||
|
bool isFileImport;
|
||||||
|
|
||||||
|
ImportStmt() { type = AstType::ImportStmt; }
|
||||||
|
|
||||||
|
ImportStmt(String _path, bool _isFileImport, SourceLocation _loc) :
|
||||||
|
path(std::move(_path)), isFileImport(_isFileImport)
|
||||||
|
{
|
||||||
|
type = AstType::ImportStmt;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<ImportStmt '{}'{}>", path, isFileImport ? " [file]" : "");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
39
src/Ast/Stmt/InterfaceDefStmt.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/InterfaceDefStmt.hpp
|
||||||
|
@brief 接口定义 AST 节点
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct InterfaceDefStmt final : public Stmt
|
||||||
|
{
|
||||||
|
struct Method
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
DynArray<Expr*> params;
|
||||||
|
Expr *retType;
|
||||||
|
SourceLocation location;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isPublic;
|
||||||
|
String name;
|
||||||
|
DynArray<Method> methods;
|
||||||
|
|
||||||
|
InterfaceDefStmt() { type = AstType::InterfaceDefStmt; }
|
||||||
|
|
||||||
|
InterfaceDefStmt(bool _p, String _n, DynArray<Method> _m, SourceLocation _loc)
|
||||||
|
: isPublic(_p), name(std::move(_n)), methods(std::move(_m))
|
||||||
|
{
|
||||||
|
type = AstType::InterfaceDefStmt;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override { return "<InterfaceDefStmt>"; }
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
59
src/Ast/Stmt/StructDefStmt.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/StructDefStmt.hpp
|
||||||
|
@brief 结构体定义 AST 节点
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct StructDefStmt final : public Stmt
|
||||||
|
{
|
||||||
|
struct Field
|
||||||
|
{
|
||||||
|
bool isPublic;
|
||||||
|
bool typeInfer;
|
||||||
|
|
||||||
|
String name;
|
||||||
|
Expr *type;
|
||||||
|
Expr *initExpr;
|
||||||
|
};
|
||||||
|
bool isPublic;
|
||||||
|
String name;
|
||||||
|
DynArray<String> typeParameters;
|
||||||
|
DynArray<Field> fields;
|
||||||
|
DynArray<FnDefStmt *> methods;
|
||||||
|
|
||||||
|
StructDefStmt()
|
||||||
|
{
|
||||||
|
type = AstType::StructDefStmt;
|
||||||
|
}
|
||||||
|
StructDefStmt(bool _p,
|
||||||
|
String _n,
|
||||||
|
DynArray<String> _tp,
|
||||||
|
DynArray<Field> _f,
|
||||||
|
DynArray<FnDefStmt *> _m,
|
||||||
|
SourceLocation _loc) :
|
||||||
|
isPublic(_p),
|
||||||
|
name(std::move(_n)),
|
||||||
|
typeParameters(std::move(_tp)),
|
||||||
|
fields(std::move(_f)),
|
||||||
|
methods(std::move(_m))
|
||||||
|
{
|
||||||
|
type = AstType::StructDefStmt;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String detail = name;
|
||||||
|
if (!typeParameters.empty())
|
||||||
|
{
|
||||||
|
detail += "<...>";
|
||||||
|
}
|
||||||
|
return std::format("<StructDefStmt '{}'>", detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
47
src/Ast/Stmt/VarDecl.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Stmt/VarDecl.hpp
|
||||||
|
@brief VarDecl定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-17
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct VarDecl final : public Stmt
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
Expr *typeSpecifier;
|
||||||
|
bool isInfer; // 是否用了 := 类型推断
|
||||||
|
Expr *initExpr;
|
||||||
|
|
||||||
|
int localId = -1;
|
||||||
|
|
||||||
|
VarDecl()
|
||||||
|
{
|
||||||
|
type = AstType::VarDecl;
|
||||||
|
}
|
||||||
|
|
||||||
|
VarDecl(bool _isPublic, String _name, Expr *_typeSpecifier, bool _isInfer, Expr *_initExpr, SourceLocation _location) :
|
||||||
|
name(std::move(_name)),
|
||||||
|
typeSpecifier(_typeSpecifier),
|
||||||
|
isInfer(_isInfer),
|
||||||
|
initExpr(_initExpr) // location 指向关键字 var/const位置
|
||||||
|
{
|
||||||
|
type = AstType::VarDecl;
|
||||||
|
isPublic = _isPublic;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
const String &typeSpecifierString = (typeSpecifier ? typeSpecifier->toString() : "Any");
|
||||||
|
const String &initExprString = (initExpr ? initExpr->toString() : "(disprovided)");
|
||||||
|
return std::format("<VarDecl {}: {} = {}>", name, typeSpecifierString, initExprString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
37
src/Ast/Stmt/WhileStmt.hpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*!
|
||||||
|
@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
|
||||||
100
src/Ast/TypeExpr.hpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/TypeExpr.hpp
|
||||||
|
@brief 类型表达式 AST 定义:支持泛型与空安全
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct NamedTypeExpr final : public TypeExpr
|
||||||
|
{
|
||||||
|
DynArray<String> path;
|
||||||
|
DynArray<Expr *> arguments;
|
||||||
|
|
||||||
|
NamedTypeExpr()
|
||||||
|
{
|
||||||
|
type = AstType::NamedTypeExpr;
|
||||||
|
}
|
||||||
|
NamedTypeExpr(DynArray<String> _p, DynArray<Expr *> _args, SourceLocation _loc) :
|
||||||
|
path(std::move(_p)), arguments(std::move(_args))
|
||||||
|
{
|
||||||
|
type = AstType::NamedTypeExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String detail = "";
|
||||||
|
for (size_t i = 0; i < path.size(); ++i)
|
||||||
|
{
|
||||||
|
detail += path[i];
|
||||||
|
if (i < path.size() - 1)
|
||||||
|
detail += ".";
|
||||||
|
}
|
||||||
|
if (!arguments.empty())
|
||||||
|
{
|
||||||
|
detail += "<";
|
||||||
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
|
{
|
||||||
|
detail += arguments[i]->toString();
|
||||||
|
if (i < arguments.size() - 1)
|
||||||
|
detail += ", ";
|
||||||
|
}
|
||||||
|
detail += ">";
|
||||||
|
}
|
||||||
|
return std::format("<NamedTypeExpr '{}'>", detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NullableTypeExpr final : public TypeExpr
|
||||||
|
{
|
||||||
|
Expr *inner;
|
||||||
|
|
||||||
|
NullableTypeExpr(Expr *_inner, SourceLocation _loc) : inner(_inner)
|
||||||
|
{
|
||||||
|
type = AstType::NullableTypeExpr;
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<NullableTypeExpr '{}?'>", inner->toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FnTypeExpr final : public TypeExpr
|
||||||
|
{
|
||||||
|
// func (paratypes...) -> return_type
|
||||||
|
|
||||||
|
DynArray<Expr *> paraTypes;
|
||||||
|
Expr *returnType;
|
||||||
|
|
||||||
|
FnTypeExpr(DynArray<Expr *> _paraTypes, Expr *_returnType) :
|
||||||
|
paraTypes(std::move(_paraTypes)), returnType(_returnType)
|
||||||
|
{
|
||||||
|
type = AstType::FnTypeExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
String detail = "<FnTypeExpr 'func (";
|
||||||
|
|
||||||
|
for (auto &pt : paraTypes)
|
||||||
|
{
|
||||||
|
if (pt != paraTypes.front())
|
||||||
|
{
|
||||||
|
detail += ", ";
|
||||||
|
}
|
||||||
|
detail += pt->toString();
|
||||||
|
}
|
||||||
|
detail += ") -> ";
|
||||||
|
detail += returnType->toString();
|
||||||
|
detail += "'>";
|
||||||
|
|
||||||
|
return detail;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
111
src/Bytecode/Bytecode.hpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Bytecode/Bytecode.hpp
|
||||||
|
@brief 字节码Bytecode定义
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Object/ObjectBase.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
using Instruction = std::uint32_t;
|
||||||
|
|
||||||
|
enum class OpCode : std::uint8_t
|
||||||
|
{
|
||||||
|
Exit,
|
||||||
|
Exit_MaxRecursionDepthExceeded,
|
||||||
|
|
||||||
|
LoadK,
|
||||||
|
LoadTrue,
|
||||||
|
LoadFalse,
|
||||||
|
LoadNull,
|
||||||
|
|
||||||
|
FastCall,
|
||||||
|
Call,
|
||||||
|
Return,
|
||||||
|
|
||||||
|
LoadFn,
|
||||||
|
|
||||||
|
Jmp,
|
||||||
|
JmpIfFalse,
|
||||||
|
|
||||||
|
Mov,
|
||||||
|
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
BitXor,
|
||||||
|
|
||||||
|
IntFastAdd,
|
||||||
|
IntFastSub,
|
||||||
|
IntFastMul,
|
||||||
|
IntFastDiv,
|
||||||
|
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
Greater,
|
||||||
|
Less,
|
||||||
|
GreaterEqual,
|
||||||
|
LessEqual,
|
||||||
|
|
||||||
|
GetGlobal,
|
||||||
|
SetGlobal,
|
||||||
|
GetUpval,
|
||||||
|
SetUpval,
|
||||||
|
Copy,
|
||||||
|
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Op
|
||||||
|
{
|
||||||
|
[[nodiscard]] inline constexpr Instruction iABx(OpCode op, std::uint8_t a, std::uint16_t bx)
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||||
|
| (static_cast<std::uint32_t>(bx) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline constexpr Instruction iABC(OpCode op, std::uint8_t a, std::uint8_t b, std::uint8_t c)
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||||
|
| (static_cast<std::uint32_t>(b) << 16) | (static_cast<std::uint32_t>(c) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline constexpr Instruction iAsBx(OpCode op, std::uint8_t a, std::int16_t sbx)
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(op) | (static_cast<std::uint32_t>(a) << 8)
|
||||||
|
| (static_cast<std::uint32_t>(static_cast<std::uint16_t>(sbx)) << 16);
|
||||||
|
}
|
||||||
|
} // namespace Op
|
||||||
|
|
||||||
|
struct UpvalueInfo
|
||||||
|
{
|
||||||
|
uint8_t index;
|
||||||
|
bool isLocal;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Proto
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
DynArray<Instruction> code;
|
||||||
|
DynArray<SourceLocation *> locations;
|
||||||
|
DynArray<Value> constants;
|
||||||
|
DynArray<UpvalueInfo> upvalues;
|
||||||
|
uint8_t maxRegisters = 0;
|
||||||
|
uint8_t numParams = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompiledModule
|
||||||
|
{
|
||||||
|
DynArray<Proto *> protos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Fig
|
||||||
102
src/Bytecode/Disassembler.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Bytecode/Disassembler.cpp
|
||||||
|
@brief 字节码反汇编器实现
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Bytecode/Disassembler.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
void Disassembler::DisassembleModule(const CompiledModule *module, std::ostream &stream)
|
||||||
|
{
|
||||||
|
if (!module) return;
|
||||||
|
stream << "--- Module Disassembly ---" << std::endl;
|
||||||
|
for (auto *proto : module->protos)
|
||||||
|
{
|
||||||
|
DisassembleProto(proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disassembler::DisassembleProto(const Proto *proto, std::ostream &stream)
|
||||||
|
{
|
||||||
|
if (!proto) return;
|
||||||
|
|
||||||
|
stream << std::format("\n--- Proto: {} (Regs: {}, Params: {}) ---\n",
|
||||||
|
proto->name, proto->maxRegisters, proto->numParams);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < proto->code.size(); ++i)
|
||||||
|
{
|
||||||
|
Instruction inst = proto->code[i];
|
||||||
|
OpCode op = static_cast<OpCode>(inst & 0xFF);
|
||||||
|
uint8_t a = (inst >> 8) & 0xFF;
|
||||||
|
|
||||||
|
stream << std::format("[{:04}] {:<12} ", i, magic_enum::enum_name(op));
|
||||||
|
|
||||||
|
Format fmt = GetFormat(op);
|
||||||
|
if (fmt == Format::ABC)
|
||||||
|
{
|
||||||
|
uint8_t b = (inst >> 16) & 0xFF;
|
||||||
|
uint8_t c = (inst >> 24) & 0xFF;
|
||||||
|
stream << std::format("A:{:<3} B:{:<3} C:{:<3}", a, b, c);
|
||||||
|
}
|
||||||
|
else if (fmt == Format::ABx)
|
||||||
|
{
|
||||||
|
uint16_t bx = (inst >> 16) & 0xFFFF;
|
||||||
|
stream << std::format("A:{:<3} Bx:{:<5}", a, bx);
|
||||||
|
|
||||||
|
// 自动关联常量池
|
||||||
|
if (op == OpCode::LoadK && bx < proto->constants.size())
|
||||||
|
{
|
||||||
|
stream << std::format(" ; {}", proto->constants[bx].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fmt == Format::AsBx)
|
||||||
|
{
|
||||||
|
int16_t sbx = static_cast<int16_t>((inst >> 16) & 0xFFFF);
|
||||||
|
stream << std::format("A:{:<3} sBx:{:<5}", a, sbx);
|
||||||
|
|
||||||
|
// 计算跳转绝对地址
|
||||||
|
if (op == OpCode::Jmp || op == OpCode::JmpIfFalse)
|
||||||
|
{
|
||||||
|
stream << std::format(" ; to [{:04}]", i + sbx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proto->constants.empty())
|
||||||
|
{
|
||||||
|
stream << "Constants:\n";
|
||||||
|
for (size_t i = 0; i < proto->constants.size(); ++i)
|
||||||
|
{
|
||||||
|
stream << std::format(" [{}] {}\n", i, proto->constants[i].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream << "--- End Disassembly ---\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
Disassembler::Format Disassembler::GetFormat(OpCode op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case OpCode::LoadK:
|
||||||
|
case OpCode::Mov:
|
||||||
|
case OpCode::GetGlobal:
|
||||||
|
case OpCode::SetGlobal:
|
||||||
|
case OpCode::LoadFn:
|
||||||
|
return Format::ABx;
|
||||||
|
|
||||||
|
case OpCode::Exit:
|
||||||
|
case OpCode::Exit_MaxRecursionDepthExceeded:
|
||||||
|
case OpCode::Jmp:
|
||||||
|
case OpCode::JmpIfFalse:
|
||||||
|
return Format::AsBx;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Format::ABC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace Fig
|
||||||
35
src/Bytecode/Disassembler.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Bytecode/Disassembler.hpp
|
||||||
|
@brief 字节码反汇编器:物理还原指令语义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Bytecode/Bytecode.hpp>
|
||||||
|
#include <Compiler/Compiler.hpp>
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Disassembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// 反汇编整个模块
|
||||||
|
static void DisassembleModule(const CompiledModule *module, std::ostream & = CoreIO::GetStdOut());
|
||||||
|
|
||||||
|
// 反汇编单个函数原型
|
||||||
|
static void DisassembleProto(const Proto *proto, std::ostream & = CoreIO::GetStdOut());
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class Format
|
||||||
|
{
|
||||||
|
ABC,
|
||||||
|
ABx,
|
||||||
|
AsBx
|
||||||
|
};
|
||||||
|
|
||||||
|
static Format GetFormat(OpCode op);
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
67
src/Compiler/CompileTest.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
#include <Sema/Analyzer.hpp>
|
||||||
|
#include <Compiler/Compiler.hpp>
|
||||||
|
#include <Bytecode/Disassembler.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using namespace Fig;
|
||||||
|
|
||||||
|
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/tests/Compiler/test_basic.fig";
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(filePath.toStdString()))
|
||||||
|
{
|
||||||
|
std::cerr << "CRITICAL: Test file not found at: " << filePath << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceManager sm{filePath};
|
||||||
|
String source = sm.Read();
|
||||||
|
|
||||||
|
if (!sm.read || source.length() == 0)
|
||||||
|
{
|
||||||
|
std::cerr << "CRITICAL: SourceManager failed to read: " << filePath << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer(source, filePath);
|
||||||
|
|
||||||
|
Diagnostics diagnostics;
|
||||||
|
Parser parser(lexer, sm, filePath, diagnostics);
|
||||||
|
|
||||||
|
diagnostics.EmitAll(sm);
|
||||||
|
|
||||||
|
auto pRes = parser.Parse();
|
||||||
|
if (!pRes)
|
||||||
|
{
|
||||||
|
ReportError(pRes.error(), sm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program *program = *pRes;
|
||||||
|
std::cout << "Successfully parsed nodes: " << program->nodes.size() << "\n";
|
||||||
|
|
||||||
|
Analyzer analyzer(sm);
|
||||||
|
auto aRes = analyzer.Analyze(program);
|
||||||
|
if (!aRes)
|
||||||
|
{
|
||||||
|
ReportError(aRes.error(), sm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Diagnostics diag;
|
||||||
|
Compiler compiler(sm, diag);
|
||||||
|
auto cRes = compiler.Compile(program);
|
||||||
|
if (!cRes)
|
||||||
|
{
|
||||||
|
ReportError(cRes.error(), sm);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正式的 Disassembler
|
||||||
|
Disassembler::DisassembleModule(*cRes);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
142
src/Compiler/Compiler.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Compiler/Compiler.cpp
|
||||||
|
@brief 编译器主逻辑实现:物理 Bootstrapper 与双步扫描
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
#include <Compiler/Compiler.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
Result<CompiledModule *, Error> Compiler::Compile(Program *program)
|
||||||
|
{
|
||||||
|
module = new CompiledModule();
|
||||||
|
if (program->nodes.empty())
|
||||||
|
{
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预留 Protos[0] 给 Bootstrapper
|
||||||
|
Proto *bootProto = new Proto();
|
||||||
|
bootProto->name = "[bootstrapper]";
|
||||||
|
module->protos.push_back(bootProto);
|
||||||
|
|
||||||
|
int initIdx = -1;
|
||||||
|
int mainIdx = -1;
|
||||||
|
|
||||||
|
SourceLocation *mainFnLoc = nullptr;
|
||||||
|
SourceLocation *initFnLoc = nullptr;
|
||||||
|
|
||||||
|
// 预扫描顶层函数
|
||||||
|
for (auto *stmt : program->nodes)
|
||||||
|
{
|
||||||
|
if (stmt->type == AstType::FnDefStmt)
|
||||||
|
{
|
||||||
|
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||||
|
int idx = (int) module->protos.size();
|
||||||
|
|
||||||
|
Proto *p = new Proto();
|
||||||
|
p->name = f->name;
|
||||||
|
p->numParams = (uint8_t) f->params.size();
|
||||||
|
p->maxRegisters = p->numParams;
|
||||||
|
f->protoIndex = idx;
|
||||||
|
|
||||||
|
module->protos.push_back(p);
|
||||||
|
|
||||||
|
// 连接物理符号到索引
|
||||||
|
if (f->resolvedSymbol)
|
||||||
|
{
|
||||||
|
f->resolvedSymbol->index = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->name == "init")
|
||||||
|
{
|
||||||
|
initIdx = idx;
|
||||||
|
initFnLoc = &stmt->location;
|
||||||
|
}
|
||||||
|
if (f->name == "main")
|
||||||
|
{
|
||||||
|
mainIdx = idx;
|
||||||
|
mainFnLoc = &stmt->location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrapper 中编译所有语句
|
||||||
|
FuncState bootState(bootProto, nullptr);
|
||||||
|
current = &bootState;
|
||||||
|
|
||||||
|
for (auto *stmt : program->nodes)
|
||||||
|
{
|
||||||
|
auto res = compileStmt(stmt);
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
return std::unexpected(res.error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发射 Bootstrapper 引导指令
|
||||||
|
if (initIdx != -1)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::FastCall, (uint8_t) initIdx, 0, 0), initFnLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainIdx != -1)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::FastCall, (uint8_t) mainIdx, 0, 0), mainFnLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(Op::iAsBx(OpCode::Exit, 0, 0), &program->nodes.back()->location);
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compiler::getGlobalID(const String &name)
|
||||||
|
{
|
||||||
|
if (globalIDMap.contains(name))
|
||||||
|
return globalIDMap[name];
|
||||||
|
int id = (int) globalIDMap.size();
|
||||||
|
globalIDMap[name] = id;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Register, Error> Compiler::allocateReg(const SourceLocation &loc)
|
||||||
|
{
|
||||||
|
if (current->freereg >= MAX_REGISTERS)
|
||||||
|
{
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::RegisterOverflow, "too many registers", "", loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
Register reg = current->freereg++;
|
||||||
|
if (reg >= current->proto->maxRegisters)
|
||||||
|
{
|
||||||
|
current->proto->maxRegisters = reg + 1;
|
||||||
|
}
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::freeReg(Register count)
|
||||||
|
{
|
||||||
|
if (current->freereg >= count)
|
||||||
|
{
|
||||||
|
current->freereg -= count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Compiler::addConstant(Value val)
|
||||||
|
{
|
||||||
|
if (current->constantMap.contains(val))
|
||||||
|
return current->constantMap[val];
|
||||||
|
int idx = (int) current->proto->constants.size();
|
||||||
|
current->proto->constants.push_back(val);
|
||||||
|
current->constantMap[val] = idx;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::emit(Instruction inst, SourceLocation *loc)
|
||||||
|
{
|
||||||
|
current->proto->code.push_back(inst);
|
||||||
|
current->proto->locations.push_back(loc);
|
||||||
|
}
|
||||||
|
} // namespace Fig
|
||||||
55
src/Compiler/Compiler.hpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Compiler/Compiler.hpp
|
||||||
|
@brief 编译器定义:物理直连 Bootstrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Ast.hpp>
|
||||||
|
#include <Bytecode/Bytecode.hpp>
|
||||||
|
#include <Error/Diagnostics.hpp>
|
||||||
|
#include <Object/Object.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
using Register = std::uint8_t;
|
||||||
|
|
||||||
|
class Compiler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static constexpr Register MAX_REGISTERS = 250;
|
||||||
|
static constexpr Register NO_REG = 255;
|
||||||
|
|
||||||
|
struct FuncState
|
||||||
|
{
|
||||||
|
Proto *proto;
|
||||||
|
Register freereg;
|
||||||
|
FuncState *enclosing;
|
||||||
|
HashMap<Value, int> constantMap;
|
||||||
|
|
||||||
|
FuncState(Proto *p, FuncState *e)
|
||||||
|
: proto(p), freereg(p->numParams), enclosing(e) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
FuncState *current = nullptr;
|
||||||
|
CompiledModule *module = nullptr;
|
||||||
|
SourceManager &manager;
|
||||||
|
Diagnostics &diag;
|
||||||
|
|
||||||
|
HashMap<String, int> globalIDMap;
|
||||||
|
int getGlobalID(const String& name);
|
||||||
|
|
||||||
|
Result<Register, Error> allocateReg(const SourceLocation &loc);
|
||||||
|
void freeReg(Register count = 1);
|
||||||
|
int addConstant(Value val);
|
||||||
|
|
||||||
|
void emit(Instruction inst, SourceLocation *loc);
|
||||||
|
|
||||||
|
Result<void, Error> compileStmt(Stmt *stmt);
|
||||||
|
Result<Register, Error> compileExpr(Expr *expr, Register target = NO_REG);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Compiler(SourceManager &m, Diagnostics &d) : manager(m), diag(d) {}
|
||||||
|
Result<CompiledModule *, Error> Compile(Program *program);
|
||||||
|
};
|
||||||
|
}
|
||||||
477
src/Compiler/ExprCompiler.cpp
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Compiler/ExprCompiler.cpp
|
||||||
|
@brief 表达式编译
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Expr/CallExpr.hpp>
|
||||||
|
#include <Ast/Expr/IdentiExpr.hpp>
|
||||||
|
#include <Ast/Expr/InfixExpr.hpp>
|
||||||
|
#include <Ast/Expr/LiteralExpr.hpp>
|
||||||
|
#include <Compiler/Compiler.hpp>
|
||||||
|
#include <charconv>
|
||||||
|
#include <limits>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
static Result<Value, Error> parsePhysicalNumber(const String &raw, const SourceLocation &loc)
|
||||||
|
{
|
||||||
|
char buffer[128];
|
||||||
|
int j = 0;
|
||||||
|
bool isFloat = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < raw.length() && j < 127; ++i)
|
||||||
|
{
|
||||||
|
char32_t c = raw[i];
|
||||||
|
if (c == '_')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 检查开头的无效字符
|
||||||
|
if (j == 0 && (c == '.' || c == 'e' || c == 'E'))
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::SyntaxError, "unexpected leading character", "", loc));
|
||||||
|
|
||||||
|
if (c == '.' || c == 'e' || c == 'E')
|
||||||
|
isFloat = true;
|
||||||
|
buffer[j++] = (char) c;
|
||||||
|
}
|
||||||
|
buffer[j] = '\0';
|
||||||
|
|
||||||
|
// 检查16进制/2进制前缀
|
||||||
|
bool isHexOrBin = false;
|
||||||
|
int base = 10;
|
||||||
|
const char *start = buffer;
|
||||||
|
|
||||||
|
if (j >= 2 && buffer[0] == '0')
|
||||||
|
{
|
||||||
|
if (buffer[1] == 'x' || buffer[1] == 'X')
|
||||||
|
{
|
||||||
|
base = 16;
|
||||||
|
start += 2;
|
||||||
|
isHexOrBin = true;
|
||||||
|
}
|
||||||
|
else if (buffer[1] == 'b' || buffer[1] == 'B')
|
||||||
|
{
|
||||||
|
base = 2;
|
||||||
|
start += 2;
|
||||||
|
isHexOrBin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
// 如果既有浮点标记又是0x开头,可能是16进制浮点
|
||||||
|
auto fmt =
|
||||||
|
(isHexOrBin && base == 16) ? std::chars_format::hex : std::chars_format::general;
|
||||||
|
|
||||||
|
double dVal;
|
||||||
|
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, fmt);
|
||||||
|
|
||||||
|
if (ec != std::errc() || ptr != buffer + j)
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::SyntaxError, "invalid float literal", "", loc));
|
||||||
|
|
||||||
|
return Value::FromDouble(dVal);
|
||||||
|
}
|
||||||
|
else if (isHexOrBin)
|
||||||
|
{
|
||||||
|
// 16进制或2进制整数
|
||||||
|
int64_t iVal;
|
||||||
|
auto [ptr, ec] = std::from_chars(start, buffer + j, iVal, base);
|
||||||
|
|
||||||
|
if (ec != std::errc() || ptr != buffer + j)
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::SyntaxError, "integer overflow or invalid literal", "", loc));
|
||||||
|
|
||||||
|
if (iVal >= std::numeric_limits<int32_t>::min()
|
||||||
|
&& iVal <= std::numeric_limits<int32_t>::max())
|
||||||
|
{
|
||||||
|
return Value::FromInt(static_cast<int32_t>(iVal));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Value::FromDouble(static_cast<double>(iVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 10进制数字,可能是整数或浮点数
|
||||||
|
double dVal;
|
||||||
|
auto [ptr, ec] = std::from_chars(start, buffer + j, dVal, std::chars_format::general);
|
||||||
|
|
||||||
|
if (ec != std::errc() || ptr != buffer + j)
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::SyntaxError, "invalid number literal", "", loc));
|
||||||
|
|
||||||
|
// 检查是否是整数(没有小数部分且不超出int32范围)
|
||||||
|
if (dVal == std::floor(dVal) && dVal >= std::numeric_limits<int32_t>::min()
|
||||||
|
&& dVal <= std::numeric_limits<int32_t>::max())
|
||||||
|
{
|
||||||
|
return Value::FromInt(static_cast<int32_t>(dVal));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Value::FromDouble(dVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Register, Error> Compiler::compileExpr(Expr *expr, Register target)
|
||||||
|
{
|
||||||
|
if (expr == nullptr)
|
||||||
|
{
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::InternalError, "null expr in compiler", "", {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (expr->type)
|
||||||
|
{
|
||||||
|
case AstType::LiteralExpr: {
|
||||||
|
auto *l = static_cast<LiteralExpr *>(expr);
|
||||||
|
Register r = (target == NO_REG) ? *allocateReg(l->location) : target;
|
||||||
|
|
||||||
|
const Token &tok = l->literal;
|
||||||
|
if (tok.type == TokenType::LiteralNumber)
|
||||||
|
{
|
||||||
|
auto vRes =
|
||||||
|
parsePhysicalNumber(manager.GetSub(tok.index, tok.length), l->location);
|
||||||
|
if (!vRes)
|
||||||
|
return std::unexpected(vRes.error());
|
||||||
|
emit(
|
||||||
|
Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(addConstant(*vRes))),
|
||||||
|
&l->location);
|
||||||
|
}
|
||||||
|
else if (tok.type == TokenType::LiteralString)
|
||||||
|
{
|
||||||
|
int kIdx = addConstant(Value::GetNullInstance()); // TODO: String 支持
|
||||||
|
emit(Op::iABx(OpCode::LoadK, r, static_cast<uint16_t>(kIdx)), &l->location);
|
||||||
|
}
|
||||||
|
else if (tok.type == TokenType::LiteralNull)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->location);
|
||||||
|
}
|
||||||
|
else if (tok.type == TokenType::LiteralTrue)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::LoadTrue, r, 0, 0), &l->location);
|
||||||
|
}
|
||||||
|
else if (tok.type == TokenType::LiteralFalse)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::LoadFalse, r, 0, 0), &l->location);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::IdentiExpr: {
|
||||||
|
auto *i = static_cast<IdentiExpr *>(expr);
|
||||||
|
Symbol *sym = i->resolvedSymbol;
|
||||||
|
|
||||||
|
if (sym->location == SymbolLocation::Local)
|
||||||
|
{
|
||||||
|
// no-copy for temp eval
|
||||||
|
if (target == NO_REG)
|
||||||
|
return static_cast<Register>(sym->index);
|
||||||
|
|
||||||
|
// 仅在被强制指定目标(如参数装填)时发射搬运指令
|
||||||
|
if (target != sym->index)
|
||||||
|
{
|
||||||
|
emit(
|
||||||
|
Op::iABx(OpCode::Mov, target, static_cast<uint16_t>(sym->index)),
|
||||||
|
&i->location);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r = (target == NO_REG) ? *allocateReg(i->location) : target;
|
||||||
|
if (sym->location == SymbolLocation::Upvalue)
|
||||||
|
{
|
||||||
|
emit(
|
||||||
|
Op::iABC(OpCode::GetUpval, r, static_cast<uint8_t>(sym->index), 0),
|
||||||
|
&i->location);
|
||||||
|
}
|
||||||
|
else if (sym->location == SymbolLocation::Global)
|
||||||
|
{
|
||||||
|
int gId = getGlobalID(i->name);
|
||||||
|
emit(Op::iABx(OpCode::GetGlobal, r, static_cast<uint16_t>(gId)), &i->location);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::CallExpr: {
|
||||||
|
auto *c = static_cast<CallExpr *>(expr);
|
||||||
|
Register mark = current->freereg; // 记录调用前的栈顶水位
|
||||||
|
Register baseReg = current->freereg; // 锁定滑窗基址
|
||||||
|
|
||||||
|
// 连续装填参数,占据 baseReg, baseReg+1, baseReg+2...
|
||||||
|
for (auto *arg : c->args.args)
|
||||||
|
{
|
||||||
|
auto allocRes = allocateReg(arg->location);
|
||||||
|
if (!allocRes)
|
||||||
|
{
|
||||||
|
return allocRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register argTarget = *allocRes;
|
||||||
|
auto res = compileExpr(arg, argTarget);
|
||||||
|
if (!res)
|
||||||
|
return std::unexpected(res.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isGlobalFastCall = false;
|
||||||
|
if (c->callee->type == AstType::IdentiExpr)
|
||||||
|
{
|
||||||
|
auto *id = static_cast<IdentiExpr *>(c->callee);
|
||||||
|
// 只有在全局区的函数,才能使用 FastCall
|
||||||
|
if (id->resolvedSymbol->location == SymbolLocation::Global)
|
||||||
|
{
|
||||||
|
isGlobalFastCall = true;
|
||||||
|
int protoIdx = id->resolvedSymbol->index;
|
||||||
|
emit(
|
||||||
|
Op::iABC(
|
||||||
|
OpCode::FastCall,
|
||||||
|
static_cast<uint8_t>(protoIdx),
|
||||||
|
baseReg,
|
||||||
|
static_cast<uint8_t>(c->args.args.size())),
|
||||||
|
&c->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGlobalFastCall)
|
||||||
|
{
|
||||||
|
// 动态闭包调用
|
||||||
|
// 先获取闭包对象所在的物理寄存器
|
||||||
|
auto r_fn = compileExpr(c->callee);
|
||||||
|
if (!r_fn)
|
||||||
|
return std::unexpected(r_fn.error());
|
||||||
|
|
||||||
|
// 使用动态 Call 指令,RA 是指向堆闭包的寄存器
|
||||||
|
emit(
|
||||||
|
Op::iABC(
|
||||||
|
OpCode::Call,
|
||||||
|
*r_fn,
|
||||||
|
baseReg,
|
||||||
|
static_cast<uint8_t>(c->args.args.size())),
|
||||||
|
&c->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
// free arg temps
|
||||||
|
current->freereg = mark;
|
||||||
|
|
||||||
|
// 目若 target 未指定,allocateReg 将复用 baseReg,实现零开销回写
|
||||||
|
|
||||||
|
Register r_dest;
|
||||||
|
if (target == NO_REG)
|
||||||
|
{
|
||||||
|
auto res = allocateReg(c->location);
|
||||||
|
if (!res)
|
||||||
|
return std::unexpected(res.error());
|
||||||
|
r_dest = *res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r_dest = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r_dest != baseReg)
|
||||||
|
{
|
||||||
|
emit(Op::iABx(OpCode::Mov, r_dest, baseReg), &c->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r_dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::InfixExpr: {
|
||||||
|
auto *in = static_cast<InfixExpr *>(expr);
|
||||||
|
if (in->op == BinaryOperator::Assign)
|
||||||
|
{
|
||||||
|
auto r_val = compileExpr(in->right, target);
|
||||||
|
if (!r_val)
|
||||||
|
return std::unexpected(r_val.error());
|
||||||
|
|
||||||
|
if (in->left->type == AstType::IdentiExpr)
|
||||||
|
{
|
||||||
|
auto *lid = static_cast<IdentiExpr *>(in->left);
|
||||||
|
Symbol *sym = lid->resolvedSymbol;
|
||||||
|
if (sym->location == SymbolLocation::Local)
|
||||||
|
{
|
||||||
|
emit(
|
||||||
|
Op::iABx(OpCode::Mov, static_cast<Register>(sym->index), *r_val),
|
||||||
|
&lid->location);
|
||||||
|
}
|
||||||
|
else if (sym->location == SymbolLocation::Upvalue)
|
||||||
|
{
|
||||||
|
emit(
|
||||||
|
Op::iABC(
|
||||||
|
OpCode::SetUpval, *r_val, static_cast<Register>(sym->index), 0),
|
||||||
|
&lid->location);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit(
|
||||||
|
Op::iABx(
|
||||||
|
OpCode::SetGlobal,
|
||||||
|
*r_val,
|
||||||
|
static_cast<uint16_t>(getGlobalID(lid->name))),
|
||||||
|
&lid->location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
|
||||||
|
auto r_l = compileExpr(in->left);
|
||||||
|
if (!r_l)
|
||||||
|
return std::unexpected(r_l.error());
|
||||||
|
auto r_r = compileExpr(in->right);
|
||||||
|
if (!r_r)
|
||||||
|
return std::unexpected(r_r.error());
|
||||||
|
|
||||||
|
bool isInt = in->left->resolvedType.is(TypeTag::Int)
|
||||||
|
&& in->right->resolvedType.is(TypeTag::Int);
|
||||||
|
OpCode op;
|
||||||
|
switch (in->op)
|
||||||
|
{
|
||||||
|
case BinaryOperator::Add: {
|
||||||
|
op = (isInt ? (OpCode::IntFastAdd) : (OpCode::Add));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Subtract: {
|
||||||
|
op = (isInt ? (OpCode::IntFastSub) : (OpCode::Sub));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Multiply: {
|
||||||
|
op = (isInt ? (OpCode::IntFastMul) : (OpCode::Mul));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Divide: {
|
||||||
|
op = (isInt ? (OpCode::IntFastDiv) : (OpCode::Div));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Modulo: {
|
||||||
|
op = OpCode::Mod;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::BitXor: {
|
||||||
|
op = OpCode::BitXor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Equal: {
|
||||||
|
op = OpCode::Equal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::NotEqual: {
|
||||||
|
op = OpCode::NotEqual;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Greater: {
|
||||||
|
op = OpCode::Greater;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::Less: {
|
||||||
|
op = OpCode::Less;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::GreaterEqual: {
|
||||||
|
op = OpCode::GreaterEqual;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BinaryOperator::LessEqual: {
|
||||||
|
op = OpCode::LessEqual;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return std::unexpected(Error(
|
||||||
|
ErrorType::InternalError,
|
||||||
|
"unsupported binary operator",
|
||||||
|
"",
|
||||||
|
in->location));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放左右操作数产生的临时寄存器
|
||||||
|
current->freereg = mark;
|
||||||
|
|
||||||
|
// 复用已释放的物理槽位存放计算结果
|
||||||
|
Register r_d;
|
||||||
|
if (target == NO_REG)
|
||||||
|
{
|
||||||
|
auto res = allocateReg(in->location);
|
||||||
|
if (!res)
|
||||||
|
return std::unexpected(res.error());
|
||||||
|
r_d = *res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r_d = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(Op::iABC(op, r_d, *r_l, *r_r), &in->location);
|
||||||
|
|
||||||
|
return r_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::LambdaExpr: {
|
||||||
|
auto l = static_cast<LambdaExpr *>(expr);
|
||||||
|
|
||||||
|
Proto *proto = new Proto;
|
||||||
|
proto->name = l->toString();
|
||||||
|
|
||||||
|
FuncState fs(proto, current);
|
||||||
|
FuncState *old = current;
|
||||||
|
current = &fs;
|
||||||
|
|
||||||
|
if (l->isExprBody)
|
||||||
|
{
|
||||||
|
auto result = compileExpr(static_cast<Expr *>(l->body));
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(Op::iABC(OpCode::Return, *result, 0, 0), &l->location);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = compileStmt(static_cast<BlockStmt *>(l->body));
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return std::unexpected(result.error());
|
||||||
|
}
|
||||||
|
auto _r = allocateReg(l->body->location);
|
||||||
|
if (!_r)
|
||||||
|
{
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r = *_r;
|
||||||
|
emit(Op::iABC(OpCode::LoadNull, r, 0, 0), &l->body->location);
|
||||||
|
emit(Op::iABC(OpCode::Return, r, 0, 0), &l->body->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
int protoIndex = (int) module->protos.size();
|
||||||
|
module->protos.push_back(proto);
|
||||||
|
|
||||||
|
current = old;
|
||||||
|
if (target == NO_REG)
|
||||||
|
{
|
||||||
|
auto _r = allocateReg(expr->location);
|
||||||
|
if (!_r)
|
||||||
|
{
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
|
emit(Op::iABx(OpCode::LoadFn, *_r, protoIndex), &l->body->location);
|
||||||
|
return *_r;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit(Op::iABx(OpCode::LoadFn, target, protoIndex), &l->body->location);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::InternalError, "unsupported expr", "", expr->location));
|
||||||
|
}
|
||||||
|
} // namespace Fig
|
||||||
269
src/Compiler/StmtCompiler.cpp
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Compiler/StmtCompiler.cpp
|
||||||
|
@brief 语句编译
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Stmt/FnDefStmt.hpp>
|
||||||
|
#include <Ast/Stmt/IfStmt.hpp>
|
||||||
|
#include <Ast/Stmt/VarDecl.hpp>
|
||||||
|
#include <Ast/Stmt/WhileStmt.hpp>
|
||||||
|
#include <Compiler/Compiler.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
Result<void, Error> Compiler::compileStmt(Stmt *stmt)
|
||||||
|
{
|
||||||
|
if (stmt == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (stmt->type)
|
||||||
|
{
|
||||||
|
case AstType::BlockStmt: {
|
||||||
|
auto *b = static_cast<BlockStmt *>(stmt);
|
||||||
|
for (auto *n : b->nodes)
|
||||||
|
{
|
||||||
|
auto res = compileStmt(n);
|
||||||
|
if (!res)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::VarDecl: {
|
||||||
|
auto *v = static_cast<VarDecl *>(stmt);
|
||||||
|
if (current->enclosing == nullptr) // 处理全局变量
|
||||||
|
{
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
auto regRes = compileExpr(v->initExpr);
|
||||||
|
if (!regRes)
|
||||||
|
return std::unexpected(regRes.error());
|
||||||
|
|
||||||
|
emit(Op::iABx(OpCode::SetGlobal,
|
||||||
|
*regRes,
|
||||||
|
static_cast<uint16_t>(getGlobalID(v->name))),
|
||||||
|
&v->location);
|
||||||
|
current->freereg = mark; // 释放初始化表达式的临时占用
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 抬升水位,锁死局部变量的物理槽位
|
||||||
|
Register targetReg = static_cast<Register>(v->localId);
|
||||||
|
while (current->freereg <= targetReg)
|
||||||
|
{
|
||||||
|
auto allocRes = allocateReg(v->location);
|
||||||
|
if (!allocRes)
|
||||||
|
{
|
||||||
|
return std::unexpected(allocRes.error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto regRes = compileExpr(v->initExpr, targetReg);
|
||||||
|
if (!regRes)
|
||||||
|
return std::unexpected(regRes.error());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::FnDefStmt: {
|
||||||
|
auto *f = static_cast<FnDefStmt *>(stmt);
|
||||||
|
|
||||||
|
if (f->protoIndex == -1) // 闭包环境没有被扫到
|
||||||
|
{
|
||||||
|
Proto *newProto = new Proto();
|
||||||
|
newProto->name = f->name;
|
||||||
|
newProto->numParams = static_cast<uint8_t>(f->params.size());
|
||||||
|
newProto->maxRegisters = newProto->numParams; // sync
|
||||||
|
|
||||||
|
f->protoIndex = static_cast<int>(module->protos.size());
|
||||||
|
module->protos.push_back(newProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取静态原型 (flat protos)
|
||||||
|
Proto *p = module->protos[f->protoIndex];
|
||||||
|
|
||||||
|
p->upvalues = f->upvalues;
|
||||||
|
|
||||||
|
FuncState fs(p, current);
|
||||||
|
FuncState *old = current;
|
||||||
|
current = &fs;
|
||||||
|
|
||||||
|
auto res = compileStmt(f->body);
|
||||||
|
if (!res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (p->code.empty() || static_cast<OpCode>(p->code.back() & 0xFF) != OpCode::Return)
|
||||||
|
{
|
||||||
|
emit(Op::iABC(OpCode::Return, 0, 0, 0), &f->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
current = old;
|
||||||
|
|
||||||
|
// 如果是局部闭包,在当前栈帧分配寄存器并生成 LoadFn
|
||||||
|
if (f->resolvedSymbol->location == SymbolLocation::Local)
|
||||||
|
{
|
||||||
|
Register targetReg = static_cast<Register>(f->resolvedSymbol->index);
|
||||||
|
|
||||||
|
while (current->freereg <= targetReg)
|
||||||
|
{
|
||||||
|
auto allocRes = allocateReg(f->location);
|
||||||
|
if (!allocRes)
|
||||||
|
return std::unexpected(allocRes.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 LoadFn: RA = 目标寄存器, Bx = Proto 在 module->protos 中的绝对索引
|
||||||
|
emit(Op::iABx(OpCode::LoadFn, targetReg, static_cast<uint16_t>(f->protoIndex)),
|
||||||
|
&f->location);
|
||||||
|
}
|
||||||
|
else if (f->resolvedSymbol->location == SymbolLocation::Global)
|
||||||
|
{
|
||||||
|
auto result = allocateReg(f->location);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return std::unexpected(result.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r = *result;
|
||||||
|
emit(Op::iABx(OpCode::LoadFn, r, static_cast<uint16_t>(f->protoIndex)),
|
||||||
|
&f->location);
|
||||||
|
|
||||||
|
int gId = getGlobalID(f->name);
|
||||||
|
emit(Op::iABx(OpCode::SetGlobal, r, static_cast<std::uint16_t>(gId)),
|
||||||
|
&f->location);
|
||||||
|
|
||||||
|
freeReg();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::IfStmt: {
|
||||||
|
auto *i = static_cast<IfStmt *>(stmt);
|
||||||
|
DynArray<int> exitJumps;
|
||||||
|
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
auto r_cond = compileExpr(i->cond);
|
||||||
|
if (!r_cond)
|
||||||
|
return std::unexpected(r_cond.error());
|
||||||
|
|
||||||
|
int jmpToNext = static_cast<int>(current->proto->code.size());
|
||||||
|
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &i->location);
|
||||||
|
current->freereg = mark; // 回收条件表达式临时槽位
|
||||||
|
|
||||||
|
if (auto r = compileStmt(i->consequent); !r)
|
||||||
|
return r;
|
||||||
|
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||||
|
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &i->location);
|
||||||
|
|
||||||
|
int targetIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
current->proto->code[jmpToNext] = Op::iAsBx(
|
||||||
|
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(targetIdx - jmpToNext - 1));
|
||||||
|
|
||||||
|
for (auto *elif : i->elifs)
|
||||||
|
{
|
||||||
|
Register elifMark = current->freereg;
|
||||||
|
auto ec = compileExpr(elif->cond);
|
||||||
|
if (!ec)
|
||||||
|
return std::unexpected(ec.error());
|
||||||
|
|
||||||
|
int nextElif = static_cast<int>(current->proto->code.size());
|
||||||
|
emit(Op::iAsBx(OpCode::JmpIfFalse, *ec, 0), &elif->location);
|
||||||
|
current->freereg = elifMark; // 回收 elif 临时槽位
|
||||||
|
|
||||||
|
if (auto r = compileStmt(elif->consequent); !r)
|
||||||
|
return r;
|
||||||
|
exitJumps.push_back(static_cast<int>(current->proto->code.size()));
|
||||||
|
emit(Op::iAsBx(OpCode::Jmp, 0, 0), &elif->location);
|
||||||
|
|
||||||
|
int target = static_cast<int>(current->proto->code.size());
|
||||||
|
|
||||||
|
current->proto->code.resize(nextElif);
|
||||||
|
current->proto->code[nextElif] = Op::iAsBx(
|
||||||
|
OpCode::JmpIfFalse, *ec, static_cast<int16_t>(target - nextElif - 1));
|
||||||
|
|
||||||
|
current->proto->locations.resize(nextElif);
|
||||||
|
current->proto->locations[nextElif] = &elif->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->alternate)
|
||||||
|
{
|
||||||
|
if (auto r = compileStmt(i->alternate); !r)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int endIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
for (int pos : exitJumps)
|
||||||
|
{
|
||||||
|
current->proto->code[pos] =
|
||||||
|
Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(endIdx - pos - 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::WhileStmt: {
|
||||||
|
auto *w = static_cast<WhileStmt *>(stmt);
|
||||||
|
int startIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
auto r_cond = compileExpr(w->cond);
|
||||||
|
if (!r_cond)
|
||||||
|
return std::unexpected(r_cond.error());
|
||||||
|
|
||||||
|
int exitJmpIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
emit(Op::iAsBx(OpCode::JmpIfFalse, *r_cond, 0), &w->location);
|
||||||
|
current->freereg = mark; // 回收循环条件临时槽位
|
||||||
|
|
||||||
|
if (auto r = compileStmt(w->body); !r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
int backJmpIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
emit(Op::iAsBx(OpCode::Jmp, 0, static_cast<int16_t>(startIdx - backJmpIdx - 1)),
|
||||||
|
&w->location);
|
||||||
|
|
||||||
|
int endIdx = static_cast<int>(current->proto->code.size());
|
||||||
|
current->proto->code[exitJmpIdx] = Op::iAsBx(
|
||||||
|
OpCode::JmpIfFalse, *r_cond, static_cast<int16_t>(endIdx - exitJmpIdx - 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::ReturnStmt: {
|
||||||
|
auto *rs = static_cast<ReturnStmt *>(stmt);
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
Register retReg;
|
||||||
|
|
||||||
|
if (rs->value)
|
||||||
|
{
|
||||||
|
auto r = compileExpr(rs->value);
|
||||||
|
if (!r)
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
retReg = *r;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto r = allocateReg(rs->location);
|
||||||
|
if (!r)
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
emit(Op::iABC(OpCode::LoadNull, *r, 0, 0), &rs->location);
|
||||||
|
retReg = *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(Op::iABC(OpCode::Return, retReg, 0, 0), &rs->location);
|
||||||
|
current->freereg = mark; // 回收返回值计算的占用
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AstType::ExprStmt: {
|
||||||
|
Register mark = current->freereg; // mark
|
||||||
|
auto reg = compileExpr(static_cast<ExprStmt *>(stmt)->expr);
|
||||||
|
if (!reg)
|
||||||
|
return std::unexpected(reg.error());
|
||||||
|
|
||||||
|
current->freereg = mark; // 彻底抛弃孤立表达式的副作用
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} // namespace Fig
|
||||||
13
src/Core/Core.hpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/Core.hpp
|
||||||
|
@brief Core总合集
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
#include <Core/RuntimeTime.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
49
src/Core/CoreIO.cpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreIO.cpp
|
||||||
|
@brief 标准输入输出链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Fig::CoreIO
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) || defined(__APPLE__) || defined (__linux__) || defined (__unix__)
|
||||||
|
std::ostream &GetStdOut()
|
||||||
|
{
|
||||||
|
static std::ostream &out = std::cout;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdErr()
|
||||||
|
{
|
||||||
|
static std::ostream &err = std::cerr;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdLog()
|
||||||
|
{
|
||||||
|
static std::ostream &log = std::clog;
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
std::istream &GetStdCin()
|
||||||
|
{
|
||||||
|
static std::istream &cin = std::cin;
|
||||||
|
return cin;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// link
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void InitConsoleIO()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleCP(CP_UTF8);
|
||||||
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
20
src/Core/CoreIO.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*!
|
||||||
|
@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
|
||||||
143
src/Core/CoreInfos.hpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreInfos.hpp
|
||||||
|
@brief 核心系统信息
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/String/String.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#define __FCORE_VERSION "0.5.0-alpha"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define __FCORE_PLATFORM "Windows"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define __FCORE_PLATFORM "Apple"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define __FCORE_PLATFORM "Linux"
|
||||||
|
#elif defined(__unix__)
|
||||||
|
#define __FCORE_PLATFORM "Unix"
|
||||||
|
#else
|
||||||
|
#define __FCORE_PLATFORM "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "llvm-mingw"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "MinGW"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "GCC"
|
||||||
|
#endif
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "Clang"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define __FCORE_COMPILER "MSVC"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SIZE_MAX == 18446744073709551615ull
|
||||||
|
#define __FCORE_ARCH "64"
|
||||||
|
#else
|
||||||
|
#define __FCORE_ARCH "86"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __FCORE_LINK_DEPS
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
||||||
|
inline constexpr std::string_view LICENSE = "MIT";
|
||||||
|
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
||||||
|
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
||||||
|
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||||
|
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||||
|
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||||
|
|
||||||
|
const std::string LOGO =
|
||||||
|
R"(
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒██████████▓▒▒▒▒
|
||||||
|
▒▒▒▒▒██████████▓▒▒▒▒
|
||||||
|
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒█████████▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒███▓▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||||
|
)";
|
||||||
|
|
||||||
|
const std::string LICENSE_TEXT =
|
||||||
|
R"(
|
||||||
|
MIT License (Fig)
|
||||||
|
|
||||||
|
Copyright (c) 2026 PuqiAR <im@puqiar.top>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
1. The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
2. This project includes code from the following projects with their respective licenses:
|
||||||
|
- magic_enum (MIT License)
|
||||||
|
Copyright (c) 2019 - 2024 Daniil Goncharov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
- json (MIT LICENSE) (for LSP Server JSON-RPC)
|
||||||
|
Copyright (c) 2013-2026 Niels Lohmann
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
)";
|
||||||
|
|
||||||
|
}; // namespace Core
|
||||||
|
}; // namespace Fig
|
||||||
25
src/Core/RuntimeTime.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/RuntimeTime.cpp
|
||||||
|
@brief 系统时间库实现(steady_clock)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/RuntimeTime.hpp>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace Fig::Time
|
||||||
|
{
|
||||||
|
Clock::time_point start_time;
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
static bool flag = false;
|
||||||
|
if (flag)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
start_time = Clock::now();
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
17
src/Core/RuntimeTime.hpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/RuntimeTime.hpp
|
||||||
|
@brief 系统时间库定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace Fig::Time
|
||||||
|
{
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
extern Clock::time_point start_time; // since process start
|
||||||
|
void init();
|
||||||
|
};
|
||||||
59
src/Core/SourceLocations.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/SourceLocations
|
||||||
|
@brief SourcePosition + SourceLocation定义,全局代码定位
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct SourcePosition
|
||||||
|
{
|
||||||
|
size_t line, column, tok_length;
|
||||||
|
|
||||||
|
SourcePosition() { line = column = tok_length = 0; }
|
||||||
|
SourcePosition(size_t _line, size_t _column, size_t _tok_length)
|
||||||
|
{
|
||||||
|
line = _line;
|
||||||
|
column = _column;
|
||||||
|
tok_length = _tok_length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SourceLocation
|
||||||
|
{
|
||||||
|
SourcePosition sp;
|
||||||
|
|
||||||
|
Deps::String fileName;
|
||||||
|
Deps::String packageName;
|
||||||
|
Deps::String functionName;
|
||||||
|
|
||||||
|
SourceLocation() {}
|
||||||
|
SourceLocation(SourcePosition _sp,
|
||||||
|
Deps::String _fileName,
|
||||||
|
Deps::String _packageName,
|
||||||
|
Deps::String _functionName)
|
||||||
|
{
|
||||||
|
sp = std::move(_sp);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
packageName = std::move(_packageName);
|
||||||
|
functionName = std::move(_functionName);
|
||||||
|
}
|
||||||
|
SourceLocation(size_t line,
|
||||||
|
size_t column,
|
||||||
|
size_t tok_length,
|
||||||
|
Deps::String _fileName,
|
||||||
|
Deps::String _packageName,
|
||||||
|
Deps::String _functionName)
|
||||||
|
{
|
||||||
|
sp = SourcePosition(line, column, tok_length);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
packageName = std::move(_packageName);
|
||||||
|
functionName = std::move(_functionName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
34
src/Deps/Deps.hpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/Deps.hpp
|
||||||
|
@brief 依赖库集合
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
#include <Deps/DynArray/DynArray.hpp>
|
||||||
|
#include <Deps/HashMap/HashMap.hpp>
|
||||||
|
#include <Deps/String/CharUtils.hpp>
|
||||||
|
#include <Deps/String/String.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <expected>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
#ifdef __FCORE_LINK_DEPS
|
||||||
|
using Deps::String;
|
||||||
|
using Deps::HashMap;
|
||||||
|
using Deps::CharUtils;
|
||||||
|
using Deps::DynArray;
|
||||||
|
|
||||||
|
template <class _Tp, class _Err>
|
||||||
|
using Result = std::expected<_Tp, _Err>;
|
||||||
|
#endif
|
||||||
|
}; // namespace Fig
|
||||||
14
src/Deps/DynArray/DynArray.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/DynArray/DynArray
|
||||||
|
@brief 依赖库DynArray定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
template<class _Tp, class _Allocator = std::allocator<_Tp>>
|
||||||
|
using DynArray = std::vector<_Tp, _Allocator>;
|
||||||
|
};
|
||||||
20
src/Deps/HashMap/HashMap.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/HashMap/HashMap.hpp
|
||||||
|
@brief 依赖库HashMap定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
template <class _Key,
|
||||||
|
class _Tp,
|
||||||
|
class _Hash = std::hash<_Key>,
|
||||||
|
class _Pred = std::equal_to<_Key>,
|
||||||
|
class _Alloc = std::allocator<std::pair<const _Key, _Tp> >>
|
||||||
|
using HashMap = std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>;
|
||||||
|
};
|
||||||
130
src/Deps/String/CharUtils.hpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/String/CharUtils.hpp
|
||||||
|
@brief char32_t type实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Fig::Deps
|
||||||
|
{
|
||||||
|
class CharUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using U32 = char32_t;
|
||||||
|
|
||||||
|
// ===== 基础 =====
|
||||||
|
|
||||||
|
static constexpr bool isValidScalar(U32 c) noexcept { return c <= 0x10FFFF && !(c >= 0xD800 && c <= 0xDFFF); }
|
||||||
|
|
||||||
|
static constexpr bool isAscii(U32 c) noexcept { return c <= 0x7F; }
|
||||||
|
|
||||||
|
static constexpr bool isControl(U32 c) noexcept { return (c <= 0x1F) || (c == 0x7F); }
|
||||||
|
|
||||||
|
static constexpr bool isPrintable(U32 c) noexcept { return !isControl(c); }
|
||||||
|
|
||||||
|
// ===== ASCII 分类 =====
|
||||||
|
|
||||||
|
static constexpr bool isAsciiLower(U32 c) noexcept { return c >= U'a' && c <= U'z'; }
|
||||||
|
static constexpr bool isAsciiUpper(U32 c) noexcept { return c >= U'A' && c <= U'Z'; }
|
||||||
|
static constexpr bool isAsciiAlpha(U32 c) noexcept { return isAsciiLower(c) || isAsciiUpper(c); }
|
||||||
|
static constexpr bool isAsciiDigit(U32 c) noexcept { return c >= U'0' && c <= U'9'; }
|
||||||
|
|
||||||
|
static constexpr bool isAsciiHexDigit(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return isAsciiDigit(c) || (c >= U'a' && c <= U'f') || (c >= U'A' && c <= U'F');
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool isAsciiSpace(U32 c) noexcept { return c == U' ' || (c >= 0x09 && c <= 0x0D); }
|
||||||
|
|
||||||
|
static constexpr bool isAsciiPunct(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return (c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode White_Space =====
|
||||||
|
|
||||||
|
static constexpr bool isSpace(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiSpace(c);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 0x0085:
|
||||||
|
case 0x00A0:
|
||||||
|
case 0x1680:
|
||||||
|
case 0x2000:
|
||||||
|
case 0x2001:
|
||||||
|
case 0x2002:
|
||||||
|
case 0x2003:
|
||||||
|
case 0x2004:
|
||||||
|
case 0x2005:
|
||||||
|
case 0x2006:
|
||||||
|
case 0x2007:
|
||||||
|
case 0x2008:
|
||||||
|
case 0x2009:
|
||||||
|
case 0x200A:
|
||||||
|
case 0x2028:
|
||||||
|
case 0x2029:
|
||||||
|
case 0x202F:
|
||||||
|
case 0x205F:
|
||||||
|
case 0x3000: return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode Decimal_Number =====
|
||||||
|
|
||||||
|
static constexpr bool isDigit(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiDigit(c);
|
||||||
|
|
||||||
|
return (c >= 0x0660 && c <= 0x0669) || (c >= 0x06F0 && c <= 0x06F9) || (c >= 0x0966 && c <= 0x096F)
|
||||||
|
|| (c >= 0x09E6 && c <= 0x09EF) || (c >= 0x0A66 && c <= 0x0A6F) || (c >= 0x0AE6 && c <= 0x0AEF)
|
||||||
|
|| (c >= 0x0B66 && c <= 0x0B6F) || (c >= 0x0BE6 && c <= 0x0BEF) || (c >= 0x0C66 && c <= 0x0C6F)
|
||||||
|
|| (c >= 0x0CE6 && c <= 0x0CEF) || (c >= 0x0D66 && c <= 0x0D6F) || (c >= 0x0E50 && c <= 0x0E59)
|
||||||
|
|| (c >= 0x0ED0 && c <= 0x0ED9) || (c >= 0x0F20 && c <= 0x0F29) || (c >= 0x1040 && c <= 0x1049)
|
||||||
|
|| (c >= 0x17E0 && c <= 0x17E9) || (c >= 0x1810 && c <= 0x1819) || (c >= 0xFF10 && c <= 0xFF19);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Unicode Letter =====
|
||||||
|
|
||||||
|
static constexpr bool isAlpha(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiAlpha(c);
|
||||||
|
|
||||||
|
return (c >= 0x00C0 && c <= 0x02AF) || (c >= 0x0370 && c <= 0x052F) || (c >= 0x0530 && c <= 0x058F)
|
||||||
|
|| (c >= 0x0590 && c <= 0x05FF) || (c >= 0x0600 && c <= 0x06FF) || (c >= 0x0900 && c <= 0x097F)
|
||||||
|
|| (c >= 0x3040 && c <= 0x30FF) || (c >= 0x3100 && c <= 0x312F) || (c >= 0x4E00 && c <= 0x9FFF)
|
||||||
|
|| (c >= 0xAC00 && c <= 0xD7AF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 标点 / 符号 / 分隔符(工程近似)=====
|
||||||
|
|
||||||
|
static constexpr bool isPunct(U32 c) noexcept
|
||||||
|
{
|
||||||
|
if (isAscii(c)) return isAsciiPunct(c);
|
||||||
|
return (c >= 0x2000 && c <= 0x206F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool isSymbol(U32 c) noexcept
|
||||||
|
{
|
||||||
|
return (c >= 0x20A0 && c <= 0x20CF) || // currency
|
||||||
|
(c >= 0x2100 && c <= 0x214F) || // letterlike
|
||||||
|
(c >= 0x2190 && c <= 0x21FF) || // arrows
|
||||||
|
(c >= 0x2600 && c <= 0x26FF) || // misc symbols
|
||||||
|
(c >= 0x1F300 && c <= 0x1FAFF); // emoji block
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 组合 =====
|
||||||
|
|
||||||
|
static constexpr bool isAlnum(U32 c) noexcept { return isAlpha(c) || isDigit(c); }
|
||||||
|
|
||||||
|
static constexpr bool isHexDigit(U32 c) noexcept { return isAsciiHexDigit(c); }
|
||||||
|
|
||||||
|
static constexpr bool isIdentifierStart(U32 c) noexcept { return isAlpha(c) || c == U'_'; }
|
||||||
|
|
||||||
|
static constexpr bool isIdentifierContinue(U32 c) noexcept { return isAlnum(c) || c == U'_'; }
|
||||||
|
};
|
||||||
|
};
|
||||||
1074
src/Deps/String/String.hpp
Normal file
144
src/Deps/String/StringTest.cpp
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Deps/String/StringTest.cpp
|
||||||
|
@brief String类测试代码
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
#include "String.hpp"
|
||||||
|
|
||||||
|
using Fig::Deps::String;
|
||||||
|
|
||||||
|
static void test_ascii_sso()
|
||||||
|
{
|
||||||
|
String s("hello");
|
||||||
|
assert(s.size() == 5);
|
||||||
|
assert(s[0] == U'h');
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
s.push_back(U'!');
|
||||||
|
assert(s.toStdString() == "hello!");
|
||||||
|
|
||||||
|
s.pop_back();
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
assert(s.starts_with("he"));
|
||||||
|
assert(s.ends_with("lo"));
|
||||||
|
assert(s.contains(U'e'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ascii_heap()
|
||||||
|
{
|
||||||
|
String a("abcdefghijklmnopqrstuvwxyz"); // > SSO
|
||||||
|
assert(a.size() == 26);
|
||||||
|
|
||||||
|
String b("123");
|
||||||
|
a += b;
|
||||||
|
|
||||||
|
assert(a.ends_with("123"));
|
||||||
|
assert(a.find(U'1') == 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_utf8_decode()
|
||||||
|
{
|
||||||
|
String s("你好");
|
||||||
|
assert(s.size() == 2);
|
||||||
|
assert(s.toStdString() == "你好");
|
||||||
|
|
||||||
|
s.push_back(U'!');
|
||||||
|
assert(s.toStdString() == "你好!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_concat_modes()
|
||||||
|
{
|
||||||
|
String a("abc");
|
||||||
|
String b("你好");
|
||||||
|
|
||||||
|
String c = a + b;
|
||||||
|
assert(c.size() == 5);
|
||||||
|
assert(c.toStdString() == "abc你好");
|
||||||
|
|
||||||
|
String d = b + a;
|
||||||
|
assert(d.toStdString() == "你好abc");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_substr_erase_insert()
|
||||||
|
{
|
||||||
|
String s("abcdef");
|
||||||
|
|
||||||
|
String sub = s.substr(2, 3);
|
||||||
|
assert(sub.toStdString() == "cde");
|
||||||
|
|
||||||
|
s.erase(2, 2);
|
||||||
|
assert(s.toStdString() == "abef");
|
||||||
|
|
||||||
|
s.insert(2, String("CD"));
|
||||||
|
assert(s.toStdString() == "abCDef");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_replace()
|
||||||
|
{
|
||||||
|
String s("hello world");
|
||||||
|
s.replace(6, 5, String("Fig"));
|
||||||
|
assert(s.toStdString() == "hello Fig");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_find_rfind()
|
||||||
|
{
|
||||||
|
String s("abcabcabc");
|
||||||
|
|
||||||
|
assert(s.find(String("abc")) == 0);
|
||||||
|
assert(s.find(String("abc"), 1) == 3);
|
||||||
|
assert(s.rfind(String("abc")) == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_compare()
|
||||||
|
{
|
||||||
|
String a("abc");
|
||||||
|
String b("abd");
|
||||||
|
String c("abc");
|
||||||
|
|
||||||
|
assert(a.compare(b) < 0);
|
||||||
|
assert(b.compare(a) > 0);
|
||||||
|
assert(a.compare(c) == 0);
|
||||||
|
assert(a == c);
|
||||||
|
assert(a != b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_resize_append()
|
||||||
|
{
|
||||||
|
String s("abc");
|
||||||
|
s.resize(5, U'x');
|
||||||
|
assert(s.toStdString() == "abcxx");
|
||||||
|
|
||||||
|
s.append(3, U'y');
|
||||||
|
assert(s.toStdString() == "abcxxyyy");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_std_interop()
|
||||||
|
{
|
||||||
|
std::string stds = "hello";
|
||||||
|
String s(stds);
|
||||||
|
assert(s.toStdString() == "hello");
|
||||||
|
|
||||||
|
s += " world";
|
||||||
|
assert(s.toStdString() == "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_ascii_sso();
|
||||||
|
test_ascii_heap();
|
||||||
|
test_utf8_decode();
|
||||||
|
test_concat_modes();
|
||||||
|
test_substr_erase_insert();
|
||||||
|
test_replace();
|
||||||
|
test_find_rfind();
|
||||||
|
test_compare();
|
||||||
|
test_resize_append();
|
||||||
|
test_std_interop();
|
||||||
|
|
||||||
|
std::cout << "All String tests passed.\n";
|
||||||
|
}
|
||||||
47
src/Error/Diagnostics.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Diagnostics.hpp
|
||||||
|
@brief 诊断信息收集器:用于存储警告与非致命错误
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-03-08
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Diagnostics
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DynArray<Error> errors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Report(Error err)
|
||||||
|
{
|
||||||
|
errors.push_back(std::move(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一打印所有收集到的信息
|
||||||
|
void EmitAll(const SourceManager &manager)
|
||||||
|
{
|
||||||
|
for (const auto &err : errors)
|
||||||
|
{
|
||||||
|
ReportError(err, manager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasErrors() const
|
||||||
|
{
|
||||||
|
for (const auto &err : errors)
|
||||||
|
{
|
||||||
|
if (!err.IsWarning()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DynArray<Error>& GetErrors() const { return errors; }
|
||||||
|
|
||||||
|
void Clear() { errors.clear(); }
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
200
src/Error/Error.cpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Error.cpp
|
||||||
|
@brief 错误报告实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/Core.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
void ColoredPrint(const char *color, const char *msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColoredPrint(const char *color, const String &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MultipleStr(const char *c, size_t n)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
for (size_t i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
buf += c;
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ErrorTypeToString(ErrorType type)
|
||||||
|
{
|
||||||
|
using enum ErrorType;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case UnusedSymbol: return "UnusedSymbol";
|
||||||
|
case UnnecessarySemicolon: return "UnnecessarySemicolon";
|
||||||
|
|
||||||
|
case MayBeNull: return "MaybeNull";
|
||||||
|
|
||||||
|
case UnterminatedString: return "UnterminatedString";
|
||||||
|
case UnterminatedComments: return "UnterminatedComments";
|
||||||
|
case InvalidNumberLiteral: return "InvalidNumberLiteral";
|
||||||
|
case InvalidCharacter: return "InvalidCharacter";
|
||||||
|
case InvalidSymbol: return "InvalidSymbol";
|
||||||
|
|
||||||
|
case ExpectedExpression: return "ExpectedExpression";
|
||||||
|
case SyntaxError: return "SyntaxError";
|
||||||
|
|
||||||
|
case RedeclarationError: return "RedeclarationError";
|
||||||
|
case UseUndeclaredIdentifier: return "UseUndeclaredIdentifier";
|
||||||
|
case NotAnLvalue: return "NotAnLvalue";
|
||||||
|
case TypeError: return "TypeError";
|
||||||
|
|
||||||
|
case TooManyLocals: return "TooManyLocals";
|
||||||
|
case TooManyConstants: return "TooManyConstants";
|
||||||
|
|
||||||
|
case RegisterOverflow: return "RegisterOverflow";
|
||||||
|
case InternalError:
|
||||||
|
return "InternalError";
|
||||||
|
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||||
|
}
|
||||||
|
return "UnknownError";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintSystemInfos()
|
||||||
|
{
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
std::stringstream build_info;
|
||||||
|
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH
|
||||||
|
<< '[' << Core::COMPILER << ']' << '\n'
|
||||||
|
<< " Build Time: " << Core::COMPILE_TIME;
|
||||||
|
|
||||||
|
const std::string &build_info_str = build_info.str();
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
err << build_info_str << '\n';
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||||
|
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||||
|
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||||
|
|
||||||
|
namespace TC = TerminalColors;
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
|
||||||
|
uint8_t level = ErrorLevel(error.type);
|
||||||
|
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
||||||
|
const char *level_color =
|
||||||
|
(level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||||
|
|
||||||
|
err << "🔥 "
|
||||||
|
<< level_color
|
||||||
|
//<< '(' << level_name << ')'
|
||||||
|
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color
|
||||||
|
<< ErrorTypeToString(error.type) << TC::Reset << '\n';
|
||||||
|
|
||||||
|
const SourceLocation &location = error.location;
|
||||||
|
|
||||||
|
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.'
|
||||||
|
<< location.functionName << '\'' << " " << location.fileName << " (" << TC::DarkGray
|
||||||
|
<< location.sp.line << ":" << location.sp.column << TC::Cyan << ')' << TC::Reset
|
||||||
|
<< '\n';
|
||||||
|
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||||
|
|
||||||
|
// 尝试打印上3行 下2行
|
||||||
|
|
||||||
|
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
||||||
|
while (!srcManager.HasLine(line_end))
|
||||||
|
{
|
||||||
|
--line_end;
|
||||||
|
}
|
||||||
|
while (!srcManager.HasLine(line_start))
|
||||||
|
{
|
||||||
|
++line_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &getLineNumWidth = [](size_t l) {
|
||||||
|
unsigned int cnt = 0;
|
||||||
|
while (l != 0)
|
||||||
|
{
|
||||||
|
l = l / 10;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
};
|
||||||
|
unsigned int max_line_number_width = getLineNumWidth(line_end);
|
||||||
|
for (size_t i = line_start; i <= line_end; ++i)
|
||||||
|
{
|
||||||
|
unsigned int offset = 2 + 2 + 1;
|
||||||
|
// ' └─ '
|
||||||
|
if (i == location.sp.line)
|
||||||
|
{
|
||||||
|
err << TC::DarkGray << " └─ " << TC::Reset;
|
||||||
|
}
|
||||||
|
else if (i < location.sp.line)
|
||||||
|
{
|
||||||
|
err << TC::DarkGray << " │ " << TC::Reset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err << MultipleStr(" ", offset);
|
||||||
|
}
|
||||||
|
unsigned int cur_line_number_width = getLineNumWidth(i);
|
||||||
|
|
||||||
|
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow
|
||||||
|
<< i << TC::Reset;
|
||||||
|
err << " │ " << srcManager.GetLine(i) << '\n';
|
||||||
|
if (i == location.sp.line)
|
||||||
|
{
|
||||||
|
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
||||||
|
err << MultipleStr(" ", error_col_offset)
|
||||||
|
<< MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||||
|
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
||||||
|
|
||||||
|
err << MultipleStr(" ", error_col_offset)
|
||||||
|
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2)
|
||||||
|
<< "╰─ " << level_color << error.message << TC::Reset << "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err << "\n";
|
||||||
|
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||||
|
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset
|
||||||
|
<< "\n";
|
||||||
|
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||||
|
err << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
||||||
|
// assert(srcManager.HasLine(error.location.sp.line));
|
||||||
|
|
||||||
|
PrintSystemInfos();
|
||||||
|
PrintErrorInfo(error, srcManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportErrors(const std::vector<Error> &errors, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
std::ostream &ost = CoreIO::GetStdErr();
|
||||||
|
PrintSystemInfos();
|
||||||
|
for (const auto &err : errors)
|
||||||
|
{
|
||||||
|
PrintErrorInfo(err, srcManager);
|
||||||
|
ost << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace Fig
|
||||||