Compare commits
11 Commits
dd0b2904ba
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 663fe39070 | |||
| 6b75e028ff | |||
| 878157c2fc | |||
| 35e479fd05 | |||
| 51e831cc6a | |||
| 35b98c4d7f | |||
| 877253cbbc | |||
| cfcdfde170 | |||
| 5e75402b43 | |||
| 642ce66f75 | |||
| 58212a3715 |
33
.ci/Dockerfile
Normal file
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
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: DontAlign
|
||||||
|
|
||||||
|
# 连续赋值时,对齐所有等号
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
|
||||||
|
# 连续声明时,对齐所有声明的变量名
|
||||||
|
AlignConsecutiveDeclarations: true
|
||||||
|
|
||||||
|
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
|
||||||
|
# 水平对齐二元和三元表达式的操作数
|
||||||
|
AlignOperands: true
|
||||||
|
|
||||||
|
# 对齐连续的尾随的注释
|
||||||
|
AlignTrailingComments: true
|
||||||
|
|
||||||
|
# 允许函数声明的所有参数在放在下一行
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
|
||||||
|
# 允许短的块放在同一行
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的case标签放在同一行
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
|
||||||
|
# 允许短的if语句保持在同一行
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
|
||||||
|
# 允许短的循环保持在同一行
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
|
||||||
|
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
||||||
|
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
|
||||||
|
# 总是在多行string字面量前换行
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
|
||||||
|
# 总是在template声明后换行
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
|
||||||
|
# false表示函数实参要么都在同一行,要么都各自一行
|
||||||
|
BinPackArguments: false
|
||||||
|
|
||||||
|
# false表示所有形参要么都在同一行,要么都各自一行
|
||||||
|
BinPackParameters: false
|
||||||
|
|
||||||
|
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
|
||||||
|
BraceWrapping:
|
||||||
|
# class定义后面
|
||||||
|
AfterClass: true
|
||||||
|
# 控制语句后面
|
||||||
|
AfterControlStatement: true
|
||||||
|
# enum定义后面
|
||||||
|
AfterEnum: true
|
||||||
|
# 函数定义后面
|
||||||
|
AfterFunction: true
|
||||||
|
# 命名空间定义后面
|
||||||
|
AfterNamespace: true
|
||||||
|
# struct定义后面
|
||||||
|
AfterStruct: true
|
||||||
|
# union定义后面
|
||||||
|
AfterUnion: true
|
||||||
|
# extern之后
|
||||||
|
AfterExternBlock: false
|
||||||
|
# catch之前
|
||||||
|
BeforeCatch: true
|
||||||
|
# else之前
|
||||||
|
BeforeElse: true
|
||||||
|
# 缩进大括号
|
||||||
|
IndentBraces: false
|
||||||
|
# 分离空函数
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
# 分离空语句
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
# 分离空命名空间
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
|
||||||
|
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
|
||||||
|
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
|
||||||
|
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
|
||||||
|
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
|
||||||
|
# 注:这里认为语句块也属于函数
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
|
||||||
|
# 在三元运算符前换行
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
|
||||||
|
# 在构造函数的初始化列表的冒号后换行
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
|
||||||
|
#BreakInheritanceList: AfterColon
|
||||||
|
|
||||||
|
BreakStringLiterals: false
|
||||||
|
|
||||||
|
# 每行字符的限制,0表示没有限制
|
||||||
|
ColumnLimit: 120
|
||||||
|
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
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
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
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
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
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
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
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
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
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
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.py
Normal file
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(30)
|
||||||
|
t1 = tt()
|
||||||
|
print('cost: ',t1-t0, 'result:', result)
|
||||||
84
ExampleCodes/SpeedTest/fibBenchmark.fig
Normal file
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
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
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();
|
||||||
3
compile_flags.txt
Normal file
3
compile_flags.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-std=c++2b
|
||||||
|
-static
|
||||||
|
-stdlib=libc++
|
||||||
BIN
docs/FigRuntimeValueSystem.png
Normal file
BIN
docs/FigRuntimeValueSystem.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 633 KiB |
24
docs/zh_CN/01-简介.md
Normal file
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
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
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
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
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
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实现
|
||||||
14
src/Ast/Ast.hpp
Normal file
14
src/Ast/Ast.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Ast.hpp
|
||||||
|
@brief Ast总链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Expr/IdentiExpr.hpp>
|
||||||
|
#include <Ast/Expr/InfixExpr.hpp>
|
||||||
|
#include <Ast/Expr/LiteralExpr.hpp>
|
||||||
|
#include <Ast/Expr/PrefixExpr.hpp>
|
||||||
|
|
||||||
51
src/Ast/Base.hpp
Normal file
51
src/Ast/Base.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Base.hpp
|
||||||
|
@brief AstNode基类定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class AstType : std::uint8_t
|
||||||
|
{
|
||||||
|
AstNode, // 基类
|
||||||
|
Expr, // 表达式
|
||||||
|
Stmt, // 语句
|
||||||
|
|
||||||
|
IdentiExpr, // 标识符表达式
|
||||||
|
LiteralExpr, // 字面量表达式
|
||||||
|
PrefixExpr, // 一元 前缀表达式
|
||||||
|
InfixExpr, // 二元 中缀表达式
|
||||||
|
};
|
||||||
|
struct AstNode
|
||||||
|
{
|
||||||
|
AstType type = AstType::AstNode;
|
||||||
|
SourceLocation location;
|
||||||
|
|
||||||
|
virtual String toString() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Expr : public AstNode
|
||||||
|
{
|
||||||
|
Expr()
|
||||||
|
{
|
||||||
|
type = AstType::Expr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stmt : public AstNode
|
||||||
|
{
|
||||||
|
Stmt()
|
||||||
|
{
|
||||||
|
type = AstType::Stmt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
36
src/Ast/Expr/IdentiExpr.hpp
Normal file
36
src/Ast/Expr/IdentiExpr.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/IdentiExpr.hpp
|
||||||
|
@brief IdentiExpr定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct IdentiExpr final : Expr
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
|
||||||
|
IdentiExpr()
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentiExpr(String _name, SourceLocation _loc)
|
||||||
|
{
|
||||||
|
type = AstType::IdentiExpr;
|
||||||
|
name = std::move(_name);
|
||||||
|
location = std::move(_loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<IdentiExpr: {}>", name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
39
src/Ast/Expr/InfixExpr.hpp
Normal file
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
|
||||||
36
src/Ast/Expr/LiteralExpr.hpp
Normal file
36
src/Ast/Expr/LiteralExpr.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Expr/LiteralExpr.hpp
|
||||||
|
@brief 字面量表达式定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Base.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct LiteralExpr final : Expr
|
||||||
|
{
|
||||||
|
Token token;
|
||||||
|
|
||||||
|
LiteralExpr()
|
||||||
|
{
|
||||||
|
type = AstType::LiteralExpr;
|
||||||
|
}
|
||||||
|
LiteralExpr(const Token& token, SourceLocation _location) : token(token)
|
||||||
|
{
|
||||||
|
type = AstType::LiteralExpr;
|
||||||
|
location = std::move(_location);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual String toString() const override
|
||||||
|
{
|
||||||
|
return std::format("<LiteralExpr: {}>", magic_enum::enum_name(token.type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
39
src/Ast/Expr/PrefixExpr.hpp
Normal file
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
184
src/Ast/Operator.cpp
Normal file
184
src/Ast/Operator.cpp
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.cpp
|
||||||
|
@brief 运算符定义内函数实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Ast/Operator.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, UnaryOperator> unaryOpMap{
|
||||||
|
{TokenType::Tilde, UnaryOperator::BitNot},
|
||||||
|
{TokenType::Minus, UnaryOperator::Negate},
|
||||||
|
{TokenType::Not, UnaryOperator::Not},
|
||||||
|
{TokenType::Ampersand, UnaryOperator::AddressOf},
|
||||||
|
};
|
||||||
|
return unaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap()
|
||||||
|
{
|
||||||
|
static HashMap<TokenType, BinaryOperator> binaryOpMap{
|
||||||
|
{TokenType::Plus, BinaryOperator::Add},
|
||||||
|
{TokenType::Minus, BinaryOperator::Subtract},
|
||||||
|
{TokenType::Asterisk, BinaryOperator::Multiply},
|
||||||
|
{TokenType::Slash, BinaryOperator::Divide},
|
||||||
|
{TokenType::Percent, BinaryOperator::Modulo},
|
||||||
|
|
||||||
|
{TokenType::Equal, BinaryOperator::Equal},
|
||||||
|
{TokenType::NotEqual, BinaryOperator::NotEqual},
|
||||||
|
{TokenType::Less, BinaryOperator::Less},
|
||||||
|
{TokenType::Greater, BinaryOperator::Greater},
|
||||||
|
{TokenType::LessEqual, BinaryOperator::LessEqual},
|
||||||
|
{TokenType::GreaterEqual, BinaryOperator::GreaterEqual},
|
||||||
|
|
||||||
|
{TokenType::Is, BinaryOperator::Is},
|
||||||
|
|
||||||
|
{TokenType::And, BinaryOperator::LogicalAnd},
|
||||||
|
{TokenType::Or, BinaryOperator::LogicalOr},
|
||||||
|
|
||||||
|
{TokenType::Power, BinaryOperator::Power},
|
||||||
|
|
||||||
|
{TokenType::Assign, BinaryOperator::Assign},
|
||||||
|
{TokenType::PlusEqual, BinaryOperator::AddAssign},
|
||||||
|
{TokenType::MinusEqual, BinaryOperator::SubAssign},
|
||||||
|
{TokenType::AsteriskEqual, BinaryOperator::MultiplyAssign},
|
||||||
|
{TokenType::SlashEqual, BinaryOperator::DivideAssign},
|
||||||
|
{TokenType::PercentEqual, BinaryOperator::ModuloAssign},
|
||||||
|
{TokenType::CaretEqual, BinaryOperator::BitXorAssign},
|
||||||
|
|
||||||
|
{TokenType::Pipe, BinaryOperator::BitOr},
|
||||||
|
{TokenType::Ampersand, BinaryOperator::BitAnd},
|
||||||
|
{TokenType::ShiftLeft, BinaryOperator::ShiftLeft},
|
||||||
|
{TokenType::ShiftRight, BinaryOperator::ShiftRight},
|
||||||
|
|
||||||
|
{TokenType::Dot, BinaryOperator::MemberAccess},
|
||||||
|
};
|
||||||
|
return binaryOpMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赋值 < 三元 < 逻辑或 < 逻辑与 < 位运算 < 比较 < 位移 < 加减 < 乘除 < 幂 < 一元 < 成员访问 < (后缀)
|
||||||
|
|
||||||
|
/*
|
||||||
|
暂划分:
|
||||||
|
二元运算符:0 - 20000
|
||||||
|
一元运算符:20001 - 40000
|
||||||
|
后缀/成员/其他:40001 - 60001
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<UnaryOperator, BindingPower> unbpm{
|
||||||
|
{UnaryOperator::BitNot, 20001},
|
||||||
|
{UnaryOperator::Negate, 20001},
|
||||||
|
{UnaryOperator::Not, 20001},
|
||||||
|
{UnaryOperator::AddressOf, 20001},
|
||||||
|
};
|
||||||
|
return unbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap()
|
||||||
|
{
|
||||||
|
static HashMap<BinaryOperator, BindingPower> bnbpm{
|
||||||
|
{BinaryOperator::Assign, 100},
|
||||||
|
{BinaryOperator::AddAssign, 100},
|
||||||
|
{BinaryOperator::SubAssign, 100},
|
||||||
|
{BinaryOperator::MultiplyAssign, 100},
|
||||||
|
{BinaryOperator::DivideAssign, 100},
|
||||||
|
{BinaryOperator::ModuloAssign, 100},
|
||||||
|
{BinaryOperator::BitXorAssign, 100},
|
||||||
|
|
||||||
|
{BinaryOperator::LogicalOr, 500},
|
||||||
|
{BinaryOperator::LogicalAnd, 550},
|
||||||
|
|
||||||
|
{BinaryOperator::BitOr, 1000},
|
||||||
|
{BinaryOperator::BitXor, 1100},
|
||||||
|
{BinaryOperator::BitAnd, 1200},
|
||||||
|
|
||||||
|
{BinaryOperator::Equal, 2000},
|
||||||
|
{BinaryOperator::NotEqual, 2000},
|
||||||
|
|
||||||
|
{BinaryOperator::Less, 2100},
|
||||||
|
{BinaryOperator::LessEqual, 2100},
|
||||||
|
{BinaryOperator::Greater, 2100},
|
||||||
|
{BinaryOperator::GreaterEqual, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::Is, 2100},
|
||||||
|
|
||||||
|
{BinaryOperator::ShiftLeft, 3000},
|
||||||
|
{BinaryOperator::ShiftRight, 3000},
|
||||||
|
|
||||||
|
{BinaryOperator::Add, 4000},
|
||||||
|
{BinaryOperator::Subtract, 4000},
|
||||||
|
{BinaryOperator::Multiply, 4500},
|
||||||
|
{BinaryOperator::Divide, 4500},
|
||||||
|
|
||||||
|
{BinaryOperator::Power, 5000},
|
||||||
|
|
||||||
|
{BinaryOperator::MemberAccess, 40001},
|
||||||
|
};
|
||||||
|
return bnbpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator op)
|
||||||
|
{
|
||||||
|
return GetUnaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
return GetBinaryOpBindingPowerMap().at(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
右结合,左绑定力 >= 右
|
||||||
|
a = b = c
|
||||||
|
a = (b = c)
|
||||||
|
*/
|
||||||
|
case BinaryOperator::Assign:
|
||||||
|
case BinaryOperator::AddAssign:
|
||||||
|
case BinaryOperator::SubAssign:
|
||||||
|
case BinaryOperator::MultiplyAssign:
|
||||||
|
case BinaryOperator::DivideAssign:
|
||||||
|
case BinaryOperator::ModuloAssign:
|
||||||
|
case BinaryOperator::BitXorAssign:
|
||||||
|
case BinaryOperator::Power: return GetBinaryOpLBp(op) - 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
左结合, 左绑定力 < 右
|
||||||
|
a * b * c
|
||||||
|
(a * b) * c
|
||||||
|
*/
|
||||||
|
return GetBinaryOpLBp(op) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary /* = true*/)
|
||||||
|
{
|
||||||
|
if (binary)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
return GetUnaryOpMap().contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetUnaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &token)
|
||||||
|
{
|
||||||
|
return GetBinaryOpMap().at(token.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
82
src/Ast/Operator.hpp
Normal file
82
src/Ast/Operator.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Ast/Operator.hpp
|
||||||
|
@brief 运算符定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class UnaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
BitNot, // 位运算取反 ~
|
||||||
|
Negate, // 取反 -
|
||||||
|
Not, // 逻辑非 ! / not
|
||||||
|
AddressOf, // 取引用 &
|
||||||
|
};
|
||||||
|
enum class BinaryOperator : std::uint8_t
|
||||||
|
{
|
||||||
|
Add, // 加 +
|
||||||
|
Subtract, // 减 -
|
||||||
|
Multiply, // 乘 *
|
||||||
|
Divide, // 除 /
|
||||||
|
Modulo, // 取模 %
|
||||||
|
|
||||||
|
Equal, // 等于 ==
|
||||||
|
NotEqual, // 不等于 !=
|
||||||
|
Less, // 小于 <
|
||||||
|
Greater, // 大于 >
|
||||||
|
LessEqual, // 小于等于 <=
|
||||||
|
GreaterEqual, // 大于等于 >=
|
||||||
|
|
||||||
|
Is, // is操作符
|
||||||
|
|
||||||
|
LogicalAnd, // 逻辑与 && / and
|
||||||
|
LogicalOr, // 逻辑或 || / or
|
||||||
|
|
||||||
|
Power, // 幂运算 **
|
||||||
|
|
||||||
|
Assign, // 赋值(修改) =
|
||||||
|
AddAssign, // +=
|
||||||
|
SubAssign, // -=
|
||||||
|
MultiplyAssign, // *=
|
||||||
|
DivideAssign, // /=
|
||||||
|
ModuloAssign, // %=
|
||||||
|
BitXorAssign, // ^=
|
||||||
|
|
||||||
|
// 位运算
|
||||||
|
BitAnd, // 按位与 &
|
||||||
|
BitOr, // 按位或 |
|
||||||
|
BitXor, // 异或 ^
|
||||||
|
ShiftLeft, // 左移
|
||||||
|
ShiftRight, // 右移
|
||||||
|
|
||||||
|
// 成员访问
|
||||||
|
MemberAccess, // .
|
||||||
|
};
|
||||||
|
|
||||||
|
using BindingPower = unsigned int;
|
||||||
|
|
||||||
|
HashMap<TokenType, UnaryOperator> &GetUnaryOpMap();
|
||||||
|
HashMap<TokenType, BinaryOperator> &GetBinaryOpMap();
|
||||||
|
|
||||||
|
HashMap<UnaryOperator, BindingPower> &GetUnaryOpBindingPowerMap();
|
||||||
|
HashMap<BinaryOperator, BindingPower> &GetBinaryOpBindingPowerMap();
|
||||||
|
|
||||||
|
BindingPower GetUnaryOpRBp(UnaryOperator);
|
||||||
|
|
||||||
|
BindingPower GetBinaryOpLBp(BinaryOperator);
|
||||||
|
BindingPower GetBinaryOpRBp(BinaryOperator);
|
||||||
|
|
||||||
|
bool IsTokenOp(TokenType type, bool binary = true);
|
||||||
|
|
||||||
|
UnaryOperator TokenToUnaryOp(const Token &);
|
||||||
|
BinaryOperator TokenToBinaryOp(const Token &);
|
||||||
|
}; // namespace Fig
|
||||||
13
src/Core/Core.hpp
Normal file
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>
|
||||||
37
src/Core/CoreIO.cpp
Normal file
37
src/Core/CoreIO.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreIO.cpp
|
||||||
|
@brief 标准输入输出链接
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/CoreIO.hpp>
|
||||||
|
#include <Core/CoreInfos.hpp>
|
||||||
|
|
||||||
|
namespace Fig::CoreIO
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) || defined(__APPLE__) || defined (__linux__) || defined (__unix__)
|
||||||
|
std::ostream &GetStdOut()
|
||||||
|
{
|
||||||
|
static std::ostream &out = std::cout;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdErr()
|
||||||
|
{
|
||||||
|
static std::ostream &err = std::cerr;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
std::ostream &GetStdLog()
|
||||||
|
{
|
||||||
|
static std::ostream &log = std::clog;
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
std::istream &GetStdCin()
|
||||||
|
{
|
||||||
|
static std::istream &cin = std::cin;
|
||||||
|
return cin;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// link
|
||||||
|
#endif
|
||||||
|
};
|
||||||
18
src/Core/CoreIO.hpp
Normal file
18
src/Core/CoreIO.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreIO.hpp
|
||||||
|
@brief 标准输入输出链接定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Fig::CoreIO
|
||||||
|
{
|
||||||
|
std::ostream &GetStdOut();
|
||||||
|
std::ostream &GetStdErr();
|
||||||
|
std::ostream &GetStdLog();
|
||||||
|
std::istream &GetStdCin();
|
||||||
|
};
|
||||||
67
src/Core/CoreInfos.hpp
Normal file
67
src/Core/CoreInfos.hpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Core/CoreInfos.hpp
|
||||||
|
@brief 核心系统信息
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/String/String.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#define __FCORE_VERSION "0.5.0-alpha"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define __FCORE_PLATFORM "Windows"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define __FCORE_PLATFORM "Apple"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define __FCORE_PLATFORM "Linux"
|
||||||
|
#elif defined(__unix__)
|
||||||
|
#define __FCORE_PLATFORM "Unix"
|
||||||
|
#else
|
||||||
|
#define __FCORE_PLATFORM "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "llvm-mingw"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "MinGW"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "GCC"
|
||||||
|
#endif
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "Clang"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define __FCORE_COMPILER "MSVC"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SIZE_MAX == 18446744073709551615ull
|
||||||
|
#define __FCORE_ARCH "64"
|
||||||
|
#else
|
||||||
|
#define __FCORE_ARCH "86"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __FCORE_LINK_DEPS
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
||||||
|
inline constexpr std::string_view LICENSE = "MIT";
|
||||||
|
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
||||||
|
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
||||||
|
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||||
|
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||||
|
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||||
|
}; // namespace Core
|
||||||
|
}; // namespace Fig
|
||||||
25
src/Core/RuntimeTime.cpp
Normal file
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
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
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
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
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
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
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'_'; }
|
||||||
|
};
|
||||||
|
};
|
||||||
1094
src/Deps/String/String.hpp
Normal file
1094
src/Deps/String/String.hpp
Normal file
File diff suppressed because it is too large
Load Diff
144
src/Deps/String/StringTest.cpp
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";
|
||||||
|
}
|
||||||
165
src/Error/Error.cpp
Normal file
165
src/Error/Error.cpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Error.cpp
|
||||||
|
@brief 错误报告实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Core/Core.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
void ColoredPrint(const char *color, const char *msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColoredPrint(const char *color, const std::string &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColoredPrint(const char *color, const String &msg, std::ostream &ost = CoreIO::GetStdErr())
|
||||||
|
{
|
||||||
|
ost << color << msg << TerminalColors::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string MultipleStr(const char *c, size_t n)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
for (size_t i = 0; i < n; ++i) { buf += c; }
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ErrorTypeToString(ErrorType type)
|
||||||
|
{
|
||||||
|
using enum ErrorType;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case UnusedSymbol: return "UnusedSymbol";
|
||||||
|
|
||||||
|
case MayBeNull: return "MaybeNull";
|
||||||
|
|
||||||
|
case UnterminatedString: return "UnterminatedString";
|
||||||
|
case UnterminatedComments: return "UnterminatedComments";
|
||||||
|
case InvalidNumberLiteral: return "InvalidNumberLiteral";
|
||||||
|
case InvalidCharacter: return "InvalidCharacter";
|
||||||
|
case InvalidSymbol: return "InvalidSymbol";
|
||||||
|
|
||||||
|
case ExpectedExpression: return "ExpectedExpression";
|
||||||
|
case SyntaxError: return "SyntaxError";
|
||||||
|
|
||||||
|
// default: return "Some one forgot to add case to `ErrorTypeToString`";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintSystemInfos()
|
||||||
|
{
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
std::stringstream build_info;
|
||||||
|
build_info << "\r🌘 Fig v" << Core::VERSION << " on " << Core::PLATFORM << ' ' << Core::ARCH << '['
|
||||||
|
<< Core::COMPILER << ']' << '\n'
|
||||||
|
<< " Build Time: " << Core::COMPILE_TIME;
|
||||||
|
|
||||||
|
const std::string &build_info_str = build_info.str();
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
err << build_info_str << '\n';
|
||||||
|
err << MultipleStr("─", build_info_str.size()) << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintErrorInfo(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
static constexpr const char *MinorColor = "\033[38;2;138;227;198m";
|
||||||
|
static constexpr const char *MediumColor = "\033[38;2;255;199;95m";
|
||||||
|
static constexpr const char *CriticalColor = "\033[38;2;255;107;107m";
|
||||||
|
|
||||||
|
namespace TC = TerminalColors;
|
||||||
|
std::ostream &err = CoreIO::GetStdErr();
|
||||||
|
|
||||||
|
uint8_t level = ErrorLevel(error.type);
|
||||||
|
// const char *level_name = (level == 1 ? "Minor" : (level == 2 ? "Medium" : "Critical"));
|
||||||
|
const char *level_color = (level == 1 ? MinorColor : (level == 2 ? MediumColor : CriticalColor));
|
||||||
|
|
||||||
|
err << "🔥 "
|
||||||
|
<< level_color
|
||||||
|
//<< '(' << level_name << ')'
|
||||||
|
<< 'E' << static_cast<int>(error.type) << TC::Reset << ": " << level_color << ErrorTypeToString(error.type)
|
||||||
|
<< TC::Reset << '\n';
|
||||||
|
|
||||||
|
const SourceLocation &location = error.location;
|
||||||
|
|
||||||
|
err << TC::DarkGray << " ┌─> Fn " << TC::Cyan << '\'' << location.packageName << '.' << location.functionName
|
||||||
|
<< '\'' << " " << location.fileName << " (" << TC::DarkGray << location.sp.line << ":" << location.sp.column
|
||||||
|
<< TC::Cyan << ')' << TC::Reset << '\n';
|
||||||
|
err << TC::DarkGray << " │" << '\n' << " │" << TC::Reset << '\n';
|
||||||
|
|
||||||
|
// 尝试打印上3行 下2行
|
||||||
|
|
||||||
|
int64_t line_start = location.sp.line - 3, line_end = location.sp.line + 2;
|
||||||
|
while (!srcManager.HasLine(line_end)) { --line_end; }
|
||||||
|
while (!srcManager.HasLine(line_start)) { ++line_start; }
|
||||||
|
|
||||||
|
const auto &getLineNumWidth = [](size_t l) {
|
||||||
|
unsigned int cnt = 0;
|
||||||
|
while (l != 0)
|
||||||
|
{
|
||||||
|
l = l / 10;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
};
|
||||||
|
unsigned int max_line_number_width = getLineNumWidth(line_end);
|
||||||
|
for (size_t i = line_start; i <= line_end; ++i)
|
||||||
|
{
|
||||||
|
unsigned int offset = 2 + 2 + 1;
|
||||||
|
// ' └─ '
|
||||||
|
if (i == location.sp.line) { err << TC::DarkGray << " └─ " << TC::Reset; }
|
||||||
|
else if (i < location.sp.line) { err << TC::DarkGray << " │ " << TC::Reset; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err << MultipleStr(" ", offset);
|
||||||
|
}
|
||||||
|
unsigned int cur_line_number_width = getLineNumWidth(i);
|
||||||
|
|
||||||
|
err << MultipleStr(" ", max_line_number_width - cur_line_number_width) << TC::Yellow << i << TC::Reset;
|
||||||
|
err << " │ " << srcManager.GetLine(i) << '\n';
|
||||||
|
if (i == location.sp.line)
|
||||||
|
{
|
||||||
|
unsigned int error_col_offset = offset + 1 + max_line_number_width + 2;
|
||||||
|
err << MultipleStr(" ", error_col_offset) << MultipleStr(" ", location.sp.column - 1) << TC::LightGreen
|
||||||
|
<< MultipleStr("^", location.sp.tok_length) << TC::Reset << '\n';
|
||||||
|
|
||||||
|
err << MultipleStr(" ", error_col_offset)
|
||||||
|
<< MultipleStr(" ", location.sp.column - 1 + location.sp.tok_length / 2) << "╰─ " << level_color
|
||||||
|
<< error.message << TC::Reset << "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err << "\n";
|
||||||
|
err << "❓ " << TC::DarkGray << "Thrower: " << error.thrower_loc.function_name() << " ("
|
||||||
|
<< error.thrower_loc.file_name() << ":" << error.thrower_loc.line() << ")" << TC::Reset << "\n";
|
||||||
|
err << "💡 " << TC::Blue << "Suggestion: " << error.suggestion << TC::Reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportError(const Error &error, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
assert(srcManager.read && "ReportError: srcManager doesn't read source");
|
||||||
|
assert(srcManager.HasLine(error.location.sp.line));
|
||||||
|
|
||||||
|
PrintSystemInfos();
|
||||||
|
PrintErrorInfo(error, srcManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportErrors(const std::vector<Error> &errors, const SourceManager &srcManager)
|
||||||
|
{
|
||||||
|
std::ostream &ost = CoreIO::GetStdErr();
|
||||||
|
PrintSystemInfos();
|
||||||
|
for (const auto &err : errors)
|
||||||
|
{
|
||||||
|
PrintErrorInfo(err, srcManager);
|
||||||
|
ost << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace Fig
|
||||||
167
src/Error/Error.hpp
Normal file
167
src/Error/Error.hpp
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Error/Error.hpp
|
||||||
|
@brief Error定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <SourceManager/SourceManager.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
#include <source_location>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
0-1000 Minor
|
||||||
|
1001-2000 Medium
|
||||||
|
2001-3000 Critical
|
||||||
|
*/
|
||||||
|
enum class ErrorType : unsigned int
|
||||||
|
{
|
||||||
|
/* Minor */
|
||||||
|
UnusedSymbol = 0,
|
||||||
|
|
||||||
|
/* Medium */
|
||||||
|
MayBeNull = 1001,
|
||||||
|
|
||||||
|
/* Critical */
|
||||||
|
|
||||||
|
// lexer errors
|
||||||
|
UnterminatedString = 2001,
|
||||||
|
UnterminatedComments,
|
||||||
|
InvalidNumberLiteral,
|
||||||
|
InvalidCharacter,
|
||||||
|
InvalidSymbol,
|
||||||
|
|
||||||
|
// parser errors
|
||||||
|
ExpectedExpression,
|
||||||
|
SyntaxError,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *ErrorTypeToString(ErrorType type);
|
||||||
|
|
||||||
|
struct Error
|
||||||
|
{
|
||||||
|
ErrorType type;
|
||||||
|
String message;
|
||||||
|
String suggestion;
|
||||||
|
|
||||||
|
SourceLocation location;
|
||||||
|
std::source_location thrower_loc;
|
||||||
|
|
||||||
|
Error() {}
|
||||||
|
Error(ErrorType _type,
|
||||||
|
const String &_message,
|
||||||
|
const String &_suggestion,
|
||||||
|
const SourceLocation &_location,
|
||||||
|
const std::source_location &_throwerloc = std::source_location::current())
|
||||||
|
{
|
||||||
|
type = _type;
|
||||||
|
message = _message;
|
||||||
|
suggestion = _suggestion;
|
||||||
|
location = _location;
|
||||||
|
thrower_loc = _throwerloc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace TerminalColors
|
||||||
|
{
|
||||||
|
constexpr const char *Reset = "\033[0m";
|
||||||
|
constexpr const char *Bold = "\033[1m";
|
||||||
|
constexpr const char *Dim = "\033[2m";
|
||||||
|
constexpr const char *Italic = "\033[3m";
|
||||||
|
constexpr const char *Underline = "\033[4m";
|
||||||
|
constexpr const char *Blink = "\033[5m";
|
||||||
|
constexpr const char *Reverse = "\033[7m"; // 前背景反色
|
||||||
|
constexpr const char *Hidden = "\033[8m"; // 隐藏文本
|
||||||
|
constexpr const char *Strike = "\033[9m"; // 删除线
|
||||||
|
|
||||||
|
constexpr const char *Black = "\033[30m";
|
||||||
|
constexpr const char *Red = "\033[31m";
|
||||||
|
constexpr const char *Green = "\033[32m";
|
||||||
|
constexpr const char *Yellow = "\033[33m";
|
||||||
|
constexpr const char *Blue = "\033[34m";
|
||||||
|
constexpr const char *Magenta = "\033[35m";
|
||||||
|
constexpr const char *Cyan = "\033[36m";
|
||||||
|
constexpr const char *White = "\033[37m";
|
||||||
|
|
||||||
|
constexpr const char *LightBlack = "\033[90m";
|
||||||
|
constexpr const char *LightRed = "\033[91m";
|
||||||
|
constexpr const char *LightGreen = "\033[92m";
|
||||||
|
constexpr const char *LightYellow = "\033[93m";
|
||||||
|
constexpr const char *LightBlue = "\033[94m";
|
||||||
|
constexpr const char *LightMagenta = "\033[95m";
|
||||||
|
constexpr const char *LightCyan = "\033[96m";
|
||||||
|
constexpr const char *LightWhite = "\033[97m";
|
||||||
|
|
||||||
|
constexpr const char *DarkRed = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *DarkGreen = "\033[38;2;0;100;0m";
|
||||||
|
constexpr const char *DarkYellow = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *DarkBlue = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *DarkMagenta = "\033[38;2;100;0;100m";
|
||||||
|
constexpr const char *DarkCyan = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *DarkGray = "\033[38;2;64;64;64m";
|
||||||
|
constexpr const char *Gray = "\033[38;2;128;128;128m";
|
||||||
|
constexpr const char *Silver = "\033[38;2;192;192;192m";
|
||||||
|
|
||||||
|
constexpr const char *Navy = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *RoyalBlue = "\033[38;2;65;105;225m";
|
||||||
|
constexpr const char *ForestGreen = "\033[38;2;34;139;34m";
|
||||||
|
constexpr const char *Olive = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *Teal = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *Maroon = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *Purple = "\033[38;2;128;0;128m";
|
||||||
|
constexpr const char *Orange = "\033[38;2;255;165;0m";
|
||||||
|
constexpr const char *Gold = "\033[38;2;255;215;0m";
|
||||||
|
constexpr const char *Pink = "\033[38;2;255;192;203m";
|
||||||
|
constexpr const char *Crimson = "\033[38;2;220;20;60m";
|
||||||
|
|
||||||
|
constexpr const char *OnBlack = "\033[40m";
|
||||||
|
constexpr const char *OnRed = "\033[41m";
|
||||||
|
constexpr const char *OnGreen = "\033[42m";
|
||||||
|
constexpr const char *OnYellow = "\033[43m";
|
||||||
|
constexpr const char *OnBlue = "\033[44m";
|
||||||
|
constexpr const char *OnMagenta = "\033[45m";
|
||||||
|
constexpr const char *OnCyan = "\033[46m";
|
||||||
|
constexpr const char *OnWhite = "\033[47m";
|
||||||
|
|
||||||
|
constexpr const char *OnLightBlack = "\033[100m";
|
||||||
|
constexpr const char *OnLightRed = "\033[101m";
|
||||||
|
constexpr const char *OnLightGreen = "\033[102m";
|
||||||
|
constexpr const char *OnLightYellow = "\033[103m";
|
||||||
|
constexpr const char *OnLightBlue = "\033[104m";
|
||||||
|
constexpr const char *OnLightMagenta = "\033[105m";
|
||||||
|
constexpr const char *OnLightCyan = "\033[106m";
|
||||||
|
constexpr const char *OnLightWhite = "\033[107m";
|
||||||
|
|
||||||
|
constexpr const char *OnDarkBlue = "\033[48;2;0;0;128m";
|
||||||
|
constexpr const char *OnGreenYellow = "\033[48;2;173;255;47m";
|
||||||
|
constexpr const char *OnOrange = "\033[48;2;255;165;0m";
|
||||||
|
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
||||||
|
}; // namespace TerminalColors
|
||||||
|
|
||||||
|
inline uint8_t ErrorLevel(ErrorType t)
|
||||||
|
{
|
||||||
|
unsigned int id = static_cast<int>(t);
|
||||||
|
if (id <= 1000)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (id > 1000 && id <= 2000)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (id > 2000)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportError(const Error &error, const SourceManager &srcManager);
|
||||||
|
}; // namespace Fig
|
||||||
287
src/Lexer/Lexer.cpp
Normal file
287
src/Lexer/Lexer.cpp
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Lexer/Lexer.cpp
|
||||||
|
@brief 词法分析器(materialized lexeme)实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
总则:
|
||||||
|
Lexer不涉及语义部分,语义为Parser及之后的部分确定!
|
||||||
|
确定边界 --> 分词
|
||||||
|
无法确定 --> 错误的源,报错
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanComments()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 2, TokenType::Comments);
|
||||||
|
rd.skip(2); // 跳过 //
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
if (rd.current() == U'\n')
|
||||||
|
{
|
||||||
|
rd.next(); // skip '\n'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rd.next();
|
||||||
|
} while (rd.hasNext());
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanMultilineComments()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 2, TokenType::Comments);
|
||||||
|
SourcePosition startPos = rd.currentPosition();
|
||||||
|
rd.skip(2); // 跳过 / *
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::UnterminatedComments,
|
||||||
|
"unterminated multiline comments",
|
||||||
|
"insert '*/'",
|
||||||
|
makeSourceLocation(startPos)));
|
||||||
|
}
|
||||||
|
if (rd.current() == U'*' && rd.peekIf() == U'/')
|
||||||
|
{
|
||||||
|
rd.skip(2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanIdentifierOrKeyword()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 1, TokenType::Identifier);
|
||||||
|
String value; // 用于判断是标识符还是关键字
|
||||||
|
value.push_back(rd.produce()); // 加入第一个
|
||||||
|
|
||||||
|
while (CharUtils::isIdentifierContinue(rd.current())) // continue: _ / 0-9 / aA - zZ
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
value.push_back(rd.produce());
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Token::keywordMap.contains(value))
|
||||||
|
{
|
||||||
|
tok.type = Token::keywordMap.at(value);
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::scanNumberLiteral()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::LiteralNumber);
|
||||||
|
state = State::ScanDec;
|
||||||
|
|
||||||
|
if (rd.current() == U'0')
|
||||||
|
{
|
||||||
|
char32_t _peek = std::tolower(rd.peekIf());
|
||||||
|
if (_peek == U'b')
|
||||||
|
{
|
||||||
|
state = State::ScanBin;
|
||||||
|
rd.skip(2); // 跳过 0b
|
||||||
|
tok.length += 2;
|
||||||
|
}
|
||||||
|
else if (_peek == U'x')
|
||||||
|
{
|
||||||
|
state = State::ScanHex;
|
||||||
|
rd.skip(2); // 跳过 0x
|
||||||
|
tok.length += 2;
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// return std::unexpected(Error(ErrorType::InvalidNumberLiteral,
|
||||||
|
// std::format("bad number postfix 0{}", String(_peek)),
|
||||||
|
// "correct it",
|
||||||
|
// makeSourceLocation(rd.currentPosition())));
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char32_t current = rd.current();
|
||||||
|
if (state == State::ScanDec && !CharUtils::isDigit(current))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == State::ScanHex && !CharUtils::isHexDigit(current))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == State::ScanBin && current != U'0' && current != U'1')
|
||||||
|
{
|
||||||
|
// return std::unexpected(
|
||||||
|
// Error(ErrorType::InvalidNumberLiteral,
|
||||||
|
// std::format("invalid binary number literal, scanning '{}'", String(¤t)),
|
||||||
|
// "correct it",
|
||||||
|
// makeSourceLocation(rd.currentPosition())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
} while (!rd.isAtEnd());
|
||||||
|
|
||||||
|
// 科学计数法
|
||||||
|
while (!rd.isAtEnd() && state == State::ScanDec
|
||||||
|
&& (rd.current() == U'e' || rd.current() == U'E' || rd.current() == U'_' || rd.current() == U'+'
|
||||||
|
|| rd.current() == U'-' || CharUtils::isDigit(rd.current())))
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
Result<Token, Error> Lexer::scanStringLiteral()
|
||||||
|
{
|
||||||
|
state = (rd.current() == U'"' ? State::ScanStringDQ : Lexer::State::ScanStringSQ);
|
||||||
|
|
||||||
|
SourcePosition startPos = rd.currentPosition();
|
||||||
|
|
||||||
|
rd.next(); // skip " / '
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::LiteralString);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (state == State::ScanStringDQ && rd.current() == U'"')
|
||||||
|
{
|
||||||
|
rd.next(); // skip '"'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (state == State::ScanStringSQ && rd.current() == U'\'')
|
||||||
|
{
|
||||||
|
rd.next(); // skip `'`
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return std::unexpected(
|
||||||
|
Error(ErrorType::UnterminatedString,
|
||||||
|
"unterminated string literal",
|
||||||
|
std::format("insert '{}'", String((state == State::ScanStringDQ ? "\"" : "'"))),
|
||||||
|
makeSourceLocation(startPos)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tok.length++;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
Result<Token, Error> Lexer::scanPunct()
|
||||||
|
{
|
||||||
|
Token tok(rd.currentIndex(), 0, TokenType::Illegal);
|
||||||
|
|
||||||
|
auto startsWith = [&](const String &prefix) -> bool {
|
||||||
|
for (const auto &p : Token::punctMap)
|
||||||
|
{
|
||||||
|
const String &op = p.first;
|
||||||
|
if (op.starts_with(prefix))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
String sym;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
String candidate = sym + rd.current();
|
||||||
|
if (startsWith(candidate))
|
||||||
|
{
|
||||||
|
rd.next();
|
||||||
|
tok.length++;
|
||||||
|
sym = candidate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (!rd.isAtEnd() && CharUtils::isPunct(rd.current()));
|
||||||
|
|
||||||
|
if (!Token::punctMap.contains(sym))
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::InvalidSymbol,
|
||||||
|
std::format("invalid symbol `{}`", sym),
|
||||||
|
"correct it",
|
||||||
|
makeSourceLocation(rd.currentPosition())));
|
||||||
|
}
|
||||||
|
tok.type = Token::punctMap.at(sym);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lexer::skipWhitespaces()
|
||||||
|
{
|
||||||
|
while (!rd.isAtEnd())
|
||||||
|
{
|
||||||
|
char32_t current = rd.current();
|
||||||
|
if (current == EOF || !CharUtils::isAsciiSpace(current)) // 检查 EOF
|
||||||
|
break;
|
||||||
|
rd.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> Lexer::NextToken()
|
||||||
|
{
|
||||||
|
if (rd.isAtEnd())
|
||||||
|
{
|
||||||
|
return Token(rd.currentIndex(), 0, TokenType::EndOfFile);
|
||||||
|
}
|
||||||
|
if (rd.current() == U'\0')
|
||||||
|
{
|
||||||
|
return Token(rd.currentIndex(), 1, TokenType::EndOfFile);
|
||||||
|
}
|
||||||
|
if (rd.current() == U'/' && rd.peekIf() == U'/')
|
||||||
|
{
|
||||||
|
return scanComments();
|
||||||
|
}
|
||||||
|
else if (rd.current() == U'/' && rd.peekIf() == U'*')
|
||||||
|
{
|
||||||
|
return scanMultilineComments();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isIdentifierStart(rd.current()))
|
||||||
|
{
|
||||||
|
return scanIdentifierOrKeyword();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isDigit(rd.current()))
|
||||||
|
{
|
||||||
|
return scanNumberLiteral();
|
||||||
|
}
|
||||||
|
else if (rd.current() == U'"' || rd.current() == U'\'')
|
||||||
|
{
|
||||||
|
return scanStringLiteral();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isPunct(rd.current()))
|
||||||
|
{
|
||||||
|
return scanPunct();
|
||||||
|
}
|
||||||
|
else if (CharUtils::isSpace(rd.current()))
|
||||||
|
{
|
||||||
|
skipWhitespaces();
|
||||||
|
return NextToken();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(
|
||||||
|
ErrorType::InvalidCharacter,
|
||||||
|
std::format("invalid character '{}' (U+{})", String(rd.current()), static_cast<int>(rd.current())),
|
||||||
|
"correct it",
|
||||||
|
makeSourceLocation(rd.currentPosition())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; // namespace Fig
|
||||||
165
src/Lexer/Lexer.hpp
Normal file
165
src/Lexer/Lexer.hpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Lexer/Lexer.hpp
|
||||||
|
@brief 词法分析器(materialized lexeme)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-13
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class SourceReader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
String source;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
SourcePosition pos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SourceReader()
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
pos.line = pos.column = 1;
|
||||||
|
}
|
||||||
|
SourceReader(const String &_source) // copy
|
||||||
|
{
|
||||||
|
source = _source;
|
||||||
|
index = 0;
|
||||||
|
pos.line = pos.column = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourcePosition ¤tPosition() { return pos; }
|
||||||
|
|
||||||
|
inline char32_t current() const
|
||||||
|
{
|
||||||
|
assert(index < source.length() && "SourceReader: get current failed, index out of range");
|
||||||
|
return source[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t currentIf() const
|
||||||
|
{
|
||||||
|
if (index >= source.length())
|
||||||
|
{
|
||||||
|
return U'\0';
|
||||||
|
}
|
||||||
|
return source[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hasNext() const { return index < source.length() - 1; }
|
||||||
|
|
||||||
|
inline char32_t peek() const
|
||||||
|
{
|
||||||
|
assert((index + 1) < source.length() && "SourceReader: get peek failed, index out of range");
|
||||||
|
return source[index + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t peekIf() const
|
||||||
|
{
|
||||||
|
if ((index + 1) < source.length()) { return source[index + 1]; }
|
||||||
|
return 0xFFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char32_t produce()
|
||||||
|
{
|
||||||
|
// returns current rune, then next
|
||||||
|
char32_t c = current();
|
||||||
|
next();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void next()
|
||||||
|
{
|
||||||
|
char32_t consumed = currentIf();
|
||||||
|
|
||||||
|
++index;
|
||||||
|
if (consumed == U'\n')
|
||||||
|
{
|
||||||
|
++pos.line;
|
||||||
|
pos.column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++pos.column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void skip(size_t n)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < n; ++i) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t currentIndex() const { return index; }
|
||||||
|
|
||||||
|
inline bool isAtEnd() const { return index >= source.length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Lexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class State : uint8_t
|
||||||
|
{
|
||||||
|
Error,
|
||||||
|
Standby,
|
||||||
|
End,
|
||||||
|
|
||||||
|
ScanComments, // 单行注释
|
||||||
|
ScanMultilineComments, // 多行注释
|
||||||
|
ScanIdentifier, // 关键字也算
|
||||||
|
|
||||||
|
ScanDec, // 十进制数字, 如 1.2 31, 3.14e+3, 1_000_0000
|
||||||
|
ScanBin, // 二进制数字, 如 0b0001 / 0B0001
|
||||||
|
ScanHex, // 十六进制数字, 如 0xABCD / 0XabCd
|
||||||
|
ScanStringDQ, // 双引号字符串, 如 "hello, world!"
|
||||||
|
ScanStringSQ, // 单引号字符串, 如 'hello'
|
||||||
|
ScanBool, // 布尔字面量, true / false
|
||||||
|
ScanNull, // 空值字面量, null
|
||||||
|
|
||||||
|
ScanPunct, // 符号
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
String fileName;
|
||||||
|
SourceReader rd;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Result<Token, Error> scanComments();
|
||||||
|
Result<Token, Error> scanMultilineComments();
|
||||||
|
|
||||||
|
Result<Token, Error> scanIdentifierOrKeyword();
|
||||||
|
|
||||||
|
Result<Token, Error> scanNumberLiteral();
|
||||||
|
Result<Token, Error> scanStringLiteral(); // 支持多行
|
||||||
|
// Result<Token, Error> scanBoolLiteral(); 由 scanIdentifier...扫描
|
||||||
|
// Result<Token, Error> scanLiteralNull(); 由 scanIdentifier...扫描
|
||||||
|
|
||||||
|
Result<Token, Error> scanPunct();
|
||||||
|
|
||||||
|
void skipWhitespaces();
|
||||||
|
|
||||||
|
public:
|
||||||
|
State state = State::Standby;
|
||||||
|
|
||||||
|
Lexer() {}
|
||||||
|
Lexer(const String &source, String _fileName)
|
||||||
|
{
|
||||||
|
rd = SourceReader(source);
|
||||||
|
fileName = std::move(_fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceLocation makeSourceLocation(SourcePosition current_pos)
|
||||||
|
{
|
||||||
|
current_pos.tok_length = 1;
|
||||||
|
return SourceLocation(
|
||||||
|
current_pos, fileName, "[internal lexer]", String(magic_enum::enum_name(state).data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Token, Error> NextToken();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
43
src/Lexer/LexerTest.cpp
Normal file
43
src/Lexer/LexerTest.cpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include <Error/Error.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using namespace Fig;
|
||||||
|
|
||||||
|
String fileName = "test.fig";
|
||||||
|
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||||
|
|
||||||
|
SourceManager manager(filePath);
|
||||||
|
manager.Read();
|
||||||
|
|
||||||
|
if (!manager.read)
|
||||||
|
{
|
||||||
|
std::cerr << "Couldn't read file";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer(manager.GetSource(), fileName);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result.has_value())
|
||||||
|
{
|
||||||
|
ReportError(result.error(), manager);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const Token &token = *result;
|
||||||
|
const String &lexeme = manager.GetSub(token.index, token.length);
|
||||||
|
const auto &type = magic_enum::enum_name(token.type);
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
std::cout << "EOF: " << type << '\n';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::cout << lexeme << " --> " << type << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/Parser/ExprParser.cpp
Normal file
160
src/Parser/ExprParser.cpp
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/ExprParser.hpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 表达式解析实现 (pratt)
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
Result<LiteralExpr *, Error> Parser::parseLiteralExpr() // 当前token为literal时调用
|
||||||
|
{
|
||||||
|
state = State::ParsingLiteralExpr;
|
||||||
|
const Token &literal_token = consumeToken();
|
||||||
|
LiteralExpr *node = new LiteralExpr(literal_token, makeSourcelocation(literal_token));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
Result<IdentiExpr *, Error> Parser::parseIdentiExpr() // 当前token为Identifier调用
|
||||||
|
{
|
||||||
|
state = State::ParsingIdentiExpr;
|
||||||
|
const Token &identifier = consumeToken();
|
||||||
|
IdentiExpr *node =
|
||||||
|
new IdentiExpr(srcManager.GetSub(identifier.index, identifier.length), makeSourcelocation(identifier));
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<InfixExpr *, Error> Parser::parseInfixExpr(Expr *lhs) // 当前token为 op
|
||||||
|
{
|
||||||
|
state = State::ParsingInfixExpr;
|
||||||
|
const Token &op_token = consumeToken();
|
||||||
|
BinaryOperator op = TokenToBinaryOp(op_token);
|
||||||
|
BindingPower rbp = GetBinaryOpRBp(op);
|
||||||
|
|
||||||
|
const auto &rhs_result = parseExpression(rbp);
|
||||||
|
if (!rhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(rhs_result.error());
|
||||||
|
}
|
||||||
|
Expr *rhs = *rhs_result;
|
||||||
|
|
||||||
|
InfixExpr *node = new InfixExpr(lhs, op, rhs);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<PrefixExpr *, Error> Parser::parsePrefixExpr() // 当前token为op
|
||||||
|
{
|
||||||
|
state = State::ParsingPrefixExpr;
|
||||||
|
const Token &op_token = consumeToken();
|
||||||
|
UnaryOperator op = TokenToUnaryOp(op_token);
|
||||||
|
|
||||||
|
BindingPower rbp = GetUnaryOpRBp(op);
|
||||||
|
const auto &rhs_result = parseExpression(rbp);
|
||||||
|
if (!rhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(rhs_result.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr *rhs = *rhs_result;
|
||||||
|
PrefixExpr *node = new PrefixExpr(op, rhs);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<TokenType> Parser::getTerminators() // 返回当前state的终止条件(终止符)
|
||||||
|
{
|
||||||
|
using enum State;
|
||||||
|
|
||||||
|
static const std::unordered_set<TokenType> baseTerminators = {TokenType::EndOfFile, TokenType::Semicolon};
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
default: return baseTerminators;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Parser::shouldTerminate()
|
||||||
|
{
|
||||||
|
const Token &token = currentToken();
|
||||||
|
const auto &terminators = getTerminators();
|
||||||
|
return terminators.contains(token.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Expr *, Error> Parser::parseExpression(BindingPower rbp)
|
||||||
|
{
|
||||||
|
Expr *lhs = nullptr;
|
||||||
|
Token token = currentToken();
|
||||||
|
|
||||||
|
if (token.isIdentifier())
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parseIdentiExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
else if (token.isLiteral())
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parseLiteralExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
else if (IsTokenOp(token.type, false)) // 是否是一元运算符
|
||||||
|
{
|
||||||
|
const auto &lhs_result = parsePrefixExpr();
|
||||||
|
if (!lhs_result)
|
||||||
|
{
|
||||||
|
return std::unexpected(lhs_result.error());
|
||||||
|
}
|
||||||
|
lhs = *lhs_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lhs)
|
||||||
|
{
|
||||||
|
return std::unexpected(Error(ErrorType::ExpectedExpression,
|
||||||
|
"expected expression",
|
||||||
|
"insert expressions",
|
||||||
|
makeSourcelocation(prevToken())));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (shouldTerminate())
|
||||||
|
{
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = currentToken();
|
||||||
|
if (IsTokenOp(token.type /* isBinary = true */)) // 是否为二元运算符
|
||||||
|
{
|
||||||
|
BinaryOperator op = TokenToBinaryOp(token);
|
||||||
|
BindingPower lbp = GetBinaryOpLBp(op);
|
||||||
|
if (rbp >= lbp)
|
||||||
|
{
|
||||||
|
// 前操作数的右绑定力比当前操作数的左绑定力大
|
||||||
|
// lhs被吸走
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &result = parseInfixExpr(lhs);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
lhs = *result;
|
||||||
|
}
|
||||||
|
// 后缀运算符优先级非常大,几乎永远跟在操作数后面,因此我们可以直接结合
|
||||||
|
// 而不用走正常路径
|
||||||
|
else if (0) {}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
18
src/Parser/Parser.cpp
Normal file
18
src/Parser/Parser.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/Parser.cpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
DynArray<AstNode *> Parser::parseAll()
|
||||||
|
{
|
||||||
|
DynArray<AstNode *> nodes;
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
};
|
||||||
162
src/Parser/Parser.hpp
Normal file
162
src/Parser/Parser.hpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Parser/Parser.hpp
|
||||||
|
@brief 语法分析器(Pratt + 手动递归下降) 定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/Ast.hpp>
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Error/Error.hpp>
|
||||||
|
#include <Lexer/Lexer.hpp>
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Lexer &lexer;
|
||||||
|
SourceManager &srcManager;
|
||||||
|
|
||||||
|
size_t index = 0; // token在buffer下标
|
||||||
|
DynArray<Token> buffer;
|
||||||
|
|
||||||
|
String fileName;
|
||||||
|
|
||||||
|
bool isEOF = false;
|
||||||
|
|
||||||
|
Token nextToken()
|
||||||
|
{
|
||||||
|
assert(!isEOF && "nextToken: eof but called nextToken");
|
||||||
|
if (index + 1 < buffer.size())
|
||||||
|
{
|
||||||
|
return buffer[++index];
|
||||||
|
}
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
ReportError(result.error(), srcManager);
|
||||||
|
std::exit(-1);
|
||||||
|
}
|
||||||
|
const Token &token = result.value();
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
isEOF = true;
|
||||||
|
}
|
||||||
|
buffer.push_back(token);
|
||||||
|
index++;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token prevToken()
|
||||||
|
{
|
||||||
|
if (buffer.size() < 2)
|
||||||
|
{
|
||||||
|
return currentToken();
|
||||||
|
}
|
||||||
|
return buffer[buffer.size() - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token currentToken()
|
||||||
|
{
|
||||||
|
if (buffer.empty())
|
||||||
|
{
|
||||||
|
return nextToken();
|
||||||
|
}
|
||||||
|
return buffer.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
Token peekToken(size_t lookahead = 1)
|
||||||
|
{
|
||||||
|
assert(!isEOF && "peekToken: eof but called peekToken");
|
||||||
|
|
||||||
|
size_t peekIndex = index + lookahead;
|
||||||
|
while (peekIndex >= buffer.size() && !isEOF)
|
||||||
|
{
|
||||||
|
const auto &result = lexer.NextToken();
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
ReportError(result.error(), srcManager);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
const Token &token = result.value();
|
||||||
|
if (token.type == TokenType::EndOfFile)
|
||||||
|
{
|
||||||
|
isEOF = true;
|
||||||
|
}
|
||||||
|
buffer.push_back(token);
|
||||||
|
}
|
||||||
|
if (peekIndex >= buffer.size()) // 没有那么多token
|
||||||
|
{
|
||||||
|
return buffer.back(); // back是EOF Token
|
||||||
|
}
|
||||||
|
return buffer[peekIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token consumeToken()
|
||||||
|
{
|
||||||
|
if (isEOF)
|
||||||
|
return buffer.back();
|
||||||
|
Token current = currentToken();
|
||||||
|
nextToken();
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class State : std::uint8_t
|
||||||
|
{
|
||||||
|
Standby,
|
||||||
|
|
||||||
|
ParsingLiteralExpr,
|
||||||
|
ParsingIdentiExpr,
|
||||||
|
|
||||||
|
ParsingInfixExpr,
|
||||||
|
ParsingPrefixExpr,
|
||||||
|
} state;
|
||||||
|
|
||||||
|
Parser(Lexer &_lexer, SourceManager &_srcManager, String _fileName) :
|
||||||
|
lexer(_lexer), srcManager(_srcManager), fileName(std::move(_fileName))
|
||||||
|
{
|
||||||
|
state = State::Standby;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SourceLocation makeSourcelocation(const Token &tok)
|
||||||
|
{
|
||||||
|
auto [line, column] = srcManager.GetLineColumn(tok.index);
|
||||||
|
return SourceLocation(
|
||||||
|
SourcePosition(
|
||||||
|
line,
|
||||||
|
column,
|
||||||
|
tok.length
|
||||||
|
), fileName, "[internal parser]", magic_enum::enum_name(state).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expressions */
|
||||||
|
Result<LiteralExpr *, Error> parseLiteralExpr(); // 当前token为literal时调用
|
||||||
|
Result<IdentiExpr *, Error> parseIdentiExpr(); // 当前token为Identifier调用
|
||||||
|
|
||||||
|
Result<InfixExpr *, Error> parseInfixExpr(Expr *); // 由 parseExpression递归调用, 当前token为op
|
||||||
|
Result<PrefixExpr *, Error> parsePrefixExpr(); // 由 parseExpression递归调用, 当前token为op
|
||||||
|
|
||||||
|
std::unordered_set<TokenType> getTerminators(); // 返回当前state的终止条件(终止符)
|
||||||
|
bool shouldTerminate(); // 通过state判断该不该终止表达式解析
|
||||||
|
|
||||||
|
Result<Expr *, Error> parseExpression(BindingPower = 0);
|
||||||
|
|
||||||
|
/* Statements */
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DynArray<AstNode *> parseAll();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
29
src/Parser/ParserTest.cpp
Normal file
29
src/Parser/ParserTest.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <Parser/Parser.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
using namespace Fig;
|
||||||
|
|
||||||
|
String fileName = "test.fig";
|
||||||
|
String filePath = "T:/Files/Maker/Code/MyCodingLanguage/The Fig Project/Fig/test.fig";
|
||||||
|
SourceManager srcManager(filePath);
|
||||||
|
|
||||||
|
String source = srcManager.Read();
|
||||||
|
if (!srcManager.read)
|
||||||
|
{
|
||||||
|
std::cerr << "Couldn't read file";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lexer lexer(source, fileName);
|
||||||
|
Parser parser(lexer, srcManager, fileName);
|
||||||
|
// const auto &result = parser.parseExpression();
|
||||||
|
// if (!result)
|
||||||
|
// {
|
||||||
|
// ReportError(result.error(), srcManager);
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
// Expr *expr = *result;
|
||||||
|
// std::cout << expr->toString() << '\n';
|
||||||
|
}
|
||||||
127
src/SourceManager/SourceManager.hpp
Normal file
127
src/SourceManager/SourceManager.hpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*!
|
||||||
|
@file src/SourceManager/SourceManager.hpp
|
||||||
|
@brief 源代码管理
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class SourceManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
String filePath;
|
||||||
|
String source;
|
||||||
|
std::vector<String> lines;
|
||||||
|
std::vector<size_t> lineStartIndex; // 每行在整个源字符串中的起始 index
|
||||||
|
|
||||||
|
void preprocessLineIndices()
|
||||||
|
{
|
||||||
|
lineStartIndex.clear();
|
||||||
|
lineStartIndex.push_back(0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < source.length(); ++i)
|
||||||
|
{
|
||||||
|
if (source[i] == U'\n')
|
||||||
|
{
|
||||||
|
lineStartIndex.push_back(i + 1);
|
||||||
|
}
|
||||||
|
else if (source[i] == U'\r')
|
||||||
|
{
|
||||||
|
// 处理 CRLF,只在 \n 处记录
|
||||||
|
if (i + 1 < source.length() && source[i + 1] == U'\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lineStartIndex.push_back(i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool read = false;
|
||||||
|
String &Read()
|
||||||
|
{
|
||||||
|
std::fstream fs(filePath.toStdString());
|
||||||
|
if (!fs.is_open())
|
||||||
|
{
|
||||||
|
read = false;
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(fs, line))
|
||||||
|
{
|
||||||
|
source += line + '\n';
|
||||||
|
lines.push_back(String(line));
|
||||||
|
}
|
||||||
|
if (lines.empty())
|
||||||
|
{
|
||||||
|
lines.push_back(String()); // 填充一个空的
|
||||||
|
}
|
||||||
|
read = true;
|
||||||
|
preprocessLineIndices();
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceManager() {}
|
||||||
|
SourceManager(String _path) { filePath = std::move(_path); }
|
||||||
|
|
||||||
|
bool HasLine(int64_t _line) const
|
||||||
|
{
|
||||||
|
return _line <= lines.size() && _line >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &GetLine(size_t _line) const
|
||||||
|
{
|
||||||
|
assert(_line <= lines.size() && "SourceManager: GetLine failed, index out of range");
|
||||||
|
return lines[_line - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
String GetSub(size_t _index_start, size_t _length) const
|
||||||
|
{
|
||||||
|
return source.substr(_index_start, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &GetSource() const
|
||||||
|
{
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<size_t, size_t> GetLineColumn(size_t index) const
|
||||||
|
{
|
||||||
|
if (lineStartIndex.empty())
|
||||||
|
{
|
||||||
|
return {1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp index 到合法范围(Parser报错可能传入EOF位置)
|
||||||
|
// size_t lastLine = lineStartIndex.size() - 1;
|
||||||
|
if (index < lineStartIndex[0])
|
||||||
|
{
|
||||||
|
return {1, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
// upper_bound 找到第一个 > index 的行起点
|
||||||
|
auto it = std::ranges::upper_bound(lineStartIndex.begin(), lineStartIndex.end(), index);
|
||||||
|
|
||||||
|
size_t line;
|
||||||
|
if (it == lineStartIndex.begin())
|
||||||
|
{
|
||||||
|
line = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = static_cast<size_t>(it - lineStartIndex.begin() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t column = index - lineStartIndex[line] + 1;
|
||||||
|
return {line + 1, column};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
96
src/Token/Token.cpp
Normal file
96
src/Token/Token.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Token/Token.cpp
|
||||||
|
@brief Token实现
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
#include <Token/Token.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
const HashMap<String, TokenType> Token::punctMap = {
|
||||||
|
// 三字符
|
||||||
|
{String("..."), TokenType::TripleDot},
|
||||||
|
// 双字符
|
||||||
|
{String("=="), TokenType::Equal},
|
||||||
|
{String("!="), TokenType::NotEqual},
|
||||||
|
{String("<="), TokenType::LessEqual},
|
||||||
|
{String(">="), TokenType::GreaterEqual},
|
||||||
|
{String("<<"), TokenType::ShiftLeft},
|
||||||
|
{String(">>"), TokenType::ShiftRight},
|
||||||
|
{String("+="), TokenType::PlusEqual},
|
||||||
|
{String("-="), TokenType::MinusEqual},
|
||||||
|
{String("*="), TokenType::AsteriskEqual},
|
||||||
|
{String("/="), TokenType::SlashEqual},
|
||||||
|
{String("%="), TokenType::PercentEqual},
|
||||||
|
{String("^="), TokenType::CaretEqual},
|
||||||
|
{String("++"), TokenType::DoublePlus},
|
||||||
|
{String("--"), TokenType::DoubleMinus},
|
||||||
|
{String("&&"), TokenType::DoubleAmpersand},
|
||||||
|
{String("||"), TokenType::DoublePipe},
|
||||||
|
{String(":="), TokenType::Walrus},
|
||||||
|
{String("**"), TokenType::Power},
|
||||||
|
{String("->"), TokenType::RightArrow},
|
||||||
|
{String("=>"), TokenType::DoubleArrow},
|
||||||
|
|
||||||
|
// 单字符
|
||||||
|
{String("+"), TokenType::Plus},
|
||||||
|
{String("-"), TokenType::Minus},
|
||||||
|
{String("*"), TokenType::Asterisk},
|
||||||
|
{String("/"), TokenType::Slash},
|
||||||
|
{String("%"), TokenType::Percent},
|
||||||
|
{String("^"), TokenType::Caret},
|
||||||
|
{String("&"), TokenType::Ampersand},
|
||||||
|
{String("|"), TokenType::Pipe},
|
||||||
|
{String("~"), TokenType::Tilde},
|
||||||
|
{String("="), TokenType::Assign},
|
||||||
|
{String("<"), TokenType::Less},
|
||||||
|
{String(">"), TokenType::Greater},
|
||||||
|
{String("."), TokenType::Dot},
|
||||||
|
{String(","), TokenType::Comma},
|
||||||
|
{String(":"), TokenType::Colon},
|
||||||
|
{String(";"), TokenType::Semicolon},
|
||||||
|
{String("'"), TokenType::SingleQuote},
|
||||||
|
{String("\""), TokenType::DoubleQuote},
|
||||||
|
{String("("), TokenType::LeftParen},
|
||||||
|
{String(")"), TokenType::RightParen},
|
||||||
|
{String("["), TokenType::LeftBracket},
|
||||||
|
{String("]"), TokenType::RightBracket},
|
||||||
|
{String("{"), TokenType::LeftBrace},
|
||||||
|
{String("}"), TokenType::RightBrace},
|
||||||
|
{String("?"), TokenType::Question},
|
||||||
|
{String("!"), TokenType::Not},
|
||||||
|
};
|
||||||
|
|
||||||
|
const HashMap<String, TokenType> Token::keywordMap{
|
||||||
|
{String("and"), TokenType::And},
|
||||||
|
{String("or"), TokenType::Or},
|
||||||
|
{String("not"), TokenType::Not},
|
||||||
|
{String("import"), TokenType::Import},
|
||||||
|
{String("func"), TokenType::Function},
|
||||||
|
{String("var"), TokenType::Variable},
|
||||||
|
{String("const"), TokenType::Const},
|
||||||
|
// {String("final"), TokenType::Final},
|
||||||
|
{String("while"), TokenType::While},
|
||||||
|
{String("for"), TokenType::For},
|
||||||
|
{String("if"), TokenType::If},
|
||||||
|
{String("else"), TokenType::Else},
|
||||||
|
{String("new"), TokenType::New},
|
||||||
|
{String("struct"), TokenType::Struct},
|
||||||
|
{String("interface"), TokenType::Interface},
|
||||||
|
{String("impl"), TokenType::Implement},
|
||||||
|
{String("is"), TokenType::Is},
|
||||||
|
{String("public"), TokenType::Public},
|
||||||
|
{String("return"), TokenType::Return},
|
||||||
|
{String("break"), TokenType::Break},
|
||||||
|
{String("continue"), TokenType::Continue},
|
||||||
|
{String("try"), TokenType::Try},
|
||||||
|
{String("catch"), TokenType::Catch},
|
||||||
|
{String("throw"), TokenType::Throw},
|
||||||
|
{String("Finally"), TokenType::Finally},
|
||||||
|
{String("as"), TokenType::As},
|
||||||
|
{String("true"), TokenType::LiteralTrue},
|
||||||
|
{String("false"), TokenType::LiteralFalse},
|
||||||
|
{String("null"), TokenType::LiteralNull},
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
168
src/Token/Token.hpp
Normal file
168
src/Token/Token.hpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*!
|
||||||
|
@file src/Token/Token.hpp
|
||||||
|
@brief Token定义
|
||||||
|
@author PuqiAR (im@puqiar.top)
|
||||||
|
@date 2026-02-14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
#include <Utils/magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
|
#include <Deps/Deps.hpp>
|
||||||
|
#include <Core/SourceLocations.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class TokenType : int8_t
|
||||||
|
{
|
||||||
|
Illegal = -1,
|
||||||
|
EndOfFile = 0,
|
||||||
|
|
||||||
|
Comments,
|
||||||
|
|
||||||
|
Identifier,
|
||||||
|
|
||||||
|
/* Keywords */
|
||||||
|
Package, // package
|
||||||
|
And, // and
|
||||||
|
Or, // or
|
||||||
|
Not, // not
|
||||||
|
Import, // import
|
||||||
|
Function, // func
|
||||||
|
Variable, // var
|
||||||
|
Const, // const
|
||||||
|
// Final, // final
|
||||||
|
While, // while
|
||||||
|
For, // for
|
||||||
|
If, // if
|
||||||
|
Else, // else
|
||||||
|
New, // new
|
||||||
|
Struct, // struct
|
||||||
|
Interface, // interface
|
||||||
|
Implement, // impl
|
||||||
|
Is, // is
|
||||||
|
Public, // public
|
||||||
|
Return, // return
|
||||||
|
Break, // break
|
||||||
|
Continue, // continue
|
||||||
|
Try, // try
|
||||||
|
Catch, // catch
|
||||||
|
Throw, // throw
|
||||||
|
Finally, // finally
|
||||||
|
As, // as
|
||||||
|
|
||||||
|
// TypeNull, // Null
|
||||||
|
// TypeInt, // Int
|
||||||
|
// TypeDeps::String, // Deps::String
|
||||||
|
// TypeBool, // Bool
|
||||||
|
// TypeDouble, // Double
|
||||||
|
|
||||||
|
/* Literal Types */
|
||||||
|
LiteralNumber, // number (int,float...)
|
||||||
|
LiteralString, // string
|
||||||
|
|
||||||
|
LiteralTrue, // true <-- keyword
|
||||||
|
LiteralFalse, // false <-- keyword
|
||||||
|
LiteralNull, // null (Null unique instance) <-- keyword
|
||||||
|
|
||||||
|
/* Punct */
|
||||||
|
Plus, // +
|
||||||
|
Minus, // -
|
||||||
|
Asterisk, // *
|
||||||
|
Slash, // /
|
||||||
|
Percent, // %
|
||||||
|
Caret, // ^
|
||||||
|
Ampersand, // &
|
||||||
|
Pipe, // |
|
||||||
|
Tilde, // ~
|
||||||
|
ShiftLeft, // <<
|
||||||
|
ShiftRight, // >>
|
||||||
|
// Exclamation, // !
|
||||||
|
Question, // ?
|
||||||
|
Assign, // =
|
||||||
|
Less, // <
|
||||||
|
Greater, // >
|
||||||
|
Dot, // .
|
||||||
|
Comma, // ,
|
||||||
|
Colon, // :
|
||||||
|
Semicolon, // ;
|
||||||
|
SingleQuote, // '
|
||||||
|
DoubleQuote, // "
|
||||||
|
// Backtick, // `
|
||||||
|
// At, // @
|
||||||
|
// Hash, // #
|
||||||
|
// Dollar, // $
|
||||||
|
// Backslash, // '\'
|
||||||
|
// Underscore, // _
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftBracket, // [
|
||||||
|
RightBracket, // ]
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
// LeftArrow, // <-
|
||||||
|
RightArrow, // ->
|
||||||
|
DoubleArrow, // =>
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
LessEqual, // <=
|
||||||
|
GreaterEqual, // >=
|
||||||
|
|
||||||
|
PlusEqual, // +=
|
||||||
|
MinusEqual, // -=
|
||||||
|
AsteriskEqual, // *=
|
||||||
|
SlashEqual, // /=
|
||||||
|
PercentEqual, // %=
|
||||||
|
CaretEqual, // ^=
|
||||||
|
|
||||||
|
DoublePlus, // ++
|
||||||
|
DoubleMinus, // --
|
||||||
|
DoubleAmpersand, // &&
|
||||||
|
DoublePipe, // ||
|
||||||
|
Walrus, // :=
|
||||||
|
Power, // **
|
||||||
|
|
||||||
|
TripleDot, // ... for variadic parameter
|
||||||
|
};
|
||||||
|
|
||||||
|
class Token final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const HashMap<String, TokenType> punctMap;
|
||||||
|
static const HashMap<String, TokenType> keywordMap;
|
||||||
|
|
||||||
|
size_t index, length;
|
||||||
|
// 源文件中的下标 Token长度
|
||||||
|
TokenType type;
|
||||||
|
|
||||||
|
Token() : index(0), length(0), type(TokenType::Illegal) {};
|
||||||
|
Token(size_t _index, size_t _length, TokenType _type) : index(_index), length(_length), type(_type) {}
|
||||||
|
Deps::String toString() const
|
||||||
|
{
|
||||||
|
return Deps::String(std::format("Token'{}' at {}, len {}", magic_enum::enum_name(type), index, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIdentifier() const { return type == TokenType::Identifier; }
|
||||||
|
bool isLiteral() const
|
||||||
|
{
|
||||||
|
return type == TokenType::LiteralNull || type == TokenType::LiteralTrue || type == TokenType::LiteralFalse
|
||||||
|
|| type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token &operator=(const Token &other)
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
index = other.index;
|
||||||
|
length = other.length;
|
||||||
|
type = other.type;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
2589
src/Utils/argparse/argparse.hpp
Normal file
2589
src/Utils/argparse/argparse.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1508
src/Utils/magic_enum/magic_enum.hpp
Normal file
1508
src/Utils/magic_enum/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load Diff
44
src/Utils/magic_enum/magic_enum_all.hpp
Normal file
44
src/Utils/magic_enum/magic_enum_all.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_containers.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
#include "magic_enum_format.hpp"
|
||||||
|
#include "magic_enum_fuse.hpp"
|
||||||
|
#include "magic_enum_iostream.hpp"
|
||||||
|
#include "magic_enum_switch.hpp"
|
||||||
|
#include "magic_enum_utility.hpp"
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
1174
src/Utils/magic_enum/magic_enum_containers.hpp
Normal file
1174
src/Utils/magic_enum/magic_enum_containers.hpp
Normal file
File diff suppressed because it is too large
Load Diff
222
src/Utils/magic_enum/magic_enum_flags.hpp
Normal file
222
src/Utils/magic_enum/magic_enum_flags.hpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(push)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
|
||||||
|
constexpr U values_ors() noexcept {
|
||||||
|
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
|
||||||
|
|
||||||
|
auto ors = U{0};
|
||||||
|
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
|
||||||
|
ors |= static_cast<U>(values_v<E, S>[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ors;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
// Returns name from enum-flags value.
|
||||||
|
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] auto enum_flags_name(E value, char_type sep = static_cast<char_type>('|')) -> detail::enable_if_t<E, string> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
string name;
|
||||||
|
auto check_value = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (static_cast<U>(value) & v) != 0) {
|
||||||
|
if (const auto n = detail::names_v<D, S>[i]; !n.empty()) {
|
||||||
|
check_value |= v;
|
||||||
|
if (!name.empty()) {
|
||||||
|
name.append(1, sep);
|
||||||
|
}
|
||||||
|
name.append(n.data(), n.size());
|
||||||
|
} else {
|
||||||
|
return {}; // Value out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_value != 0 && check_value == static_cast<U>(value)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains enum-flags value from integer value.
|
||||||
|
// Returns optional with enum-flags value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
if constexpr (detail::count_v<D, S> == 0) {
|
||||||
|
static_cast<void>(value);
|
||||||
|
return {}; // Empty enum.
|
||||||
|
} else {
|
||||||
|
if constexpr (detail::is_sparse_v<D, S>) {
|
||||||
|
auto check_value = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (value & v) != 0) {
|
||||||
|
check_value |= v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_value != 0 && check_value == value) {
|
||||||
|
return static_cast<D>(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
constexpr auto min = detail::min_v<D, S>;
|
||||||
|
constexpr auto max = detail::values_ors<D, S>();
|
||||||
|
|
||||||
|
if (value >= min && value <= max) {
|
||||||
|
return static_cast<D>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains enum-flags value from name.
|
||||||
|
// Returns optional with enum-flags value.
|
||||||
|
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
if constexpr (detail::count_v<D, S> == 0) {
|
||||||
|
static_cast<void>(value);
|
||||||
|
return {}; // Empty enum.
|
||||||
|
} else {
|
||||||
|
auto result = U{0};
|
||||||
|
while (!value.empty()) {
|
||||||
|
const auto d = detail::find(value, '|');
|
||||||
|
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
|
||||||
|
auto f = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (detail::cmp_equal(s, detail::names_v<D, S>[i], p)) {
|
||||||
|
f = static_cast<U>(enum_value<D, S>(i));
|
||||||
|
result |= f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f == U{0}) {
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != U{0}) {
|
||||||
|
return static_cast<D>(result);
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains value with such value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(static_cast<U>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains value with such integer value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains enumerator with such name.
|
||||||
|
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(value, std::move(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether `flags set` contains `flag`.
|
||||||
|
// Note: If `flag` equals 0, it returns false, as 0 is not a flag.
|
||||||
|
template <typename E>
|
||||||
|
constexpr auto enum_flags_test(E flags, E flag) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using U = underlying_type_t<E>;
|
||||||
|
|
||||||
|
return static_cast<U>(flag) && ((static_cast<U>(flags) & static_cast<U>(flag)) == static_cast<U>(flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether `lhs flags set` and `rhs flags set` have common flags.
|
||||||
|
// Note: If `lhs flags set` or `rhs flags set` equals 0, it returns false, as 0 is not a flag, and therfore cannot have any matching flag.
|
||||||
|
template <typename E>
|
||||||
|
constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using U = underlying_type_t<E>;
|
||||||
|
|
||||||
|
return (static_cast<U>(lhs) & static_cast<U>(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
114
src/Utils/magic_enum/magic_enum_format.hpp
Normal file
114
src/Utils/magic_enum/magic_enum_format.hpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
|
||||||
|
#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT)
|
||||||
|
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT 1
|
||||||
|
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum::customize {
|
||||||
|
// customize enum to enable/disable automatic std::format
|
||||||
|
template <typename E>
|
||||||
|
constexpr bool enum_format_enabled() noexcept {
|
||||||
|
return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT;
|
||||||
|
}
|
||||||
|
} // magic_enum::customize
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_format)
|
||||||
|
|
||||||
|
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||||
|
#include <format>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
struct std::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
|
||||||
|
template <class FormatContext>
|
||||||
|
auto format(E e, FormatContext& ctx) const {
|
||||||
|
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||||
|
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FMT_VERSION)
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : fmt::formatter<std::string_view> {
|
||||||
|
template <class FormatContext>
|
||||||
|
auto format(E e, FormatContext& ctx) const {
|
||||||
|
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||||
|
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE)
|
||||||
|
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
|
||||||
|
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
89
src/Utils/magic_enum/magic_enum_fuse.hpp
Normal file
89
src/Utils/magic_enum/magic_enum_fuse.hpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
|
||||||
|
if (hash) {
|
||||||
|
if (const auto index = enum_index(value)) {
|
||||||
|
return (*hash << log2((enum_count<E>() << 1) - 1)) | *index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
|
||||||
|
return fuse_one_enum(0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, typename... Es>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
|
||||||
|
return fuse_one_enum(fuse_enum(tail...), head);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Es>
|
||||||
|
constexpr auto typesafe_fuse_enum(Es... values) noexcept {
|
||||||
|
enum class enum_fuse_t : std::uintmax_t;
|
||||||
|
const auto fuse = fuse_enum(values...);
|
||||||
|
if (fuse) {
|
||||||
|
return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
|
||||||
|
}
|
||||||
|
return optional<enum_fuse_t>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
|
||||||
|
template <typename... Es>
|
||||||
|
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
|
||||||
|
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
|
||||||
|
static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
|
||||||
|
static_assert((detail::log2(enum_count<std::decay_t<Es>>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
|
||||||
|
#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
|
||||||
|
const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
|
||||||
|
#else
|
||||||
|
const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
|
||||||
|
#endif
|
||||||
|
return MAGIC_ENUM_ASSERT(fuse), fuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
117
src/Utils/magic_enum/magic_enum_iostream.hpp
Normal file
117
src/Utils/magic_enum/magic_enum_iostream.hpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
|
||||||
|
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||||
|
#include <iosfwd>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace ostream_operators {
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
|
||||||
|
if constexpr (detail::supported<D>::value) {
|
||||||
|
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
|
||||||
|
for (const auto c : name) {
|
||||||
|
os.put(c);
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = enum_name<D>(value); !name.empty()) {
|
||||||
|
for (const auto c : name) {
|
||||||
|
os.put(c);
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (os << static_cast<U>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||||
|
return value ? (os << *value) : os;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::ostream_operators
|
||||||
|
|
||||||
|
namespace istream_operators {
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
std::basic_string<Char, Traits> s;
|
||||||
|
is >> s;
|
||||||
|
if constexpr (detail::supported<D>::value) {
|
||||||
|
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||||
|
if (const auto v = enum_flags_cast<D>(s)) {
|
||||||
|
value = *v;
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto v = enum_cast<D>(s)) {
|
||||||
|
value = *v;
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::istream_operators
|
||||||
|
|
||||||
|
namespace iostream_operators {
|
||||||
|
|
||||||
|
using magic_enum::ostream_operators::operator<<;
|
||||||
|
using magic_enum::istream_operators::operator>>;
|
||||||
|
|
||||||
|
} // namespace magic_enum::iostream_operators
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
195
src/Utils/magic_enum/magic_enum_switch.hpp
Normal file
195
src/Utils/magic_enum/magic_enum_switch.hpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct default_result_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct identity {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nonesuch {};
|
||||||
|
|
||||||
|
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
|
||||||
|
struct invoke_result : identity<nonesuch> {};
|
||||||
|
|
||||||
|
template <typename F, typename V>
|
||||||
|
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
|
||||||
|
|
||||||
|
template <typename F, typename V>
|
||||||
|
using invoke_result_t = typename invoke_result<F, V>::type;
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||||
|
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
|
||||||
|
static_assert(std::is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
|
||||||
|
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return identity<nonesuch>{};
|
||||||
|
} else {
|
||||||
|
return std::common_type<invoke_result_t<F, enum_constant<values_v<E, S>[I]>>...>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename Result, typename F>
|
||||||
|
constexpr auto result_type() noexcept {
|
||||||
|
static_assert(std::is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
|
||||||
|
|
||||||
|
constexpr auto seq = std::make_index_sequence<count_v<E, S>>{};
|
||||||
|
using R = typename decltype(common_invocable<E, S, F>(seq))::type;
|
||||||
|
if constexpr (std::is_same_v<Result, default_result_type>) {
|
||||||
|
if constexpr (std::is_same_v<R, nonesuch>) {
|
||||||
|
return identity<void>{};
|
||||||
|
} else {
|
||||||
|
return identity<R>{};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (std::is_convertible_v<R, Result>) {
|
||||||
|
return identity<Result>{};
|
||||||
|
} else if constexpr (std::is_convertible_v<Result, R>) {
|
||||||
|
return identity<R>{};
|
||||||
|
} else {
|
||||||
|
return identity<nonesuch>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename Result, typename F, typename D = std::decay_t<E>, typename R = typename decltype(result_type<D, S, Result, F>())::type>
|
||||||
|
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
|
||||||
|
|
||||||
|
#if !defined(MAGIC_ENUM_ENABLE_HASH) && !defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
|
||||||
|
template <typename T = void>
|
||||||
|
inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
|
||||||
|
|
||||||
|
template <std::size_t I, std::size_t End, typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||||
|
constexpr decltype(auto) constexpr_switch_impl(F&& f, E value, Def&& def) {
|
||||||
|
if constexpr(I < End) {
|
||||||
|
constexpr auto v = enum_constant<enum_value<E, I, S>()>{};
|
||||||
|
if (value == v) {
|
||||||
|
if constexpr (std::is_invocable_r_v<R, F, decltype(v)>) {
|
||||||
|
return static_cast<R>(std::forward<F>(f)(v));
|
||||||
|
} else {
|
||||||
|
return def();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return constexpr_switch_impl<I + 1, End, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||||
|
constexpr decltype(auto) constexpr_switch(F&& f, E value, Def&& def) {
|
||||||
|
static_assert(is_enum_v<E>, "magic_enum::detail::constexpr_switch requires enum type.");
|
||||||
|
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return def();
|
||||||
|
} else {
|
||||||
|
return constexpr_switch_impl<0, count_v<E, S>, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
template <typename Result = detail::default_result_type, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
detail::default_result_type_lambda<R>);
|
||||||
|
#else
|
||||||
|
return detail::constexpr_switch<R, D, S>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
detail::default_result_type_lambda<R>);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result = detail::default_result_type, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||||
|
return enum_switch<Result, E, S>(std::forward<F>(f), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
[&result]() -> R { return std::forward<Result>(result); });
|
||||||
|
#else
|
||||||
|
return detail::constexpr_switch<R, D, S>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
[&result]() -> R { return std::forward<Result>(result); });
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||||
|
return enum_switch<Result, E, S>(std::forward<F>(f), value, std::forward<Result>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
138
src/Utils/magic_enum/magic_enum_utility.hpp
Normal file
138
src/Utils/magic_enum/magic_enum_utility.hpp
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||||
|
constexpr auto for_each(F&& f, std::index_sequence<I...>) {
|
||||||
|
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> || ...);
|
||||||
|
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> && ...);
|
||||||
|
|
||||||
|
if constexpr (has_void_return) {
|
||||||
|
(f(enum_constant<values_v<E, S>[I]>{}), ...);
|
||||||
|
} else if constexpr (all_same_return) {
|
||||||
|
return std::array{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||||
|
} else {
|
||||||
|
return std::tuple{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F,std::size_t... I>
|
||||||
|
constexpr bool all_invocable(std::index_sequence<I...>) {
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (std::is_invocable_v<F, enum_constant<values_v<E, S>[I]>> && ...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, detail::enable_if_t<E, int> = 0>
|
||||||
|
constexpr auto enum_for_each(F&& f) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};
|
||||||
|
|
||||||
|
if constexpr (detail::all_invocable<D, S, F>(sep)) {
|
||||||
|
return detail::for_each<D, S>(std::forward<F>(f), sep);
|
||||||
|
} else {
|
||||||
|
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_next_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) + n);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_next_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) + n) % count) + count) % count);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MAGIC_ENUM_ASSERT(false), value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_prev_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) - n);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_prev_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) - n) % count) + count) % count);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MAGIC_ENUM_ASSERT(false), value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
0
src/main.cpp
Normal file
0
src/main.cpp
Normal file
61
xmake.lua
Normal file
61
xmake.lua
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
add_rules("mode.debug", "mode.release")
|
||||||
|
add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"})
|
||||||
|
|
||||||
|
set_policy("run.autobuild", false)
|
||||||
|
|
||||||
|
if is_plat("linux") then
|
||||||
|
-- Linux: clang + libc++
|
||||||
|
set_toolchains("clang")
|
||||||
|
add_cxxflags("-stdlib=libc++")
|
||||||
|
add_ldflags("-stdlib=libc++")
|
||||||
|
elseif is_plat("windows") then
|
||||||
|
-- 1. CI cross (Linux -> Windows)
|
||||||
|
-- 2. local dev (Windows + llvm-mingw)
|
||||||
|
set_toolchains("mingw") -- llvm-mingw
|
||||||
|
add_ldflags("-Wl,--stack,268435456")
|
||||||
|
-- set_toolchains("clang")
|
||||||
|
-- static lib
|
||||||
|
-- add_ldflags("-target x86_64-w64-mingw32", "-static")
|
||||||
|
-- add_cxxflags("-stdlib=libc++")
|
||||||
|
-- add_ldflags("-stdlib=libc++")
|
||||||
|
end
|
||||||
|
|
||||||
|
set_languages("c++23")
|
||||||
|
add_includedirs("src")
|
||||||
|
|
||||||
|
add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"")
|
||||||
|
|
||||||
|
target("StringTest")
|
||||||
|
add_files("src/Deps/String/StringTest.cpp")
|
||||||
|
|
||||||
|
target("LexerTest")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Lexer/LexerTest.cpp")
|
||||||
|
|
||||||
|
target("ParserTest")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Ast/Operator.cpp")
|
||||||
|
add_files("src/Parser/ExprParser.cpp")
|
||||||
|
add_files("src/Parser/Parser.cpp")
|
||||||
|
|
||||||
|
add_files("src/Parser/ParserTest.cpp")
|
||||||
|
|
||||||
|
target("Fig")
|
||||||
|
add_files("src/Core/*.cpp")
|
||||||
|
add_files("src/Token/Token.cpp")
|
||||||
|
add_files("src/Error/Error.cpp")
|
||||||
|
add_files("src/Lexer/Lexer.cpp")
|
||||||
|
|
||||||
|
add_files("src/Ast/Operator.cpp")
|
||||||
|
add_files("src/Parser/ExprParser.cpp")
|
||||||
|
add_files("src/Parser/Parser.cpp")
|
||||||
|
|
||||||
|
add_files("src/main.cpp")
|
||||||
Reference in New Issue
Block a user