forked from PuqiAR/Fig-TreeWalker
Compare commits
21 Commits
v0.4.2-alp
...
utf32_str
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c68b2e77d | |||
| d897f41c57 | |||
| a05958319b | |||
| c0dd463b82 | |||
| edabd0aa19 | |||
| f1b30c8837 | |||
| 50ca68b1a4 | |||
|
|
ecc1bd1cc2 | ||
|
|
558ea194c7 | ||
|
|
b302cb2cc5 | ||
| b4b6d409b5 | |||
| da1a8e04de | |||
| e8aed221de | |||
| b98c1b7dd8 | |||
| 27cf09cad0 | |||
| 45e18cc773 | |||
| 4535f75058 | |||
| 01c16dee3f | |||
| 41bff72d44 | |||
| aea716ced2 | |||
| 61bffdc743 |
@@ -29,21 +29,27 @@ 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版本..."
|
||||
xmake f -p linux -a x86_64 -m release -y
|
||||
xmake build -j$(nproc)
|
||||
xmake build -j$(nproc) Fig
|
||||
echo "Linux构建成功。"
|
||||
|
||||
# 🔧 新增:构建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 的发布状态..."
|
||||
|
||||
# 准备 JSON 数据
|
||||
VERSION="$VERSION" COMMIT_MSG="$COMMIT_MSG" python3 -c "import json, os; print(json.dumps({'tag_name': os.environ.get('VERSION', ''), 'name': 'Fig ' + os.environ.get('VERSION', ''), 'body': os.environ.get('COMMIT_MSG', ''), 'draft': False, 'prerelease': False}))" > release_body.json
|
||||
|
||||
# 1. 尝试获取已有发布
|
||||
RESPONSE_TAG=$(curl -sS -H "Authorization: token $GITEA_TOKEN" "$API/releases/tags/$VERSION" 2>/dev/null || echo '{"id":0}')
|
||||
RELEASE_ID=$(echo "$RESPONSE_TAG" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "0" ]; then
|
||||
echo "✅ 找到已有发布 (ID: $RELEASE_ID),正在更新说明..."
|
||||
curl -sS -X PATCH -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @release_body.json \
|
||||
"$API/releases/$RELEASE_ID" > /dev/null
|
||||
else
|
||||
echo "未找到已有发布,准备创建新发布..."
|
||||
RESPONSE=$(curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"$VERSION\",\"name\":\"Fig $VERSION\",\"draft\":false,\"prerelease\":false}" \
|
||||
-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
|
||||
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)
|
||||
# 再次尝试获取,防止并发冲突
|
||||
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,15 +193,21 @@ 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: |
|
||||
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构建成功。'
|
||||
|
||||
# 🔧 新增:构建Windows平台安装器
|
||||
@@ -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) {
|
||||
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 进行上传"
|
||||
} else {
|
||||
Write-Host "❌ 错误:无法获取或创建发布 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版本发布完成!"
|
||||
15
ExampleCodes/FigFig/main.fig
Normal file
15
ExampleCodes/FigFig/main.fig
Normal file
@@ -0,0 +1,15 @@
|
||||
import std.io;
|
||||
|
||||
import token {Token, TokenType};
|
||||
import tokenizer {Tokenizer};
|
||||
|
||||
const src := "abc egaD";
|
||||
const tokenizer := new Tokenizer{src};
|
||||
|
||||
const result := tokenizer.TokenizeAll();
|
||||
|
||||
for var i := 0; i < result.length(); i += 1
|
||||
{
|
||||
const tok := result[i];
|
||||
io.printf("{}: {}\n", tok.literal, tok.type);
|
||||
}
|
||||
31
ExampleCodes/FigFig/token.fig
Normal file
31
ExampleCodes/FigFig/token.fig
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Example code: FigFig
|
||||
|
||||
Token.fig
|
||||
Copyright (C) 2020-2026 PuqiAR
|
||||
|
||||
*/
|
||||
struct _TokenTypes
|
||||
{
|
||||
public EOF = -1;
|
||||
public Identifier = 0;
|
||||
|
||||
public StringLiteral = 1;
|
||||
public NumberLiteral = 2;
|
||||
public True = 3;
|
||||
public False = 4;
|
||||
public Null = 5;
|
||||
|
||||
public Plus = 6;
|
||||
public Minus = 7;
|
||||
public Asterisk = 8;
|
||||
public Slash = 9;
|
||||
}
|
||||
|
||||
public const TokenType := new _TokenTypes{};
|
||||
|
||||
public struct Token
|
||||
{
|
||||
public literal: String = "";
|
||||
public type: Int = TokenType.EOF;
|
||||
}
|
||||
102
ExampleCodes/FigFig/tokenizer.fig
Normal file
102
ExampleCodes/FigFig/tokenizer.fig
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Example code: FigFig
|
||||
|
||||
Tokenizer.fig
|
||||
Copyright (C) 2020-2026 PuqiAR
|
||||
|
||||
*/
|
||||
|
||||
import token {Token, TokenType};
|
||||
|
||||
func list_contains(lst: List, value: Any) -> Bool
|
||||
{
|
||||
for var i := 0; i < lst.length(); i += 1
|
||||
{
|
||||
if lst[i] == value
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
func isspace(c: String) -> Bool
|
||||
{
|
||||
return c == " " || c == "\n" || c == "\t";
|
||||
}
|
||||
|
||||
func isalpha(c: String) -> Bool
|
||||
{
|
||||
const alb := [
|
||||
"a", "b", "c", "d",
|
||||
"e", "f", "g", "h",
|
||||
"i", "j", "k", "l",
|
||||
"m", "n", "o", "p",
|
||||
"q", "r", "s", "t",
|
||||
"u", "v", "w", "x",
|
||||
"y", "z",
|
||||
"A", "B", "C", "D",
|
||||
"E", "F", "G", "H",
|
||||
"I", "J", "K", "L",
|
||||
"M", "N", "O", "P",
|
||||
"Q", "R", "S", "T",
|
||||
"U", "V", "W", "X",
|
||||
"Y", "Z"
|
||||
];
|
||||
return list_contains(alb, c);
|
||||
}
|
||||
|
||||
|
||||
public struct Tokenizer
|
||||
{
|
||||
src: String = "";
|
||||
idx: Int = 0;
|
||||
|
||||
func next() -> Null
|
||||
{
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
func hasNext() -> Bool
|
||||
{
|
||||
return idx < src.length();
|
||||
}
|
||||
|
||||
func produce() -> String
|
||||
{
|
||||
const tmp := src[idx];
|
||||
idx += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
func current() -> String
|
||||
{
|
||||
return src[idx];
|
||||
}
|
||||
|
||||
public func TokenizeAll() -> List
|
||||
{
|
||||
var output := [];
|
||||
|
||||
const push := func (tok: Token) => output.push(tok);
|
||||
|
||||
while hasNext()
|
||||
{
|
||||
while hasNext() && isspace(current())
|
||||
{
|
||||
next();
|
||||
}
|
||||
if isalpha(current())
|
||||
{
|
||||
var identi := "";
|
||||
while hasNext() && isalpha(current())
|
||||
{
|
||||
identi += produce();
|
||||
}
|
||||
push(new Token{identi, TokenType.Identifier});
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import std.io;
|
||||
import std.time;
|
||||
import std.value;
|
||||
|
||||
func benchmark(fn: Function, arg: Any) -> Null
|
||||
{
|
||||
@@ -58,8 +59,26 @@ func fib(x)
|
||||
return fib(x - 1) + fib(x - 2);
|
||||
}
|
||||
|
||||
const n := 28;
|
||||
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");
|
||||
|
||||
29
ExampleCodes/use_std_time.fig
Normal file
29
ExampleCodes/use_std_time.fig
Normal file
@@ -0,0 +1,29 @@
|
||||
import std.io;
|
||||
import std.test;
|
||||
|
||||
var ascii_string_test := new test.Test{
|
||||
"ascii_string_test",
|
||||
func () => io.println("Hello," + " world!"),
|
||||
2
|
||||
};
|
||||
|
||||
var unicode_string_test := new test.Test{
|
||||
"unicode_string_test",
|
||||
func () => io.println("你好," + " 世界!"),
|
||||
2
|
||||
};
|
||||
|
||||
var unicode_string_inserting_test := new test.Test{
|
||||
"unicode_string_inserting_test",
|
||||
func (){
|
||||
var str := "我是你的粑粑";
|
||||
str.insert(1, "不");
|
||||
return str;
|
||||
},
|
||||
"我不是你的粑粑"
|
||||
};
|
||||
|
||||
var tests := [ascii_string_test, unicode_string_test, unicode_string_inserting_test];
|
||||
|
||||
var tester := new test.Tester{tests};
|
||||
tester.TestAll();
|
||||
72
README.md
72
README.md
@@ -1,15 +1,18 @@
|
||||
# Fig Language - A Modern Scripting Language
|
||||
|
||||
[Fig-Gitea](https://git.fig-lang.cn/PuqiAR/Fig)
|
||||
Recommend view on Gitea Endpoint
|
||||
|
||||
[简体中文](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
|
||||
|
||||
### 🚀 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
|
||||
- **First-class functions** - Lambda expressions and closures
|
||||
- **Rich data structures** - Structs, lists, maps, and tuples
|
||||
@@ -31,6 +34,12 @@
|
||||
|
||||
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
|
||||
```
|
||||
@@ -55,35 +64,6 @@ xmake run Fig [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
|
||||
Fig is designed around several core principles:
|
||||
|
||||
@@ -94,3 +74,33 @@ Replace `[file]` with the path to your input file.
|
||||
Modern ergonomics - Developer experience matters
|
||||
|
||||
Gradual learning - Simple to start, powerful when needed
|
||||
|
||||
## Performance Summary
|
||||
|
||||
**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/benchmark_result_en_0.4.2-alpha.md) | [中文](./docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md)
|
||||
|
||||
|
||||
## Language Documents
|
||||
|
||||
see ./docs/en_US/...
|
||||
|
||||
We're looking for translators to help translate our project and make it accessible to more language communities.
|
||||
@@ -4,12 +4,14 @@
|
||||
|
||||
[English](README.md "英文版本")
|
||||
|
||||
**Fig** 是一种静态类型、面向表达式的编程语言,专为清晰性、安全性和现代开发实践而设计。Fig 融合了 Go、Rust 和 JavaScript 的灵感,旨在提供高效的开发体验,同时保持强大的类型安全。
|
||||
**Fig** 是一种动态强类型的编程语言,专为清晰性、安全性和现代开发实践而设计。Fig 融合了 Go、Rust 和 JavaScript 的灵感,旨在提供高效的开发体验,同时保持强大的类型安全。
|
||||
|
||||
[语言入门教程(zh_CN)](docs/zh_CN/01-简介.md "第一章")
|
||||
|
||||
## 特性
|
||||
|
||||
### 🚀 核心语言特性
|
||||
- **静态类型与类型推断** - 强类型系统,最少类型注解
|
||||
- **动态类型与类型推断** - 强类型系统,最少类型注解
|
||||
- **现代控制流** - 完整的 `for` 循环支持,正确的作用域管理
|
||||
- **一等公民函数** - Lambda 表达式和闭包
|
||||
- **丰富的数据结构** - 结构体、列表、映射和元组
|
||||
@@ -24,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
|
||||
@@ -42,43 +51,11 @@ xmake build Fig
|
||||
|
||||
4. 运行程序:
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
xmake run Fig [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 围绕几个核心原则设计:
|
||||
|
||||
@@ -86,3 +63,31 @@ Fig 围绕几个核心原则设计:
|
||||
2. **默认为安全** - 在编译时防止常见错误
|
||||
3. **现代人机工程学** - 开发者体验很重要
|
||||
4. **渐进式学习** - 入门简单,需要时功能强大
|
||||
|
||||
## 性能概览
|
||||
|
||||
**版本:** 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/benchmark_result_en_0.4.2-alpha.md) | [中文](./docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md)
|
||||
|
||||
|
||||
## 语言文档
|
||||
在 docs/zh_CN/... 查看更多
|
||||
我们正在寻找译者来帮助翻译项目文件以便于不同语言社区的使用
|
||||
117
docs/benchmark_result/benchmark_result_en_0.4.2-alpha.md
Normal file
117
docs/benchmark_result/benchmark_result_en_0.4.2-alpha.md
Normal 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.
|
||||
117
docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md
Normal file
117
docs/benchmark_result/benchmark_result_zh_0.4.2-alpha.md
Normal 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 阶段的树遍历解释器,但它能高效执行优化算法。
|
||||
24
docs/zh_CN/01-简介.md
Normal file
24
docs/zh_CN/01-简介.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Fig 语言简介
|
||||
|
||||
## 概述
|
||||
Fig 是一门动态类型、解释执行的编程语言,专注于简洁语法和实用的语言特性。它采用树遍历解释器架构,支持多种编程范式。
|
||||
|
||||
## 实际观察到的特性
|
||||
1. **解释执行**:基于 AST 的树遍历解释器,无编译步骤
|
||||
2. **动态类型系统**:运行时类型检查,支持类型注解但不强制
|
||||
3. **混合范式**:支持函数式、面向对象和命令式风格
|
||||
4. **模块系统**:支持代码组织和复用
|
||||
5. **内置类型**:整数、浮点数、字符串、列表、映射等
|
||||
6. **垃圾回收**:基于引用计数的自动内存管理
|
||||
|
||||
## 语言设计特点
|
||||
- **渐进类型**:支持类型注解但不强制,兼顾灵活性和可读性
|
||||
- **一等公民函数**:函数可以作为参数传递和返回
|
||||
- **闭包支持**:完整的词法作用域和闭包
|
||||
- **错误处理**:异常机制和 try-catch 结构
|
||||
- **可变与不可变**:const/var 区分,平衡安全与灵活
|
||||
|
||||
## 目标应用场景
|
||||
- 脚本编写和自动化任务
|
||||
- 教育用途和学习编程
|
||||
- 配置语言和DSL
|
||||
82
docs/zh_CN/02-快速开始.md
Normal file
82
docs/zh_CN/02-快速开始.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 快速开始
|
||||
|
||||
## 运行 Fig 程序
|
||||
|
||||
Fig 语言通过解释器直接执行源代码文件。基本执行命令如下:
|
||||
|
||||
`./Fig 你的脚本.fig`
|
||||
|
||||
### 查看帮助和版本
|
||||
|
||||
显示帮助信息:
|
||||
`./Fig -h` 或 `./Fig --help`
|
||||
|
||||
显示版本信息:
|
||||
`./Fig -v` 或 `./Fig --version`
|
||||
|
||||
### 示例
|
||||
|
||||
创建一个名为 `hello.fig` 的文件,内容为:
|
||||
|
||||
```go
|
||||
import std.io;
|
||||
io.println("Hello, Fig!");
|
||||
```
|
||||
|
||||
在终端中运行:
|
||||
`./Fig hello.fig`
|
||||
|
||||
你会看到输出:`Hello, Fig!`
|
||||
|
||||
## 程序示例
|
||||
|
||||
### 简单表达式程序
|
||||
|
||||
Fig 程序可以只包含表达式:
|
||||
|
||||
```go
|
||||
1 + 2 * 3
|
||||
```
|
||||
|
||||
运行此程序会输出计算结果 `7`。
|
||||
|
||||
### 带变量的程序
|
||||
|
||||
```go
|
||||
var x = 10;
|
||||
var y = x * 2;
|
||||
y + 5
|
||||
```
|
||||
|
||||
运行输出 `25`。
|
||||
|
||||
## 错误处理
|
||||
|
||||
当源代码有语法或类型错误时,解释器会显示详细的错误信息。例如:
|
||||
|
||||
```rust
|
||||
An error occurred! Fig 0.4.2-alpha (2026-01-23 01:30:46)[llvm-mingw 64 bit on `Windows`]
|
||||
✖ TypeError: Variable `x` expects init-value type `Int`, but got 'Double'
|
||||
at 1:14 in file 'your_file.fig'
|
||||
var x: Int = 3.14;
|
||||
^
|
||||
```
|
||||
|
||||
错误信息包括:
|
||||
- 错误类型和描述
|
||||
- 发生错误的文件和位置
|
||||
- 相关的堆栈跟踪信息
|
||||
|
||||
## 运行流程
|
||||
|
||||
1. **编写代码**:使用任何文本编辑器创建 `.fig` 文件
|
||||
2. **保存文件**:确保文件扩展名为 `.fig`
|
||||
3. **执行程序**:在终端中运行 `./Fig 文件名.fig`
|
||||
4. **查看结果**:程序输出显示在终端中
|
||||
5. **调试错误**:根据错误信息修改代码
|
||||
|
||||
## 注意事项
|
||||
|
||||
- Fig 源文件必须使用 UTF-8 编码
|
||||
- 语句通常以分号结束,但程序最后一条表达式可以省略分号
|
||||
- 文件路径可以包含空格,但建议使用引号包裹:`./Fig "my script.fig"`
|
||||
213
docs/zh_CN/03-基础语法.md
Normal file
213
docs/zh_CN/03-基础语法.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# 基础语法
|
||||
|
||||
## 注释
|
||||
|
||||
Fig 支持两种注释格式:
|
||||
|
||||
单行注释:
|
||||
```go
|
||||
// 这是一个单行注释
|
||||
var x = 10; // 注释可以在语句后面
|
||||
```
|
||||
|
||||
多行注释:
|
||||
```go
|
||||
/* 这是一个
|
||||
多行注释 */
|
||||
/* 注释内可以有 // 嵌套的单行注释 */
|
||||
```
|
||||
|
||||
注释不能嵌套多个多行注释:`/* /* 嵌套 */ */` 会导致错误。
|
||||
|
||||
## 变量声明
|
||||
|
||||
### 可变变量
|
||||
```go
|
||||
var name = "Fig";
|
||||
var count = 0;
|
||||
count = count + 1; // 可以重新赋值
|
||||
```
|
||||
|
||||
### 常量
|
||||
```go
|
||||
const PI = 3.14159;
|
||||
const MAX_SIZE = 100;
|
||||
// PI = 3.14; // 错误:常量不能重新赋值
|
||||
```
|
||||
|
||||
常量必须在声明时初始化。
|
||||
|
||||
### 类型注解(可选)
|
||||
```go
|
||||
var name: String = "Fig";
|
||||
var age: Int = 14;
|
||||
const VERSION: Double = 0.4;
|
||||
```
|
||||
|
||||
类型注解提供额外的类型信息,但解释器会在运行时进行类型检查。
|
||||
|
||||
## 标识符命名
|
||||
|
||||
### 规则
|
||||
- 可以包含字母、数字和下划线
|
||||
- 不能以数字开头
|
||||
- 区分大小写
|
||||
- 支持 Unicode 字符
|
||||
|
||||
### 有效示例
|
||||
```go
|
||||
var count = 0;
|
||||
var user_name = "Alice";
|
||||
var 计数器 = 0; // 中文标识符
|
||||
var π = 3.14159; // Unicode 符号
|
||||
var _private = true; // 以下划线开头
|
||||
```
|
||||
|
||||
### 无效示例
|
||||
```
|
||||
var 123abc = 0; // 不能以数字开头
|
||||
var my-var = 0; // 不能包含连字符
|
||||
```
|
||||
|
||||
## 基本字面量
|
||||
|
||||
### 数字
|
||||
```go
|
||||
// 整数
|
||||
var a = 42; // 十进制
|
||||
var b = -100; // 负数
|
||||
var c = 0; // 零
|
||||
|
||||
// 浮点数
|
||||
var d = 3.14;
|
||||
var e = 1.0;
|
||||
var f = -0.5;
|
||||
var g = 1.23e-10; // 科学计数法
|
||||
```
|
||||
|
||||
### 字符串
|
||||
```go
|
||||
var s1 = "Hello";
|
||||
var s2 = "World";
|
||||
var s3 = "包含\"引号\"的字符串"; // 转义引号
|
||||
var s4 = "第一行\n第二行"; // 转义换行符
|
||||
```
|
||||
|
||||
多行字符串直接跨行书写:
|
||||
```rust
|
||||
var message = "这是一个
|
||||
多行字符串
|
||||
可以包含多行内容";
|
||||
```
|
||||
|
||||
### 布尔值
|
||||
```go
|
||||
var yes = true;
|
||||
var no = false;
|
||||
```
|
||||
|
||||
### 空值
|
||||
```dart
|
||||
var nothing = null;
|
||||
```
|
||||
|
||||
## 分号使用
|
||||
|
||||
所有语句都必须以分号结束:
|
||||
|
||||
```go
|
||||
var x = 10; // 正确
|
||||
var y = 20 // 错误:缺少分号
|
||||
|
||||
func add(a, b) {
|
||||
return a + b; // return 语句需要分号
|
||||
} // 函数体右花括号后不需要分号
|
||||
```
|
||||
|
||||
表达式作为独立语句时也需要分号:
|
||||
```go
|
||||
1 + 2 * 3; // 表达式语句需要分号
|
||||
io.println("test"); // 函数调用语句需要分号
|
||||
```
|
||||
|
||||
## 表达式与语句
|
||||
|
||||
### 表达式
|
||||
表达式会产生一个值(包括 `null`):
|
||||
```go
|
||||
1 + 2 // 值为 3
|
||||
x * y // 值为乘积
|
||||
funcCall(arg) // 值为函数返回值
|
||||
```
|
||||
|
||||
### 语句
|
||||
语句执行操作但不产生值:
|
||||
```go
|
||||
var x = 10; // 声明语句
|
||||
if condition {} // 条件语句
|
||||
return value; // 返回语句
|
||||
```
|
||||
|
||||
表达式可以作为语句使用(表达式语句):
|
||||
```go
|
||||
1 + 2; // 计算但丢弃结果
|
||||
io.println("hi"); // 函数调用作为语句
|
||||
```
|
||||
|
||||
## 关键字
|
||||
|
||||
Fig 语言的关键字包括:
|
||||
|
||||
| 类别 | 关键字 |
|
||||
| -------- | ----------------------------------------------------------- |
|
||||
| 声明 | `func`, `var`, `const`, `struct`, `interface`, `import` |
|
||||
| 控制流 | `if`, `else`, `while`, `for`, `return`, `break`, `continue` |
|
||||
| 错误处理 | `try`, `catch`, `throw`, `Finally` |
|
||||
| 逻辑运算 | `and`, `or`, `not` |
|
||||
| 类型相关 | `is`, `impl`, `new`, `public` |
|
||||
|
||||
这些关键字不能用作标识符名称。
|
||||
|
||||
## 操作符
|
||||
|
||||
### 算术运算符
|
||||
`+`, `-`, `*`, `/`, `%`, `**`(幂运算)
|
||||
|
||||
### 比较运算符
|
||||
`==`, `!=`, `<`, `>`, `<=`, `>=`
|
||||
|
||||
### 逻辑运算符
|
||||
`and`, `or`, `not`, `&&`, `||`, `!`
|
||||
|
||||
### 位运算符
|
||||
`&`, `|`, `^`, `~`, `<<`, `>>`
|
||||
|
||||
### 赋值运算符
|
||||
`=`, `:=`, `+=`, `-=`, `*=`, `/=`, `%=`, `^=`
|
||||
|
||||
### 其他运算符
|
||||
`.`(成员访问), `?`(三元运算), `...`(可变参数), `->`(箭头), `=>`(双箭头)
|
||||
|
||||
## 空白字符
|
||||
|
||||
Fig 对空白字符没有特殊要求:
|
||||
- 空格和制表符可以互换使用
|
||||
- 缩进不影响程序语义
|
||||
- 操作符周围的空格是可选的(但建议添加以提高可读性)
|
||||
|
||||
## 代码示例
|
||||
|
||||
```go
|
||||
// 完整的程序示例
|
||||
import std.io;
|
||||
|
||||
func calculate(a: Int, b: Int) -> Int {
|
||||
var result = a * b;
|
||||
return result + 10;
|
||||
}
|
||||
|
||||
const x = 5;
|
||||
const y = 3;
|
||||
var answer = calculate(x, y);
|
||||
io.println("结果是:" + answer.toString());
|
||||
```
|
||||
156
docs/zh_CN/04-数据类型.md
Normal file
156
docs/zh_CN/04-数据类型.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# 数据类型
|
||||
|
||||
## 类型系统
|
||||
|
||||
Fig 语言是动态类型语言,运行时进行类型检查。
|
||||
|
||||
## 基本类型
|
||||
|
||||
### Null 类型
|
||||
表示空值,只有一个值 `null`。
|
||||
```dart
|
||||
var a = null;
|
||||
```
|
||||
|
||||
### Int 类型
|
||||
64位有符号整数。
|
||||
```go
|
||||
var x = 42;
|
||||
var y = -100;
|
||||
```
|
||||
|
||||
### Double 类型
|
||||
双精度浮点数。
|
||||
```go
|
||||
var pi = 3.14;
|
||||
var speed = 2.998e8;
|
||||
```
|
||||
|
||||
### String 类型
|
||||
字符串。
|
||||
```go
|
||||
var s1 = "Hello";
|
||||
```
|
||||
|
||||
### Bool 类型
|
||||
布尔值,`true` 或 `false`。
|
||||
```go
|
||||
var yes = true;
|
||||
var no = false;
|
||||
```
|
||||
|
||||
## 复合类型
|
||||
|
||||
### List 类型
|
||||
有序集合。
|
||||
```go
|
||||
var list = [1, 2, 3];
|
||||
var mixed = [1, "text", true];
|
||||
```
|
||||
可以通过 **length()** 方法获取长度,返回Int
|
||||
通过 **get()** 方法获取指定下标,不存在的下标返回null
|
||||
|
||||
### Map 类型
|
||||
键值对集合。
|
||||
```go
|
||||
var map = {"key": "value", "num": 42};
|
||||
```
|
||||
|
||||
通过 **get()** 方法获取指定下标,不存在的下标返回 `null`
|
||||
|
||||
### Function 类型
|
||||
函数。
|
||||
```go
|
||||
var f = func(x, y) { return x + y; };
|
||||
```
|
||||
|
||||
## 自定义类型
|
||||
|
||||
### 结构体定义
|
||||
```rust
|
||||
struct Person {
|
||||
name: String;
|
||||
age: Int;
|
||||
}
|
||||
```
|
||||
|
||||
默认字段为私有,如需声明为外部可见的字段,使用 public关键字
|
||||
如
|
||||
```go
|
||||
struct Person {
|
||||
public name: String;
|
||||
public age: Int;
|
||||
|
||||
public func greeting()
|
||||
{
|
||||
io.println("hello!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 创建实例
|
||||
使用 `new` 关键字:
|
||||
```go
|
||||
var p = new Person {name: "Alice", age: 20};
|
||||
```
|
||||
|
||||
支持三种初始化方式:
|
||||
1. 命名参数:`new Person {name: "Alice", age: 20}`
|
||||
2. 位置参数:`new Person {"Alice", 20}`
|
||||
3. 简写方式:`new Person {name, age}`(使用同名变量)
|
||||
|
||||
## 类型操作
|
||||
|
||||
### 类型检查
|
||||
使用 `is` 运算符:
|
||||
```go
|
||||
var x = "hello";
|
||||
io.println(x is String); // true
|
||||
|
||||
var p = new Person {name: "Alice", age: 20};
|
||||
io.println(p is Person); // true
|
||||
```
|
||||
|
||||
### 获取类型
|
||||
使用 `value.type()` 函数(获取内部类型):
|
||||
```go
|
||||
import std.value;
|
||||
|
||||
var num = 42;
|
||||
var str = "hello";
|
||||
var lst = [1, 2, 3];
|
||||
|
||||
io.println(value.type(num)); // 获取 num 的类型
|
||||
io.println(value.type(str)); // 获取 str 的类型
|
||||
io.println(value.type(lst)); // 获取 lst 的类型
|
||||
```
|
||||
|
||||
## 类型转换
|
||||
|
||||
### 转换函数
|
||||
通过 `std.value` 模块:
|
||||
```go
|
||||
import std.value;
|
||||
|
||||
var str = value.string_from(42); // "42"
|
||||
var num = value.int_parse("123"); // 123
|
||||
var dbl = value.double_parse("3.14"); // 3.14
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### try-catch 语法
|
||||
```rust
|
||||
try
|
||||
{
|
||||
// 可能抛出异常的代码
|
||||
}
|
||||
catch(e: ErrorType)
|
||||
{
|
||||
// 处理特定类型的错误
|
||||
}
|
||||
catch(e: AnotherError)
|
||||
{
|
||||
// 处理另一种错误
|
||||
}
|
||||
```
|
||||
289
docs/zh_CN/05-函数.md
Normal file
289
docs/zh_CN/05-函数.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# 函数
|
||||
|
||||
## 函数定义
|
||||
|
||||
### 基本语法
|
||||
```go
|
||||
func 函数名(参数列表) -> 返回类型 {
|
||||
函数体
|
||||
}
|
||||
```
|
||||
|
||||
### 示例
|
||||
```go
|
||||
// 简单函数
|
||||
func greet() -> Null {
|
||||
io.println("Hello!");
|
||||
}
|
||||
|
||||
// 带参数的函数
|
||||
func add(a: Int, b: Int) -> Int {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// 省略返回类型(默认为 Any)
|
||||
func say(message: String) {
|
||||
io.println(message);
|
||||
// 没有 return,返回 null
|
||||
}
|
||||
```
|
||||
|
||||
## 参数
|
||||
|
||||
### 必需参数
|
||||
```go
|
||||
func power(base: Double, exponent: Double) -> Double {
|
||||
return base ** exponent;
|
||||
}
|
||||
```
|
||||
|
||||
### 默认参数
|
||||
参数可以指定默认值:
|
||||
```go
|
||||
func createPerson(name: String, age: Int = 18) -> Null {
|
||||
io.println(name + " is " + age + " years old");
|
||||
}
|
||||
|
||||
// 调用
|
||||
createPerson("Alice"); // 使用默认 age=18
|
||||
createPerson("Bob", 25); // 指定 age=25
|
||||
```
|
||||
|
||||
### 可变参数
|
||||
使用 `...` 表示可变参数,接收一个 List:
|
||||
```go
|
||||
func sum(numbers...) -> Int {
|
||||
var total = 0;
|
||||
var i = 0;
|
||||
for i = 0; i < numbers.length(); i = i + 1 {
|
||||
total = total + numbers.get(i);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// 调用
|
||||
sum(1, 2, 3); // 返回 6
|
||||
sum(1, 2, 3, 4, 5); // 返回 15
|
||||
```
|
||||
|
||||
可变参数不支持类型限制,获取到的总是 List 类型。
|
||||
|
||||
## 返回值
|
||||
|
||||
### 显式返回
|
||||
使用 `return` 语句返回值:
|
||||
```go
|
||||
func max(a: Int, b: Int) -> Int {
|
||||
if a > b {
|
||||
return a;
|
||||
} else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 无返回值
|
||||
函数如果没有 return 语句,返回 null:
|
||||
```go
|
||||
func log(message: String) -> Null {
|
||||
io.println("[LOG] " + message);
|
||||
// 函数结束,返回 null
|
||||
}
|
||||
|
||||
func process(data: List) {
|
||||
if data.length() == 0 {
|
||||
return; // 提前返回 null
|
||||
}
|
||||
// 处理数据...
|
||||
// 函数结束,返回 null
|
||||
}
|
||||
```
|
||||
|
||||
## 函数作为值
|
||||
|
||||
### 函数赋值
|
||||
函数是一等公民,可以赋值给变量:
|
||||
```go
|
||||
var addFunc = func(a: Int, b: Int) -> Int {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
// 调用
|
||||
var result = addFunc(3, 4); // result = 7
|
||||
```
|
||||
|
||||
### 函数作为参数
|
||||
```go
|
||||
func applyOperation(x: Int, y: Int, op: Function) -> Int {
|
||||
return op(x, y);
|
||||
}
|
||||
|
||||
func multiply(a: Int, b: Int) -> Int {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// 调用
|
||||
var product = applyOperation(3, 4, multiply); // product = 12
|
||||
```
|
||||
|
||||
### 函数作为返回值
|
||||
```go
|
||||
func makeMultiplier(factor: Int) -> Function {
|
||||
return func(x: Int) -> Int {
|
||||
return x * factor;
|
||||
};
|
||||
}
|
||||
|
||||
// 调用
|
||||
var double = makeMultiplier(2);
|
||||
var triple = makeMultiplier(3);
|
||||
|
||||
io.println(double(5)); // 输出 10
|
||||
io.println(triple(5)); // 输出 15
|
||||
```
|
||||
|
||||
## 匿名函数
|
||||
|
||||
### Lambda 表达式
|
||||
```go
|
||||
// 完整形式
|
||||
var square = func(x: Int) {
|
||||
return x * x;
|
||||
};
|
||||
|
||||
// 简写形式(单表达式)
|
||||
var squareShort = func(x: Int) => x * x;
|
||||
|
||||
// 调用
|
||||
io.println(square(5)); // 输出 25
|
||||
io.println(squareShort(5)); // 输出 25
|
||||
```
|
||||
|
||||
### 立即调用函数表达式
|
||||
```go
|
||||
var result = func(x: Int, y: Int){
|
||||
return x + y;
|
||||
}(3, 4); // result = 7
|
||||
```
|
||||
|
||||
## 闭包
|
||||
|
||||
### 捕获外部变量
|
||||
函数可以捕获其定义作用域中的变量:
|
||||
```go
|
||||
func makeCounter() -> Function {
|
||||
var count = 0;
|
||||
|
||||
return func(){
|
||||
count = count + 1;
|
||||
return count;
|
||||
};
|
||||
}
|
||||
|
||||
// 使用
|
||||
var counter = makeCounter();
|
||||
io.println(counter()); // 输出 1
|
||||
io.println(counter()); // 输出 2
|
||||
io.println(counter()); // 输出 3
|
||||
```
|
||||
|
||||
每个闭包有自己独立的捕获变量:
|
||||
```go
|
||||
var c1 = makeCounter();
|
||||
var c2 = makeCounter();
|
||||
|
||||
io.println(c1()); // 输出 1
|
||||
io.println(c1()); // 输出 2
|
||||
io.println(c2()); // 输出 1(独立的计数)
|
||||
```
|
||||
|
||||
## 递归函数
|
||||
|
||||
函数可以调用自身:
|
||||
```go
|
||||
func factorial(n: Int) -> Int {
|
||||
if n <= 1 {
|
||||
return 1;
|
||||
}
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
// 调用
|
||||
io.println(factorial(5)); // 输出 120
|
||||
```
|
||||
|
||||
### 嵌套函数定义
|
||||
```go
|
||||
func outer(x: Int) -> Int {
|
||||
func inner(y: Int) -> Int {
|
||||
return y * 2;
|
||||
}
|
||||
|
||||
return inner(x) + 1;
|
||||
}
|
||||
|
||||
// 调用
|
||||
io.println(outer(10)); // 输出 21
|
||||
```
|
||||
|
||||
## 函数调用
|
||||
|
||||
### 普通调用
|
||||
```go
|
||||
func calculate(a: Int, b: Int, c: Int) -> Int {
|
||||
return a + b * c;
|
||||
}
|
||||
|
||||
// 位置参数调用
|
||||
var result = calculate(1, 2, 3); // 1 + 2*3 = 7
|
||||
```
|
||||
|
||||
### 方法调用语法
|
||||
对象的方法调用:
|
||||
```go
|
||||
var list = [1, 2, 3];
|
||||
var length = list.length(); // 方法调用
|
||||
```
|
||||
|
||||
## 函数示例
|
||||
|
||||
### 实用函数组合
|
||||
```go
|
||||
import std.io;
|
||||
|
||||
// 高阶函数示例
|
||||
func compose(f: Function, g: Function) -> Function {
|
||||
return func(x: Any) -> Any {
|
||||
return f(g(x));
|
||||
};
|
||||
}
|
||||
|
||||
// 使用
|
||||
func addOne(x: Int) -> Int {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
func double(x: Int) -> Int {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
var addThenDouble = compose(double, addOne);
|
||||
io.println(addThenDouble(5)); // (5+1)*2 = 12
|
||||
```
|
||||
|
||||
### 回调函数模式
|
||||
```go
|
||||
func processData(data: List, callback: Function) -> Null {
|
||||
var i = 0;
|
||||
for i = 0; i < data.length(); i = i + 1 {
|
||||
var result = callback(data.get(i));
|
||||
io.println("处理结果: " + result);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用
|
||||
var numbers = [1, 2, 3, 4, 5];
|
||||
processData(numbers, func(x){
|
||||
return x * x;
|
||||
});
|
||||
```
|
||||
126
docs/zh_CN/06-面对对象.md
Normal file
126
docs/zh_CN/06-面对对象.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 面对对象
|
||||
|
||||
> Fig中只有结构体(`struct`) 遵循go圣经 组合由于继承 (struct组合尚未推出)
|
||||
|
||||
## 结构体定义
|
||||
完整语法:
|
||||
```cpp
|
||||
struct Point
|
||||
{
|
||||
public x: Int; // 公开字段
|
||||
public y: Int; // 公开字段
|
||||
}
|
||||
|
||||
struct Person
|
||||
{
|
||||
name: String; // 私有字段,无法被外部访问
|
||||
age: Int; // 私有字段,无法被外部访问
|
||||
sex: String = "Airplane"; // 私有字段,无法被外部访问
|
||||
|
||||
public func getName() -> String // 公开类函数
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 结构体初始化
|
||||
语法:
|
||||
```go
|
||||
|
||||
// 位置参数构造
|
||||
var person := new Person{
|
||||
"Fig", // name
|
||||
1, // age
|
||||
"Language" // sex
|
||||
};
|
||||
```
|
||||
|
||||
```go
|
||||
// 命名参数构造模式,可以无序
|
||||
var person := new Person{
|
||||
name: "Fig",
|
||||
age: 1,
|
||||
sex: "Language"
|
||||
};
|
||||
```
|
||||
|
||||
```go
|
||||
// 语法糖:同名变量构造
|
||||
const name := "Fig";
|
||||
const age := 1;
|
||||
const sex := "Language";
|
||||
|
||||
var person := new Person{sex, name, age}; // 可以无序,自动匹配
|
||||
```
|
||||
|
||||
请注意,同名变量构造(shorthand)模式请规范使用:
|
||||
|
||||
示例:定义
|
||||
```cpp
|
||||
struct Point
|
||||
{
|
||||
public x;
|
||||
public y;
|
||||
}
|
||||
```
|
||||
使用:
|
||||
```go
|
||||
var a := Point{1,2};
|
||||
io.println(a.x, a.y); // 1 2
|
||||
|
||||
var x := 7;
|
||||
var y := 6;
|
||||
var b := Point{y, x};
|
||||
io.println(b.x, b.y); // 7 6
|
||||
// ??
|
||||
```
|
||||
|
||||
使用该模式最好有序构造,如果你清楚你在做什么,完全可以利用它
|
||||
|
||||
## 结构体运算符重载
|
||||
|
||||
**目前不支持**
|
||||
|
||||
## 接口与实现
|
||||
`interface` 与 `implement`
|
||||
|
||||
### 定义接口
|
||||
```go
|
||||
interface Printable
|
||||
{
|
||||
toString() -> String;
|
||||
getName() -> String
|
||||
{
|
||||
return "Printable";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用 `interface` + 名字 {内容}定义结构体
|
||||
方法签名为 `method() -> type`,其中必须提供返回类型,这是约定。
|
||||
提供默认方法将在子类实现为实现情况下自动替补
|
||||
|
||||
### 实现
|
||||
```rust
|
||||
struct Person
|
||||
{
|
||||
name: String;
|
||||
age: Int;
|
||||
}
|
||||
|
||||
impl Printable for Person
|
||||
{
|
||||
toString()
|
||||
{
|
||||
return name + ":" + age;
|
||||
}
|
||||
getName()
|
||||
{
|
||||
return "Person";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
实现时不需要也不允许提供返回类型,必须与`interface`约定一致
|
||||
内部通过动态派发 vtable实现
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "fig-vscode",
|
||||
"displayName": "Fig Language",
|
||||
"description": "VSCode extension for Fig language with syntax highlighting",
|
||||
"version": "0.4.2",
|
||||
"version": "0.4.3",
|
||||
"publisher": "PuqiAR",
|
||||
"engines": {
|
||||
"vscode": "^1.90.0"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.fig",
|
||||
"match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|new|struct|interface|impl|public|return|break|continue|try|catch|throw)\\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" }
|
||||
]
|
||||
@@ -78,7 +78,7 @@
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator.comparison.fig",
|
||||
"match": "(==|!=|<=|>=|<|>|is)"
|
||||
"match": "(==|!=|<=|>=|<|>)"
|
||||
},
|
||||
{
|
||||
"name": "punctuation.separator.fig",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
@@ -24,31 +24,26 @@ namespace Fig::Ast
|
||||
Expression callee;
|
||||
FunctionArguments arg;
|
||||
|
||||
FunctionCallExpr()
|
||||
FunctionCallExpr() { type = AstType::FunctionCall; }
|
||||
|
||||
FunctionCallExpr(Expression _callee, FunctionArguments _arg) : callee(std::move(_callee)), arg(std::move(_arg))
|
||||
{
|
||||
type = AstType::FunctionCall;
|
||||
}
|
||||
|
||||
FunctionCallExpr(Expression _callee, FunctionArguments _arg) :
|
||||
callee(std::move(_callee)), arg(std::move(_arg))
|
||||
virtual String toString() override
|
||||
{
|
||||
type = AstType::FunctionCall;
|
||||
}
|
||||
|
||||
virtual FString toString() override
|
||||
{
|
||||
FString s = callee->toString();
|
||||
s += u8"(";
|
||||
String s = callee->toString();
|
||||
s += U"(";
|
||||
for (size_t i = 0; i < arg.argv.size(); ++i)
|
||||
{
|
||||
s += arg.argv[i]->toString();
|
||||
if (i + 1 < arg.argv.size())
|
||||
s += u8", ";
|
||||
if (i + 1 < arg.argv.size()) s += U",";
|
||||
}
|
||||
s += u8")";
|
||||
s += U")";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
using FunctionCall = std::shared_ptr<FunctionCallExpr>;
|
||||
}; // namespace Fig
|
||||
}; // namespace Fig::Ast
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <variant>
|
||||
|
||||
namespace Fig::Ast
|
||||
@@ -25,20 +25,11 @@ namespace Fig::Ast
|
||||
type = AstType::FunctionLiteralExpr;
|
||||
}
|
||||
|
||||
bool isExprMode() const
|
||||
{
|
||||
return std::holds_alternative<Expression>(body);
|
||||
}
|
||||
bool isExprMode() const { return std::holds_alternative<Expression>(body); }
|
||||
|
||||
BlockStatement &getBlockBody()
|
||||
{
|
||||
return std::get<BlockStatement>(body);
|
||||
}
|
||||
BlockStatement &getBlockBody() { return std::get<BlockStatement>(body); }
|
||||
|
||||
Expression &getExprBody()
|
||||
{
|
||||
return std::get<Expression>(body);
|
||||
}
|
||||
Expression &getExprBody() { return std::get<Expression>(body); }
|
||||
|
||||
~FunctionLiteralExprAst() = default;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Fig::Ast
|
||||
public:
|
||||
Expression structe;
|
||||
|
||||
std::vector<std::pair<FString, Expression>> args;
|
||||
std::vector<std::pair<String, Expression>> args;
|
||||
|
||||
enum class InitMode
|
||||
{
|
||||
@@ -25,12 +25,9 @@ namespace Fig::Ast
|
||||
.3 Person {name, age, sex};
|
||||
*/
|
||||
|
||||
InitExprAst()
|
||||
{
|
||||
type = AstType::InitExpr;
|
||||
}
|
||||
InitExprAst() { type = AstType::InitExpr; }
|
||||
|
||||
InitExprAst(Expression _structe, std::vector<std::pair<FString, Expression>> _args, InitMode _initMode) :
|
||||
InitExprAst(Expression _structe, std::vector<std::pair<String, Expression>> _args, InitMode _initMode) :
|
||||
structe(std::move(_structe)), args(std::move(_args)), initMode(_initMode)
|
||||
{
|
||||
type = AstType::InitExpr;
|
||||
|
||||
@@ -11,15 +11,11 @@ namespace Fig::Ast
|
||||
{
|
||||
public:
|
||||
Expression base;
|
||||
FString member;
|
||||
String member;
|
||||
|
||||
MemberExprAst()
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
MemberExprAst() { type = AstType::MemberExpr; }
|
||||
|
||||
MemberExprAst(Expression _base, FString _member) :
|
||||
base(std::move(_base)), member(std::move(_member))
|
||||
MemberExprAst(Expression _base, String _member) : base(std::move(_base)), member(std::move(_member))
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
@@ -33,13 +29,9 @@ namespace Fig::Ast
|
||||
Expression base;
|
||||
Expression index;
|
||||
|
||||
IndexExprAst()
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
}
|
||||
IndexExprAst() { type = AstType::IndexExpr; }
|
||||
|
||||
IndexExprAst(Expression _base, Expression _index) :
|
||||
base(std::move(_base)), index(std::move(_index))
|
||||
IndexExprAst(Expression _base, Expression _index) : base(std::move(_base)), index(std::move(_index))
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
#include <Value/value.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
|
||||
@@ -7,18 +7,10 @@ namespace Fig::Ast
|
||||
class VarExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
const FString name;
|
||||
VarExprAst() :
|
||||
name(u8"")
|
||||
{
|
||||
type = AstType::VarExpr;
|
||||
}
|
||||
VarExprAst(FString _name) :
|
||||
name(std::move(_name))
|
||||
{
|
||||
type = AstType::VarExpr;
|
||||
}
|
||||
const String name;
|
||||
VarExprAst() : name(U"") { type = AstType::VarExpr; }
|
||||
VarExprAst(String _name) : name(std::move(_name)) { type = AstType::VarExpr; }
|
||||
};
|
||||
|
||||
using VarExpr = std::shared_ptr<VarExprAst>;
|
||||
}; // namespace Fig
|
||||
}; // namespace Fig::Ast
|
||||
@@ -9,34 +9,26 @@ namespace Fig::Ast
|
||||
public:
|
||||
Expression value;
|
||||
|
||||
ThrowSt()
|
||||
{
|
||||
type = AstType::ThrowSt;
|
||||
}
|
||||
ThrowSt() { type = AstType::ThrowSt; }
|
||||
|
||||
ThrowSt(Expression _value) :
|
||||
value(std::move(_value))
|
||||
{
|
||||
type = AstType::ThrowSt;
|
||||
}
|
||||
ThrowSt(Expression _value) : value(std::move(_value)) { type = AstType::ThrowSt; }
|
||||
};
|
||||
using Throw = std::shared_ptr<ThrowSt>;
|
||||
|
||||
struct Catch
|
||||
{
|
||||
FString errVarName;
|
||||
String errVarName;
|
||||
bool hasType = false;
|
||||
FString errVarType;
|
||||
String errVarType;
|
||||
BlockStatement body;
|
||||
|
||||
Catch() {}
|
||||
Catch(FString _errVarName, FString _errVarType, BlockStatement _body) :
|
||||
Catch(String _errVarName, String _errVarType, BlockStatement _body) :
|
||||
errVarName(std::move(_errVarName)), errVarType(std::move(_errVarType)), body(std::move(_body))
|
||||
{
|
||||
hasType = true;
|
||||
}
|
||||
Catch(FString _errVarName, BlockStatement _body) :
|
||||
errVarName(std::move(_errVarName)), body(std::move(_body))
|
||||
Catch(String _errVarName, BlockStatement _body) : errVarName(std::move(_errVarName)), body(std::move(_body))
|
||||
{
|
||||
hasType = false;
|
||||
}
|
||||
@@ -49,10 +41,7 @@ namespace Fig::Ast
|
||||
std::vector<Catch> catches;
|
||||
BlockStatement finallyBlock = nullptr;
|
||||
|
||||
TrySt()
|
||||
{
|
||||
type = AstType::TrySt;
|
||||
}
|
||||
TrySt() { type = AstType::TrySt; }
|
||||
TrySt(BlockStatement _body, std::vector<Catch> _catches, BlockStatement _finallyBlock) :
|
||||
body(std::move(_body)), catches(std::move(_catches)), finallyBlock(std::move(_finallyBlock))
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
|
||||
#include <Value/value.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
@@ -20,17 +20,15 @@ namespace Fig::Ast
|
||||
class FunctionDefSt final : public StatementAst // for definition
|
||||
{
|
||||
public:
|
||||
FString name;
|
||||
String name;
|
||||
FunctionParameters paras;
|
||||
bool isPublic;
|
||||
Expression retType;
|
||||
BlockStatement body;
|
||||
|
||||
FunctionDefSt()
|
||||
{
|
||||
type = AstType::FunctionDefSt;
|
||||
}
|
||||
FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, Expression _retType, BlockStatement _body)
|
||||
FunctionDefSt() { type = AstType::FunctionDefSt; }
|
||||
FunctionDefSt(
|
||||
String _name, FunctionParameters _paras, bool _isPublic, Expression _retType, BlockStatement _body)
|
||||
{
|
||||
type = AstType::FunctionDefSt;
|
||||
|
||||
@@ -42,4 +40,4 @@ namespace Fig::Ast
|
||||
}
|
||||
};
|
||||
using FunctionDef = std::shared_ptr<FunctionDefSt>;
|
||||
}; // namespace Fig
|
||||
}; // namespace Fig::Ast
|
||||
@@ -8,19 +8,9 @@ namespace Fig::Ast
|
||||
{
|
||||
public:
|
||||
BlockStatement body;
|
||||
ElseSt()
|
||||
{
|
||||
type = AstType::ElseSt;
|
||||
}
|
||||
ElseSt(BlockStatement _body) :
|
||||
body(_body)
|
||||
{
|
||||
type = AstType::ElseSt;
|
||||
}
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<Else Ast at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
ElseSt() { type = AstType::ElseSt; }
|
||||
ElseSt(BlockStatement _body) : body(_body) { type = AstType::ElseSt; }
|
||||
virtual String toString() override { return String(std::format("<Else Ast at {}:{}>", aai.line, aai.column)); }
|
||||
};
|
||||
using Else = std::shared_ptr<ElseSt>;
|
||||
class ElseIfSt final : public StatementAst
|
||||
@@ -28,19 +18,14 @@ namespace Fig::Ast
|
||||
public:
|
||||
Expression condition;
|
||||
BlockStatement body;
|
||||
ElseIfSt()
|
||||
ElseIfSt() { type = AstType::ElseIfSt; }
|
||||
ElseIfSt(Expression _condition, BlockStatement _body) : condition(_condition), body(_body)
|
||||
{
|
||||
type = AstType::ElseIfSt;
|
||||
}
|
||||
ElseIfSt(Expression _condition,
|
||||
BlockStatement _body) :
|
||||
condition(_condition), body(_body)
|
||||
virtual String toString() override
|
||||
{
|
||||
type = AstType::ElseIfSt;
|
||||
}
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<ElseIf Ast at {}:{}>", aai.line, aai.column));
|
||||
return String(std::format("<ElseIf Ast at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
using ElseIf = std::shared_ptr<ElseIfSt>;
|
||||
@@ -51,18 +36,12 @@ namespace Fig::Ast
|
||||
BlockStatement body;
|
||||
std::vector<ElseIf> elifs;
|
||||
Else els;
|
||||
IfSt()
|
||||
{
|
||||
type = AstType::IfSt;
|
||||
}
|
||||
IfSt(Expression _condition,
|
||||
BlockStatement _body,
|
||||
std::vector<ElseIf> _elifs,
|
||||
Else _els) :
|
||||
IfSt() { type = AstType::IfSt; }
|
||||
IfSt(Expression _condition, BlockStatement _body, std::vector<ElseIf> _elifs, Else _els) :
|
||||
condition(_condition), body(_body), elifs(_elifs), els(_els)
|
||||
{
|
||||
type = AstType::IfSt;
|
||||
}
|
||||
};
|
||||
using If = std::shared_ptr<IfSt>;
|
||||
}; // namespace Fig
|
||||
}; // namespace Fig::Ast
|
||||
@@ -17,7 +17,7 @@ namespace Fig::Ast
|
||||
*/
|
||||
struct ImplementMethod
|
||||
{
|
||||
FString name;
|
||||
String name;
|
||||
FunctionParameters paras;
|
||||
BlockStatement body;
|
||||
};
|
||||
@@ -25,17 +25,14 @@ namespace Fig::Ast
|
||||
class ImplementAst final : public StatementAst
|
||||
{
|
||||
public:
|
||||
FString interfaceName;
|
||||
FString structName;
|
||||
String interfaceName;
|
||||
String structName;
|
||||
|
||||
std::vector<ImplementMethod> methods;
|
||||
|
||||
ImplementAst()
|
||||
{
|
||||
type = AstType::ImplementSt;
|
||||
}
|
||||
ImplementAst() { type = AstType::ImplementSt; }
|
||||
|
||||
ImplementAst(FString _interfaceName, FString _structName, std::vector<ImplementMethod> _methods) :
|
||||
ImplementAst(String _interfaceName, String _structName, std::vector<ImplementMethod> _methods) :
|
||||
interfaceName(std::move(_interfaceName)), structName(std::move(_structName)), methods(std::move(_methods))
|
||||
{
|
||||
type = AstType::ImplementSt;
|
||||
@@ -43,4 +40,4 @@ namespace Fig::Ast
|
||||
};
|
||||
|
||||
using Implement = std::shared_ptr<ImplementAst>;
|
||||
};
|
||||
}; // namespace Fig::Ast
|
||||
@@ -6,22 +6,27 @@
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
/*
|
||||
import std.io as stdio; // io --> stdio;
|
||||
import std.io {print, println};
|
||||
*/
|
||||
|
||||
class ImportSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
std::vector<FString> path;
|
||||
std::vector<String> path;
|
||||
std::vector<String> names;
|
||||
|
||||
ImportSt()
|
||||
{
|
||||
type = AstType::ImportSt;
|
||||
}
|
||||
String rename;
|
||||
|
||||
ImportSt(std::vector<FString> _path) :
|
||||
path(std::move(_path))
|
||||
ImportSt() { type = AstType::ImportSt; }
|
||||
|
||||
ImportSt(std::vector<String> _path, std::vector<String> _names, const String &_rename) :
|
||||
path(std::move(_path)), names(std::move(_names)), rename(_rename)
|
||||
{
|
||||
type = AstType::ImportSt;
|
||||
}
|
||||
};
|
||||
|
||||
using Import = std::shared_ptr<ImportSt>;
|
||||
};
|
||||
}; // namespace Fig::Ast
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
/*
|
||||
@@ -20,35 +19,28 @@ namespace Fig::Ast
|
||||
|
||||
*/
|
||||
|
||||
|
||||
struct InterfaceMethod
|
||||
{
|
||||
FString name;
|
||||
String name;
|
||||
FunctionParameters paras;
|
||||
Expression returnType;
|
||||
|
||||
BlockStatement defaultBody = nullptr; // nullptr is non-default func
|
||||
|
||||
bool hasDefaultBody() const
|
||||
{
|
||||
return defaultBody != nullptr;
|
||||
}
|
||||
bool hasDefaultBody() const { return defaultBody != nullptr; }
|
||||
};
|
||||
|
||||
class InterfaceDefAst final : public StatementAst
|
||||
{
|
||||
public:
|
||||
FString name;
|
||||
String name;
|
||||
std::vector<InterfaceMethod> methods;
|
||||
std::vector<FString> parents; // Feature, NOT NOW
|
||||
std::vector<String> parents; // Feature, NOT NOW
|
||||
bool isPublic;
|
||||
|
||||
InterfaceDefAst()
|
||||
{
|
||||
type = AstType::InterfaceDefSt;
|
||||
}
|
||||
InterfaceDefAst() { type = AstType::InterfaceDefSt; }
|
||||
|
||||
InterfaceDefAst(FString _name, std::vector<InterfaceMethod> _methods, bool _isPublic) :
|
||||
InterfaceDefAst(String _name, std::vector<InterfaceMethod> _methods, bool _isPublic) :
|
||||
name(std::move(_name)), methods(std::move(_methods)), isPublic(_isPublic)
|
||||
{
|
||||
type = AstType::InterfaceDefSt;
|
||||
@@ -56,4 +48,4 @@ namespace Fig::Ast
|
||||
};
|
||||
|
||||
using InterfaceDef = std::shared_ptr<InterfaceDefAst>;
|
||||
};
|
||||
}; // namespace Fig::Ast
|
||||
@@ -11,13 +11,16 @@ namespace Fig::Ast
|
||||
struct StructDefField
|
||||
{
|
||||
AccessModifier am;
|
||||
FString fieldName;
|
||||
String fieldName;
|
||||
Expression declaredType;
|
||||
Expression defaultValueExpr;
|
||||
|
||||
StructDefField() {}
|
||||
StructDefField(AccessModifier _am, FString _fieldName, Expression _declaredType, Expression _defaultValueExpr) :
|
||||
am(std::move(_am)), fieldName(std::move(_fieldName)), declaredType(std::move(_declaredType)), defaultValueExpr(std::move(_defaultValueExpr))
|
||||
StructDefField(AccessModifier _am, String _fieldName, Expression _declaredType, Expression _defaultValueExpr) :
|
||||
am(std::move(_am)),
|
||||
fieldName(std::move(_fieldName)),
|
||||
declaredType(std::move(_declaredType)),
|
||||
defaultValueExpr(std::move(_defaultValueExpr))
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -25,15 +28,12 @@ namespace Fig::Ast
|
||||
{
|
||||
public:
|
||||
bool isPublic;
|
||||
const FString name;
|
||||
const String name;
|
||||
const std::vector<StructDefField> fields; // field name (:type name = default value expression)
|
||||
// name / name: String / name: String = "Fig"
|
||||
const BlockStatement body;
|
||||
StructDefSt()
|
||||
{
|
||||
type = AstType::StructSt;
|
||||
}
|
||||
StructDefSt(bool _isPublic, FString _name, std::vector<StructDefField> _fields, BlockStatement _body) :
|
||||
StructDefSt() { type = AstType::StructSt; }
|
||||
StructDefSt(bool _isPublic, String _name, std::vector<StructDefField> _fields, BlockStatement _body) :
|
||||
isPublic(std::move(_isPublic)), name(std::move(_name)), fields(std::move(_fields)), body(std::move(_body))
|
||||
{
|
||||
type = AstType::StructSt;
|
||||
@@ -41,4 +41,4 @@ namespace Fig::Ast
|
||||
};
|
||||
|
||||
using StructDef = std::shared_ptr<StructDefSt>;
|
||||
}; // namespace Fig
|
||||
}; // namespace Fig::Ast
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
@@ -10,8 +10,8 @@ namespace Fig::Ast
|
||||
public:
|
||||
bool isPublic;
|
||||
bool isConst;
|
||||
FString name;
|
||||
// FString typeName;
|
||||
String name;
|
||||
// String typeName;
|
||||
Expression declaredType;
|
||||
Expression expr;
|
||||
|
||||
@@ -24,7 +24,8 @@ namespace Fig::Ast
|
||||
expr = nullptr;
|
||||
followupType = false;
|
||||
}
|
||||
VarDefAst(bool _isPublic, bool _isConst, FString _name, Expression _declaredType, Expression _expr, bool _followupType)
|
||||
VarDefAst(
|
||||
bool _isPublic, bool _isConst, String _name, Expression _declaredType, Expression _expr, bool _followupType)
|
||||
{
|
||||
type = AstType::VarDefSt;
|
||||
isPublic = _isPublic;
|
||||
@@ -37,4 +38,4 @@ namespace Fig::Ast
|
||||
};
|
||||
|
||||
using VarDef = std::shared_ptr<VarDefAst>;
|
||||
} // namespace Fig
|
||||
} // namespace Fig::Ast
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Token/token.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <unordered_map>
|
||||
@@ -66,43 +66,43 @@ namespace Fig::Ast
|
||||
ThrowSt,
|
||||
};
|
||||
|
||||
// static const std::unordered_map<AstType, FString> astTypeToString{
|
||||
// static const std::unordered_map<AstType, String> astTypeToString{
|
||||
// /* Base Class */
|
||||
// {AstType::_AstBase, FString(u8"Ast")},
|
||||
// {AstType::StatementBase, FString(u8"Statement")},
|
||||
// {AstType::ExpressionBase, FString(u8"Expression")},
|
||||
// {AstType::_AstBase, String(U"Ast")},
|
||||
// {AstType::StatementBase, String(U"Statement")},
|
||||
// {AstType::ExpressionBase, String(U"Expression")},
|
||||
// /* Expression */
|
||||
// {AstType::ValueExpr, FString(u8"ValueExpr")},
|
||||
// {AstType::LambdaExpr, FString(u8"LambdaExpr")},
|
||||
// {AstType::UnaryExpr, FString(u8"UnaryExpr")},
|
||||
// {AstType::BinaryExpr, FString(u8"BinaryExpr")},
|
||||
// {AstType::TernaryExpr, FString(u8"TernaryExpr")},
|
||||
// {AstType::ValueExpr, String(U"ValueExpr")},
|
||||
// {AstType::LambdaExpr, String(U"LambdaExpr")},
|
||||
// {AstType::UnaryExpr, String(U"UnaryExpr")},
|
||||
// {AstType::BinaryExpr, String(U"BinaryExpr")},
|
||||
// {AstType::TernaryExpr, String(U"TernaryExpr")},
|
||||
|
||||
// {AstType::InitExpr, FString(u8"InitExpr")},
|
||||
// {AstType::InitExpr, String(U"InitExpr")},
|
||||
|
||||
// /* Statement */
|
||||
// {AstType::BlockStatement, FString(u8"BlockStatement")},
|
||||
// {AstType::BlockStatement, String(U"BlockStatement")},
|
||||
|
||||
// {AstType::VarDefSt, FString(u8"VarSt")},
|
||||
// {AstType::FunctionDefSt, FString(u8"FunctionDefSt")},
|
||||
// {AstType::StructSt, FString(u8"StructSt")},
|
||||
// {AstType::ImplementSt, FString(u8"ImplementSt")},
|
||||
// {AstType::VarDefSt, String(U"VarSt")},
|
||||
// {AstType::FunctionDefSt, String(U"FunctionDefSt")},
|
||||
// {AstType::StructSt, String(U"StructSt")},
|
||||
// {AstType::ImplementSt, String(U"ImplementSt")},
|
||||
|
||||
// {AstType::IfSt, FString(u8"IfSt")},
|
||||
// {AstType::ElseSt, FString(u8"ElseSt")},
|
||||
// {AstType::ElseIfSt, FString(u8"ElseIfSt")},
|
||||
// {AstType::VarAssignSt, FString(u8"VarAssignSt")},
|
||||
// {AstType::WhileSt, FString(u8"WhileSt")},
|
||||
// {AstType::ReturnSt, FString(u8"ReturnSt")},
|
||||
// {AstType::BreakSt, FString(u8"BreakSt")},
|
||||
// {AstType::ContinueSt, FString(u8"ContinueSt")},
|
||||
// {AstType::IfSt, String(U"IfSt")},
|
||||
// {AstType::ElseSt, String(U"ElseSt")},
|
||||
// {AstType::ElseIfSt, String(U"ElseIfSt")},
|
||||
// {AstType::VarAssignSt, String(U"VarAssignSt")},
|
||||
// {AstType::WhileSt, String(U"WhileSt")},
|
||||
// {AstType::ReturnSt, String(U"ReturnSt")},
|
||||
// {AstType::BreakSt, String(U"BreakSt")},
|
||||
// {AstType::ContinueSt, String(U"ContinueSt")},
|
||||
// };
|
||||
|
||||
struct AstAddressInfo
|
||||
{
|
||||
size_t line, column;
|
||||
std::shared_ptr<FString> sourcePath;
|
||||
std::shared_ptr<std::vector<FString>> sourceLines;
|
||||
std::shared_ptr<String> sourcePath;
|
||||
std::shared_ptr<std::vector<String>> sourceLines;
|
||||
};
|
||||
|
||||
class _AstBase
|
||||
@@ -122,13 +122,14 @@ namespace Fig::Ast
|
||||
|
||||
void setAAI(AstAddressInfo _aai) { aai = std::move(_aai); }
|
||||
|
||||
virtual FString typeName()
|
||||
virtual String typeName()
|
||||
{
|
||||
return FString::fromStringView(FStringView::fromBasicStringView(magic_enum::enum_name(type)));
|
||||
const auto &name = magic_enum::enum_name(type);
|
||||
return String(std::string(name.data(), name.length()));
|
||||
}
|
||||
virtual FString toString()
|
||||
virtual String toString()
|
||||
{
|
||||
return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
return String(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
|
||||
AstAddressInfo getAAI() { return aai; }
|
||||
@@ -143,9 +144,9 @@ namespace Fig::Ast
|
||||
using _AstBase::operator=;
|
||||
StatementAst() { type = AstType::StatementBase; }
|
||||
|
||||
virtual FString toString() override
|
||||
virtual String toString() override
|
||||
{
|
||||
return FString(std::format("<Stmt Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
return String(std::format("<Stmt Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,9 +155,9 @@ namespace Fig::Ast
|
||||
public:
|
||||
EofStmt() { type = AstType::StatementBase; }
|
||||
|
||||
virtual FString toString() override
|
||||
virtual String toString() override
|
||||
{
|
||||
return FString(std::format("<EOF Stmt at {}:{}>", aai.line, aai.column));
|
||||
return String(std::format("<EOF Stmt at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -167,9 +168,9 @@ namespace Fig::Ast
|
||||
using _AstBase::operator=;
|
||||
ExpressionAst() { type = AstType::ExpressionBase; }
|
||||
|
||||
virtual FString toString() override
|
||||
virtual String toString() override
|
||||
{
|
||||
return FString(std::format("<Expr Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
return String(std::format("<Expr Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
enum class Operator : uint8_t
|
||||
@@ -199,6 +200,9 @@ namespace Fig::Ast
|
||||
GreaterEqual, // >=
|
||||
Is, // a is b
|
||||
|
||||
// 转换
|
||||
As, // 3.14 as Int
|
||||
|
||||
// 三目
|
||||
TernaryCond,
|
||||
|
||||
@@ -235,6 +239,8 @@ namespace Fig::Ast
|
||||
Operator::Equal, Operator::NotEqual, Operator::Less, Operator::LessEqual,
|
||||
Operator::Greater, Operator::GreaterEqual, Operator::Is,
|
||||
|
||||
Operator::As,
|
||||
|
||||
Operator::BitAnd, Operator::BitOr, Operator::BitXor, Operator::BitNot,
|
||||
Operator::ShiftLeft, Operator::ShiftRight,
|
||||
|
||||
@@ -271,6 +277,9 @@ namespace Fig::Ast
|
||||
{TokenType::GreaterEqual, Operator::GreaterEqual},
|
||||
{TokenType::Is, Operator::Is},
|
||||
|
||||
// 转换
|
||||
{TokenType::As, Operator::As},
|
||||
|
||||
// 三目
|
||||
{TokenType::Question, Operator::TernaryCond},
|
||||
|
||||
@@ -317,10 +326,10 @@ namespace Fig::Ast
|
||||
std::vector<Statement> stmts;
|
||||
BlockStatementAst() { type = AstType::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 String typeName() override { return String(U"BlockStatement"); }
|
||||
virtual String toString() override
|
||||
{
|
||||
return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
return String(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
virtual ~BlockStatementAst() = default;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <format>
|
||||
|
||||
@@ -17,13 +17,15 @@ namespace Fig::Ast
|
||||
func test2(dp1 = 10, dp2:String = "default parameter 2")
|
||||
*/
|
||||
|
||||
using PosParasType = std::vector<std::pair<FString, FString>>;
|
||||
using DefParasType = std::vector<std::pair<FString, std::pair<FString, Expression>>>;
|
||||
using PosParasType = std::vector<std::pair<String, Expression>>;
|
||||
// name type exp
|
||||
using DefParasType = std::vector<std::pair<String, std::pair<Expression, Expression>>>;
|
||||
// name type exp default value
|
||||
|
||||
PosParasType posParas;
|
||||
DefParasType defParas; // default parameters
|
||||
|
||||
FString variadicPara;
|
||||
String variadicPara;
|
||||
bool variadic = false;
|
||||
|
||||
FunctionParameters()
|
||||
@@ -34,7 +36,7 @@ namespace Fig::Ast
|
||||
posParas = std::move(_posParas);
|
||||
defParas = std::move(_defParas);
|
||||
}
|
||||
FunctionParameters(FString _variadicPara)
|
||||
FunctionParameters(String _variadicPara)
|
||||
{
|
||||
variadicPara = std::move(_variadicPara);
|
||||
variadic = true;
|
||||
@@ -50,46 +52,46 @@ namespace Fig::Ast
|
||||
return posParas == other.posParas && defParas == other.defParas && variadicPara == other.variadicPara && variadic == other.variadic;
|
||||
}
|
||||
|
||||
FString toString() const
|
||||
String toString() const
|
||||
{
|
||||
if (variadic)
|
||||
{
|
||||
return FString(variadicPara + u8"...");
|
||||
return String(variadicPara + U"...");
|
||||
}
|
||||
static const auto posParasToString = [this]() {
|
||||
FString out;
|
||||
const auto posParasToString = [this]() {
|
||||
String out;
|
||||
for (auto &p : posParas)
|
||||
{
|
||||
out += p.first;
|
||||
if (!p.second.empty())
|
||||
if (p.second != nullptr)
|
||||
{
|
||||
out += FString(u8":" + p.second);
|
||||
out += String(String(U":") + p.second->toString());
|
||||
}
|
||||
out += u8",";
|
||||
out += U",";
|
||||
}
|
||||
out.pop_back();
|
||||
return out;
|
||||
};
|
||||
static const auto defParasToString = [this]() {
|
||||
FString out;
|
||||
const auto defParasToString = [this]() {
|
||||
String out;
|
||||
for (auto &p : defParas)
|
||||
{
|
||||
out += p.first;
|
||||
if (!p.second.first.empty())
|
||||
if (p.second.first != nullptr)
|
||||
{
|
||||
out += FString(u8":" + p.second.first);
|
||||
out += String(String(U":") + p.second.first->toString());
|
||||
}
|
||||
if (p.second.second != nullptr)
|
||||
{
|
||||
out += u8"=";
|
||||
out += U"=";
|
||||
out += p.second.second->toString();
|
||||
}
|
||||
out += u8",";
|
||||
out += U",";
|
||||
}
|
||||
out.pop_back();
|
||||
return out;
|
||||
};
|
||||
return FString(std::format("{},{}", posParasToString().toBasicString(), defParasToString().toBasicString()));
|
||||
return String(std::format("{},{}", posParasToString().toBasicString(), defParasToString().toBasicString()));
|
||||
}
|
||||
};
|
||||
} // namespace Fig::Ast
|
||||
@@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
|
||||
从树遍历到虚拟机!
|
||||
|
||||
*/
|
||||
|
||||
#include "Core/fig_string.hpp"
|
||||
#include "Utils/magic_enum/magic_enum.hpp"
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <string_view>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
using OpCodeType = uint8_t;
|
||||
enum class Bytecode : OpCodeType
|
||||
{
|
||||
HALT = 0x01, // 程序终止,后跟 8 位退出码
|
||||
|
||||
POP = 0x10,
|
||||
|
||||
LOAD_NULL = 0x20,
|
||||
LOAD_TRUE = 0x21,
|
||||
LOAD_FALSE = 0x22,
|
||||
LOAD_CON8 = 0x23, // 跟 8 位索引 (0 - 255)
|
||||
LOAD_CON16 = 0x24, // 跟 16 位索引 (255 - 65535)
|
||||
LOAD_CON32 = 0x25, // 跟 32 位索引 (65536 - 2^32-1)
|
||||
|
||||
ADD = 0x30, // +
|
||||
SUB = 0x31, // -
|
||||
MUL = 0x32, // *
|
||||
DIV = 0x33, // /
|
||||
MOD = 0x34, // %
|
||||
|
||||
AND = 0x40, // &
|
||||
OR = 0x41, // |
|
||||
XOR = 0x42, // ~
|
||||
NOT = 0x43, // ! (not)
|
||||
|
||||
EQ = 0x50, // ==
|
||||
GT = 0x51, // >
|
||||
GTEQ = 0x52, // >=
|
||||
LT = 0x53, // <
|
||||
LTEQ = 0x54, // <=
|
||||
|
||||
JUMP16 = 0x60, // 跟 16 位索引 无条件
|
||||
JUMP32 = 0x61, // 跟 32 位索引 无条件
|
||||
|
||||
JUMP16_IF_TRUE = 0x62, // 跟 16 位索引 栈顶为真跳转
|
||||
JUMP32_IF_TRUE = 0x63, // 跟 32 位索引 栈顶为真跳转
|
||||
|
||||
JUMP16_IF_FALSE = 0x64, // 跟 16 位索引 栈顶为假跳转
|
||||
JUMP32_IF_FALSE = 0x65, // 跟 32 位索引 栈顶为假跳转
|
||||
|
||||
LOAD_LOCAL16 = 0x70, // 后跟 16 位索引
|
||||
LOAD_LOCAL32 = 0x71, // 后跟 32 位索引
|
||||
};
|
||||
|
||||
inline FString bytecode2string(Bytecode code)
|
||||
{
|
||||
const std::string_view &name = magic_enum::enum_name(code);
|
||||
return FString(FStringView(name));
|
||||
}
|
||||
|
||||
inline FString reverseCompile(const std::vector<uint8_t> &src)
|
||||
{
|
||||
assert(src.size() >= 1);
|
||||
|
||||
FString result;
|
||||
|
||||
using enum Bytecode;
|
||||
for (size_t i = 0; i < src.size();)
|
||||
{
|
||||
Bytecode code = Bytecode(src[i]);
|
||||
switch (code)
|
||||
{
|
||||
case HALT: {
|
||||
uint8_t quitCode = src[++i];
|
||||
result += FString(std::format("HALT {}", static_cast<uint8_t>(quitCode))) + u8"\n";
|
||||
break;
|
||||
}
|
||||
case LOAD_CON8: {
|
||||
uint8_t id = src[++i];
|
||||
result += FString(std::format("LOAD_CON8 {}", static_cast<uint8_t>(id))) + u8"\n";
|
||||
break;
|
||||
}
|
||||
case LOAD_CON16:
|
||||
case JUMP16:
|
||||
case JUMP16_IF_TRUE:
|
||||
case JUMP16_IF_FALSE:
|
||||
case LOAD_LOCAL16: {
|
||||
uint8_t high = src[++i];
|
||||
uint8_t low = src[++i];
|
||||
int32_t id = (high << 8) | low;
|
||||
result += FString(std::format("{} {}", bytecode2string(code).toBasicString(), id))
|
||||
+ u8"\n";
|
||||
break;
|
||||
}
|
||||
|
||||
case LOAD_CON32:
|
||||
case JUMP32:
|
||||
case JUMP32_IF_TRUE:
|
||||
case JUMP32_IF_FALSE:
|
||||
case LOAD_LOCAL32: {
|
||||
uint32_t b0 = static_cast<uint32_t>(src[++i]);
|
||||
uint32_t b1 = static_cast<uint32_t>(src[++i]);
|
||||
uint32_t b2 = static_cast<uint32_t>(src[++i]);
|
||||
uint32_t b3 = static_cast<uint32_t>(src[++i]);
|
||||
|
||||
uint32_t id = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
||||
result += FString(std::format("{} {}", bytecode2string(code).toBasicString(), id))
|
||||
+ u8"\n";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result += bytecode2string(code) + u8"\n";
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}; // namespace Fig
|
||||
17
src/Bytecode/CallFrame.hpp
Normal file
17
src/Bytecode/CallFrame.hpp
Normal 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
25
src/Bytecode/Chunk.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/String.hpp>
|
||||
#include <Bytecode/Instruction.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct ChunkAddressInfo
|
||||
{
|
||||
String sourcePath;
|
||||
std::vector<String> sourceLines;
|
||||
};
|
||||
|
||||
struct Chunk
|
||||
{
|
||||
Instructions ins; // vector<Instruction>
|
||||
std::vector<Object> constants; // 常量池
|
||||
|
||||
std::vector<InstructionAddressInfo> instructions_addr; // 下标和ins对齐,表示每个Instruction对应的地址
|
||||
ChunkAddressInfo addr; // 代码块独立Addr
|
||||
};
|
||||
}; // namespace Fig
|
||||
22
src/Bytecode/CompileError.hpp
Normal file
22
src/Bytecode/CompileError.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Error/error.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class CompileError : public AddressableError
|
||||
{
|
||||
using AddressableError::AddressableError;
|
||||
|
||||
virtual String toString() const override
|
||||
{
|
||||
std::string msg = std::format("[CompileError] {} in [{}] {}",
|
||||
this->message.toBasicString(),
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
|
||||
virtual String getErrorType() const override { return String(U"\1"); }
|
||||
};
|
||||
}; // namespace Fig
|
||||
20
src/Bytecode/CompiledFunction.hpp
Normal file
20
src/Bytecode/CompiledFunction.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Bytecode/Instruction.hpp>
|
||||
#include <Bytecode/Chunk.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct CompiledFunction
|
||||
{
|
||||
Chunk chunk; // 函数代码块
|
||||
|
||||
String name; // 函数名
|
||||
uint64_t posArgCount; // 位置参数数量
|
||||
uint64_t defArgCount; // 默认参数数量
|
||||
bool variadicPara; // 可变参数(是:最后为可变,否:不可变)
|
||||
|
||||
uint64_t localCount; // 局部变量数量(不包括参数)
|
||||
uint64_t slotCount; // = 总参数数量 + 局部变量数量
|
||||
};
|
||||
}; // namespace Fig
|
||||
69
src/Bytecode/Instruction.hpp
Normal file
69
src/Bytecode/Instruction.hpp
Normal 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
|
||||
107
src/Bytecode/vm_test_main.cpp
Normal file
107
src/Bytecode/vm_test_main.cpp
Normal 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{
|
||||
{},
|
||||
U"\1",
|
||||
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, U"\1", 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";
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Compiler/Compiler.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
void Compiler::compile(Ast::Statement stmt)
|
||||
{
|
||||
using enum Ast::AstType;
|
||||
using namespace Ast;
|
||||
Ast::AstType type = stmt->getType();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case VarDefSt: {
|
||||
auto vd = std::static_pointer_cast<VarDefAst>(stmt);
|
||||
const FString name = vd->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ast/astBase.hpp"
|
||||
#include <Ast/ast.hpp>
|
||||
#include <Bytecode/Bytecode.hpp>
|
||||
#include <VMValue/VMValue.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Compiler
|
||||
{
|
||||
private:
|
||||
std::vector<Ast::Statement> source;
|
||||
std::vector<OpCodeType> output; // std::vector<uint8_t>
|
||||
|
||||
std::vector<Value> constants;
|
||||
public:
|
||||
std::vector<OpCodeType> getOutput() const { return output; }
|
||||
std::vector<Value> getConstantPool() const { return constants; }
|
||||
|
||||
Compiler() {}
|
||||
Compiler(std::vector<Ast::Statement> _source) : source(std::move(_source)) {}
|
||||
|
||||
void compile_expr(Ast::Expression);
|
||||
|
||||
void compile(Ast::Statement);
|
||||
|
||||
void CompileAll();
|
||||
};
|
||||
}; // namespace Fig
|
||||
312
src/Core/CharUtils.hpp
Normal file
312
src/Core/CharUtils.hpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
class CharUtils
|
||||
{
|
||||
public:
|
||||
static bool isDigit(char32_t c) { return c >= U'0' && c <= U'9'; }
|
||||
|
||||
static bool isAlpha(char32_t c)
|
||||
{
|
||||
// ASCII字母
|
||||
if ((c >= U'A' && c <= U'Z') || (c >= U'a' && c <= U'z')) return true;
|
||||
|
||||
// Unicode字母检查
|
||||
// Ll - 小写字母
|
||||
// Lu - 大写字母
|
||||
// Lt - 标题字母
|
||||
// Lm - 修饰字母
|
||||
// Lo - 其他字母
|
||||
|
||||
// 基本拉丁字母
|
||||
if ((c >= 0x00C0 && c <= 0x00D6) || // Latin-1 Supplement
|
||||
(c >= 0x00D8 && c <= 0x00F6) || // Latin-1 Supplement
|
||||
(c >= 0x00F8 && c <= 0x02AF) || // Latin Extended
|
||||
(c >= 0x0370 && c <= 0x052F) || // Greek, Cyrillic
|
||||
(c >= 0x0591 && c <= 0x05F4) || // Hebrew
|
||||
(c >= 0x0600 && c <= 0x06FF) || // Arabic
|
||||
(c >= 0x0700 && c <= 0x0D7F) || // Syriac, Thaana, Devanagari, etc.
|
||||
(c >= 0x0E00 && c <= 0x0E7F) || // Thai
|
||||
(c >= 0x0F00 && c <= 0x0FFF) || // Tibetan
|
||||
(c >= 0x1000 && c <= 0x109F) || // Myanmar
|
||||
(c >= 0x10A0 && c <= 0x10FF) || // Georgian
|
||||
(c >= 0x1100 && c <= 0x11FF) || // Hangul Jamo
|
||||
(c >= 0x1200 && c <= 0x137F) || // Ethiopic
|
||||
(c >= 0x13A0 && c <= 0x13FF) || // Cherokee
|
||||
(c >= 0x1400 && c <= 0x167F) || // Unified Canadian Aboriginal Syllabics
|
||||
(c >= 0x1680 && c <= 0x169F) || // Ogham
|
||||
(c >= 0x16A0 && c <= 0x16FF) || // Runic
|
||||
(c >= 0x1700 && c <= 0x171F) || // Tagalog
|
||||
(c >= 0x1720 && c <= 0x173F) || // Hanunoo
|
||||
(c >= 0x1740 && c <= 0x175F) || // Buhid
|
||||
(c >= 0x1760 && c <= 0x177F) || // Tagbanwa
|
||||
(c >= 0x1780 && c <= 0x17FF) || // Khmer
|
||||
(c >= 0x1800 && c <= 0x18AF) || // Mongolian
|
||||
(c >= 0x1900 && c <= 0x194F) || // Limbu
|
||||
(c >= 0x1950 && c <= 0x197F) || // Tai Le
|
||||
(c >= 0x19E0 && c <= 0x19FF) || // Khmer Symbols
|
||||
(c >= 0x1D00 && c <= 0x1D7F) || // Phonetic Extensions
|
||||
(c >= 0x1E00 && c <= 0x1EFF) || // Latin Extended Additional
|
||||
(c >= 0x1F00 && c <= 0x1FFF) || // Greek Extended
|
||||
(c >= 0x2C00 && c <= 0x2C5F) || // Glagolitic
|
||||
(c >= 0x2C60 && c <= 0x2C7F) || // Latin Extended-C
|
||||
(c >= 0x2C80 && c <= 0x2CFF) || // Coptic
|
||||
(c >= 0x2D00 && c <= 0x2D2F) || // Georgian Supplement
|
||||
(c >= 0x2D30 && c <= 0x2D7F) || // Tifinagh
|
||||
(c >= 0x2D80 && c <= 0x2DDF) || // Ethiopic Extended
|
||||
(c >= 0x2DE0 && c <= 0x2DFF) || // Cyrillic Extended-A
|
||||
(c >= 0x2E00 && c <= 0x2E7F) || // Supplemental Punctuation
|
||||
(c >= 0xA640 && c <= 0xA69F) || // Cyrillic Extended-B
|
||||
(c >= 0xA720 && c <= 0xA7FF) || // Latin Extended-D
|
||||
(c >= 0xA800 && c <= 0xA82F) || // Syloti Nagri
|
||||
(c >= 0xA840 && c <= 0xA87F) || // Phags-pa
|
||||
(c >= 0xAC00 && c <= 0xD7AF) || // Hangul Syllables
|
||||
(c >= 0xF900 && c <= 0xFAFF) || // CJK Compatibility Ideographs
|
||||
(c >= 0xFB00 && c <= 0xFB4F) || // Alphabetic Presentation Forms
|
||||
(c >= 0xFE20 && c <= 0xFE2F) || // Combining Half Marks
|
||||
(c >= 0xFE70 && c <= 0xFEFF) || // Arabic Presentation Forms-B
|
||||
(c >= 0xFF00 && c <= 0xFFEF) || // Halfwidth and Fullwidth Forms
|
||||
(c >= 0x10000 && c <= 0x100FF) || // Linear B Syllabary
|
||||
(c >= 0x10140 && c <= 0x1018F) || // Ancient Greek Numbers
|
||||
(c >= 0x10300 && c <= 0x1032F) || // Old Italic
|
||||
(c >= 0x10330 && c <= 0x1034F) || // Gothic
|
||||
(c >= 0x10380 && c <= 0x1039F) || // Ugaritic
|
||||
(c >= 0x103A0 && c <= 0x103DF) || // Old Persian
|
||||
(c >= 0x10400 && c <= 0x1044F) || // Deseret
|
||||
(c >= 0x10450 && c <= 0x1047F) || // Shavian
|
||||
(c >= 0x10480 && c <= 0x104AF) || // Osmanya
|
||||
(c >= 0x10800 && c <= 0x1083F) || // Cypriot Syllabary
|
||||
(c >= 0x10900 && c <= 0x1091F) || // Phoenician
|
||||
(c >= 0x10A00 && c <= 0x10A5F) || // Kharoshthi
|
||||
(c >= 0x12000 && c <= 0x123FF) || // Cuneiform
|
||||
(c >= 0x1D400 && c <= 0x1D7FF) || // Mathematical Alphanumeric Symbols
|
||||
(c >= 0x1F130 && c <= 0x1F1FF) || // Enclosed Alphanumeric Supplement
|
||||
(c >= 0x20000 && c <= 0x2A6DF) || // CJK Unified Ideographs Extension B
|
||||
(c >= 0x2A700 && c <= 0x2B73F) || // CJK Unified Ideographs Extension C
|
||||
(c >= 0x2B740 && c <= 0x2B81F) || // CJK Unified Ideographs Extension D
|
||||
(c >= 0x2F800 && c <= 0x2FA1F)) // CJK Compatibility Ideographs Supplement
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// CJK统一表意文字 (中文、日文、韩文)
|
||||
if ((c >= 0x4E00 && c <= 0x9FFF) || // CJK Unified Ideographs
|
||||
(c >= 0x3400 && c <= 0x4DBF) || // CJK Unified Ideographs Extension A
|
||||
(c >= 0x2E80 && c <= 0x2EFF) || // CJK Radicals Supplement
|
||||
(c >= 0x2F00 && c <= 0x2FDF) || // Kangxi Radicals
|
||||
(c >= 0x2FF0 && c <= 0x2FFF) || // Ideographic Description Characters
|
||||
(c >= 0x3000 && c <= 0x303F) || // CJK Symbols and Punctuation
|
||||
(c >= 0x31C0 && c <= 0x31EF) || // CJK Strokes
|
||||
(c >= 0x3200 && c <= 0x32FF) || // Enclosed CJK Letters and Months
|
||||
(c >= 0x3300 && c <= 0x33FF) || // CJK Compatibility
|
||||
(c >= 0xF900 && c <= 0xFAFF) || // CJK Compatibility Ideographs
|
||||
(c >= 0xFE30 && c <= 0xFE4F) || // CJK Compatibility Forms
|
||||
(c >= 0x1F200 && c <= 0x1F2FF)) // Enclosed Ideographic Supplement
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isUpper(char32_t c)
|
||||
{
|
||||
// ASCII大写字母
|
||||
if (c >= U'A' && c <= U'Z') return true;
|
||||
|
||||
// Unicode大写字母检查
|
||||
if ((c >= 0x00C0 && c <= 0x00D6) || // Latin-1 Supplement
|
||||
(c >= 0x00D8 && c <= 0x00DE) || // Latin-1 Supplement
|
||||
(c >= 0x0100 && c <= 0x012E) || // Latin Extended-A
|
||||
(c >= 0x0132 && c <= 0x0136) || (c >= 0x0139 && c <= 0x0147) || (c >= 0x014A && c <= 0x0178)
|
||||
|| (c >= 0x0179 && c <= 0x017D) || (c >= 0x0181 && c <= 0x0182) || c == 0x0186 || c == 0x0189 || c == 0x018A
|
||||
|| c == 0x018E || c == 0x018F || c == 0x0190 || c == 0x0191 || c == 0x0193 || c == 0x0194 || c == 0x0196
|
||||
|| c == 0x0197 || c == 0x0198 || c == 0x019C || c == 0x019D || c == 0x019F || c == 0x01A0 || c == 0x01A2
|
||||
|| c == 0x01A4 || c == 0x01A7 || c == 0x01A9 || c == 0x01AC || c == 0x01AE || c == 0x01AF
|
||||
|| (c >= 0x01B1 && c <= 0x01B3) || c == 0x01B5 || c == 0x01B7 || c == 0x01B8 || c == 0x01BC || c == 0x01C4
|
||||
|| c == 0x01C7 || c == 0x01CA || c == 0x01CD || c == 0x01CF || c == 0x01D1 || c == 0x01D3 || c == 0x01D5
|
||||
|| c == 0x01D7 || c == 0x01D9 || c == 0x01DB || c == 0x01DE || c == 0x01E0 || c == 0x01E2 || c == 0x01E4
|
||||
|| c == 0x01E6 || c == 0x01E8 || c == 0x01EA || c == 0x01EC || c == 0x01EE || c == 0x01F4 || c == 0x01FA
|
||||
|| (c >= 0x01FC && c <= 0x01FF) || (c >= 0x0200 && c <= 0x0220) || (c >= 0x0222 && c <= 0x0232)
|
||||
|| (c >= 0x0370 && c <= 0x0373) || (c >= 0x0376 && c <= 0x0377) || (c >= 0x03D8 && c <= 0x03EF)
|
||||
|| (c >= 0x03F4 && c <= 0x03F7) || (c >= 0x03F9 && c <= 0x03FA) || (c >= 0x03FD && c <= 0x042F)
|
||||
|| (c >= 0x0460 && c <= 0x0480) || (c >= 0x048A && c <= 0x04C0) || (c >= 0x04C1 && c <= 0x04CD)
|
||||
|| (c >= 0x04D0 && c <= 0x0528) || (c >= 0x0531 && c <= 0x0556) || (c >= 0x10A0 && c <= 0x10C5)
|
||||
|| (c >= 0x1E00 && c <= 0x1E94) || (c >= 0x1EA0 && c <= 0x1EFE) || (c >= 0x2C00 && c <= 0x2C2E)
|
||||
|| (c >= 0x2C60 && c <= 0x2C62) || (c >= 0xA640 && c <= 0xA66C) || (c >= 0xA680 && c <= 0xA69A)
|
||||
|| (c >= 0xA722 && c <= 0xA72E) || (c >= 0xA732 && c <= 0xA76E) || (c >= 0xA779 && c <= 0xA77D)
|
||||
|| (c >= 0xA77E && c <= 0xA786) || (c >= 0xA78B && c <= 0xA78D) || (c >= 0xA790 && c <= 0xA792)
|
||||
|| (c >= 0xA796 && c <= 0xA7A8) || (c >= 0xA7AA && c <= 0xA7AE) || (c >= 0xA7B0 && c <= 0xA7B4)
|
||||
|| (c >= 0xA7B6 && c <= 0xA7BE) || (c >= 0xFF21 && c <= 0xFF3A) || // 全角大写字母
|
||||
(c >= 0x10400 && c <= 0x10427) || // Deseret
|
||||
(c >= 0x104B0 && c <= 0x104D3) || // Osage
|
||||
(c >= 0x10C80 && c <= 0x10CB2) || // Old Hungarian
|
||||
(c >= 0x118A0 && c <= 0x118DF) || // Warang Citi
|
||||
(c >= 0x1D400 && c <= 0x1D419) || // Mathematical bold capital
|
||||
(c >= 0x1D434 && c <= 0x1D44D) || // Mathematical italic capital
|
||||
(c >= 0x1D468 && c <= 0x1D481) || // Mathematical bold italic capital
|
||||
(c >= 0x1D49C && c <= 0x1D4B5) || // Mathematical script capital
|
||||
(c >= 0x1D4D0 && c <= 0x1D4E9) || // Mathematical bold script capital
|
||||
(c >= 0x1D504 && c <= 0x1D51C) || // Mathematical fraktur capital
|
||||
(c >= 0x1D538 && c <= 0x1D550) || // Mathematical double-struck capital
|
||||
(c >= 0x1D56C && c <= 0x1D585) || // Mathematical bold fraktur capital
|
||||
(c >= 0x1D5A0 && c <= 0x1D5B9) || // Mathematical sans-serif capital
|
||||
(c >= 0x1D5D4 && c <= 0x1D5ED) || // Mathematical sans-serif bold capital
|
||||
(c >= 0x1D608 && c <= 0x1D621) || // Mathematical sans-serif italic capital
|
||||
(c >= 0x1D63C && c <= 0x1D655) || // Mathematical sans-serif bold italic capital
|
||||
(c >= 0x1D670 && c <= 0x1D689) || // Mathematical monospace capital
|
||||
(c >= 0x1D6A8 && c <= 0x1D6C0) || // Mathematical bold capital Greek
|
||||
(c >= 0x1D6E2 && c <= 0x1D6FA) || // Mathematical italic capital Greek
|
||||
(c >= 0x1D71C && c <= 0x1D734) || // Mathematical bold italic capital Greek
|
||||
(c >= 0x1D756 && c <= 0x1D76E) || // Mathematical sans-serif bold capital Greek
|
||||
(c >= 0x1D790 && c <= 0x1D7A8)) // Mathematical sans-serif bold italic capital Greek
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isLower(char32_t c)
|
||||
{
|
||||
// ASCII小写字母
|
||||
if (c >= U'a' && c <= U'z') return true;
|
||||
|
||||
// Unicode小写字母检查
|
||||
if ((c >= 0x00DF && c <= 0x00F6) || // Latin-1 Supplement
|
||||
(c >= 0x00F8 && c <= 0x00FF) || // Latin-1 Supplement
|
||||
(c >= 0x0101 && c <= 0x012F) || // Latin Extended-A
|
||||
(c >= 0x0133 && c <= 0x0137) || (c >= 0x013A && c <= 0x0148) || (c >= 0x014B && c <= 0x0177)
|
||||
|| (c >= 0x017A && c <= 0x017E) || (c >= 0x0183 && c <= 0x0185) || c == 0x0188 || c == 0x018C || c == 0x0192
|
||||
|| c == 0x0195 || c == 0x0199 || c == 0x019A || c == 0x019E || c == 0x01A1 || c == 0x01A3 || c == 0x01A5
|
||||
|| c == 0x01A8 || c == 0x01AA || c == 0x01AB || c == 0x01AD || c == 0x01B0 || c == 0x01B4 || c == 0x01B6
|
||||
|| c == 0x01B9 || c == 0x01BA || c == 0x01BD || c == 0x01C6 || c == 0x01C9 || c == 0x01CC || c == 0x01CE
|
||||
|| c == 0x01D0 || c == 0x01D2 || c == 0x01D4 || c == 0x01D6 || c == 0x01D8 || c == 0x01DA || c == 0x01DC
|
||||
|| c == 0x01DD || c == 0x01DF || c == 0x01E1 || c == 0x01E3 || c == 0x01E5 || c == 0x01E7 || c == 0x01E9
|
||||
|| c == 0x01EB || c == 0x01ED || c == 0x01F3 || c == 0x01F5 || c == 0x01FB || (c >= 0x0201 && c <= 0x0217)
|
||||
|| (c >= 0x0250 && c <= 0x02A8) || (c >= 0x03B1 && c <= 0x03C1) || (c >= 0x03C3 && c <= 0x03CE)
|
||||
|| (c >= 0x0430 && c <= 0x044F) || (c >= 0x0451 && c <= 0x045C) || (c >= 0x045E && c <= 0x0481)
|
||||
|| (c >= 0x0491 && c <= 0x04BF) || (c >= 0x04C2 && c <= 0x04CE) || (c >= 0x04D1 && c <= 0x0527)
|
||||
|| (c >= 0x0561 && c <= 0x0587) || (c >= 0x1D00 && c <= 0x1D2B) || (c >= 0x1D62 && c <= 0x1D77)
|
||||
|| (c >= 0x1D79 && c <= 0x1D9A) || (c >= 0x1E01 && c <= 0x1E95) || (c >= 0x1EA1 && c <= 0x1EFF)
|
||||
|| (c >= 0x2C30 && c <= 0x2C5E) || (c >= 0x2C61 && c <= 0x2C65) || (c >= 0x2C66 && c <= 0x2C6C)
|
||||
|| (c >= 0x2C73 && c <= 0x2C74) || (c >= 0x2C76 && c <= 0x2C7B) || (c >= 0xA641 && c <= 0xA66D)
|
||||
|| (c >= 0xA681 && c <= 0xA69B) || (c >= 0xA723 && c <= 0xA72F) || (c >= 0xA733 && c <= 0xA76F)
|
||||
|| (c >= 0xA77A && c <= 0xA77C) || (c >= 0xA77F && c <= 0xA787) || (c >= 0xA78C && c <= 0xA78E)
|
||||
|| (c >= 0xA791 && c <= 0xA793) || (c >= 0xA797 && c <= 0xA7A9) || (c >= 0xA7AB && c <= 0xA7AE)
|
||||
|| (c >= 0xA7B1 && c <= 0xA7B5) || (c >= 0xA7B7 && c <= 0xA7BF) || (c >= 0xAB30 && c <= 0xAB5A)
|
||||
|| (c >= 0xAB60 && c <= 0xAB65) || (c >= 0xAB70 && c <= 0xABBF) || (c >= 0xFB00 && c <= 0xFB06)
|
||||
|| (c >= 0xFB13 && c <= 0xFB17) || (c >= 0xFF41 && c <= 0xFF5A) || // 全角小写字母
|
||||
(c >= 0x10428 && c <= 0x1044F) || // Deseret lowercase
|
||||
(c >= 0x104D8 && c <= 0x104FB) || // Osage lowercase
|
||||
(c >= 0x10CC0 && c <= 0x10CF2) || // Old Hungarian lowercase
|
||||
(c >= 0x118C0 && c <= 0x118E0) || // Warang Citi lowercase
|
||||
(c >= 0x1D41A && c <= 0x1D433) || // Mathematical bold lowercase
|
||||
(c >= 0x1D44E && c <= 0x1D467) || // Mathematical italic lowercase
|
||||
(c >= 0x1D482 && c <= 0x1D49B) || // Mathematical bold italic lowercase
|
||||
(c >= 0x1D4B6 && c <= 0x1D4CF) || // Mathematical script lowercase
|
||||
(c >= 0x1D4EA && c <= 0x1D503) || // Mathematical bold script lowercase
|
||||
(c >= 0x1D51E && c <= 0x1D537) || // Mathematical fraktur lowercase
|
||||
(c >= 0x1D552 && c <= 0x1D56B) || // Mathematical double-struck lowercase
|
||||
(c >= 0x1D586 && c <= 0x1D59F) || // Mathematical bold fraktur lowercase
|
||||
(c >= 0x1D5BA && c <= 0x1D5D3) || // Mathematical sans-serif lowercase
|
||||
(c >= 0x1D5EE && c <= 0x1D607) || // Mathematical sans-serif bold lowercase
|
||||
(c >= 0x1D622 && c <= 0x1D63B) || // Mathematical sans-serif italic lowercase
|
||||
(c >= 0x1D656 && c <= 0x1D66F) || // Mathematical sans-serif bold italic lowercase
|
||||
(c >= 0x1D68A && c <= 0x1D6A5) || // Mathematical monospace lowercase
|
||||
(c >= 0x1D6C1 && c <= 0x1D6DA) || // Mathematical bold lowercase Greek
|
||||
(c >= 0x1D6FB && c <= 0x1D714) || // Mathematical italic lowercase Greek
|
||||
(c >= 0x1D735 && c <= 0x1D74E) || // Mathematical bold italic lowercase Greek
|
||||
(c >= 0x1D76F && c <= 0x1D788) || // Mathematical sans-serif bold lowercase Greek
|
||||
(c >= 0x1D7A9 && c <= 0x1D7C2)) // Mathematical sans-serif bold italic lowercase Greek
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isAlnum(char32_t c) { return isAlpha(c) || isDigit(c); }
|
||||
|
||||
static bool isXdigit(char32_t c)
|
||||
{
|
||||
return isDigit(c) || (c >= U'a' && c <= U'f') || (c >= U'A' && c <= U'F') || (c >= 0xFF10 && c <= 0xFF19)
|
||||
|| // 全角数字
|
||||
(c >= 0xFF21 && c <= 0xFF26) || // 全角大写A-F
|
||||
(c >= 0xFF41 && c <= 0xFF46); // 全角小写a-f
|
||||
}
|
||||
|
||||
static bool isSpace(char32_t c)
|
||||
{
|
||||
return c == U' ' || c == U'\t' || c == U'\n' || c == U'\r' || c == U'\f' || c == U'\v' || c == 0x00A0
|
||||
|| c == 0x1680 || c == 0x2000 || c == 0x2001 || c == 0x2002 || c == 0x2003 || c == 0x2004 || c == 0x2005
|
||||
|| c == 0x2006 || c == 0x2007 || c == 0x2008 || c == 0x2009 || c == 0x200A || c == 0x202F || c == 0x205F
|
||||
|| c == 0x3000;
|
||||
}
|
||||
|
||||
static bool isCntrl(char32_t c) { return (c <= 0x1F) || (c >= 0x7F && c <= 0x9F) || c == 0x2028 || c == 0x2029; }
|
||||
|
||||
static bool isGraph(char32_t c) { return !isSpace(c) && !isCntrl(c) && c != 0x00AD && c != 0x180E; }
|
||||
|
||||
static bool isPrint(char32_t c) { return isGraph(c) || isSpace(c); }
|
||||
|
||||
static bool isPunct(char32_t c) { return isGraph(c) && !isAlnum(c); }
|
||||
|
||||
// 大小写转换
|
||||
static char32_t toLower(char32_t c)
|
||||
{
|
||||
if (isLower(c)) return c;
|
||||
if (c >= U'A' && c <= U'Z') return c + 32;
|
||||
if (c >= 0xC0 && c <= 0xD6 && c != 0xD7) return c + 32;
|
||||
if (c >= 0xD8 && c <= 0xDE) return c + 32;
|
||||
|
||||
assert(false && "Unsupported yet");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static char32_t toUpper(char32_t c)
|
||||
{
|
||||
if (isUpper(c)) return c;
|
||||
if (c >= U'a' && c <= U'z') return c - 32;
|
||||
if (c >= 0xE0 && c <= 0xF6 && c != 0xF7) return c - 32;
|
||||
if (c >= 0xF8 && c <= 0xFE) return c - 32;
|
||||
|
||||
assert(false && "Unsupported yet");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
// 检查是否是空白字符
|
||||
static bool isWhitespace(char32_t c)
|
||||
{
|
||||
return c == U' ' || c == U'\t' || c == U'\n' || c == U'\r' || c == U'\f' || c == U'\v' || c == 0x00A0
|
||||
|| c == 0x1680 || c == 0x2000 || c == 0x2001 || c == 0x2002 || c == 0x2003 || c == 0x2004 || c == 0x2005
|
||||
|| c == 0x2006 || c == 0x2007 || c == 0x2008 || c == 0x2009 || c == 0x200A || c == 0x2028 || c == 0x2029
|
||||
|| c == 0x202F || c == 0x205F || c == 0x3000;
|
||||
}
|
||||
|
||||
// 检查是否是标点符号
|
||||
static bool isPunctuation(char32_t c)
|
||||
{
|
||||
return (c >= 0x21 && c <= 0x2F) || (c >= 0x3A && c <= 0x40) || (c >= 0x5B && c <= 0x60)
|
||||
|| (c >= 0x7B && c <= 0x7E) || (c >= 0x2000 && c <= 0x206F) || (c >= 0x2E00 && c <= 0x2E7F)
|
||||
|| (c >= 0x3000 && c <= 0x303F) || (c >= 0xFE10 && c <= 0xFE1F) || (c >= 0xFE30 && c <= 0xFE4F)
|
||||
|| (c >= 0xFF00 && c <= 0xFF0F) || (c >= 0xFF1A && c <= 0xFF20) || (c >= 0xFF3B && c <= 0xFF40)
|
||||
|| (c >= 0xFF5B && c <= 0xFF65);
|
||||
}
|
||||
|
||||
// 检查是否是表情符号
|
||||
static bool isEmoji(char32_t c)
|
||||
{
|
||||
return (c >= 0x1F600 && c <= 0x1F64F) || // Emoticons
|
||||
(c >= 0x1F300 && c <= 0x1F5FF) || // Miscellaneous Symbols and Pictographs
|
||||
(c >= 0x1F680 && c <= 0x1F6FF) || // Transport and Map Symbols
|
||||
(c >= 0x1F900 && c <= 0x1F9FF) || // Supplemental Symbols and Pictographs
|
||||
(c >= 0x2600 && c <= 0x26FF) || // Miscellaneous Symbols
|
||||
(c >= 0x2700 && c <= 0x27BF) || // Dingbats
|
||||
(c >= 0xFE00 && c <= 0xFE0F) || // Variation Selectors
|
||||
(c >= 0x1F1E6 && c <= 0x1F1FF) || // Regional Indicator Symbols
|
||||
(c >= 0xE0020 && c <= 0xE007F); // Tags
|
||||
}
|
||||
};
|
||||
12
src/Core/String.cpp
Normal file
12
src/Core/String.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <Core/String.hpp>
|
||||
|
||||
namespace Fig::StringClass::DynamicCapacity
|
||||
{
|
||||
char32_t Iterator::operator*() const
|
||||
{
|
||||
if (str->is_ascii)
|
||||
return str->is_heap ? str->heap.ascii[index] : str->sso.ascii[index];
|
||||
else
|
||||
return str->is_heap ? str->heap.utf32[index] : str->sso.utf32[index];
|
||||
}
|
||||
};
|
||||
2052
src/Core/String.hpp
Normal file
2052
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;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,9 @@ namespace Fig
|
||||
return FStringView(reinterpret_cast<const char8_t *>(sv.data()));
|
||||
}
|
||||
|
||||
explicit FStringView(std::string_view sv)
|
||||
{
|
||||
*this = fromBasicStringView(sv);
|
||||
}
|
||||
explicit FStringView(std::string_view sv) { *this = fromBasicStringView(sv); }
|
||||
|
||||
explicit FStringView()
|
||||
{
|
||||
*this = fromBasicStringView(std::string_view(""));
|
||||
}
|
||||
explicit FStringView() { *this = fromBasicStringView(std::string_view("")); }
|
||||
|
||||
std::string_view toBasicStringView() const
|
||||
{
|
||||
@@ -34,55 +28,26 @@ namespace Fig
|
||||
}
|
||||
};
|
||||
|
||||
class FString : public std::u8string
|
||||
class String : public std::u8string
|
||||
{
|
||||
public:
|
||||
using std::u8string::u8string;
|
||||
using std::u8string::operator=;
|
||||
|
||||
FString operator+(const FString &x)
|
||||
{
|
||||
return FString(toBasicString() + x.toBasicString());
|
||||
}
|
||||
FString operator+(const char8_t *c)
|
||||
{
|
||||
return FString(*this + std::u8string(c));
|
||||
}
|
||||
String operator+(const String &x) { return String(toBasicString() + x.toBasicString()); }
|
||||
String operator+(const char8_t *c) { return String(*this + std::u8string(c)); }
|
||||
|
||||
explicit FString(const std::u8string &str)
|
||||
{
|
||||
*this = fromU8String(str);
|
||||
}
|
||||
explicit FString(std::string str)
|
||||
{
|
||||
*this = fromBasicString(str);
|
||||
}
|
||||
explicit FString(FStringView sv)
|
||||
{
|
||||
*this = fromStringView(sv);
|
||||
}
|
||||
std::string toBasicString() const
|
||||
{
|
||||
return std::string(this->begin(), this->end());
|
||||
}
|
||||
FStringView toStringView() const
|
||||
{
|
||||
return FStringView(this->data(), this->size());
|
||||
}
|
||||
explicit String(const std::u8string &str) { *this = fromU8String(str); }
|
||||
explicit String(std::string str) { *this = fromBasicString(str); }
|
||||
explicit String(FStringView sv) { *this = fromStringView(sv); }
|
||||
std::string toBasicString() const { return std::string(this->begin(), this->end()); }
|
||||
FStringView toStringView() const { return FStringView(this->data(), this->size()); }
|
||||
|
||||
static FString fromBasicString(const std::string &str)
|
||||
{
|
||||
return FString(str.begin(), str.end());
|
||||
}
|
||||
static String fromBasicString(const std::string &str) { return String(str.begin(), str.end()); }
|
||||
|
||||
static FString fromStringView(FStringView sv)
|
||||
{
|
||||
return FString(reinterpret_cast<const char *>(sv.data()));
|
||||
}
|
||||
static String fromStringView(FStringView sv) { return String(reinterpret_cast<const char *>(sv.data())); }
|
||||
|
||||
static FString fromU8String(const std::u8string &str)
|
||||
{
|
||||
return FString(str.begin(), str.end());
|
||||
}
|
||||
static String fromU8String(const std::u8string &str) { return String(str.begin(), str.end()); }
|
||||
|
||||
size_t length() const
|
||||
{
|
||||
@@ -90,17 +55,14 @@ namespace Fig
|
||||
size_t len = 0;
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if ((*it & 0xC0) != 0x80)
|
||||
{
|
||||
++len;
|
||||
}
|
||||
if ((*it & 0xC0) != 0x80) { ++len; }
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
FString getRealChar(size_t index)
|
||||
String getRealChar(size_t index)
|
||||
{
|
||||
FString ch;
|
||||
String ch;
|
||||
size_t cnt = 0;
|
||||
for (size_t i = 0; i < size();)
|
||||
{
|
||||
@@ -111,13 +73,9 @@ namespace Fig
|
||||
cplen = 3;
|
||||
else if ((at(i) & 0xe0) == 0xc0)
|
||||
cplen = 2;
|
||||
if (i + cplen > size())
|
||||
cplen = 1;
|
||||
if (i + cplen > size()) cplen = 1;
|
||||
|
||||
if (cnt == index)
|
||||
{
|
||||
ch += substr(i, cplen);
|
||||
}
|
||||
if (cnt == index) { ch += substr(i, cplen); }
|
||||
|
||||
i += cplen;
|
||||
++cnt;
|
||||
@@ -126,7 +84,7 @@ namespace Fig
|
||||
return ch;
|
||||
}
|
||||
|
||||
void realReplace(size_t index, const FString &src)
|
||||
void realReplace(size_t index, const String &src)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
for (size_t i = 0; i < size();)
|
||||
@@ -138,13 +96,9 @@ namespace Fig
|
||||
cplen = 3;
|
||||
else if ((at(i) & 0xe0) == 0xc0)
|
||||
cplen = 2;
|
||||
if (i + cplen > size())
|
||||
cplen = 1;
|
||||
if (i + cplen > size()) cplen = 1;
|
||||
|
||||
if (cnt == index)
|
||||
{
|
||||
*this = FString(substr(0, i)) + src + FString(substr(i + cplen));
|
||||
}
|
||||
if (cnt == index) { *this = String(substr(0, i)) + src + String(substr(i + cplen)); }
|
||||
|
||||
i += cplen;
|
||||
++cnt;
|
||||
@@ -164,32 +118,22 @@ namespace Fig
|
||||
cplen = 3;
|
||||
else if ((at(i) & 0xe0) == 0xc0)
|
||||
cplen = 2;
|
||||
if (i + cplen > size())
|
||||
cplen = 1;
|
||||
if (i + cplen > size()) cplen = 1;
|
||||
|
||||
i += cplen;
|
||||
++cnt;
|
||||
|
||||
if (cnt == index)
|
||||
{
|
||||
eraseStart = i;
|
||||
}
|
||||
if (cnt < index + n)
|
||||
{
|
||||
eraseCplens += cplen;
|
||||
}
|
||||
if (cnt == index) { eraseStart = i; }
|
||||
if (cnt < index + n) { eraseCplens += cplen; }
|
||||
}
|
||||
erase(eraseStart, eraseCplens);
|
||||
}
|
||||
|
||||
void realInsert(size_t index, const FString &src)
|
||||
void realInsert(size_t index, const String &src)
|
||||
{
|
||||
if (index == length())
|
||||
{
|
||||
for (auto &c : src)
|
||||
{
|
||||
push_back(c);
|
||||
}
|
||||
for (auto &c : src) { push_back(c); }
|
||||
return;
|
||||
}
|
||||
size_t cnt = 0;
|
||||
@@ -202,13 +146,9 @@ namespace Fig
|
||||
cplen = 3;
|
||||
else if ((at(i) & 0xe0) == 0xc0)
|
||||
cplen = 2;
|
||||
if (i + cplen > size())
|
||||
cplen = 1;
|
||||
if (i + cplen > size()) cplen = 1;
|
||||
|
||||
if (cnt == index)
|
||||
{
|
||||
insert(i, src);
|
||||
}
|
||||
if (cnt == index) { insert(i, src); }
|
||||
|
||||
i += cplen;
|
||||
++cnt;
|
||||
@@ -221,9 +161,9 @@ namespace Fig
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<Fig::FString>
|
||||
struct hash<Fig::String>
|
||||
{
|
||||
std::size_t operator()(const Fig::FString &s) const
|
||||
std::size_t operator()(const Fig::String &s) const
|
||||
{
|
||||
return std::hash<std::u8string>{}(static_cast<const std::u8string &>(s));
|
||||
}
|
||||
@@ -58,7 +58,7 @@ namespace Fig
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
code_point = char_data_[0];
|
||||
code_point = static_cast<char32_t>(char_data_[0]);
|
||||
break;
|
||||
case 2:
|
||||
code_point = ((char_data_[0] & 0x1F) << 6) | (char_data_[1] & 0x3F);
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#define __FCORE_VERSION "0.4.2-alpha"
|
||||
#define __FCORE_VERSION "0.4.3-alpha"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define __FCORE_PLATFORM "Windows"
|
||||
@@ -54,6 +54,5 @@ namespace Fig
|
||||
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||
inline constexpr FString MAIN_FUNCTION = u8"main";
|
||||
}; // namespace Core
|
||||
}; // namespace Fig
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
const std::unordered_map<std::size_t, FString> Warning::standardWarnings = {
|
||||
{1, FString(u8"Identifier is too similar to a keyword or a primitive type")},
|
||||
{2, FString(u8"The identifier is too abstract")}
|
||||
const std::unordered_map<std::size_t, String> Warning::standardWarnings = {
|
||||
{1, String(U"Identifier is too similar to a keyword or a primitive type")},
|
||||
{2, String(U"The identifier is too abstract")}
|
||||
};
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <Utils/magic_enum/magic_enum.hpp>
|
||||
#include <unordered_map>
|
||||
@@ -11,16 +11,17 @@ namespace Fig
|
||||
{
|
||||
private:
|
||||
size_t id; // the id (standard) of warning
|
||||
FString msg;
|
||||
String msg;
|
||||
size_t line, column;
|
||||
|
||||
public:
|
||||
static const std::unordered_map<size_t, FString> standardWarnings;
|
||||
Warning(size_t _id, FString _msg)
|
||||
static const std::unordered_map<size_t, String> standardWarnings;
|
||||
Warning(size_t _id, String _msg)
|
||||
{
|
||||
id = _id;
|
||||
msg = std::move(_msg);
|
||||
}
|
||||
Warning(size_t _id, FString _msg, size_t _line, size_t _column)
|
||||
Warning(size_t _id, String _msg, size_t _line, size_t _column)
|
||||
{
|
||||
id = _id;
|
||||
msg = std::move(_msg);
|
||||
@@ -28,28 +29,12 @@ namespace Fig
|
||||
column = _column;
|
||||
}
|
||||
|
||||
auto getIDName()
|
||||
{
|
||||
return standardWarnings.at(id);
|
||||
}
|
||||
|
||||
auto getID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
auto getMsg()
|
||||
{
|
||||
return msg;
|
||||
}
|
||||
auto getLine()
|
||||
{
|
||||
return line;
|
||||
}
|
||||
auto getColumn()
|
||||
{
|
||||
return column;
|
||||
}
|
||||
auto getIDName() { return standardWarnings.at(id); }
|
||||
|
||||
auto getID() { return id; }
|
||||
auto getMsg() { return msg; }
|
||||
auto getLine() { return line; }
|
||||
auto getColumn() { return column; }
|
||||
};
|
||||
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <format>
|
||||
@@ -14,20 +14,29 @@ namespace Fig
|
||||
{
|
||||
public:
|
||||
explicit AddressableError() {}
|
||||
explicit AddressableError(FString _msg,
|
||||
explicit AddressableError(String _msg,
|
||||
size_t _line,
|
||||
size_t _column,
|
||||
FString _sourcePath,
|
||||
std::vector<FString> _sourceLines,
|
||||
String _sourcePath,
|
||||
std::vector<String> _sourceLines,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
src_loc(loc), line(_line), column(_column), sourcePath(std::move(_sourcePath)), sourceLines(std::move(_sourceLines))
|
||||
src_loc(loc),
|
||||
line(_line),
|
||||
column(_column),
|
||||
sourcePath(std::move(_sourcePath)),
|
||||
sourceLines(std::move(_sourceLines))
|
||||
{
|
||||
message = _msg;
|
||||
}
|
||||
virtual FString toString() const
|
||||
virtual String toString() const
|
||||
{
|
||||
std::string msg = std::format("[AddressableError] {} at {}:{}, in [{}] {}", std::string(this->message.begin(), this->message.end()), this->line, this->column, this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
std::string msg = std::format("[AddressableError] {} at {}:{}, in [{}] {}",
|
||||
this->message.toBasicString(),
|
||||
this->line,
|
||||
this->column,
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
@@ -38,37 +47,36 @@ namespace Fig
|
||||
|
||||
virtual size_t getLine() const { return line; }
|
||||
virtual size_t getColumn() const { return column; }
|
||||
FString getMessage() const { return message; }
|
||||
FString getSourcePath() const { return sourcePath; }
|
||||
std::vector<FString> getSourceLines() const { return sourceLines; }
|
||||
String getMessage() const { return message; }
|
||||
String getSourcePath() const { return sourcePath; }
|
||||
std::vector<String> getSourceLines() const { return sourceLines; }
|
||||
|
||||
virtual FString getErrorType() const
|
||||
{
|
||||
return FString(u8"AddressableError");
|
||||
}
|
||||
virtual String getErrorType() const { return String(U"\1"); }
|
||||
|
||||
protected:
|
||||
size_t line, column;
|
||||
FString message;
|
||||
String message;
|
||||
|
||||
FString sourcePath;
|
||||
std::vector<FString> sourceLines;
|
||||
String sourcePath;
|
||||
std::vector<String> sourceLines;
|
||||
};
|
||||
|
||||
class UnaddressableError : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit UnaddressableError() {}
|
||||
explicit UnaddressableError(FString _msg,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
explicit UnaddressableError(String _msg, std::source_location loc = std::source_location::current()) :
|
||||
src_loc(loc)
|
||||
{
|
||||
message = _msg;
|
||||
}
|
||||
virtual FString toString() const
|
||||
virtual String toString() const
|
||||
{
|
||||
std::string msg = std::format("[UnaddressableError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
std::string msg = std::format("[UnaddressableError] {} in [{}] {}",
|
||||
this->message.toBasicString(),
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
@@ -76,15 +84,12 @@ namespace Fig
|
||||
return msg.c_str();
|
||||
}
|
||||
std::source_location src_loc;
|
||||
FString getMessage() const { return message; }
|
||||
String getMessage() const { return message; }
|
||||
|
||||
virtual FString getErrorType() const
|
||||
{
|
||||
return FString(u8"UnaddressableError");
|
||||
}
|
||||
virtual String getErrorType() const { return String(U"\1"); }
|
||||
|
||||
protected:
|
||||
FString message;
|
||||
String message;
|
||||
};
|
||||
|
||||
class SyntaxError : public AddressableError
|
||||
@@ -92,37 +97,36 @@ namespace Fig
|
||||
public:
|
||||
using AddressableError::AddressableError;
|
||||
|
||||
virtual FString toString() const override
|
||||
virtual String toString() const override
|
||||
{
|
||||
std::string msg = std::format("[SyntaxError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
std::string msg = std::format("[SyntaxError] {} in [{}] {}",
|
||||
this->message.toBasicString(),
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
|
||||
virtual FString getErrorType() const override
|
||||
{
|
||||
return FString(u8"SyntaxError");
|
||||
}
|
||||
virtual String getErrorType() const override { return String(U"\1"); }
|
||||
};
|
||||
|
||||
class RuntimeError final : public UnaddressableError
|
||||
{
|
||||
public:
|
||||
using UnaddressableError::UnaddressableError;
|
||||
explicit RuntimeError(FString _msg,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
explicit RuntimeError(String _msg, std::source_location loc = std::source_location::current()) :
|
||||
UnaddressableError(_msg, loc)
|
||||
{
|
||||
}
|
||||
virtual FString toString() const override
|
||||
virtual String toString() const override
|
||||
{
|
||||
std::string msg = std::format("[RuntimeError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
std::string msg = std::format("[RuntimeError] {} in [{}] {}",
|
||||
this->message.toBasicString(),
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
|
||||
virtual FString getErrorType() const override
|
||||
{
|
||||
return FString(u8"RuntimeError");
|
||||
}
|
||||
virtual String getErrorType() const override { return String(U"\1"); }
|
||||
};
|
||||
|
||||
} // namespace Fig
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <print>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
namespace ErrorLog
|
||||
@@ -87,7 +86,7 @@ namespace Fig
|
||||
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
||||
}; // namespace TerminalColors
|
||||
|
||||
inline void coloredPrint(const char *colorCode, FString msg)
|
||||
inline void coloredPrint(const char *colorCode, String msg)
|
||||
{
|
||||
std::print("{}{}{}", colorCode, msg.toBasicString(), TerminalColors::Reset);
|
||||
}
|
||||
@@ -97,47 +96,90 @@ namespace Fig
|
||||
std::print("{}{}{}", colorCode, msg, TerminalColors::Reset);
|
||||
}
|
||||
|
||||
inline void logFigErrorInterface(const String &errorClass, const String &errorMessage)
|
||||
{
|
||||
namespace TC = TerminalColors;
|
||||
coloredPrint(TC::LightWhite, U"Uncaught Fig exception:\n");
|
||||
coloredPrint(TC::LightRed, U"✖ ");
|
||||
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();
|
||||
const String &fileName = err.getSourcePath();
|
||||
const std::vector<String> &sourceLines = err.getSourceLines();
|
||||
|
||||
std::print("\n");
|
||||
namespace TC = TerminalColors;
|
||||
coloredPrint(TC::LightWhite, "An error occurred! ");
|
||||
coloredPrint(TC::White, std::format("Fig {} ({})[{} {} bit on `{}`]\n",Core::VERSION, Core::COMPILE_TIME, Core::COMPILER, Core::ARCH, Core::PLATFORM));
|
||||
coloredPrint(TC::LightRed, "✖ ");
|
||||
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()));
|
||||
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 pointerLine;
|
||||
coloredPrint(TC::LightWhite, U"An error occurred! ");
|
||||
coloredPrint(TC::White,
|
||||
std::format("Fig {} ({})[{} {} bit on `{}`]\n",
|
||||
Core::VERSION,
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM));
|
||||
coloredPrint(TC::LightRed, U"✖ ");
|
||||
coloredPrint(
|
||||
TC::LightRed,
|
||||
std::format("{}: {}\n", err.getErrorType().toBasicString(), String(err.getMessage()).toBasicString()));
|
||||
coloredPrint(
|
||||
TC::White,
|
||||
std::format(" at {}:{} in file '{}'\n", err.getLine(), err.getColumn(), fileName.toBasicString()));
|
||||
|
||||
String lineContent;
|
||||
String pointerLine;
|
||||
|
||||
if (fileName != U"\1")
|
||||
{
|
||||
lineContent = ((int64_t(err.getLine()) - int64_t(1)) >= 0 ? sourceLines[err.getLine() - 1] : U"\1");
|
||||
for (size_t i = 1; i < err.getColumn(); ++i)
|
||||
{
|
||||
if (lineContent[i - 1] == U'\t')
|
||||
{
|
||||
pointerLine += U'\t';
|
||||
}
|
||||
if (lineContent[i - 1] == U'\t') { pointerLine += U'\t'; }
|
||||
else
|
||||
{
|
||||
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::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()));
|
||||
}
|
||||
|
||||
inline void logUnaddressableError(const UnaddressableError &err)
|
||||
{
|
||||
std::print("\n");
|
||||
namespace TC = TerminalColors;
|
||||
coloredPrint(TC::LightWhite, "An error occurred! ");
|
||||
coloredPrint(TC::White, std::format("Fig {} ({})[{} {} bit on `{}`]\n", Core::VERSION, Core::COMPILE_TIME, Core::COMPILER, Core::ARCH, Core::PLATFORM));
|
||||
coloredPrint(TC::DarkRed, "✖");
|
||||
coloredPrint(TC::Red, std::format("{}: {}\n", err.getErrorType().toBasicString(), FString(err.getMessage()).toBasicString()));
|
||||
coloredPrint(TC::DarkGray, std::format("🔧 in function '{}' ({}:{})", err.src_loc.function_name(), err.src_loc.file_name(), err.src_loc.line()));
|
||||
coloredPrint(TC::LightWhite, U"An error occurred! ");
|
||||
coloredPrint(TC::White,
|
||||
std::format("Fig {} ({})[{} {} bit on `{}`]\n",
|
||||
Core::VERSION,
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM));
|
||||
coloredPrint(TC::DarkRed, U"✖");
|
||||
coloredPrint(
|
||||
TC::Red,
|
||||
std::format("{}: {}\n", err.getErrorType().toBasicString(), String(err.getMessage()).toBasicString()));
|
||||
coloredPrint(TC::DarkGray,
|
||||
std::format("🔧 in function '{}' ({}:{})",
|
||||
err.src_loc.function_name(),
|
||||
err.src_loc.file_name(),
|
||||
err.src_loc.line()));
|
||||
}
|
||||
}; // namespace ErrorLog
|
||||
}; // namespace Fig
|
||||
@@ -1,17 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ast/Statements/InterfaceDefSt.hpp"
|
||||
#include "Value/interface.hpp"
|
||||
#include <Value/Type.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Value/VariableSlot.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/String.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Evaluator/Value/VariableSlot.hpp>
|
||||
#include <Evaluator/Core/ExprResult.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -21,97 +26,126 @@ namespace Fig
|
||||
TypeInfo interfaceType;
|
||||
TypeInfo structType;
|
||||
|
||||
std::unordered_map<FString, Function> implMethods;
|
||||
std::unordered_map<String, 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>
|
||||
{
|
||||
private:
|
||||
FString scopeName;
|
||||
std::unordered_map<FString, std::shared_ptr<VariableSlot>> variables;
|
||||
String scopeName;
|
||||
std::unordered_map<String, std::shared_ptr<VariableSlot>> variables;
|
||||
|
||||
std::unordered_map<std::size_t, Function> functions;
|
||||
std::unordered_map<std::size_t, FString> functionNames;
|
||||
// std::unordered_map<std::size_t, Function> functions;
|
||||
// std::unordered_map<std::size_t, String> functionNames;
|
||||
|
||||
// implRegistry <Struct, ordered list of ImplRecord>
|
||||
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> implRegistry;
|
||||
std::unordered_map<TypeInfo, OperationRecord, TypeInfoHash> opRegistry;
|
||||
|
||||
public:
|
||||
ContextPtr parent;
|
||||
|
||||
Context(const Context &) = default;
|
||||
Context(const FString &name, ContextPtr p = nullptr) : scopeName(name), parent(p) {}
|
||||
Context(const String &name, ContextPtr p = nullptr) : scopeName(name), parent(p) {}
|
||||
|
||||
void setParent(ContextPtr _parent) { parent = _parent; }
|
||||
|
||||
void setScopeName(FString _name) { scopeName = std::move(_name); }
|
||||
void setScopeName(String _name) { scopeName = std::move(_name); }
|
||||
|
||||
FString getScopeName() const { return scopeName; }
|
||||
String getScopeName() const { return scopeName; }
|
||||
|
||||
void merge(const Context &c)
|
||||
{
|
||||
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());
|
||||
opRegistry.insert(c.opRegistry.begin(), c.opRegistry.end());
|
||||
// structTypeNames.insert(c.structTypeNames.begin(),
|
||||
// c.structTypeNames.end());
|
||||
}
|
||||
|
||||
std::unordered_map<size_t, Function> getFunctions() const { return functions; }
|
||||
std::unordered_map<size_t, Function> getFunctions() const
|
||||
{
|
||||
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 String &name)
|
||||
{
|
||||
auto it = variables.find(name);
|
||||
if (it != variables.end()) return it->second;
|
||||
if (parent) return parent->get(name);
|
||||
throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
AccessModifier getAccessModifier(const FString &name)
|
||||
AccessModifier getAccessModifier(const String &name)
|
||||
{
|
||||
if (variables.contains(name)) { return variables[name]->am; }
|
||||
else if (parent != nullptr) { return parent->getAccessModifier(name); }
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
}
|
||||
bool isVariableMutable(const FString &name)
|
||||
bool isVariableMutable(const String &name)
|
||||
{
|
||||
AccessModifier am = getAccessModifier(name); // may throw
|
||||
return !isAccessConst(am);
|
||||
}
|
||||
bool isVariablePublic(const FString &name)
|
||||
bool isVariablePublic(const String &name)
|
||||
{
|
||||
AccessModifier am = getAccessModifier(name); // may throw
|
||||
return isAccessPublic(am);
|
||||
}
|
||||
void set(const FString &name, ObjectPtr value)
|
||||
void set(const String &name, ObjectPtr value)
|
||||
{
|
||||
if (variables.contains(name))
|
||||
{
|
||||
if (!isVariableMutable(name))
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Variable '{}' is immutable", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable '{}' is immutable", name.toBasicString())));
|
||||
}
|
||||
variables[name]->value = value;
|
||||
}
|
||||
else if (parent != nullptr) { parent->set(name, value); }
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
}
|
||||
void _update(const FString &name, ObjectPtr value)
|
||||
void _update(const String &name, ObjectPtr value)
|
||||
{
|
||||
if (variables.contains(name)) { variables[name]->value = value; }
|
||||
else if (parent != nullptr) { parent->_update(name, value); }
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
}
|
||||
void def(const FString &name,
|
||||
void def(const String &name,
|
||||
const TypeInfo &ti,
|
||||
AccessModifier am,
|
||||
const ObjectPtr &value = Object::getNullInstance())
|
||||
@@ -119,15 +153,9 @@ namespace Fig
|
||||
if (containsInThisScope(name))
|
||||
{
|
||||
throw RuntimeError(
|
||||
FString(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
||||
String(std::format("Variable '{}' already defined in this scope", 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;
|
||||
}
|
||||
// if (ti == ValueType::StructType)
|
||||
// {
|
||||
// auto &st = value->as<StructType>();
|
||||
@@ -135,43 +163,29 @@ namespace Fig
|
||||
// }
|
||||
}
|
||||
void
|
||||
defReference(const FString &name, const TypeInfo &ti, AccessModifier am, std::shared_ptr<VariableSlot> target)
|
||||
defReference(const String &name, const TypeInfo &ti, AccessModifier am, std::shared_ptr<VariableSlot> target)
|
||||
{
|
||||
if (containsInThisScope(name))
|
||||
{
|
||||
throw RuntimeError(
|
||||
FString(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
||||
String(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
||||
}
|
||||
variables[name] = std::make_shared<VariableSlot>(
|
||||
name,
|
||||
target->value,
|
||||
ti,
|
||||
am,
|
||||
true,
|
||||
target
|
||||
);
|
||||
variables[name] = std::make_shared<VariableSlot>(name, target->value, ti, am, true, target);
|
||||
}
|
||||
std::optional<Function> getFunction(std::size_t id)
|
||||
|
||||
std::optional<String> getFunctionName(std::size_t id)
|
||||
{
|
||||
auto it = functions.find(id);
|
||||
if (it != functions.end()) { return it->second; }
|
||||
else if (parent) { return parent->getFunction(id); }
|
||||
else
|
||||
for (auto &[name, slot] : variables)
|
||||
{
|
||||
if (slot->declaredType == ValueType::Function)
|
||||
{
|
||||
const Function &fn = slot->value->as<Function>();
|
||||
if (fn.id == id) { return name; }
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
std::optional<FString> getFunctionName(std::size_t id)
|
||||
{
|
||||
auto it = functionNames.find(id);
|
||||
if (it != functionNames.end()) { return it->second; }
|
||||
else if (parent) { return parent->getFunctionName(id); }
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
// std::optional<FString> getStructName(std::size_t id)
|
||||
// std::optional<String> getStructName(std::size_t id)
|
||||
// {
|
||||
// auto it = structTypeNames.find(id);
|
||||
// if (it != structTypeNames.end())
|
||||
@@ -187,21 +201,21 @@ namespace Fig
|
||||
// return std::nullopt;
|
||||
// }
|
||||
// }
|
||||
bool contains(const FString &name)
|
||||
bool contains(const String &name)
|
||||
{
|
||||
if (variables.contains(name)) { return true; }
|
||||
else if (parent != nullptr) { return parent->contains(name); }
|
||||
return false;
|
||||
}
|
||||
bool containsInThisScope(const FString &name) const { return variables.contains(name); }
|
||||
bool containsInThisScope(const String &name) const { return variables.contains(name); }
|
||||
|
||||
TypeInfo getTypeInfo(const FString &name) { return get(name)->declaredType; }
|
||||
TypeInfo getTypeInfo(const String &name) { return get(name)->declaredType; }
|
||||
bool isInFunctionContext()
|
||||
{
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<Function ") == 0) { return true; }
|
||||
if (ctx->getScopeName().find(U"\1") == 0) { return true; }
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
@@ -211,10 +225,7 @@ namespace Fig
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<While ") == 0 or ctx->getScopeName().find(u8"<For ") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (ctx->getScopeName().find(U"\1") == 0 or ctx->getScopeName().find(U"\1") == 0) { return true; }
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
@@ -233,6 +244,13 @@ namespace Fig
|
||||
return parent && parent->hasImplRegisted(structType, interfaceType);
|
||||
}
|
||||
|
||||
std::unordered_map<TypeInfo, std::vector<ImplRecord>, TypeInfoHash> getImplRegistry() 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);
|
||||
@@ -261,7 +279,7 @@ namespace Fig
|
||||
list.push_back(record); // order is the level
|
||||
}
|
||||
|
||||
bool hasMethodImplemented(const TypeInfo &structType, const FString &functionName) const
|
||||
bool hasMethodImplemented(const TypeInfo &structType, const String &functionName) const
|
||||
{
|
||||
auto it = implRegistry.find(structType);
|
||||
if (it != implRegistry.end())
|
||||
@@ -275,7 +293,7 @@ namespace Fig
|
||||
return parent && parent->hasMethodImplemented(structType, functionName);
|
||||
}
|
||||
|
||||
bool hasDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) const
|
||||
bool hasDefaultImplementedMethod(const TypeInfo &structType, const String &functionName) const
|
||||
{
|
||||
auto it = implRegistry.find(structType);
|
||||
if (it == implRegistry.end()) return false;
|
||||
@@ -304,7 +322,7 @@ namespace Fig
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName)
|
||||
Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const String &functionName)
|
||||
{
|
||||
// O(N²)
|
||||
// SLOW
|
||||
@@ -345,7 +363,7 @@ namespace Fig
|
||||
assert(false);
|
||||
}
|
||||
|
||||
const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const
|
||||
const Function &getImplementedMethod(const TypeInfo &structType, const String &functionName) const
|
||||
{
|
||||
auto it = implRegistry.find(structType);
|
||||
if (it != implRegistry.end())
|
||||
@@ -363,6 +381,74 @@ namespace Fig
|
||||
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
|
||||
{
|
||||
const Context *ctx = this;
|
||||
|
||||
103
src/Evaluator/Core/Eval.cpp
Normal file
103
src/Evaluator/Core/Eval.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#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(String(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(String(std::format("err type of expr: {}", magic_enum::enum_name(type))));
|
||||
}
|
||||
}
|
||||
return Object::getNullInstance(); // ignore warning
|
||||
}
|
||||
}; // namespace Fig
|
||||
439
src/Evaluator/Core/EvalBinary.cpp
Normal file
439
src/Evaluator/Core/EvalBinary.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
#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(String(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(U"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(
|
||||
U"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 String &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(String(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(
|
||||
String(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 == U"true") { return Object::getTrueInstance(); }
|
||||
else if (str == U"false") { return Object::getFalseInstance(); }
|
||||
return ExprResult::error(
|
||||
genTypeError(String(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(String(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(U"UnsupportedOp",
|
||||
std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)),
|
||||
bin);
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
201
src/Evaluator/Core/EvalFunctionCall.cpp
Normal file
201
src/Evaluator/Core/EvalFunctionCall.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
#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 String &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(U"ObjectNotCallable",
|
||||
std::format("Object `{}` isn't callable", fnObj->toString().toBasicString()),
|
||||
call->callee);
|
||||
}
|
||||
|
||||
const Function &fn = fnObj->as<Function>();
|
||||
|
||||
const String &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(U"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>(String(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(String(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(U"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(
|
||||
U"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(U"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++)
|
||||
{
|
||||
String 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(U"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
|
||||
364
src/Evaluator/Core/EvalInitExpr.cpp
Normal file
364
src/Evaluator/Core/EvalInitExpr.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#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 String &structName = structeLv.name();
|
||||
if (!structTypeVal->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(U"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(U"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(
|
||||
U"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(U"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(
|
||||
U"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(U"StructInitArgumentMismatchError",
|
||||
std::format("Structure '{}' expects {} to {} fields, but {} were provided",
|
||||
structName.toBasicString(),
|
||||
minArgs,
|
||||
maxArgs,
|
||||
initExpr->args.size()),
|
||||
initExpr);
|
||||
}
|
||||
|
||||
std::vector<std::pair<String, 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>(
|
||||
String(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 String &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,
|
||||
ctx)); // it can't be null here
|
||||
|
||||
// type check
|
||||
if (!isTypeMatch(expectedType, defaultVal, ctx))
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"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(
|
||||
U"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 String &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
|
||||
if (instanceCtx->containsInThisScope(fieldName))
|
||||
{
|
||||
throw EvaluatorError(U"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(
|
||||
U"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(
|
||||
U"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(U"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(
|
||||
U"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(
|
||||
U"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
ContextPtr stDefCtx = structT.defContext;
|
||||
|
||||
// load struct method
|
||||
for (auto &[id, fn] : stDefCtx->getFunctions())
|
||||
{
|
||||
const String &funcName = fn.name;
|
||||
const auto &funcSlot = stDefCtx->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));
|
||||
}
|
||||
}; // namespace Fig
|
||||
248
src/Evaluator/Core/EvalLvObject.cpp
Normal file
248
src/Evaluator/Core/EvalLvObject.cpp
Normal 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 String &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(U"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 String &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(U"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(
|
||||
U"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(U"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(
|
||||
U"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(
|
||||
U"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(
|
||||
U"TypeError",
|
||||
std::format("Type `String` indices must be `Int`, got '{}'", prettyType(index).toBasicString()),
|
||||
ie->index);
|
||||
}
|
||||
String &string = base->as<ValueType::StringClass>();
|
||||
ValueType::IntClass indexVal = index->as<ValueType::IntClass>();
|
||||
if (indexVal >= string.length())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"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(
|
||||
U"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(
|
||||
U"TypeError",
|
||||
std::format("Expression '{}' doesn't refer to a lvalue", exp->typeName().toBasicString()),
|
||||
exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
673
src/Evaluator/Core/EvalStatement.cpp
Normal file
673
src/Evaluator/Core/EvalStatement.cpp
Normal file
@@ -0,0 +1,673 @@
|
||||
#include <Evaluator/Core/ExprResult.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Ast/Expressions/FunctionCall.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Core/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>
|
||||
|
||||
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(
|
||||
U"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(U"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)
|
||||
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 String &fnName = fnDef->name;
|
||||
if (ctx->containsInThisScope(fnName))
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"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(
|
||||
U"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>(String(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<String> _fieldNames;
|
||||
for (Ast::StructDefField field : stDef->fields)
|
||||
{
|
||||
if (Utils::vectorContains(field.fieldName, _fieldNames))
|
||||
{
|
||||
throw EvaluatorError(U"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(U"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 String &interfaceName = ifd->name;
|
||||
|
||||
if (ctx->containsInThisScope(interfaceName))
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"RedeclarationError",
|
||||
std::format("Interface `{}` already declared in this scope", interfaceName.toBasicString()),
|
||||
ifd);
|
||||
}
|
||||
TypeInfo type(interfaceName, true); // register interface
|
||||
ctx->def(interfaceName,
|
||||
type,
|
||||
(ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const),
|
||||
std::make_shared<Object>(InterfaceType(type, ifd->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(U"DuplicateImplError",
|
||||
std::format("Duplicate implement `{}` for `{}`",
|
||||
interfaceType.toString().toBasicString(),
|
||||
structType.toString().toBasicString()),
|
||||
ip);
|
||||
}
|
||||
if (!ctx->contains(ip->interfaceName))
|
||||
{
|
||||
throw EvaluatorError(U"InterfaceNotFoundError",
|
||||
std::format("Interface '{}' not found", ip->interfaceName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
if (!ctx->contains(ip->structName))
|
||||
{
|
||||
throw EvaluatorError(U"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(
|
||||
U"NotAInterfaceError",
|
||||
std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
if (!structTypeObj->is<StructType>())
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"NotAStructType",
|
||||
std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
|
||||
auto &implementMethods = ip->methods;
|
||||
|
||||
if (ip->interfaceName == U"Operation")
|
||||
{
|
||||
// 运算符重载
|
||||
/*
|
||||
impl Operation for xxx
|
||||
{
|
||||
add(l, r) {...}
|
||||
}
|
||||
*/
|
||||
if (ValueType::isTypeBuiltin(structType))
|
||||
{
|
||||
throw EvaluatorError(U"BadUserError",
|
||||
std::format("Don't overload built-in type operators plz! `{}`",
|
||||
prettyType(structTypeObj).toBasicString()),
|
||||
ip);
|
||||
}
|
||||
|
||||
using enum Ast::Operator;
|
||||
static const std::unordered_map<String, std::pair<Ast::Operator, size_t>> magic_name_to_op = {
|
||||
// 算术
|
||||
{U"Add", {Ast::Operator::Add, 2}},
|
||||
{U"Sub", {Ast::Operator::Subtract, 2}},
|
||||
{U"Mul", {Ast::Operator::Multiply, 2}},
|
||||
{U"Div", {Ast::Operator::Divide, 2}},
|
||||
{U"Mod", {Ast::Operator::Modulo, 2}},
|
||||
{U"Pow", {Ast::Operator::Power, 2}},
|
||||
|
||||
// 逻辑(一元)
|
||||
{U"Neg", {Ast::Operator::Subtract, 1}}, // 一元负号
|
||||
{U"Not", {Ast::Operator::Not, 1}},
|
||||
|
||||
// 逻辑(二元)
|
||||
{U"And", {Ast::Operator::And, 2}},
|
||||
{U"Or", {Ast::Operator::Or, 2}},
|
||||
|
||||
// 比较
|
||||
{U"Equal", {Ast::Operator::Equal, 2}},
|
||||
{U"NotEqual", {Ast::Operator::NotEqual, 2}},
|
||||
{U"LessThan", {Ast::Operator::Less, 2}},
|
||||
{U"LessEqual", {Ast::Operator::LessEqual, 2}},
|
||||
{U"GreaterThan", {Ast::Operator::Greater, 2}},
|
||||
{U"GreaterEqual", {Ast::Operator::GreaterEqual, 2}},
|
||||
{U"Is", {Ast::Operator::Is, 2}},
|
||||
|
||||
// 位运算(一元)
|
||||
{U"BitNot", {Ast::Operator::BitNot, 1}},
|
||||
|
||||
// 位运算(二元)
|
||||
{U"BitAnd", {Ast::Operator::BitAnd, 2}},
|
||||
{U"BitOr", {Ast::Operator::BitOr, 2}},
|
||||
{U"BitXor", {Ast::Operator::BitXor, 2}},
|
||||
{U"ShiftLeft", {Ast::Operator::ShiftLeft, 2}},
|
||||
{U"ShiftRight", {Ast::Operator::ShiftRight, 2}},
|
||||
};
|
||||
for (auto &implMethod : implementMethods)
|
||||
{
|
||||
const String &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(
|
||||
U"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(U"InterfaceSignatureMismatch",
|
||||
std::format("Operator {} for {} arg count must be {}, got {}",
|
||||
opName.toBasicString(),
|
||||
structLv.name().toBasicString(),
|
||||
expectArgCnt,
|
||||
paraCnt),
|
||||
ip);
|
||||
}
|
||||
|
||||
String opFnName(U"Operation." + prettyType(structTypeObj) + U"." + opName);
|
||||
|
||||
ContextPtr fnCtx = std::make_shared<Context>(
|
||||
String(std::format("<Function {}>", opFnName.toBasicString())), ctx);
|
||||
|
||||
const auto &fillOpFnParas = [this, structType, implMethod, opFnName, fnCtx, ctx, paraCnt](
|
||||
const std::vector<ObjectPtr> &args) {
|
||||
const Ast::FunctionParameters ¶s = implMethod.paras;
|
||||
for (size_t i = 0; i < paraCnt; ++i)
|
||||
{
|
||||
const TypeInfo ¶Type =
|
||||
actualType(check_unwrap_stres(eval(paras.posParas[i].second, ctx)));
|
||||
if (paraType != ValueType::Any && paraType != structType)
|
||||
{
|
||||
throw EvaluatorError(
|
||||
U"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]);
|
||||
}
|
||||
};
|
||||
|
||||
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<String, Ast::InterfaceMethod> ifaceMethods;
|
||||
for (auto &m : interface.methods)
|
||||
{
|
||||
if (ifaceMethods.contains(m.name))
|
||||
{
|
||||
throw EvaluatorError(U"InterfaceDuplicateMethodError",
|
||||
std::format("Interface '{}' has duplicate method '{}'",
|
||||
interfaceType.toString().toBasicString(),
|
||||
m.name.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
ifaceMethods[m.name] = m;
|
||||
}
|
||||
|
||||
std::unordered_set<String> implemented;
|
||||
|
||||
for (auto &implMethod : implementMethods)
|
||||
{
|
||||
const String &name = implMethod.name;
|
||||
|
||||
// ---- redundant impl ----
|
||||
if (!ifaceMethods.contains(name))
|
||||
{
|
||||
throw EvaluatorError(U"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(U"DuplicateImplementMethodError",
|
||||
std::format("Duplicate implement method '{}'", name.toBasicString()),
|
||||
ip);
|
||||
}
|
||||
|
||||
auto &ifMethod = ifaceMethods[name];
|
||||
|
||||
// ---- signature check ----
|
||||
if (!isInterfaceSignatureMatch(implMethod, ifMethod))
|
||||
{
|
||||
throw EvaluatorError(U"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(U"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(U"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(
|
||||
U"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(
|
||||
U"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(
|
||||
U"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>(
|
||||
String(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>(
|
||||
String(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;
|
||||
|
||||
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(
|
||||
U"TypeError",
|
||||
std::format("Condition must be boolean, but got '{}'", prettyType(condVal).toBasicString()),
|
||||
forSt->condition);
|
||||
}
|
||||
if (!condVal->as<ValueType::BoolClass>()) { break; }
|
||||
iteration++;
|
||||
ContextPtr iterationContext = std::make_shared<Context>(
|
||||
String(std::format(
|
||||
"<For {}:{}, Iteration {}>", forSt->getAAI().line, forSt->getAAI().column, iteration)),
|
||||
loopContext); // every loop has its own context
|
||||
StatementResult sr = evalBlockStatement(forSt->body, iterationContext);
|
||||
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>(
|
||||
String(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 String &errVarName = cat.errVarName;
|
||||
TypeInfo errVarType = (cat.hasType ? TypeInfo(cat.errVarType) : ValueType::Any);
|
||||
if (isTypeMatch(errVarType, sr.result, ctx))
|
||||
{
|
||||
ContextPtr catchCtx = std::make_shared<Context>(
|
||||
String(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(U"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(U"TypeError", U"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(U"BreakOutsideLoopError", U"`break` statement outside loop", stmt);
|
||||
}
|
||||
if (!ctx->isInLoopContext())
|
||||
{
|
||||
throw EvaluatorError(U"BreakOutsideLoopError", U"`break` statement outside loop", stmt);
|
||||
}
|
||||
return StatementResult::breakFlow();
|
||||
}
|
||||
|
||||
case ContinueSt: {
|
||||
if (!ctx->parent)
|
||||
{
|
||||
throw EvaluatorError(U"ContinueOutsideLoopError", U"`continue` statement outside loop", stmt);
|
||||
}
|
||||
if (!ctx->isInLoopContext())
|
||||
{
|
||||
throw EvaluatorError(U"ContinueOutsideLoopError", U"`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>(
|
||||
String(std::format("<Block at {}:{}>", block->getAAI().line, block->getAAI().column)), ctx);
|
||||
return evalBlockStatement(block, blockCtx);
|
||||
}
|
||||
|
||||
default:
|
||||
throw RuntimeError(
|
||||
String(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType()))));
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
21
src/Evaluator/Core/EvalTernary.cpp
Normal file
21
src/Evaluator/Core/EvalTernary.cpp
Normal 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(
|
||||
U"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));
|
||||
}
|
||||
};
|
||||
48
src/Evaluator/Core/EvalUnary.cpp
Normal file
48
src/Evaluator/Core/EvalUnary.cpp
Normal 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(U"UnsupportedOpError",
|
||||
std::format("Unsupported op '{}' for unary expression", magic_enum::enum_name(op)),
|
||||
un);
|
||||
}
|
||||
}
|
||||
}
|
||||
}; // namespace Fig
|
||||
19
src/Evaluator/Core/ExprResult.cpp
Normal file
19
src/Evaluator/Core/ExprResult.cpp
Normal 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));
|
||||
}
|
||||
};
|
||||
72
src/Evaluator/Core/ExprResult.hpp
Normal file
72
src/Evaluator/Core/ExprResult.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/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
|
||||
114
src/Evaluator/Core/ResolveModulePath.cpp
Normal file
114
src/Evaluator/Core/ResolveModulePath.cpp
Normal 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<String> &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 String &modPathStrTop = pathVec.at(0);
|
||||
fs::path modPath;
|
||||
|
||||
bool found = false;
|
||||
for (auto &parentFolder : pathToFind)
|
||||
{
|
||||
modPath = parentFolder / String(modPathStrTop + U".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 / String(modPathStrTop + U".fig").toBasicString();
|
||||
/*
|
||||
if module name is a directory, we require [module
|
||||
name].fig at the directory
|
||||
*/
|
||||
if (!fs::exists(modPath))
|
||||
{
|
||||
throw RuntimeError(String(std::format("requires module file, {}\\{}",
|
||||
modPathStrTop.toBasicString(),
|
||||
String(modPathStrTop + U".fig").toBasicString())));
|
||||
}
|
||||
found = true;
|
||||
path = modPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
throw RuntimeError(String(std::format("Could not find module `{}`", modPathStrTop.toBasicString())));
|
||||
|
||||
bool found2 = false;
|
||||
|
||||
for (size_t i = 1; i < pathVec.size(); ++i) // has next module
|
||||
{
|
||||
const String &next = pathVec.at(i);
|
||||
modPath = modPath.parent_path(); // get the folder
|
||||
modPath = modPath / String(next + U".fig").toBasicString();
|
||||
if (fs::exists(modPath))
|
||||
{
|
||||
if (i != pathVec.size() - 1)
|
||||
throw RuntimeError(String(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(String(std::format("Could not find module `{}`", next.toBasicString())));
|
||||
if (i == pathVec.size() - 1)
|
||||
{
|
||||
// `next` is the last module
|
||||
modPath = modPath / String(next + U".fig").toBasicString();
|
||||
if (!fs::exists(modPath))
|
||||
{
|
||||
throw RuntimeError(String(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(String(std::format("Could not find module `{}`", pathVec.end()->toBasicString())));
|
||||
|
||||
return path;
|
||||
}
|
||||
}; // namespace Fig
|
||||
37
src/Evaluator/Core/StatementResult.hpp
Normal file
37
src/Evaluator/Core/StatementResult.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/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; }
|
||||
};
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Value/value.hpp"
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Evaluator/Value/value_forward.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace Fig
|
||||
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()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Value/VariableSlot.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Evaluator/Value/VariableSlot.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -24,18 +24,15 @@ namespace Fig
|
||||
|
||||
ContextPtr ctx;
|
||||
|
||||
LvObject(std::shared_ptr<VariableSlot> _slot, ContextPtr _ctx) :
|
||||
slot(std::move(_slot)), ctx(_ctx)
|
||||
LvObject(std::shared_ptr<VariableSlot> _slot, ContextPtr _ctx) : slot(std::move(_slot)), ctx(_ctx)
|
||||
{
|
||||
kind = Kind::Variable;
|
||||
}
|
||||
LvObject(ObjectPtr _v, size_t _index, Kind _kind, ContextPtr _ctx) :
|
||||
value(_v), numIndex(_index), ctx(_ctx)
|
||||
LvObject(ObjectPtr _v, size_t _index, Kind _kind, ContextPtr _ctx) : value(_v), numIndex(_index), ctx(_ctx)
|
||||
{
|
||||
kind = _kind;
|
||||
}
|
||||
LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind, ContextPtr _ctx) :
|
||||
value(_v), mapIndex(_index), ctx(_ctx)
|
||||
LvObject(ObjectPtr _v, ObjectPtr _index, Kind _kind, ContextPtr _ctx) : value(_v), mapIndex(_index), ctx(_ctx)
|
||||
{
|
||||
kind = _kind;
|
||||
}
|
||||
@@ -51,27 +48,26 @@ namespace Fig
|
||||
{
|
||||
List &list = value->as<List>();
|
||||
if (numIndex >= list.size())
|
||||
throw RuntimeError(FString(
|
||||
std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
throw RuntimeError(
|
||||
String(std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
return list.at(numIndex).value;
|
||||
}
|
||||
else if (kind == Kind::MapElement) // map
|
||||
{
|
||||
Map &map = value->as<Map>();
|
||||
if (!map.contains(mapIndex))
|
||||
throw RuntimeError(FString(
|
||||
std::format("Key {} not found", mapIndex->toString().toBasicString())));
|
||||
throw RuntimeError(String(std::format("Key {} not found", mapIndex->toString().toBasicString())));
|
||||
return map.at(mapIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// string
|
||||
FString &string = value->as<ValueType::StringClass>();
|
||||
String &string = value->as<ValueType::StringClass>();
|
||||
if (numIndex >= string.length())
|
||||
throw RuntimeError(FString(
|
||||
std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
throw RuntimeError(
|
||||
String(std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
|
||||
return std::make_shared<Object>(string.getRealChar(numIndex));
|
||||
return std::make_shared<Object>(String(string[numIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,26 +78,21 @@ namespace Fig
|
||||
auto s = resolve(slot);
|
||||
if (!isTypeMatch(s->declaredType, v, ctx))
|
||||
{
|
||||
throw RuntimeError(
|
||||
FString(
|
||||
std::format("Variable `{}` expects type `{}`, but got '{}'",
|
||||
throw RuntimeError(String(std::format("Variable `{}` expects type `{}`, but got '{}'",
|
||||
s->name.toBasicString(),
|
||||
s->declaredType.toString().toBasicString(),
|
||||
prettyType(v).toBasicString())));
|
||||
}
|
||||
if (isAccessConst(s->am))
|
||||
{
|
||||
throw RuntimeError(FString(
|
||||
std::format("Variable `{}` is immutable", s->name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Variable `{}` is immutable", s->name.toBasicString())));
|
||||
}
|
||||
s->value = v;
|
||||
}
|
||||
else if (kind == Kind::ListElement)
|
||||
{
|
||||
List &list = value->as<List>();
|
||||
if (numIndex >= list.size())
|
||||
throw RuntimeError(FString(
|
||||
std::format("Index {} out of range", numIndex)));
|
||||
if (numIndex >= list.size()) throw RuntimeError(String(std::format("Index {} out of range", numIndex)));
|
||||
list[numIndex] = v;
|
||||
}
|
||||
else if (kind == Kind::MapElement) // map
|
||||
@@ -111,25 +102,23 @@ namespace Fig
|
||||
}
|
||||
else if (kind == Kind::StringElement)
|
||||
{
|
||||
FString &string = value->as<ValueType::StringClass>();
|
||||
String &string = value->as<ValueType::StringClass>();
|
||||
if (numIndex >= string.length())
|
||||
throw RuntimeError(FString(
|
||||
std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
throw RuntimeError(
|
||||
String(std::format("Index {} out of range {}", numIndex, value->toString().toBasicString())));
|
||||
|
||||
if (v->getTypeInfo() != ValueType::String)
|
||||
throw RuntimeError(FString(
|
||||
std::format("Could not assign {} to sub string", v->toString().toBasicString())
|
||||
));
|
||||
const FString &strReplace = v->as<ValueType::StringClass>();
|
||||
throw RuntimeError(
|
||||
String(std::format("Could not assign {} to sub string", v->toString().toBasicString())));
|
||||
const String &strReplace = v->as<ValueType::StringClass>();
|
||||
if (strReplace.length() > 1)
|
||||
throw RuntimeError(FString(
|
||||
std::format("Could not assign {} to sub string, expects length 1", v->toString().toBasicString())
|
||||
));
|
||||
string.realReplace(numIndex, strReplace);
|
||||
throw RuntimeError(String(std::format("Could not assign {} to sub string, expects length 1",
|
||||
v->toString().toBasicString())));
|
||||
string.replace(numIndex, strReplace.length(), strReplace);
|
||||
}
|
||||
}
|
||||
|
||||
FString name() const { return resolve(slot)->name; }
|
||||
String name() const { return resolve(slot)->name; }
|
||||
TypeInfo declaredType() const { return resolve(slot)->declaredType; }
|
||||
AccessModifier access() const { return resolve(slot)->am; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
@@ -14,44 +14,32 @@ namespace Fig
|
||||
private:
|
||||
size_t id;
|
||||
|
||||
std::map<String, size_t> &getTypeMap()
|
||||
{
|
||||
static std::map<String, size_t> typeMap;
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
public:
|
||||
friend class TypeInfoHash;
|
||||
|
||||
FString name;
|
||||
String name;
|
||||
|
||||
FString toString() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
String toString() const { return name; }
|
||||
|
||||
static std::map<FString, size_t> typeMap;
|
||||
|
||||
static size_t getID(FString _name)
|
||||
{
|
||||
return typeMap.at(_name);
|
||||
}
|
||||
size_t getInstanceID() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
size_t getInstanceID() const { return id; }
|
||||
|
||||
TypeInfo();
|
||||
explicit TypeInfo(const FString &_name, bool reg = false);
|
||||
explicit TypeInfo(const String &_name, bool reg = false);
|
||||
TypeInfo(const TypeInfo &other) = default;
|
||||
|
||||
bool operator==(const TypeInfo &other) const
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
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);
|
||||
}
|
||||
std::size_t operator()(const TypeInfo &ti) const { return std::hash<size_t>{}(ti.id); }
|
||||
};
|
||||
|
||||
// class Value;
|
||||
@@ -75,11 +63,11 @@ namespace Fig
|
||||
using DoubleClass = double;
|
||||
using BoolClass = bool;
|
||||
using NullClass = std::monostate;
|
||||
using StringClass = FString;
|
||||
using StringClass = Fig::String;
|
||||
|
||||
static const std::unordered_set<TypeInfo, TypeInfoHash> builtinTypes
|
||||
inline bool isTypeBuiltin(const TypeInfo &type)
|
||||
{
|
||||
Any,
|
||||
static const std::unordered_set<TypeInfo, TypeInfoHash> builtinTypes{Any,
|
||||
Null,
|
||||
Int,
|
||||
String,
|
||||
@@ -91,25 +79,17 @@ namespace Fig
|
||||
List,
|
||||
Map,
|
||||
Module,
|
||||
InterfaceType
|
||||
};
|
||||
|
||||
inline bool isTypeBuiltin(const TypeInfo &type)
|
||||
{
|
||||
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());
|
||||
}
|
||||
size_t operator()(const Fig::TypeInfo &t) { return std::hash<size_t>{}(t.getInstanceID()); }
|
||||
};
|
||||
};
|
||||
}; // namespace std
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Evaluator/Value/value_forward.hpp>
|
||||
|
||||
#include <memory>
|
||||
namespace Fig
|
||||
@@ -12,7 +11,7 @@ namespace Fig
|
||||
|
||||
struct VariableSlot
|
||||
{
|
||||
FString name;
|
||||
String name;
|
||||
ObjectPtr value;
|
||||
TypeInfo declaredType;
|
||||
AccessModifier am;
|
||||
@@ -20,4 +19,4 @@ namespace Fig
|
||||
bool isRef = false;
|
||||
std::shared_ptr<VariableSlot> refTarget;
|
||||
};
|
||||
}
|
||||
} // namespace Fig
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Evaluator/Context/context_forward.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
@@ -11,51 +11,97 @@
|
||||
namespace Fig
|
||||
{
|
||||
class Object;
|
||||
|
||||
class Function
|
||||
{
|
||||
public:
|
||||
std::size_t id;
|
||||
String name;
|
||||
|
||||
enum FnType
|
||||
{
|
||||
Normal,
|
||||
Builtin,
|
||||
MemberType
|
||||
} type;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Ast::FunctionParameters paras;
|
||||
TypeInfo retType;
|
||||
Ast::BlockStatement body;
|
||||
|
||||
bool isBuiltin = false;
|
||||
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()) {}
|
||||
Function() : id(nextId()), type(Normal)
|
||||
{
|
||||
// 需要初始化 union !
|
||||
new (¶s) Ast::FunctionParameters();
|
||||
new (&retType) TypeInfo();
|
||||
new (&body) Ast::BlockStatement();
|
||||
}
|
||||
|
||||
Function(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body, ContextPtr _closureContext) :
|
||||
Function(const String &_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(std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> fn, int argc) :
|
||||
id(nextId()), isBuiltin(true), builtin(fn), builtinParamCount(argc) {}
|
||||
Function(const String &_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 String &_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) = default;
|
||||
Function(Function &&) noexcept = default;
|
||||
Function &operator=(const Function &) = default;
|
||||
Function &operator=(Function &&) noexcept = default;
|
||||
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);
|
||||
}
|
||||
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()
|
||||
@@ -63,5 +109,44 @@ namespace Fig
|
||||
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 (¶s) 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -1,27 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Evaluator/Context/context_forward.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Module
|
||||
{
|
||||
FString name;
|
||||
String name;
|
||||
ContextPtr ctx;
|
||||
|
||||
Module() = default;
|
||||
|
||||
Module(FString n, ContextPtr c) :
|
||||
name(std::move(n)),
|
||||
ctx(std::move(c))
|
||||
{
|
||||
}
|
||||
Module(String n, ContextPtr c) : name(std::move(n)), ctx(std::move(c)) {}
|
||||
|
||||
bool operator==(const Module &o) const noexcept
|
||||
{
|
||||
return name == o.name;
|
||||
}
|
||||
bool operator==(const Module &o) const noexcept { return name == o.name; }
|
||||
};
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Evaluator/Context/context_forward.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.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 <vector>
|
||||
|
||||
@@ -14,21 +14,17 @@ namespace Fig
|
||||
struct Field
|
||||
{
|
||||
AccessModifier am;
|
||||
FString name;
|
||||
String name;
|
||||
TypeInfo type;
|
||||
Ast::Expression defaultValue;
|
||||
|
||||
Field(AccessModifier _am, FString _name, TypeInfo _type, Ast::Expression _defaultValue) :
|
||||
am(_am), name(std::move(_name)), type(std::move(_type)), defaultValue(std::move(_defaultValue)) {}
|
||||
Field(AccessModifier _am, String _name, TypeInfo _type, Ast::Expression _defaultValue) :
|
||||
am(_am), name(std::move(_name)), type(std::move(_type)), defaultValue(std::move(_defaultValue))
|
||||
{
|
||||
}
|
||||
|
||||
bool isPublic() const
|
||||
{
|
||||
return am == AccessModifier::Public || am == AccessModifier::PublicConst;
|
||||
}
|
||||
bool isConst() const
|
||||
{
|
||||
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
||||
}
|
||||
bool isPublic() const { return am == AccessModifier::Public || am == AccessModifier::PublicConst; }
|
||||
bool isConst() const { return am == AccessModifier::Const || am == AccessModifier::PublicConst; }
|
||||
};
|
||||
|
||||
struct StructType
|
||||
@@ -41,7 +37,9 @@ namespace Fig
|
||||
|
||||
// ===== Constructors =====
|
||||
StructType(TypeInfo _type, ContextPtr _defContext, std::vector<Field> _fields, bool _builtin = false) :
|
||||
type(std::move(_type)), defContext(std::move(_defContext)), fields(std::move(_fields)), builtin(_builtin) {}
|
||||
type(std::move(_type)), defContext(std::move(_defContext)), fields(std::move(_fields)), builtin(_builtin)
|
||||
{
|
||||
}
|
||||
|
||||
StructType(const StructType &other) = default;
|
||||
StructType(StructType &&) noexcept = default;
|
||||
@@ -49,14 +47,8 @@ namespace Fig
|
||||
StructType &operator=(StructType &&) noexcept = default;
|
||||
|
||||
// ===== Comparison =====
|
||||
bool operator==(const StructType &other) const noexcept
|
||||
{
|
||||
return type == other.type;
|
||||
}
|
||||
bool operator!=(const StructType &other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
bool operator==(const StructType &other) const noexcept { return type == other.type; }
|
||||
bool operator!=(const StructType &other) const noexcept { return !(*this == other); }
|
||||
|
||||
private:
|
||||
static std::size_t nextId()
|
||||
@@ -72,9 +64,6 @@ namespace std
|
||||
template <>
|
||||
struct hash<Fig::Field>
|
||||
{
|
||||
size_t operator()(const Fig::Field &f)
|
||||
{
|
||||
return std::hash<Fig::FString>{}(f.name);
|
||||
}
|
||||
size_t operator()(const Fig::Field &f) { return std::hash<Fig::String>{}(f.name); }
|
||||
};
|
||||
}; // namespace std
|
||||
@@ -1,36 +1,37 @@
|
||||
#include "Value/structType.hpp"
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Context/context.hpp>
|
||||
#include <Evaluator/Value/interface.hpp>
|
||||
#include <Evaluator/Value/structType.hpp>
|
||||
#include <Evaluator/Value/value_forward.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Evaluator/Context/context.hpp>
|
||||
|
||||
// #include <iostream>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
std::map<FString, size_t> TypeInfo::typeMap = {};
|
||||
|
||||
TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement
|
||||
id(1), name(FString(u8"Any"))
|
||||
id(1), name(String(U"Any"))
|
||||
{
|
||||
}
|
||||
TypeInfo::TypeInfo(const FString &_name, bool reg)
|
||||
TypeInfo::TypeInfo(const String &_name, bool reg)
|
||||
{
|
||||
static size_t id_count = 0;
|
||||
name = _name;
|
||||
// std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n";
|
||||
if (reg)
|
||||
{
|
||||
typeMap[name] = ++id_count;
|
||||
getTypeMap()[name] = ++id_count;
|
||||
id = id_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!typeMap.contains(_name))
|
||||
if (!getTypeMap().contains(_name))
|
||||
{
|
||||
throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("No type named '{}'", _name.toBasicString())));
|
||||
// *this = ValueType::String;
|
||||
}
|
||||
id = typeMap.at(name); // may throw
|
||||
id = getTypeMap().at(name); // may throw
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,28 +85,37 @@ namespace Fig
|
||||
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)
|
||||
String prettyType(std::shared_ptr<const Object> obj)
|
||||
{
|
||||
return actualType(obj).toString();
|
||||
}
|
||||
|
||||
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
||||
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
|
||||
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
|
||||
const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4
|
||||
const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5
|
||||
const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6
|
||||
const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7
|
||||
const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8
|
||||
const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9
|
||||
const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10
|
||||
const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11
|
||||
const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12
|
||||
const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13
|
||||
const TypeInfo ValueType::Any(U"Any", true); // id: 1
|
||||
const TypeInfo ValueType::Null(U"Null", true); // id: 2
|
||||
const TypeInfo ValueType::Int(U"Int", true); // id: 3
|
||||
const TypeInfo ValueType::String(U"String", true); // id: 4
|
||||
const TypeInfo ValueType::Bool(U"Bool", true); // id: 5
|
||||
const TypeInfo ValueType::Double(U"Double", true); // id: 6
|
||||
const TypeInfo ValueType::Function(U"Function", true); // id: 7
|
||||
const TypeInfo ValueType::StructType(U"StructType", true); // id: 8
|
||||
const TypeInfo ValueType::StructInstance(U"StructInstance", true); // id: 9
|
||||
const TypeInfo ValueType::List(U"List", true); // id: 10
|
||||
const TypeInfo ValueType::Map(U"Map", true); // id: 11
|
||||
const TypeInfo ValueType::Module(U"Module", true); // id: 12
|
||||
const TypeInfo ValueType::InterfaceType(U"InterfaceType", true); // id: 13
|
||||
|
||||
namespace ValueType
|
||||
{
|
||||
|
||||
};
|
||||
bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx)
|
||||
{
|
||||
return ctx->hasImplRegisted(structType, interfaceType);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
#pragma once
|
||||
#include <Value/function.hpp>
|
||||
#include <Value/interface.hpp>
|
||||
#include <Value/structType.hpp>
|
||||
#include <Value/structInstance.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/valueError.hpp>
|
||||
#include <Value/module.hpp>
|
||||
#include <Value/value_forward.hpp>
|
||||
#include <Core/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>
|
||||
@@ -33,34 +37,31 @@ namespace Fig
|
||||
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);
|
||||
String prettyType(std::shared_ptr<const Object> obj);
|
||||
|
||||
bool operator==(const Object &, const Object &);
|
||||
|
||||
struct Element
|
||||
{
|
||||
ObjectPtr value;
|
||||
Element(ObjectPtr _value) :
|
||||
value(_value) {}
|
||||
Element(ObjectPtr _value) : value(_value) {}
|
||||
|
||||
bool operator==(const Element &other) const
|
||||
{
|
||||
return *value == *other.value;
|
||||
}
|
||||
bool operator==(const Element &other) const { return *value == *other.value; }
|
||||
|
||||
void deepCopy(const Element &e)
|
||||
{
|
||||
value = std::make_shared<Object>(*e.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) {}
|
||||
ValueKey(ObjectPtr _value) : value(_value) {}
|
||||
|
||||
void deepCopy(const ValueKey &vk) { value = std::make_shared<Object>(*vk.value); }
|
||||
};
|
||||
@@ -74,11 +75,12 @@ namespace Fig
|
||||
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,
|
||||
using VariantType = std::variant<ValueType::NullClass,
|
||||
ValueType::IntClass,
|
||||
ValueType::DoubleClass,
|
||||
ValueType::StringClass,
|
||||
@@ -91,214 +93,219 @@ namespace Fig
|
||||
Module,
|
||||
InterfaceType>;
|
||||
|
||||
std::unordered_map<TypeInfo,
|
||||
std::unordered_map<FString,
|
||||
std::function<ObjectPtr(std::vector<ObjectPtr>)>>,
|
||||
TypeInfoHash>
|
||||
static std::unordered_map<TypeInfo, std::unordered_map<String, BuiltinTypeMemberFn>, TypeInfoHash>
|
||||
getMemberTypeFunctions()
|
||||
{
|
||||
static const std::unordered_map<TypeInfo, std::unordered_map<String, BuiltinTypeMemberFn>, TypeInfoHash>
|
||||
memberTypeFunctions{
|
||||
{ValueType::Null, {}},
|
||||
{ValueType::Int, {}},
|
||||
{ValueType::Double, {}},
|
||||
{ValueType::String, {
|
||||
{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{ValueType::String,
|
||||
{
|
||||
{U"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 = as<ValueType::StringClass>();
|
||||
throw RuntimeError(
|
||||
String(std::format("`length` expects 0 arguments, {} got", args.size())));
|
||||
const String &str = object->as<ValueType::StringClass>();
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(str.length()));
|
||||
}},
|
||||
{u8"replace", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"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 = as<ValueType::StringClass>();
|
||||
throw RuntimeError(
|
||||
String(std::format("`replace` expects 2 arguments, {} got", args.size())));
|
||||
String &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"));
|
||||
throw RuntimeError(String("`replace` arg 1 expects type Int"));
|
||||
}
|
||||
if (!arg2->is<ValueType::StringClass>())
|
||||
{
|
||||
throw RuntimeError(FString(
|
||||
"`replace` arg 2 expects type String"));
|
||||
throw RuntimeError(String("`replace` arg 2 expects type String"));
|
||||
}
|
||||
str.realReplace(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::StringClass>());
|
||||
const String &target = arg2->as<ValueType::StringClass>();
|
||||
str.replace(arg1->as<ValueType::IntClass>(), target.length(), target);
|
||||
return Object::getNullInstance();
|
||||
}},
|
||||
{u8"erase", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"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 = as<ValueType::StringClass>();
|
||||
throw RuntimeError(
|
||||
String(std::format("`erase` expects 2 arguments, {} got", args.size())));
|
||||
String &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"));
|
||||
throw RuntimeError(String("`erase` arg 1 expects type Int"));
|
||||
}
|
||||
if (!arg2->is<ValueType::IntClass>())
|
||||
{
|
||||
throw RuntimeError(FString(
|
||||
"`erase` arg 2 expects type Int"));
|
||||
throw RuntimeError(String("`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"));
|
||||
throw RuntimeError(String("`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"));
|
||||
throw RuntimeError(String("`erase`: length is not long enough to erase"));
|
||||
}
|
||||
str.realErase(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::IntClass>());
|
||||
str.erase(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::IntClass>());
|
||||
return Object::getNullInstance();
|
||||
}},
|
||||
{u8"insert", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"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 = as<ValueType::StringClass>();
|
||||
throw RuntimeError(
|
||||
String(std::format("`insert` expects 2 arguments, {} got", args.size())));
|
||||
String &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"));
|
||||
throw RuntimeError(String("`insert` arg 1 expects type Int"));
|
||||
}
|
||||
if (!arg2->is<ValueType::StringClass>())
|
||||
{
|
||||
throw RuntimeError(FString(
|
||||
"`insert` arg 2 expects type String"));
|
||||
throw RuntimeError(String("`insert` arg 2 expects type String"));
|
||||
}
|
||||
str.realInsert(arg1->as<ValueType::IntClass>(), arg2->as<ValueType::StringClass>());
|
||||
ValueType::IntClass dest = arg1->as<ValueType::IntClass>();
|
||||
const String &src = arg2->as<ValueType::StringClass>();
|
||||
str.insert(dest, src);
|
||||
return Object::getNullInstance();
|
||||
}},
|
||||
}},
|
||||
{ValueType::Function, {}},
|
||||
{ValueType::StructType, {}},
|
||||
{ValueType::StructInstance, {}},
|
||||
{ValueType::List, {
|
||||
{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{ValueType::List,
|
||||
{
|
||||
{U"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 = as<List>();
|
||||
throw RuntimeError(
|
||||
String(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", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"get",
|
||||
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
if (args.size() != 1)
|
||||
throw RuntimeError(FString(
|
||||
std::format("`get` expects 1 arguments, {} got", args.size())));
|
||||
throw RuntimeError(
|
||||
String(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())));
|
||||
throw RuntimeError(
|
||||
String(std::format("`get` argument 1 expects Int, {} got",
|
||||
arg->getTypeInfo().toString().toBasicString())));
|
||||
ValueType::IntClass i = arg->as<ValueType::IntClass>();
|
||||
const List &list = as<List>();
|
||||
if (i >= list.size())
|
||||
return Object::getNullInstance();
|
||||
const List &list = object->as<List>();
|
||||
if (i >= list.size()) return Object::getNullInstance();
|
||||
return list[i].value;
|
||||
}},
|
||||
{u8"push", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"push",
|
||||
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
if (args.size() != 1)
|
||||
throw RuntimeError(FString(
|
||||
std::format("`push` expects 1 arguments, {} got", args.size())));
|
||||
throw RuntimeError(
|
||||
String(std::format("`push` expects 1 arguments, {} got", args.size())));
|
||||
ObjectPtr arg = args[0];
|
||||
List &list = as<List>();
|
||||
List &list = object->as<List>();
|
||||
list.push_back(arg);
|
||||
return Object::getNullInstance();
|
||||
}},
|
||||
}},
|
||||
{ValueType::Map, {
|
||||
{u8"get", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{ValueType::Map,
|
||||
{
|
||||
{U"get",
|
||||
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
if (args.size() != 1)
|
||||
throw RuntimeError(FString(
|
||||
std::format("`get` expects 1 arguments, {} got", args.size())));
|
||||
throw RuntimeError(
|
||||
String(std::format("`get` expects 1 arguments, {} got", args.size())));
|
||||
ObjectPtr index = args[0];
|
||||
const Map &map = as<Map>();
|
||||
if (!map.contains(index))
|
||||
return Object::getNullInstance();
|
||||
const Map &map = object->as<Map>();
|
||||
if (!map.contains(index)) return Object::getNullInstance();
|
||||
return map.at(index);
|
||||
}},
|
||||
{u8"contains", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
{U"contains",
|
||||
[](ObjectPtr object, std::vector<ObjectPtr> args) -> ObjectPtr {
|
||||
if (args.size() != 1)
|
||||
throw RuntimeError(FString(
|
||||
std::format("`contains` expects 1 arguments, {} got", args.size())));
|
||||
throw RuntimeError(
|
||||
String(std::format("`contains` expects 1 arguments, {} got", args.size())));
|
||||
ObjectPtr index = args[0];
|
||||
const Map &map = as<Map>();
|
||||
return std::make_shared<Object>(
|
||||
map.contains(index));
|
||||
const Map &map = object->as<Map>();
|
||||
return std::make_shared<Object>(map.contains(index));
|
||||
}},
|
||||
}},
|
||||
{ValueType::Module, {}},
|
||||
{ValueType::InterfaceType, {}},
|
||||
};
|
||||
std::unordered_map<TypeInfo, std::unordered_map<FString, int>, TypeInfoHash> memberTypeFunctionsParas{
|
||||
return memberTypeFunctions;
|
||||
}
|
||||
|
||||
static std::unordered_map<TypeInfo, std::unordered_map<String, int>, TypeInfoHash> getMemberTypeFunctionsParas()
|
||||
{
|
||||
static const std::unordered_map<TypeInfo, std::unordered_map<String, int>, TypeInfoHash>
|
||||
memberTypeFunctionsParas{
|
||||
{ValueType::Null, {}},
|
||||
{ValueType::Int, {}},
|
||||
{ValueType::Double, {}},
|
||||
{ValueType::String, {
|
||||
{u8"length", 0},
|
||||
{u8"replace", 2},
|
||||
{u8"erase", 2},
|
||||
{u8"insert", 2},
|
||||
{ValueType::String,
|
||||
{
|
||||
{U"length", 0},
|
||||
{U"replace", 2},
|
||||
{U"erase", 2},
|
||||
{U"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::List, {{U"length", 0}, {U"get", 1}, {U"push", 1}}},
|
||||
{ValueType::Map,
|
||||
{
|
||||
{U"get", 1},
|
||||
{U"contains", 1},
|
||||
}},
|
||||
{ValueType::Module, {}},
|
||||
{ValueType::InterfaceType, {}},
|
||||
};
|
||||
bool hasMemberFunction(const FString &name) const
|
||||
{
|
||||
return memberTypeFunctions.at(getTypeInfo()).contains(name);
|
||||
return memberTypeFunctionsParas;
|
||||
}
|
||||
std::function<ObjectPtr(std::vector<ObjectPtr>)> getMemberFunction(const FString &name) const
|
||||
|
||||
bool hasMemberFunction(const String &name) const
|
||||
{
|
||||
return memberTypeFunctions.at(getTypeInfo()).at(name);
|
||||
return getMemberTypeFunctions().at(getTypeInfo()).contains(name);
|
||||
}
|
||||
int getMemberFunctionParaCount(const FString &name) const
|
||||
BuiltinTypeMemberFn getMemberFunction(const String &name) const
|
||||
{
|
||||
return memberTypeFunctionsParas.at(getTypeInfo()).at(name);
|
||||
return getMemberTypeFunctions().at(getTypeInfo()).at(name);
|
||||
}
|
||||
int getMemberFunctionParaCount(const String &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() : 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;
|
||||
@@ -312,7 +319,7 @@ namespace Fig
|
||||
else if (ti == ValueType::Double)
|
||||
return Object(ValueType::DoubleClass(0.0));
|
||||
else if (ti == ValueType::String)
|
||||
return Object(ValueType::StringClass(u8""));
|
||||
return Object(ValueType::StringClass(U""));
|
||||
else if (ti == ValueType::Bool)
|
||||
return Object(ValueType::BoolClass(false));
|
||||
else if (ti == ValueType::List)
|
||||
@@ -359,7 +366,8 @@ namespace Fig
|
||||
|
||||
TypeInfo getTypeInfo() const
|
||||
{
|
||||
return std::visit([](auto &&val) -> TypeInfo {
|
||||
return std::visit(
|
||||
[](auto &&val) -> TypeInfo {
|
||||
using T = std::decay_t<decltype(val)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, ValueType::NullClass>)
|
||||
@@ -414,84 +422,91 @@ namespace Fig
|
||||
else if (is<ValueType::DoubleClass>())
|
||||
return as<ValueType::DoubleClass>();
|
||||
else
|
||||
throw RuntimeError(u8"getNumericValue: Not a numeric value");
|
||||
throw RuntimeError(U"getNumericValue: Not a numeric value");
|
||||
}
|
||||
|
||||
FString toStringIO() const
|
||||
String toStringIO() const
|
||||
{
|
||||
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
|
||||
return toString();
|
||||
}
|
||||
|
||||
FString toString() const
|
||||
String 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<ValueType::NullClass>()) return String(U"null");
|
||||
if (is<ValueType::IntClass>()) return String(std::to_string(as<ValueType::IntClass>()));
|
||||
if (is<ValueType::DoubleClass>()) return String(std::format("{}", as<ValueType::DoubleClass>()));
|
||||
if (is<ValueType::StringClass>()) return String(String(U"\"") + as<ValueType::StringClass>() + String(U"\""));
|
||||
if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? String(U"true") : String(U"false");
|
||||
if (is<Function>())
|
||||
return FString(std::format("<Function '{}' at {:p}>",
|
||||
as<Function>().id,
|
||||
static_cast<const void *>(&as<Function>())));
|
||||
return String(std::format(
|
||||
"<Function '{}' at {:p}>", as<Function>().id, static_cast<const void *>(&as<Function>())));
|
||||
if (is<StructType>())
|
||||
return FString(std::format("<StructType '{}' at {:p}>",
|
||||
return String(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}>",
|
||||
return String(std::format("<StructInstance '{}' at {:p}>",
|
||||
as<StructInstance>().parentType.toString().toBasicString(),
|
||||
static_cast<const void *>(&as<StructInstance>())));
|
||||
if (is<List>())
|
||||
{
|
||||
FString output(u8"[");
|
||||
if (visited.contains(this)) { return U"[...]"; }
|
||||
visited.insert(this);
|
||||
|
||||
String output(U"[");
|
||||
const List &list = as<List>();
|
||||
bool first_flag = true;
|
||||
for (auto &ele : list)
|
||||
{
|
||||
if (!first_flag)
|
||||
output += u8", ";
|
||||
output += ele.value->toString();
|
||||
if (!first_flag) output += U", ";
|
||||
output += ele.value->toString(visited);
|
||||
first_flag = false;
|
||||
}
|
||||
output += u8"]";
|
||||
output += U"]";
|
||||
return output;
|
||||
}
|
||||
if (is<Map>())
|
||||
{
|
||||
FString output(u8"{");
|
||||
if (visited.contains(this)) { return U"{...}"; }
|
||||
visited.insert(this);
|
||||
|
||||
String output(U"{");
|
||||
const Map &map = as<Map>();
|
||||
bool first_flag = true;
|
||||
for (auto &[key, value] : map)
|
||||
{
|
||||
if (!first_flag)
|
||||
output += u8", ";
|
||||
output += key.value->toString() + FString(u8" : ") + value->toString();
|
||||
if (!first_flag) output += U", ";
|
||||
output += key.value->toString(visited) + String(U" : ") + value->toString(visited);
|
||||
first_flag = false;
|
||||
}
|
||||
output += u8"}";
|
||||
output += U"}";
|
||||
return output;
|
||||
}
|
||||
if (is<Module>())
|
||||
{
|
||||
return FString(std::format(
|
||||
"<Module '{}' at {:p}>",
|
||||
return String(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}>",
|
||||
return String(std::format("<InterfaceType '{}' at {:p}>",
|
||||
as<InterfaceType>().type.toString().toBasicString(),
|
||||
static_cast<const void *>(&as<InterfaceType>())));
|
||||
}
|
||||
return FString(u8"<error>");
|
||||
return String(U"<error>");
|
||||
}
|
||||
|
||||
String 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)
|
||||
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();
|
||||
@@ -503,69 +518,62 @@ namespace Fig
|
||||
friend Object operator+(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
if (lhs.isNull() || rhs.isNull())
|
||||
throw ValueError(FString(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
|
||||
throw ValueError(String(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));
|
||||
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)));
|
||||
return Object(String(lhs.as<ValueType::StringClass>() + rhs.as<ValueType::StringClass>()));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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));
|
||||
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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));
|
||||
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;
|
||||
}
|
||||
String result;
|
||||
const String &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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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)));
|
||||
if (rnv == 0) throw ValueError(String(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
|
||||
// bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() / rnv;
|
||||
// if (bothInt)
|
||||
@@ -575,18 +583,18 @@ namespace Fig
|
||||
// DO NOT convert it to INT
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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)));
|
||||
if (rv == 0) throw ValueError(String(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
|
||||
|
||||
ValueType::IntClass q = lv / rv;
|
||||
ValueType::IntClass r = lv % rv;
|
||||
@@ -597,63 +605,69 @@ namespace Fig
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
auto rnv = rhs.getNumericValue();
|
||||
if (rnv == 0)
|
||||
throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
|
||||
if (rnv == 0) throw ValueError(String(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs)));
|
||||
auto result = std::fmod(lhs.getNumericValue(), rnv);
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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())));
|
||||
throw ValueError(
|
||||
String(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())));
|
||||
if (v.isNull()) throw ValueError(String(U"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(
|
||||
String(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())));
|
||||
throw ValueError(
|
||||
String(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) { return lhs.data == rhs.data; }
|
||||
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)));
|
||||
throw ValueError(String(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)
|
||||
@@ -661,7 +675,7 @@ namespace Fig
|
||||
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)));
|
||||
throw ValueError(String(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
|
||||
}
|
||||
friend bool operator>=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs > rhs; }
|
||||
|
||||
@@ -669,61 +683,67 @@ namespace Fig
|
||||
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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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())));
|
||||
throw ValueError(
|
||||
String(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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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)));
|
||||
throw ValueError(String(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));
|
||||
if (bothInt) return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "**", base, exp)));
|
||||
throw ValueError(String(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)
|
||||
|
||||
@@ -8,10 +8,13 @@ namespace Fig
|
||||
{
|
||||
public:
|
||||
using UnaddressableError::UnaddressableError;
|
||||
virtual FString toString() const override
|
||||
virtual String toString() const override
|
||||
{
|
||||
std::string msg = std::format("[ValueError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
std::string msg = std::format("[ValueError] {} in [{}] {}",
|
||||
std::string(this->message.begin(), this->message.end()),
|
||||
this->src_loc.file_name(),
|
||||
this->src_loc.function_name());
|
||||
return String(msg);
|
||||
}
|
||||
};
|
||||
};
|
||||
}; // namespace Fig
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +1,45 @@
|
||||
#include "Ast/Expressions/InitExpr.hpp"
|
||||
#pragma once
|
||||
#include "Ast/AccessModifier.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 <Value/Type.hpp>
|
||||
#include <Evaluator/Value/Type.hpp>
|
||||
#include <Ast/ast.hpp>
|
||||
|
||||
#include <Context/context.hpp>
|
||||
#include <Evaluator/Context/context.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Module/builtins.hpp>
|
||||
#include <Value/LvObject.hpp>
|
||||
#include <Evaluator/Value/LvObject.hpp>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
|
||||
#include <Evaluator/Core/StatementResult.hpp>
|
||||
#include <Evaluator/Core/ExprResult.hpp>
|
||||
#include <memory>
|
||||
#include <source_location>
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
ContextPtr global;
|
||||
|
||||
public:
|
||||
FString sourcePath;
|
||||
std::vector<FString> sourceLines;
|
||||
String sourcePath;
|
||||
std::vector<String> sourceLines;
|
||||
|
||||
void SetSourcePath(const FString &sp)
|
||||
{
|
||||
sourcePath = sp;
|
||||
}
|
||||
void SetSourcePath(const String &sp) { sourcePath = sp; }
|
||||
|
||||
void SetSourceLines(const std::vector<FString> &sl)
|
||||
{
|
||||
sourceLines = sl;
|
||||
}
|
||||
void SetSourceLines(const std::vector<String> &sl) { sourceLines = sl; }
|
||||
|
||||
void SetGlobalContext(ContextPtr ctx)
|
||||
{
|
||||
@@ -82,70 +47,109 @@ namespace Fig
|
||||
global = ctx;
|
||||
}
|
||||
|
||||
void CreateGlobalContext()
|
||||
{
|
||||
global = std::make_shared<Context>(
|
||||
FString(u8"<Global>"));
|
||||
}
|
||||
void CreateGlobalContext() { global = std::make_shared<Context>(String(U"\1")); }
|
||||
|
||||
void RegisterBuiltins() // only function
|
||||
{
|
||||
assert(global != nullptr);
|
||||
|
||||
for (auto &[name, fn] : Builtins::builtinFunctions)
|
||||
for (auto &[name, fn] : Builtins::getBuiltinFunctions())
|
||||
{
|
||||
int argc = Builtins::getBuiltinFunctionParamCount(name);
|
||||
Function f(fn, argc);
|
||||
global->def(
|
||||
name,
|
||||
ValueType::Function,
|
||||
AccessModifier::Const,
|
||||
std::make_shared<Object>(f));
|
||||
Function f(name, fn, argc);
|
||||
global->def(name, ValueType::Function, AccessModifier::Const, std::make_shared<Object>(f));
|
||||
}
|
||||
|
||||
global->setImplRecord(
|
||||
Builtins::getTypeErrorStructTypeInfo(),
|
||||
Builtins::getErrorInterfaceTypeInfo(),
|
||||
ImplRecord{
|
||||
.interfaceType = Builtins::getErrorInterfaceTypeInfo(),
|
||||
.structType = Builtins::getTypeErrorStructTypeInfo(),
|
||||
.implMethods = {
|
||||
{U"\1",
|
||||
Function(U"\1",
|
||||
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>(U"\1")),
|
||||
Ast::Operator::Add,
|
||||
std::make_shared<Ast::FunctionCallExpr>(
|
||||
std::make_shared<Ast::VarExprAst>(U"\1"), Ast::FunctionArguments{})))})),
|
||||
nullptr)},
|
||||
{U"\1",
|
||||
Function(U"\1",
|
||||
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>(String(U"\1"))))})),
|
||||
nullptr)},
|
||||
{U"\1",
|
||||
Function(U"\1",
|
||||
Ast::FunctionParameters{},
|
||||
ValueType::String,
|
||||
std::make_shared<Ast::BlockStatementAst>(std::vector<Ast::Statement>(
|
||||
{std::make_shared<Ast::ReturnSt>(std::make_shared<Ast::VarExprAst>(U"\1"))})),
|
||||
nullptr)},
|
||||
}});
|
||||
}
|
||||
|
||||
void RegisterBuiltinsValue()
|
||||
{
|
||||
assert(global != nullptr);
|
||||
|
||||
for (auto &[name, val] : Builtins::builtinValues)
|
||||
for (auto &[name, val] : Builtins::getBuiltinValues())
|
||||
{
|
||||
global->def(
|
||||
name,
|
||||
val->getTypeInfo(),
|
||||
AccessModifier::Const,
|
||||
val);
|
||||
global->def(name, val->getTypeInfo(), AccessModifier::Const, val);
|
||||
}
|
||||
}
|
||||
|
||||
bool isInterfaceSignatureMatch(const Ast::ImplementMethod &, const Ast::InterfaceMethod &);
|
||||
|
||||
/* Left-value eval*/
|
||||
LvObject evalVarExpr(Ast::VarExpr, ContextPtr);
|
||||
LvObject evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b
|
||||
LvObject evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b]
|
||||
ObjectPtr genTypeError(const String &_msg,
|
||||
const Ast::AstBase &_ast,
|
||||
ContextPtr ctx,
|
||||
std::source_location loc = std::source_location::current())
|
||||
{
|
||||
ContextPtr stCtx = std::make_shared<Context>(U"\1");
|
||||
stCtx->def(U"\1", 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*/
|
||||
RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call
|
||||
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
|
||||
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
|
||||
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
|
||||
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 evalFunctionCall(const Function &, const Ast::FunctionArguments &, const FString &, ContextPtr); // function call
|
||||
RvObject eval(Ast::Expression, ContextPtr);
|
||||
ExprResult executeFunction(const Function &fn, const Ast::FunctionCallArgs &, ContextPtr); // fn, fn context
|
||||
|
||||
ExprResult evalFunctionCall(const Ast::FunctionCall &,
|
||||
ContextPtr); // function call
|
||||
|
||||
ExprResult eval(Ast::Expression, ContextPtr);
|
||||
|
||||
StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block
|
||||
StatementResult evalStatement(Ast::Statement, ContextPtr); // statement
|
||||
|
||||
std::filesystem::path resolveModulePath(const std::vector<FString> &);
|
||||
std::filesystem::path resolveModulePath(const std::vector<String> &);
|
||||
ContextPtr loadModule(const std::filesystem::path &);
|
||||
|
||||
StatementResult evalImportSt(Ast::Import, ContextPtr);
|
||||
|
||||
StatementResult Run(std::vector<Ast::AstBase>); // Entry
|
||||
|
||||
void handle_error(const StatementResult &, const Ast::Statement &, const ContextPtr &);
|
||||
|
||||
void printStackTrace();
|
||||
};
|
||||
}; // namespace Fig
|
||||
@@ -8,41 +8,44 @@ namespace Fig
|
||||
class EvaluatorError final : public AddressableError
|
||||
{
|
||||
public:
|
||||
FString typeName;
|
||||
String typeName;
|
||||
using AddressableError::AddressableError;
|
||||
EvaluatorError(FString _typeName,
|
||||
FString msg,
|
||||
EvaluatorError(String _typeName,
|
||||
String msg,
|
||||
Ast::AstBase ast,
|
||||
std::source_location loc = std::source_location::current())
|
||||
{
|
||||
message = msg;
|
||||
line = ast->getAAI().line;
|
||||
column = ast->getAAI().column;
|
||||
|
||||
src_loc = std::move(loc);
|
||||
|
||||
typeName = std::move(_typeName);
|
||||
|
||||
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())
|
||||
if (ast != nullptr)
|
||||
{
|
||||
message = FString::fromBasicString(std::string(msg.data()));
|
||||
line = ast->getAAI().line;
|
||||
column = ast->getAAI().column;
|
||||
|
||||
src_loc = std::move(loc);
|
||||
|
||||
typeName = std::move(_typeName);
|
||||
|
||||
sourcePath = *ast->getAAI().sourcePath;
|
||||
sourceLines = *ast->getAAI().sourceLines;
|
||||
}
|
||||
}
|
||||
// EvaluatorError(String _typeName,
|
||||
// std::string_view msg,
|
||||
// Ast::AstBase ast,
|
||||
// std::source_location loc = std::source_location::current())
|
||||
// {
|
||||
// message = String(std::string(msg.data()));
|
||||
// line = ast->getAAI().line;
|
||||
// column = ast->getAAI().column;
|
||||
|
||||
virtual FString getErrorType() const override { return typeName; }
|
||||
// src_loc = std::move(loc);
|
||||
|
||||
// typeName = std::move(_typeName);
|
||||
|
||||
// sourcePath = *ast->getAAI().sourcePath;
|
||||
// sourceLines = *ast->getAAI().sourceLines;
|
||||
// }
|
||||
|
||||
virtual String getErrorType() const override { return typeName; }
|
||||
};
|
||||
}; // namespace Fig
|
||||
62
src/IR/IR.hpp
Normal file
62
src/IR/IR.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/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
|
||||
{
|
||||
String name;
|
||||
|
||||
uint16_t paramCount;
|
||||
uint16_t localCount; // 不含参数
|
||||
uint16_t regCount; // param + locals + temps
|
||||
|
||||
std::vector<Inst> code;
|
||||
};
|
||||
}; // namespace Fig::IR
|
||||
74
src/IR/IRInterpreter.hpp
Normal file
74
src/IR/IRInterpreter.hpp
Normal 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
14
src/IR/ir_test_main.cpp
Normal 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[] = {
|
||||
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Token/token.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Core/CharUtils.hpp>
|
||||
#include <Utils/utils.hpp>
|
||||
|
||||
#if 0
|
||||
@@ -17,112 +17,110 @@
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
const std::unordered_map<FString, TokenType> Lexer::symbol_map{
|
||||
const std::unordered_map<String, TokenType> Lexer::symbol_map{
|
||||
// 三字符
|
||||
{FString(u8"..."), TokenType::TripleDot},
|
||||
{String(U"..."), TokenType::TripleDot},
|
||||
// 双字符
|
||||
{FString(u8"=="), TokenType::Equal},
|
||||
{FString(u8"!="), TokenType::NotEqual},
|
||||
{FString(u8"<="), TokenType::LessEqual},
|
||||
{FString(u8">="), TokenType::GreaterEqual},
|
||||
{FString(u8"<<"), TokenType::ShiftLeft},
|
||||
{FString(u8">>"), TokenType::ShiftRight},
|
||||
{FString(u8"+="), TokenType::PlusEqual},
|
||||
{FString(u8"-="), TokenType::MinusEqual},
|
||||
{FString(u8"*="), TokenType::AsteriskEqual},
|
||||
{FString(u8"/="), TokenType::SlashEqual},
|
||||
{FString(u8"%="), TokenType::PercentEqual},
|
||||
{FString(u8"^="), TokenType::CaretEqual},
|
||||
{FString(u8"++"), TokenType::DoublePlus},
|
||||
{FString(u8"--"), TokenType::DoubleMinus},
|
||||
{FString(u8"&&"), TokenType::DoubleAmpersand},
|
||||
{FString(u8"||"), TokenType::DoublePipe},
|
||||
{FString(u8":="), TokenType::Walrus},
|
||||
{FString(u8"**"), TokenType::Power},
|
||||
{FString(u8"->"), TokenType::RightArrow},
|
||||
{FString(u8"=>"), TokenType::DoubleArrow},
|
||||
{String(U"=="), TokenType::Equal},
|
||||
{String(U"!="), TokenType::NotEqual},
|
||||
{String(U"<="), TokenType::LessEqual},
|
||||
{String(U">="), TokenType::GreaterEqual},
|
||||
{String(U"<<"), TokenType::ShiftLeft},
|
||||
{String(U">>"), TokenType::ShiftRight},
|
||||
{String(U"+="), TokenType::PlusEqual},
|
||||
{String(U"-="), TokenType::MinusEqual},
|
||||
{String(U"*="), TokenType::AsteriskEqual},
|
||||
{String(U"/="), TokenType::SlashEqual},
|
||||
{String(U"%="), TokenType::PercentEqual},
|
||||
{String(U"^="), TokenType::CaretEqual},
|
||||
{String(U"++"), TokenType::DoublePlus},
|
||||
{String(U"--"), TokenType::DoubleMinus},
|
||||
{String(U"&&"), TokenType::DoubleAmpersand},
|
||||
{String(U"||"), TokenType::DoublePipe},
|
||||
{String(U":="), TokenType::Walrus},
|
||||
{String(U"**"), TokenType::Power},
|
||||
{String(U"->"), TokenType::RightArrow},
|
||||
{String(U"=>"), TokenType::DoubleArrow},
|
||||
|
||||
// 单字符
|
||||
{FString(u8"+"), TokenType::Plus},
|
||||
{FString(u8"-"), TokenType::Minus},
|
||||
{FString(u8"*"), TokenType::Asterisk},
|
||||
{FString(u8"/"), TokenType::Slash},
|
||||
{FString(u8"%"), TokenType::Percent},
|
||||
{FString(u8"^"), TokenType::Caret},
|
||||
{FString(u8"&"), TokenType::Ampersand},
|
||||
{FString(u8"|"), TokenType::Pipe},
|
||||
{FString(u8"~"), TokenType::Tilde},
|
||||
{FString(u8"="), TokenType::Assign},
|
||||
{FString(u8"<"), TokenType::Less},
|
||||
{FString(u8">"), TokenType::Greater},
|
||||
{FString(u8"."), TokenType::Dot},
|
||||
{FString(u8","), TokenType::Comma},
|
||||
{FString(u8":"), TokenType::Colon},
|
||||
{FString(u8";"), TokenType::Semicolon},
|
||||
{FString(u8"'"), TokenType::SingleQuote},
|
||||
{FString(u8"\""), TokenType::DoubleQuote},
|
||||
{FString(u8"("), TokenType::LeftParen},
|
||||
{FString(u8")"), TokenType::RightParen},
|
||||
{FString(u8"["), TokenType::LeftBracket},
|
||||
{FString(u8"]"), TokenType::RightBracket},
|
||||
{FString(u8"{"), TokenType::LeftBrace},
|
||||
{FString(u8"}"), TokenType::RightBrace},
|
||||
{FString(u8"?"), TokenType::Question},
|
||||
{FString(u8"!"), TokenType::Not},
|
||||
{String(U"+"), TokenType::Plus},
|
||||
{String(U"-"), TokenType::Minus},
|
||||
{String(U"*"), TokenType::Asterisk},
|
||||
{String(U"/"), TokenType::Slash},
|
||||
{String(U"%"), TokenType::Percent},
|
||||
{String(U"^"), TokenType::Caret},
|
||||
{String(U"&"), TokenType::Ampersand},
|
||||
{String(U"|"), TokenType::Pipe},
|
||||
{String(U"~"), TokenType::Tilde},
|
||||
{String(U"="), TokenType::Assign},
|
||||
{String(U"<"), TokenType::Less},
|
||||
{String(U">"), TokenType::Greater},
|
||||
{String(U"."), TokenType::Dot},
|
||||
{String(U","), TokenType::Comma},
|
||||
{String(U":"), TokenType::Colon},
|
||||
{String(U";"), TokenType::Semicolon},
|
||||
{String(U"'"), TokenType::SingleQuote},
|
||||
{String(U"\""), TokenType::DoubleQuote},
|
||||
{String(U"("), TokenType::LeftParen},
|
||||
{String(U")"), TokenType::RightParen},
|
||||
{String(U"["), TokenType::LeftBracket},
|
||||
{String(U"]"), TokenType::RightBracket},
|
||||
{String(U"{"), TokenType::LeftBrace},
|
||||
{String(U"}"), TokenType::RightBrace},
|
||||
{String(U"?"), TokenType::Question},
|
||||
{String(U"!"), TokenType::Not},
|
||||
};
|
||||
|
||||
const std::unordered_map<FString, TokenType> Lexer::keyword_map{
|
||||
{FString(u8"and"), TokenType::And},
|
||||
{FString(u8"or"), TokenType::Or},
|
||||
{FString(u8"not"), TokenType::Not},
|
||||
{FString(u8"import"), TokenType::Import},
|
||||
{FString(u8"func"), TokenType::Function},
|
||||
{FString(u8"var"), TokenType::Variable},
|
||||
{FString(u8"const"), TokenType::Const},
|
||||
// {FString(u8"final"), TokenType::Final},
|
||||
{FString(u8"while"), TokenType::While},
|
||||
{FString(u8"for"), TokenType::For},
|
||||
{FString(u8"if"), TokenType::If},
|
||||
{FString(u8"else"), TokenType::Else},
|
||||
{FString(u8"new"), TokenType::New},
|
||||
{FString(u8"struct"), TokenType::Struct},
|
||||
{FString(u8"interface"), TokenType::Interface},
|
||||
{FString(u8"impl"), TokenType::Implement},
|
||||
{FString(u8"is"), TokenType::Is},
|
||||
{FString(u8"public"), TokenType::Public},
|
||||
{FString(u8"return"), TokenType::Return},
|
||||
{FString(u8"break"), TokenType::Break},
|
||||
{FString(u8"continue"), TokenType::Continue},
|
||||
{FString(u8"try"), TokenType::Try},
|
||||
{FString(u8"catch"), TokenType::Catch},
|
||||
{FString(u8"throw"), TokenType::Throw},
|
||||
{FString(u8"Finally"), TokenType::Finally},
|
||||
const std::unordered_map<String, TokenType> Lexer::keyword_map{
|
||||
{String(U"and"), TokenType::And},
|
||||
{String(U"or"), TokenType::Or},
|
||||
{String(U"not"), TokenType::Not},
|
||||
{String(U"import"), TokenType::Import},
|
||||
{String(U"func"), TokenType::Function},
|
||||
{String(U"var"), TokenType::Variable},
|
||||
{String(U"const"), TokenType::Const},
|
||||
// {String(U"final"), TokenType::Final},
|
||||
{String(U"while"), TokenType::While},
|
||||
{String(U"for"), TokenType::For},
|
||||
{String(U"if"), TokenType::If},
|
||||
{String(U"else"), TokenType::Else},
|
||||
{String(U"new"), TokenType::New},
|
||||
{String(U"struct"), TokenType::Struct},
|
||||
{String(U"interface"), TokenType::Interface},
|
||||
{String(U"impl"), TokenType::Implement},
|
||||
{String(U"is"), TokenType::Is},
|
||||
{String(U"public"), TokenType::Public},
|
||||
{String(U"return"), TokenType::Return},
|
||||
{String(U"break"), TokenType::Break},
|
||||
{String(U"continue"), TokenType::Continue},
|
||||
{String(U"try"), TokenType::Try},
|
||||
{String(U"catch"), TokenType::Catch},
|
||||
{String(U"throw"), TokenType::Throw},
|
||||
{String(U"Finally"), TokenType::Finally},
|
||||
{String(U"as"), TokenType::As},
|
||||
|
||||
// {FString(u8"Null"), TokenType::TypeNull},
|
||||
// {FString(u8"Int"), TokenType::TypeInt},
|
||||
// {FString(u8"String"), TokenType::TypeString},
|
||||
// {FString(u8"Bool"), TokenType::TypeBool},
|
||||
// {FString(u8"Double"), TokenType::TypeDouble},
|
||||
// {String(U"Null"), TokenType::TypeNull},
|
||||
// {String(U"Int"), TokenType::TypeInt},
|
||||
// {String(U"String"), TokenType::TypeString},
|
||||
// {String(U"Bool"), TokenType::TypeBool},
|
||||
// {String(U"Double"), TokenType::TypeDouble},
|
||||
};
|
||||
void Lexer::skipLine()
|
||||
{
|
||||
while (*it != U'\n' and hasNext())
|
||||
{
|
||||
next();
|
||||
}
|
||||
while (current() != U'\n' and hasNext()) { next(); }
|
||||
next(); // skip '\n'
|
||||
++line;
|
||||
}
|
||||
Token Lexer::scanIdentifier()
|
||||
{
|
||||
FString identifier;
|
||||
String identifier;
|
||||
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c.isAlnum() || c == U'_')
|
||||
char32_t c = current();
|
||||
if (CharUtils::isAlnum(c) || c == U'_')
|
||||
{
|
||||
identifier += c.getString();
|
||||
identifier += c;
|
||||
next();
|
||||
}
|
||||
else
|
||||
@@ -130,23 +128,27 @@ namespace Fig
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this->keyword_map.contains(identifier))
|
||||
{
|
||||
return Token(identifier, this->keyword_map.at(identifier));
|
||||
}
|
||||
else if (identifier == u8"true" || identifier == u8"false")
|
||||
{
|
||||
return Token(identifier, TokenType::LiteralBool);
|
||||
}
|
||||
else if (identifier == u8"null")
|
||||
if (this->keyword_map.contains(identifier)) { return Token(identifier, this->keyword_map.at(identifier)); }
|
||||
else if (identifier == U"\1" || identifier == U"\1") { return Token(identifier, TokenType::LiteralBool); }
|
||||
else if (identifier == U"\1")
|
||||
{
|
||||
// null instance
|
||||
return Token(identifier, TokenType::LiteralNull);
|
||||
}
|
||||
if (keyword_map.contains(Utils::toLower(identifier)))
|
||||
{
|
||||
pushWarning(1, identifier); // Identifier is too similar to a keyword or a primitive type
|
||||
}
|
||||
// const auto &toLower = [](const String &str) -> String
|
||||
// {
|
||||
// String res;
|
||||
// for (auto c : str)
|
||||
// {
|
||||
// res += CharUtils::toLower(c);
|
||||
// }
|
||||
// return res;
|
||||
// };
|
||||
|
||||
// if (keyword_map.contains(toLower(identifier)))
|
||||
// {
|
||||
// pushWarning(1, identifier); // Identifier is too similar to a keyword or a primitive type
|
||||
// }
|
||||
if (identifier.length() <= 1)
|
||||
{
|
||||
pushWarning(2, identifier); // The identifier is too abstract
|
||||
@@ -155,12 +157,12 @@ namespace Fig
|
||||
}
|
||||
Token Lexer::scanString()
|
||||
{
|
||||
FString str;
|
||||
String str;
|
||||
bool unterminated = true;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
size_t str_start_col = column - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
char32_t c = current();
|
||||
if (c == U'"' || c == U'\n')
|
||||
{
|
||||
next();
|
||||
@@ -169,74 +171,74 @@ namespace Fig
|
||||
}
|
||||
else if (c == U'\\') // c is '\'
|
||||
{
|
||||
if (it.isEnd())
|
||||
if (!hasNext())
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, it.column(), SourceInfo(this));
|
||||
error = SyntaxError(U"\1", this->line, column, SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
next();
|
||||
UTF8Char ec = *it;
|
||||
char32_t ec = current();
|
||||
if (ec == U'n')
|
||||
{
|
||||
next();
|
||||
str += u8"\n";
|
||||
str += U"\1";
|
||||
}
|
||||
else if (ec == U't')
|
||||
{
|
||||
next();
|
||||
str += u8"\t";
|
||||
str += U"\1";
|
||||
}
|
||||
else if (ec == U'v')
|
||||
{
|
||||
next();
|
||||
str += u8"\v";
|
||||
str += U"\1";
|
||||
}
|
||||
else if (ec == U'b')
|
||||
{
|
||||
next();
|
||||
str += u8"\b";
|
||||
str += U"\1";
|
||||
}
|
||||
else if (ec == U'"')
|
||||
{
|
||||
next();
|
||||
str += u8"\"";
|
||||
str += U"\1";
|
||||
}
|
||||
else if (ec == U'\'')
|
||||
{
|
||||
next();
|
||||
str += u8"'";
|
||||
str += U"\1";
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Unsupported escape character: {}",
|
||||
FString(ec.getString()).toBasicString())),
|
||||
error =
|
||||
SyntaxError(String(std::format("Unsupported escape character: {}", String(ec).toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
str += c;
|
||||
next();
|
||||
}
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
|
||||
error = SyntaxError(U"Unterminated FString", this->line, str_start_col, SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanRawString()
|
||||
{
|
||||
FString str;
|
||||
String str;
|
||||
bool unterminated = true;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
size_t str_start_col = column - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
char32_t c = current();
|
||||
if (c == U'"' || c == U'\n')
|
||||
{
|
||||
next();
|
||||
@@ -245,27 +247,27 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
str += c;
|
||||
next();
|
||||
}
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
|
||||
error = SyntaxError(U"Unterminated FString", this->line, str_start_col, SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanMultilineString()
|
||||
{
|
||||
FString str;
|
||||
String str;
|
||||
bool unterminated = true;
|
||||
|
||||
uint8_t end = 0;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
size_t str_start_col = column - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
char32_t c = current();
|
||||
if (c == U'"')
|
||||
{
|
||||
if (end == 3)
|
||||
@@ -280,99 +282,99 @@ namespace Fig
|
||||
}
|
||||
else if (c == U'\\') // c is '\'
|
||||
{
|
||||
if (it.isEnd())
|
||||
if (!hasNext())
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, it.column(), SourceInfo(this));
|
||||
error = SyntaxError(U"Unterminated FString", this->line, column, SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
next();
|
||||
UTF8Char ec = *it;
|
||||
char32_t ec = current();
|
||||
if (ec == U'n')
|
||||
{
|
||||
next();
|
||||
str += u8"\n";
|
||||
str += U"\n";
|
||||
}
|
||||
else if (ec == U't')
|
||||
{
|
||||
next();
|
||||
str += u8"\t";
|
||||
str += U"\t";
|
||||
}
|
||||
else if (ec == U'v')
|
||||
{
|
||||
next();
|
||||
str += u8"\v";
|
||||
str += U"\v";
|
||||
}
|
||||
else if (ec == U'b')
|
||||
{
|
||||
next();
|
||||
str += u8"\b";
|
||||
str += U"\b";
|
||||
}
|
||||
else if (ec == U'"')
|
||||
{
|
||||
next();
|
||||
str += u8"\"";
|
||||
str += U"\"";
|
||||
}
|
||||
else if (ec == U'\'')
|
||||
{
|
||||
next();
|
||||
str += u8"'";
|
||||
str += U"\'";
|
||||
}
|
||||
else if (ec == U'\\')
|
||||
{
|
||||
next();
|
||||
str += u8"\\";
|
||||
str += U"\\";
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Unsupported escape character: {}",
|
||||
FString(ec.getString()).toBasicString())),
|
||||
error = SyntaxError(
|
||||
String(std::format("Unsupported escape character: {}", String(ec).toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
str += c;
|
||||
}
|
||||
end = 0;
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col, SourceInfo(this));
|
||||
error = SyntaxError(U"\1", this->line, str_start_col, SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanNumber()
|
||||
{
|
||||
FString numStr;
|
||||
String numStr;
|
||||
bool hasPoint = false;
|
||||
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char ch = *it;
|
||||
char32_t ch = current();
|
||||
|
||||
if (ch.isDigit() || ch == U'e')
|
||||
if (CharUtils::isDigit(ch) || ch == U'e')
|
||||
{
|
||||
numStr += ch.getString();
|
||||
numStr += ch;
|
||||
next();
|
||||
}
|
||||
else if (ch == U'-' && !numStr.empty() && (numStr.ends_with(U'e') || numStr.ends_with(U'E')))
|
||||
{
|
||||
numStr += ch.getString();
|
||||
numStr += ch;
|
||||
next();
|
||||
}
|
||||
else if (ch == U'+' && !numStr.empty() && (numStr.ends_with(U'e') || numStr.ends_with(U'E')))
|
||||
{
|
||||
numStr += ch.getString();
|
||||
numStr += ch;
|
||||
next();
|
||||
}
|
||||
else if (ch == U'.' && !hasPoint)
|
||||
{
|
||||
hasPoint = true;
|
||||
numStr += ch.getString();
|
||||
numStr += ch;
|
||||
next();
|
||||
}
|
||||
else
|
||||
@@ -384,9 +386,9 @@ namespace Fig
|
||||
|
||||
if (numStr.ends_with(U'e'))
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
@@ -394,7 +396,7 @@ namespace Fig
|
||||
bool hasDigit = false;
|
||||
for (auto it = numStr.begin(); it != numStr.end(); ++it)
|
||||
{
|
||||
if (isdigit(*it))
|
||||
if (CharUtils::isDigit(*it))
|
||||
{
|
||||
hasDigit = true;
|
||||
break;
|
||||
@@ -403,55 +405,55 @@ namespace Fig
|
||||
|
||||
if (!hasDigit)
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
|
||||
size_t ePos = numStr.find(U'e');
|
||||
if (ePos != FString::npos)
|
||||
if (ePos != String::npos)
|
||||
{
|
||||
if (ePos == 0)
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
if (ePos + 1 >= numStr.length())
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
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]);
|
||||
char32_t c = numStr[i];
|
||||
if (c == U'+' || c == U'-')
|
||||
{
|
||||
if (i != ePos + 1)
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c.isDigit()) { hasDigitAfterE = true; }
|
||||
if (CharUtils::isDigit(c)) { hasDigitAfterE = true; }
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
@@ -459,9 +461,9 @@ namespace Fig
|
||||
|
||||
if (!hasDigitAfterE)
|
||||
{
|
||||
error = SyntaxError(FString(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
error = SyntaxError(String(std::format("Illegal number literal: {}", numStr.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
return IllegalTok;
|
||||
}
|
||||
@@ -471,25 +473,24 @@ namespace Fig
|
||||
}
|
||||
Token Lexer::scanSymbol()
|
||||
{
|
||||
FString sym;
|
||||
UTF8Char ch = *it;
|
||||
sym += ch.getString();
|
||||
String sym;
|
||||
char32_t ch = current();
|
||||
sym += ch;
|
||||
|
||||
auto startsWith = [&](const FString &prefix) -> bool {
|
||||
auto startsWith = [&](const String &prefix) -> bool {
|
||||
for (const auto &p : symbol_map)
|
||||
{
|
||||
const FString &op = p.first;
|
||||
if (op.starts_with(prefix))
|
||||
return true;
|
||||
const String &op = p.first;
|
||||
if (op.starts_with(prefix)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!startsWith(sym))
|
||||
{
|
||||
error = SyntaxError(FString(std::format("No such operator: {}", sym.toBasicString())),
|
||||
error = SyntaxError(String(std::format("No such operator: {}", sym.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
next();
|
||||
return IllegalTok;
|
||||
@@ -497,11 +498,10 @@ namespace Fig
|
||||
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char peek = it.peek();
|
||||
if (!peek.isPunct())
|
||||
break;
|
||||
char32_t peek_ch = peek();
|
||||
if (!CharUtils::isPunct(peek_ch)) break;
|
||||
|
||||
FString candidate = sym + FString(peek.getString());
|
||||
String candidate = sym + peek_ch;
|
||||
|
||||
if (startsWith(candidate))
|
||||
{
|
||||
@@ -516,9 +516,9 @@ namespace Fig
|
||||
|
||||
if (!symbol_map.contains(sym))
|
||||
{
|
||||
error = SyntaxError(FString(std::format("No such operator: {}", sym.toBasicString())),
|
||||
error = SyntaxError(String(std::format("No such operator: {}", sym.toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
next();
|
||||
return IllegalTok;
|
||||
@@ -532,37 +532,34 @@ namespace Fig
|
||||
{
|
||||
// entry: when iterator current char is '/' and peek is '/' or '*'
|
||||
// current char is '/'
|
||||
FString comment;
|
||||
String comment;
|
||||
|
||||
if (it.peek() == U'/') // single-line comment
|
||||
if (peek() == U'/') // single-line comment
|
||||
{
|
||||
next(); // skip first '/'
|
||||
next(); // skip second '/'
|
||||
|
||||
UTF8Char c = *it;
|
||||
char32_t c = current();
|
||||
while (c != U'\n' and hasNext())
|
||||
{
|
||||
comment += c.getString();
|
||||
comment += c;
|
||||
next();
|
||||
c = *it;
|
||||
c = current();
|
||||
}
|
||||
|
||||
if (hasNext() && c == U'\n')
|
||||
{
|
||||
next();
|
||||
}
|
||||
if (hasNext() && c == U'\n') { next(); }
|
||||
}
|
||||
else // multi-line comment
|
||||
{
|
||||
next(); // skip '/'
|
||||
next(); // skip '*'
|
||||
|
||||
UTF8Char c = *it;
|
||||
char32_t c = current();
|
||||
bool terminated = false;
|
||||
|
||||
while (hasNext())
|
||||
{
|
||||
if (c == U'*' and hasNext() and it.peek() == U'/')
|
||||
if (c == U'*' and hasNext() and peek() == U'/')
|
||||
{
|
||||
next(); // skip '*'
|
||||
next(); // skip '/'
|
||||
@@ -571,16 +568,15 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
comment += c.getString();
|
||||
comment += c;
|
||||
next();
|
||||
c = *it;
|
||||
c = current();
|
||||
}
|
||||
}
|
||||
|
||||
if (!terminated)
|
||||
{
|
||||
error =
|
||||
SyntaxError(FString(u8"Unterminated multiline comment"), this->line, it.column(), SourceInfo(this));
|
||||
error = SyntaxError(String(U"\1"), this->line, column, SourceInfo(this));
|
||||
next();
|
||||
return IllegalTok;
|
||||
}
|
||||
@@ -590,76 +586,61 @@ namespace Fig
|
||||
}
|
||||
Token Lexer::nextToken()
|
||||
{
|
||||
if (!hasNext())
|
||||
if (!hasNext()) { return EOFTok.setPos(getCurrentLine(), getCurrentColumn()); }
|
||||
char32_t ch = current();
|
||||
while (hasNext())
|
||||
{
|
||||
return EOFTok;
|
||||
ch = current();
|
||||
if (!CharUtils::isSpace(ch))
|
||||
{
|
||||
break;
|
||||
}
|
||||
UTF8Char ch = *it;
|
||||
while (ch.isSpace())
|
||||
{
|
||||
next();
|
||||
ch = *it;
|
||||
if (!hasNext())
|
||||
{
|
||||
return EOFTok.setPos(getCurrentLine(), getCurrentColumn());
|
||||
}
|
||||
}
|
||||
last_line = getCurrentLine();
|
||||
last_column = getCurrentColumn();
|
||||
if (ch == U'/')
|
||||
{
|
||||
UTF8Char c{u8""};
|
||||
char32_t c;
|
||||
if (!hasNext())
|
||||
{
|
||||
next();
|
||||
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||
return Token(U"\1", this->symbol_map.at(U"\1")).setPos(last_line, last_column);
|
||||
}
|
||||
c = it.peek();
|
||||
c = peek();
|
||||
if (c != U'/' and c != U'*')
|
||||
{
|
||||
next();
|
||||
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||
return Token(U"\1", this->symbol_map.at(U"\1")).setPos(last_line, last_column);
|
||||
}
|
||||
scanComments().setPos(last_line, last_column);
|
||||
return nextToken();
|
||||
// now we ignore comments to avoid some stupid bugs
|
||||
}
|
||||
if (ch == U'r' and hasNext() and it.peek() == U'"')
|
||||
if (ch == U'r' and hasNext() and peek() == U'"')
|
||||
{
|
||||
// r""
|
||||
// raw FString
|
||||
// raw String
|
||||
next();
|
||||
next();
|
||||
return scanRawString().setPos(last_line, last_column);
|
||||
}
|
||||
if (ch.isAlpha() || ch == U'_')
|
||||
{
|
||||
return scanIdentifier().setPos(last_line, last_column);
|
||||
}
|
||||
if (CharUtils::isAlpha(ch) || ch == U'_') { return scanIdentifier().setPos(last_line, last_column); }
|
||||
else if (ch == U'"')
|
||||
{
|
||||
next();
|
||||
return scanString().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch.isDigit())
|
||||
{
|
||||
return scanNumber().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch.isPunct())
|
||||
{
|
||||
return scanSymbol().setPos(last_line, last_column);
|
||||
}
|
||||
else if (CharUtils::isDigit(ch)) { return scanNumber().setPos(last_line, last_column); }
|
||||
else if (CharUtils::isPunct(ch)) { return scanSymbol().setPos(last_line, last_column); }
|
||||
else
|
||||
{
|
||||
error =
|
||||
SyntaxError(FString(std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())),
|
||||
SyntaxError(String(std::format("Cannot tokenize char: '{}'", String(ch).toBasicString())),
|
||||
this->line,
|
||||
it.column(),
|
||||
column,
|
||||
SourceInfo(this));
|
||||
if (hasNext())
|
||||
{
|
||||
next();
|
||||
}
|
||||
if (hasNext()) { next(); }
|
||||
return IllegalTok.setPos(last_line, last_column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <Token/token.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/utf8_iterator.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Core/warning.hpp>
|
||||
|
||||
namespace Fig
|
||||
@@ -19,26 +18,33 @@ namespace Fig
|
||||
{
|
||||
private:
|
||||
size_t line;
|
||||
const FString source;
|
||||
const String source;
|
||||
SyntaxError error;
|
||||
UTF8Iterator it;
|
||||
|
||||
FString sourcePath;
|
||||
std::vector<FString> sourceLines;
|
||||
|
||||
String sourcePath;
|
||||
std::vector<String> sourceLines;
|
||||
std::vector<Warning> warnings;
|
||||
|
||||
int64_t idx = 0;
|
||||
|
||||
size_t last_line, last_column, column = 1;
|
||||
|
||||
bool hasNext()
|
||||
char32_t current() const { return source[idx]; }
|
||||
|
||||
char32_t peek() const {
|
||||
if (idx + 1 >= source.length())
|
||||
{
|
||||
return !this->it.isEnd();
|
||||
return U'\0';
|
||||
}
|
||||
return source[idx + 1];
|
||||
}
|
||||
|
||||
bool hasNext() { return idx < source.length(); }
|
||||
|
||||
void skipLine();
|
||||
inline void next()
|
||||
{
|
||||
if (*it == U'\n')
|
||||
if (current() == U'\n')
|
||||
{
|
||||
++this->line;
|
||||
this->column = 1;
|
||||
@@ -47,43 +53,31 @@ namespace Fig
|
||||
{
|
||||
++this->column;
|
||||
}
|
||||
++it;
|
||||
++idx;
|
||||
}
|
||||
|
||||
void pushWarning(size_t id, FString msg)
|
||||
void pushWarning(size_t id, String msg)
|
||||
{
|
||||
warnings.push_back(Warning(id, std::move(msg), getCurrentLine(), getCurrentColumn()));
|
||||
}
|
||||
void pushWarning(size_t id, FString msg, size_t line, size_t column)
|
||||
void pushWarning(size_t id, String msg, size_t line, size_t column)
|
||||
{
|
||||
warnings.push_back(Warning(id, std::move(msg), line, column));
|
||||
}
|
||||
|
||||
public:
|
||||
static const std::unordered_map<FString, TokenType> symbol_map;
|
||||
static const std::unordered_map<FString, TokenType> keyword_map;
|
||||
static const std::unordered_map<String, TokenType> symbol_map;
|
||||
static const std::unordered_map<String, TokenType> keyword_map;
|
||||
|
||||
inline Lexer(const FString &_source, const FString &_sourcePath, const std::vector<FString> &_sourceLines) :
|
||||
source(_source), it(source), sourcePath(_sourcePath), sourceLines(_sourceLines)
|
||||
inline Lexer(const String &_source, const String &_sourcePath, const std::vector<String> &_sourceLines) :
|
||||
source(_source), sourcePath(_sourcePath), sourceLines(_sourceLines)
|
||||
{
|
||||
line = 1;
|
||||
}
|
||||
inline size_t getCurrentLine()
|
||||
{
|
||||
return line;
|
||||
}
|
||||
inline size_t getCurrentColumn()
|
||||
{
|
||||
return column;
|
||||
}
|
||||
SyntaxError getError() const
|
||||
{
|
||||
return error;
|
||||
}
|
||||
std::vector<Warning> getWarnings() const
|
||||
{
|
||||
return warnings;
|
||||
}
|
||||
inline size_t getCurrentLine() { return line; }
|
||||
inline size_t getCurrentColumn() { return column; }
|
||||
SyntaxError getError() const { return error; }
|
||||
std::vector<Warning> getWarnings() const { return warnings; }
|
||||
Token nextToken();
|
||||
|
||||
Token scanNumber();
|
||||
|
||||
@@ -31,6 +31,65 @@ public interface Error
|
||||
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 Int {}
|
||||
public struct Null {}
|
||||
|
||||
@@ -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,7 +116,7 @@ public func format(objects ...) -> Any
|
||||
|
||||
public func formatByListArgs(objects) -> Any
|
||||
{
|
||||
if value.type(objects) != "List"
|
||||
if not (objects is List)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ if objects.length() < 1
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
Library/std/std.fig
|
||||
*/
|
||||
|
||||
import io; // link std.io
|
||||
import value; // link std.type
|
||||
import math; // link std.math
|
||||
import io as std_io;
|
||||
import value as std_value;
|
||||
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
|
||||
86
src/Module/Library/std/test/test.fig
Normal file
86
src/Module/Library/std/test/test.fig
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,17 @@ public struct Time
|
||||
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
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import _builtins;
|
||||
|
||||
public func type(object: Any) -> String
|
||||
public func _type(object: Any) -> String
|
||||
{
|
||||
return __fvalue_type(object);
|
||||
}
|
||||
|
||||
427
src/Module/builtins.cpp
Normal file
427
src/Module/builtins.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include <Ast/Expressions/BinaryExpr.hpp>
|
||||
#include <Ast/Expressions/FunctionCall.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 <Evaluator/Context/context.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Evaluator/Value/structType.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Module/builtins.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <print>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Fig::Builtins
|
||||
{
|
||||
const TypeInfo &getErrorInterfaceTypeInfo()
|
||||
{
|
||||
static const TypeInfo ErrorInterfaceTypeInfo(U"Error", true);
|
||||
return ErrorInterfaceTypeInfo;
|
||||
}
|
||||
const TypeInfo &getTypeErrorStructTypeInfo()
|
||||
{
|
||||
static const TypeInfo TypeErrorStructTypeInfo(U"TypeError", true);
|
||||
return TypeErrorStructTypeInfo;
|
||||
}
|
||||
const TypeInfo &getOperationInterfaceTypeInfo()
|
||||
{
|
||||
static const TypeInfo OperationInterfaceTypeInfo(U"Operation", true);
|
||||
return OperationInterfaceTypeInfo;
|
||||
}
|
||||
const std::unordered_map<String, ObjectPtr> &getBuiltinValues()
|
||||
{
|
||||
static const std::unordered_map<String, ObjectPtr> builtinValues = {
|
||||
{U"null", Object::getNullInstance()},
|
||||
{U"true", Object::getTrueInstance()},
|
||||
{U"false", Object::getFalseInstance()},
|
||||
{U"Error",
|
||||
std::make_shared<Object>(InterfaceType(getErrorInterfaceTypeInfo(),
|
||||
{Ast::InterfaceMethod(U"toString",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(U"String"),
|
||||
nullptr),
|
||||
Ast::InterfaceMethod(U"getErrorClass",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(U"String"),
|
||||
nullptr),
|
||||
Ast::InterfaceMethod(U"getErrorMessage",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(U"String"),
|
||||
nullptr)}))},
|
||||
{U"TypeError", std::make_shared<Object>(StructType(
|
||||
getTypeErrorStructTypeInfo(),
|
||||
std::make_shared<Context>(U"<Built-in `TypeError`>"),
|
||||
{Field(AccessModifier::Public, U"msg", ValueType::String, nullptr)}
|
||||
))},
|
||||
{U"Operation", std::make_shared<Object>(InterfaceType(getOperationInterfaceTypeInfo(), {}))},
|
||||
|
||||
{U"Any", std::make_shared<Object>(StructType(ValueType::Any, nullptr, {}, true))},
|
||||
{U"Int", std::make_shared<Object>(StructType(ValueType::Int, nullptr, {}, true))},
|
||||
{U"Null", std::make_shared<Object>(StructType(ValueType::Null, nullptr, {}, true))},
|
||||
{U"String", std::make_shared<Object>(StructType(ValueType::String, nullptr, {}, true))},
|
||||
{U"Bool", std::make_shared<Object>(StructType(ValueType::Bool, nullptr, {}, true))},
|
||||
{U"Double", std::make_shared<Object>(StructType(ValueType::Double, nullptr, {}, true))},
|
||||
{U"Function", std::make_shared<Object>(StructType(ValueType::Function, nullptr, {}, true))},
|
||||
{U"List", std::make_shared<Object>(StructType(ValueType::List, nullptr, {}, true))},
|
||||
{U"Map", std::make_shared<Object>(StructType(ValueType::Map, nullptr, {}, true))},
|
||||
// Type `StructType` `StructInstance` `Module` `InterfaceType`
|
||||
// Not allowed to call constructor!
|
||||
|
||||
{U"type", std::make_shared<Object>(Function(
|
||||
U"type",
|
||||
[](const std::vector<ObjectPtr> &_args) -> ObjectPtr
|
||||
{
|
||||
const ObjectPtr &arg = _args[0];
|
||||
return std::make_shared<Object>(prettyType(arg));
|
||||
},
|
||||
1
|
||||
))},
|
||||
};
|
||||
return builtinValues;
|
||||
}
|
||||
const std::unordered_map<String, int> &getBuiltinFunctionArgCounts()
|
||||
{
|
||||
static const std::unordered_map<String, int> builtinFunctionArgCounts = {
|
||||
{U"__fstdout_print", -1}, // variadic
|
||||
{U"__fstdout_println", -1}, // variadic
|
||||
{U"__fstdin_read", 0},
|
||||
{U"__fstdin_readln", 0},
|
||||
{U"__fvalue_type", 1},
|
||||
{U"__fvalue_int_parse", 1},
|
||||
{U"__fvalue_int_from", 1},
|
||||
{U"__fvalue_double_parse", 1},
|
||||
{U"__fvalue_double_from", 1},
|
||||
{U"__fvalue_string_from", 1},
|
||||
{U"__ftime_now_ns", 0},
|
||||
/* math start */
|
||||
{U"__fmath_acos", 1},
|
||||
{U"__fmath_acosh", 1},
|
||||
{U"__fmath_asin", 1},
|
||||
{U"__fmath_asinh", 1},
|
||||
{U"__fmath_atan", 1},
|
||||
{U"__fmath_atan2", 2},
|
||||
{U"__fmath_atanh", 1},
|
||||
{U"__fmath_ceil", 1},
|
||||
{U"__fmath_cos", 1},
|
||||
{U"__fmath_cosh", 1},
|
||||
{U"__fmath_exp", 1},
|
||||
{U"__fmath_expm1", 1},
|
||||
{U"__fmath_fabs", 1},
|
||||
{U"__fmath_floor", 1},
|
||||
{U"__fmath_fmod", 2},
|
||||
{U"__fmath_frexp", 1},
|
||||
{U"__fmath_gcd", 2},
|
||||
{U"__fmath_hypot", 2},
|
||||
{U"__fmath_isequal", 2},
|
||||
{U"__fmath_log", 1},
|
||||
{U"__fmath_log10", 1},
|
||||
{U"__fmath_log1p", 1},
|
||||
{U"__fmath_log2", 1},
|
||||
{U"__fmath_sin", 1},
|
||||
{U"__fmath_sinh", 1},
|
||||
{U"__fmath_sqrt", 1},
|
||||
{U"__fmath_tan", 1},
|
||||
{U"__fmath_tanh", 1},
|
||||
{U"__fmath_trunc", 1},
|
||||
};
|
||||
return builtinFunctionArgCounts;
|
||||
}
|
||||
const std::unordered_map<String, BuiltinFunction> &getBuiltinFunctions()
|
||||
{
|
||||
static const std::unordered_map<String, BuiltinFunction> builtinFunctions{
|
||||
{U"__fstdout_print",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); }
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{U"__fstdout_println",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); }
|
||||
std::print("\n");
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{U"__fstdin_read",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
return std::make_shared<Object>(String(input));
|
||||
}},
|
||||
{U"__fstdin_readln",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
return std::make_shared<Object>(String(line));
|
||||
}},
|
||||
{U"__fvalue_type",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
return std::make_shared<Object>(args[0]->getTypeInfo().toString());
|
||||
}},
|
||||
{U"__fvalue_int_parse",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
String str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
{
|
||||
ValueType::IntClass val = std::stoi(str.toBasicString());
|
||||
return std::make_shared<Object>(val);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(String(std::format("Invalid int string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{U"__fvalue_int_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::DoubleClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::IntClass>(val->as<ValueType::DoubleClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::IntClass>(val->as<ValueType::BoolClass>() ? 1 : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(String(std::format("Type '{}' cannot be converted to int",
|
||||
val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{U"__fvalue_double_parse",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
String str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
{
|
||||
ValueType::DoubleClass val = std::stod(str.toBasicString());
|
||||
return std::make_shared<Object>(ValueType::DoubleClass(val));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(String(std::format("Invalid double string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{U"__fvalue_double_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::IntClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::DoubleClass>(val->as<ValueType::IntClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
ValueType::DoubleClass(val->as<ValueType::BoolClass>() ? 1.0 : 0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(String(std::format("Type '{}' cannot be converted to double",
|
||||
val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{U"__fvalue_string_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
return std::make_shared<Object>(val->toStringIO());
|
||||
}},
|
||||
{U"__ftime_now_ns",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
// returns nanoseconds
|
||||
using namespace Fig::Time;
|
||||
auto now = Clock::now();
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time).count()));
|
||||
}},
|
||||
|
||||
/* math start */
|
||||
{U"__fmath_acos",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(acos(d));
|
||||
}},
|
||||
{U"__fmath_acosh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(acosh(d));
|
||||
}},
|
||||
{U"__fmath_asin",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(asin(d));
|
||||
}},
|
||||
{U"__fmath_asinh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(asinh(d));
|
||||
}},
|
||||
{U"__fmath_atan",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(atan(d));
|
||||
}},
|
||||
{U"__fmath_atan2",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass y = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass x = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(atan2(y, x));
|
||||
}},
|
||||
{U"__fmath_atanh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(atanh(d));
|
||||
}},
|
||||
{U"__fmath_ceil",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(ceil(d));
|
||||
}},
|
||||
{U"__fmath_cos",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(cos(d));
|
||||
}},
|
||||
{U"__fmath_cosh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(cosh(d));
|
||||
}},
|
||||
{U"__fmath_exp",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(exp(d));
|
||||
}},
|
||||
{U"__fmath_expm1",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(expm1(d));
|
||||
}},
|
||||
{U"__fmath_fabs",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(fabs(d));
|
||||
}},
|
||||
{U"__fmath_floor",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(floor(d));
|
||||
}},
|
||||
{U"__fmath_fmod",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(fmod(x, y));
|
||||
}},
|
||||
{U"__fmath_frexp",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
int e;
|
||||
return std::make_shared<Object>(List({std::make_shared<Object>(frexp(d, &e)),
|
||||
std::make_shared<Object>(static_cast<ValueType::IntClass>(e))}));
|
||||
}},
|
||||
{U"__fmath_gcd",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::IntClass x = args[0]->as<ValueType::IntClass>();
|
||||
ValueType::IntClass y = args[1]->as<ValueType::IntClass>();
|
||||
return std::make_shared<Object>(std::gcd(x, y));
|
||||
}},
|
||||
{U"__fmath_hypot",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(hypot(x, y));
|
||||
}},
|
||||
{U"__fmath_isequal",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
static const double epsilon = 1e-9;
|
||||
return std::make_shared<Object>(fabs(x - y) < epsilon);
|
||||
}},
|
||||
{U"__fmath_log",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log(d));
|
||||
}},
|
||||
{U"__fmath_log10",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log10(d));
|
||||
}},
|
||||
{U"__fmath_log1p",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log1p(d));
|
||||
}},
|
||||
{U"__fmath_log2",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log2(d));
|
||||
}},
|
||||
{U"__fmath_sin",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sin(d));
|
||||
}},
|
||||
{U"__fmath_sinh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sinh(d));
|
||||
}},
|
||||
{U"__fmath_sqrt",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sqrt(d));
|
||||
}},
|
||||
{U"__fmath_tan",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(tan(d));
|
||||
}},
|
||||
{U"__fmath_tanh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(tanh(d));
|
||||
}},
|
||||
{U"__fmath_trunc",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(trunc(d));
|
||||
}},
|
||||
};
|
||||
return builtinFunctions;
|
||||
}
|
||||
} // namespace Fig::Builtins
|
||||
@@ -1,28 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ast/Expressions/VarExpr.hpp"
|
||||
#include "Ast/Statements/VarDef.hpp"
|
||||
#include "Ast/astBase.hpp"
|
||||
#include <Ast/Statements/InterfaceDefSt.hpp>
|
||||
#include <Ast/Expressions/VarExpr.hpp>
|
||||
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Evaluator/Value/value.hpp>
|
||||
#include <Core/runtimeTime.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <print>
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
namespace Builtins
|
||||
{
|
||||
const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true);
|
||||
|
||||
/*
|
||||
// error's interface like:
|
||||
interface Error
|
||||
@@ -33,391 +26,47 @@ namespace Fig
|
||||
}
|
||||
*/
|
||||
|
||||
const std::unordered_map<FString, ObjectPtr> builtinValues = {
|
||||
{u8"null", Object::getNullInstance()},
|
||||
{u8"true", Object::getTrueInstance()},
|
||||
{u8"false", Object::getFalseInstance()},
|
||||
{u8"Error",
|
||||
std::make_shared<Object>(InterfaceType(ErrorInterfaceTypeInfo,
|
||||
{Ast::InterfaceMethod(u8"toString",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(u8"String"),
|
||||
nullptr),
|
||||
Ast::InterfaceMethod(u8"getErrorClass",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(u8"String"),
|
||||
nullptr),
|
||||
Ast::InterfaceMethod(u8"getErrorMessage",
|
||||
Ast::FunctionParameters({}, {}),
|
||||
std::make_shared<Ast::VarExprAst>(u8"String"),
|
||||
nullptr)}))},
|
||||
const TypeInfo &getErrorInterfaceTypeInfo();
|
||||
|
||||
{u8"Any", std::make_shared<Object>(StructType(ValueType::Any, nullptr, {}, true))},
|
||||
{u8"Int", std::make_shared<Object>(StructType(ValueType::Int, nullptr, {}, true))},
|
||||
{u8"Null", std::make_shared<Object>(StructType(ValueType::Null, nullptr, {}, true))},
|
||||
{u8"String", std::make_shared<Object>(StructType(ValueType::String, nullptr, {}, true))},
|
||||
{u8"Bool", std::make_shared<Object>(StructType(ValueType::Bool, nullptr, {}, true))},
|
||||
{u8"Double", std::make_shared<Object>(StructType(ValueType::Double, nullptr, {}, true))},
|
||||
{u8"Function", std::make_shared<Object>(StructType(ValueType::Function, nullptr, {}, true))},
|
||||
{u8"List", std::make_shared<Object>(StructType(ValueType::List, nullptr, {}, true))},
|
||||
{u8"Map", std::make_shared<Object>(StructType(ValueType::Map, nullptr, {}, true))},
|
||||
// Type `StructType` `StructInstance` `Module` `InterfaceType`
|
||||
// Not allowed to call constructor!
|
||||
};
|
||||
const TypeInfo &getTypeErrorStructTypeInfo();
|
||||
/*
|
||||
interface Operation
|
||||
{
|
||||
add(..., ...) -> ...;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
const TypeInfo &getOperationInterfaceTypeInfo();
|
||||
|
||||
const std::unordered_map<String, ObjectPtr> &getBuiltinValues();
|
||||
|
||||
using BuiltinFunction = std::function<ObjectPtr(const std::vector<ObjectPtr> &)>;
|
||||
|
||||
const std::unordered_map<FString, int> builtinFunctionArgCounts = {
|
||||
{u8"__fstdout_print", -1}, // variadic
|
||||
{u8"__fstdout_println", -1}, // variadic
|
||||
{u8"__fstdin_read", 0},
|
||||
{u8"__fstdin_readln", 0},
|
||||
{u8"__fvalue_type", 1},
|
||||
{u8"__fvalue_int_parse", 1},
|
||||
{u8"__fvalue_int_from", 1},
|
||||
{u8"__fvalue_double_parse", 1},
|
||||
{u8"__fvalue_double_from", 1},
|
||||
{u8"__fvalue_string_from", 1},
|
||||
{u8"__ftime_now_ns", 0},
|
||||
/* math start */
|
||||
{u8"__fmath_acos", 1},
|
||||
{u8"__fmath_acosh", 1},
|
||||
{u8"__fmath_asin", 1},
|
||||
{u8"__fmath_asinh", 1},
|
||||
{u8"__fmath_atan", 1},
|
||||
{u8"__fmath_atan2", 2},
|
||||
{u8"__fmath_atanh", 1},
|
||||
{u8"__fmath_ceil", 1},
|
||||
{u8"__fmath_cos", 1},
|
||||
{u8"__fmath_cosh", 1},
|
||||
{u8"__fmath_exp", 1},
|
||||
{u8"__fmath_expm1", 1},
|
||||
{u8"__fmath_fabs", 1},
|
||||
{u8"__fmath_floor", 1},
|
||||
{u8"__fmath_fmod", 2},
|
||||
{u8"__fmath_frexp", 1},
|
||||
{u8"__fmath_gcd", 2},
|
||||
{u8"__fmath_hypot", 2},
|
||||
{u8"__fmath_isequal", 2},
|
||||
{u8"__fmath_log", 1},
|
||||
{u8"__fmath_log10", 1},
|
||||
{u8"__fmath_log1p", 1},
|
||||
{u8"__fmath_log2", 1},
|
||||
{u8"__fmath_sin", 1},
|
||||
{u8"__fmath_sinh", 1},
|
||||
{u8"__fmath_sqrt", 1},
|
||||
{u8"__fmath_tan", 1},
|
||||
{u8"__fmath_tanh", 1},
|
||||
{u8"__fmath_trunc", 1},
|
||||
};
|
||||
const std::unordered_map<String, int> &getBuiltinFunctionArgCounts();
|
||||
const std::unordered_map<String, BuiltinFunction> &getBuiltinFunctions();
|
||||
|
||||
const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
|
||||
{u8"__fstdout_print",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); }
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{u8"__fstdout_println",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); }
|
||||
std::print("\n");
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{u8"__fstdin_read",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
return std::make_shared<Object>(FString::fromBasicString(input));
|
||||
}},
|
||||
{u8"__fstdin_readln",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
return std::make_shared<Object>(FString::fromBasicString(line));
|
||||
}},
|
||||
{u8"__fvalue_type",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
return std::make_shared<Object>(args[0]->getTypeInfo().toString());
|
||||
}},
|
||||
{u8"__fvalue_int_parse",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
FString str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
inline bool isBuiltinFunction(const String &name)
|
||||
{
|
||||
ValueType::IntClass val = std::stoi(str.toBasicString());
|
||||
return std::make_shared<Object>(val);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Invalid int string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_int_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::DoubleClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::IntClass>(val->as<ValueType::DoubleClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::IntClass>(val->as<ValueType::BoolClass>() ? 1 : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Type '{}' cannot be converted to int",
|
||||
val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_double_parse",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
FString str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
{
|
||||
ValueType::DoubleClass val = std::stod(str.toBasicString());
|
||||
return std::make_shared<Object>(ValueType::DoubleClass(val));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Invalid double string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_double_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::IntClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
static_cast<ValueType::DoubleClass>(val->as<ValueType::IntClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(
|
||||
ValueType::DoubleClass(val->as<ValueType::BoolClass>() ? 1.0 : 0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Type '{}' cannot be converted to double",
|
||||
val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_string_from",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
return std::make_shared<Object>(val->toStringIO());
|
||||
}},
|
||||
{u8"__ftime_now_ns",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
// returns nanoseconds
|
||||
using namespace Fig::Time;
|
||||
auto now = Clock::now();
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time).count()));
|
||||
}},
|
||||
|
||||
/* math start */
|
||||
{u8"__fmath_acos",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(acos(d));
|
||||
}},
|
||||
{u8"__fmath_acosh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(acosh(d));
|
||||
}},
|
||||
{u8"__fmath_asin",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(asin(d));
|
||||
}},
|
||||
{u8"__fmath_asinh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(asinh(d));
|
||||
}},
|
||||
{u8"__fmath_atan",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(atan(d));
|
||||
}},
|
||||
{u8"__fmath_atan2",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass y = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass x = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(atan2(y, x));
|
||||
}},
|
||||
{u8"__fmath_atanh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(atanh(d));
|
||||
}},
|
||||
{u8"__fmath_ceil",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(ceil(d));
|
||||
}},
|
||||
{u8"__fmath_cos",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(cos(d));
|
||||
}},
|
||||
{u8"__fmath_cosh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(cosh(d));
|
||||
}},
|
||||
{u8"__fmath_exp",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(exp(d));
|
||||
}},
|
||||
{u8"__fmath_expm1",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(expm1(d));
|
||||
}},
|
||||
{u8"__fmath_fabs",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(fabs(d));
|
||||
}},
|
||||
{u8"__fmath_floor",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(floor(d));
|
||||
}},
|
||||
{u8"__fmath_fmod",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(fmod(x, y));
|
||||
}},
|
||||
{u8"__fmath_frexp",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
int e;
|
||||
return std::make_shared<Object>(List({std::make_shared<Object>(frexp(d, &e)),
|
||||
std::make_shared<Object>(static_cast<ValueType::IntClass>(e))}));
|
||||
}},
|
||||
{u8"__fmath_gcd",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::IntClass x = args[0]->as<ValueType::IntClass>();
|
||||
ValueType::IntClass y = args[1]->as<ValueType::IntClass>();
|
||||
return std::make_shared<Object>(std::gcd(x, y));
|
||||
}},
|
||||
{u8"__fmath_hypot",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
return std::make_shared<Object>(hypot(x, y));
|
||||
}},
|
||||
{u8"__fmath_isequal",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ValueType::DoubleClass x = args[0]->getNumericValue();
|
||||
ValueType::DoubleClass y = args[1]->getNumericValue();
|
||||
static const double epsilon = 1e-9;
|
||||
return std::make_shared<Object>(fabs(x - y) < epsilon);
|
||||
}},
|
||||
{u8"__fmath_log",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log(d));
|
||||
}},
|
||||
{u8"__fmath_log10",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log10(d));
|
||||
}},
|
||||
{u8"__fmath_log1p",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log1p(d));
|
||||
}},
|
||||
{u8"__fmath_log2",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(log2(d));
|
||||
}},
|
||||
{u8"__fmath_sin",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sin(d));
|
||||
}},
|
||||
{u8"__fmath_sinh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sinh(d));
|
||||
}},
|
||||
{u8"__fmath_sqrt",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(sqrt(d));
|
||||
}},
|
||||
{u8"__fmath_tan",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(tan(d));
|
||||
}},
|
||||
{u8"__fmath_tanh",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(tanh(d));
|
||||
}},
|
||||
{u8"__fmath_trunc",
|
||||
[](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
ValueType::DoubleClass d = val->getNumericValue();
|
||||
return std::make_shared<Object>(trunc(d));
|
||||
}},
|
||||
};
|
||||
|
||||
inline bool isBuiltinFunction(const FString &name)
|
||||
{
|
||||
return builtinFunctions.find(name) != builtinFunctions.end();
|
||||
return getBuiltinFunctions().find(name) != getBuiltinFunctions().end();
|
||||
}
|
||||
|
||||
inline BuiltinFunction getBuiltinFunction(const FString &name)
|
||||
inline BuiltinFunction getBuiltinFunction(const String &name)
|
||||
{
|
||||
auto it = builtinFunctions.find(name);
|
||||
if (it == builtinFunctions.end())
|
||||
auto it = getBuiltinFunctions().find(name);
|
||||
if (it == getBuiltinFunctions().end())
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
inline int getBuiltinFunctionParamCount(const FString &name)
|
||||
inline int getBuiltinFunctionParamCount(const String &name)
|
||||
{
|
||||
auto it = builtinFunctionArgCounts.find(name);
|
||||
if (it == builtinFunctionArgCounts.end())
|
||||
auto it = getBuiltinFunctionArgCounts().find(name);
|
||||
if (it == getBuiltinFunctionArgCounts().end())
|
||||
{
|
||||
throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
throw RuntimeError(String(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "Ast/Expressions/InitExpr.hpp"
|
||||
#include <Ast/Expressions/VarExpr.hpp>
|
||||
#include <Ast/Statements/ErrorFlow.hpp>
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
@@ -5,63 +7,78 @@
|
||||
#include <Error/error.hpp>
|
||||
#include <Token/token.hpp>
|
||||
#include <Parser/parser.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// Operator : pair<LeftBindingPower, RightBindingPower>
|
||||
|
||||
const std::unordered_map<Ast::Operator, std::pair<Parser::Precedence, Parser::Precedence>> Parser::opPrecedence = {
|
||||
// 算术
|
||||
{Ast::Operator::Add, {10, 11}},
|
||||
{Ast::Operator::Subtract, {10, 11}},
|
||||
{Ast::Operator::Multiply, {20, 21}},
|
||||
{Ast::Operator::Divide, {20, 21}},
|
||||
{Ast::Operator::Modulo, {20, 21}},
|
||||
{Ast::Operator::Power, {30, 29}},
|
||||
// 赋值类 - 右结合
|
||||
{Ast::Operator::Assign, {20, 10}},
|
||||
{Ast::Operator::PlusAssign, {20, 10}},
|
||||
{Ast::Operator::MinusAssign, {20, 10}},
|
||||
{Ast::Operator::SlashAssign, {20, 10}},
|
||||
{Ast::Operator::AsteriskAssign, {20, 10}},
|
||||
{Ast::Operator::PercentAssign, {20, 10}},
|
||||
{Ast::Operator::CaretAssign, {20, 10}},
|
||||
|
||||
// 逻辑
|
||||
{Ast::Operator::And, {5, 6}},
|
||||
{Ast::Operator::Or, {4, 5}},
|
||||
// {Ast::Operator::Not, {30, 31}}, // 一元
|
||||
// 三元条件 - 特殊处理,通常是右结合
|
||||
{Ast::Operator::TernaryCond, {30, 20}},
|
||||
|
||||
// 比较
|
||||
{Ast::Operator::Equal, {7, 8}},
|
||||
{Ast::Operator::NotEqual, {7, 8}},
|
||||
{Ast::Operator::Less, {8, 9}},
|
||||
{Ast::Operator::LessEqual, {8, 9}},
|
||||
{Ast::Operator::Greater, {8, 9}},
|
||||
{Ast::Operator::GreaterEqual, {8, 9}},
|
||||
{Ast::Operator::Is, {8, 9}},
|
||||
// 逻辑或
|
||||
{Ast::Operator::Or, {40, 41}}, // leftBP < rightBP,左结合
|
||||
|
||||
// 位运算
|
||||
{Ast::Operator::BitAnd, {6, 7}},
|
||||
{Ast::Operator::BitOr, {4, 5}},
|
||||
{Ast::Operator::BitXor, {5, 6}},
|
||||
// {Ast::Operator::BitNot, {30, 31}}, // 一元
|
||||
{Ast::Operator::ShiftLeft, {15, 16}},
|
||||
{Ast::Operator::ShiftRight, {15, 16}},
|
||||
// 逻辑与
|
||||
{Ast::Operator::And, {50, 51}}, // 比 Or 高
|
||||
|
||||
{Ast::Operator::Assign, {2, 1}}, // 右结合
|
||||
{Ast::Operator::PlusAssign, {2, 1}},
|
||||
{Ast::Operator::MinusAssign, {2, 1}},
|
||||
{Ast::Operator::SlashAssign, {2, 1}},
|
||||
{Ast::Operator::AsteriskAssign, {2, 1}},
|
||||
{Ast::Operator::PercentAssign, {2, 1}},
|
||||
{Ast::Operator::CaretAssign, {2, 1}},
|
||||
// 位或
|
||||
{Ast::Operator::BitOr, {60, 61}},
|
||||
|
||||
// 海象运算符
|
||||
// {Ast::Operator::Walrus, {2, 1}}, // 右结合
|
||||
// 位异或
|
||||
{Ast::Operator::BitXor, {70, 71}},
|
||||
|
||||
// // 点运算符
|
||||
// {Ast::Operator::Dot, {40, 41}},
|
||||
{Ast::Operator::TernaryCond, {3, 2}},
|
||||
// 位与
|
||||
{Ast::Operator::BitAnd, {80, 81}},
|
||||
|
||||
// 相等比较
|
||||
{Ast::Operator::Equal, {90, 91}},
|
||||
{Ast::Operator::NotEqual, {90, 91}},
|
||||
|
||||
// 关系比较
|
||||
{Ast::Operator::Less, {100, 101}},
|
||||
{Ast::Operator::LessEqual, {100, 101}},
|
||||
{Ast::Operator::Greater, {100, 101}},
|
||||
{Ast::Operator::GreaterEqual, {100, 101}},
|
||||
{Ast::Operator::Is, {100, 101}},
|
||||
|
||||
// 转换
|
||||
{Ast::Operator::As, {105, 106}},
|
||||
|
||||
// 位移
|
||||
{Ast::Operator::ShiftLeft, {110, 111}},
|
||||
{Ast::Operator::ShiftRight, {110, 111}},
|
||||
|
||||
// 加减
|
||||
{Ast::Operator::Add, {120, 121}},
|
||||
{Ast::Operator::Subtract, {120, 121}},
|
||||
|
||||
// 乘除模
|
||||
{Ast::Operator::Multiply, {130, 131}},
|
||||
{Ast::Operator::Divide, {130, 131}},
|
||||
{Ast::Operator::Modulo, {130, 131}},
|
||||
|
||||
// 幂运算 - 右结合
|
||||
{Ast::Operator::Power, {140, 139}}, // leftBP > rightBP
|
||||
};
|
||||
|
||||
// 赋值 < 三元 < 逻辑或 < 逻辑与 < 位运算 < 比较 < 位移 < 加减 < 乘除 < 幂 < 一元
|
||||
|
||||
// 一元运算符的优先级比所有二元运算符都高
|
||||
const std::unordered_map<Ast::Operator, Parser::Precedence> Parser::unaryOpPrecedence = {
|
||||
{Ast::Operator::Subtract, 150}, // -
|
||||
{Ast::Operator::BitAnd, 150}, // &
|
||||
{Ast::Operator::BitNot, 150}, // ~
|
||||
{Ast::Operator::Not, 150}, // !
|
||||
{Ast::Operator::BitAnd, 150}, // &(取地址)
|
||||
};
|
||||
|
||||
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
||||
@@ -70,7 +87,7 @@ namespace Fig
|
||||
bool isConst = (currentToken().getType() == TokenType::Const ? true : false);
|
||||
next();
|
||||
expect(TokenType::Identifier);
|
||||
FString name = currentToken().getValue();
|
||||
String name = currentToken().getValue();
|
||||
next();
|
||||
Ast::Expression declaredType = nullptr;
|
||||
bool hasSpecificType = false;
|
||||
@@ -85,12 +102,12 @@ namespace Fig
|
||||
next(); // consume `;`, no using expectConsume here cause we don't need to check again
|
||||
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, declaredType, nullptr, false);
|
||||
}
|
||||
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus");
|
||||
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, U"\1");
|
||||
bool followupType = false;
|
||||
|
||||
if (isThis(TokenType::Walrus))
|
||||
{
|
||||
if (hasSpecificType) throwAddressableError<SyntaxError>(FString(u8""));
|
||||
if (hasSpecificType) throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
followupType = true;
|
||||
}
|
||||
next();
|
||||
@@ -101,7 +118,7 @@ namespace Fig
|
||||
|
||||
ObjectPtr Parser::__parseValue()
|
||||
{
|
||||
FString _val = currentToken().getValue();
|
||||
String _val = currentToken().getValue();
|
||||
if (currentToken().getType() == TokenType::LiteralNumber)
|
||||
{
|
||||
if (_val.contains(u8'.') || _val.contains(u8'e'))
|
||||
@@ -114,7 +131,7 @@ namespace Fig
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
|
||||
throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
}
|
||||
return std::make_shared<Object>(d);
|
||||
}
|
||||
@@ -128,7 +145,7 @@ namespace Fig
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
|
||||
throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
}
|
||||
return std::make_shared<Object>(i);
|
||||
}
|
||||
@@ -136,7 +153,7 @@ namespace Fig
|
||||
else if (currentToken().getType() == TokenType::LiteralString) { return std::make_shared<Object>(_val); }
|
||||
else if (currentToken().getType() == TokenType::LiteralBool)
|
||||
{
|
||||
return std::make_shared<Object>((_val == u8"true" ? true : false));
|
||||
return std::make_shared<Object>((_val == U"\1" ? true : false));
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::LiteralNull) { return Object::getNullInstance(); }
|
||||
else
|
||||
@@ -159,7 +176,7 @@ namespace Fig
|
||||
|
||||
Ast::FunctionParameters::PosParasType pp;
|
||||
Ast::FunctionParameters::DefParasType dp;
|
||||
FString variaPara;
|
||||
String variaPara;
|
||||
while (true)
|
||||
{
|
||||
if (isThis(TokenType::RightParen))
|
||||
@@ -167,13 +184,13 @@ namespace Fig
|
||||
next();
|
||||
return Ast::FunctionParameters(pp, dp);
|
||||
}
|
||||
expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current
|
||||
FString pname = currentToken().getValue();
|
||||
expect(TokenType::Identifier, String(U"\1")); // check current
|
||||
String pname = currentToken().getValue();
|
||||
next(); // skip pname
|
||||
if (isThis(TokenType::Assign)) // =
|
||||
{
|
||||
next();
|
||||
dp.push_back({pname, {ValueType::Any.name, parseExpression(0, TokenType::Comma)}});
|
||||
dp.push_back({pname, {makeAst<Ast::VarExprAst>(U"\1"), parseExpression(0, TokenType::Comma)}});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
@@ -182,13 +199,12 @@ namespace Fig
|
||||
else if (isThis(TokenType::Colon)) // :
|
||||
{
|
||||
next(); // skip `:`
|
||||
expect(TokenType::Identifier, FString(u8"Type name"));
|
||||
FString ti(currentToken().getValue());
|
||||
next(); // skip type name
|
||||
Ast::Expression type_exp =
|
||||
parseExpression(0, TokenType::Comma, TokenType::RightParen, TokenType::Assign);
|
||||
if (isThis(TokenType::Assign)) // =
|
||||
{
|
||||
next(); // skip `=`
|
||||
dp.push_back({pname, {ti, parseExpression(0, TokenType::Comma)}});
|
||||
dp.push_back({pname, {type_exp, parseExpression(0, TokenType::Comma)}});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
@@ -196,7 +212,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.push_back({pname, ti});
|
||||
pp.push_back({pname, type_exp});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
@@ -208,16 +224,13 @@ namespace Fig
|
||||
variaPara = pname;
|
||||
next(); // skip `...`
|
||||
if (!isThis(TokenType::RightParen))
|
||||
throwAddressableError<SyntaxError>(
|
||||
u8"Expects right paren, variable parameter function can only have one parameter",
|
||||
currentAAI.line,
|
||||
currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(U"\1", currentAAI.line, currentAAI.column);
|
||||
next(); // skip `)`
|
||||
return Ast::FunctionParameters(variaPara);
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.push_back({pname, ValueType::Any.name});
|
||||
pp.push_back({pname, makeAst<Ast::VarExprAst>(U"\1")});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
@@ -227,7 +240,7 @@ namespace Fig
|
||||
}
|
||||
Ast::FunctionDef Parser::__parseFunctionDef(bool isPublic)
|
||||
{
|
||||
FString funcName = currentToken().getValue();
|
||||
String funcName = currentToken().getValue();
|
||||
next();
|
||||
expect(TokenType::LeftParen);
|
||||
Ast::FunctionParameters params = __parseFunctionParameters();
|
||||
@@ -246,9 +259,9 @@ namespace Fig
|
||||
Ast::StructDef Parser::__parseStructDef(bool isPublic)
|
||||
{
|
||||
// entry: current is struct name
|
||||
FString structName = currentToken().getValue();
|
||||
String structName = currentToken().getValue();
|
||||
next();
|
||||
expect(TokenType::LeftBrace, u8"struct body");
|
||||
expect(TokenType::LeftBrace, U"\1");
|
||||
next();
|
||||
bool braceClosed = false;
|
||||
|
||||
@@ -265,7 +278,7 @@ namespace Fig
|
||||
|
||||
auto __parseStructField = [this](bool isPublic) -> Ast::StructDefField {
|
||||
AccessModifier am = AccessModifier::Normal;
|
||||
FString fieldName;
|
||||
String fieldName;
|
||||
if (isThis(TokenType::Identifier))
|
||||
{
|
||||
fieldName = currentToken().getValue();
|
||||
@@ -275,13 +288,13 @@ namespace Fig
|
||||
else if (isThis(TokenType::Const))
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"field name");
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
fieldName = currentToken().getValue();
|
||||
am = (isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(std::format("expect field name or field attribute")));
|
||||
throwAddressableError<SyntaxError>(String(std::format("expect field name or field attribute")));
|
||||
}
|
||||
Ast::Expression fieldType = nullptr;
|
||||
if (isThis(TokenType::Colon))
|
||||
@@ -293,7 +306,7 @@ namespace Fig
|
||||
if (isThis(TokenType::Assign))
|
||||
{
|
||||
next();
|
||||
if (isEOF()) throwAddressableError<SyntaxError>(FString(u8"expect an expression"));
|
||||
if (isEOF()) throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
initExpr = parseExpression(0);
|
||||
}
|
||||
expectSemicolon();
|
||||
@@ -337,7 +350,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString("Invalid syntax"));
|
||||
throwAddressableError<SyntaxError>(String("Invalid syntax"));
|
||||
}
|
||||
}
|
||||
else if (isThis(TokenType::Function))
|
||||
@@ -354,21 +367,21 @@ namespace Fig
|
||||
else if (isThis(TokenType::Variable))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString("Variables are not allowed to be defined within a structure."));
|
||||
String("Variables are not allowed to be defined within a structure."));
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString("Invalid syntax"));
|
||||
throwAddressableError<SyntaxError>(String("Invalid syntax"));
|
||||
}
|
||||
}
|
||||
if (!braceClosed) { throwAddressableError<SyntaxError>(FString("braces are not closed")); }
|
||||
if (!braceClosed) { throwAddressableError<SyntaxError>(String("braces are not closed")); }
|
||||
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts));
|
||||
}
|
||||
|
||||
Ast::InterfaceDef Parser::__parseInterfaceDef(bool isPublic)
|
||||
{
|
||||
// entry: current is interface name
|
||||
FString interfaceName = currentToken().getValue();
|
||||
String interfaceName = currentToken().getValue();
|
||||
next(); // consume name
|
||||
expect(TokenType::LeftBrace); // `{
|
||||
next(); // consume `{`
|
||||
@@ -384,7 +397,7 @@ namespace Fig
|
||||
}
|
||||
if (isThis(TokenType::Identifier))
|
||||
{
|
||||
FString funcName = currentToken().getValue();
|
||||
String funcName = currentToken().getValue();
|
||||
next(); // consume func name
|
||||
|
||||
expect(TokenType::LeftParen);
|
||||
@@ -408,7 +421,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Invalid syntax"), currentAAI.line, currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(String(U"\1"), currentAAI.line, currentAAI.column);
|
||||
}
|
||||
}
|
||||
return makeAst<Ast::InterfaceDefAst>(interfaceName, methods, isPublic);
|
||||
@@ -418,15 +431,15 @@ namespace Fig
|
||||
{
|
||||
// entry: current is `impl`
|
||||
next(); // consume `impl`
|
||||
expect(TokenType::Identifier, u8"interface name");
|
||||
FString interfaceName = currentToken().getValue();
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
String interfaceName = currentToken().getValue();
|
||||
next(); // consume interface name
|
||||
|
||||
expect(TokenType::For);
|
||||
next(); // consume `for`
|
||||
|
||||
expect(TokenType::Identifier, u8"struct name");
|
||||
FString structName = currentToken().getValue();
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
String structName = currentToken().getValue();
|
||||
next(); // consume name
|
||||
expect(TokenType::LeftBrace); // {
|
||||
next(); // consume `{`
|
||||
@@ -442,7 +455,7 @@ namespace Fig
|
||||
}
|
||||
if (isThis(TokenType::Identifier))
|
||||
{
|
||||
FString funcName = currentToken().getValue();
|
||||
String funcName = currentToken().getValue();
|
||||
next(); // consume func name
|
||||
expect(TokenType::LeftParen);
|
||||
Ast::FunctionParameters paras = __parseFunctionParameters();
|
||||
@@ -452,7 +465,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Invalid syntax"), currentAAI.line, currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(String(U"\1"), currentAAI.line, currentAAI.column);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,16 +510,16 @@ namespace Fig
|
||||
next(); // consume `catch`
|
||||
expect(TokenType::LeftParen);
|
||||
next(); // consume `(`
|
||||
expect(TokenType::Identifier, u8"error receive var name");
|
||||
FString errVarName = currentToken().getValue();
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
String errVarName = currentToken().getValue();
|
||||
next(); // consume name
|
||||
|
||||
bool hasType = false;
|
||||
FString errVarType;
|
||||
String errVarType;
|
||||
if (isThis(TokenType::Colon)) // :
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"error type");
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
errVarType = currentToken().getValue();
|
||||
next(); // consume var type
|
||||
hasType = true;
|
||||
@@ -526,8 +539,7 @@ namespace Fig
|
||||
{
|
||||
if (finallyBlock != nullptr)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
u8"Duplicate try finally-block", currentAAI.line, currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(U"\1", currentAAI.line, currentAAI.column);
|
||||
}
|
||||
next(); // consume `finally`
|
||||
expect(TokenType::LeftBrace);
|
||||
@@ -563,8 +575,7 @@ namespace Fig
|
||||
else if (isThis(TokenType::Interface)) { stmt = __parseInterfaceDef(true); }
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`"));
|
||||
throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
}
|
||||
}
|
||||
else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) { stmt = __parseVarDef(false); }
|
||||
@@ -575,22 +586,19 @@ namespace Fig
|
||||
}
|
||||
else if (isThis(TokenType::Struct))
|
||||
{
|
||||
expectPeek(TokenType::Identifier, u8"struct name");
|
||||
expectPeek(TokenType::Identifier, U"\1");
|
||||
next();
|
||||
stmt = __parseStructDef(false);
|
||||
}
|
||||
else if (isThis(TokenType::Interface))
|
||||
{
|
||||
expectPeek(TokenType::Identifier, u8"interface name");
|
||||
expectPeek(TokenType::Identifier, U"\1");
|
||||
next();
|
||||
stmt = __parseInterfaceDef(false);
|
||||
}
|
||||
else if (isThis(TokenType::Implement)) { stmt = __parseImplement(); }
|
||||
else if (isThis(TokenType::If)) { stmt = __parseIf(); }
|
||||
else if (isThis(TokenType::Else))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"`else` without matching `if`"));
|
||||
}
|
||||
else if (isThis(TokenType::Else)) { throwAddressableError<SyntaxError>(String(U"\1")); }
|
||||
else if (isThis(TokenType::LeftBrace)) { stmt = __parseBlockStatement(); }
|
||||
else if (isThis(TokenType::While)) { stmt = __parseWhile(); }
|
||||
else if (isThis(TokenType::For)) { stmt = __parseFor(); }
|
||||
@@ -608,7 +616,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(u8"invalid syntax", currentAAI.line, currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(U"\1", currentAAI.line, currentAAI.column);
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
@@ -708,15 +716,12 @@ namespace Fig
|
||||
// 2. expression stmt:i++, foo()
|
||||
// ❌ not allowed:if/while/for/block stmt
|
||||
|
||||
if (isThis(TokenType::LeftBrace))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(u8"BlockStatement cannot be used as for loop increment");
|
||||
}
|
||||
if (isThis(TokenType::LeftBrace)) { throwAddressableError<SyntaxError>(U"\1"); }
|
||||
|
||||
if (isThis(TokenType::If) || isThis(TokenType::While) || isThis(TokenType::For) || isThis(TokenType::Return)
|
||||
|| isThis(TokenType::Break) || isThis(TokenType::Continue))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(u8"Control flow statements cannot be used as for loop increment");
|
||||
throwAddressableError<SyntaxError>(U"\1");
|
||||
}
|
||||
|
||||
Ast::Expression exp = parseExpression(0, TokenType::LeftBrace);
|
||||
@@ -770,7 +775,7 @@ namespace Fig
|
||||
return makeAst<Ast::BreakSt>();
|
||||
}
|
||||
|
||||
Ast::VarExpr Parser::__parseVarExpr(FString name)
|
||||
Ast::VarExpr Parser::__parseVarExpr(String name)
|
||||
{
|
||||
return makeAst<Ast::VarExprAst>(name);
|
||||
}
|
||||
@@ -853,7 +858,7 @@ namespace Fig
|
||||
{
|
||||
// entry: current is `{`
|
||||
next(); // consume `{`
|
||||
std::vector<std::pair<FString, Ast::Expression>> args;
|
||||
std::vector<std::pair<String, Ast::Expression>> args;
|
||||
/*
|
||||
3 ways of calling constructor
|
||||
.1 Person {"Fig", 1, "IDK"};
|
||||
@@ -867,10 +872,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;
|
||||
@@ -881,29 +882,19 @@ namespace Fig
|
||||
{
|
||||
// 1 Person {"Fig", 1, "IDK"};
|
||||
Ast::Expression expr = parseExpression(0, TokenType::Comma, TokenType::RightBrace);
|
||||
args.push_back({FString(), std::move(expr)});
|
||||
args.push_back({String(), std::move(expr)});
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
// 2 Person {name: "Fig", age: 1, sex: "IDK"};
|
||||
expect(TokenType::Identifier);
|
||||
FString fieldName = currentToken().getValue();
|
||||
String fieldName = currentToken().getValue();
|
||||
next(); // consume identifier
|
||||
expect(TokenType::Colon);
|
||||
next(); // consume colon
|
||||
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
|
||||
@@ -911,16 +902,34 @@ namespace Fig
|
||||
else if (!isThis(TokenType::RightBrace))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString(std::format("Expect `,` or `}}` in struct initialization expression, got {}",
|
||||
String(std::format("Expect `,` or `}}` in struct initialization expression, got {}",
|
||||
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<String, 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));
|
||||
return makeAst<Ast::InitExprAst>(structe, args, static_cast<Ast::InitExprAst::InitMode>(mode));
|
||||
}
|
||||
|
||||
Ast::Expression Parser::__parseTupleOrParenExpr()
|
||||
@@ -961,7 +970,7 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Expect ')' or ',' after expression in parentheses"));
|
||||
throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
}
|
||||
return nullptr; // to suppress compiler warning
|
||||
}
|
||||
@@ -989,10 +998,12 @@ namespace Fig
|
||||
Ast::Import Parser::__parseImport()
|
||||
{
|
||||
next(); // consume `import`
|
||||
std::vector<FString> path;
|
||||
std::vector<String> path;
|
||||
std::vector<String> names;
|
||||
String rename;
|
||||
while (true)
|
||||
{
|
||||
expect(TokenType::Identifier, u8"package name");
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
path.push_back(currentToken().getValue());
|
||||
next(); // consume package name
|
||||
if (isThis(TokenType::Semicolon)) { break; }
|
||||
@@ -1000,25 +1011,53 @@ namespace Fig
|
||||
{
|
||||
next(); // consume `.`
|
||||
}
|
||||
else if (isThis(TokenType::As)) { break; }
|
||||
else if (isThis(TokenType::LeftBrace)) { break; }
|
||||
else
|
||||
{
|
||||
throw SyntaxError();
|
||||
throwAddressableError<SyntaxError>(U"\1");
|
||||
}
|
||||
}
|
||||
if (isThis(TokenType::As))
|
||||
{
|
||||
next(); // consume `as`
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
rename = currentToken().getValue();
|
||||
next(); // consume name
|
||||
}
|
||||
else if (isThis(TokenType::LeftBrace))
|
||||
{
|
||||
next(); // consume `{`
|
||||
while (true)
|
||||
{
|
||||
if (isThis(TokenType::RightBrace))
|
||||
{
|
||||
next(); // consume `}`
|
||||
break;
|
||||
}
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // consume `,`
|
||||
}
|
||||
expect(TokenType::Identifier, U"\1");
|
||||
names.push_back(currentToken().getValue());
|
||||
next();
|
||||
}
|
||||
}
|
||||
expectSemicolon();
|
||||
return makeAst<Ast::ImportSt>(path);
|
||||
return makeAst<Ast::ImportSt>(path, names, rename);
|
||||
}
|
||||
|
||||
Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2)
|
||||
Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2, TokenType stop3)
|
||||
{
|
||||
Ast::Expression lhs;
|
||||
Ast::Operator op;
|
||||
|
||||
Token tok = currentToken();
|
||||
if (tok == EOFTok) throwAddressableError<SyntaxError>(FString(u8"Unexpected end of expression"));
|
||||
if (tok.getType() == stop || tok.getType() == stop2)
|
||||
if (tok == EOFTok) throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
if (tok.getType() == stop || tok.getType() == stop2 || tok.getType() == stop3)
|
||||
{
|
||||
if (lhs == nullptr) throwAddressableError<SyntaxError>(FString(u8"Expected expression"));
|
||||
if (lhs == nullptr) throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
return lhs;
|
||||
}
|
||||
if (tok.getType() == TokenType::LeftBracket)
|
||||
@@ -1039,7 +1078,7 @@ namespace Fig
|
||||
if (currentToken().getType() == TokenType::Identifier)
|
||||
{
|
||||
// err
|
||||
throwAddressableError<SyntaxError>(FString(u8"Function literal should not have a name"));
|
||||
throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
}
|
||||
expect(TokenType::LeftParen);
|
||||
lhs = __parseFunctionLiteralExpr();
|
||||
@@ -1051,7 +1090,7 @@ namespace Fig
|
||||
}
|
||||
else if (tok.isIdentifier())
|
||||
{
|
||||
FString id = tok.getValue();
|
||||
String id = tok.getValue();
|
||||
next();
|
||||
lhs = __parseVarExpr(id);
|
||||
}
|
||||
@@ -1071,22 +1110,18 @@ namespace Fig
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(u8"Unexpected token in expression:") + tok.toString());
|
||||
throwAddressableError<SyntaxError>(String(U"\1") + tok.toString());
|
||||
}
|
||||
|
||||
// infix / (postfix) ?
|
||||
while (true)
|
||||
{
|
||||
tok = currentToken();
|
||||
if (tok.getType() == stop || tok.getType() == stop2 || tok == EOFTok) break;
|
||||
if (tok.getType() == stop || tok.getType() == stop2 || tok.getType() == stop3 || tok == EOFTok) break;
|
||||
|
||||
/* Postfix */
|
||||
|
||||
if (tok.getType() == TokenType::LeftBrace)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString(u8"Since Fig v0.4.2, please use new struct{} to avoid syntax ambiguity"));
|
||||
}
|
||||
if (tok.getType() == TokenType::LeftBrace) { throwAddressableError<SyntaxError>(String(U"\1")); }
|
||||
|
||||
if (tok.getType() == TokenType::LeftParen)
|
||||
{
|
||||
@@ -1109,10 +1144,9 @@ namespace Fig
|
||||
{
|
||||
next(); // consume '.'
|
||||
Token idTok = currentToken();
|
||||
if (!idTok.isIdentifier())
|
||||
throwAddressableError<SyntaxError>(FString(u8"Expected identifier after '.'"));
|
||||
if (!idTok.isIdentifier()) throwAddressableError<SyntaxError>(String(U"\1"));
|
||||
|
||||
FString member = idTok.getValue();
|
||||
String member = idTok.getValue();
|
||||
next(); // consume identifier
|
||||
|
||||
lhs = makeAst<Ast::MemberExprAst>(lhs, member);
|
||||
@@ -1168,8 +1202,7 @@ namespace Fig
|
||||
auto stmt = __parseStatement();
|
||||
if (!output.empty() && stmt->getType() == Ast::AstType::PackageSt)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
u8"Package must be at the beginning of the file", currentAAI.line, currentAAI.column);
|
||||
throwAddressableError<SyntaxError>(U"\1", currentAAI.line, currentAAI.column);
|
||||
}
|
||||
pushNode(stmt);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <Token/token.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/ast.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Error/error.hpp>
|
||||
|
||||
#include <memory>
|
||||
@@ -22,8 +23,8 @@ namespace Fig
|
||||
std::vector<Ast::AstBase> output;
|
||||
std::vector<Token> previousTokens;
|
||||
|
||||
std::shared_ptr<FString> sourcePathPtr;
|
||||
std::shared_ptr<std::vector<FString>> sourceLinesPtr;
|
||||
std::shared_ptr<String> sourcePathPtr;
|
||||
std::shared_ptr<std::vector<String>> sourceLinesPtr;
|
||||
|
||||
size_t tokenPruduced = 0;
|
||||
size_t currentTokenIndex = 0;
|
||||
@@ -75,16 +76,16 @@ namespace Fig
|
||||
static const std::unordered_map<Ast::Operator, std::pair<Precedence, Precedence>> opPrecedence;
|
||||
static const std::unordered_map<Ast::Operator, Precedence> unaryOpPrecedence;
|
||||
|
||||
Parser(const Lexer &_lexer, FString _sourcePath, std::vector<FString> _sourceLines) : lexer(_lexer)
|
||||
Parser(const Lexer &_lexer, String _sourcePath, std::vector<String> _sourceLines) : lexer(_lexer)
|
||||
{
|
||||
sourcePathPtr = std::make_shared<FString>(_sourcePath);
|
||||
sourceLinesPtr = std::make_shared<std::vector<FString>>(_sourceLines);
|
||||
sourcePathPtr = std::make_shared<String>(_sourcePath);
|
||||
sourceLinesPtr = std::make_shared<std::vector<String>>(_sourceLines);
|
||||
}
|
||||
|
||||
AddressableError *getError() const { return error.get(); }
|
||||
|
||||
template <class _ErrT, typename = AddressableError>
|
||||
void throwAddressableError(FString msg,
|
||||
void throwAddressableError(String msg,
|
||||
size_t line,
|
||||
size_t column,
|
||||
std::source_location loc = std::source_location::current())
|
||||
@@ -95,7 +96,7 @@ namespace Fig
|
||||
throw spError;
|
||||
}
|
||||
template <class _ErrT, typename = AddressableError>
|
||||
void throwAddressableError(FString msg, std::source_location loc = std::source_location::current())
|
||||
void throwAddressableError(String msg, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
static_assert(std::is_base_of_v<AddressableError, _ErrT>, "_ErrT must derive from AddressableError");
|
||||
// line, column provide by `currentAAI`
|
||||
@@ -105,7 +106,7 @@ namespace Fig
|
||||
}
|
||||
|
||||
template <class _ErrT, typename = UnaddressableError>
|
||||
void throwUnaddressableError(FString msg, std::source_location loc = std::source_location::current())
|
||||
void throwUnaddressableError(String msg, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
static_assert(std::is_base_of_v<AddressableError, _ErrT>, "_ErrT must derive from AddressableError");
|
||||
_ErrT spError(msg, loc);
|
||||
@@ -153,10 +154,8 @@ namespace Fig
|
||||
tokenPruduced++;
|
||||
if (tok == IllegalTok) throw lexer.getError();
|
||||
currentTokenIndex = tokenPruduced - 1;
|
||||
setCurrentAAI(Ast::AstAddressInfo{.line = tok.line,
|
||||
.column = tok.column,
|
||||
.sourcePath = sourcePathPtr,
|
||||
.sourceLines = sourceLinesPtr});
|
||||
setCurrentAAI(Ast::AstAddressInfo{
|
||||
.line = tok.line, .column = tok.column, .sourcePath = sourcePathPtr, .sourceLines = sourceLinesPtr});
|
||||
|
||||
previousTokens.push_back(tok);
|
||||
}
|
||||
@@ -203,7 +202,7 @@ namespace Fig
|
||||
{
|
||||
if (peekToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
|
||||
throwAddressableError<SyntaxError>(String(std::format("Expected `{}`, but got `{}`",
|
||||
magic_enum::enum_name(type),
|
||||
magic_enum::enum_name(peekToken().getType()))),
|
||||
loc);
|
||||
@@ -214,31 +213,29 @@ namespace Fig
|
||||
{
|
||||
if (currentToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString(std::format("Expected `{}`, but got `{}`",
|
||||
throwAddressableError<SyntaxError>(String(std::format("Expected `{}`, but got `{}`",
|
||||
magic_enum::enum_name(type),
|
||||
magic_enum::enum_name(currentToken().getType()))),
|
||||
loc);
|
||||
}
|
||||
}
|
||||
|
||||
void expectPeek(TokenType type, FString expected, std::source_location loc = std::source_location::current())
|
||||
void expectPeek(TokenType type, String expected, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
if (peekToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
|
||||
throwAddressableError<SyntaxError>(String(std::format("Expected `{}`, but got `{}`",
|
||||
expected.toBasicString(),
|
||||
magic_enum::enum_name(peekToken().getType()))),
|
||||
loc);
|
||||
}
|
||||
}
|
||||
|
||||
void expect(TokenType type, FString expected, std::source_location loc = std::source_location::current())
|
||||
void expect(TokenType type, String expected, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
if (currentToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(
|
||||
FString(std::format("Expected `{}`, but got `{}`",
|
||||
throwAddressableError<SyntaxError>(String(std::format("Expected `{}`, but got `{}`",
|
||||
expected.toBasicString(),
|
||||
magic_enum::enum_name(currentToken().getType()))),
|
||||
loc);
|
||||
@@ -265,7 +262,7 @@ namespace Fig
|
||||
expectConsume(TokenType::Semicolon, loc);
|
||||
}
|
||||
|
||||
void expectConsume(TokenType type, FString expected, std::source_location loc = std::source_location::current())
|
||||
void expectConsume(TokenType type, String expected, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
expect(type, expected, loc);
|
||||
next();
|
||||
@@ -280,8 +277,6 @@ namespace Fig
|
||||
bool isNext(TokenType type) { return peekToken().getType() == type; }
|
||||
bool isThis(TokenType type) { return currentToken().getType() == type; }
|
||||
|
||||
static constexpr FString varDefTypeFollowed = u8"(Followed)";
|
||||
|
||||
Ast::VarDef __parseVarDef(bool); // entry: current is keyword `var` or `const` (isConst: Bool)
|
||||
ObjectPtr __parseValue();
|
||||
Ast::ValueExpr __parseValueExpr();
|
||||
@@ -295,7 +290,7 @@ namespace Fig
|
||||
Ast::Break __parseBreak(); // entry: current is Token::Break
|
||||
Ast::Continue __parseContinue(); // entry: current is Token::Continue
|
||||
|
||||
Ast::VarExpr __parseVarExpr(FString);
|
||||
Ast::VarExpr __parseVarExpr(String);
|
||||
Ast::FunctionDef __parseFunctionDef(bool); // entry: current is Token::Identifier (isPublic: Bool)
|
||||
Ast::StructDef __parseStructDef(bool); // entry: current is Token::Identifier (struct name) arg(isPublic: bool)
|
||||
Ast::InterfaceDef
|
||||
@@ -314,7 +309,6 @@ namespace Fig
|
||||
|
||||
Ast::InitExpr __parseInitExpr(Ast::Expression); // entry: current is `{`, ahead is struct type exp.
|
||||
|
||||
|
||||
Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`
|
||||
|
||||
Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function
|
||||
@@ -322,7 +316,10 @@ namespace Fig
|
||||
Ast::Import __parseImport(); // entry: current is Token::Import
|
||||
|
||||
Ast::Statement __parseStatement(bool = true); // entry: (idk)
|
||||
Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
|
||||
Ast::Expression parseExpression(Precedence,
|
||||
TokenType = TokenType::Semicolon,
|
||||
TokenType = TokenType::Semicolon,
|
||||
TokenType = TokenType::Semicolon);
|
||||
std::vector<Ast::AstBase> parseAll();
|
||||
};
|
||||
}; // namespace Fig
|
||||
62
src/Repl/Repl.cpp
Normal file
62
src/Repl/Repl.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Error/errorLog.hpp>
|
||||
#include <Core/String.hpp>
|
||||
#include <Repl/Repl.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
void Repl::Start() const
|
||||
{
|
||||
ostream << getPrompt() << "\n";
|
||||
|
||||
const String &sourcePath = U"<stdin>";
|
||||
const std::vector<String> &sourceLines{};
|
||||
|
||||
Evaluator evaluator;
|
||||
|
||||
evaluator.CreateGlobalContext();
|
||||
evaluator.RegisterBuiltinsValue();
|
||||
evaluator.SetSourcePath(sourcePath);
|
||||
evaluator.SetSourceLines(sourceLines);
|
||||
|
||||
while (true)
|
||||
{
|
||||
ostream << "\r\n>>";
|
||||
const String &line = readline();
|
||||
|
||||
if (line.empty())
|
||||
{
|
||||
ostream << Object::getNullInstance()->toString().toBasicString();
|
||||
continue;
|
||||
}
|
||||
if (line == u8"!exit") { break; }
|
||||
|
||||
Lexer lexer(line, sourcePath, sourceLines);
|
||||
Parser parser(lexer, sourcePath, sourceLines);
|
||||
|
||||
std::vector<AstBase> program;
|
||||
try
|
||||
{
|
||||
program = parser.parseAll();
|
||||
|
||||
StatementResult sr = evaluator.Run(program);
|
||||
ObjectPtr result = sr.result;
|
||||
ostream << result->toString().toBasicString() << '\n';
|
||||
}
|
||||
catch (AddressableError &e)
|
||||
{
|
||||
ostream << "Oops!\n";
|
||||
ErrorLog::logAddressableError(e);
|
||||
ostream << "\n";
|
||||
}
|
||||
catch (UnaddressableError &e)
|
||||
{
|
||||
ostream << "Oops!\n";
|
||||
ErrorLog::logUnaddressableError(e);
|
||||
ostream << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Fig
|
||||
54
src/Repl/Repl.hpp
Normal file
54
src/Repl/Repl.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/core.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
#include <Parser/parser.hpp>
|
||||
#include <Evaluator/evaluator.hpp>
|
||||
#include <Utils/AstPrinter.hpp>
|
||||
#include <Utils/utils.hpp>
|
||||
#include <Error/errorLog.hpp>
|
||||
#include <Core/runtimeTime.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Repl
|
||||
{
|
||||
private:
|
||||
std::istream &istream;
|
||||
std::ostream &ostream;
|
||||
|
||||
public:
|
||||
Repl(std::istream &_istream = std::cin, std::ostream &_ostream = std::cout) :
|
||||
istream(_istream), ostream(_ostream)
|
||||
{
|
||||
}
|
||||
|
||||
String readline() const
|
||||
{
|
||||
std::string buf;
|
||||
std::getline(istream, buf);
|
||||
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
static std::string getPrompt()
|
||||
{
|
||||
static const std::string prompt = std::format("Fig {} ({})[{} {}-bit on `{}`]\n",
|
||||
Core::VERSION,
|
||||
Core::COMPILE_TIME,
|
||||
Core::COMPILER,
|
||||
Core::ARCH,
|
||||
Core::PLATFORM)
|
||||
+ "Type '!exit' to exit\n" + "feel free to type!";
|
||||
return prompt;
|
||||
}
|
||||
|
||||
void Start() const;
|
||||
};
|
||||
|
||||
}; // namespace Fig
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <format>
|
||||
#include <Utils/magic_enum/magic_enum.hpp>
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/String.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -44,6 +44,7 @@ namespace Fig
|
||||
Catch, // catch
|
||||
Throw, // throw
|
||||
Finally, // finally
|
||||
As, // as
|
||||
|
||||
// TypeNull, // Null
|
||||
// TypeInt, // Int
|
||||
@@ -53,7 +54,7 @@ namespace Fig
|
||||
|
||||
/* Literal Types (not keyword)*/
|
||||
LiteralNumber, // number (int,float...)
|
||||
LiteralString, // FString
|
||||
LiteralString, // String
|
||||
LiteralBool, // bool (true/false)
|
||||
LiteralNull, // null (Null unique instance)
|
||||
|
||||
@@ -120,56 +121,40 @@ namespace Fig
|
||||
friend bool operator==(const Token &l, const Token &r);
|
||||
|
||||
private:
|
||||
FString value;
|
||||
String value;
|
||||
TokenType type;
|
||||
|
||||
public:
|
||||
size_t line, column;
|
||||
|
||||
inline Token() {};
|
||||
inline Token(const FString &_value, TokenType _type) :
|
||||
value(_value), type(_type) {}
|
||||
inline Token(const FString &_value, TokenType _type, size_t _line, size_t _column) :
|
||||
value(_value), type(_type)
|
||||
inline Token(const String &_value, TokenType _type) : value(_value), type(_type) {}
|
||||
inline Token(const String &_value, TokenType _type, size_t _line, size_t _column) : value(_value), type(_type)
|
||||
{
|
||||
line = _line;
|
||||
column = _column;
|
||||
}
|
||||
const Token& setPos(size_t _line, size_t _column)
|
||||
const Token &setPos(size_t _line, size_t _column)
|
||||
{
|
||||
line = _line;
|
||||
column = _column;
|
||||
return *this;
|
||||
}
|
||||
size_t getLength()
|
||||
size_t getLength() { return value.length(); }
|
||||
const String &getValue() const { return value; }
|
||||
inline String toString() const
|
||||
{
|
||||
return value.length();
|
||||
}
|
||||
const FString& getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
inline FString toString() const
|
||||
{
|
||||
return FString(std::format(
|
||||
"Token('{}',{})",
|
||||
this->value.toBasicString(),
|
||||
magic_enum::enum_name(type)));
|
||||
return String(std::format("Token('{}',{})", this->value.toBasicString(), magic_enum::enum_name(type)));
|
||||
}
|
||||
|
||||
bool isIdentifier()
|
||||
{
|
||||
return type == TokenType::Identifier;
|
||||
}
|
||||
bool isIdentifier() { return type == TokenType::Identifier; }
|
||||
bool isLiteral()
|
||||
{
|
||||
return type == TokenType::LiteralNull || type == TokenType::LiteralBool || type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||
return type == TokenType::LiteralNull || type == TokenType::LiteralBool || type == TokenType::LiteralNumber
|
||||
|| type == TokenType::LiteralString;
|
||||
}
|
||||
|
||||
TokenType getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
TokenType getType() const { return type; }
|
||||
};
|
||||
|
||||
inline bool operator==(const Token &l, const Token &r)
|
||||
@@ -177,6 +162,6 @@ namespace Fig
|
||||
return l.type == r.type and l.value == r.value;
|
||||
}
|
||||
|
||||
static Token IllegalTok(u8"ILLEGAL", TokenType::Illegal);
|
||||
static Token EOFTok(u8"EOF", TokenType::EndOfFile);
|
||||
static Token IllegalTok(U"ILLEGAL", TokenType::Illegal);
|
||||
static Token EOFTok(U"EOF", TokenType::EndOfFile);
|
||||
} // namespace Fig
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user