Compare commits
25 Commits
v0.4.3-alp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e7f84593a | |||
| ca0396568b | |||
| a00be02359 | |||
| e0a76ea1da | |||
| e98beb03d7 | |||
| 966b6eb805 | |||
| 9310252adc | |||
| 1dadaca4cc | |||
| 537011df32 | |||
| 764c4269a5 | |||
| e1c765aade | |||
| 52b75f17da | |||
| 27e5de3ea2 | |||
| 1e1d6c3284 | |||
| 642ba33243 | |||
| 90d4134f73 | |||
| d897f41c57 | |||
| a05958319b | |||
| c0dd463b82 | |||
| edabd0aa19 | |||
| f1b30c8837 | |||
| 50ca68b1a4 | |||
|
|
ecc1bd1cc2 | ||
|
|
558ea194c7 | ||
|
|
b302cb2cc5 |
@@ -29,16 +29,22 @@ jobs:
|
||||
git clone https://git.fig-lang.cn/${{ github.repository }} .
|
||||
git checkout ${{ github.ref }}
|
||||
|
||||
- name: 设置版本
|
||||
- name: 设置版本和提交信息
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
VERSION="${{ inputs.version }}"
|
||||
else
|
||||
VERSION="${GITHUB_REF#refs/tags/}"
|
||||
VERSION="${{ github.ref_name }}"
|
||||
fi
|
||||
echo "构建版本: $VERSION"
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
# 拿提交消息
|
||||
COMMIT_MSG=$(git log -3 --pretty=%B)
|
||||
echo "COMMIT_MSG<<EOF" >> $GITHUB_ENV
|
||||
echo "$COMMIT_MSG" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
|
||||
- name: 构建项目 (Linux)
|
||||
run: |
|
||||
echo "开始构建Linux版本..."
|
||||
@@ -83,48 +89,74 @@ jobs:
|
||||
GITEA_TOKEN: ${{ secrets.CI_TOKEN }}
|
||||
run: |
|
||||
VERSION="${{ env.VERSION }}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION="${{ github.ref_name }}"
|
||||
fi
|
||||
COMMIT_MSG="${{ env.COMMIT_MSG }}"
|
||||
API="https://git.fig-lang.cn/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
echo "正在为Linux版本创建/更新发布 $VERSION ..."
|
||||
echo "正在检查版本 $VERSION 的发布状态..."
|
||||
|
||||
RESPONSE=$(curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"$VERSION\",\"name\":\"Fig $VERSION\",\"draft\":false,\"prerelease\":false}" \
|
||||
"$API/releases" 2>/dev/null || echo '{"id":0}')
|
||||
# 准备 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
|
||||
|
||||
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
# 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 [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
|
||||
echo "尝试通过标签获取已有发布的ID..."
|
||||
RELEASE_ID=$(curl -sS -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$API/releases/tags/$VERSION" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "0" ]; then
|
||||
echo "✅ 找到已有发布 (ID: $RELEASE_ID),正在更新说明..."
|
||||
curl -sS -X PATCH -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @release_body.json \
|
||||
"$API/releases/$RELEASE_ID" > /dev/null
|
||||
else
|
||||
echo "未找到已有发布,准备创建新发布..."
|
||||
RESPONSE=$(curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @release_body.json \
|
||||
"$API/releases" 2>/dev/null || echo '{"id":0}')
|
||||
|
||||
RELEASE_ID=$(echo "$RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
|
||||
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
|
||||
# 再次尝试获取,防止并发冲突
|
||||
RESPONSE_TAG=$(curl -sS -H "Authorization: token $GITEA_TOKEN" "$API/releases/tags/$VERSION" 2>/dev/null || echo '{"id":0}')
|
||||
RELEASE_ID=$(echo "$RESPONSE_TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$RELEASE_ID" ]; then
|
||||
echo "错误:无法获取或创建发布 ID"
|
||||
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "0" ]; then
|
||||
echo "❌ 错误:无法获取或创建发布 ID"
|
||||
exit 1
|
||||
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" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @Fig-$VERSION-linux-x86_64.tar.gz \
|
||||
"$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-linux-x86_64.tar.gz"
|
||||
--data-binary "@$PACKAGE_ZIP" \
|
||||
"$API/releases/$RELEASE_ID/assets?name=$PACKAGE_ZIP" > /dev/null
|
||||
|
||||
echo "正在上传 $PACKAGE_SHA ..."
|
||||
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: text/plain" \
|
||||
--data-binary @Fig-$VERSION-linux-x86_64.sha256 \
|
||||
"$API/releases/$RELEASE_ID/assets?name=Fig-$VERSION-linux-x86_64.sha256"
|
||||
--data-binary "@$PACKAGE_SHA" \
|
||||
"$API/releases/$RELEASE_ID/assets?name=$PACKAGE_SHA" > /dev/null
|
||||
|
||||
# 🔧 新增:上传Linux安装器
|
||||
if [ -f "Installer/ConsoleInstaller/dist/linux/FigSetup-Linux" ]; then
|
||||
# 🔧 上传Linux安装器
|
||||
INSTALLER="Installer/ConsoleInstaller/dist/linux/FigSetup-Linux"
|
||||
if [ -f "$INSTALLER" ]; then
|
||||
echo "正在上传Linux安装器..."
|
||||
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @Installer/ConsoleInstaller/dist/linux/FigSetup-Linux \
|
||||
"$API/releases/$RELEASE_ID/assets?name=FigSetup-Linux"
|
||||
--data-binary "@$INSTALLER" \
|
||||
"$API/releases/$RELEASE_ID/assets?name=FigSetup-Linux" > /dev/null
|
||||
fi
|
||||
|
||||
echo "✅ Linux版本发布完成!"
|
||||
@@ -151,9 +183,9 @@ jobs:
|
||||
run: |
|
||||
$env:Path = "C:\Program Files\Git\cmd;$env:Path"
|
||||
git clone https://git.fig-lang.cn/$env:GITHUB_REPOSITORY .
|
||||
git checkout $env:GITHUB_REF
|
||||
git checkout ${{ github.ref }}
|
||||
|
||||
- name: 设置版本
|
||||
- name: 设置版本和提交信息
|
||||
run: |
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') {
|
||||
@@ -161,10 +193,16 @@ jobs:
|
||||
if (-not $VERSION) { $VERSION = $env:VERSION_INPUT }
|
||||
if (-not $VERSION) { $VERSION = "dev-build" }
|
||||
} else {
|
||||
$VERSION = $env:GITHUB_REF_NAME
|
||||
$VERSION = "${{ github.ref_name }}"
|
||||
}
|
||||
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)
|
||||
run: |
|
||||
@@ -186,14 +224,11 @@ jobs:
|
||||
run: |
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 🔧 修改:与发布步骤使用相同的版本号计算逻辑
|
||||
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') {
|
||||
$VERSION = $env:INPUT_VERSION
|
||||
if (-not $VERSION) { $VERSION = "dev-build" }
|
||||
} else {
|
||||
$VERSION = $env:GITHUB_REF_NAME
|
||||
$VERSION = $env:VERSION
|
||||
if (-not $VERSION) {
|
||||
$VERSION = "${{ github.ref_name }}"
|
||||
Write-Host "⚠️ 警告:从环境变量获取 VERSION 失败,回退到 github.ref_name: $VERSION"
|
||||
}
|
||||
|
||||
Write-Host "打包版本: $VERSION"
|
||||
|
||||
$PACKAGE_NAME = "Fig-${VERSION}-windows-x86_64"
|
||||
@@ -231,91 +266,100 @@ jobs:
|
||||
run: |
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 重新计算版本号(不使用 $env:VERSION)
|
||||
if ($env:GITHUB_EVENT_NAME -eq 'workflow_dispatch') {
|
||||
$VERSION = $env:INPUT_VERSION
|
||||
if (-not $VERSION) { $VERSION = "dev-build" }
|
||||
} else {
|
||||
$VERSION = $env:GITHUB_REF_NAME
|
||||
$VERSION = $env:VERSION
|
||||
$COMMIT_MSG = $env:COMMIT_MSG
|
||||
|
||||
if (-not $VERSION) {
|
||||
$VERSION = "${{ 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
|
||||
$API = "https://git.fig-lang.cn/api/v1/repos/$REPO"
|
||||
$TOKEN = $env:GITEA_TOKEN
|
||||
$HEADERS = @{
|
||||
Authorization = "token $TOKEN"
|
||||
'Content-Type' = 'application/json'
|
||||
}
|
||||
|
||||
# 🔧 新增:检查必需的文件是否存在
|
||||
$ZIP_FILE = "Fig-$VERSION-windows-x86_64.zip"
|
||||
$HASH_FILE = "Fig-$VERSION-windows-x86_64.sha256"
|
||||
$INSTALLER_PATH = "Installer\ConsoleInstaller\dist\windows\FigSetup.exe"
|
||||
|
||||
if (-not (Test-Path $ZIP_FILE)) {
|
||||
Write-Host "❌ 错误:找不到ZIP文件 $ZIP_FILE"
|
||||
Get-ChildItem *.zip
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "正在为Windows版本创建/更新发布 $VERSION ..."
|
||||
|
||||
# 🔧 关键修改:直接创建发布,不先检查(与Linux逻辑一致)
|
||||
$CREATE_BODY = @{
|
||||
tag_name = $VERSION
|
||||
name = "Fig $VERSION"
|
||||
body = $COMMIT_MSG
|
||||
draft = $false
|
||||
prerelease = $false
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
Write-Host "创建发布请求体: $CREATE_BODY"
|
||||
Write-Host "正在检查版本 $VERSION 的发布状态..."
|
||||
$RELEASE_ID = $null
|
||||
|
||||
# 直接创建发布(Gitea会自动处理重复创建)
|
||||
$RESPONSE = Invoke-RestMethod -Method Post -Uri "$API/releases" `
|
||||
-Headers @{
|
||||
Authorization = "token $TOKEN"
|
||||
'Content-Type' = 'application/json'
|
||||
} -Body $CREATE_BODY -ErrorAction SilentlyContinue
|
||||
|
||||
if (-not $RESPONSE -or -not $RESPONSE.id) {
|
||||
# 如果创建失败,尝试通过标签获取已有发布的ID
|
||||
Write-Host "创建失败,尝试获取已有发布..."
|
||||
$RESPONSE = Invoke-RestMethod -Uri "$API/releases/tags/$VERSION" `
|
||||
-Headers @{Authorization = "token $TOKEN" } `
|
||||
-ErrorAction SilentlyContinue
|
||||
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 ($RESPONSE -and $RESPONSE.id) {
|
||||
$RELEASE_ID = $RESPONSE.id
|
||||
Write-Host "✅ 使用发布 ID: $RELEASE_ID 进行上传"
|
||||
} else {
|
||||
Write-Host "❌ 错误:无法获取或创建发布 ID"
|
||||
exit 1
|
||||
if (-not $RELEASE_ID) {
|
||||
try {
|
||||
Write-Host "正在创建新发布: $VERSION ..."
|
||||
$RESPONSE = Invoke-RestMethod -Method Post -Uri "$API/releases" -Headers $HEADERS -Body $CREATE_BODY -ErrorAction Stop
|
||||
$RELEASE_ID = $RESPONSE.id
|
||||
Write-Host "✅ 发布创建成功 (ID: $RELEASE_ID)"
|
||||
} catch {
|
||||
$err = $_.Exception.Message
|
||||
Write-Host "❌ 创建发布失败: $err"
|
||||
# 最后一次尝试:再次尝试按标签获取,防止由于并发导致的冲突
|
||||
try {
|
||||
$RETRY = Invoke-RestMethod -Uri "$API/releases/tags/$VERSION" -Headers $HEADERS -ErrorAction Stop
|
||||
$RELEASE_ID = $RETRY.id
|
||||
Write-Host "✅ 重试获取发布成功 (ID: $RELEASE_ID)"
|
||||
} catch {
|
||||
Write-Host "❌ 无法获取或创建发布 ID"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 上传资产
|
||||
Write-Host "正在上传 ZIP 文件..."
|
||||
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=$ZIP_FILE" `
|
||||
-Headers @{
|
||||
Authorization = "token $TOKEN"
|
||||
'Content-Type' = 'application/octet-stream'
|
||||
} -InFile $ZIP_FILE -ErrorAction SilentlyContinue
|
||||
Write-Host "正在上传文件..."
|
||||
$ASSETS = @(
|
||||
@{ Name = $ZIP_FILE; Path = $ZIP_FILE; ContentType = "application/octet-stream" },
|
||||
@{ Name = $HASH_FILE; Path = $HASH_FILE; ContentType = "text/plain" }
|
||||
)
|
||||
|
||||
Write-Host "正在上传校验文件..."
|
||||
Invoke-RestMethod -Method Post -Uri "$API/releases/$RELEASE_ID/assets?name=$HASH_FILE" `
|
||||
-Headers @{
|
||||
Authorization = "token $TOKEN"
|
||||
'Content-Type' = 'text/plain'
|
||||
} -InFile $HASH_FILE -ErrorAction SilentlyContinue
|
||||
|
||||
# 上传Windows安装器
|
||||
if (Test-Path $INSTALLER_PATH) {
|
||||
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 $INSTALLER_PATH -ErrorAction SilentlyContinue
|
||||
} else {
|
||||
Write-Host "⚠️ 警告:未找到安装器文件 $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版本发布完成!"
|
||||
30
ExampleCodes/3-Structure.fig
Normal file
30
ExampleCodes/3-Structure.fig
Normal file
@@ -0,0 +1,30 @@
|
||||
import std.io;
|
||||
|
||||
struct Point
|
||||
{
|
||||
x: Int; // type specifiers are optional
|
||||
y: Int; // type specifiers are optional
|
||||
|
||||
// x and y are private fields, can only reached by internal context
|
||||
|
||||
public func toString() -> String
|
||||
{
|
||||
return "(" + (x as String) + "," + (y as String) + ")";
|
||||
}
|
||||
// public func toString() {} is ok
|
||||
}
|
||||
|
||||
// make points
|
||||
|
||||
var p1 := new Point{1, 2};
|
||||
io.println(p1.toString()); // (1,2)
|
||||
|
||||
var p2 := new Point{x: 2, y: 3};
|
||||
io.println(p2.toString()); // (2,3)
|
||||
|
||||
var x := 114;
|
||||
var y := 514;
|
||||
|
||||
var p3 := new Point{y, x}; // shorthand mode, can be unordered, auto match field and variable!
|
||||
// = Point{x: x, y: y}
|
||||
io.println(p3.toString()); // (114,514)
|
||||
94
ExampleCodes/4-Interface.fig
Normal file
94
ExampleCodes/4-Interface.fig
Normal file
@@ -0,0 +1,94 @@
|
||||
import std.io;
|
||||
|
||||
interface Document
|
||||
{
|
||||
getDepth() -> Int; // return type is necessary
|
||||
getName() -> String;
|
||||
|
||||
/* toString() -> String
|
||||
{
|
||||
// default implementation
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
struct File
|
||||
{
|
||||
public depth: Int;
|
||||
public name: String;
|
||||
}
|
||||
|
||||
impl Document for File
|
||||
{
|
||||
getDepth()
|
||||
{
|
||||
return depth;
|
||||
}
|
||||
getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
struct Folder
|
||||
{
|
||||
public depth: Int;
|
||||
public name: String;
|
||||
public childs: List = [];
|
||||
}
|
||||
|
||||
impl Document for Folder
|
||||
{
|
||||
getDepth()
|
||||
{
|
||||
return depth;
|
||||
}
|
||||
|
||||
getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
const root_folder := new Folder{
|
||||
0,
|
||||
"root",
|
||||
[
|
||||
new File{
|
||||
1,
|
||||
"joyo.txt"
|
||||
},
|
||||
new Folder{
|
||||
2,
|
||||
"joyoyo",
|
||||
[
|
||||
new File{
|
||||
3,
|
||||
"JOYO2.txt"
|
||||
},
|
||||
new Folder{
|
||||
3,
|
||||
"joyoyoyo"
|
||||
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
func print_directory(root: Document)
|
||||
{
|
||||
io.print(" " * root.getDepth());
|
||||
io.println(root.getDepth(), root.getName());
|
||||
if root is Folder
|
||||
{
|
||||
for var i := 0; i < root.childs.length(); i += 1
|
||||
{
|
||||
var child := root.childs[i];
|
||||
print_directory(child);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
print_directory(root_folder);
|
||||
@@ -2,3 +2,14 @@ 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);
|
||||
}
|
||||
@@ -8,13 +8,95 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@ def fib(x:int) -> int:
|
||||
|
||||
if __name__ == '__main__':
|
||||
t0 = tt()
|
||||
result = fib(25)
|
||||
result = fib(30)
|
||||
t1 = tt()
|
||||
print('cost: ',t1-t0, 'result:', result)
|
||||
|
||||
@@ -42,7 +42,7 @@ def resolveInstallPath(os: int):
|
||||
return default_path[os]
|
||||
|
||||
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
|
||||
|
||||
rel = requests.get(api_url).text
|
||||
|
||||
34
README.md
34
README.md
@@ -34,6 +34,12 @@ Recommend view on Gitea Endpoint
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
# Recommend
|
||||
```
|
||||
or
|
||||
|
||||
```bash
|
||||
git clone https://github.com/PuqiAR/Fig.git
|
||||
```
|
||||
@@ -71,6 +77,32 @@ Replace `[file]` with the path to your input file.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -90,7 +122,7 @@ Tail Recursion : ▎ 0.40ms
|
||||
|
||||
**Key Insight:** Algorithm choice dominates performance in this tree-walker implementation.
|
||||
|
||||
**Detailed Reports:** [English](./docs/benchmark_result/benchmark_result_en_0.4.2-alpha.md) | [中文](./docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md)
|
||||
**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
|
||||
|
||||
@@ -26,10 +26,17 @@
|
||||
### 使用教程
|
||||
1. 克隆存储库:
|
||||
|
||||
```bash
|
||||
git clone https://git.fig-lang.cn/PuqiAR/Fig.git
|
||||
# 推荐
|
||||
```
|
||||
|
||||
或
|
||||
```bash
|
||||
git clone https://github.com/PuqiAR/Fig.git
|
||||
```
|
||||
|
||||
|
||||
2. 切换到项目目录:
|
||||
|
||||
```bash
|
||||
@@ -59,6 +66,33 @@ Fig 围绕几个核心原则设计:
|
||||
|
||||
## 性能概览
|
||||
|
||||
**版本:** 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
|
||||
|
||||
@@ -78,7 +112,7 @@ Fig 围绕几个核心原则设计:
|
||||
|
||||
**核心发现:** 在此树遍历实现中,算法选择主导性能表现。
|
||||
|
||||
**详细报告:** [English](./docs/benchmark_result/benchmark_result_en_0.4.2-alpha.md) | [中文](./docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md)
|
||||
**详细报告:** [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)
|
||||
|
||||
|
||||
## 语言文档
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
@@ -162,9 +162,9 @@ Fig 语言的关键字包括:
|
||||
| -------- | ----------------------------------------------------------- |
|
||||
| 声明 | `func`, `var`, `const`, `struct`, `interface`, `import` |
|
||||
| 控制流 | `if`, `else`, `while`, `for`, `return`, `break`, `continue` |
|
||||
| 错误处理 | `try`, `catch`, `throw`, `Finally` |
|
||||
| 逻辑运算 | `and`, `or`, `not` |
|
||||
| 类型相关 | `is`, `impl`, `new`, `public` |
|
||||
| 错误处理 | `try`, `catch`, `throw`, `finally` |
|
||||
| 逻辑运算 | `and`, `or`, `not` |
|
||||
| 类型相关 | `is`, `as`, `impl`, `new`, `public` |
|
||||
|
||||
这些关键字不能用作标识符名称。
|
||||
|
||||
|
||||
@@ -35,10 +35,22 @@ namespace Fig::Ast
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
interface IO : Writable + Readable
|
||||
{
|
||||
}
|
||||
|
||||
interface XX
|
||||
{
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
class InterfaceDefAst final : public StatementAst
|
||||
{
|
||||
public:
|
||||
FString name;
|
||||
std::vector<Expression> bundles;
|
||||
std::vector<InterfaceMethod> methods;
|
||||
std::vector<FString> parents; // Feature, NOT NOW
|
||||
bool isPublic;
|
||||
@@ -48,8 +60,8 @@ namespace Fig::Ast
|
||||
type = AstType::InterfaceDefSt;
|
||||
}
|
||||
|
||||
InterfaceDefAst(FString _name, std::vector<InterfaceMethod> _methods, bool _isPublic) :
|
||||
name(std::move(_name)), methods(std::move(_methods)), isPublic(_isPublic)
|
||||
InterfaceDefAst(FString _name, std::vector<Expression> _bundles, std::vector<InterfaceMethod> _methods, bool _isPublic) :
|
||||
name(std::move(_name)), bundles(std::move(_bundles)), methods(std::move(_methods)), isPublic(_isPublic)
|
||||
{
|
||||
type = AstType::InterfaceDefSt;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,8 @@ namespace Fig::Ast
|
||||
|
||||
virtual FString typeName()
|
||||
{
|
||||
return FString::fromStringView(FStringView::fromBasicStringView(magic_enum::enum_name(type)));
|
||||
const auto &name = magic_enum::enum_name(type);
|
||||
return FString::fromBasicString(std::string(name.data(), name.length()));
|
||||
}
|
||||
virtual FString toString()
|
||||
{
|
||||
|
||||
1585
src/Core/String.hpp
Normal file
1585
src/Core/String.hpp
Normal file
File diff suppressed because it is too large
Load Diff
68
src/Core/StringTest.cpp
Normal file
68
src/Core/StringTest.cpp
Normal 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;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,13 @@ namespace Fig
|
||||
// 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> result;
|
||||
|
||||
@@ -183,9 +183,9 @@ namespace Fig
|
||||
// we've checked argument count before, so here
|
||||
// must be a default value
|
||||
|
||||
// evaluate default value in definition context
|
||||
// evaluate default value in definition context!
|
||||
ObjectPtr defaultVal = check_unwrap(eval(field.defaultValue,
|
||||
ctx)); // it can't be null here
|
||||
defContext)); // it can't be null here
|
||||
|
||||
// type check
|
||||
if (!isTypeMatch(expectedType, defaultVal, ctx))
|
||||
@@ -345,13 +345,12 @@ namespace Fig
|
||||
}
|
||||
}
|
||||
}
|
||||
ContextPtr stDefCtx = structT.defContext;
|
||||
|
||||
// load struct method
|
||||
for (auto &[id, fn] : stDefCtx->getFunctions())
|
||||
for (auto &[id, fn] : defContext->getFunctions())
|
||||
{
|
||||
const FString &funcName = fn.name;
|
||||
const auto &funcSlot = stDefCtx->get(funcName);
|
||||
const auto &funcSlot = defContext->get(funcName);
|
||||
|
||||
instanceCtx->def(funcName,
|
||||
ValueType::Function,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Evaluator/Core/ExprResult.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Ast/Expressions/FunctionCall.hpp>
|
||||
@@ -14,6 +15,7 @@
|
||||
|
||||
#include <Utils/utils.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -63,6 +65,10 @@ namespace Fig
|
||||
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));
|
||||
@@ -165,6 +171,7 @@ namespace Fig
|
||||
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))
|
||||
{
|
||||
@@ -173,11 +180,46 @@ namespace Fig
|
||||
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, ifd->methods)));
|
||||
std::make_shared<Object>(InterfaceType(type, methods)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
@@ -327,7 +369,7 @@ namespace Fig
|
||||
FString(std::format("<Function {}>", opFnName.toBasicString())), ctx);
|
||||
|
||||
const auto &fillOpFnParas = [this, structType, implMethod, opFnName, fnCtx, ctx, paraCnt](
|
||||
const std::vector<ObjectPtr> &args) {
|
||||
const std::vector<ObjectPtr> &args) -> StatementResult {
|
||||
const Ast::FunctionParameters ¶s = implMethod.paras;
|
||||
for (size_t i = 0; i < paraCnt; ++i)
|
||||
{
|
||||
@@ -345,6 +387,7 @@ namespace Fig
|
||||
}
|
||||
fnCtx->def(paras.posParas[i].first, paraType, AccessModifier::Normal, args[i]);
|
||||
}
|
||||
return StatementResult::normal();
|
||||
};
|
||||
|
||||
if (paraCnt == 1)
|
||||
@@ -536,6 +579,11 @@ namespace Fig
|
||||
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
|
||||
{
|
||||
@@ -549,11 +597,12 @@ namespace Fig
|
||||
}
|
||||
if (!condVal->as<ValueType::BoolClass>()) { break; }
|
||||
iteration++;
|
||||
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
|
||||
|
||||
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())
|
||||
|
||||
@@ -80,6 +80,10 @@ namespace Fig
|
||||
if (kind == Kind::Variable)
|
||||
{
|
||||
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))
|
||||
{
|
||||
throw RuntimeError(
|
||||
@@ -89,11 +93,6 @@ namespace Fig
|
||||
s->declaredType.toString().toBasicString(),
|
||||
prettyType(v).toBasicString())));
|
||||
}
|
||||
if (isAccessConst(s->am))
|
||||
{
|
||||
throw RuntimeError(FString(
|
||||
std::format("Variable `{}` is immutable", s->name.toBasicString())));
|
||||
}
|
||||
s->value = v;
|
||||
}
|
||||
else if (kind == Kind::ListElement)
|
||||
|
||||
@@ -437,8 +437,10 @@ namespace Fig
|
||||
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>().id, static_cast<const void *>(&as<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(),
|
||||
@@ -651,7 +653,8 @@ namespace Fig
|
||||
}
|
||||
|
||||
// comparison
|
||||
friend bool operator==(const Object &lhs, const Object &rhs) {
|
||||
friend bool operator==(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
return nearlyEqual(lhs.getNumericValue(), rhs.getNumericValue());
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Fig
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c == U'"' || c == U'\n')
|
||||
if (c == U'"')
|
||||
{
|
||||
next();
|
||||
unterminated = false;
|
||||
|
||||
3
src/Module/CppLibrary/CppLibrary.hpp
Normal file
3
src/Module/CppLibrary/CppLibrary.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "File/File.hpp"
|
||||
74
src/Module/CppLibrary/File/File.hpp
Normal file
74
src/Module/CppLibrary/File/File.hpp
Normal 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
|
||||
101
src/Module/Library/std/file/file.fig
Normal file
101
src/Module/Library/std/file/file.fig
Normal 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
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
Copyright © 2025 PuqiAR. All rights reserved.
|
||||
*/
|
||||
|
||||
import std.value; // `type` function and string_from
|
||||
// import std.value; // `type` function and string_from
|
||||
|
||||
|
||||
struct FormatError
|
||||
@@ -37,7 +37,7 @@ public func format(objects ...) -> Any
|
||||
}
|
||||
|
||||
var fmt := objects[0];
|
||||
var fmtType := value.type(fmt);
|
||||
var fmtType := type(fmt);
|
||||
if fmtType != "String"
|
||||
{
|
||||
throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
|
||||
@@ -88,7 +88,7 @@ public func format(objects ...) -> Any
|
||||
throw new FormatError{"require enough format expression"};
|
||||
}
|
||||
|
||||
result += value.string_from(objects[argIndex]);
|
||||
result += objects[argIndex] as String;
|
||||
argIndex += 1;
|
||||
|
||||
i = endIndex + 1;
|
||||
@@ -116,17 +116,17 @@ public func format(objects ...) -> Any
|
||||
|
||||
public func formatByListArgs(objects) -> Any
|
||||
{
|
||||
if value.type(objects) != "List"
|
||||
if not (objects is List)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if objects.length() < 1
|
||||
if objects.length() < 1
|
||||
{
|
||||
throw new FormatError{"Require format string"};
|
||||
}
|
||||
|
||||
var fmt := objects[0];
|
||||
var fmtType := value.type(fmt);
|
||||
var fmtType := type(fmt);
|
||||
if fmtType != "String"
|
||||
{
|
||||
throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType};
|
||||
@@ -177,7 +177,7 @@ if objects.length() < 1
|
||||
throw new FormatError{"require enough format expression"};
|
||||
}
|
||||
|
||||
result += value.string_from(objects[argIndex]);
|
||||
result += objects[argIndex] as String;
|
||||
argIndex += 1;
|
||||
|
||||
i = endIndex + 1;
|
||||
|
||||
@@ -5,13 +5,19 @@
|
||||
#include <Ast/Statements/ControlSt.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Evaluator/Context/context.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
|
||||
#include <Evaluator/Value/structType.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Module/builtins.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Evaluator/Context/context.hpp>
|
||||
|
||||
#include <Module/CppLibrary/CppLibrary.hpp>
|
||||
|
||||
#include <Module/builtins.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <print>
|
||||
#include <iostream>
|
||||
@@ -132,9 +138,18 @@ namespace Fig::Builtins
|
||||
{u8"__fmath_tan", 1},
|
||||
{u8"__fmath_tanh", 1},
|
||||
{u8"__fmath_trunc", 1},
|
||||
|
||||
/* file start */
|
||||
{u8"__fstdfile_open", 2},
|
||||
{u8"__fstdfile_close", 1},
|
||||
{u8"__fstdfile_is_open", 1},
|
||||
|
||||
{u8"__fstdfile_read", 1},
|
||||
{u8"__fstdfile_write", 2},
|
||||
};
|
||||
return builtinFunctionArgCounts;
|
||||
}
|
||||
|
||||
const std::unordered_map<FString, BuiltinFunction> &getBuiltinFunctions()
|
||||
{
|
||||
static const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
|
||||
@@ -421,6 +436,47 @@ namespace Fig::Builtins
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(trunc(d));
|
||||
}},
|
||||
/* file start */
|
||||
{u8"__fstdfile_open",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
const FString &path = args[0]->as<ValueType::StringClass>();
|
||||
const ValueType::IntClass &mode = args[1]->as<ValueType::IntClass>();
|
||||
|
||||
CppLibrary::File *f = CppLibrary::FileManager::getInstance().GetNextFreeFile();
|
||||
f->fs->open(path.toBasicString(), static_cast<unsigned int>(mode));
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(f->id));
|
||||
}},
|
||||
{u8"__fstdfile_close",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
const ValueType::IntClass &id = args[0]->as<ValueType::IntClass>();
|
||||
CppLibrary::FileManager::getInstance().CloseFile(id);
|
||||
return Object::getNullInstance();
|
||||
}},
|
||||
{u8"__fstdfile_is_open",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
const ValueType::IntClass &id = args[0]->as<ValueType::IntClass>();
|
||||
return std::make_shared<Object>(CppLibrary::FileManager::getInstance().GetFile(id)->fs->is_open());
|
||||
}},
|
||||
{u8"__fstdfile_read",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
const ValueType::IntClass &id = args[0]->as<ValueType::IntClass>();
|
||||
CppLibrary::File *f = CppLibrary::FileManager::getInstance().GetFile(id);
|
||||
|
||||
char *buf = new char[CppLibrary::FileManager::MAX_FILE_BUF];
|
||||
f->fs->read(buf, CppLibrary::FileManager::MAX_FILE_BUF);
|
||||
return std::make_shared<Object>(ValueType::StringClass(reinterpret_cast<const char8_t *>(buf)));
|
||||
}},
|
||||
{u8"__fstdfile_write",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
const ValueType::IntClass &id = args[0]->as<ValueType::IntClass>();
|
||||
const ValueType::StringClass &str = args[1]->as<ValueType::StringClass>();
|
||||
|
||||
CppLibrary::File *f = CppLibrary::FileManager::getInstance().GetFile(id);
|
||||
const char *data = reinterpret_cast<const char *>(str.c_str());
|
||||
f->fs->write(data, str.size());
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(str.length()));
|
||||
// bytes wrote
|
||||
}},
|
||||
};
|
||||
return builtinFunctions;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -44,6 +45,19 @@ namespace Fig
|
||||
using BuiltinFunction = std::function<ObjectPtr(const std::vector<ObjectPtr> &)>;
|
||||
|
||||
const std::unordered_map<FString, int> &getBuiltinFunctionArgCounts();
|
||||
|
||||
/*
|
||||
File
|
||||
*/
|
||||
|
||||
struct FileHandler
|
||||
{
|
||||
int64_t id;
|
||||
std::fstream *fs;
|
||||
};
|
||||
|
||||
std::vector<FileHandler *> &getFileHandlers(); // global
|
||||
|
||||
const std::unordered_map<FString, BuiltinFunction> &getBuiltinFunctions();
|
||||
|
||||
inline bool isBuiltinFunction(const FString &name)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Ast/Expressions/InitExpr.hpp"
|
||||
#include <Ast/Expressions/VarExpr.hpp>
|
||||
#include <Ast/Statements/ErrorFlow.hpp>
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
@@ -6,6 +7,7 @@
|
||||
#include <Error/error.hpp>
|
||||
#include <Token/token.hpp>
|
||||
#include <Parser/parser.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -384,6 +386,29 @@ namespace Fig
|
||||
// entry: current is interface name
|
||||
FString interfaceName = currentToken().getValue();
|
||||
next(); // consume name
|
||||
|
||||
std::vector<Ast::Expression> bundles;
|
||||
|
||||
if (isThis(TokenType::Colon))
|
||||
{
|
||||
next(); // consume `:`
|
||||
// parse bundle interfaces
|
||||
|
||||
if (isThis(TokenType::LeftBrace))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(u8"expect interfaces to bundle");
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
if (isThis(TokenType::LeftBrace)) { break; }
|
||||
bundles.push_back(parseExpression(0, TokenType::Plus, TokenType::LeftBrace));
|
||||
if (isThis(TokenType::Plus))
|
||||
{
|
||||
next(); // consume `+`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(TokenType::LeftBrace); // `{
|
||||
next(); // consume `{`
|
||||
|
||||
@@ -425,7 +450,7 @@ namespace Fig
|
||||
throwAddressableError<SyntaxError>(FString(u8"Invalid syntax"), currentAAI.line, currentAAI.column);
|
||||
}
|
||||
}
|
||||
return makeAst<Ast::InterfaceDefAst>(interfaceName, methods, isPublic);
|
||||
return makeAst<Ast::InterfaceDefAst>(interfaceName, bundles, methods, isPublic);
|
||||
}
|
||||
|
||||
Ast::Implement Parser::__parseImplement()
|
||||
@@ -881,10 +906,6 @@ namespace Fig
|
||||
if (mode == 0)
|
||||
{
|
||||
if (isThis(TokenType::Identifier) && isNext(TokenType::Colon)) { mode = 2; }
|
||||
else if (isThis(TokenType::Identifier) && (isNext(TokenType::Comma) || isNext(TokenType::RightBrace)))
|
||||
{
|
||||
mode = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = 1;
|
||||
@@ -908,16 +929,6 @@ namespace Fig
|
||||
Ast::Expression expr = parseExpression(0, TokenType::Comma, TokenType::RightBrace);
|
||||
args.push_back({fieldName, std::move(expr)});
|
||||
}
|
||||
else if (mode == 3)
|
||||
{
|
||||
// 3 Person {name, age, sex};
|
||||
expect(TokenType::Identifier);
|
||||
FString fieldName = currentToken().getValue();
|
||||
Ast::Expression expr = makeAst<Ast::VarExprAst>(fieldName);
|
||||
args.push_back({fieldName, std::move(expr)});
|
||||
next(); // consume identifier
|
||||
}
|
||||
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // consume comma
|
||||
@@ -929,6 +940,33 @@ namespace Fig
|
||||
currentToken().toString().toBasicString())));
|
||||
}
|
||||
}
|
||||
bool shorthand = true;
|
||||
if (mode == 1)
|
||||
{
|
||||
for (auto &[name, exp] : args)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
shorthand = false;
|
||||
}
|
||||
if (exp->getType() != Ast::AstType::VarExpr)
|
||||
{
|
||||
shorthand = false;
|
||||
}
|
||||
}
|
||||
if (shorthand)
|
||||
{
|
||||
mode = 3; // all are identifiers, so it's shorthand mode, not positional
|
||||
std::vector<std::pair<FString, Ast::Expression>> nargs;
|
||||
for (auto &[name, exp] : args)
|
||||
{
|
||||
const Ast::VarExpr var = std::static_pointer_cast<Ast::VarExprAst>(exp);
|
||||
nargs.push_back({var->name, exp});
|
||||
}
|
||||
args = nargs;
|
||||
}
|
||||
}
|
||||
|
||||
expect(TokenType::RightBrace);
|
||||
next(); // consume `}`
|
||||
return makeAst<Ast::InitExprAst>(structe, args, static_cast<Ast::InitExprAst::InitMode>(mode));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Error/errorLog.hpp>
|
||||
@@ -23,14 +24,8 @@ namespace Fig
|
||||
|
||||
while (true)
|
||||
{
|
||||
ostream << "\r\n>>";
|
||||
ostream << "\r>>";
|
||||
const FString &line = readline();
|
||||
|
||||
if (line.empty())
|
||||
{
|
||||
ostream << Object::getNullInstance()->toString().toBasicString();
|
||||
continue;
|
||||
}
|
||||
if (line == u8"!exit") { break; }
|
||||
|
||||
Lexer lexer(line, sourcePath, sourceLines);
|
||||
@@ -43,6 +38,10 @@ namespace Fig
|
||||
|
||||
StatementResult sr = evaluator.Run(program);
|
||||
ObjectPtr result = sr.result;
|
||||
if (result->is<ValueType::NullClass>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ostream << result->toString().toBasicString() << '\n';
|
||||
}
|
||||
catch (AddressableError &e)
|
||||
|
||||
Reference in New Issue
Block a user