55 Commits

Author SHA1 Message Date
4e7f84593a 修正api地址,原因:仓库改名Fig-TreeWalker 2026-02-13 13:20:02 +08:00
ca0396568b 添加了标准库 std.file。支持简单的文件读写 2026-02-12 14:29:26 +08:00
a00be02359 格式化一处代码 2026-02-10 15:21:00 +08:00
e0a76ea1da Repl不打印null 2026-02-10 14:49:34 +08:00
e98beb03d7 修复定义变量未提供值时内存泄漏的bug。修复lexer解析字符串不能多行的bug 2026-02-09 14:25:15 +08:00
966b6eb805 文件名错了 use_std_time.fig -> use_std_test.fig 2026-02-09 13:42:56 +08:00
9310252adc 添加interface的示例,上一个commit其实是”添加接口组合!“ 2026-02-09 13:21:53 +08:00
1dadaca4cc 添加结构体组合, interface x : a + b {} 2026-02-08 22:20:12 +08:00
537011df32 修改LvObject set方法,先检查是否可变再检查类型匹配。更符合直觉 2026-02-08 13:27:59 +08:00
764c4269a5 修改 toString Function类型打印,加上了名字 2026-02-08 13:15:03 +08:00
e1c765aade For循环略微性能提升 2026-02-08 09:18:31 +08:00
52b75f17da 修正教程 03 2026-02-07 16:26:41 +08:00
27e5de3ea2 修改readme 2026-02-07 16:23:41 +08:00
1e1d6c3284 增加 0.4.3-alpha的详细报告。同时生成了报告pdf 2026-02-07 16:21:16 +08:00
642ba33243 Merge branch 'main' of https://git.fig-lang.cn/PuqiAR/Fig 2026-02-05 22:23:34 +08:00
90d4134f73 UTF32St 2026-02-05 22:23:30 +08:00
d897f41c57 解除对 FStringView的依赖 2026-02-05 12:12:24 +08:00
a05958319b 写了一个超级叼的String. UTF32 + PureASCII + SSO, element语义为codepoint 2026-02-05 12:09:19 +08:00
c0dd463b82 修改readme installation 2026-02-04 21:04:03 +08:00
edabd0aa19 build.yml: 获取历史3次提交消息 2026-02-04 20:58:43 +08:00
f1b30c8837 Merge branch 'main' of https://git.fig-lang.cn/PuqiAR/Fig 2026-02-04 20:34:42 +08:00
50ca68b1a4 修复了parser解析initexpr模式判断错误的问题。修复标准库改名忘改的问题 2026-02-04 20:34:35 +08:00
zi2ven
ecc1bd1cc2 change build.yml 3 2026-02-04 20:28:41 +08:00
zi2ven
558ea194c7 change build.yml 2 2026-02-04 20:09:24 +08:00
zi2ven
b302cb2cc5 change build.yml 2026-02-04 19:53:26 +08:00
b4b6d409b5 显示指定构建target为 Fig 2026-02-04 19:21:52 +08:00
da1a8e04de v0.4.3-alpha
[Fix] 函数调用时参数类型求值使用错误作用域的问题
[Fix] 结构体定义中不可以使用自身类型的bug
[Fix] import导致的重定义bug
[Impl] Parser的precedence调整

[Feat] 支持运算符重载, impl Operator for xxx {} 具体见 lang.fig中解释
[Feat] 新增标准库 std.test
[Feat] 新增内置函数 type,接收一个参数,返回该参数的类型(value.type改名value._type)
        推荐使用该函数替换 value._type
[Feat] 新增转换运算符 as,转换失败时抛出TypeError,支持任意类型到String、部分类型到Int, Double等等转换
        TypeError实现了Error interface,可被用户catch
        推荐使用该feat或与std.value标准库结合
[Feat] 抛出实现Error interface的错误现在会有不一样的log
[Feat] import增加cache(ast + source lines),但import一个module每次会得到不同对象而不是复用,请注意
2026-02-04 19:12:18 +08:00
e8aed221de 新增builtin函数 type,接收一个参数,获取类型字符串。(类似 js) 标准库 std.value.type函数更名 _type返回底层类型 2026-02-04 18:28:18 +08:00
b98c1b7dd8 挺大的改动。增加 as运算符,转换不了抛出 TypeError。import语法更新。修复try一点错误。现在表达式运算返回ExprResult。通过3个宏实现简便错误传播与解包 unwrap 2026-02-04 18:14:30 +08:00
27cf09cad0 消除UTF8_iterator编译警告 2026-02-03 19:41:24 +08:00
45e18cc773 规范 include 2026-02-03 19:20:51 +08:00
4535f75058 修复了函数调用时求值类型使用的作用域错误的问题。结构体中现在可以使用自己 2026-02-03 18:49:40 +08:00
01c16dee3f [Feat] 支持运算符重载!详见文档或 Library/lang/lang.fig中的定义。通过 impl Operation for xxx实现重载
[Impl] 函数参数指定现在也接受一个 exp,逐渐改动其他中...
2026-02-02 16:11:08 +08:00
41bff72d44 这是一条 msg. ( 正文:error log修改。新增std.tester。parser precedence重写 2026-02-01 20:01:59 +08:00
aea716ced2 回档之后的重写。部分问题修复。添加了什么我也忘了 2026-02-01 15:52:28 +08:00
61bffdc743 文档与readme更新 2026-02-01 13:59:20 +08:00
ca4ae143b4 v0.4.2-alpha
[Fix][Impl] 为了消除类构造带来的语法歧义,同时保持实现简洁和括号省略的语法,自此版本,引入了 `new` 操作符
            造成歧义的原方法:
                if a == A{}
            条件是 a == A,还是 a == A{} ?

            因此,现在使用 new a_struct{}来构造类
[Opti] 相较于 Fig v0.4.1-alpha版本,release O3同编译条件下
       Fib普通递归法性能提升 ~50%
       具体方式:
            增加了小整数优化,-128~127的整数现在会直接从IntPool获取而不是新构造
            ...忘了
[Fix] 类构造 shorthand模式忘写了,现在补上了
[Feat][Impl] 类型声明现在接受一个表达式,原为Identifier。实现 var start: time.Time = time.now() 的效果
             这是符合语法和语言特性的支持,类型为一等公民。类似Python的 <class 'type'>

[Impl] 修改了部分错误输出的细节
2026-01-22 08:24:14 +08:00
21641f888e 恢复原来的构建逻辑 2026-01-19 17:33:52 +08:00
def69e031f qswl x3 2026-01-19 17:26:46 +08:00
009a70fc64 气死我了x2 2026-01-19 17:24:29 +08:00
e01b4e9849 气死我了 2026-01-19 17:19:12 +08:00
da262a4cf1 尝试修复 build.yml 版本构建 2026-01-19 17:09:44 +08:00
f76e28ee8d 修改 build action 2026-01-19 16:57:57 +08:00
a324cf17f6 尝试修复windows构建版本获取问题,代码是ds写的。我不会actions 2026-01-19 16:27:14 +08:00
caf058dd55 [VER] v0.4.1-alpha
[Fix] 修复struct定义创建instanceCtx时拷贝类方法错误的Bug,表现在同一类的2个不同实例内置函数一样的问题
      (即 addr A != addr B, addr A.method == addr B.method)
      修复后为 (addr A != addr B, addr A.method != addr B.method)
      方法的closureContext指向instance的Context
      修复后 std.time可以正常使用
2026-01-19 16:10:12 +08:00
9e3f17711f [VER] 0.4.0-alpha
[Fix] 修复恶性Bug: Parser: parseExpression没有正确解析二元表达式,没有用到 right binding power的问题,表现在生成类似 a * b * c时,结果为 a * (b * c) 的Bug
[Impl][Fix] 修复跨文件(如import)报错信息错误的问题,现在Ast跟踪保存文件信息,报错统一从Error父类获取
[...] 忘了,好困不管了
2026-01-19 04:13:55 +08:00
d398d457b5 [VER] v0.3.9-alpha
[Feat] is 操作符现在可以直接判断内置数据类型, 如 10 is Int
[Fix] evalMemberExpr的lhs可以为右值,修复原来限制为左值的BUG,如调用一个函数返回结果为struct且访问member触发此bug
[Impl] 可更换的std::dynamic_pointer_cast更换为static版本,更快!
[Feat] 增加标准库 std.time,以及用到的builtin: __ftime_now_ns,但目前 Time类有点BUG
[...] 剩下的忘了
2026-01-15 17:51:01 +08:00
ccf80536b3 [Fix] 蠢蛋clang! 2026-01-14 21:35:48 +08:00
13fdbec0c4 [VER] v0.3.8-alpha
[Impl][Fix] 更改resolveModulePath实现,使用绝对路径查找内置库
2026-01-14 21:31:11 +08:00
99e00492f5 删除蠢蛋注释 2026-01-14 17:37:27 +08:00
310d79acc5 我忘记改版本号了 2026-01-14 17:34:24 +08:00
e28921ae02 [VER] 0.3.7-alpha
[Fix] 修复科学表达式数字解析的问题(Lexer引起) 由 Satklomi发现,感谢
[Feat] 增加Compiler相关定义,将开发BytecodeVM
[Tip] Evaluator进入Bug fix阶段,新功能延缓开发。转向VM
2026-01-14 17:28:38 +08:00
1ccc63419d [VER] v0.3.6-alpha 发布, 完整的Installer已准备!
[w] Change log 在之前的提交
2026-01-04 14:51:32 +08:00
3b5e99242f [Action] 尝试修复win打包计算问题 2026-01-04 14:22:33 +08:00
85bdab5db3 [Action] 重写win发布 2026-01-04 14:19:38 +08:00
115 changed files with 10430 additions and 3828 deletions

View File

@@ -29,21 +29,27 @@ jobs:
git clone https://git.fig-lang.cn/${{ github.repository }} . git clone https://git.fig-lang.cn/${{ github.repository }} .
git checkout ${{ github.ref }} git checkout ${{ github.ref }}
- name: 设置版本 - name: 设置版本和提交信息
run: | run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.version }}" VERSION="${{ inputs.version }}"
else else
VERSION="${GITHUB_REF#refs/tags/}" VERSION="${{ github.ref_name }}"
fi fi
echo "构建版本: $VERSION" echo "构建版本: $VERSION"
echo "VERSION=$VERSION" >> $GITHUB_ENV 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) - name: 构建项目 (Linux)
run: | run: |
echo "开始构建Linux版本..." echo "开始构建Linux版本..."
xmake f -p linux -a x86_64 -m release -y xmake f -p linux -a x86_64 -m release -y
xmake build -j$(nproc) xmake build -j$(nproc) Fig
echo "Linux构建成功。" echo "Linux构建成功。"
# 🔧 新增构建Linux平台安装器 # 🔧 新增构建Linux平台安装器
@@ -61,7 +67,7 @@ jobs:
# 安装依赖并构建 # 安装依赖并构建
pip install --upgrade pip pip install --upgrade pip
pip install -r requirements.txt pip install -r requirements.txt
python3 -m PyInstaller -F -n fig-setup --distpath ./dist/linux main.py python3 -m PyInstaller -F -n FigSetup-Linux --distpath ./dist/linux main.py
echo "Linux安装器构建完成" echo "Linux安装器构建完成"
- name: 打包Linux发布文件 - name: 打包Linux发布文件
@@ -83,48 +89,74 @@ jobs:
GITEA_TOKEN: ${{ secrets.CI_TOKEN }} GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
run: | run: |
VERSION="${{ env.VERSION }}" 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 }}" API="https://git.fig-lang.cn/api/v1/repos/${{ github.repository }}"
echo "正在为Linux版本创建/更新发布 $VERSION ..." 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" \ RESPONSE=$(curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"tag_name\":\"$VERSION\",\"name\":\"Fig $VERSION\",\"draft\":false,\"prerelease\":false}" \ -d @release_body.json \
"$API/releases" 2>/dev/null || echo '{"id":0}') "$API/releases" 2>/dev/null || echo '{"id":0}')
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
echo "尝试通过标签获取已有发布的ID..." # 再次尝试获取,防止并发冲突
RELEASE_ID=$(curl -sS -H "Authorization: token $GITEA_TOKEN" \ RESPONSE_TAG=$(curl -sS -H "Authorization: token $GITEA_TOKEN" "$API/releases/tags/$VERSION" 2>/dev/null || echo '{"id":0}')
"$API/releases/tags/$VERSION" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2) RELEASE_ID=$(echo "$RESPONSE_TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
fi
fi fi
if [ -z "$RELEASE_ID" ]; then if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
echo "错误:无法获取或创建发布 ID" echo "错误:无法获取或创建发布 ID"
exit 1 exit 1
fi fi
echo "使用发布 ID: $RELEASE_ID 进行上传" 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" \ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/octet-stream" \ -H "Content-Type: application/octet-stream" \
--data-binary @Fig-$VERSION-linux-x86_64.tar.gz \ --data-binary "@$PACKAGE_ZIP" \
"$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-linux-x86_64.tar.gz" "$API/releases/$RELEASE_ID/assets?name=$PACKAGE_ZIP" > /dev/null
echo "正在上传 $PACKAGE_SHA ..."
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: text/plain" \ -H "Content-Type: text/plain" \
--data-binary @Fig-$VERSION-linux-x86_64.sha256 \ --data-binary "@$PACKAGE_SHA" \
"$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-linux-x86_64.sha256" "$API/releases/$RELEASE_ID/assets?name=$PACKAGE_SHA" > /dev/null
# 🔧 新增:上传Linux安装器 # 🔧 上传Linux安装器
if [ -f "Installer/ConsoleInstaller/dist/linux/fig-setup" ]; then INSTALLER="Installer/ConsoleInstaller/dist/linux/FigSetup-Linux"
if [ -f "$INSTALLER" ]; then
echo "正在上传Linux安装器..." echo "正在上传Linux安装器..."
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/octet-stream" \ -H "Content-Type: application/octet-stream" \
--data-binary @Installer/ConsoleInstaller/dist/linux/fig-setup \ --data-binary "@$INSTALLER" \
"$API/releases/$RELEASE_ID/assets?name=fig-setup" "$API/releases/$RELEASE_ID/assets?name=FigSetup-Linux" > /dev/null
fi fi
echo "✅ Linux版本发布完成" echo "✅ Linux版本发布完成"
@@ -151,9 +183,9 @@ jobs:
run: | run: |
$env:Path = "C:\Program Files\Git\cmd;$env:Path" $env:Path = "C:\Program Files\Git\cmd;$env:Path"
git clone https://git.fig-lang.cn/$env:GITHUB_REPOSITORY . git clone https://git.fig-lang.cn/$env:GITHUB_REPOSITORY .
git checkout $env:GITHUB_REF git checkout ${{ github.ref }}
- name: 设置版本 - name: 设置版本和提交信息
run: | run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') { if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') {
@@ -161,15 +193,21 @@ jobs:
if (-not $VERSION) { $VERSION = $env:VERSION_INPUT } if (-not $VERSION) { $VERSION = $env:VERSION_INPUT }
if (-not $VERSION) { $VERSION = "dev-build" } if (-not $VERSION) { $VERSION = "dev-build" }
} else { } else {
$VERSION = $env:GITHUB_REF_NAME $VERSION = "${{ github.ref_name }}"
} }
Write-Host "构建版本: $VERSION" Write-Host "构建版本: $VERSION"
"VERSION=$VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
# 确保无 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) - name: 构建项目 (Windows Native)
run: | run: |
xmake f -p windows -a x86_64 -m release -y xmake f -p windows -a x86_64 -m release -y
xmake build -j $env:NUMBER_OF_PROCESSORS xmake build -j $env:NUMBER_OF_PROCESSORS Fig
Write-Host 'Windows构建成功。' Write-Host 'Windows构建成功。'
# 🔧 新增构建Windows平台安装器 # 🔧 新增构建Windows平台安装器
@@ -186,12 +224,12 @@ jobs:
run: | run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 确保版本号不为空
$VERSION = $env:VERSION $VERSION = $env:VERSION
if ([string]::IsNullOrEmpty($VERSION)) { if (-not $VERSION) {
Write-Host "警告:版本号为空,使用默认值 dev-build" $VERSION = "${{ github.ref_name }}"
$VERSION = "dev-build" Write-Host "⚠️ 警告:从环境变量获取 VERSION 失败,回退到 github.ref_name: $VERSION"
} }
Write-Host "打包版本: $VERSION"
$PACKAGE_NAME = "Fig-${VERSION}-windows-x86_64" $PACKAGE_NAME = "Fig-${VERSION}-windows-x86_64"
Write-Host "打包名称: $PACKAGE_NAME" Write-Host "打包名称: $PACKAGE_NAME"
@@ -228,57 +266,100 @@ jobs:
run: | run: |
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 重新计算版本号(不使用 $env:VERSION $VERSION = $env:VERSION
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') { $COMMIT_MSG = $env:COMMIT_MSG
$VERSION = $env:INPUT_VERSION
if (-not $VERSION) { $VERSION = "dev-build" } if (-not $VERSION) {
} else { $VERSION = "${{ github.ref_name }}"
$VERSION = $env:GITHUB_REF_NAME Write-Host "⚠️ 警告:从环境变量获取 VERSION 失败,回退到 github.ref_name: $VERSION"
} }
Write-Host "正在上传Windows版本到发布: $VERSION" if (-not $VERSION) {
Write-Host "❌ 错误:版本号仍然为空,无法创建发布。"
exit 1
}
$REPO = $env:GITHUB_REPOSITORY $REPO = $env:GITHUB_REPOSITORY
$API = "https://git.fig-lang.cn/api/v1/repos/$REPO" $API = "https://git.fig-lang.cn/api/v1/repos/$REPO"
$TOKEN = $env:GITEA_TOKEN $TOKEN = $env:GITEA_TOKEN
$HEADERS = @{
Authorization = "token $TOKEN"
'Content-Type' = 'application/json'
}
Write-Host "正在上传Windows版本到发布 $VERSION ..." $ZIP_FILE = "Fig-$VERSION-windows-x86_64.zip"
$HASH_FILE = "Fig-$VERSION-windows-x86_64.sha256"
$INSTALLER_PATH = "Installer\ConsoleInstaller\dist\windows\FigSetup.exe"
# 1. 尝试通过标签获取 Release ID if (-not (Test-Path $ZIP_FILE)) {
$RELEASE_RESPONSE = Invoke-RestMethod -Uri "$API/releases/tags/$VERSION" -Headers @{Authorization = "token $TOKEN" } -ErrorAction SilentlyContinue Write-Host "❌ 错误找不到ZIP文件 $ZIP_FILE"
if ($RELEASE_RESPONSE -and $RELEASE_RESPONSE.id) { exit 1
$RELEASE_ID = $RELEASE_RESPONSE.id }
Write-Host "找到已有发布 ID: $RELEASE_ID"
} else { $CREATE_BODY = @{
# 如果不存在,则创建新发布 tag_name = $VERSION
Write-Host '发布不存在,正在创建...' name = "Fig $VERSION"
$CREATE_BODY = @{tag_name = $VERSION; name = "Fig $VERSION"; draft = $false; prerelease = $false } | ConvertTo-Json body = $COMMIT_MSG
$CREATE_RESPONSE = Invoke-RestMethod -Method Post -Uri "$API/releases" -Headers @{Authorization = "token $TOKEN"; 'Content-Type' = 'application/json' } -Body $CREATE_BODY draft = $false
if ($CREATE_RESPONSE -and $CREATE_RESPONSE.id) { prerelease = $false
$RELEASE_ID = $CREATE_RESPONSE.id } | ConvertTo-Json -Compress
Write-Host "创建新发布 ID: $RELEASE_ID"
} else { Write-Host "正在检查版本 $VERSION 的发布状态..."
Write-Host '错误:无法获取或创建发布' $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 exit 1
} }
} }
# 2. 使用 Release ID 上传资产
Write-Host '正在上传 ZIP 文件...'
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-windows-x86_64.zip" -Headers @{Authorization = "token $TOKEN"; 'Content-Type' = 'application/octet-stream' } -InFile "Fig-$VERSION-windows-x86_64.zip"
Write-Host '正在上传校验文件...'
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-windows-x86_64.sha256" -Headers @{Authorization = "token $TOKEN"; 'Content-Type' = 'text/plain' } -InFile "Fig-$VERSION-windows-x86_64.sha256"
# 🔧 新增上传Windows安装器
$InstallerPath = "Installer\ConsoleInstaller\dist\windows\FigSetup.exe"
if (Test-Path $InstallerPath) {
Write-Host '正在上传Windows安装器...'
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=FigSetup.exe" `
-Headers @{
Authorization = "token $TOKEN"
'Content-Type' = 'application/octet-stream'
} -InFile $InstallerPath
} }
Write-Host '✅ Windows版本发布完成' # 上传资产
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版本发布完成"

View File

@@ -11,3 +11,69 @@ invalid!
const Pi := 3.14; // recommended, auto detect type const Pi := 3.14; // recommended, auto detect type
// equal -> const Pi: Double = 3.14; // 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,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

@@ -6,6 +6,6 @@ def fib(x:int) -> int:
if __name__ == '__main__': if __name__ == '__main__':
t0 = tt() t0 = tt()
result = fib(25) result = fib(30)
t1 = tt() t1 = tt()
print('cost: ',t1-t0, 'result:', result) 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,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();

View File

@@ -9,7 +9,7 @@ import json
import zipfile import zipfile
import platform import platform
from os import path as ospath from os import path as ospath
from os import mkdir,chdir from os import mkdir,chdir,remove,rename
from sys import exit, argv from sys import exit, argv
@@ -42,19 +42,14 @@ def resolveInstallPath(os: int):
return default_path[os] return default_path[os]
def getReleases(): def getReleases():
api_postfix = '/api/v1/repos/PuqiAR/Fig/releases' api_postfix = '/api/v1/repos/PuqiAR/Fig-TreeWalker/releases'
api_url = GITEA_URL + api_postfix api_url = GITEA_URL + api_postfix
rel = requests.get(api_url).text rel = requests.get(api_url).text
# print(rel) # print(rel)
return rel return rel
def install(url, path:str) -> None: def install(url, path:str, filename:str) -> None:
if not ospath.exists(path):
mkdir(path)
filename = path.split('/')[-1]
response = requests.get(url, stream=True) response = requests.get(url, stream=True)
total_size = int(response.headers.get('content-length', 0)) total_size = int(response.headers.get('content-length', 0))
@@ -64,12 +59,29 @@ def install(url, path:str) -> None:
f.write(data) f.write(data)
b.update(len(data)) b.update(len(data))
print()
print(f'{filename} download completed.') print(f'{filename} download completed.')
print(f'unziping to {path} ...') print(f'unziping to {path} ...')
destZipPath = ospath.dirname(path)
with zipfile.ZipFile(filename) as zip: with zipfile.ZipFile(filename) as zip:
zip.extractall(path) zip.extractall(destZipPath) # 解压到安装目录上一层
rename(ospath.join(destZipPath, ospath.splitext(filename)[0]), path)
print()
print('unziping completed') print('unziping completed')
print('\n==========')
print('cleaning...')
remove(filename)
def osEnumToStr(os: int) -> str:
if os == Windows:
return 'windows'
elif os == Linux:
return 'linux'
elif os == Darwin:
return 'darwin'
return 'other'
def main() -> None: def main() -> None:
@@ -84,7 +96,7 @@ def main() -> None:
print(f'Install to (default: {dpath}): ', end='') print(f'Install to (default: {dpath}): ', end='')
path = input() path = input()
if path.isspace(): if not path:
path = dpath path = dpath
print() print()
@@ -97,7 +109,7 @@ def main() -> None:
print('No version has been released!') print('No version has been released!')
exit(0) exit(0)
print('There are {} versions:' % len(releases)) print(f'There are {len(releases)} versions:')
i = 1 i = 1
for release in releases: for release in releases:
print(f" {i} {release['name']} - {release['body']}") print(f" {i} {release['name']} - {release['body']}")
@@ -114,8 +126,7 @@ def main() -> None:
print() print()
version = insVersion version = insVersion
if not usrInput.isspace(): if usrInput:
if '.' in usrInput: if '.' in usrInput:
for release in releases: for release in releases:
if release['tag_name'].find(usrInput) != -1: if release['tag_name'].find(usrInput) != -1:
@@ -135,13 +146,15 @@ def main() -> None:
print(f"Installing Fig-{version['tag_name']}") print(f"Installing Fig-{version['tag_name']}")
url = None url = None
assetName = None
for asset in version['assets']: for asset in version['assets']:
if asset['name'].find(osName) != -1: assetName = asset['name']
if assetName.find(osEnumToStr(osName)) != -1 and assetName.find('.zip') != -1:
url = asset['browser_download_url'] url = asset['browser_download_url']
break break
if url: if url:
install(url, path) install(url, path, assetName)
else: else:
print('Could not find artifact:') print('Could not find artifact:')
print(version['assets']) print(version['assets'])

View File

@@ -1,15 +1,18 @@
# Fig Language - A Modern Scripting Language # Fig Language - A Modern Scripting Language
[Fig-Gitea](https://git.fig-lang.cn/PuqiAR/Fig) [Fig-Gitea](https://git.fig-lang.cn/PuqiAR/Fig)
Recommend view on Gitea Endpoint
[简体中文](README_ZH-CN.md "Chinese version") [简体中文](README_ZH-CN.md "Chinese version")
**Fig** is a statically-typed, expression-oriented programming language designed for clarity, safety, and modern development practices. With features inspired by Go, Rust, and JavaScript, Fig aims to provide a productive development experience while maintaining strong type safety. **Fig** is a dynamically strongly typed programming language designed for clarity, safety, and modern development practices. With features inspired by Go, Rust, and JavaScript, Fig aims to provide a productive development experience while maintaining strong type safety.
[LanguageTutorial(zh_CN)](docs/zh_CN/01-简介.md "Chinese version")
## Features ## Features
### 🚀 Core Language Features ### 🚀 Core Language Features
- **Static typing with type inference** - Strong typing with minimal annotations - **Dynamic typing with type inference** - Strong typing with minimal annotations
- **Modern control flow** - Full `for` loop support with proper scoping - **Modern control flow** - Full `for` loop support with proper scoping
- **First-class functions** - Lambda expressions and closures - **First-class functions** - Lambda expressions and closures
- **Rich data structures** - Structs, lists, maps, and tuples - **Rich data structures** - Structs, lists, maps, and tuples
@@ -31,6 +34,12 @@
1. Clone the repository: 1. Clone the repository:
```bash
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
# Recommend
```
or
```bash ```bash
git clone https://github.com/PuqiAR/Fig.git git clone https://github.com/PuqiAR/Fig.git
``` ```
@@ -55,35 +64,6 @@ xmake run Fig [file]
Replace `[file]` with the path to your input file. Replace `[file]` with the path to your input file.
### 📁 Project Structure
.
├── ExampleCodes # Example programs & performance tests
│ └── SpeedTest # Performance benchmark samples
├── LICENSE # Project license
├── Logo # Project logo assets
├── README.md # English README
├── README_ZH-CN.md # Chinese README
├── compile_flags.txt # Compiler flags helper
├── fig-vscode # VSCode extension project
│ ├── node_modules # Extension dependencies
│ ├── out # Built extension output
│ ├── src # Extension source code
│ └── syntaxes # Syntax highlighting definition
├── src # Core Fig language source
│ ├── Ast # AST definitions
│ ├── Context # Runtime context
│ ├── Core # Core utilities (UTF8/string/etc.)
│ ├── Error # Error handling system
│ ├── Evaluator # Interpreter / evaluator
│ ├── Lexer # Lexical analyzer
│ ├── Module # Modules and builtins
│ ├── Parser # Parser
│ ├── Token # Token definitions
│ ├── Utils # Utilities & helper headers
│ └── Value # Runtime type/value system
├── test.fig # Test script
└── xmake.lua # Xmake build config
## Language Philosophy ## Language Philosophy
Fig is designed around several core principles: Fig is designed around several core principles:
@@ -94,3 +74,59 @@ Replace `[file]` with the path to your input file.
Modern ergonomics - Developer experience matters Modern ergonomics - Developer experience matters
Gradual learning - Simple to start, powerful when needed Gradual learning - Simple to start, powerful when needed
## Performance Summary
**Version:** 0.4.3-alpha (Tree-walker Interpreter)
**Test Hardware:** i5-13490F, Windows 11
**Execution Times for Fibonacci(30):**
* Naive Recursion: **5.47s** (~2.14× faster than 0.4.2-alpha)
* Memoization: **0.55ms** (~1.69× faster than 0.4.2-alpha)
* Iteration: **0.10ms** (~3.73× faster than 0.4.2-alpha)
* Tail Recursion: **0.16ms** (~2.55× faster than 0.4.2-alpha)
**Visual Comparison:**
```
Naive Recursion : █████████████████████████ 5.47s
Memoization : ▉ 0.55ms
Iteration : ▍ 0.10ms
Tail Recursion : ▎ 0.16ms
```
**Key Insight:** Algorithm choice still dominates performance, while 0.4.3-alpha shows significant improvements in function call and loop efficiency.
**Detailed Reports:** [English](./docs/benchmark_result/0.4.3-alpha/benchmark_result_en_0.4.3-alpha.pdf) | [中文](./docs/benchmark_result/0.4.3-alpha/benchmark_result_zh_0.4.3-alpha.pdf)
---
## Older version reports...
**Version:** 0.4.2-alpha (Tree-walker Interpreter)
**Test Hardware:** i5-13490F, Windows 11
**Execution Times for Fibonacci(30):**
- Naive Recursion: **11.72s**
- Memoization: **0.93ms** (12,600× faster)
- Iteration: **0.37ms** (31,300× faster)
- Tail Recursion: **0.40ms** (29,200× faster)
**Visual Comparison:**
```
Naive Recursion : ████████████████████████████████████████ 11.72s
Memoization : ▉ 0.93ms
Iteration : ▍ 0.37ms
Tail Recursion : ▎ 0.40ms
```
**Key Insight:** Algorithm choice dominates performance in this tree-walker implementation.
**Detailed Reports:** [English](./docs/benchmark_result/0.4.2-alpha/benchmark_result_en_0.4.2-alpha.pdf) | [中文](./docs/benchmark_result/0.4.2-alpha/benchmark_result_zh_0.4.2-alpha.pdf)
## Language Documents
see ./docs/en_US/...
We're looking for translators to help translate our project and make it accessible to more language communities.

View File

@@ -4,12 +4,14 @@
[English](README.md "英文版本") [English](README.md "英文版本")
**Fig** 是一种静态类型、面向表达式的编程语言专为清晰性、安全性和现代开发实践而设计。Fig 融合了 Go、Rust 和 JavaScript 的灵感,旨在提供高效的开发体验,同时保持强大的类型安全。 **Fig** 是一种动态强类型的编程语言专为清晰性、安全性和现代开发实践而设计。Fig 融合了 Go、Rust 和 JavaScript 的灵感,旨在提供高效的开发体验,同时保持强大的类型安全。
[语言入门教程(zh_CN)](docs/zh_CN/01-简介.md "第一章")
## 特性 ## 特性
### 🚀 核心语言特性 ### 🚀 核心语言特性
- **态类型与类型推断** - 强类型系统,最少类型注解 - **态类型与类型推断** - 强类型系统,最少类型注解
- **现代控制流** - 完整的 `for` 循环支持,正确的作用域管理 - **现代控制流** - 完整的 `for` 循环支持,正确的作用域管理
- **一等公民函数** - Lambda 表达式和闭包 - **一等公民函数** - Lambda 表达式和闭包
- **丰富的数据结构** - 结构体、列表、映射和元组 - **丰富的数据结构** - 结构体、列表、映射和元组
@@ -24,10 +26,17 @@
### 使用教程 ### 使用教程
1. 克隆存储库: 1. 克隆存储库:
```bash
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
# 推荐
```
```bash ```bash
git clone https://github.com/PuqiAR/Fig.git git clone https://github.com/PuqiAR/Fig.git
``` ```
2. 切换到项目目录: 2. 切换到项目目录:
```bash ```bash
@@ -42,43 +51,11 @@ xmake build Fig
4. 运行程序: 4. 运行程序:
```bash ```bash
xmake run Fig [file] xmake run Fig [file]
``` ```
`[file]`替换为输入文件的路径。 `[file]`替换为输入文件的路径。
### 📁 项目结构
.
├── ExampleCodes # 示例代码与性能测试样例
│ └── SpeedTest # 性能相关测试示例
├── LICENSE # 项目开源协议
├── Logo # 项目标识资源
├── README.md # 英文 README
├── README_ZH-CN.md # 中文 README
├── compile_flags.txt # C/C++ 编译器参数提示
├── fig-vscode # VSCode 插件项目
│ ├── node_modules # VSCode 插件依赖
│ ├── out # 构建产物
│ ├── src # VSCode 插件源码
│ └── syntaxes # 语法高亮定义
├── src # Fig 语言核心源码
│ ├── Ast # 抽象语法树节点
│ ├── Context # 运行上下文
│ ├── Core # 核心基础设施(字符串/UTF8 等)
│ ├── Error # 错误系统
│ ├── Evaluator # 解释执行器
│ ├── Lexer # 词法分析器
│ ├── Module # 模块与内置库
│ ├── Parser # 语法解析器
│ ├── Token # Token 定义
│ ├── Utils # 实用工具与第三方 header
│ └── Value # 运行时类型系统与值表示
├── test.fig # 测试脚本
└── xmake.lua # Xmake 构建脚本
## 语言设计哲学 ## 语言设计哲学
Fig 围绕几个核心原则设计: Fig 围绕几个核心原则设计:
@@ -86,3 +63,58 @@ Fig 围绕几个核心原则设计:
2. **默认为安全** - 在编译时防止常见错误 2. **默认为安全** - 在编译时防止常见错误
3. **现代人机工程学** - 开发者体验很重要 3. **现代人机工程学** - 开发者体验很重要
4. **渐进式学习** - 入门简单,需要时功能强大 4. **渐进式学习** - 入门简单,需要时功能强大
## 性能概览
**版本:** 0.4.3-alpha (树遍历解释器)
**测试硬件:** i5-13490F, Windows 11
**计算 Fibonacci(30) 的执行时间:**
* 朴素递归: **5.47秒** (~比0.4.2-alpha快2.14倍)
* 记忆化: **0.55毫秒** (~比0.4.2-alpha快1.69倍)
* 迭代: **0.10毫秒** (~比0.4.2-alpha快3.73倍)
* 尾递归: **0.16毫秒** (~比0.4.2-alpha快2.55倍)
**可视化对比:**
```
朴素递归 : █████████████████████████ 5.47秒
记忆化递归 : ▉ 0.55毫秒
迭代算法 : ▍ 0.10毫秒
尾递归 : ▎ 0.16毫秒
```
**核心发现:** 算法选择仍然主导性能,同时 0.4.3-alpha 在函数调用和循环效率上有明显提升。
**详细报告:** [English](./docs/benchmark_result/0.4.3-alpha/benchmark_result_en_0.4.3-alpha.pdf) | [中文](./docs/benchmark_result/0.4.3-alpha/benchmark_result_zh_0.4.3-alpha.pdf)
---
## 旧版本报告...
**版本:** 0.4.2-alpha (树遍历解释器)
**测试硬件:** i5-13490F, Windows 11
**计算 Fibonacci(30) 的执行时间:**
- 朴素递归: **11.72秒**
- 记忆化: **0.93毫秒** (快12,600倍)
- 迭代: **0.37毫秒** (快31,300倍)
- 尾递归: **0.40毫秒** (快29,200倍)
**可视化对比:**
```
朴素递归 : ████████████████████████████████████████ 11.72秒
记忆化递归 : ▉ 0.93毫秒
迭代算法 : ▍ 0.37毫秒
尾递归 : ▎ 0.40毫秒
```
**核心发现:** 在此树遍历实现中,算法选择主导性能表现。
**详细报告:** [English](./docs/benchmark_result/0.4.2-alpha/benchmark_result_en_0.4.2-alpha.pdf) | [中文](./docs/benchmark_result/0.4.2-alpha/benchmark_result_zh_0.4.2-alpha.pdf)
## 语言文档
在 docs/zh_CN/... 查看更多
我们正在寻找译者来帮助翻译项目文件以便于不同语言社区的使用

View File

@@ -0,0 +1,117 @@
# Fig Language Performance Benchmark Report
## Version: 0.4.2-alpha (Tree-walker Interpreter)
### Test Environment
- **CPU:** Intel Core i5-13490F
- **OS:** Windows 11
- **Compiler/Interpreter:** Fig Tree-walker v0.4.2-alpha
- **Test Date:** Current test execution
### Executive Summary
This benchmark evaluates the performance of four different Fibonacci algorithm implementations in Fig language, calculating the 30th Fibonacci number (832,040). The results demonstrate significant performance variations based on algorithmic approach, highlighting the interpreter's efficiency characteristics.
## Performance Results
### Raw Execution Times
| Algorithm | Time (seconds) | Time (milliseconds) | Relative Speed |
| --------------------------- | -------------- | ------------------- | ---------------- |
| `fib` (Naive Recursion) | 11.7210479 s | 11721.0479 ms | 1.00× (baseline) |
| `fib_memo` (Memoization) | 0.0009297 s | 0.9297 ms | 12,600× faster |
| `fib_iter` (Iterative) | 0.0003746 s | 0.3746 ms | 31,300× faster |
| `fib_tail` (Tail Recursion) | 0.0004009 s | 0.4009 ms | 29,200× faster |
### Visual Performance Comparison
```
Naive Recursion : ████████████████████████████████████████ 11.72s
Memoization : ▉ 0.93ms
Iteration : ▍ 0.37ms
Tail Recursion : ▎ 0.40ms
```
## Detailed Analysis
### 1. Naive Recursive Implementation (`fib`)
- **Time:** 11.721 seconds (11,721 ms)
- **Algorithm Complexity:** O(2ⁿ) exponential
- **Performance Notes:**
- Demonstrates the high cost of repeated function calls in tree-walker interpreters
- Shows exponential time complexity with just n=30
- Highlights the need for algorithmic optimization in interpreted languages
### 2. Memoized Recursive Implementation (`fib_memo`)
- **Time:** 0.93 milliseconds
- **Algorithm Complexity:** O(n) linear (with memoization overhead)
- **Performance Notes:**
- 12,600× speedup over naive recursion
- Shows efficient hash table/dictionary operations in Fig
- Demonstrates that caching can overcome interpreter overhead
### 3. Iterative Implementation (`fib_iter`)
- **Time:** 0.375 milliseconds
- **Algorithm Complexity:** O(n) linear
- **Performance Notes:**
- Fastest implementation (31,300× faster than naive)
- Shows efficient loop execution and variable operations
- Minimal function call overhead
### 4. Tail Recursive Implementation (`fib_tail`)
- **Time:** 0.401 milliseconds
- **Algorithm Complexity:** O(n) linear
- **Performance Notes:**
- Comparable to iterative approach (slightly slower due to recursion overhead)
- Current interpreter does not implement Tail Call Optimization (TCO)
- Shows linear recursion is efficient for moderate depths (n=30)
## Technical Insights
### Interpreter Performance Characteristics
1. **Function Call Overhead:** Significant, as shown by the naive recursion performance
2. **Loop Efficiency:** Excellent, with iterative approaches performing best
3. **Memory Access:** Hash table operations (memoization) are efficient
4. **Recursion Depth:** Linear recursion (tail recursion) performs well up to moderate depths
### Algorithmic Impact
The benchmark clearly demonstrates that **algorithm choice has a greater impact than interpreter optimization** in this version:
- Poor algorithm (naive recursion): 11.7 seconds
- Good algorithm (any O(n) approach): < 1 millisecond
## Version-Specific Observations (v0.4.2-alpha)
### Strengths
- Excellent performance for iterative algorithms
- Efficient basic operations (arithmetic, loops, conditionals)
- Effective memory access patterns for cached results
- Linear recursion performance acceptable for typical use cases
### Areas for Improvement
- High function call overhead in deeply recursive scenarios
- No tail call optimization implemented
- Exponential algorithm performance shows interpreter limits
## Recommendations for Developers
1. **Prefer iterative solutions** for performance-critical code
2. **Use memoization** for recursive problems with overlapping subproblems
3. **Tail recursion is acceptable** for linear recursion patterns
4. **Avoid exponential algorithms** in interpreted code
5. **Benchmark different approaches** as algorithmic choice dominates performance
## Conclusion
Fig v0.4.2-alpha demonstrates **practical performance for well-designed algorithms**. While the tree-walker interpreter has inherent overhead for certain patterns (like deep recursion), it executes efficient O(n) algorithms with sub-millisecond performance for n=30.
The interpreter shows particular strength in:
- Iterative loop execution
- Basic arithmetic and control flow
- Dictionary/table operations for caching
The performance characteristics are suitable for a wide range of application domains, provided developers employ standard algorithmic optimization techniques.
---
**Report Generated:** Based on actual benchmark execution
**Interpreter Type:** Tree-walker
**Version:** 0.4.2-alpha
**Key Takeaway:** Algorithmic efficiency dominates performance; Fig executes optimized algorithms efficiently despite being an alpha-stage tree-walker interpreter.

View File

@@ -0,0 +1,117 @@
# Fig 语言性能基准测试报告
## 版本: 0.4.2-alpha (树遍历解释器)
### 测试环境
- **CPU:** Intel Core i5-13490F
- **操作系统:** Windows 11
- **编译器/解释器:** Fig 树遍历解释器 v0.4.2-alpha
- **测试日期:** 当前测试执行
### 执行摘要
本基准测试评估了 Fig 语言中四种不同斐波那契算法实现的性能计算第30个斐波那契数832,040。结果显示基于算法方法的显著性能差异突出了解释器的效率特性。
## 性能结果
### 原始执行时间
| 算法 | 时间(秒) | 时间(毫秒) | 相对速度 |
| ------------------- | ------------ | ------------- | ------------ |
| `fib` (朴素递归) | 11.7210479 s | 11721.0479 ms | 1.00× (基准) |
| `fib_memo` (记忆化) | 0.0009297 s | 0.9297 ms | 12,600× 更快 |
| `fib_iter` (迭代) | 0.0003746 s | 0.3746 ms | 31,300× 更快 |
| `fib_tail` (尾递归) | 0.0004009 s | 0.4009 ms | 29,200× 更快 |
### 可视化性能对比
```
朴素递归 : ████████████████████████████████████████ 11.72秒
记忆化递归 : ▉ 0.93毫秒
迭代算法 : ▍ 0.37毫秒
尾递归 : ▎ 0.40毫秒
```
## 详细分析
### 1. 朴素递归实现 (`fib`)
- **时间:** 11.721 秒 (11,721 毫秒)
- **算法复杂度:** O(2ⁿ) 指数级
- **性能说明:**
- 展示了树遍历解释器中重复函数调用的高成本
- 在仅 n=30 的情况下显示了指数时间复杂度
- 突出了解释型语言中算法优化的必要性
### 2. 记忆化递归实现 (`fib_memo`)
- **时间:** 0.93 毫秒
- **算法复杂度:** O(n) 线性(含记忆化开销)
- **性能说明:**
- 比朴素递归快 12,600 倍
- 显示 Fig 中哈希表/字典操作的高效性
- 证明缓存可以克服解释器开销
### 3. 迭代实现 (`fib_iter`)
- **时间:** 0.375 毫秒
- **算法复杂度:** O(n) 线性
- **性能说明:**
- 最快的实现(比朴素递归快 31,300 倍)
- 显示高效的循环执行和变量操作
- 函数调用开销最小
### 4. 尾递归实现 (`fib_tail`)
- **时间:** 0.401 毫秒
- **算法复杂度:** O(n) 线性
- **性能说明:**
- 与迭代方法相当(由于递归开销略慢)
- 当前解释器未实现尾调用优化TCO
- 显示线性递归在中等深度n=30下是高效的
## 技术洞察
### 解释器性能特征
1. **函数调用开销:** 显著,如朴素递归性能所示
2. **循环效率:** 优秀,迭代方法表现最佳
3. **内存访问:** 哈希表操作(记忆化)高效
4. **递归深度:** 线性递归(尾递归)在中等深度下表现良好
### 算法影响
基准测试清楚地表明,**在此版本中,算法选择比解释器优化影响更大**
- 差算法朴素递归11.7 秒
- 好算法(任何 O(n) 方法):< 1 毫秒
## 版本特定观察 (v0.4.2-alpha)
### 优势
- 迭代算法性能优秀
- 基本操作算术循环条件判断高效
- 缓存结果的内存访问模式有效
- 线性递归性能在典型用例中可接受
### 改进空间
- 深度递归场景中函数调用开销高
- 未实现尾调用优化
- 指数算法性能显示解释器限制
## 给开发者的建议
1. **性能关键代码优先使用迭代解决方案**
2. **对于有重叠子问题的递归问题使用记忆化**
3. **线性递归模式可接受尾递归**
4. **避免在解释型代码中使用指数算法**
5. **基准测试不同方法**因为算法选择主导性能
## 结论
Fig v0.4.2-alpha 展示了**对设计良好的算法的实用性能**。虽然树遍历解释器对某些模式如深度递归有固有开销但它能以亚毫秒性能执行高效的 O(n) 算法n=30时
解释器在以下方面表现特别出色
- 迭代循环执行
- 基本算术和控制流
- 用于缓存的字典/表操作
性能特征适用于广泛的应用领域前提是开发者采用标准的算法优化技术
---
**报告生成时间:** 基于实际基准测试执行
**解释器类型:** 树遍历解释器
**版本:** 0.4.2-alpha
**关键要点:** 算法效率主导性能尽管 Fig alpha 阶段的树遍历解释器但它能高效执行优化算法

View File

@@ -0,0 +1,163 @@
# Fig Language Performance Benchmark Report
## Version: 0.4.3-alpha (Tree Traversal Interpreter)
### Preface
This report presents benchmark tests of Fibonacci algorithms in Fig v0.4.3-alpha tree traversal interpreter, compared with version 0.4.2-alpha. Results show significant performance improvements in function calls, loops, and recursion optimizations in 0.4.3-alpha, especially in iterative and tail-recursive implementations.
### Test Environment
* **CPU:** Intel Core i5-13490F
* **Operating System:** Windows 11
* **Interpreter:** Fig Tree Traversal Interpreter v0.4.3-alpha
* **Test Date:** Current execution
### Executive Summary
This benchmark evaluates four different Fibonacci algorithm implementations in Fig, computing the 30th Fibonacci number (832,040). Algorithm choice remains the dominant factor for performance, while interpreter improvements in function call and loop efficiency are also reflected.
## Performance Results
### Latest Floating Execution Time (0.4.3-alpha)
| Algorithm | Time (s) | Time (ms) | Relative Speed |
| --------------------------- | ----------- | ---------- | ---------------- |
| `fib` (Naive Recursion) | 5.471 s | 5471.37 ms | 1.00× (baseline) |
| `fib_memo` (Memoization) | 0.0005503 s | 0.5503 ms | 9,950× faster |
| `fib_iter` (Iterative) | 0.0001004 s | 0.1004 ms | 54,500× faster |
| `fib_tail` (Tail Recursion) | 0.0001573 s | 0.1573 ms | 34,800× faster |
### Comparison with 0.4.2-alpha
| Algorithm | 0.4.2-alpha Time | 0.4.3-alpha Time | Performance Gain |
| --------------------------- | ---------------- | ---------------- | ---------------- |
| `fib` (Naive Recursion) | 11.721 s | 5.471 s | ~2.14× |
| `fib_memo` (Memoization) | 0.930 ms | 0.550 ms | ~1.69× |
| `fib_iter` (Iterative) | 0.375 ms | 0.100 ms | ~3.73× |
| `fib_tail` (Tail Recursion) | 0.401 ms | 0.157 ms | ~2.55× |
---
## Visual Performance Comparison (Horizontal Bar Placeholder)
0.4.2-alpha vs 0.4.3-alpha
**Note:** Each line contains two bars: gray for 0.4.2-alpha, blue for 0.4.3-alpha
<table>
<tr>
<th>Algorithm</th><th>Performance Comparison</th>
</tr>
<tr>
<td>fib</td>
<td>
<span style="display:inline-block; background-color:#555555; width:350px; height:20px;"></span>
<span style="display:inline-block;"> 11.72 s</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:165px; height:20px;"></span>
<span style="display:inline-block;"> 5.47 s</span>
</td>
</tr>
<tr>
<td>fib_memo</td>
<td>
<span style="display:inline-block; background-color:#555555; width:28px; height:20px;"></span>
<span style="display:inline-block;"> 0.93 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:17px; height:20px;"></span>
<span style="display:inline-block;"> 0.55 ms</span>
</td>
</tr>
<tr>
<td>fib_iter</td>
<td>
<span style="display:inline-block; background-color:#555555; width:11px; height:20px;"></span>
<span style="display:inline-block;"> 0.375 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:3px; height:20px;"></span>
<span style="display:inline-block;"> 0.100 ms</span>
</td>
</tr>
<tr>
<td>fib_tail</td>
<td>
<span style="display:inline-block; background-color:#555555; width:12px; height:20px;"></span>
<span style="display:inline-block;"> 0.401 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:5px; height:20px;"></span>
<span style="display:inline-block;"> 0.157 ms</span>
</td>
</tr>
</table>
---
## Detailed Analysis
### 1. Naive Recursion (`fib`)
* **Time:** 5.471 seconds (5471 ms)
* **Algorithm Complexity:** O(2ⁿ) exponential
* **Performance Notes:**
* Reduced by roughly half compared to 0.4.2-alpha
* Function call overhead optimization effective, but exponential growth remains the bottleneck
### 2. Memoized Recursion (`fib_memo`)
* **Time:** 0.550 ms
* **Algorithm Complexity:** O(n) linear
* **Performance Notes:**
* Hash table / cache access efficiency improved
* Sub-millisecond execution suitable for overlapping subproblems
### 3. Iterative (`fib_iter`)
* **Time:** 0.100 ms
* **Algorithm Complexity:** O(n) linear
* **Performance Notes:**
* Fastest implementation, ~3.7× improvement over 0.4.2-alpha
* Loop and arithmetic operation optimization significant
### 4. Tail Recursion (`fib_tail`)
* **Time:** 0.157 ms
* **Algorithm Complexity:** O(n) linear
* **Performance Notes:**
* Slightly slower than iterative, ~2.5× improvement over 0.4.2-alpha
* Tree traversal interpreter optimizations for recursion effective; TCO not implemented
---
## Technical Insights
* Function call overhead significantly reduced
* Loop and arithmetic operations show greatest efficiency gains
* Hash table / cache access highly efficient
* Algorithm choice remains the dominant factor for performance
---
## Recommendations for Developers
1. Prioritize iterative solutions for performance-critical code
2. Use memoization for recursion with overlapping subproblems
3. Tail recursion is suitable for moderate depth, but TCO is not implemented
4. Avoid exponential algorithms in interpreted code
5. Benchmark different implementations, as algorithm choice dominates performance
---
## Conclusion
Fig v0.4.3-alpha tree traversal interpreter shows significant improvements in function call and loop optimizations, particularly benefiting iterative and tail-recursive implementations.
O(n) algorithms execute at sub-millisecond speeds, while exponential recursion remains limited. Overall interpreter performance is adequate for practical applications.
**Report Generated:** Based on actual benchmark execution
**Interpreter Type:** Tree Traversal Interpreter
**Version:** 0.4.3-alpha

View File

@@ -0,0 +1,161 @@
# Fig 语言性能基准测试报告
## 版本: 0.4.3-alpha (树遍历解释器)
### 前言
本报告基于 Fig v0.4.3-alpha 树遍历解释器对 Fibonacci 算法进行了基准测试,并与 0.4.2-alpha 版本对比。结果显示 0.4.3-alpha 在函数调用、循环和递归优化方面都有显著提升,尤其是迭代和尾递归实现性能改善明显。
### 测试环境
* **CPU:** Intel Core i5-13490F
* **操作系统:** Windows 11
* **编译器/解释器:** Fig 树遍历解释器 v0.4.3-alpha
* **测试日期:** 当前测试执行
### 执行摘要
本基准测试评估了 Fig 中四种不同斐波那契算法实现的性能计算第30个斐波那契数832,040。结果显示算法选择仍是性能主导因素同时反映了解释器在函数调用和循环方面的优化效果。
## 性能结果
### 最新浮动执行时间 (0.4.3-alpha)
| 算法 | 时间(秒) | 时间(毫秒) | 相对速度 |
| ------------------- | ----------- | ---------- | ------------ |
| `fib` (朴素递归) | 5.471 s | 5471.37 ms | 1.00× (基准) |
| `fib_memo` (记忆化) | 0.0005503 s | 0.5503 ms | 9,950× 更快 |
| `fib_iter` (迭代) | 0.0001004 s | 0.1004 ms | 54,500× 更快 |
| `fib_tail` (尾递归) | 0.0001573 s | 0.1573 ms | 34,800× 更快 |
### 与 0.4.2-alpha 对比
| 算法 | 0.4.2-alpha 时间 | 0.4.3-alpha 时间 | 性能提升倍数 |
| ------------------- | ---------------- | ---------------- | ------------ |
| `fib` (朴素递归) | 11.721 s | 5.471 s | ~2.14× |
| `fib_memo` (记忆化) | 0.930 ms | 0.550 ms | ~1.69× |
| `fib_iter` (迭代) | 0.375 ms | 0.100 ms | ~3.73× |
| `fib_tail` (尾递归) | 0.401 ms | 0.157 ms | ~2.55× |
---
## 可视化性能对比(横向柱状图占位符)
0.4.2-alpha vs 0.4.3-alpha
**说明:** 每行两条条形:灰色表示 0.4.2-alpha蓝色表示 0.4.3-alpha
<table>
<tr>
<th>算法</th><th>性能对比</th>
</tr>
<tr>
<td>fib</td>
<td>
<span style="display:inline-block; background-color:#555555; width:350px; height:20px;"></span>
<span style="display:inline-block;"> 11.72 s</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:165px; height:20px;"></span>
<span style="display:inline-block;"> 5.47 s</span>
</td>
</tr>
<tr>
<td>fib_memo</td>
<td>
<span style="display:inline-block; background-color:#555555; width:28px; height:20px;"></span>
<span style="display:inline-block;"> 0.93 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:17px; height:20px;"></span>
<span style="display:inline-block;"> 0.55 ms</span>
</td>
</tr>
<tr>
<td>fib_iter</td>
<td>
<span style="display:inline-block; background-color:#555555; width:11px; height:20px;"></span>
<span style="display:inline-block;"> 0.375 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:3px; height:20px;"></span>
<span style="display:inline-block;"> 0.100 ms</span>
</td>
</tr>
<tr>
<td>fib_tail</td>
<td>
<span style="display:inline-block; background-color:#555555; width:12px; height:20px;"></span>
<span style="display:inline-block;"> 0.401 ms</span><br>
<span style="display:inline-block; background-color:#1E90FF; width:5px; height:20px;"></span>
<span style="display:inline-block;"> 0.157 ms</span>
</td>
</tr>
</table>
## 详细分析
### 1. 朴素递归实现 (`fib`)
* **时间:** 5.471 秒 (5471 毫秒)
* **算法复杂度:** O(2ⁿ) 指数级
* **性能说明:**
* 相比 0.4.2-alpha 减少约一半
* 函数调用开销优化有效,但指数增长仍是瓶颈
### 2. 记忆化递归实现 (`fib_memo`)
* **时间:** 0.550 毫秒
* **算法复杂度:** O(n) 线性
* **性能说明:**
* 哈希表/缓存访问效率进一步提高
* 亚毫秒级执行,适合重叠子问题
### 3. 迭代实现 (`fib_iter`)
* **时间:** 0.100 毫秒
* **算法复杂度:** O(n) 线性
* **性能说明:**
* 最快实现,比 0.4.2-alpha 提升 3.7 倍
* 循环和算术操作优化显著
### 4. 尾递归实现 (`fib_tail`)
* **时间:** 0.157 毫秒
* **算法复杂度:** O(n) 线性
* **性能说明:**
* 相比迭代略慢,但比 0.4.2-alpha 提升 2.5 倍
* 树遍历解释器对递归调用优化有效TCO 未实现
---
## 技术洞察
* 函数调用开销显著下降
* 循环和算术操作效率提升最大
* 哈希表/缓存访问效率高
* 算法选择仍是性能主导
---
## 给开发者的建议
1. 性能关键代码优先迭代
2. 重叠子问题递归使用记忆化
3. 尾递归可用于中等深度,但 TCO 未实现
4. 避免指数算法
5. 基准测试不同实现,算法选择主导性能
---
## 结论
Fig v0.4.3-alpha 树遍历解释器在函数调用和循环优化上有显著提升,尤其是迭代和尾递归实现性能改善明显。
O(n) 算法执行亚毫秒级,指数递归仍受限,但整体解释器性能在实际应用中已足够优秀。
**报告生成时间:** 基于实际基准测试执行
**解释器类型:** 树遍历解释器
**版本:** 0.4.3-alpha

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实现

View File

@@ -2,7 +2,7 @@
"name": "fig-vscode", "name": "fig-vscode",
"displayName": "Fig Language", "displayName": "Fig Language",
"description": "VSCode extension for Fig language with syntax highlighting", "description": "VSCode extension for Fig language with syntax highlighting",
"version": "0.0.2", "version": "0.4.3",
"publisher": "PuqiAR", "publisher": "PuqiAR",
"engines": { "engines": {
"vscode": "^1.90.0" "vscode": "^1.90.0"

View File

@@ -57,7 +57,7 @@
"patterns": [ "patterns": [
{ {
"name": "keyword.control.fig", "name": "keyword.control.fig",
"match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b" "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|new|struct|interface|impl|public|return|break|continue|try|catch|throw|is|as)\\b"
}, },
{ "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" } { "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" }
] ]
@@ -78,7 +78,7 @@
}, },
{ {
"name": "keyword.operator.comparison.fig", "name": "keyword.operator.comparison.fig",
"match": "(==|!=|<=|>=|<|>|is)" "match": "(==|!=|<=|>=|<|>)"
}, },
{ {
"name": "punctuation.separator.fig", "name": "punctuation.separator.fig",

View File

@@ -2,7 +2,7 @@
#pragma once #pragma once
#include <Ast/astBase.hpp> #include <Ast/astBase.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/value.hpp>
namespace Fig::Ast namespace Fig::Ast
{ {

View File

@@ -7,7 +7,7 @@ namespace Fig::Ast
class InitExprAst final : public ExpressionAst class InitExprAst final : public ExpressionAst
{ {
public: public:
FString structName; Expression structe;
std::vector<std::pair<FString, Expression>> args; std::vector<std::pair<FString, Expression>> args;
@@ -30,8 +30,8 @@ namespace Fig::Ast
type = AstType::InitExpr; type = AstType::InitExpr;
} }
InitExprAst(FString _structName, std::vector<std::pair<FString, Expression>> _args, InitMode _initMode) : InitExprAst(Expression _structe, std::vector<std::pair<FString, Expression>> _args, InitMode _initMode) :
structName(std::move(_structName)), args(std::move(_args)), initMode(_initMode) structe(std::move(_structe)), args(std::move(_args)), initMode(_initMode)
{ {
type = AstType::InitExpr; type = AstType::InitExpr;
} }

View File

@@ -2,7 +2,7 @@
#include <Ast/astBase.hpp> #include <Ast/astBase.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/value.hpp>
namespace Fig::Ast namespace Fig::Ast
{ {

View File

@@ -3,7 +3,7 @@
#include <Ast/astBase.hpp> #include <Ast/astBase.hpp>
#include <Ast/functionParameters.hpp> #include <Ast/functionParameters.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/value.hpp>
namespace Fig::Ast namespace Fig::Ast
{ {
@@ -23,14 +23,14 @@ namespace Fig::Ast
FString name; FString name;
FunctionParameters paras; FunctionParameters paras;
bool isPublic; bool isPublic;
FString retType; Expression retType;
BlockStatement body; BlockStatement body;
FunctionDefSt() :
retType(ValueType::Null.name) FunctionDefSt()
{ {
type = AstType::FunctionDefSt; type = AstType::FunctionDefSt;
} }
FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, FString _retType, BlockStatement _body) FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, Expression _retType, BlockStatement _body)
{ {
type = AstType::FunctionDefSt; type = AstType::FunctionDefSt;

View File

@@ -6,18 +6,26 @@
namespace Fig::Ast namespace Fig::Ast
{ {
/*
import std.io as stdio; // io --> stdio;
import std.io {print, println};
*/
class ImportSt final : public StatementAst class ImportSt final : public StatementAst
{ {
public: public:
std::vector<FString> path; std::vector<FString> path;
std::vector<FString> names;
FString rename;
ImportSt() ImportSt()
{ {
type = AstType::ImportSt; type = AstType::ImportSt;
} }
ImportSt(std::vector<FString> _path) : ImportSt(std::vector<FString> _path, std::vector<FString> _names, const FString &_rename) :
path(std::move(_path)) path(std::move(_path)), names(std::move(_names)), rename(_rename)
{ {
type = AstType::ImportSt; type = AstType::ImportSt;
} }

View File

@@ -25,7 +25,7 @@ namespace Fig::Ast
{ {
FString name; FString name;
FunctionParameters paras; FunctionParameters paras;
FString returnType; Expression returnType;
BlockStatement defaultBody = nullptr; // nullptr is non-default func BlockStatement defaultBody = nullptr; // nullptr is non-default func
@@ -35,10 +35,22 @@ namespace Fig::Ast
} }
}; };
/*
interface IO : Writable + Readable
{
}
interface XX
{
}
*/
class InterfaceDefAst final : public StatementAst class InterfaceDefAst final : public StatementAst
{ {
public: public:
FString name; FString name;
std::vector<Expression> bundles;
std::vector<InterfaceMethod> methods; std::vector<InterfaceMethod> methods;
std::vector<FString> parents; // Feature, NOT NOW std::vector<FString> parents; // Feature, NOT NOW
bool isPublic; bool isPublic;
@@ -48,8 +60,8 @@ namespace Fig::Ast
type = AstType::InterfaceDefSt; type = AstType::InterfaceDefSt;
} }
InterfaceDefAst(FString _name, std::vector<InterfaceMethod> _methods, bool _isPublic) : InterfaceDefAst(FString _name, std::vector<Expression> _bundles, std::vector<InterfaceMethod> _methods, bool _isPublic) :
name(std::move(_name)), methods(std::move(_methods)), isPublic(_isPublic) name(std::move(_name)), bundles(std::move(_bundles)), methods(std::move(_methods)), isPublic(_isPublic)
{ {
type = AstType::InterfaceDefSt; type = AstType::InterfaceDefSt;
} }

View File

@@ -12,12 +12,12 @@ namespace Fig::Ast
{ {
AccessModifier am; AccessModifier am;
FString fieldName; FString fieldName;
FString tiName; Expression declaredType;
Expression defaultValueExpr; Expression defaultValueExpr;
StructDefField() {} StructDefField() {}
StructDefField(AccessModifier _am, FString _fieldName, FString _tiName, Expression _defaultValueExpr) : StructDefField(AccessModifier _am, FString _fieldName, Expression _declaredType, Expression _defaultValueExpr) :
am(std::move(_am)), fieldName(std::move(_fieldName)), tiName(std::move(_tiName)), defaultValueExpr(std::move(_defaultValueExpr)) am(std::move(_am)), fieldName(std::move(_fieldName)), declaredType(std::move(_declaredType)), defaultValueExpr(std::move(_defaultValueExpr))
{ {
} }
}; };

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Ast/astBase.hpp> #include <Ast/astBase.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
namespace Fig::Ast namespace Fig::Ast
{ {
@@ -11,22 +11,28 @@ namespace Fig::Ast
bool isPublic; bool isPublic;
bool isConst; bool isConst;
FString name; FString name;
FString typeName; // FString typeName;
Expression declaredType;
Expression expr; Expression expr;
VarDefAst() : bool followupType;
typeName(ValueType::Any.name)
VarDefAst()
{ {
type = AstType::VarDefSt; type = AstType::VarDefSt;
declaredType = nullptr;
expr = nullptr;
followupType = false;
} }
VarDefAst(bool _isPublic, bool _isConst, FString _name, FString _info, Expression _expr) : VarDefAst(bool _isPublic, bool _isConst, FString _name, Expression _declaredType, Expression _expr, bool _followupType)
typeName(std::move(_info))
{ {
type = AstType::VarDefSt; type = AstType::VarDefSt;
isPublic = _isPublic; isPublic = _isPublic;
isConst = _isConst; isConst = _isConst;
name = std::move(_name); name = std::move(_name);
declaredType = std::move(_declaredType);
expr = std::move(_expr); expr = std::move(_expr);
followupType = _followupType;
} }
}; };

View File

@@ -34,6 +34,7 @@ namespace Fig::Ast
ListExpr, // [1, "2", 3 ListExpr, // [1, "2", 3
TupleExpr, // (1, 2, 3) TupleExpr, // (1, 2, 3)
MapExpr, // {a: 1} MapExpr, // {a: 1}
InitExpr, // struct{"123", 456} InitExpr, // struct{"123", 456}
FunctionLiteralExpr, FunctionLiteralExpr,
@@ -100,6 +101,8 @@ namespace Fig::Ast
struct AstAddressInfo struct AstAddressInfo
{ {
size_t line, column; size_t line, column;
std::shared_ptr<FString> sourcePath;
std::shared_ptr<std::vector<FString>> sourceLines;
}; };
class _AstBase class _AstBase
@@ -117,30 +120,21 @@ namespace Fig::Ast
_AstBase() {} _AstBase() {}
void setAAI(AstAddressInfo _aai) void setAAI(AstAddressInfo _aai) { aai = std::move(_aai); }
{
aai = std::move(_aai);
}
virtual FString typeName() virtual FString typeName()
{ {
return FString::fromStringView( const auto &name = magic_enum::enum_name(type);
FStringView::fromBasicStringView(magic_enum::enum_name(type))); return FString::fromBasicString(std::string(name.data(), name.length()));
} }
virtual FString toString() virtual FString toString()
{ {
return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column)); return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
} }
AstAddressInfo getAAI() AstAddressInfo getAAI() { return aai; }
{
return aai;
}
AstType getType() AstType getType() { return type; }
{
return type;
}
}; };
class StatementAst : public _AstBase class StatementAst : public _AstBase
@@ -148,10 +142,7 @@ namespace Fig::Ast
public: public:
using _AstBase::_AstBase; using _AstBase::_AstBase;
using _AstBase::operator=; using _AstBase::operator=;
StatementAst() StatementAst() { type = AstType::StatementBase; }
{
type = AstType::StatementBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -162,10 +153,7 @@ namespace Fig::Ast
class EofStmt final : public StatementAst class EofStmt final : public StatementAst
{ {
public: public:
EofStmt() EofStmt() { type = AstType::StatementBase; }
{
type = AstType::StatementBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -178,10 +166,7 @@ namespace Fig::Ast
public: public:
using _AstBase::_AstBase; using _AstBase::_AstBase;
using _AstBase::operator=; using _AstBase::operator=;
ExpressionAst() ExpressionAst() { type = AstType::ExpressionBase; }
{
type = AstType::ExpressionBase;
}
virtual FString toString() override virtual FString toString() override
{ {
@@ -215,6 +200,9 @@ namespace Fig::Ast
GreaterEqual, // >= GreaterEqual, // >=
Is, // a is b Is, // a is b
// 转换
As, // 3.14 as Int
// 三目 // 三目
TernaryCond, TernaryCond,
@@ -238,41 +226,26 @@ namespace Fig::Ast
}; };
static const std::unordered_set<Operator> unaryOps{ static const std::unordered_set<Operator> unaryOps{
Operator::Not, Operator::Not, // !
Operator::Subtract, Operator::Subtract, // -
Operator::BitNot, Operator::BitNot, // ~
Operator::BitAnd, // reference operator &
}; };
static const std::unordered_set<Operator> binaryOps{ static const std::unordered_set<Operator> binaryOps{
Operator::Add, Operator::Add, Operator::Subtract, Operator::Multiply, Operator::Divide,
Operator::Subtract, Operator::Modulo, Operator::Power, Operator::And, Operator::Or,
Operator::Multiply,
Operator::Divide,
Operator::Modulo,
Operator::Power,
Operator::And,
Operator::Or,
Operator::Equal, Operator::Equal, Operator::NotEqual, Operator::Less, Operator::LessEqual,
Operator::NotEqual, Operator::Greater, Operator::GreaterEqual, Operator::Is,
Operator::Less,
Operator::LessEqual,
Operator::Greater,
Operator::GreaterEqual,
Operator::Is,
Operator::BitAnd, Operator::As,
Operator::BitOr,
Operator::BitXor,
Operator::BitNot,
Operator::ShiftLeft,
Operator::ShiftRight,
Operator::Assign, Operator::BitAnd, Operator::BitOr, Operator::BitXor, Operator::BitNot,
Operator::PlusAssign, Operator::ShiftLeft, Operator::ShiftRight,
Operator::MinusAssign,
Operator::AsteriskAssign, Operator::Assign, Operator::PlusAssign, Operator::MinusAssign, Operator::AsteriskAssign,
Operator::SlashAssign, Operator::SlashAssign, Operator::CaretAssign
Operator::CaretAssign
// Operator::Walrus, // Operator::Walrus,
// Operator::Dot // Operator::Dot
@@ -304,6 +277,9 @@ namespace Fig::Ast
{TokenType::GreaterEqual, Operator::GreaterEqual}, {TokenType::GreaterEqual, Operator::GreaterEqual},
{TokenType::Is, Operator::Is}, {TokenType::Is, Operator::Is},
// 转换
{TokenType::As, Operator::As},
// 三目 // 三目
{TokenType::Question, Operator::TernaryCond}, {TokenType::Question, Operator::TernaryCond},
@@ -348,19 +324,9 @@ namespace Fig::Ast
{ {
public: public:
std::vector<Statement> stmts; std::vector<Statement> stmts;
BlockStatementAst() BlockStatementAst() { type = AstType::BlockStatement; }
{ BlockStatementAst(std::vector<Statement> _stmts) : stmts(std::move(_stmts)) { type = AstType::BlockStatement; }
type = AstType::BlockStatement; virtual FString typeName() override { return FString(u8"BlockStatement"); }
}
BlockStatementAst(std::vector<Statement> _stmts) :
stmts(std::move(_stmts))
{
type = AstType::BlockStatement;
}
virtual FString typeName() override
{
return FString(u8"BlockStatement");
}
virtual FString toString() override virtual FString toString() override
{ {
return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column)); return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Ast/astBase.hpp> #include <Ast/astBase.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <format> #include <format>
@@ -17,8 +17,10 @@ namespace Fig::Ast
func test2(dp1 = 10, dp2:String = "default parameter 2") func test2(dp1 = 10, dp2:String = "default parameter 2")
*/ */
using PosParasType = std::vector<std::pair<FString, FString>>; using PosParasType = std::vector<std::pair<FString, Expression>>;
using DefParasType = std::vector<std::pair<FString, std::pair<FString, Expression>>>; // name type exp
using DefParasType = std::vector<std::pair<FString, std::pair<Expression, Expression>>>;
// name type exp default value
PosParasType posParas; PosParasType posParas;
DefParasType defParas; // default parameters DefParasType defParas; // default parameters
@@ -56,28 +58,28 @@ namespace Fig::Ast
{ {
return FString(variadicPara + u8"..."); return FString(variadicPara + u8"...");
} }
static const auto posParasToString = [this]() { const auto posParasToString = [this]() {
FString out; FString out;
for (auto &p : posParas) for (auto &p : posParas)
{ {
out += p.first; out += p.first;
if (!p.second.empty()) if (p.second != nullptr)
{ {
out += FString(u8":" + p.second); out += FString(u8":" + p.second->toString());
} }
out += u8","; out += u8",";
} }
out.pop_back(); out.pop_back();
return out; return out;
}; };
static const auto defParasToString = [this]() { const auto defParasToString = [this]() {
FString out; FString out;
for (auto &p : defParas) for (auto &p : defParas)
{ {
out += p.first; out += p.first;
if (!p.second.first.empty()) if (p.second.first != nullptr)
{ {
out += FString(u8":" + p.second.first); out += FString(u8":" + p.second.first->toString());
} }
if (p.second.second != nullptr) if (p.second.second != nullptr)
{ {

View File

@@ -0,0 +1,17 @@
#pragma once
#include <Bytecode/CompiledFunction.hpp>
#include <Bytecode/Instruction.hpp>
#include <Bytecode/Chunk.hpp>
namespace Fig
{
struct CallFrame
{
uint64_t ip; // 函数第一个指令 index
uint64_t base; // 第一个参数在栈中位置偏移量
CompiledFunction fn; // 编译过的函数体
};
};

25
src/Bytecode/Chunk.hpp Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <Core/fig_string.hpp>
#include <Bytecode/Instruction.hpp>
#include <Evaluator/Value/value.hpp>
#include <vector>
namespace Fig
{
struct ChunkAddressInfo
{
FString sourcePath;
std::vector<FString> sourceLines;
};
struct Chunk
{
Instructions ins; // vector<Instruction>
std::vector<Object> constants; // 常量池
std::vector<InstructionAddressInfo> instructions_addr; // 下标和ins对齐表示每个Instruction对应的地址
ChunkAddressInfo addr; // 代码块独立Addr
};
};

View File

@@ -0,0 +1,22 @@
#pragma once
#include <Error/error.hpp>
namespace Fig
{
class CompileError : public AddressableError
{
using AddressableError::AddressableError;
virtual FString toString() const override
{
std::string msg = std::format("[CompileError] {} in [{}] {}",
this->message.toBasicString(),
this->src_loc.file_name(),
this->src_loc.function_name());
return FString(msg);
}
virtual FString getErrorType() const override { return FString(u8"CompileError"); }
};
};

View File

@@ -0,0 +1,21 @@
#pragma once
#include <Bytecode/Instruction.hpp>
#include <Bytecode/Chunk.hpp>
namespace Fig
{
struct CompiledFunction
{
Chunk chunk; // 函数代码块
FString name; // 函数名
uint64_t posArgCount; // 位置参数数量
uint64_t defArgCount; // 默认参数数量
bool variadicPara; // 可变参数(是:最后为可变,否:不可变)
uint64_t localCount; // 局部变量数量(不包括参数)
uint64_t slotCount; // = 总参数数量 + 局部变量数量
};
};

View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include <cassert>
#include <vector>
namespace Fig
{
using u8 = uint8_t;
enum class OpCode : u8
{
HALT = 0, // 程序结束
RETURN,
LOAD_LOCAL,
LOAD_CONST,
STORE_LOCAL,
LT,
LTET,
GT,
GTET,
ADD,
SUB,
MUL,
DIV,
JUMP, // + 64 offset (int64_t)
JUMP_IF_FALSE, // + 64 offset (int64_t)
CALL,
};
static constexpr int MAX_LOCAL_COUNT = UINT64_MAX;
static constexpr int MAX_CONSTANT_COUNT = UINT64_MAX;
static constexpr int MAX_FUNCTION_ARG_COUNT = UINT64_MAX;
inline OpCode getLastOpCode()
{
return OpCode::RETURN;
}
struct InstructionAddressInfo
{
size_t line, column;
};
using ProgramCounter = uint64_t;
using InstructionPoint = uint64_t;
class Instruction
{
public:
OpCode code;
int64_t operand;
Instruction(OpCode _code) : code(_code) {}
Instruction(OpCode _code, int64_t _operand)
{
code = _code;
operand = _operand;
}
};
using Instructions = std::vector<Instruction>;
}; // namespace Fig

View File

@@ -0,0 +1,107 @@
#include <Bytecode/Chunk.hpp>
#include <Bytecode/Instruction.hpp>
#include <Bytecode/CompiledFunction.hpp>
#include <VirtualMachine/VirtualMachine.hpp>
#include <chrono>
#include <iostream>
using namespace Fig;
int main()
{
/*
func fib(x)
{
if x <= 1
{
return x;
}
return fib(x - 1) + fib(x - 2);
}
*/
// ---------------- fib ----------------
Instructions fib_ins{
/* 0 */ {OpCode::LOAD_LOCAL, 0}, // x
/* 1 */ {OpCode::LOAD_CONST, 0}, // 1
/* 2 */ {OpCode::LTET}, // x <= 1
/* 3 */ {OpCode::JUMP_IF_FALSE, 2}, // false -> jump to 6
/* 4 */ {OpCode::LOAD_LOCAL, 0}, // return x
/* 5 */ {OpCode::RETURN},
/* 6 */ {OpCode::LOAD_LOCAL, 0}, // x
/* 7 */ {OpCode::LOAD_CONST, 0}, // 1
/* 8 */ {OpCode::SUB}, // x - 1
/* 9 */ {OpCode::LOAD_CONST, 2}, // fib
/* 10 */ {OpCode::CALL, 1}, // fib(x-1)
/* 11 */ {OpCode::LOAD_LOCAL, 0}, // x
/* 12 */ {OpCode::LOAD_CONST, 1}, // 2
/* 13 */ {OpCode::SUB}, // x - 2
/* 14 */ {OpCode::LOAD_CONST, 2}, // fib
/* 15 */ {OpCode::CALL, 1}, // fib(x-2)
/* 16 */ {OpCode::ADD},
/* 17 */ {OpCode::RETURN},
};
std::vector<Object> fib_consts{
Object((int64_t) 1), // 0
Object((int64_t) 2), // 1
Object(), // 2 fib (回填)
};
CompiledFunction fib_fn{
{},
u8"fib",
1, // posArgCount
0,
false,
0, // localCount
1 // slotCount = 参数 x
};
// fib 自引用
fib_consts[2] = Object(Function(&fib_fn));
Chunk fib_chunk{fib_ins, fib_consts, {}, ChunkAddressInfo{}};
fib_fn.chunk = fib_chunk;
// ---------------- main ----------------
Instructions main_ins{
{OpCode::LOAD_CONST, 0}, // 30
{OpCode::LOAD_CONST, 1}, // fib
{OpCode::CALL, 1},
{OpCode::RETURN},
};
std::vector<Object> main_consts{
Object((int64_t)251), // 0
Object(Function(&fib_fn)), // 1
};
Chunk main_chunk{main_ins, main_consts, {}, ChunkAddressInfo{}};
CompiledFunction main_fn{main_chunk, u8"main", 0, 0, false, 0, 0};
CallFrame entry{.ip = 0, .base = 0, .fn = main_fn};
VirtualMachine vm(entry);
using Clock = std::chrono::high_resolution_clock;
auto start = Clock::now();
Object result = vm.Execute();
auto end = Clock::now();
auto duration_secs = std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cout << result.toString().toBasicString() << "\n";
std::cout << "cost: " << duration_secs << "s. " << duration_ms << "ms" << "\n";
}

1585
src/Core/String.hpp Normal file

File diff suppressed because it is too large Load Diff

68
src/Core/StringTest.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include <iostream>
#include <unordered_map>
#include "String.hpp"
using namespace Fig::StringClass::DynamicCapacity;
int main()
{
std::cout << "=== String Test ===\n";
// 1. 构造
String s1("Hello");
String s2("World");
String s3(U"你好世界"); // UTF-8中文
std::cout << "s1: " << s1.toBasicString() << "\n";
std::cout << "s2: " << s2.toBasicString() << "\n";
std::cout << "s3: " << s3.toBasicString() << "\n";
// 2. operator+
String s4 = s1 + String(", ") + s2;
std::cout << "s4 (s1 + ', ' + s2): " << s4.toBasicString() << "\n";
// 3. operator+=
s1 += String("!!!");
std::cout << "s1 after += '!!!': " << s1.toBasicString() << "\n";
// 4. operator==
std::cout << "s1 == 'Hello!!!'? " << (s1 == String("Hello!!!") ? "true" : "false") << "\n";
std::cout << "s1 == s2? " << (s1 == s2 ? "true" : "false") << "\n";
// 5. set / at / operator[]
s2.set(0, 'w'); // 'World' -> 'world'
std::cout << "s2 after set(0,'w'): " << s2.toBasicString() << "\n";
std::cout << "s2.at(1): " << String(s2.at(1)).toBasicString() << "\n";
std::cout << "s2[2]: " << String(s2[2]).toBasicString() << "\n";
// 6. reverse
s1.reverse();
std::cout << "s1 reversed: " << s1.toBasicString() << "\n";
// 7. clear
s3.clear();
std::cout << "s3 cleared, empty? " << (s3.empty() ? "true" : "false") << "\n";
// 8. reserve & shrink_to_fit
s4.reserve(100);
std::cout << "s4 reserved 100, length: " << s4.length() << "\n";
s4.shrink_to_fit();
std::cout << "s4 shrink_to_fit done, length: " << s4.length() << "\n";
// 9. reverse UTF-8 string
String utf8Str(U"🌟🚀"); // emoji
std::cout << "utf8Str: " << utf8Str.toBasicString() << "\n";
utf8Str.reverse();
std::cout << "utf8Str reversed: " << utf8Str.toBasicString() << "\n";
// 10. STL
std::unordered_map<String, String> map = {
{String("我去"), String("123")}
};
std::cout << map[String("我去")];
return 0;
}

View File

@@ -4,7 +4,7 @@
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
#define __FCORE_VERSION "0.3.6-alpha" #define __FCORE_VERSION "0.4.3-alpha"
#if defined(_WIN32) #if defined(_WIN32)
#define __FCORE_PLATFORM "Windows" #define __FCORE_PLATFORM "Windows"

View File

@@ -0,0 +1,21 @@
#pragma once
#include <filesystem>
#ifdef _WIN32
#include <libloaderapi.h>
#endif
namespace Fig
{
inline std::filesystem::path getExecutablePath()
{
#ifdef _WIN32
wchar_t buffer[MAX_PATH];
GetModuleFileNameW(nullptr, buffer, MAX_PATH);
return std::filesystem::path(buffer);
#else
return std::filesystem::canonical("/proc/self/exe");
#endif
}
}; // namespace Fig

View File

@@ -38,11 +38,16 @@ namespace Fig
{ {
public: public:
using std::u8string::u8string; using std::u8string::u8string;
using std::u8string::operator=;
FString operator+(const FString &x) FString operator+(const FString &x)
{ {
return FString(toBasicString() + x.toBasicString()); return FString(toBasicString() + x.toBasicString());
} }
FString operator+(const char8_t *c)
{
return FString(*this + std::u8string(c));
}
explicit FString(const std::u8string &str) explicit FString(const std::u8string &str)
{ {

18
src/Core/runtimeTime.cpp Normal file
View File

@@ -0,0 +1,18 @@
#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;
}
};

10
src/Core/runtimeTime.hpp Normal file
View File

@@ -0,0 +1,10 @@
#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

@@ -58,7 +58,7 @@ namespace Fig
switch (len) switch (len)
{ {
case 1: case 1:
code_point = char_data_[0]; code_point = static_cast<char32_t>(char_data_[0]);
break; break;
case 2: case 2:
code_point = ((char_data_[0] & 0x1F) << 6) | (char_data_[1] & 0x3F); code_point = ((char_data_[0] & 0x1F) << 6) | (char_data_[1] & 0x3F);

View File

@@ -6,6 +6,7 @@
#include <format> #include <format>
#include <source_location> #include <source_location>
#include <string> #include <string>
#include <vector>
namespace Fig namespace Fig
{ {
@@ -16,8 +17,10 @@ namespace Fig
explicit AddressableError(FString _msg, explicit AddressableError(FString _msg,
size_t _line, size_t _line,
size_t _column, size_t _column,
FString _sourcePath,
std::vector<FString> _sourceLines,
std::source_location loc = std::source_location::current()) : std::source_location loc = std::source_location::current()) :
src_loc(loc), line(_line), column(_column) src_loc(loc), line(_line), column(_column), sourcePath(std::move(_sourcePath)), sourceLines(std::move(_sourceLines))
{ {
message = _msg; message = _msg;
} }
@@ -33,9 +36,11 @@ namespace Fig
} }
std::source_location src_loc; std::source_location src_loc;
size_t getLine() const { return line; } virtual size_t getLine() const { return line; }
size_t getColumn() const { return column; } virtual size_t getColumn() const { return column; }
FString getMessage() const { return message; } FString getMessage() const { return message; }
FString getSourcePath() const { return sourcePath; }
std::vector<FString> getSourceLines() const { return sourceLines; }
virtual FString getErrorType() const virtual FString getErrorType() const
{ {
@@ -45,6 +50,9 @@ namespace Fig
protected: protected:
size_t line, column; size_t line, column;
FString message; FString message;
FString sourcePath;
std::vector<FString> sourceLines;
}; };
class UnaddressableError : public std::exception class UnaddressableError : public std::exception
@@ -84,14 +92,6 @@ namespace Fig
public: public:
using AddressableError::AddressableError; using AddressableError::AddressableError;
explicit SyntaxError(FString _msg,
size_t _line,
size_t _column,
std::source_location loc = std::source_location::current()) :
AddressableError(_msg, _line, _column, loc)
{
}
virtual FString toString() const override virtual FString toString() const override
{ {
std::string msg = std::format("[SyntaxError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name()); std::string msg = std::format("[SyntaxError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name());

View File

@@ -97,9 +97,20 @@ namespace Fig
std::print("{}{}{}", colorCode, msg, TerminalColors::Reset); std::print("{}{}{}", colorCode, msg, TerminalColors::Reset);
} }
inline void logFigErrorInterface(const FString &errorClass, const FString &errorMessage)
inline void logAddressableError(const AddressableError &err, FString fileName, std::vector<FString> sourceLines)
{ {
namespace TC = TerminalColors;
coloredPrint(TC::LightWhite, "Uncaught Fig exception:\n");
coloredPrint(TC::LightRed, "");
coloredPrint(TC::Red, errorClass.toBasicString() + ": ");
coloredPrint(TC::Red, errorMessage.toBasicString() + "\n");
}
inline void logAddressableError(const AddressableError &err)
{
const FString &fileName = err.getSourcePath();
const std::vector<FString> &sourceLines = err.getSourceLines();
std::print("\n"); std::print("\n");
namespace TC = TerminalColors; namespace TC = TerminalColors;
coloredPrint(TC::LightWhite, "An error occurred! "); coloredPrint(TC::LightWhite, "An error occurred! ");
@@ -107,21 +118,30 @@ namespace Fig
coloredPrint(TC::LightRed, ""); coloredPrint(TC::LightRed, "");
coloredPrint(TC::LightRed, std::format("{}: {}\n", err.getErrorType().toBasicString(), FString(err.getMessage()).toBasicString())); coloredPrint(TC::LightRed, std::format("{}: {}\n", err.getErrorType().toBasicString(), FString(err.getMessage()).toBasicString()));
coloredPrint(TC::White, std::format(" at {}:{} in file '{}'\n", err.getLine(), err.getColumn(), fileName.toBasicString())); coloredPrint(TC::White, std::format(" at {}:{} in file '{}'\n", err.getLine(), err.getColumn(), fileName.toBasicString()));
FString lineContent = ((int64_t(err.getLine()) - int64_t(1)) >= 0 ? sourceLines[err.getLine() - 1] : u8"<No Source>");
coloredPrint(TC::LightBlue, std::format(" {}\n", lineContent.toBasicString())); FString lineContent;
FString pointerLine; FString pointerLine;
if (fileName != u8"<stdin>")
{
lineContent = ((int64_t(err.getLine()) - int64_t(1)) >= 0 ? sourceLines[err.getLine() - 1] : u8"<No Source>");
for (size_t i = 1; i < err.getColumn(); ++i) for (size_t i = 1; i < err.getColumn(); ++i)
{ {
if (lineContent[i - 1] == U'\t') if (lineContent[i - 1] == U'\t') { pointerLine += U'\t'; }
{
pointerLine += U'\t';
}
else else
{ {
pointerLine += U' '; pointerLine += U' ';
} }
} }
pointerLine += U'^'; pointerLine += U'^';
}
else
{
lineContent = fileName;
}
coloredPrint(TC::LightBlue, std::format(" {}\n", lineContent.toBasicString()));
coloredPrint(TC::LightGreen, std::format(" {}\n", pointerLine.toBasicString())); coloredPrint(TC::LightGreen, std::format(" {}\n", pointerLine.toBasicString()));
coloredPrint(TC::DarkGray, std::format("🔧 in function '{}' ({}:{})\n", err.src_loc.function_name(), err.src_loc.file_name(), err.src_loc.line())); coloredPrint(TC::DarkGray, std::format("🔧 in function '{}' ({}:{})\n", err.src_loc.function_name(), err.src_loc.file_name(), err.src_loc.line()));
} }

View File

@@ -1,16 +1,22 @@
#pragma once #pragma once
#include "Value/interface.hpp"
#include <Value/Type.hpp>
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <functional>
#include <unordered_map> #include <unordered_map>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <Context/context_forward.hpp> #include <Ast/astBase.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Evaluator/Value/function.hpp>
#include <Evaluator/Value/interface.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Context/context_forward.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/value.hpp>
#include <Value/VariableSlot.hpp> #include <Evaluator/Value/VariableSlot.hpp>
#include <Evaluator/Core/ExprResult.hpp>
namespace Fig namespace Fig
{ {
@@ -23,27 +29,41 @@ namespace Fig
std::unordered_map<FString, Function> implMethods; std::unordered_map<FString, Function> implMethods;
}; };
struct OperationRecord
{
using UnaryOpFn = std::function<ExprResult(const ObjectPtr &)>;
using BinaryOpFn = std::function<ExprResult(const ObjectPtr &, const ObjectPtr &)>;
std::unordered_map<Ast::Operator, UnaryOpFn> unOpRec;
std::unordered_map<Ast::Operator, BinaryOpFn> binOpRec;
bool hasUnaryOp(Ast::Operator op) const { return unOpRec.contains(op); }
bool hasBinaryOp(Ast::Operator op) const { return binOpRec.contains(op); }
const UnaryOpFn &getUnaryOpFn(Ast::Operator op) const { return unOpRec.at(op); }
const BinaryOpFn &getBinaryOpFn(Ast::Operator op) const { return binOpRec.at(op); }
};
class Context : public std::enable_shared_from_this<Context> class Context : public std::enable_shared_from_this<Context>
{ {
private: private:
FString scopeName; FString scopeName;
std::unordered_map<FString, std::shared_ptr<VariableSlot>> variables; std::unordered_map<FString, std::shared_ptr<VariableSlot>> variables;
std::unordered_map<std::size_t, Function> functions; // std::unordered_map<std::size_t, Function> functions;
std::unordered_map<std::size_t, FString> functionNames; // std::unordered_map<std::size_t, FString> functionNames;
// implRegistry <Struct, ordered list of ImplRecord> // implRegistry <Struct, ordered list of ImplRecord>
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
implRegistry; std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> opRegistry;
public: public:
ContextPtr parent; ContextPtr parent;
Context(const Context &) = default; Context(const Context &) = default;
Context(const FString &name, ContextPtr p = nullptr) : Context(const FString &name, ContextPtr p = nullptr) : scopeName(name), parent(p) {}
scopeName(name), parent(p)
{
}
void setParent(ContextPtr _parent) { parent = _parent; } void setParent(ContextPtr _parent) { parent = _parent; }
@@ -54,17 +74,31 @@ namespace Fig
void merge(const Context &c) void merge(const Context &c)
{ {
variables.insert(c.variables.begin(), c.variables.end()); variables.insert(c.variables.begin(), c.variables.end());
functions.insert(c.functions.begin(), c.functions.end());
functionNames.insert(c.functionNames.begin(),
c.functionNames.end());
implRegistry.insert(c.implRegistry.begin(), c.implRegistry.end()); implRegistry.insert(c.implRegistry.begin(), c.implRegistry.end());
opRegistry.insert(c.opRegistry.begin(), c.opRegistry.end());
// structTypeNames.insert(c.structTypeNames.begin(), // structTypeNames.insert(c.structTypeNames.begin(),
// c.structTypeNames.end()); // c.structTypeNames.end());
} }
void clear()
{
variables.clear();
implRegistry.clear();
opRegistry.clear();
}
std::unordered_map<size_t, Function> getFunctions() const std::unordered_map<size_t, Function> getFunctions() const
{ {
return functions; std::unordered_map<size_t, Function> result;
for (auto &[name, slot] : variables)
{
if (slot->declaredType == ValueType::Function)
{
const Function &fn = slot->value->as<Function>();
result[fn.id] = fn;
}
}
return result;
} }
std::shared_ptr<VariableSlot> get(const FString &name) std::shared_ptr<VariableSlot> get(const FString &name)
@@ -72,20 +106,15 @@ namespace Fig
auto it = variables.find(name); auto it = variables.find(name);
if (it != variables.end()) return it->second; if (it != variables.end()) return it->second;
if (parent) return parent->get(name); if (parent) return parent->get(name);
throw RuntimeError(FString(std::format("Variable '{}' not defined", throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
name.toBasicString())));
} }
AccessModifier getAccessModifier(const FString &name) AccessModifier getAccessModifier(const FString &name)
{ {
if (variables.contains(name)) { return variables[name]->am; } if (variables.contains(name)) { return variables[name]->am; }
else if (parent != nullptr) else if (parent != nullptr) { return parent->getAccessModifier(name); }
{
return parent->getAccessModifier(name);
}
else else
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
"Variable '{}' not defined", name.toBasicString())));
} }
} }
bool isVariableMutable(const FString &name) bool isVariableMutable(const FString &name)
@@ -104,16 +133,14 @@ namespace Fig
{ {
if (!isVariableMutable(name)) if (!isVariableMutable(name))
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("Variable '{}' is immutable", name.toBasicString())));
"Variable '{}' is immutable", name.toBasicString())));
} }
variables[name]->value = value; variables[name]->value = value;
} }
else if (parent != nullptr) { parent->set(name, value); } else if (parent != nullptr) { parent->set(name, value); }
else else
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
"Variable '{}' not defined", name.toBasicString())));
} }
} }
void _update(const FString &name, ObjectPtr value) void _update(const FString &name, ObjectPtr value)
@@ -122,8 +149,7 @@ namespace Fig
else if (parent != nullptr) { parent->_update(name, value); } else if (parent != nullptr) { parent->_update(name, value); }
else else
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
"Variable '{}' not defined", name.toBasicString())));
} }
} }
void def(const FString &name, void def(const FString &name,
@@ -133,38 +159,38 @@ namespace Fig
{ {
if (containsInThisScope(name)) if (containsInThisScope(name))
{ {
throw RuntimeError(FString( throw RuntimeError(
std::format("Variable '{}' already defined in this scope", FString(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
name.toBasicString())));
}
variables[name] =
std::make_shared<VariableSlot>(name, value, ti, am);
if (ti == ValueType::Function
and value->getTypeInfo() == ValueType::Function)
{
auto &fn = value->as<Function>();
functions[fn.id] = fn;
functionNames[fn.id] = name;
} }
variables[name] = std::make_shared<VariableSlot>(name, value, ti, am);
// if (ti == ValueType::StructType) // if (ti == ValueType::StructType)
// { // {
// auto &st = value->as<StructType>(); // auto &st = value->as<StructType>();
// structTypeNames[st.id] = name; // structTypeNames[st.id] = name;
// } // }
} }
std::optional<Function> getFunction(std::size_t id) void
defReference(const FString &name, const TypeInfo &ti, AccessModifier am, std::shared_ptr<VariableSlot> target)
{ {
auto it = functions.find(id); if (containsInThisScope(name))
if (it != functions.end()) { return it->second; } {
else if (parent) { return parent->getFunction(id); } throw RuntimeError(
else { return std::nullopt; } FString(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
} }
variables[name] = std::make_shared<VariableSlot>(name, target->value, ti, am, true, target);
}
std::optional<FString> getFunctionName(std::size_t id) std::optional<FString> getFunctionName(std::size_t id)
{ {
auto it = functionNames.find(id); for (auto &[name, slot] : variables)
if (it != functionNames.end()) { return it->second; } {
else if (parent) { return parent->getFunctionName(id); } if (slot->declaredType == ValueType::Function)
else { return std::nullopt; } {
const Function &fn = slot->value->as<Function>();
if (fn.id == id) { return name; }
}
}
return std::nullopt;
} }
// std::optional<FString> getStructName(std::size_t id) // std::optional<FString> getStructName(std::size_t id)
// { // {
@@ -188,24 +214,15 @@ namespace Fig
else if (parent != nullptr) { return parent->contains(name); } else if (parent != nullptr) { return parent->contains(name); }
return false; return false;
} }
bool containsInThisScope(const FString &name) const bool containsInThisScope(const FString &name) const { return variables.contains(name); }
{
return variables.contains(name);
}
TypeInfo getTypeInfo(const FString &name) TypeInfo getTypeInfo(const FString &name) { return get(name)->declaredType; }
{
return get(name)->declaredType;
}
bool isInFunctionContext() bool isInFunctionContext()
{ {
ContextPtr ctx = shared_from_this(); ContextPtr ctx = shared_from_this();
while (ctx) while (ctx)
{ {
if (ctx->getScopeName().find(u8"<Function ") == 0) if (ctx->getScopeName().find(u8"<Function ") == 0) { return true; }
{
return true;
}
ctx = ctx->parent; ctx = ctx->parent;
} }
return false; return false;
@@ -215,8 +232,7 @@ namespace Fig
ContextPtr ctx = shared_from_this(); ContextPtr ctx = shared_from_this();
while (ctx) while (ctx)
{ {
if (ctx->getScopeName().find(u8"<While ") == 0 if (ctx->getScopeName().find(u8"<While ") == 0 or ctx->getScopeName().find(u8"<For ") == 0)
or ctx->getScopeName().find(u8"<For ") == 0)
{ {
return true; return true;
} }
@@ -225,8 +241,7 @@ namespace Fig
return false; return false;
} }
bool hasImplRegisted(const TypeInfo &structType, bool hasImplRegisted(const TypeInfo &structType, const TypeInfo &interfaceType) const
const TypeInfo &interfaceType) const
{ {
auto it = implRegistry.find(structType); auto it = implRegistry.find(structType);
if (it != implRegistry.end()) if (it != implRegistry.end())
@@ -239,9 +254,14 @@ namespace Fig
return parent && parent->hasImplRegisted(structType, interfaceType); return parent && parent->hasImplRegisted(structType, interfaceType);
} }
std::optional<ImplRecord> std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> getImplRegistry() const
getImplRecord(const TypeInfo &structType, {
const TypeInfo &interfaceType) const return implRegistry;
}
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> &getImplRegistry() { return implRegistry; }
std::optional<ImplRecord> getImplRecord(const TypeInfo &structType, const TypeInfo &interfaceType) const
{ {
auto it = implRegistry.find(structType); auto it = implRegistry.find(structType);
if (it != implRegistry.end()) if (it != implRegistry.end())
@@ -257,9 +277,7 @@ namespace Fig
return std::nullopt; return std::nullopt;
} }
void setImplRecord(const TypeInfo &structType, void setImplRecord(const TypeInfo &structType, const TypeInfo &interfaceType, const ImplRecord &record)
const TypeInfo &interfaceType,
const ImplRecord &record)
{ {
auto &list = implRegistry[structType]; auto &list = implRegistry[structType];
@@ -271,8 +289,7 @@ namespace Fig
list.push_back(record); // order is the level list.push_back(record); // order is the level
} }
bool hasMethodImplemented(const TypeInfo &structType, bool hasMethodImplemented(const TypeInfo &structType, const FString &functionName) const
const FString &functionName) const
{ {
auto it = implRegistry.find(structType); auto it = implRegistry.find(structType);
if (it != implRegistry.end()) if (it != implRegistry.end())
@@ -283,19 +300,16 @@ namespace Fig
} }
} }
return parent return parent && parent->hasMethodImplemented(structType, functionName);
&& parent->hasMethodImplemented(structType, functionName);
} }
bool hasDefaultImplementedMethod(const TypeInfo &structType, bool hasDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) const
const FString &functionName) const
{ {
auto it = implRegistry.find(structType); auto it = implRegistry.find(structType);
if (it == implRegistry.end()) return false; if (it == implRegistry.end()) return false;
std::vector<TypeInfo> implementedInterfaces; std::vector<TypeInfo> implementedInterfaces;
for (auto &record : it->second) for (auto &record : it->second) implementedInterfaces.push_back(record.interfaceType);
implementedInterfaces.push_back(record.interfaceType);
for (auto &[_, slot] : variables) for (auto &[_, slot] : variables)
{ {
@@ -303,8 +317,7 @@ namespace Fig
InterfaceType &interface = slot->value->as<InterfaceType>(); InterfaceType &interface = slot->value->as<InterfaceType>();
bool implemented = std::any_of( bool implemented = std::any_of(implementedInterfaces.begin(),
implementedInterfaces.begin(),
implementedInterfaces.end(), implementedInterfaces.end(),
[&](const TypeInfo &ti) { return ti == interface.type; }); [&](const TypeInfo &ti) { return ti == interface.type; });
@@ -312,16 +325,14 @@ namespace Fig
for (auto &method : interface.methods) for (auto &method : interface.methods)
{ {
if (method.name == functionName && method.hasDefaultBody()) if (method.name == functionName && method.hasDefaultBody()) return true;
return true;
} }
} }
return false; return false;
} }
Function getDefaultImplementedMethod(const TypeInfo &structType, Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName)
const FString &functionName)
{ {
// O(N²) // O(N²)
// SLOW // SLOW
@@ -335,8 +346,7 @@ namespace Fig
if (it == implRegistry.end()) assert(false); if (it == implRegistry.end()) assert(false);
std::vector<TypeInfo> implementedInterfaces; std::vector<TypeInfo> implementedInterfaces;
for (auto &record : it->second) for (auto &record : it->second) implementedInterfaces.push_back(record.interfaceType);
implementedInterfaces.push_back(record.interfaceType);
for (auto &[_, slot] : variables) for (auto &[_, slot] : variables)
{ {
@@ -344,8 +354,7 @@ namespace Fig
InterfaceType &interface = slot->value->as<InterfaceType>(); InterfaceType &interface = slot->value->as<InterfaceType>();
bool implemented = std::any_of( bool implemented = std::any_of(implementedInterfaces.begin(),
implementedInterfaces.begin(),
implementedInterfaces.end(), implementedInterfaces.end(),
[&](const TypeInfo &ti) { return ti == interface.type; }); [&](const TypeInfo &ti) { return ti == interface.type; });
@@ -356,21 +365,15 @@ namespace Fig
if (method.name == functionName) if (method.name == functionName)
{ {
if (!method.hasDefaultBody()) assert(false); if (!method.hasDefaultBody()) assert(false);
return method;
return Function(method.paras,
TypeInfo(method.returnType),
method.defaultBody,
shared_from_this());
} }
} }
} }
assert(false); assert(false);
return Function(); // ignore warning
} }
const Function &getImplementedMethod(const TypeInfo &structType, const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const
const FString &functionName) const
{ {
auto it = implRegistry.find(structType); auto it = implRegistry.find(structType);
if (it != implRegistry.end()) if (it != implRegistry.end())
@@ -382,13 +385,80 @@ namespace Fig
} }
} }
if (parent) if (parent) return parent->getImplementedMethod(structType, functionName);
return parent->getImplementedMethod(structType, functionName);
assert(false); // not found assert(false); // not found
throw ""; // ignore warning throw ""; // ignore warning
} }
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> &getOpRegistry() { return opRegistry; }
bool hasOperatorImplemented(const TypeInfo &type, Ast::Operator op, bool isUnary = false) const
{
auto it = opRegistry.find(type);
if (it != opRegistry.end())
{
const OperationRecord &rec = it->second;
if (isUnary)
return rec.hasUnaryOp(op);
else
return rec.hasBinaryOp(op);
}
if (parent) return parent->hasOperatorImplemented(type, op, isUnary);
return false;
}
std::optional<OperationRecord::UnaryOpFn> getUnaryOperatorFn(const TypeInfo &type, Ast::Operator op) const
{
auto it = opRegistry.find(type);
if (it != opRegistry.end())
{
const OperationRecord &rec = it->second;
if (rec.hasUnaryOp(op)) return rec.getUnaryOpFn(op);
}
if (parent) return parent->getUnaryOperatorFn(type, op);
return std::nullopt;
}
std::optional<OperationRecord::BinaryOpFn> getBinaryOperatorFn(const TypeInfo &type, Ast::Operator op) const
{
auto it = opRegistry.find(type);
if (it != opRegistry.end())
{
const OperationRecord &rec = it->second;
if (rec.hasBinaryOp(op)) return rec.getBinaryOpFn(op);
}
if (parent) return parent->getBinaryOperatorFn(type, op);
return std::nullopt;
}
void registerUnaryOperator(const TypeInfo &type, Ast::Operator op, OperationRecord::UnaryOpFn fn)
{
opRegistry[type].unOpRec[op] = std::move(fn);
}
void registerBinaryOperator(const TypeInfo &type, Ast::Operator op, OperationRecord::BinaryOpFn fn)
{
opRegistry[type].binOpRec[op] = std::move(fn);
}
bool removeUnaryOperator(const TypeInfo &type, Ast::Operator op)
{
auto it = opRegistry.find(type);
if (it != opRegistry.end()) return it->second.unOpRec.erase(op) > 0;
return false;
}
bool removeBinaryOperator(const TypeInfo &type, Ast::Operator op)
{
auto it = opRegistry.find(type);
if (it != opRegistry.end()) return it->second.binOpRec.erase(op) > 0;
return false;
}
void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const
{ {
const Context *ctx = this; const Context *ctx = this;
@@ -403,8 +473,7 @@ namespace Fig
os << "[STACK TRACE]\n"; os << "[STACK TRACE]\n";
for (int i = static_cast<int>(chain.size()) - 1; i >= 0; --i) for (int i = static_cast<int>(chain.size()) - 1; i >= 0; --i)
{ {
os << " #" << (chain.size() - 1 - i) << " " os << " #" << (chain.size() - 1 - i) << " " << chain[i]->scopeName.toBasicString() << "\n";
<< chain[i]->scopeName.toBasicString() << "\n";
} }
} }
}; };

107
src/Evaluator/Core/Eval.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Evaluator/Core/ExprResult.hpp>
namespace Fig
{
ExprResult Evaluator::eval(Ast::Expression exp, ContextPtr ctx)
{
using Ast::AstType;
AstType type = exp->getType();
switch (type)
{
case AstType::ValueExpr: {
auto val = std::static_pointer_cast<Ast::ValueExprAst>(exp);
return val->val;
}
case AstType::VarExpr: {
auto varExpr = std::static_pointer_cast<Ast::VarExprAst>(exp);
return check_unwrap_lv(evalVarExpr(varExpr, ctx)).get(); // LvObject -> RvObject
}
case AstType::BinaryExpr: {
auto bin = std::static_pointer_cast<Ast::BinaryExprAst>(exp);
return evalBinary(bin, ctx);
}
case AstType::UnaryExpr: {
auto un = std::static_pointer_cast<Ast::UnaryExprAst>(exp);
return evalUnary(un, ctx);
}
case AstType::TernaryExpr: {
auto te = std::static_pointer_cast<Ast::TernaryExprAst>(exp);
return evalTernary(te, ctx);
}
case AstType::MemberExpr:
case AstType::IndexExpr: return check_unwrap_lv(evalLv(exp, ctx)).get();
case AstType::FunctionCall: {
auto fnCall = std::static_pointer_cast<Ast::FunctionCallExpr>(exp);
return evalFunctionCall(fnCall, ctx);
}
case AstType::FunctionLiteralExpr: {
auto fnLiteral = std::static_pointer_cast<Ast::FunctionLiteralExprAst>(exp);
Ast::BlockStatement body = nullptr;
if (fnLiteral->isExprMode())
{
Ast::Expression exprBody = fnLiteral->getExprBody();
const Ast::AstAddressInfo &aai = exprBody->getAAI();
Ast::Return st = std::make_shared<Ast::ReturnSt>(exprBody);
st->setAAI(aai);
body = std::make_shared<Ast::BlockStatementAst>();
body->stmts.push_back(st); // convert to Ast::Statement
body->setAAI(aai);
}
else
{
body = fnLiteral->getBlockBody();
}
Function fn(FString(std::format("<LambdaFn>")),fnLiteral->paras, ValueType::Any, body, ctx
/*
pass the ctx(fnLiteral eval context) as closure context
*/
);
return std::make_shared<Object>(std::move(fn));
}
case AstType::InitExpr: {
auto initExpr = std::static_pointer_cast<Ast::InitExprAst>(exp);
return evalInitExpr(initExpr, ctx);
}
case AstType::ListExpr: {
auto lstExpr = std::static_pointer_cast<Ast::ListExprAst>(exp);
List list;
for (auto &exp : lstExpr->val) { list.push_back(check_unwrap(eval(exp, ctx))); }
return std::make_shared<Object>(std::move(list));
}
case AstType::MapExpr: {
auto mapExpr = std::static_pointer_cast<Ast::MapExprAst>(exp);
Map map;
for (auto &[key, value] : mapExpr->val) {
map[check_unwrap(eval(key, ctx))] = check_unwrap(eval(value, ctx));
}
return std::make_shared<Object>(std::move(map));
}
default: {
throw RuntimeError(FString(std::format("err type of expr: {}", magic_enum::enum_name(type))));
}
}
return Object::getNullInstance(); // ignore warning
}
};

View File

@@ -0,0 +1,437 @@
#include <Ast/Expressions/BinaryExpr.hpp>
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/Value/IntPool.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Evaluator/Core/ExprResult.hpp>
#include <exception>
#include <memory>
#include <functional>
namespace Fig
{
ExprResult Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx)
{
using Ast::Operator;
Operator op = bin->op;
Ast::Expression lexp = bin->lexp, rexp = bin->rexp;
const auto &tryInvokeOverloadFn =
[ctx, op](const ObjectPtr &lhs, const ObjectPtr &rhs, const std::function<ExprResult()> &rollback) {
if (lhs->is<StructInstance>() && lhs->getTypeInfo() == rhs->getTypeInfo())
{
// 运算符重载
const TypeInfo &type = actualType(lhs);
if (ctx->hasOperatorImplemented(type, op))
{
const auto &fnOpt = ctx->getBinaryOperatorFn(type, op);
return (*fnOpt)(lhs, rhs);
}
}
return rollback();
};
switch (op)
{
case Operator::Add: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() + rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs + *rhs);
});
}
case Operator::Subtract: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() - rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs - *rhs);
});
}
case Operator::Multiply: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() * rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs * *rhs);
});
}
case Operator::Divide: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs / *rhs); });
}
case Operator::Modulo: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs->as<ValueType::IntClass>();
ValueType::IntClass rv = rhs->as<ValueType::IntClass>();
if (rv == 0) { throw ValueError(FString(std::format("Modulo by zero: {} % {}", lv, rv))); }
ValueType::IntClass result = lv / rv;
ValueType::IntClass r = lv % rv;
if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; }
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(*lhs % *rhs);
});
}
case Operator::Is: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs, ctx, bin]() {
const TypeInfo &lhsType = lhs->getTypeInfo();
const TypeInfo &rhsType = rhs->getTypeInfo();
if (lhs->is<StructInstance>() && rhs->is<StructType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const StructType &st = rhs->as<StructType>();
return std::make_shared<Object>(si.parentType == st.type);
}
if (lhs->is<StructInstance>() && rhs->is<InterfaceType>())
{
const StructInstance &si = lhs->as<StructInstance>();
const InterfaceType &it = rhs->as<InterfaceType>();
return std::make_shared<Object>(implements(si.parentType, it.type, ctx));
}
if (ValueType::isTypeBuiltin(lhsType) && rhsType == ValueType::StructType)
{
const StructType &st = rhs->as<StructType>();
const TypeInfo &type = st.type;
/*
如果是内置类型(e.g. Int, String)
那么 eval出来String这个字出来的是StructType
而出来的StructType.type就不会是一个独立的TypeInfo,而是内置的ValueType::String
依次我们可以判断内置类型
e.g:
"123" is String
L OP R
其中 L 类型为 String
而 R 类型为 StructType (builtins.hpp) 中注册
拿到 R 的 StructType, 其中的 type 为 String
*/
if (lhs->getTypeInfo() == type) { return Object::getTrueInstance(); }
return Object::getFalseInstance();
}
throw EvaluatorError(u8"TypeError",
std::format("Unsupported operator `is` for '{}' && '{}'",
prettyType(lhs).toBasicString(),
prettyType(rhs).toBasicString()),
bin->lexp);
});
}
case Operator::As: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs, ctx, bin, this]() -> ExprResult {
if (!rhs->is<StructType>())
{
throw EvaluatorError(
u8"OperatorError",
std::format("Operator `as` requires right hand side operand a struct type, but got '{}'",
prettyType(rhs).toBasicString()),
bin->rexp);
}
const StructType &targetStructType = rhs->as<StructType>();
const TypeInfo &targetType = targetStructType.type;
const TypeInfo &sourceType = lhs->getTypeInfo();
if (targetType == sourceType) { return lhs; }
if (targetType == ValueType::String) { return std::make_shared<Object>(lhs->toStringIO()); }
if (sourceType == ValueType::Int)
{
if (targetType == ValueType::Double)
{
return std::make_shared<Object>(
static_cast<ValueType::DoubleClass>(lhs->as<ValueType::IntClass>()));
}
}
else if (sourceType == ValueType::Double)
{
if (targetType == ValueType::Int)
{
return IntPool::getInstance().createInt(
static_cast<ValueType::IntClass>(lhs->as<ValueType::DoubleClass>()));
}
}
else if (sourceType == ValueType::String)
{
const FString &str = lhs->as<ValueType::StringClass>();
if (targetType == ValueType::Int)
{
try
{
return IntPool::getInstance().createInt(
static_cast<ValueType::IntClass>(std::stoll(str.toBasicString())));
}
catch (std::exception &e)
{
return ExprResult::error(
genTypeError(FString(std::format("Cannot cast type `{}` to `{}`, bad int string {}",
prettyType(lhs).toBasicString(),
prettyType(rhs).toBasicString(),
str.toBasicString())),
bin->rexp,
ctx));
}
}
if (targetType == ValueType::Double)
{
try
{
return std::make_shared<Object>(std::stod(str.toBasicString()));
}
catch (std::exception &e)
{
return ExprResult::error(genTypeError(
FString(std::format("Cannot cast type `{}` to `{}`, bad double string {}",
prettyType(lhs).toBasicString(),
prettyType(rhs).toBasicString(),
str.toBasicString())),
bin->rexp,
ctx));
}
}
if (targetType == ValueType::Bool)
{
if (str == u8"true") { return Object::getTrueInstance(); }
else if (str == u8"false") { return Object::getFalseInstance(); }
return ExprResult::error(
genTypeError(FString(std::format("Cannot cast type `{}` to `{}`, bad bool string {}",
prettyType(lhs).toBasicString(),
prettyType(rhs).toBasicString(),
str.toBasicString())),
bin->rexp,
ctx));
}
}
else if (sourceType == ValueType::Bool)
{
if (targetType == ValueType::Int)
{
return IntPool::getInstance().createInt(static_cast<ValueType::IntClass>(lhs->as<ValueType::BoolClass>()));
}
if (targetType == ValueType::Double)
{
return std::make_shared<Object>(static_cast<ValueType::DoubleClass>(lhs->as<ValueType::BoolClass>()));
}
}
return ExprResult::error(genTypeError(FString(std::format("Cannot cast type `{}` to `{}`",
prettyType(lhs).toBasicString(),
prettyType(rhs).toBasicString())),
bin->rexp,
ctx));
});
}
case Operator::BitAnd: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() & rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_and(*lhs, *rhs));
});
}
case Operator::BitOr: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() | rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_or(*lhs, *rhs));
});
}
case Operator::BitXor: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() ^ rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(bit_xor(*lhs, *rhs));
});
}
case Operator::ShiftLeft: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() << rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_left(*lhs, *rhs));
});
}
case Operator::ShiftRight: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() {
if (lhs->is<ValueType::IntClass>() && rhs->is<ValueType::IntClass>())
{
ValueType::IntClass result = lhs->as<ValueType::IntClass>() >> rhs->as<ValueType::IntClass>();
return IntPool::getInstance().createInt(result);
}
return std::make_shared<Object>(shift_right(*lhs, *rhs));
});
}
case Operator::Assign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
lv.set(rhs);
return rhs;
}
case Operator::And: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
if (lhs->is<bool>() && !isBoolObjectTruthy(lhs)) { return Object::getFalseInstance(); }
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs && *rhs); });
}
case Operator::Or: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
if (lhs->is<bool>() && isBoolObjectTruthy(lhs)) { return Object::getTrueInstance(); }
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs || *rhs); });
}
case Operator::Equal: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs == *rhs); });
}
case Operator::NotEqual: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs != *rhs); });
}
case Operator::Less: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs < *rhs); });
}
case Operator::LessEqual: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs <= *rhs); });
}
case Operator::Greater: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs > *rhs); });
}
case Operator::GreaterEqual: {
ObjectPtr lhs = check_unwrap(eval(lexp, ctx));
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
return tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs >= *rhs); });
}
case Operator::PlusAssign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
const ObjectPtr &lhs = lv.get();
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
const ObjectPtr &result = check_unwrap(
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs + *rhs); }));
lv.set(result);
return rhs;
}
case Operator::MinusAssign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
const ObjectPtr &lhs = lv.get();
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
const ObjectPtr &result = check_unwrap(
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs - *rhs); }));
lv.set(result);
return rhs;
}
case Operator::AsteriskAssign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
const ObjectPtr &lhs = lv.get();
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
const ObjectPtr &result = check_unwrap(
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs * *rhs); }));
lv.set(result);
return rhs;
}
case Operator::SlashAssign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
const ObjectPtr &lhs = lv.get();
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
const ObjectPtr &result = check_unwrap(
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs / *rhs); }));
lv.set(result);
return rhs;
}
case Operator::PercentAssign: {
LvObject lv = check_unwrap_lv(evalLv(lexp, ctx));
const ObjectPtr &lhs = lv.get();
ObjectPtr rhs = check_unwrap(eval(rexp, ctx));
const ObjectPtr &result = check_unwrap(
tryInvokeOverloadFn(lhs, rhs, [lhs, rhs]() { return std::make_shared<Object>(*lhs % *rhs); }));
lv.set(result);
return rhs;
}
default:
throw EvaluatorError(u8"UnsupportedOp",
std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)),
bin);
}
}
}; // namespace Fig

View File

@@ -0,0 +1,206 @@
#include <Ast/functionParameters.hpp>
#include <Evaluator/Value/value.hpp>
#include <Ast/Expressions/FunctionCall.hpp>
#include <Evaluator/Value/function.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Evaluator/Core/ExprResult.hpp>
namespace Fig
{
ExprResult Evaluator::executeFunction(const Function &fn,
const Ast::FunctionCallArgs &args,
ContextPtr fnCtx) // new context for fn, already filled paras
{
// const FString &fnName = fn.name;
if (fn.type == Function::Builtin || fn.type == Function::MemberType)
{
if (fn.type == Function::Builtin) { return fn.builtin(args.argv); }
else
{
return fn.mtFn(nullptr,
args.argv); // wrapped member type function (`this` provided by evalMemberExpr)
}
}
// else: normal fn, args is needless
for (const auto &stmt : fn.body->stmts)
{
StatementResult sr = evalStatement(stmt, fnCtx);
if (sr.isError())
{
handle_error(sr, stmt, fnCtx);
}
if (!sr.isNormal())
{
return sr.result;
}
}
return Object::getNullInstance();
}
ExprResult Evaluator::evalFunctionCall(const Ast::FunctionCall &call, ContextPtr ctx)
{
RvObject fnObj = check_unwrap(eval(call->callee, ctx));
if (fnObj->getTypeInfo() != ValueType::Function)
{
throw EvaluatorError(u8"ObjectNotCallable",
std::format("Object `{}` isn't callable", fnObj->toString().toBasicString()),
call->callee);
}
const Function &fn = fnObj->as<Function>();
const FString &fnName = fn.name;
const Ast::FunctionArguments &fnArgs = call->arg;
Ast::FunctionCallArgs evaluatedArgs;
if (fn.type == Function::Builtin || fn.type == Function::MemberType)
{
for (const auto &argExpr : fnArgs.argv) { evaluatedArgs.argv.push_back(check_unwrap(eval(argExpr, ctx))); }
if (fn.builtinParamCount != -1 && fn.builtinParamCount != evaluatedArgs.getLength())
{
throw EvaluatorError(u8"BuiltinArgumentMismatchError",
std::format("Builtin function '{}' expects {} arguments, but {} were provided",
fnName.toBasicString(),
fn.builtinParamCount,
evaluatedArgs.getLength()),
(fnArgs.getLength() > 0 ? fnArgs.argv.back() : call));
}
return executeFunction(fn, evaluatedArgs, nullptr);
}
// check argument, all types of parameters
Ast::FunctionParameters fnParas = fn.paras;
// create new context for function call
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())),
fn.closureContext);
if (fnParas.variadic)
goto VariadicFilling;
else
goto NormalFilling;
NormalFilling: {
if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
{
throw RuntimeError(FString(std::format("Function '{}' expects {} to {} arguments, but {} were provided",
fnName.toBasicString(),
fnParas.posParas.size(),
fnParas.size(),
fnArgs.getLength())));
}
// positional parameters type check
size_t i;
for (i = 0; i < fnParas.posParas.size(); i++)
{
const TypeInfo &expectedType = actualType(check_unwrap(eval(fnParas.posParas[i].second, fn.closureContext))); // look up type info, if exists a type
// with the name, use it, else throw
ObjectPtr argVal = check_unwrap(eval(fnArgs.argv[i], ctx));
TypeInfo actualType = argVal->getTypeInfo();
if (!isTypeMatch(expectedType, argVal, fn.closureContext))
{
throw EvaluatorError(u8"ArgumentTypeMismatchError",
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
fnName.toBasicString(),
fnParas.posParas[i].first.toBasicString(),
expectedType.toString().toBasicString(),
actualType.toString().toBasicString()),
fnArgs.argv[i]);
}
evaluatedArgs.argv.push_back(argVal);
}
// default parameters type check
for (; i < fnArgs.getLength(); i++)
{
size_t defParamIndex = i - fnParas.posParas.size();
const TypeInfo &expectedType =
actualType(check_unwrap(eval(fnParas.defParas[defParamIndex].second.first, fn.closureContext)));
ObjectPtr defaultVal = check_unwrap(eval(fnParas.defParas[defParamIndex].second.second, fn.closureContext));
if (!isTypeMatch(expectedType, defaultVal, fn.closureContext))
{
throw EvaluatorError(
u8"DefaultParameterTypeError",
std::format(
"In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'",
fnName.toBasicString(),
fnParas.defParas[defParamIndex].first.toBasicString(),
prettyType(defaultVal).toBasicString(),
expectedType.toString().toBasicString()),
fnArgs.argv[i]);
}
ObjectPtr argVal = check_unwrap(eval(fnArgs.argv[i], ctx));
TypeInfo actualType = argVal->getTypeInfo();
if (!isTypeMatch(expectedType, argVal, fn.closureContext))
{
throw EvaluatorError(u8"ArgumentTypeMismatchError",
std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'",
fnName.toBasicString(),
fnParas.defParas[defParamIndex].first.toBasicString(),
expectedType.toString().toBasicString(),
actualType.toString().toBasicString()),
fnArgs.argv[i]);
}
evaluatedArgs.argv.push_back(argVal);
}
// default parameters filling
for (; i < fnParas.size(); i++)
{
size_t defParamIndex = i - fnParas.posParas.size();
ObjectPtr defaultVal = check_unwrap(eval(fnParas.defParas[defParamIndex].second.second, fn.closureContext));
evaluatedArgs.argv.push_back(defaultVal);
}
// define parameters in new context
for (size_t j = 0; j < fnParas.size(); j++)
{
FString paramName;
TypeInfo paramType;
if (j < fnParas.posParas.size())
{
paramName = fnParas.posParas[j].first;
paramType = actualType(check_unwrap(eval(fnParas.posParas[j].second, fn.closureContext)));
}
else
{
size_t defParamIndex = j - fnParas.posParas.size();
paramName = fnParas.defParas[defParamIndex].first;
paramType =
actualType(check_unwrap(eval(fnParas.defParas[defParamIndex].second.first, fn.closureContext)));
}
AccessModifier argAm = AccessModifier::Normal;
newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
}
goto ExecuteBody;
}
VariadicFilling: {
List list;
for (auto &exp : fnArgs.argv)
{
list.push_back(check_unwrap(eval(exp, ctx))); // eval arguments in current scope
}
newContext->def(fnParas.variadicPara, ValueType::List, AccessModifier::Normal, std::make_shared<Object>(list));
goto ExecuteBody;
}
ExecuteBody: {
// execute function body
ObjectPtr retVal = check_unwrap(executeFunction(fn, evaluatedArgs, newContext));
if (!isTypeMatch(fn.retType, retVal, ctx))
{
throw EvaluatorError(u8"ReturnTypeMismatchError",
std::format("Function '{}' expects return type '{}', but got type '{}'",
fnName.toBasicString(),
fn.retType.toString().toBasicString(),
prettyType(retVal).toBasicString()),
fn.body);
}
return retVal;
}
}
}; // namespace Fig

View File

@@ -0,0 +1,363 @@
#include <Evaluator/Core/ExprResult.hpp>
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
namespace Fig
{
ExprResult Evaluator::evalInitExpr(Ast::InitExpr initExpr, ContextPtr ctx)
{
LvObject structeLv = check_unwrap_lv(evalLv(initExpr->structe, ctx));
ObjectPtr structTypeVal = structeLv.get();
const FString &structName = structeLv.name();
if (!structTypeVal->is<StructType>())
{
throw EvaluatorError(u8"NotAStructTypeError",
std::format("'{}' is not a structure type", structName.toBasicString()),
initExpr);
}
const StructType &structT = structTypeVal->as<StructType>();
if (structT.builtin)
{
const TypeInfo &type = structT.type;
auto &args = initExpr->args;
size_t argSize = args.size();
if (argSize > 1)
{
throw EvaluatorError(u8"StructInitArgumentMismatchError",
std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided",
type.toString().toBasicString(),
argSize),
initExpr);
}
// default value
if (argSize == 0)
{
if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function)
{
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
return std::make_shared<Object>(Object::defaultValue(type));
}
ObjectPtr val = check_unwrap(eval(args[0].second, ctx));
auto err = [&](const char *msg) {
throw EvaluatorError(u8"BuiltinInitTypeMismatchError",
std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg),
initExpr);
};
// ===================== Int =====================
if (type == ValueType::Int)
{
if (!val->is<ValueType::IntClass>()) err("expects Int");
return std::make_shared<Object>(val->as<ValueType::IntClass>());
}
// ===================== Double =====================
if (type == ValueType::Double)
{
if (!val->is<ValueType::DoubleClass>()) err("expects Double");
return std::make_shared<Object>(val->as<ValueType::DoubleClass>());
}
// ===================== Bool =====================
if (type == ValueType::Bool)
{
if (!val->is<ValueType::BoolClass>()) err("expects Bool");
return std::make_shared<Object>(val->as<ValueType::BoolClass>());
}
// ===================== String =====================
if (type == ValueType::String)
{
if (!val->is<ValueType::StringClass>()) err("expects String");
return std::make_shared<Object>(val->as<ValueType::StringClass>());
}
// ===================== Null =====================
if (type == ValueType::Null)
{
// Null basically ignores input but keep invariant strict:
if (!val->is<ValueType::NullClass>()) err("expects Null");
return Object::getNullInstance();
}
// ===================== List =====================
if (type == ValueType::List)
{
if (!val->is<List>()) err("expects List");
const auto &src = val->as<List>();
auto copied = std::make_shared<Object>(List{});
auto &dst = copied->as<List>();
dst.reserve(src.size());
for (auto &e : src) dst.push_back(e); // shallow element copy, but new container
return copied;
}
// ===================== Map =====================
if (type == ValueType::Map)
{
if (!val->is<Map>()) err("expects Map");
const auto &src = val->as<Map>();
auto copied = std::make_shared<Object>(Map{});
auto &dst = copied->as<Map>();
for (auto &[k, v] : src) dst.emplace(k, v);
return copied;
}
throw EvaluatorError(
u8"BuiltinNotConstructibleError",
std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()),
initExpr);
}
ContextPtr defContext = structT.defContext; // definition context
// check init args
size_t minArgs = 0;
size_t maxArgs = structT.fields.size();
for (auto &f : structT.fields)
{
if (f.defaultValue == nullptr) minArgs++;
}
size_t got = initExpr->args.size();
if (got > maxArgs || got < minArgs)
{
throw EvaluatorError(u8"StructInitArgumentMismatchError",
std::format("Structure '{}' expects {} to {} fields, but {} were provided",
structName.toBasicString(),
minArgs,
maxArgs,
initExpr->args.size()),
initExpr);
}
std::vector<std::pair<FString, ObjectPtr>> evaluatedArgs;
auto evalArguments = [&evaluatedArgs, initExpr, ctx, this](){
for (const auto &[argName, argExpr] : initExpr->args)
{
evaluatedArgs.push_back({argName, check_unwrap(eval(argExpr, ctx))});
}
return ExprResult::normal(Object::getNullInstance());
};
ContextPtr instanceCtx =
std::make_shared<Context>(FString(std::format("<StructInstance {}>", structName.toBasicString())), defContext);
/*
3 ways of calling constructor
.1 Person {"Fig", 1, "IDK"};
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
.3 Person {name, age, sex};
*/
{
using enum Ast::InitExprAst::InitMode;
if (initExpr->initMode == Positional)
{
evalArguments();
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = field.name;
const TypeInfo &expectedType = field.type;
if (i >= evaluatedArgs.size())
{
// we've checked argument count before, so here
// must be a default value
// evaluate default value in definition context!
ObjectPtr defaultVal = check_unwrap(eval(field.defaultValue,
defContext)); // it can't be null here
// type check
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(expectedType, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, expectedType, field.am, argVal);
}
}
else if (initExpr->initMode == Named)
{
evalArguments();
// named
for (size_t i = 0; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
if (instanceCtx->containsInThisScope(fieldName))
{
throw EvaluatorError(u8"StructFieldRedeclarationError",
std::format("Field '{}' already initialized in structure '{}'",
fieldName.toBasicString(),
structName.toBasicString()),
initExpr);
}
if (i + 1 > got)
{
// use default value //
// evaluate default value in definition context
ObjectPtr defaultVal = check_unwrap(eval(field.defaultValue,
defContext)); // it can't be null here
// type check
const TypeInfo &expectedType = field.type;
if (!isTypeMatch(expectedType, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
expectedType.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, defaultVal);
continue;
}
const ObjectPtr &argVal = evaluatedArgs[i].second;
if (!isTypeMatch(field.type, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
fieldName.toBasicString(),
field.type.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
instanceCtx->def(fieldName, field.type, field.am, argVal);
}
}
else
{
// shorthand, can be unordered
// in this mode, initExpr args are all VarExpr
// field name is the variable name
for (const auto &[argName, argExpr] : initExpr->args)
{
// assert(argExpr->getType() == Ast::AstType::VarExpr);
// argName is var name
const ObjectPtr &argVal = check_unwrap(eval(argExpr, ctx)); // get the value
// find field
auto fieldIt = std::find_if(structT.fields.begin(),
structT.fields.end(),
[&argName](const Field &f) { return f.name == argName; });
if (fieldIt == structT.fields.end())
{
// throw EvaluatorError(u8"StructFieldNotFoundError",
// std::format("Field '{}' not found in structure '{}'",
// argName.toBasicString(),
// structName.toBasicString()),
// initExpr);
initExpr->initMode = Positional;
return evalInitExpr(initExpr, ctx);
}
const Field &field = *fieldIt;
if (!isTypeMatch(field.type, argVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
field.name.toBasicString(),
field.type.toString().toBasicString(),
prettyType(argVal).toBasicString()),
initExpr);
}
// field.name is argName (var name)
// Point{x=x, y=y} --> Point{x, y}
instanceCtx->def(field.name, field.type, field.am, argVal);
}
// fill default values
size_t currentFieldCount =
initExpr->args.size(); // we have already check argument count, min <= got <= max
// so remain fields start from currentFieldCount to maxArgs
for (size_t i = currentFieldCount; i < maxArgs; ++i)
{
const Field &field = structT.fields[i];
// evaluate default value in definition context
ObjectPtr defaultVal = check_unwrap(eval(field.defaultValue,
defContext)); // it can't be null here
// type check
if (!isTypeMatch(field.type, defaultVal, ctx))
{
throw EvaluatorError(
u8"StructFieldTypeMismatchError",
std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'",
structName.toBasicString(),
field.name.toBasicString(),
field.type.toString().toBasicString(),
prettyType(defaultVal).toBasicString()),
initExpr);
}
instanceCtx->def(field.name, field.type, field.am, defaultVal);
}
}
}
// load struct method
for (auto &[id, fn] : defContext->getFunctions())
{
const FString &funcName = fn.name;
const auto &funcSlot = defContext->get(funcName);
instanceCtx->def(funcName,
ValueType::Function,
funcSlot->am,
std::make_shared<Object>(Function(funcName, fn.paras, fn.retType, fn.body, instanceCtx)));
}
return std::make_shared<Object>(StructInstance(structT.type, instanceCtx));
}
};

View File

@@ -0,0 +1,248 @@
#include "Evaluator/Core/ExprResult.hpp"
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
namespace Fig
{
ExprResult Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx)
{
const FString &name = var->name;
// 调试信息
// std::cerr << "=== DEBUG evalVarExpr ===" << std::endl;
// std::cerr << "Looking for: " << name.toBasicString() << std::endl;
// std::cerr << "Context: " << ctx->getScopeName().toBasicString() << std::endl;
// // 打印上下文
// ContextPtr current = ctx;
// int depth = 0;
// while (current)
// {
// std::cerr << " [" << depth << "] " << current->getScopeName().toBasicString();
// if (current->containsInThisScope(name))
// {
// std::cerr << " -> FOUND HERE!" << std::endl;
// auto slot = current->get(name);
// std::cerr << " Type: " << slot->declaredType.toString().toBasicString() << std::endl;
// std::cerr << " Value: " << (slot->value ? slot->value->toString().toBasicString() : "null")
// << std::endl;
// }
// else
// {
// std::cerr << " -> not found" << std::endl;
// }
// current = current->parent;
// depth++;
// }
// end
if (!ctx->contains(name)) { throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); }
return LvObject(ctx->get(name), ctx);
}
ExprResult Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx)
{
// LvObject base = evalLv(me->base, ctx);
RvObject baseVal = check_unwrap(eval(me->base, ctx));
const FString &member = me->member;
if (baseVal->getTypeInfo() == ValueType::Module)
{
// std::cerr << "=== DEBUG evalMemberExpr (Module) ===" << std::endl;
// std::cerr << "Module object: " << baseVal->toString().toBasicString() << std::endl;
const Module &mod = baseVal->as<Module>();
// std::cerr << "Module context: " << mod.ctx->getScopeName().toBasicString() << std::endl;
// std::cerr << "Looking for member: " << member.toBasicString() << std::endl;
// if (mod.ctx->contains(member))
// {
// std::cerr << "Found in module context!" << std::endl;
// if (mod.ctx->isVariablePublic(member)) { std::cerr << "And it's public!" << std::endl; }
// else
// {
// std::cerr << "But it's NOT public!" << std::endl;
// }
// }
// else
// {
// std::cerr << "NOT found in module context!" << std::endl;
// }
if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member))
{
return LvObject(mod.ctx->get(member), ctx);
}
else
{
throw EvaluatorError(u8"VariableNotFoundError",
std::format("`{}` has not variable '{}', check if it is public",
baseVal->toString().toBasicString(),
member.toBasicString()),
me->base);
}
}
if (baseVal->hasMemberFunction(member))
{
return LvObject(std::make_shared<VariableSlot>(
member,
std::make_shared<Object>(Function(
member,
[baseVal, member](ObjectPtr self, std::vector<ObjectPtr> args) -> ObjectPtr {
if (self) { return baseVal->getMemberFunction(member)(self, args); }
return baseVal->getMemberFunction(member)(baseVal, args);
},
baseVal->getMemberFunctionParaCount(member))),
ValueType::Function,
AccessModifier::PublicConst),
ctx); // fake l-value
}
if (ctx->hasMethodImplemented(baseVal->getTypeInfo(), member))
{
// builtin type implementation!
// e.g. impl xxx for Int
auto &fn = ctx->getImplementedMethod(baseVal->getTypeInfo(), member);
Function boundFn(member,
fn.paras,
fn.retType,
fn.body,
ctx // current context
);
return LvObject(
std::make_shared<VariableSlot>(
member, std::make_shared<Object>(boundFn), ValueType::Function, AccessModifier::PublicConst),
ctx);
}
if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found
{
throw EvaluatorError(
u8"NoAttributeError",
std::format("`{}` has not attribute '{}'", baseVal->toString().toBasicString(), member.toBasicString()),
me->base);
}
const StructInstance &si = baseVal->as<StructInstance>();
if (ctx->hasMethodImplemented(si.parentType, member))
{
auto &fn = ctx->getImplementedMethod(si.parentType, member);
Function boundFn(member,
fn.paras,
fn.retType,
fn.body,
si.localContext // create a new function and set closure context
// to struct instance context
);
return LvObject(
std::make_shared<VariableSlot>(
member, std::make_shared<Object>(boundFn), ValueType::Function, AccessModifier::PublicConst),
ctx);
}
else if (si.localContext->containsInThisScope(member) && si.localContext->isVariablePublic(member))
{
return LvObject(si.localContext->get(member), ctx);
}
else if (ctx->hasDefaultImplementedMethod(si.parentType, member))
{
const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member);
Function fn(member, ifm.paras, actualType(check_unwrap(eval(ifm.returnType, ctx))), ifm.defaultBody, ctx);
return LvObject(std::make_shared<VariableSlot>(
member, std::make_shared<Object>(fn), ValueType::Function, AccessModifier::PublicConst),
ctx);
}
else
{
throw EvaluatorError(u8"NoAttributeError",
std::format("`{}` has not attribute '{}' and no interfaces have been implemented it",
baseVal->toString().toBasicString(),
member.toBasicString()),
me->base);
}
}
ExprResult Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx)
{
RvObject base = check_unwrap(eval(ie->base, ctx));
RvObject index = check_unwrap(eval(ie->index, ctx));
const TypeInfo &type = base.get()->getTypeInfo();
if (type == ValueType::List)
{
if (index->getTypeInfo() != ValueType::Int)
{
throw EvaluatorError(
u8"TypeError",
std::format("Type `List` indices must be `Int`, got '{}'", prettyType(index).toBasicString()),
ie->index);
}
List &list = base.get()->as<List>();
ValueType::IntClass indexVal = index->as<ValueType::IntClass>();
if (indexVal >= list.size())
{
throw EvaluatorError(
u8"IndexOutOfRangeError",
std::format("Index {} out of list `{}` range", indexVal, base->toString().toBasicString()),
ie->index);
}
return LvObject(base, indexVal, LvObject::Kind::ListElement, ctx);
}
else if (type == ValueType::Map) { return LvObject(base, index, LvObject::Kind::MapElement, ctx); }
else if (type == ValueType::String)
{
if (index->getTypeInfo() != ValueType::Int)
{
throw EvaluatorError(
u8"TypeError",
std::format("Type `String` indices must be `Int`, got '{}'", prettyType(index).toBasicString()),
ie->index);
}
FString &string = base->as<ValueType::StringClass>();
ValueType::IntClass indexVal = index->as<ValueType::IntClass>();
if (indexVal >= string.length())
{
throw EvaluatorError(
u8"IndexOutOfRangeError",
std::format("Index {} out of string `{}` range", indexVal, base->toString().toBasicString()),
ie->index);
}
return LvObject(base, indexVal, LvObject::Kind::StringElement, ctx);
}
else
{
throw EvaluatorError(
u8"NoSubscriptableError",
std::format("`{}` object is not subscriptable", base->getTypeInfo().toString().toBasicString()),
ie->base);
}
}
ExprResult Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx)
{
using Ast::Operator;
using Ast::AstType;
switch (exp->getType())
{
case AstType::VarExpr: {
Ast::VarExpr var = std::static_pointer_cast<Ast::VarExprAst>(exp);
return evalVarExpr(var, ctx);
}
case AstType::MemberExpr: {
Ast::MemberExpr me = std::static_pointer_cast<Ast::MemberExprAst>(exp);
return evalMemberExpr(me, ctx);
}
case AstType::IndexExpr: {
Ast::IndexExpr ie = std::static_pointer_cast<Ast::IndexExprAst>(exp);
return evalIndexExpr(ie, ctx);
}
default: {
throw EvaluatorError(
u8"TypeError",
std::format("Expression '{}' doesn't refer to a lvalue", exp->typeName().toBasicString()),
exp);
}
}
}
}; // namespace Fig

View File

@@ -0,0 +1,723 @@
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Evaluator/Core/ExprResult.hpp>
#include <Ast/AccessModifier.hpp>
#include <Ast/Expressions/FunctionCall.hpp>
#include <Ast/astBase.hpp>
#include <Ast/functionParameters.hpp>
#include <Core/fig_string.hpp>
#include <Evaluator/Core/StatementResult.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/structType.hpp>
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Utils/utils.hpp>
#include <unordered_map>
#include <unordered_set>
namespace Fig
{
StatementResult Evaluator::evalStatement(Ast::Statement stmt, ContextPtr ctx)
{
using enum Ast::AstType;
switch (stmt->getType())
{
case ImportSt: {
auto i = std::static_pointer_cast<Ast::ImportSt>(stmt);
return evalImportSt(i, ctx);
}
case VarDefSt: {
auto varDef = std::static_pointer_cast<Ast::VarDefAst>(stmt);
if (ctx->containsInThisScope(varDef->name))
{
throw EvaluatorError(
u8"RedeclarationError",
std::format("Variable `{}` already declared in this scope", varDef->name.toBasicString()),
varDef);
}
RvObject value = nullptr;
if (varDef->expr) { value = check_unwrap_stres(eval(varDef->expr, ctx)); }
TypeInfo declaredType; // default is Any
const Ast::Expression &declaredTypeExp = varDef->declaredType;
if (varDef->followupType) { declaredType = actualType(value); }
else if (declaredTypeExp)
{
ObjectPtr declaredTypeValue = check_unwrap_stres(eval(declaredTypeExp, ctx));
declaredType = actualType(declaredTypeValue);
if (value != nullptr && !isTypeMatch(declaredType, value, ctx))
{
throw EvaluatorError(u8"TypeError",
std::format("Variable `{}` expects init-value type `{}`, but got '{}'",
varDef->name.toBasicString(),
prettyType(declaredTypeValue).toBasicString(),
prettyType(value).toBasicString()),
varDef->expr);
}
else if (value == nullptr)
{
value = std::make_shared<Object>(Object::defaultValue(declaredType));
} // else -> Ok
} // else -> type is Any (default)
else
{
value = Object::getNullInstance();
}
AccessModifier am =
(varDef->isConst ? (varDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const) :
(varDef->isPublic ? AccessModifier::Public : AccessModifier::Normal));
ctx->def(varDef->name, declaredType, am, value);
return StatementResult::normal();
}
case FunctionDefSt: {
auto fnDef = std::static_pointer_cast<Ast::FunctionDefSt>(stmt);
const FString &fnName = fnDef->name;
if (ctx->containsInThisScope(fnName))
{
throw EvaluatorError(
u8"RedeclarationError",
std::format("Function `{}` already declared in this scope", fnName.toBasicString()),
fnDef);
}
TypeInfo returnType = ValueType::Any;
if (fnDef->retType)
{
ObjectPtr returnTypeValue = check_unwrap_stres(eval(fnDef->retType, ctx));
returnType = actualType(returnTypeValue);
}
Function fn(fnName, fnDef->paras, returnType, fnDef->body, ctx);
ctx->def(fnName,
ValueType::Function,
(fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const),
std::make_shared<Object>(fn));
return StatementResult::normal();
}
case StructSt: {
auto stDef = std::static_pointer_cast<Ast::StructDefSt>(stmt);
if (ctx->containsInThisScope(stDef->name))
{
throw EvaluatorError(
u8"RedeclarationError",
std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()),
stDef);
}
TypeInfo type(stDef->name, true); // register type name
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
stDef->name.toBasicString(),
stDef->getAAI().line,
stDef->getAAI().column)),
ctx);
ObjectPtr structTypeObj = std::make_shared<Object>(StructType(type, defContext, {}));
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
ctx->def(stDef->name, ValueType::StructType, am, structTypeObj); // predef
defContext->def(stDef->name,
ValueType::StructType,
AccessModifier::Const,
structTypeObj); // predef to itself, always const
std::vector<Field> fields;
std::vector<FString> _fieldNames;
for (Ast::StructDefField field : stDef->fields)
{
if (Utils::vectorContains(field.fieldName, _fieldNames))
{
throw EvaluatorError(u8"RedeclarationError",
std::format("Field '{}' already defined in structure '{}'",
field.fieldName.toBasicString(),
stDef->name.toBasicString()),
stDef);
}
TypeInfo fieldType = ValueType::Any;
if (field.declaredType)
{
ObjectPtr declaredTypeValue = check_unwrap_stres(eval(field.declaredType, ctx));
fieldType = actualType(declaredTypeValue);
}
fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr));
}
structTypeObj->as<StructType>().fields = fields;
const Ast::BlockStatement &body = stDef->body;
for (auto &st : body->stmts)
{
if (st->getType() != Ast::AstType::FunctionDefSt)
{
throw EvaluatorError(u8"UnexpectedStatementInStructError",
std::format("Unexpected statement `{}` in struct declaration",
st->toString().toBasicString()),
st);
}
evalStatement(st, defContext); // function def st
}
return StatementResult::normal();
}
case InterfaceDefSt: {
auto ifd = std::static_pointer_cast<Ast::InterfaceDefAst>(stmt);
const FString &interfaceName = ifd->name;
const std::vector<Ast::Expression> &bundle_exprs = ifd->bundles;
if (ctx->containsInThisScope(interfaceName))
{
throw EvaluatorError(
u8"RedeclarationError",
std::format("Interface `{}` already declared in this scope", interfaceName.toBasicString()),
ifd);
}
std::vector<Ast::InterfaceMethod> bundle_methods;
std::unordered_map<FString, FString> cache_methods;
// K: interface method name V: method owner (interface)
for (const auto &exp : bundle_exprs)
{
ObjectPtr itf_val = check_unwrap_stres(eval(exp, ctx));
if (!itf_val->is<InterfaceType>())
{
throw EvaluatorError(u8"TypeError",
std::format("Cannot bundle type '{}' that is not interface",
prettyType(itf_val).toBasicString()),
exp);
}
const InterfaceType &itfType = itf_val->as<InterfaceType>();
for (const auto &method : itfType.methods)
{
if (cache_methods.contains(method.name))
{
throw EvaluatorError(u8"DuplicateInterfaceMethodError",
std::format("Interface `{}` has duplicate method '{}' with '{}.{}'",
itfType.type.toString().toBasicString(),
method.name.toBasicString(),
cache_methods[method.name].toBasicString(),
method.name.toBasicString()),
ifd);
}
cache_methods[method.name] = itfType.type.toString();
bundle_methods.push_back(method);
}
}
std::vector<Ast::InterfaceMethod> methods(ifd->methods);
methods.insert(methods.end(), bundle_methods.begin(), bundle_methods.end());
TypeInfo type(interfaceName, true); // register interface
ctx->def(interfaceName,
type,
(ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const),
std::make_shared<Object>(InterfaceType(type, methods)));
return StatementResult::normal();
}
case ImplementSt: {
auto ip = std::static_pointer_cast<Ast::ImplementAst>(stmt);
TypeInfo structType(ip->structName);
TypeInfo interfaceType(ip->interfaceName);
if (ctx->hasImplRegisted(structType, interfaceType))
{
throw EvaluatorError(u8"DuplicateImplError",
std::format("Duplicate implement `{}` for `{}`",
interfaceType.toString().toBasicString(),
structType.toString().toBasicString()),
ip);
}
if (!ctx->contains(ip->interfaceName))
{
throw EvaluatorError(u8"InterfaceNotFoundError",
std::format("Interface '{}' not found", ip->interfaceName.toBasicString()),
ip);
}
if (!ctx->contains(ip->structName))
{
throw EvaluatorError(u8"StructNotFoundError",
std::format("Struct '{}' not found", ip->structName.toBasicString()),
ip);
}
auto interfaceSlot = ctx->get(ip->interfaceName);
auto structSlot = ctx->get(ip->structName);
LvObject interfaceLv(interfaceSlot, ctx);
LvObject structLv(structSlot, ctx);
ObjectPtr interfaceObj = interfaceLv.get();
ObjectPtr structTypeObj = structLv.get();
if (!interfaceObj->is<InterfaceType>())
{
throw EvaluatorError(
u8"NotAInterfaceError",
std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()),
ip);
}
if (!structTypeObj->is<StructType>())
{
throw EvaluatorError(
u8"NotAStructType",
std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()),
ip);
}
auto &implementMethods = ip->methods;
if (ip->interfaceName == u8"Operation")
{
// 运算符重载
/*
impl Operation for xxx
{
add(l, r) {...}
}
*/
if (ValueType::isTypeBuiltin(structType))
{
throw EvaluatorError(u8"BadUserError",
std::format("Don't overload built-in type operators plz! `{}`",
prettyType(structTypeObj).toBasicString()),
ip);
}
using enum Ast::Operator;
static const std::unordered_map<FString, std::pair<Ast::Operator, size_t>> magic_name_to_op = {
// 算术
{u8"Add", {Ast::Operator::Add, 2}},
{u8"Sub", {Ast::Operator::Subtract, 2}},
{u8"Mul", {Ast::Operator::Multiply, 2}},
{u8"Div", {Ast::Operator::Divide, 2}},
{u8"Mod", {Ast::Operator::Modulo, 2}},
{u8"Pow", {Ast::Operator::Power, 2}},
// 逻辑(一元)
{u8"Neg", {Ast::Operator::Subtract, 1}}, // 一元负号
{u8"Not", {Ast::Operator::Not, 1}},
// 逻辑(二元)
{u8"And", {Ast::Operator::And, 2}},
{u8"Or", {Ast::Operator::Or, 2}},
// 比较
{u8"Equal", {Ast::Operator::Equal, 2}},
{u8"NotEqual", {Ast::Operator::NotEqual, 2}},
{u8"LessThan", {Ast::Operator::Less, 2}},
{u8"LessEqual", {Ast::Operator::LessEqual, 2}},
{u8"GreaterThan", {Ast::Operator::Greater, 2}},
{u8"GreaterEqual", {Ast::Operator::GreaterEqual, 2}},
{u8"Is", {Ast::Operator::Is, 2}},
// 位运算(一元)
{u8"BitNot", {Ast::Operator::BitNot, 1}},
// 位运算(二元)
{u8"BitAnd", {Ast::Operator::BitAnd, 2}},
{u8"BitOr", {Ast::Operator::BitOr, 2}},
{u8"BitXor", {Ast::Operator::BitXor, 2}},
{u8"ShiftLeft", {Ast::Operator::ShiftLeft, 2}},
{u8"ShiftRight", {Ast::Operator::ShiftRight, 2}},
};
for (auto &implMethod : implementMethods)
{
const FString &opName = implMethod.name;
if (!magic_name_to_op.contains(opName))
{
// ... 现在忽略
// 未来可能报错
continue;
}
auto [op, expectArgCnt] = magic_name_to_op.at(opName);
// type op isUnary(1-->true, 2-->false)
if (ctx->hasOperatorImplemented(structType, op, (expectArgCnt == 1 ? true : false)))
{
throw EvaluatorError(
u8"DuplicateImplementError",
std::format("{} has already implement by another interface", opName.toBasicString()),
ip);
}
size_t paraCnt = implMethod.paras.posParas.size(); // 必须为位置参数!
if (paraCnt != expectArgCnt || implMethod.paras.size() != expectArgCnt)
{
// 特化报错,更详细易读
throw EvaluatorError(u8"InterfaceSignatureMismatch",
std::format("Operator {} for {} arg count must be {}, got {}",
opName.toBasicString(),
structLv.name().toBasicString(),
expectArgCnt,
paraCnt),
ip);
}
FString opFnName(u8"Operation." + prettyType(structTypeObj) + u8"." + opName);
ContextPtr fnCtx = std::make_shared<Context>(
FString(std::format("<Function {}>", opFnName.toBasicString())), ctx);
const auto &fillOpFnParas = [this, structType, implMethod, opFnName, fnCtx, ctx, paraCnt](
const std::vector<ObjectPtr> &args) -> StatementResult {
const Ast::FunctionParameters &paras = implMethod.paras;
for (size_t i = 0; i < paraCnt; ++i)
{
const TypeInfo &paraType =
actualType(check_unwrap_stres(eval(paras.posParas[i].second, ctx)));
if (paraType != ValueType::Any && paraType != structType)
{
throw EvaluatorError(
u8"ParameterTypeError",
std::format("Invalid op fn parameter type '{}' of `{}`, must be `{}`",
paraType.toString().toBasicString(),
paras.posParas[i].first.toBasicString(),
structType.toString().toBasicString()),
paras.posParas[i].second);
}
fnCtx->def(paras.posParas[i].first, paraType, AccessModifier::Normal, args[i]);
}
return StatementResult::normal();
};
if (paraCnt == 1)
{
ctx->registerUnaryOperator(structType, op, [=, this](const ObjectPtr &value) -> ExprResult {
fillOpFnParas({value});
return executeFunction(Function(opFnName,
implMethod.paras, // parameters
structType, // return type --> struct type
implMethod.body, // body
ctx // closure context
),
Ast::FunctionCallArgs{.argv = {value}},
fnCtx);
});
}
else
{
ctx->registerBinaryOperator(
structType, op, [=, this](const ObjectPtr &lhs, const ObjectPtr &rhs) {
fillOpFnParas({lhs, rhs});
return executeFunction(Function(opFnName,
implMethod.paras, // parameters
structType, // return type --> struct type
implMethod.body, // body
ctx // closure context
),
Ast::FunctionCallArgs{.argv = {lhs, rhs}},
fnCtx);
});
}
}
return StatementResult::normal();
}
InterfaceType &interface = interfaceObj->as<InterfaceType>();
// ===== interface implementation validation =====
ImplRecord record{interfaceType, structType, {}};
std::unordered_map<FString, Ast::InterfaceMethod> ifaceMethods;
for (auto &m : interface.methods)
{
if (ifaceMethods.contains(m.name))
{
throw EvaluatorError(u8"InterfaceDuplicateMethodError",
std::format("Interface '{}' has duplicate method '{}'",
interfaceType.toString().toBasicString(),
m.name.toBasicString()),
ip);
}
ifaceMethods[m.name] = m;
}
std::unordered_set<FString> implemented;
for (auto &implMethod : implementMethods)
{
const FString &name = implMethod.name;
// ---- redundant impl ----
if (!ifaceMethods.contains(name))
{
throw EvaluatorError(u8"RedundantImplementationError",
std::format("Struct '{}' implements extra method '{}' "
"which is not required by interface '{}'",
structType.toString().toBasicString(),
name.toBasicString(),
interfaceType.toString().toBasicString()),
ip);
}
if (implemented.contains(name))
{
throw EvaluatorError(u8"DuplicateImplementMethodError",
std::format("Duplicate implement method '{}'", name.toBasicString()),
ip);
}
auto &ifMethod = ifaceMethods[name];
// ---- signature check ----
if (!isInterfaceSignatureMatch(implMethod, ifMethod))
{
throw EvaluatorError(u8"InterfaceSignatureMismatch",
std::format("Interface method '{}({})' signature mismatch with "
"implementation '{}({})'",
ifMethod.name.toBasicString(),
ifMethod.paras.toString().toBasicString(),
implMethod.name.toBasicString(),
implMethod.paras.toString().toBasicString()),
ip);
}
if (ctx->hasMethodImplemented(structType, name))
{
throw EvaluatorError(u8"DuplicateImplementMethodError",
std::format("Method '{}' already implemented by another interface "
"for struct '{}'",
name.toBasicString(),
structType.toString().toBasicString()),
ip);
}
implemented.insert(name);
ObjectPtr returnTypeValue = check_unwrap_stres(eval(ifMethod.returnType, ctx));
record.implMethods[name] =
Function(implMethod.name, implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx);
}
for (auto &m : interface.methods)
{
if (implemented.contains(m.name)) continue;
if (m.hasDefaultBody()) continue;
throw EvaluatorError(u8"MissingImplementationError",
std::format("Struct '{}' does not implement required interface method '{}' "
"and interface '{}' provides no default implementation",
structType.toString().toBasicString(),
m.name.toBasicString(),
interfaceType.toString().toBasicString()),
ip);
}
ctx->setImplRecord(structType, interfaceType, record);
return StatementResult::normal();
}
case IfSt: {
auto ifSt = std::static_pointer_cast<Ast::IfSt>(stmt);
ObjectPtr condVal = check_unwrap_stres(eval(ifSt->condition, ctx));
if (condVal->getTypeInfo() != ValueType::Bool)
{
throw EvaluatorError(
u8"TypeError",
std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()),
ifSt->condition);
}
if (condVal->as<ValueType::BoolClass>()) { return evalBlockStatement(ifSt->body, ctx); }
// else
for (const auto &elif : ifSt->elifs)
{
ObjectPtr elifCondVal = check_unwrap_stres(eval(elif->condition, ctx));
if (elifCondVal->getTypeInfo() != ValueType::Bool)
{
throw EvaluatorError(
u8"TypeError",
std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()),
ifSt->condition);
}
if (elifCondVal->as<ValueType::BoolClass>()) { return evalBlockStatement(elif->body, ctx); }
}
if (ifSt->els) { return evalBlockStatement(ifSt->els->body, ctx); }
return StatementResult::normal();
};
case WhileSt: {
auto whileSt = std::static_pointer_cast<Ast::WhileSt>(stmt);
while (true)
{
ObjectPtr condVal = check_unwrap_stres(eval(whileSt->condition, ctx));
if (condVal->getTypeInfo() != ValueType::Bool)
{
throw EvaluatorError(
u8"TypeError",
std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()),
whileSt->condition);
}
if (!condVal->as<ValueType::BoolClass>()) { break; }
ContextPtr loopContext = std::make_shared<Context>(
FString(std::format("<While {}:{}>", whileSt->getAAI().line, whileSt->getAAI().column)),
ctx); // every loop has its own context
StatementResult sr = evalBlockStatement(whileSt->body, loopContext);
if (sr.shouldReturn()) { return sr; }
if (sr.shouldBreak()) { break; }
if (sr.shouldContinue()) { continue; }
}
return StatementResult::normal();
};
case ForSt: {
auto forSt = std::static_pointer_cast<Ast::ForSt>(stmt);
ContextPtr loopContext = std::make_shared<Context>(
FString(std::format("<For {}:{}>", forSt->getAAI().line, forSt->getAAI().column)),
ctx); // for loop has its own context
evalStatement(forSt->initSt,
loopContext); // ignore init statement result
size_t iteration = 0;
ContextPtr iterationContext = std::make_shared<Context>(
FString(std::format(
"<For {}:{}, Iteration {}>", forSt->getAAI().line, forSt->getAAI().column, iteration)),
loopContext); // every loop has its own context
while (true) // use while loop to simulate for loop, cause we
// need to check condition type every iteration
{
ObjectPtr condVal = check_unwrap_stres(eval(forSt->condition, loopContext));
if (condVal->getTypeInfo() != ValueType::Bool)
{
throw EvaluatorError(
u8"TypeError",
std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()),
forSt->condition);
}
if (!condVal->as<ValueType::BoolClass>()) { break; }
iteration++;
StatementResult sr = evalBlockStatement(forSt->body, iterationContext);
iterationContext->clear();
iterationContext->setScopeName(FString(std::format(
"<For {}:{}, Iteration {}>", forSt->getAAI().line, forSt->getAAI().column, iteration)));
if (sr.shouldReturn()) { return sr; }
if (sr.shouldBreak()) { break; }
if (sr.shouldContinue())
{
// continue to next iteration
continue;
}
evalStatement(forSt->incrementSt,
loopContext); // ignore increment statement result
}
return StatementResult::normal();
}
case TrySt: {
auto tryst = std::static_pointer_cast<Ast::TrySt>(stmt);
ContextPtr tryCtx = std::make_shared<Context>(
FString(std::format("<Try at {}:{}>", tryst->getAAI().line, tryst->getAAI().column)), ctx);
StatementResult sr = StatementResult::normal();
bool crashed = false;
for (auto &stmt : tryst->body->stmts)
{
sr = evalStatement(stmt, tryCtx); // eval in try context
if (sr.isError())
{
crashed = true;
break;
}
}
bool catched = false;
for (auto &cat : tryst->catches)
{
const FString &errVarName = cat.errVarName;
TypeInfo errVarType = (cat.hasType ? TypeInfo(cat.errVarType) : ValueType::Any);
if (isTypeMatch(errVarType, sr.result, ctx))
{
ContextPtr catchCtx = std::make_shared<Context>(
FString(
std::format("<Catch at {}:{}>", cat.body->getAAI().line, cat.body->getAAI().column)),
ctx);
catchCtx->def(errVarName, errVarType, AccessModifier::Normal, sr.result);
sr = evalBlockStatement(cat.body, catchCtx);
catched = true;
break;
}
}
if (!catched && crashed)
{
throw EvaluatorError(u8"UncaughtExceptionError",
std::format("Uncaught exception: {}", sr.result->toString().toBasicString()),
tryst);
}
if (tryst->finallyBlock) { sr = evalBlockStatement(tryst->finallyBlock, ctx); }
return sr;
}
case ThrowSt: {
auto ts = std::static_pointer_cast<Ast::ThrowSt>(stmt);
ObjectPtr value = check_unwrap_stres(eval(ts->value, ctx));
if (value->is<ValueType::NullClass>())
{
throw EvaluatorError(u8"TypeError", u8"Why did you throw a null?", ts);
}
return StatementResult::errorFlow(value);
}
case ReturnSt: {
auto returnSt = std::static_pointer_cast<Ast::ReturnSt>(stmt);
ObjectPtr returnValue = Object::getNullInstance(); // default is null
if (returnSt->retValue) returnValue = check_unwrap_stres(eval(returnSt->retValue, ctx));
return StatementResult::returnFlow(returnValue);
}
case BreakSt: {
if (!ctx->parent)
{
throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
}
if (!ctx->isInLoopContext())
{
throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt);
}
return StatementResult::breakFlow();
}
case ContinueSt: {
if (!ctx->parent)
{
throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
}
if (!ctx->isInLoopContext())
{
throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt);
}
return StatementResult::continueFlow();
}
case ExpressionStmt: {
auto exprStmt = std::static_pointer_cast<Ast::ExpressionStmtAst>(stmt);
return check_unwrap_stres(eval(exprStmt->exp, ctx));
}
case BlockStatement: {
auto block = std::static_pointer_cast<Ast::BlockStatementAst>(stmt);
ContextPtr blockCtx = std::make_shared<Context>(
FString(std::format("<Block at {}:{}>", block->getAAI().line, block->getAAI().column)), ctx);
return evalBlockStatement(block, blockCtx);
}
default:
throw RuntimeError(
FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType()))));
}
}
}; // namespace Fig

View File

@@ -0,0 +1,21 @@
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Evaluator/Core/ExprResult.hpp>
namespace Fig
{
ExprResult Evaluator::evalTernary(Ast::TernaryExpr te, ContextPtr ctx)
{
RvObject condVal = check_unwrap(eval(te->condition, ctx));
if (condVal->getTypeInfo() != ValueType::Bool)
{
throw EvaluatorError(
u8"TypeError",
std::format("Condition must be boolean, got '{}'", prettyType(condVal).toBasicString()),
te->condition);
}
ValueType::BoolClass cond = condVal->as<ValueType::BoolClass>();
return (cond ? eval(te->valueT, ctx) : eval(te->valueF, ctx));
}
};

View File

@@ -0,0 +1,48 @@
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
#include <Evaluator/Core/ExprResult.hpp>
namespace Fig
{
ExprResult Evaluator::evalUnary(Ast::UnaryExpr un, ContextPtr ctx)
{
using Ast::Operator;
Operator op = un->op;
Ast::Expression exp = un->exp;
ObjectPtr value = check_unwrap(eval(exp, ctx));
const auto &tryInvokeOverloadFn = [ctx, op](const ObjectPtr &rhs, const std::function<ExprResult()> &rollback) {
if (rhs->is<StructInstance>())
{
// 运算符重载
const TypeInfo &type = actualType(rhs);
if (ctx->hasOperatorImplemented(type, op))
{
const auto &fnOpt = ctx->getUnaryOperatorFn(type, op);
return (*fnOpt)(rhs);
}
}
return rollback();
};
switch (op)
{
case Operator::Not: {
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(!(*value)); });
}
case Operator::Subtract: {
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(-(*value)); });
}
case Operator::BitNot: {
return tryInvokeOverloadFn(value, [value]() { return std::make_shared<Object>(bit_not(*value)); });
}
default: {
throw EvaluatorError(u8"UnsupportedOpError",
std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)),
un);
}
}
}
}; // namespace Fig

View File

@@ -0,0 +1,19 @@
#include <Evaluator/Core/ExprResult.hpp>
#include <Evaluator/Core/StatementResult.hpp>
namespace Fig
{
StatementResult ExprResult::toStatementResult() const
{
if (isError())
{
if (isResultLv())
{
return StatementResult::errorFlow(std::get<LvObject>(result).get());
}
return StatementResult::errorFlow(std::get<RvObject>(result));
}
if (isResultLv()) { return StatementResult::normal(std::get<LvObject>(result).get()); }
return StatementResult::normal(std::get<RvObject>(result));
}
};

View File

@@ -0,0 +1,72 @@
#pragma once
#include <Core/fig_string.hpp>
#include <Evaluator/Value/value_forward.hpp>
#include <Evaluator/Value/LvObject.hpp>
#include <variant>
#include <cassert>
namespace Fig
{
struct StatementResult;
struct ExprResult
{
std::variant<LvObject, RvObject> result;
enum class Flow
{
Normal,
Error,
} flow;
ExprResult(ObjectPtr _result, Flow _flow = Flow::Normal) : result(_result), flow(_flow) {}
ExprResult(const LvObject &_result, Flow _flow = Flow::Normal) : result(_result), flow(_flow) {}
static ExprResult normal(ObjectPtr _result) { return ExprResult(_result); }
static ExprResult normal(const LvObject &_result) { return ExprResult(_result); }
static ExprResult error(ObjectPtr _result) { return ExprResult(_result, Flow::Error); }
static ExprResult error(const LvObject &_result) { return ExprResult(_result, Flow::Error); }
bool isNormal() const { return flow == Flow::Normal; }
bool isError() const { return flow == Flow::Error; }
bool isResultLv() const { return std::holds_alternative<LvObject>(result); }
ObjectPtr &unwrap()
{
if (!isNormal()) { assert(false && "unwrap abnormal ExprResult!"); }
return std::get<RvObject>(result);
}
const ObjectPtr &unwrap() const
{
if (!isNormal()) { assert(false && "unwrap abnormal ExprResult!"); }
return std::get<RvObject>(result);
}
const LvObject &unwrap_lv() const { return std::get<LvObject>(result); }
StatementResult toStatementResult() const;
};
#define check_unwrap(expr) \
({ \
auto _r = (expr); \
if (_r.isError()) return _r; \
_r.unwrap(); \
})
#define check_unwrap_lv(expr) \
({ \
auto _r = (expr); \
if (_r.isError()) return _r; \
_r.unwrap_lv(); \
})
#define check_unwrap_stres(expr) \
({ \
auto _r = (expr); \
if (_r.isError()) return _r.toStatementResult(); \
_r.unwrap(); \
})
}; // namespace Fig

View File

@@ -0,0 +1,114 @@
#include <Core/executablePath.hpp>
#include <Evaluator/evaluator.hpp>
#include <Evaluator/evaluator_error.hpp>
namespace Fig
{
std::filesystem::path Evaluator::resolveModulePath(const std::vector<FString> &pathVec)
{
namespace fs = std::filesystem;
static const std::vector<fs::path> defaultLibraryPath{"Library", "Library/fpm"};
std::vector<fs::path> pathToFind(defaultLibraryPath);
fs::path interpreterPath = getExecutablePath().parent_path();
for (fs::path &p : pathToFind)
{
p = interpreterPath / p; // 相对路径 -> 绝对路径
}
pathToFind.insert(
pathToFind.begin(),
fs::path(this->sourcePath.toBasicString()).parent_path()); // first search module at the source file path
fs::path path;
/*
Example:
import comp.config;
*/
const FString &modPathStrTop = pathVec.at(0);
fs::path modPath;
bool found = false;
for (auto &parentFolder : pathToFind)
{
modPath = parentFolder / FString(modPathStrTop + u8".fig").toBasicString();
if (fs::exists(modPath))
{
path = modPath;
found = true;
break;
}
else
{
modPath = parentFolder / modPathStrTop.toBasicString();
if (fs::is_directory(modPath)) // comp is a directory
{
modPath = modPath / FString(modPathStrTop + u8".fig").toBasicString();
/*
if module name is a directory, we require [module
name].fig at the directory
*/
if (!fs::exists(modPath))
{
throw RuntimeError(FString(std::format("requires module file, {}\\{}",
modPathStrTop.toBasicString(),
FString(modPathStrTop + u8".fig").toBasicString())));
}
found = true;
path = modPath;
break;
}
}
}
if (!found)
throw RuntimeError(FString(std::format("Could not find module `{}`", modPathStrTop.toBasicString())));
bool found2 = false;
for (size_t i = 1; i < pathVec.size(); ++i) // has next module
{
const FString &next = pathVec.at(i);
modPath = modPath.parent_path(); // get the folder
modPath = modPath / FString(next + u8".fig").toBasicString();
if (fs::exists(modPath))
{
if (i != pathVec.size() - 1)
throw RuntimeError(FString(std::format(
"expects {} as parent directory and find next module, but got a file", next.toBasicString())));
// it's the last module
found2 = true;
path = modPath;
break;
}
// `next` is a folder
modPath = modPath.parent_path() / next.toBasicString();
if (!fs::exists(modPath))
throw RuntimeError(FString(std::format("Could not find module `{}`", next.toBasicString())));
if (i == pathVec.size() - 1)
{
// `next` is the last module
modPath = modPath / FString(next + u8".fig").toBasicString();
if (!fs::exists(modPath))
{
throw RuntimeError(FString(std::format(
"expects {} as parent directory and find next module, but got a file", next.toBasicString())));
}
found2 = true;
path = modPath;
}
}
if (!found2 && !fs::exists(modPath))
throw RuntimeError(FString(std::format("Could not find module `{}`", pathVec.end()->toBasicString())));
return path;
}
};

View File

@@ -0,0 +1,37 @@
#pragma once
#include <Core/fig_string.hpp>
#include <Evaluator/Value/value.hpp>
namespace Fig
{
struct StatementResult
{
ObjectPtr result;
enum class Flow
{
Normal,
Return,
Break,
Continue,
Error
} flow;
StatementResult(ObjectPtr val, Flow f = Flow::Normal) : result(val), flow(f) {}
static StatementResult normal(ObjectPtr val = Object::getNullInstance())
{
return StatementResult(val, Flow::Normal);
}
static StatementResult returnFlow(ObjectPtr val) { return StatementResult(val, Flow::Return); }
static StatementResult breakFlow() { return StatementResult(Object::getNullInstance(), Flow::Break); }
static StatementResult continueFlow() { return StatementResult(Object::getNullInstance(), Flow::Continue); }
static StatementResult errorFlow(ObjectPtr val) { return StatementResult(val, Flow::Error); }
bool isNormal() const { return flow == Flow::Normal; }
bool shouldReturn() const { return flow == Flow::Return; }
bool shouldBreak() const { return flow == Flow::Break; }
bool shouldContinue() const { return flow == Flow::Continue; }
bool isError() const { return flow == Flow::Error; }
};
};

View File

@@ -0,0 +1,45 @@
#pragma once
#include <Evaluator/Value/value.hpp>
#include <Core/fig_string.hpp>
#include <Evaluator/Value/value_forward.hpp>
#include <Evaluator/Value/Type.hpp>
#include <array>
#include <memory>
namespace Fig
{
class IntPool
{
private:
static constexpr ValueType::IntClass CACHE_MIN = -128;
static constexpr ValueType::IntClass CACHE_MAX = 127;
std::array<ObjectPtr, CACHE_MAX - CACHE_MIN + 1> cache;
public:
IntPool()
{
for (ValueType::IntClass i = CACHE_MIN; i <= CACHE_MAX; ++i)
{
cache[i - CACHE_MIN] = std::make_shared<Object>(i);
}
}
ObjectPtr createInt(ValueType::IntClass val) const
{
if (val >= CACHE_MIN && val <= CACHE_MAX) { return cache[val - CACHE_MIN]; }
return std::make_shared<Object>(val);
}
Object createIntCopy(ValueType::IntClass val) const
{
if (val >= CACHE_MIN && val <= CACHE_MAX) { return *cache[val - CACHE_MIN]; }
return Object(val);
}
static const IntPool &getInstance()
{
static IntPool pool;
return pool;
}
};
}; // namespace Fig

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Value/VariableSlot.hpp> #include <Evaluator/Value/VariableSlot.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/value.hpp>
namespace Fig namespace Fig
{ {
@@ -80,6 +80,10 @@ namespace Fig
if (kind == Kind::Variable) if (kind == Kind::Variable)
{ {
auto s = resolve(slot); auto s = resolve(slot);
if (isAccessConst(s->am))
{
throw RuntimeError(FString(std::format("Variable `{}` is immutable", s->name.toBasicString())));
}
if (!isTypeMatch(s->declaredType, v, ctx)) if (!isTypeMatch(s->declaredType, v, ctx))
{ {
throw RuntimeError( throw RuntimeError(
@@ -87,12 +91,7 @@ namespace Fig
std::format("Variable `{}` expects type `{}`, but got '{}'", std::format("Variable `{}` expects type `{}`, but got '{}'",
s->name.toBasicString(), s->name.toBasicString(),
s->declaredType.toString().toBasicString(), s->declaredType.toString().toBasicString(),
v->getTypeInfo().toString().toBasicString()))); prettyType(v).toBasicString())));
}
if (isAccessConst(s->am))
{
throw RuntimeError(FString(
std::format("Variable `{}` is immutable", s->name.toBasicString())));
} }
s->value = v; s->value = v;
} }

View File

@@ -0,0 +1,95 @@
#pragma once
#include <Core/fig_string.hpp>
#include <unordered_set>
#include <variant>
#include <map>
namespace Fig
{
class TypeInfo final
{
private:
size_t id;
std::map<FString, size_t> &getTypeMap()
{
static std::map<FString, size_t> typeMap;
return typeMap;
}
public:
friend class TypeInfoHash;
FString name;
FString toString() const { return name; }
size_t getInstanceID() const { return id; }
TypeInfo();
explicit TypeInfo(const FString &_name, bool reg = false);
TypeInfo(const TypeInfo &other) = default;
bool operator==(const TypeInfo &other) const { return id == other.id; }
};
class TypeInfoHash
{
public:
std::size_t operator()(const TypeInfo &ti) const { return std::hash<size_t>{}(ti.id); }
};
// class Value;
namespace ValueType
{
extern const TypeInfo Any;
extern const TypeInfo Null;
extern const TypeInfo Int;
extern const TypeInfo String;
extern const TypeInfo Bool;
extern const TypeInfo Double;
extern const TypeInfo Function;
extern const TypeInfo StructType;
extern const TypeInfo StructInstance;
extern const TypeInfo List;
extern const TypeInfo Map;
extern const TypeInfo Module;
extern const TypeInfo InterfaceType;
using IntClass = int64_t;
using DoubleClass = double;
using BoolClass = bool;
using NullClass = std::monostate;
using StringClass = FString;
inline bool isTypeBuiltin(const TypeInfo &type)
{
static const std::unordered_set<TypeInfo, TypeInfoHash> builtinTypes{Any,
Null,
Int,
String,
Bool,
Double,
Function,
StructType,
StructInstance,
List,
Map,
Module,
InterfaceType};
return builtinTypes.contains(type);
}
}; // namespace ValueType
}; // namespace Fig
namespace std
{
template <>
struct hash<Fig::TypeInfo>
{
size_t operator()(const Fig::TypeInfo &t) { return std::hash<size_t>{}(t.getInstanceID()); }
};
}; // namespace std

View File

@@ -3,12 +3,13 @@
#include <Ast/AccessModifier.hpp> #include <Ast/AccessModifier.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/value_forward.hpp>
#include <memory> #include <memory>
namespace Fig namespace Fig
{ {
class Object;
using ObjectPtr = std::shared_ptr<Object>;
struct VariableSlot struct VariableSlot
{ {
FString name; FString name;

View File

@@ -0,0 +1,149 @@
#pragma once
#include <Ast/functionParameters.hpp>
#include <Evaluator/Context/context_forward.hpp>
#include <atomic>
#include <functional>
#include <memory>
#include <vector>
namespace Fig
{
class Object;
class Function
{
public:
std::size_t id;
FString name;
enum FnType
{
Normal,
Builtin,
MemberType
} type;
union
{
struct
{
Ast::FunctionParameters paras;
TypeInfo retType;
Ast::BlockStatement body;
};
std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> builtin;
std::function<std::shared_ptr<Object>(std::shared_ptr<Object>,
const std::vector<std::shared_ptr<Object>> &)>
mtFn;
};
int builtinParamCount = -1;
std::shared_ptr<Context> closureContext;
// ===== Constructors =====
Function() : id(nextId()), type(Normal)
{
// 需要初始化 union
new (&paras) Ast::FunctionParameters();
new (&retType) TypeInfo();
new (&body) Ast::BlockStatement();
}
Function(const FString &_name,
Ast::FunctionParameters _paras,
TypeInfo _retType,
Ast::BlockStatement _body,
ContextPtr _closureContext) :
id(nextId()), // 分配唯一 ID
name(_name),
paras(std::move(_paras)),
retType(std::move(_retType)),
body(std::move(_body)),
closureContext(std::move(_closureContext))
{
type = Normal;
}
Function(const FString &_name, std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> fn, int argc) :
id(nextId()), name(_name), type(Builtin), builtin(fn), builtinParamCount(argc)
{
type = Builtin;
}
Function(const FString &_name, std::function<std::shared_ptr<Object>(std::shared_ptr<Object>,
const std::vector<std::shared_ptr<Object>> &)> fn,
int argc) :
id(nextId()), name(_name), type(MemberType), mtFn(fn), builtinParamCount(argc)
{
type = MemberType;
}
// ===== Copy / Move =====
Function(const Function &other) { copyFrom(other); }
Function &operator=(const Function &other)
{
if (this != &other)
{
destroy();
copyFrom(other);
}
return *this;
};
~Function() { destroy(); }
// ===== Comparison =====
bool operator==(const Function &other) const noexcept { return id == other.id; }
bool operator!=(const Function &other) const noexcept { return !(*this == other); }
private:
static std::size_t nextId()
{
static std::atomic<std::size_t> counter{1};
return counter++;
}
void destroy()
{
switch (type)
{
case Normal:
paras.~FunctionParameters();
retType.~TypeInfo();
body.~shared_ptr();
break;
case Builtin: builtin.~function(); break;
case MemberType: mtFn.~function(); break;
}
}
void copyFrom(const Function &other)
{
name = other.name;
type = other.type;
id = nextId(); // 每个复制都生成新的ID
builtinParamCount = other.builtinParamCount;
closureContext = other.closureContext;
switch (type)
{
case Normal:
new (&paras) Ast::FunctionParameters(other.paras);
new (&retType) TypeInfo(other.retType);
new (&body) Ast::BlockStatement(other.body);
break;
case Builtin:
new (&builtin) std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)>(
other.builtin);
break;
case MemberType:
new (&mtFn) std::function<std::shared_ptr<Object>(
std::shared_ptr<Object>, const std::vector<std::shared_ptr<Object>> &)>(other.mtFn);
break;
}
}
};
} // namespace Fig

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Ast/Statements/InterfaceDefSt.hpp> #include <Ast/Statements/InterfaceDefSt.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
#include <vector> #include <vector>

View File

@@ -2,7 +2,7 @@
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Context/context_forward.hpp> #include <Evaluator/Context/context_forward.hpp>
namespace Fig namespace Fig
{ {

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Context/context_forward.hpp> #include <Evaluator/Context/context_forward.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
namespace Fig namespace Fig
{ {

View File

@@ -3,9 +3,9 @@
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Ast/Statements/StructDefSt.hpp> #include <Ast/Statements/StructDefSt.hpp>
#include <Value/Type.hpp> #include <Evaluator/Value/Type.hpp>
#include <Context/context_forward.hpp> #include <Evaluator/Context/context_forward.hpp>
#include <atomic> #include <atomic>
#include <vector> #include <vector>

View File

@@ -1,12 +1,14 @@
#include <Value/Type.hpp> #include <Evaluator/Value/interface.hpp>
#include <Value/value.hpp> #include <Evaluator/Value/structType.hpp>
#include <Context/context.hpp> #include <Evaluator/Value/value_forward.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/value.hpp>
#include <Evaluator/Context/context.hpp>
// #include <iostream> // #include <iostream>
namespace Fig namespace Fig
{ {
std::map<FString, size_t> TypeInfo::typeMap = {};
TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement
id(1), name(FString(u8"Any")) id(1), name(FString(u8"Any"))
@@ -19,19 +21,17 @@ namespace Fig
// std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n"; // std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n";
if (reg) if (reg)
{ {
typeMap[name] = ++id_count; getTypeMap()[name] = ++id_count;
id = id_count; id = id_count;
} }
else else
{ {
if (!typeMap.contains(_name)) if (!getTypeMap().contains(_name))
{ {
throw RuntimeError(FString(std::format( throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString())));
"No type named '{}'",
_name.toBasicString())));
// *this = ValueType::String; // *this = ValueType::String;
} }
id = typeMap.at(name); // may throw id = getTypeMap().at(name); // may throw
} }
} }
@@ -41,10 +41,7 @@ namespace Fig
ObjectPtr value = key.value; ObjectPtr value = key.value;
const TypeInfo &type = value->getTypeInfo(); const TypeInfo &type = value->getTypeInfo();
if (type == ValueType::Int) if (type == ValueType::Int) { return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>()); }
{
return std::hash<ValueType::IntClass>{}(value->as<ValueType::IntClass>());
}
if (type == ValueType::Double) if (type == ValueType::Double)
{ {
return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>()); return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>());
@@ -61,10 +58,7 @@ namespace Fig
{ {
auto HashFields = [](std::vector<Field> fields) { auto HashFields = [](std::vector<Field> fields) {
size_t r = 0; size_t r = 0;
for (auto &f : fields) for (auto &f : fields) { r += std::hash<Field>{}(f); }
{
r += std::hash<Field>{}(f);
}
return r; return r;
}; };
const StructType &st = value->as<StructType>(); const StructType &st = value->as<StructType>();
@@ -73,19 +67,35 @@ namespace Fig
if (type == ValueType::StructInstance) if (type == ValueType::StructInstance)
{ {
const StructInstance &si = value->as<StructInstance>(); const StructInstance &si = value->as<StructInstance>();
return std::hash<TypeInfo>{}(si.parentType) + std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext))); return std::hash<TypeInfo>{}(si.parentType)
+ std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext)));
} }
assert(false); assert(false);
throw ""; // ignore warning throw ""; // ignore warning
} }
} }
FString prettyType(std::shared_ptr<const Object> obj) TypeInfo actualType(std::shared_ptr<const Object> obj)
{ {
auto t = obj->getTypeInfo(); auto t = obj->getTypeInfo();
if (t == ValueType::StructInstance)
return obj->as<StructInstance>().parentType.toString(); // dispatch builtin struct types (like Int{}, List{} e.g...)
return t.toString(); if (t == ValueType::StructType)
{
return obj->as<StructType>().type;
}
if (t == ValueType::InterfaceType)
{
return obj->as<InterfaceType>().type;
}
if (t == ValueType::StructInstance) return obj->as<StructInstance>().parentType;
return t;
}
FString prettyType(std::shared_ptr<const Object> obj)
{
return actualType(obj).toString();
} }
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
@@ -102,8 +112,6 @@ namespace Fig
const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12
const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13 const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13
bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx) bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx)
{ {
return ctx->hasImplRegisted(structType, interfaceType); return ctx->hasImplRegisted(structType, interfaceType);
@@ -111,28 +119,25 @@ namespace Fig
bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx) bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx)
{ {
if (expected == ValueType::Any) if (expected == ValueType::Any) return true;
return true;
TypeInfo actual = obj->getTypeInfo(); TypeInfo actual = obj->getTypeInfo();
if (obj->is<StructInstance>()) if (obj->is<StructType>())
{
const StructType &t = obj->as<StructType>();
if (expected == t.type) // the StructType typeinfo
{
return true;
}
}
else if (obj->is<StructInstance>())
{ {
const StructInstance &si = obj->as<StructInstance>(); const StructInstance &si = obj->as<StructInstance>();
if (si.parentType == expected) if (si.parentType == expected) { return true; }
{ if (implements(si.parentType, expected, ctx)) { return true; }
return true;
} }
if (implements(si.parentType, expected, ctx))
{
return true;
}
return false;
}
else
{
return expected == actual; return expected == actual;
} }
}
} // namespace Fig } // namespace Fig

View File

@@ -0,0 +1,754 @@
#pragma once
#include <Core/fig_string.hpp>
#include <Evaluator/Value/function.hpp>
#include <Evaluator/Value/interface.hpp>
#include <Evaluator/Value/structType.hpp>
#include <Evaluator/Value/structInstance.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Evaluator/Value/valueError.hpp>
#include <Evaluator/Value/module.hpp>
#include <Evaluator/Value/value_forward.hpp>
#include <cassert>
// #include <iostream>
#include <memory>
#include <unordered_set>
#include <variant>
#include <cmath>
#include <string>
#include <format>
#include <functional>
#include <unordered_map>
namespace Fig
{
inline bool isDoubleInteger(ValueType::DoubleClass d)
{
return std::floor(d) == d;
}
inline bool isNumberExceededIntLimit(ValueType::DoubleClass d)
{
static constexpr auto intMaxAsDouble =
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::max());
static constexpr auto intMinAsDouble =
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
return d > intMaxAsDouble || d < intMinAsDouble;
}
inline bool nearlyEqual(ValueType::DoubleClass l, ValueType::DoubleClass r, ValueType::DoubleClass epsilon = 1e-9)
{
return std::abs(l - r) < epsilon;
}
TypeInfo actualType(std::shared_ptr<const Object> obj);
FString prettyType(std::shared_ptr<const Object> obj);
bool operator==(const Object &, const Object &);
struct Element
{
ObjectPtr value;
Element(ObjectPtr _value) : value(_value) {}
bool operator==(const Element &other) const { return *value == *other.value; }
void deepCopy(const Element &e) { value = std::make_shared<Object>(*e.value); }
};
using List = std::vector<Element>;
struct ValueKey
{
ObjectPtr value;
ValueKey(ObjectPtr _value) : value(_value) {}
void deepCopy(const ValueKey &vk) { value = std::make_shared<Object>(*vk.value); }
};
struct ValueKeyHash
{
size_t operator()(const ValueKey &key) const;
};
using Map = std::unordered_map<ValueKey, ObjectPtr, ValueKeyHash>;
bool isTypeMatch(const TypeInfo &, ObjectPtr, ContextPtr);
bool implements(const TypeInfo &, const TypeInfo &, ContextPtr);
using BuiltinTypeMemberFn = std::function<ObjectPtr(ObjectPtr, std::vector<ObjectPtr>)>;
class Object : public std::enable_shared_from_this<Object>
{
public:
using VariantType = std::variant<ValueType::NullClass,
ValueType::IntClass,
ValueType::DoubleClass,
ValueType::StringClass,
ValueType::BoolClass,
Function,
StructType,
StructInstance,
List,
Map,
Module,
InterfaceType>;
static std::unordered_map<TypeInfo, std::unordered_map<FString, BuiltinTypeMemberFn>, TypeInfoHash>
getMemberTypeFunctions()
{
static const std::unordered_map<TypeInfo, std::unordered_map<FString, BuiltinTypeMemberFn>, TypeInfoHash>
memberTypeFunctions{
{ValueType::Null, {}},
{ValueType::Int, {}},
{ValueType::Double, {}},
{ValueType::String,
{
{u8"length",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(
FString(std::format("`length` expects 0 arguments, {} got", args.size())));
const FString &str = object->as<ValueType::StringClass>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(str.length()));
}},
{u8"replace",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 2)
throw RuntimeError(
FString(std::format("`replace` expects 2 arguments, {} got", args.size())));
FString &str = object->as<ValueType::StringClass>();
ObjectPtr arg1 = args[0];
ObjectPtr arg2 = args[1];
if (!arg1->is<ValueType::IntClass>())
{
throw RuntimeError(FString("`replace` arg 1 expects type Int"));
}
if (!arg2->is<ValueType::StringClass>())
{
throw RuntimeError(FString("`replace` arg 2 expects type String"));
}
str.realReplace(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::StringClass>());
return Object::getNullInstance();
}},
{u8"erase",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 2)
throw RuntimeError(
FString(std::format("`erase` expects 2 arguments, {} got", args.size())));
FString &str = object->as<ValueType::StringClass>();
ObjectPtr arg1 = args[0];
ObjectPtr arg2 = args[1];
if (!arg1->is<ValueType::IntClass>())
{
throw RuntimeError(FString("`erase` arg 1 expects type Int"));
}
if (!arg2->is<ValueType::IntClass>())
{
throw RuntimeError(FString("`erase` arg 2 expects type Int"));
}
ValueType::IntClass index = arg1->as<ValueType::IntClass>();
ValueType::IntClass n = arg2->as<ValueType::IntClass>();
if (index < 0 || n < 0)
{
throw RuntimeError(FString("`erase`: index and n must greater or equal to 0"));
}
if (index + n > str.length())
{
throw RuntimeError(FString("`erase`: length is not long enough to erase"));
}
str.realErase(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::IntClass>());
return Object::getNullInstance();
}},
{u8"insert",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 2)
throw RuntimeError(
FString(std::format("`insert` expects 2 arguments, {} got", args.size())));
FString &str = object->as<ValueType::StringClass>();
ObjectPtr arg1 = args[0];
ObjectPtr arg2 = args[1];
if (!arg1->is<ValueType::IntClass>())
{
throw RuntimeError(FString("`insert` arg 1 expects type Int"));
}
if (!arg2->is<ValueType::StringClass>())
{
throw RuntimeError(FString("`insert` arg 2 expects type String"));
}
str.realInsert(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::StringClass>());
return Object::getNullInstance();
}},
}},
{ValueType::Function, {}},
{ValueType::StructType, {}},
{ValueType::StructInstance, {}},
{ValueType::List,
{
{u8"length",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(
FString(std::format("`length` expects 0 arguments, {} got", args.size())));
const List &list = object->as<List>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(list.size()));
}},
{u8"get",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(
FString(std::format("`get` expects 1 arguments, {} got", args.size())));
ObjectPtr arg = args[0];
if (arg->getTypeInfo() != ValueType::Int)
throw RuntimeError(
FString(std::format("`get` argument 1 expects Int, {} got",
arg->getTypeInfo().toString().toBasicString())));
ValueType::IntClass i = arg->as<ValueType::IntClass>();
const List &list = object->as<List>();
if (i >= list.size()) return Object::getNullInstance();
return list[i].value;
}},
{u8"push",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(
FString(std::format("`push` expects 1 arguments, {} got", args.size())));
ObjectPtr arg = args[0];
List &list = object->as<List>();
list.push_back(arg);
return Object::getNullInstance();
}},
}},
{ValueType::Map,
{
{u8"get",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(
FString(std::format("`get` expects 1 arguments, {} got", args.size())));
ObjectPtr index = args[0];
const Map &map = object->as<Map>();
if (!map.contains(index)) return Object::getNullInstance();
return map.at(index);
}},
{u8"contains",
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 1)
throw RuntimeError(
FString(std::format("`contains` expects 1 arguments, {} got", args.size())));
ObjectPtr index = args[0];
const Map &map = object->as<Map>();
return std::make_shared<Object>(map.contains(index));
}},
}},
{ValueType::Module, {}},
{ValueType::InterfaceType, {}},
};
return memberTypeFunctions;
}
static std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash>
getMemberTypeFunctionsParas()
{
static const std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash>
memberTypeFunctionsParas{
{ValueType::Null, {}},
{ValueType::Int, {}},
{ValueType::Double, {}},
{ValueType::String,
{
{u8"length", 0},
{u8"replace", 2},
{u8"erase", 2},
{u8"insert", 2},
}},
{ValueType::Function, {}},
{ValueType::StructType, {}},
{ValueType::StructInstance, {}},
{ValueType::List, {{u8"length", 0}, {u8"get", 1}, {u8"push", 1}}},
{ValueType::Map,
{
{u8"get", 1},
{u8"contains", 1},
}},
{ValueType::Module, {}},
{ValueType::InterfaceType, {}},
};
return memberTypeFunctionsParas;
}
bool hasMemberFunction(const FString &name) const
{
return getMemberTypeFunctions().at(getTypeInfo()).contains(name);
}
BuiltinTypeMemberFn getMemberFunction(const FString &name) const
{
return getMemberTypeFunctions().at(getTypeInfo()).at(name);
}
int getMemberFunctionParaCount(const FString &name) const
{
return getMemberTypeFunctionsParas().at(getTypeInfo()).at(name);
}
VariantType data;
Object() : data(ValueType::NullClass{}) {}
Object(const ValueType::NullClass &n) : data(n) {}
Object(const ValueType::IntClass &i) : data(i) {}
explicit Object(const ValueType::DoubleClass &d) : data(d) {}
Object(const ValueType::StringClass &s) : data(s) {}
Object(const ValueType::BoolClass &b) : data(b) {}
Object(const Function &f) : data(f) {}
Object(const StructType &s) : data(s) {}
Object(const StructInstance &s) : data(s) {}
Object(const List &l) : data(l) {}
Object(const Map &m) : data(m) {}
Object(const Module &m) : data(m) {}
Object(const InterfaceType &i) : data(i) {}
Object(const Object &) = default;
Object(Object &&) noexcept = default;
Object &operator=(const Object &) = default;
Object &operator=(Object &&) noexcept = default;
static Object defaultValue(TypeInfo ti)
{
if (ti == ValueType::Int)
return Object(ValueType::IntClass(0));
else if (ti == ValueType::Double)
return Object(ValueType::DoubleClass(0.0));
else if (ti == ValueType::String)
return Object(ValueType::StringClass(u8""));
else if (ti == ValueType::Bool)
return Object(ValueType::BoolClass(false));
else if (ti == ValueType::List)
return Object(List{});
else if (ti == ValueType::Map)
return Object(Map{});
else
return *getNullInstance();
}
template <typename T>
bool is() const
{
return std::holds_alternative<T>(data);
}
template <typename T>
T &as()
{
return std::get<T>(data);
}
template <typename T>
const T &as() const
{
return std::get<T>(data);
}
static std::shared_ptr<Object> getNullInstance()
{
static std::shared_ptr<Object> n = std::make_shared<Object>(ValueType::NullClass{});
return n;
}
static std::shared_ptr<Object> getTrueInstance()
{
static std::shared_ptr<Object> t = std::make_shared<Object>(true);
return t;
}
static std::shared_ptr<Object> getFalseInstance()
{
static std::shared_ptr<Object> f = std::make_shared<Object>(false);
return f;
}
TypeInfo getTypeInfo() const
{
return std::visit(
[](auto &&val) -> TypeInfo {
using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, ValueType::NullClass>)
return ValueType::Null;
else if constexpr (std::is_same_v<T, ValueType::IntClass>)
return ValueType::Int;
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
return ValueType::Double;
else if constexpr (std::is_same_v<T, ValueType::StringClass>)
return ValueType::String;
else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
return ValueType::Bool;
else if constexpr (std::is_same_v<T, Function>)
return ValueType::Function;
else if constexpr (std::is_same_v<T, StructType>)
return ValueType::StructType;
else if constexpr (std::is_same_v<T, StructInstance>)
return ValueType::StructInstance;
else if constexpr (std::is_same_v<T, List>)
return ValueType::List;
else if constexpr (std::is_same_v<T, Map>)
return ValueType::Map;
else if constexpr (std::is_same_v<T, Module>)
return ValueType::Module;
else if constexpr (std::is_same_v<T, InterfaceType>)
return ValueType::InterfaceType;
else
return ValueType::Any;
},
data);
}
bool isNull() const { return is<ValueType::NullClass>(); }
bool isNumeric() const { return is<ValueType::IntClass>() || is<ValueType::DoubleClass>(); }
ValueType::DoubleClass getNumericValue() const
{
if (is<ValueType::IntClass>())
return static_cast<ValueType::DoubleClass>(as<ValueType::IntClass>());
else if (is<ValueType::DoubleClass>())
return as<ValueType::DoubleClass>();
else
throw RuntimeError(u8"getNumericValue: Not a numeric value");
}
FString toStringIO() const
{
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
return toString();
}
FString toString(std::unordered_set<const Object *> &visited) const
{
if (is<ValueType::NullClass>()) return FString(u8"null");
if (is<ValueType::IntClass>()) return FString(std::to_string(as<ValueType::IntClass>()));
if (is<ValueType::DoubleClass>()) return FString(std::format("{}", as<ValueType::DoubleClass>()));
if (is<ValueType::StringClass>()) return FString(u8"\"" + as<ValueType::StringClass>() + u8"\"");
if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
if (is<Function>())
return FString(std::format("<Function '{}'({}) at {:p}>",
as<Function>().name.toBasicString(),
as<Function>().id,
static_cast<const void *>(&as<Function>())));
if (is<StructType>())
return FString(std::format("<StructType '{}' at {:p}>",
as<StructType>().type.toString().toBasicString(),
static_cast<const void *>(&as<StructType>())));
if (is<StructInstance>())
return FString(std::format("<StructInstance '{}' at {:p}>",
as<StructInstance>().parentType.toString().toBasicString(),
static_cast<const void *>(&as<StructInstance>())));
if (is<List>())
{
if (visited.contains(this)) { return u8"[...]"; }
visited.insert(this);
FString output(u8"[");
const List &list = as<List>();
bool first_flag = true;
for (auto &ele : list)
{
if (!first_flag) output += u8", ";
output += ele.value->toString(visited);
first_flag = false;
}
output += u8"]";
return output;
}
if (is<Map>())
{
if (visited.contains(this)) { return u8"{...}"; }
visited.insert(this);
FString output(u8"{");
const Map &map = as<Map>();
bool first_flag = true;
for (auto &[key, value] : map)
{
if (!first_flag) output += u8", ";
output += key.value->toString(visited) + FString(u8" : ") + value->toString(visited);
first_flag = false;
}
output += u8"}";
return output;
}
if (is<Module>())
{
return FString(std::format("<Module '{}' at {:p}>",
as<Module>().name.toBasicString(),
static_cast<const void *>(&as<Module>())));
}
if (is<InterfaceType>())
{
return FString(std::format("<InterfaceType '{}' at {:p}>",
as<InterfaceType>().type.toString().toBasicString(),
static_cast<const void *>(&as<InterfaceType>())));
}
return FString(u8"<error>");
}
FString toString() const
{
std::unordered_set<const Object *> visited{};
return toString(visited);
}
private:
static std::string
makeTypeErrorMessage(const char *prefix, const char *op, const Object &lhs, const Object &rhs)
{
auto lhs_type = lhs.getTypeInfo().name.toBasicString();
auto rhs_type = rhs.getTypeInfo().name.toBasicString();
return std::format("{}: {} '{}' {}", prefix, lhs_type, op, rhs_type);
}
public:
// math
friend Object operator+(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() + rhs.getNumericValue();
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return Object(FString(lhs.as<ValueType::StringClass>() + rhs.as<ValueType::StringClass>()));
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs)));
}
friend Object operator-(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() - rhs.getNumericValue();
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
}
friend Object operator*(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() * rhs.getNumericValue();
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::IntClass>())
{
FString result;
const FString &l = lhs.as<ValueType::StringClass>();
for (size_t i = 0; i < rhs.getNumericValue(); ++i) { result += l; }
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
}
friend Object operator/(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs)));
if (lhs.isNumeric() && rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0) throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
// bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() / rnv;
// if (bothInt)
// return Object(static_cast<ValueType::IntClass>(result));
// int / int maybe decimals
// DO NOT convert it to INT
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
}
friend Object operator%(const Object &lhs, const Object &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
if (lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>())
{
ValueType::IntClass lv = lhs.as<ValueType::IntClass>();
ValueType::IntClass rv = lhs.as<ValueType::IntClass>();
if (rv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
ValueType::IntClass q = lv / rv;
ValueType::IntClass r = lv % rv;
if (r != 0 && ((lv < 0) != (rv < 0))) { q -= 1; }
return q;
}
if (lhs.isNumeric() && rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
auto result = std::fmod(lhs.getNumericValue(), rnv);
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
}
// logic
friend Object operator&&(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FString(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() && rhs.as<ValueType::BoolClass>());
}
friend Object operator||(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FString(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() || rhs.as<ValueType::BoolClass>());
}
friend Object operator!(const Object &v)
{
if (!v.is<ValueType::BoolClass>())
throw ValueError(
FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(!v.as<ValueType::BoolClass>());
}
friend Object operator-(const Object &v)
{
if (v.isNull()) throw ValueError(FString(u8"Unary minus cannot be applied to null"));
if (v.is<ValueType::IntClass>()) return Object(-v.as<ValueType::IntClass>());
if (v.is<ValueType::DoubleClass>()) return Object(-v.as<ValueType::DoubleClass>());
throw ValueError(
FString(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString())));
}
friend Object operator~(const Object &v)
{
if (!v.is<ValueType::IntClass>())
throw ValueError(
FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(~v.as<ValueType::IntClass>());
}
// comparison
friend bool operator==(const Object &lhs, const Object &rhs)
{
if (lhs.isNumeric() && rhs.isNumeric())
{
return nearlyEqual(lhs.getNumericValue(), rhs.getNumericValue());
}
return lhs.data == rhs.data;
}
friend bool operator!=(const Object &lhs, const Object &rhs) { return !(lhs == rhs); }
friend bool operator<(const Object &lhs, const Object &rhs)
{
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() < rhs.as<ValueType::StringClass>();
throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs)));
}
friend bool operator<=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs < rhs; }
friend bool operator>(const Object &lhs, const Object &rhs)
{
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() > rhs.as<ValueType::StringClass>();
throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
}
friend bool operator>=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs > rhs; }
// bitwise
friend Object bit_and(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FString(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() & rhs.as<ValueType::IntClass>());
}
friend Object bit_or(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FString(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() | rhs.as<ValueType::IntClass>());
}
friend Object bit_xor(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FString(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() ^ rhs.as<ValueType::IntClass>());
}
friend Object bit_not(const Object &v)
{
if (!v.is<ValueType::IntClass>())
throw ValueError(
FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
return Object(~v.as<ValueType::IntClass>());
}
friend Object shift_left(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FString(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() << rhs.as<ValueType::IntClass>());
}
friend Object shift_right(const Object &lhs, const Object &rhs)
{
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FString(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() >> rhs.as<ValueType::IntClass>());
}
friend Object power(const Object &base, const Object &exp)
{
if (base.isNull() || exp.isNull())
throw ValueError(FString(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp)));
if (base.isNumeric() && exp.isNumeric())
{
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
auto result = std::pow(base.getNumericValue(), exp.getNumericValue());
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
return Object(result);
}
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "**", base, exp)));
}
};
inline bool isBoolObjectTruthy(ObjectPtr obj)
{
assert(obj->is<bool>());
return obj->as<bool>();
}
using RvObject = ObjectPtr;
inline bool operator==(const ValueKey &l, const ValueKey &r)
{
return *l.value == *r.value;
}
} // namespace Fig

View File

@@ -0,0 +1,10 @@
#pragma once
#include <memory>
namespace Fig
{
class Object;
using ObjectPtr = std::shared_ptr<Object>;
}; // namespace Fig

File diff suppressed because it is too large Load Diff

View File

@@ -1,61 +1,33 @@
#include "Ast/Statements/ImplementSt.hpp" #pragma once
#include "Ast/Statements/InterfaceDefSt.hpp" #include "Ast/AccessModifier.hpp"
#include "Value/Type.hpp" #include "Ast/Expressions/BinaryExpr.hpp"
#include "Ast/Expressions/ValueExpr.hpp"
#include "Ast/Expressions/VarExpr.hpp"
#include "Ast/Statements/ControlSt.hpp"
#include "Ast/astBase.hpp"
#include "Ast/functionParameters.hpp"
#include "Value/value.hpp"
#include <Ast/Expressions/FunctionCall.hpp>
#include <Ast/Expressions/InitExpr.hpp>
#include <Ast/Statements/ImplementSt.hpp>
#include <Ast/Statements/InterfaceDefSt.hpp>
#include <Evaluator/Value/Type.hpp>
#include <Ast/ast.hpp> #include <Ast/ast.hpp>
#include <Context/context.hpp> #include <Evaluator/Context/context.hpp>
#include <Error/error.hpp> #include <Error/error.hpp>
#include <Module/builtins.hpp> #include <Module/builtins.hpp>
#include <Value/LvObject.hpp> #include <Evaluator/Value/LvObject.hpp>
#include <cstddef>
#include <filesystem> #include <filesystem>
#include <Evaluator/Core/StatementResult.hpp>
#include <Evaluator/Core/ExprResult.hpp>
#include <memory>
#include <source_location>
namespace Fig namespace Fig
{ {
struct StatementResult
{
ObjectPtr result;
enum class Flow
{
Normal,
Return,
Break,
Continue,
Error
} flow;
StatementResult(ObjectPtr val, Flow f = Flow::Normal) :
result(val), flow(f)
{
}
static StatementResult normal(ObjectPtr val = Object::getNullInstance())
{
return StatementResult(val, Flow::Normal);
}
static StatementResult returnFlow(ObjectPtr val)
{
return StatementResult(val, Flow::Return);
}
static StatementResult breakFlow()
{
return StatementResult(Object::getNullInstance(), Flow::Break);
}
static StatementResult continueFlow()
{
return StatementResult(Object::getNullInstance(), Flow::Continue);
}
static StatementResult errorFlow(ObjectPtr val)
{
return StatementResult(val, Flow::Error);
}
bool isNormal() const { return flow == Flow::Normal; }
bool shouldReturn() const { return flow == Flow::Return; }
bool shouldBreak() const { return flow == Flow::Break; }
bool shouldContinue() const { return flow == Flow::Continue; }
bool isError() const { return flow == Flow::Error; }
};
class Evaluator class Evaluator
{ {
private: private:
@@ -63,11 +35,11 @@ namespace Fig
public: public:
FString sourcePath; FString sourcePath;
std::vector<FString> sourceLines;
void SetSourcePath(const FString &sp) void SetSourcePath(const FString &sp) { sourcePath = sp; }
{
sourcePath = sp; void SetSourceLines(const std::vector<FString> &sl) { sourceLines = sl; }
}
void SetGlobalContext(ContextPtr ctx) void SetGlobalContext(ContextPtr ctx)
{ {
@@ -75,59 +47,98 @@ namespace Fig
global = ctx; global = ctx;
} }
void CreateGlobalContext() void CreateGlobalContext() { global = std::make_shared<Context>(FString(u8"<Global>")); }
{
global = std::make_shared<Context>(
FString(u8"<Global>"));
}
void RegisterBuiltins() // only function void RegisterBuiltins() // only function
{ {
assert(global != nullptr); assert(global != nullptr);
for (auto &[name, fn] : Builtins::builtinFunctions) for (auto &[name, fn] : Builtins::getBuiltinFunctions())
{ {
int argc = Builtins::getBuiltinFunctionParamCount(name); int argc = Builtins::getBuiltinFunctionParamCount(name);
Function f(fn, argc); Function f(name, fn, argc);
global->def( global->def(name, ValueType::Function, AccessModifier::Const, std::make_shared<Object>(f));
name,
ValueType::Function,
AccessModifier::Const,
std::make_shared<Object>(f));
} }
global->setImplRecord(
Builtins::getTypeErrorStructTypeInfo(),
Builtins::getErrorInterfaceTypeInfo(),
ImplRecord{
.interfaceType = Builtins::getErrorInterfaceTypeInfo(),
.structType = Builtins::getTypeErrorStructTypeInfo(),
.implMethods = {
{u8"toString",
Function(
u8"toString",
Ast::FunctionParameters{},
ValueType::String,
std::make_shared<Ast::BlockStatementAst>(std::vector<Ast::Statement>(
{std::make_shared<Ast::ReturnSt>(std::make_shared<Ast::BinaryExprAst>(
std::make_shared<Ast::ValueExprAst>(std::make_shared<Object>(u8"TypeError: ")),
Ast::Operator::Add,
std::make_shared<Ast::FunctionCallExpr>(
std::make_shared<Ast::VarExprAst>(u8"getErrorMessage"),
Ast::FunctionArguments{})))})),
nullptr)},
{u8"getErrorClass",
Function(u8"getErrorClass",
Ast::FunctionParameters{},
ValueType::String,
std::make_shared<Ast::BlockStatementAst>(std::vector<Ast::Statement>(
{std::make_shared<Ast::ReturnSt>(std::make_shared<Ast::ValueExprAst>(
std::make_shared<Object>(FString(u8"TypeError"))))})),
nullptr)},
{u8"getErrorMessage",
Function(u8"getErrorMessage",
Ast::FunctionParameters{},
ValueType::String,
std::make_shared<Ast::BlockStatementAst>(std::vector<Ast::Statement>(
{std::make_shared<Ast::ReturnSt>(std::make_shared<Ast::VarExprAst>(u8"msg"))})),
nullptr)},
}});
} }
void RegisterBuiltinsValue() void RegisterBuiltinsValue()
{ {
assert(global != nullptr); assert(global != nullptr);
for (auto &[name, val] : Builtins::builtinValues) for (auto &[name, val] : Builtins::getBuiltinValues())
{ {
global->def( global->def(name, val->getTypeInfo(), AccessModifier::Const, val);
name,
val->getTypeInfo(),
AccessModifier::Const,
val);
} }
} }
bool isInterfaceSignatureMatch(const Ast::ImplementMethod &, const Ast::InterfaceMethod &); bool isInterfaceSignatureMatch(const Ast::ImplementMethod &, const Ast::InterfaceMethod &);
/* Left-value eval*/ ObjectPtr genTypeError(const FString &_msg,
LvObject evalVarExpr(Ast::VarExpr, ContextPtr); const Ast::AstBase &_ast,
LvObject evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b ContextPtr ctx,
LvObject evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b] std::source_location loc = std::source_location::current())
{
ContextPtr stCtx = std::make_shared<Context>(u8"<TypeError Instance>");
stCtx->def(u8"msg", ValueType::String, AccessModifier::Const, std::make_shared<Object>(_msg));
return std::make_shared<Object>(StructInstance(Builtins::getTypeErrorStructTypeInfo(), stCtx));
}
LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b] /* Left-value eval*/
ExprResult evalVarExpr(Ast::VarExpr, ContextPtr); // identifier: a, b, c
ExprResult evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b
ExprResult evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b]
ExprResult evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b]
/* Right-value eval*/ /* Right-value eval*/
ExprResult evalInitExpr(Ast::InitExpr, ContextPtr);
ExprResult evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
ExprResult evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
ExprResult evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *.... ExprResult executeFunction(const Function &fn, const Ast::FunctionCallArgs &, ContextPtr); // fn, fn context
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
RvObject evalFunctionCall(const Function &, const Ast::FunctionArguments &, const FString &, ContextPtr); // function call ExprResult evalFunctionCall(const Ast::FunctionCall &,
RvObject eval(Ast::Expression, ContextPtr); ContextPtr); // function call
ExprResult eval(Ast::Expression, ContextPtr);
StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block
StatementResult evalStatement(Ast::Statement, ContextPtr); // statement StatementResult evalStatement(Ast::Statement, ContextPtr); // statement
@@ -139,6 +150,8 @@ namespace Fig
StatementResult Run(std::vector<Ast::AstBase>); // Entry StatementResult Run(std::vector<Ast::AstBase>); // Entry
void handle_error(const StatementResult &, const Ast::Statement &, const ContextPtr &);
void printStackTrace(); void printStackTrace();
}; };
}; // namespace Fig }; // namespace Fig

View File

@@ -10,18 +10,31 @@ namespace Fig
public: public:
FString typeName; FString typeName;
using AddressableError::AddressableError; using AddressableError::AddressableError;
EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) EvaluatorError(FString _typeName,
FString msg,
Ast::AstBase ast,
std::source_location loc = std::source_location::current())
{ {
message = msg; message = msg;
line = ast->getAAI().line;
column = ast->getAAI().column;
src_loc = std::move(loc); src_loc = std::move(loc);
typeName = std::move(_typeName); typeName = std::move(_typeName);
if (ast != nullptr)
{
line = ast->getAAI().line;
column = ast->getAAI().column;
sourcePath = *ast->getAAI().sourcePath;
sourceLines = *ast->getAAI().sourceLines;
} }
EvaluatorError(FString _typeName, std::string_view msg, Ast::AstBase ast, std::source_location loc = std::source_location::current())
}
EvaluatorError(FString _typeName,
std::string_view msg,
Ast::AstBase ast,
std::source_location loc = std::source_location::current())
{ {
message = FString::fromBasicString(std::string(msg.data())); message = FString::fromBasicString(std::string(msg.data()));
line = ast->getAAI().line; line = ast->getAAI().line;
@@ -30,13 +43,11 @@ namespace Fig
src_loc = std::move(loc); src_loc = std::move(loc);
typeName = std::move(_typeName); typeName = std::move(_typeName);
sourcePath = *ast->getAAI().sourcePath;
sourceLines = *ast->getAAI().sourceLines;
} }
virtual FString getErrorType() const override virtual FString getErrorType() const override { return typeName; }
{
return typeName;
}
};
}; };
}; // namespace Fig

62
src/IR/IR.hpp Normal file
View File

@@ -0,0 +1,62 @@
#pragma once
#include <Core/fig_string.hpp>
#include <vector>
#include <cstdint>
namespace Fig::IR
{
using Reg = uint16_t;
using Label = uint32_t;
constexpr Reg INVALID_REG = 0xFFFF;
enum class Op : uint8_t
{
// ---- control ----
Nop,
Jmp,
Br, // conditional branch
Ret,
Call,
// ---- arithmetic ----
Add,
Sub,
Mul,
Div,
// ---- compare ----
Lt,
Le,
Gt,
Ge,
Eq,
// ---- data ----
LoadImm, // immediate -> reg
Mov,
};
struct Inst
{
Op op;
Reg dst; // 结果寄存器
Reg a; // operand a
Reg b; // operand b
int64_t imm; // immediate / jump offset
};
struct Function
{
FString name;
uint16_t paramCount;
uint16_t localCount; // 不含参数
uint16_t regCount; // param + locals + temps
std::vector<Inst> code;
};
};

74
src/IR/IRInterpreter.hpp Normal file
View File

@@ -0,0 +1,74 @@
#pragma once
#include <IR/IR.hpp>
#include <cassert>
namespace Fig::IR
{
struct VirtualMachine
{
std::vector<Function *> functions;
int64_t execute(Function *fn, const int64_t *args)
{
assert(fn != nullptr);
std::vector<int64_t> regs(fn->regCount);
// load params
size_t ip = 0;
while (ip < fn->code.size())
{
const Inst &ins = fn->code[ip++];
switch (ins.op)
{
case Op::Nop: break;
case Op::LoadImm: regs[ins.dst] = ins.imm; break;
case Op::Mov: regs[ins.dst] = regs[ins.a]; break;
case Op::Add: regs[ins.dst] = regs[ins.a] + regs[ins.b]; break;
case Op::Sub: regs[ins.dst] = regs[ins.a] - regs[ins.b]; break;
case Op::Mul: regs[ins.dst] = regs[ins.a] * regs[ins.b]; break;
case Op::Div: regs[ins.dst] = regs[ins.a] / regs[ins.b]; break;
case Op::Lt: regs[ins.dst] = regs[ins.a] < regs[ins.b]; break;
case Op::Le: regs[ins.dst] = regs[ins.a] <= regs[ins.b]; break;
case Op::Gt: regs[ins.dst] = regs[ins.a] > regs[ins.b]; break;
case Op::Ge: regs[ins.dst] = regs[ins.a] >= regs[ins.b]; break;
case Op::Eq: regs[ins.dst] = regs[ins.a] == regs[ins.b]; break;
case Op::Jmp: ip = static_cast<size_t>(static_cast<int64_t>(ip) + ins.imm); break;
case Op::Br:
if (regs[ins.a] == 0) { ip = static_cast<size_t>(static_cast<int64_t>(ip) + ins.imm); }
break;
case Op::Call: {
// currently supports 1-arg call via reg a
Function *callee = functions.at(static_cast<size_t>(ins.imm));
int64_t arg0 = regs[ins.a];
int64_t ret = execute(callee, &arg0);
regs[ins.dst] = ret;
break;
}
case Op::Ret: return regs[ins.a];
}
}
// unreachable normally
return 0;
}
};
}; // namespace Fig::IR

14
src/IR/ir_test_main.cpp Normal file
View File

@@ -0,0 +1,14 @@
#include <IR/IRInterpreter.hpp>
#include <IR/IR.hpp>
int main()
{
using namespace Fig;
IR::VirtualMachine vm;
using namespace IR;
IR::Inst fib_ir[] = {
};
}

View File

@@ -6,6 +6,14 @@
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Utils/utils.hpp> #include <Utils/utils.hpp>
#if 0
#include <iostream> // debug
#endif
#ifndef SourceInfo
#define SourceInfo(ptr) (ptr->sourcePath), (ptr->sourceLines)
#endif
namespace Fig namespace Fig
{ {
@@ -58,7 +66,10 @@ namespace Fig
{FString(u8"["), TokenType::LeftBracket}, {FString(u8"["), TokenType::LeftBracket},
{FString(u8"]"), TokenType::RightBracket}, {FString(u8"]"), TokenType::RightBracket},
{FString(u8"{"), TokenType::LeftBrace}, {FString(u8"{"), TokenType::LeftBrace},
{FString(u8"}"), TokenType::RightBrace}}; {FString(u8"}"), TokenType::RightBrace},
{FString(u8"?"), TokenType::Question},
{FString(u8"!"), TokenType::Not},
};
const std::unordered_map<FString, TokenType> Lexer::keyword_map{ const std::unordered_map<FString, TokenType> Lexer::keyword_map{
{FString(u8"and"), TokenType::And}, {FString(u8"and"), TokenType::And},
@@ -73,6 +84,7 @@ namespace Fig
{FString(u8"for"), TokenType::For}, {FString(u8"for"), TokenType::For},
{FString(u8"if"), TokenType::If}, {FString(u8"if"), TokenType::If},
{FString(u8"else"), TokenType::Else}, {FString(u8"else"), TokenType::Else},
{FString(u8"new"), TokenType::New},
{FString(u8"struct"), TokenType::Struct}, {FString(u8"struct"), TokenType::Struct},
{FString(u8"interface"), TokenType::Interface}, {FString(u8"interface"), TokenType::Interface},
{FString(u8"impl"), TokenType::Implement}, {FString(u8"impl"), TokenType::Implement},
@@ -85,6 +97,7 @@ namespace Fig
{FString(u8"catch"), TokenType::Catch}, {FString(u8"catch"), TokenType::Catch},
{FString(u8"throw"), TokenType::Throw}, {FString(u8"throw"), TokenType::Throw},
{FString(u8"Finally"), TokenType::Finally}, {FString(u8"Finally"), TokenType::Finally},
{FString(u8"as"), TokenType::As},
// {FString(u8"Null"), TokenType::TypeNull}, // {FString(u8"Null"), TokenType::TypeNull},
// {FString(u8"Int"), TokenType::TypeInt}, // {FString(u8"Int"), TokenType::TypeInt},
@@ -149,7 +162,7 @@ namespace Fig
while (hasNext()) while (hasNext())
{ {
UTF8Char c = *it; UTF8Char c = *it;
if (c == U'"' || c == U'\n') if (c == U'"')
{ {
next(); next();
unterminated = false; unterminated = false;
@@ -159,7 +172,7 @@ namespace Fig
{ {
if (it.isEnd()) if (it.isEnd())
{ {
error = SyntaxError(u8"Unterminated FString", this->line, it.column()); error = SyntaxError(u8"Unterminated FString", this->line, it.column(), SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
next(); next();
@@ -196,12 +209,11 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FString( error = SyntaxError(FString(std::format("Unsupported escape character: {}",
std::format(
"Unsupported escape character: {}",
FString(ec.getString()).toBasicString())), FString(ec.getString()).toBasicString())),
this->line, this->line,
it.column()); it.column(),
SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
} }
@@ -213,7 +225,7 @@ namespace Fig
} }
if (unterminated) if (unterminated)
{ {
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col); error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
return Token(str, TokenType::LiteralString); return Token(str, TokenType::LiteralString);
@@ -240,7 +252,7 @@ namespace Fig
} }
if (unterminated) if (unterminated)
{ {
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col); error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
return Token(str, TokenType::LiteralString); return Token(str, TokenType::LiteralString);
@@ -271,7 +283,7 @@ namespace Fig
{ {
if (it.isEnd()) if (it.isEnd())
{ {
error = SyntaxError(u8"Unterminated FString", this->line, it.column()); error = SyntaxError(u8"Unterminated FString", this->line, it.column(), SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
next(); next();
@@ -313,12 +325,11 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FString( error = SyntaxError(FString(std::format("Unsupported escape character: {}",
std::format(
"Unsupported escape character: {}",
FString(ec.getString()).toBasicString())), FString(ec.getString()).toBasicString())),
this->line, this->line,
it.column()); it.column(),
SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
} }
@@ -330,7 +341,7 @@ namespace Fig
} }
if (unterminated) if (unterminated)
{ {
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col); error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
return Token(str, TokenType::LiteralString); return Token(str, TokenType::LiteralString);
@@ -339,21 +350,27 @@ namespace Fig
{ {
FString numStr; FString numStr;
bool hasPoint = false; bool hasPoint = false;
// 负号(减号) 直接交由 scanSymbol处理在parser中被分类->与数字结合/变为操作数
while (hasNext()) while (hasNext())
{ {
UTF8Char ch = *it; UTF8Char ch = *it;
if (ch.isDigit() or ch == U'e') // . / e / - for scientific counting
if (ch.isDigit() || ch == U'e')
{ {
numStr += ch.getString(); numStr += ch.getString();
next(); next();
} }
else if (ch == U'-' and numStr.ends_with(U'-')) else if (ch == U'-' && !numStr.empty() && (numStr.ends_with(U'e') || numStr.ends_with(U'E')))
{ {
numStr += ch.getString(); numStr += ch.getString();
next(); next();
} }
else if (ch == U'.' and not hasPoint) else if (ch == U'+' && !numStr.empty() && (numStr.ends_with(U'e') || numStr.ends_with(U'E')))
{
numStr += ch.getString();
next();
}
else if (ch == U'.' && !hasPoint)
{ {
hasPoint = true; hasPoint = true;
numStr += ch.getString(); numStr += ch.getString();
@@ -364,22 +381,93 @@ namespace Fig
break; break;
} }
} }
// Numbers in Fig-lang if (numStr.empty()) { return IllegalTok; }
/*
114514 if (numStr.ends_with(U'e'))
1145.14
1.14e3 -> 1140
1.14e-3 -> 0.00114
.3 -> 0.3
*/
// checking legality
if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数
{ {
error = SyntaxError(FString( error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
std::format("Ellegal number literal: {}", numStr.toBasicString())), this->line,
this->line, it.column()); it.column(),
SourceInfo(this));
return IllegalTok; return IllegalTok;
} }
bool hasDigit = false;
for (auto it = numStr.begin(); it != numStr.end(); ++it)
{
if (isdigit(*it))
{
hasDigit = true;
break;
}
}
if (!hasDigit)
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
size_t ePos = numStr.find(U'e');
if (ePos != FString::npos)
{
if (ePos == 0)
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
if (ePos + 1 >= numStr.length())
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
bool hasDigitAfterE = false;
for (size_t i = ePos + 1; i < numStr.length(); ++i)
{
UTF8Char c = std::u8string(1,numStr[i]);
if (c == U'+' || c == U'-')
{
if (i != ePos + 1)
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
continue;
}
if (c.isDigit()) { hasDigitAfterE = true; }
else
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
}
if (!hasDigitAfterE)
{
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
this->line,
it.column(),
SourceInfo(this));
return IllegalTok;
}
}
return Token(numStr, TokenType::LiteralNumber); return Token(numStr, TokenType::LiteralNumber);
} }
Token Lexer::scanSymbol() Token Lexer::scanSymbol()
@@ -400,9 +488,10 @@ namespace Fig
if (!startsWith(sym)) if (!startsWith(sym))
{ {
error = SyntaxError( error = SyntaxError(FString(std::format("No such operator: {}", sym.toBasicString())),
FString(std::format("No such operator: {}", sym.toBasicString())), this->line,
this->line, it.column()); it.column(),
SourceInfo(this));
next(); next();
return IllegalTok; return IllegalTok;
} }
@@ -428,13 +517,14 @@ namespace Fig
if (!symbol_map.contains(sym)) if (!symbol_map.contains(sym))
{ {
error = SyntaxError( error = SyntaxError(FString(std::format("No such operator: {}", sym.toBasicString())),
FString(std::format("No such operator: {}", sym.toBasicString())), this->line,
this->line, it.column()); it.column(),
SourceInfo(this));
next(); next();
return IllegalTok; return IllegalTok;
} }
// std::cerr << Token(sym, symbol_map.at(sym)).toString().toBasicString() << '\n;
next(); next();
return Token(sym, symbol_map.at(sym)); return Token(sym, symbol_map.at(sym));
} }
@@ -490,7 +580,8 @@ namespace Fig
if (!terminated) if (!terminated)
{ {
error = SyntaxError(FString(u8"Unterminated multiline comment"), this->line, it.column()); error =
SyntaxError(FString(u8"Unterminated multiline comment"), this->line, it.column(), SourceInfo(this));
next(); next();
return IllegalTok; return IllegalTok;
} }
@@ -522,13 +613,13 @@ namespace Fig
if (!hasNext()) if (!hasNext())
{ {
next(); next();
// return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column); return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
} }
c = it.peek(); c = it.peek();
if (c != U'/' and c != U'*') if (c != U'/' and c != U'*')
{ {
next(); next();
// return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column); return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
} }
scanComments().setPos(last_line, last_column); scanComments().setPos(last_line, last_column);
return nextToken(); return nextToken();
@@ -561,9 +652,11 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FString( error =
std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())), SyntaxError(FString(std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())),
this->line, it.column()); this->line,
it.column(),
SourceInfo(this));
if (hasNext()) if (hasNext())
{ {
next(); next();

View File

@@ -23,6 +23,9 @@ namespace Fig
SyntaxError error; SyntaxError error;
UTF8Iterator it; UTF8Iterator it;
FString sourcePath;
std::vector<FString> sourceLines;
std::vector<Warning> warnings; std::vector<Warning> warnings;
size_t last_line, last_column, column = 1; size_t last_line, last_column, column = 1;
@@ -60,8 +63,8 @@ namespace Fig
static const std::unordered_map<FString, TokenType> symbol_map; static const std::unordered_map<FString, TokenType> symbol_map;
static const std::unordered_map<FString, TokenType> keyword_map; static const std::unordered_map<FString, TokenType> keyword_map;
inline Lexer(const FString &_source) : inline Lexer(const FString &_source, const FString &_sourcePath, const std::vector<FString> &_sourceLines) :
source(_source), it(source) source(_source), it(source), sourcePath(_sourcePath), sourceLines(_sourceLines)
{ {
line = 1; line = 1;
} }

View File

@@ -0,0 +1,3 @@
#pragma once
#include "File/File.hpp"

View File

@@ -0,0 +1,74 @@
#pragma once
#include <cassert>
#include <cstdint>
#include <fstream>
#include <limits>
#include <vector>
namespace Fig::CppLibrary
{
using FileIDType = uint16_t;
struct File
{
FileIDType id;
std::fstream *fs;
};
class FileManager
{
private:
std::vector<File *> handlers;
std::vector<FileIDType> free_handlers;
FileIDType allocated = 0;
public:
static constexpr FileIDType MAX_HANDLERS = std::numeric_limits<FileIDType>::max();
static constexpr unsigned int MAX_FILE_BUF = 961200; // bytes
FileIDType AllocFile(std::fstream *fs)
{
FileIDType id = allocated++;
File *f = new File{.id = id, .fs = fs};
handlers.push_back(f);
return id;
}
void CloseFile(FileIDType id)
{
assert(id < allocated && "CloseHandler: id out of range");
File *f = handlers[id];
if (f == nullptr) { return; }
f->fs->close();
delete f->fs;
delete f;
free_handlers.push_back(id);
handlers[id] = nullptr;
}
File *GetNextFreeFile()
{
// if there is no free handler, create a new one
if (free_handlers.size() > 0)
{
FileIDType id = *free_handlers.begin();
handlers[id] = new File{.id = id, .fs = new std::fstream};
free_handlers.erase(free_handlers.begin());
return handlers[id];
}
return handlers[AllocFile(new std::fstream)];
}
File *GetFile(FileIDType id)
{
assert(id < allocated && "GetFile: id out of range");
return handlers[id];
}
static FileManager &getInstance()
{
static FileManager fm;
return fm;
}
};
}; // namespace Fig::CppLibrary

View File

@@ -31,6 +31,65 @@ public interface Error
getErrorMessage() -> String; getErrorMessage() -> String;
} }
public struct TypeError
{
public msg: String;
}
impl Error for TypeError
{
toString()
{
return "TypeError: " + getErrorMessage();
}
getErrorClass()
{
return "TypeError";
}
getErrorMessage()
{
return msg;
}
}
// Operation interface
public interface Operation
{
// math
Add(T, T) -> T;
Sub(T, T) -> T;
Mul(T, T) -> T;
Div(T, T) -> T;
Mod(T, T) -> T;
Pow(T, T) -> T;
// logic
Neg(T) -> T;
Not(T) -> T;
And(T, T) -> T;
Or(T, T) -> T;
// comparision
Equal(T, T) -> T;
NotEqual(T, T) -> T;
LessThan(T, T) -> T;
LessEqual(T, T) -> T;
GreaterThan(T, T) -> T;
GreaterEqual(T, T) -> T;
Is(T, T) -> T;
// Bit
BitNot(T) -> T;
BitAnd(T, T) -> T;
BitOr(T, T) -> T;
BitXor(T, T) -> T;
ShiftLeft(T, T) -> T;
ShiftRight(T, T) -> T:
}
public struct Any {} public struct Any {}
public struct Int {} public struct Int {}
public struct Null {} public struct Null {}

View File

@@ -0,0 +1,101 @@
/*
Official Module `std.file`
Library/std/file/file.fig
Copyright © 2026 PuqiAR. All rights reserved.
*/
import _builtins;
import std.io;
public struct __OpenMode
{
public App: Int = 1;
public Ate: Int = 2;
public Bin: Int = 4;
public In: Int = 8;
public Out: Int = 16;
public Trunc: Int = 32;
public __openmode_to_string: Map =
{
1 : "App",
2 : "Ate",
4 : "Bin",
8 : "In",
16 : "Out",
32 : "Trunc"
};
}
public const OpenMode := new __OpenMode{};
public func repr_mode(mode: Int) -> String
{
return OpenMode.__openmode_to_string[mode];
}
public struct FileError
{
msg: String;
}
impl Error for FileError
{
getErrorClass()
{
return "FileError";
}
getErrorMessage()
{
return msg;
}
toString()
{
return getErrorClass() + ": " + msg;
}
}
public struct File
{
path: String;
mode: Int;
id: Int;
public func read() -> Any
{
return __fstdfile_read(id);
}
public func write(object: String) -> Null
{
__fstdfile_write(id, object);
}
public func close() -> Null
{
__fstdfile_close(id);
}
public func getID() -> Int
{
return id;
}
}
public func open(path: String, mode: Int)
{
const id := __fstdfile_open(path, mode);
if not __fstdfile_is_open(id)
{
throw new FileError{"File " + path + " open failed"};
}
return new File{
path: path,
mode: mode,
id: id
};
}

View File

@@ -5,19 +5,42 @@
Copyright © 2025 PuqiAR. All rights reserved. Copyright © 2025 PuqiAR. All rights reserved.
*/ */
import std.value; // `type` function and string_from // import std.value; // `type` function and string_from
struct FormatError
{
public msg: String;
}
impl Error for FormatError
{
getErrorClass()
{
return "FormatError";
}
getErrorMessage()
{
return getErrorClass() + ": " + msg;
}
toString()
{
return getErrorMessage();
}
}
public func format(objects ...) -> Any public func format(objects ...) -> Any
{ {
if objects.length() < 1 if objects.length() < 1
{ {
return null; throw new FormatError{"Require format string"};
} }
var fmt := objects[0]; var fmt := objects[0];
if value.type(fmt) != "String" var fmtType := type(fmt);
if fmtType != "String"
{ {
return null; throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
} }
var result := ""; var result := "";
@@ -33,7 +56,7 @@ public func format(objects ...) -> Any
{ {
if (i + 1 >= length) if (i + 1 >= length)
{ {
return null; throw new FormatError{"unclosed brace"};
} }
var nextChar = fmt[i + 1]; var nextChar = fmt[i + 1];
@@ -57,15 +80,15 @@ public func format(objects ...) -> Any
if endIndex == -1 if endIndex == -1
{ {
return null; throw new FormatError{"unclosed brace"};
} }
if argIndex >= objects.length() if argIndex >= objects.length()
{ {
return null; throw new FormatError{"require enough format expression"};
} }
result += value.string_from(objects[argIndex]); result += objects[argIndex] as String;
argIndex += 1; argIndex += 1;
i = endIndex + 1; i = endIndex + 1;
@@ -79,7 +102,7 @@ public func format(objects ...) -> Any
continue; continue;
} }
return null; throw new FormatError{"invalid format syntax"};
} }
else else
{ {
@@ -93,19 +116,20 @@ public func format(objects ...) -> Any
public func formatByListArgs(objects) -> Any public func formatByListArgs(objects) -> Any
{ {
if value.type(objects) != "List" if not (objects is List)
{ {
return null; return null;
} }
if objects.length() < 1 if objects.length() < 1
{ {
return null; throw new FormatError{"Require format string"};
} }
var fmt := objects[0]; var fmt := objects[0];
if value.type(fmt) != "String" var fmtType := type(fmt);
if fmtType != "String"
{ {
return null; throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
} }
var result := ""; var result := "";
@@ -121,7 +145,7 @@ public func formatByListArgs(objects) -> Any
{ {
if (i + 1 >= length) if (i + 1 >= length)
{ {
return null; throw new FormatError{"unclosed brace"};
} }
var nextChar = fmt[i + 1]; var nextChar = fmt[i + 1];
@@ -145,15 +169,15 @@ public func formatByListArgs(objects) -> Any
if endIndex == -1 if endIndex == -1
{ {
return null; throw new FormatError{"unclosed brace"};
} }
if argIndex >= objects.length() if argIndex >= objects.length()
{ {
return null; throw new FormatError{"require enough format expression"};
} }
result += value.string_from(objects[argIndex]); result += objects[argIndex] as String;
argIndex += 1; argIndex += 1;
i = endIndex + 1; i = endIndex + 1;
@@ -167,7 +191,7 @@ public func formatByListArgs(objects) -> Any
continue; continue;
} }
return null; throw new FormatError{"invalid format syntax"};
} }
else else
{ {

View File

@@ -43,7 +43,8 @@ public func println(objects...) -> Int
public func printf(objects...) -> Any public func printf(objects...) -> Any
{ {
__fstdout_print(formater.formatByListArgs(objects)); var result := formater.formatByListArgs(objects);
__fstdout_print(result);
} }
// inputs // inputs

View File

@@ -3,6 +3,12 @@
Library/std/std.fig Library/std/std.fig
*/ */
import io; // link std.io import io as std_io;
import value; // link std.type import value as std_value;
import math; // link std.math import math as std_math;
import test as std_test;
public const io := std_io; // link std.io
public const value := std_value; // link std.type
public const math := std_math; // link std.math
public const test := std_test; // link std.test

View File

@@ -0,0 +1,86 @@
/*
Official Module `std.test`
Library/std/test/test.fig
Copyright © 2025 PuqiAR. All rights reserved.
*/
import std.time;
import std.io;
// import std.value;
// import std.math;
// import std.formater;
// const library_load_time := time.now();
// io.println("All std libraries loaded! Cost:", library_load_time.toSeconds(), "s\n");
public struct Test
{
public case: String;
public fn: Function;
public expect_result: Any;
public func Run()
{
const start := time.now();
const result := fn();
const end := time.now();
const duration := new time.Time{ end.since(start) };
if result != expect_result
{
io.println("❌ Test '" + case + "'" + " failed");
io.println(" expect", expect_result, ", got", result);
return 1;
}
else
{
io.println("✔ Test '" + case + "'" + " succeed");
io.println(" result:", result);
return 0;
}
}
}
public struct Tester
{
public tests: List = [];
public func AddTest(test: Test)
{
tests.push(test);
}
public func TestAll()
{
const numtests := tests.length();
var success := 0;
var fail := 0;
for var i := 0; i < numtests; i += 1
{
io.printf("({}/{})", i + 1, numtests);
var result := tests[i].Run();
if result == 0
{
success += 1;
}
else
{
fail += 1;
}
}
io.println();
io.println("=" * 100);
io.println("All tests executed");
io.printf(" ({}/{}) success tested!\n", success, numtests);
io.printf(" ({}/{}) failed!\n", fail, numtests);
}
}

View File

@@ -0,0 +1,62 @@
/*
Official Module `time`
Library/time/time.fig
Copyright © 2026 PuqiAR. All rights reserved.
*/
import _builtins; // provides __ftime_now_ns (int64_t)
public struct Time
{
ns: Int; // int64_t, private
public func toNanos() -> Int
{
return ns;
}
public func toMicros() -> Int
{
return __fvalue_int_from(ns / 1000); // convert double to int
}
public func toMillis() -> Double
{
return ns / 1000 / 1000;
}
public func toSeconds() -> Double
{
return ns / 1000 / 1000 / 1000;
}
public func since(other: Time) -> Int
{
const time_ns := other.toNanos();
const result := ns - time_ns;
if result < 0
{
throw "time has been reversed! 😢";
}
return result;
}
// TODO: support `-` operator when Fig supports overload
// supported now! 26-2-2. PuqiAR
}
impl Operation for Time
{
Sub(l: Time, r: Time)
{
return new Time{
l.since(r)
};
}
}
public func now() -> Time
{
return new Time{__ftime_now_ns()};
}

View File

@@ -7,7 +7,7 @@
import _builtins; import _builtins;
public func type(object: Any) -> String public func _type(object: Any) -> String
{ {
return __fvalue_type(object); return __fvalue_type(object);
} }

Some files were not shown because too many files have changed in this diff Show More