Compare commits

..

11 Commits

69 changed files with 12037 additions and 0 deletions

33
.ci/Dockerfile Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,11 @@
# Xmake cache
.xmake/
build/
# MacOS Cache
.DS_Store
.vscode
.VSCodeCounter
/test.fig

View 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
*/

View 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));

View 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)

View 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);

View 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);
}

View 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;
}

View 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;
}
}

View 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);

View 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)

View 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");

View 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;
}

View 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
View File

@@ -0,0 +1,3 @@
-std=c++2b
-static
-stdlib=libc++

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

24
docs/zh_CN/01-简介.md Normal file
View File

@@ -0,0 +1,24 @@
# Fig 语言简介
## 概述
Fig 是一门动态类型、解释执行的编程语言,专注于简洁语法和实用的语言特性。它采用树遍历解释器架构,支持多种编程范式。
## 实际观察到的特性
1. **解释执行**:基于 AST 的树遍历解释器,无编译步骤
2. **动态类型系统**:运行时类型检查,支持类型注解但不强制
3. **混合范式**:支持函数式、面向对象和命令式风格
4. **模块系统**:支持代码组织和复用
5. **内置类型**:整数、浮点数、字符串、列表、映射等
6. **垃圾回收**:基于引用计数的自动内存管理
## 语言设计特点
- **渐进类型**:支持类型注解但不强制,兼顾灵活性和可读性
- **一等公民函数**:函数可以作为参数传递和返回
- **闭包支持**:完整的词法作用域和闭包
- **错误处理**:异常机制和 try-catch 结构
- **可变与不可变**const/var 区分,平衡安全与灵活
## 目标应用场景
- 脚本编写和自动化任务
- 教育用途和学习编程
- 配置语言和DSL

View 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"`

View 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());
```

View 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
View 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;
});
```

View 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)模式请规范使用:
&nbsp;
示例:定义
```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
View 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
View 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

View 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);
}
};
};

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
};

View 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
View 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

View 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>;
};

View 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>;
};

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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(&current)),
// "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
View 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 &currentPosition() { return pos; }
inline char32_t current() const
{
assert(index < source.length() && "SourceReader: get current failed, index out of range");
return source[index];
}
inline char32_t currentIf() const
{
if (index >= source.length())
{
return U'\0';
}
return source[index];
}
inline bool hasNext() const { return index < source.length() - 1; }
inline char32_t peek() const
{
assert((index + 1) < source.length() && "SourceReader: get peek failed, index out of range");
return source[index + 1];
}
inline char32_t peekIf() const
{
if ((index + 1) < source.length()) { return source[index + 1]; }
return 0xFFFD;
}
inline char32_t produce()
{
// returns current rune, then next
char32_t c = current();
next();
return c;
}
inline void next()
{
char32_t consumed = currentIf();
++index;
if (consumed == U'\n')
{
++pos.line;
pos.column = 1;
}
else
{
++pos.column;
}
}
inline void skip(size_t n)
{
for (size_t i = 0; i < n; ++i) { next(); }
}
inline size_t currentIndex() const { return index; }
inline bool isAtEnd() const { return index >= source.length(); }
};
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
View 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
View 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
View 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
View 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
View 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';
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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
View File

61
xmake.lua Normal file
View 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")