forked from PuqiAR/Fig-TreeWalker
v0.3.1
This commit is contained in:
1
.build_session
Normal file
1
.build_session
Normal file
@@ -0,0 +1 @@
|
|||||||
|
20251216111638
|
||||||
1
.build_timestamp
Normal file
1
.build_timestamp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2025-12-16 11:16:38
|
||||||
214
.clang-format
Normal file
214
.clang-format
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
|
||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: LLVM
|
||||||
|
|
||||||
|
# 访问说明符(public、private等)的偏移
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
|
||||||
|
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
|
||||||
|
# 连续赋值时,对齐所有等号
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
|
||||||
|
# 连续声明时,对齐所有声明的变量名
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
|
||||||
|
# 右对齐逃脱换行(使用反斜杠换行)的反斜杠
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
|
||||||
|
# 水平对齐二元和三元表达式的操作数
|
||||||
|
AlignOperands: true
|
||||||
|
|
||||||
|
# 对齐连续的尾随的注释
|
||||||
|
AlignTrailingComments: true
|
||||||
|
|
||||||
|
# 允许函数声明的所有参数在放在下一行
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
|
||||||
|
# 允许短的块放在同一行
|
||||||
|
AllowShortBlocksOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的case标签放在同一行
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
|
||||||
|
# 允许短的if语句保持在同一行
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
|
||||||
|
# 允许短的循环保持在同一行
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
|
||||||
|
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
|
||||||
|
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
|
||||||
|
# 总是在多行string字面量前换行
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
|
||||||
|
# 总是在template声明后换行
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
|
||||||
|
# false表示函数实参要么都在同一行,要么都各自一行
|
||||||
|
BinPackArguments: true
|
||||||
|
|
||||||
|
# false表示所有形参要么都在同一行,要么都各自一行
|
||||||
|
BinPackParameters: true
|
||||||
|
|
||||||
|
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
|
||||||
|
BraceWrapping:
|
||||||
|
# class定义后面
|
||||||
|
AfterClass: true
|
||||||
|
# 控制语句后面
|
||||||
|
AfterControlStatement: true
|
||||||
|
# enum定义后面
|
||||||
|
AfterEnum: true
|
||||||
|
# 函数定义后面
|
||||||
|
AfterFunction: true
|
||||||
|
# 命名空间定义后面
|
||||||
|
AfterNamespace: true
|
||||||
|
# struct定义后面
|
||||||
|
AfterStruct: true
|
||||||
|
# union定义后面
|
||||||
|
AfterUnion: true
|
||||||
|
# extern之后
|
||||||
|
AfterExternBlock: false
|
||||||
|
# catch之前
|
||||||
|
BeforeCatch: true
|
||||||
|
# else之前
|
||||||
|
BeforeElse: true
|
||||||
|
# 缩进大括号
|
||||||
|
IndentBraces: false
|
||||||
|
# 分离空函数
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
# 分离空语句
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
# 分离空命名空间
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
|
||||||
|
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
|
||||||
|
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
|
||||||
|
# Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
|
||||||
|
# Allman(总是在大括号前换行), GNU(总是在大括号前换行,并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
|
||||||
|
# 注:这里认为语句块也属于函数
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
|
||||||
|
# 在三元运算符前换行
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
|
||||||
|
# 在构造函数的初始化列表的冒号后换行
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
|
||||||
|
#BreakInheritanceList: AfterColon
|
||||||
|
|
||||||
|
BreakStringLiterals: false
|
||||||
|
|
||||||
|
# 每行字符的限制,0表示没有限制
|
||||||
|
ColumnLimit: 0
|
||||||
|
|
||||||
|
CompactNamespaces: true
|
||||||
|
|
||||||
|
# 构造函数的初始化列表要么都在同一行,要么都各自一行
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
|
||||||
|
# 构造函数的初始化列表的缩进宽度
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
|
||||||
|
# 延续的行的缩进宽度
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
|
||||||
|
# 去除C++11的列表初始化的大括号{后和}前的空格
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
|
||||||
|
# 继承最常用的指针和引用的对齐方式
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
|
||||||
|
# 固定命名空间注释
|
||||||
|
FixNamespaceComments: true
|
||||||
|
|
||||||
|
# 缩进case标签
|
||||||
|
IndentCaseLabels: true
|
||||||
|
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
|
||||||
|
# 缩进宽度
|
||||||
|
IndentWidth: 4
|
||||||
|
|
||||||
|
# 函数返回类型换行时,缩进函数声明或函数定义的函数名
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
|
||||||
|
# 保留在块开始处的空行
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
|
||||||
|
# 连续空行的最大数量
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
|
||||||
|
# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
|
||||||
|
NamespaceIndentation: All
|
||||||
|
|
||||||
|
# 指针和引用的对齐: Left, Right, Middle
|
||||||
|
PointerAlignment: Right
|
||||||
|
|
||||||
|
# 允许重新排版注释
|
||||||
|
ReflowComments: true
|
||||||
|
|
||||||
|
# 允许排序#include
|
||||||
|
SortIncludes: false
|
||||||
|
|
||||||
|
# 允许排序 using 声明
|
||||||
|
SortUsingDeclarations: false
|
||||||
|
|
||||||
|
# 在C风格类型转换后添加空格
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
# true -> (int) 0.1 false-> (int)0.1
|
||||||
|
|
||||||
|
# 在Template 关键字后面添加空格
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
|
||||||
|
# 在赋值运算符之前添加空格
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
|
||||||
|
# SpaceBeforeCpp11BracedList: true
|
||||||
|
|
||||||
|
# SpaceBeforeCtorInitializerColon: true
|
||||||
|
|
||||||
|
# SpaceBeforeInheritanceColon: true
|
||||||
|
|
||||||
|
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
|
||||||
|
# SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
|
||||||
|
# 在空的圆括号中添加空格
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
|
||||||
|
# 在尾随的评论前添加的空格数(只适用于//)
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
|
||||||
|
# 在尖括号的<后和>前添加空格
|
||||||
|
SpacesInAngles: false
|
||||||
|
|
||||||
|
# 在C风格类型转换的括号中添加空格
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
|
||||||
|
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
|
||||||
|
SpacesInContainerLiterals: true
|
||||||
|
|
||||||
|
# 在圆括号的(后和)前添加空格
|
||||||
|
SpacesInParentheses: false
|
||||||
|
|
||||||
|
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
|
||||||
|
# 标准: Cpp03, Cpp11, Auto
|
||||||
|
Standard: Auto
|
||||||
|
|
||||||
|
# tab宽度
|
||||||
|
TabWidth: 4
|
||||||
|
|
||||||
|
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
|
||||||
|
UseTab: Never
|
||||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Xmake cache
|
||||||
|
.xmake/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# MacOS Cache
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.VSCodeCounter
|
||||||
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
11
Tools/SpeedTest/fib.fig
Normal file
11
Tools/SpeedTest/fib.fig
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fun fib(x:Int) -> Int
|
||||||
|
{
|
||||||
|
if (x <= 1)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result := fib(25);
|
||||||
|
__fstdout_println("result: ", result);
|
||||||
11
Tools/SpeedTest/fib.py
Normal file
11
Tools/SpeedTest/fib.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from time import time as tt
|
||||||
|
|
||||||
|
def fib(x:int) -> int:
|
||||||
|
if x <= 1: return x;
|
||||||
|
return fib(x-1) + fib(x-2)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
t0 = tt()
|
||||||
|
result = fib(25)
|
||||||
|
t1 = tt()
|
||||||
|
print('cost: ',t1-t0, 'result:', result)
|
||||||
24
Tools/SpeedTest/fibLoopTest.fig
Normal file
24
Tools/SpeedTest/fibLoopTest.fig
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
var callCnt:Int = 0;
|
||||||
|
fun fib(x:Int) -> Int
|
||||||
|
{
|
||||||
|
callCnt = callCnt + 1;
|
||||||
|
if (x <= 1)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
return fib(x-1) + fib(x-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fibx:Int;
|
||||||
|
__fstdout_print("input an index of fib ");
|
||||||
|
fibx = __fvalue_int_parse(__fstdin_read());
|
||||||
|
|
||||||
|
var cnt:Int = 0;
|
||||||
|
__fstdout_println("test forever");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
cnt = cnt + 1;
|
||||||
|
__fstdout_println("test ", cnt,",result: ", fib(fibx));
|
||||||
|
__fstdout_println("func `fib` called ", callCnt);
|
||||||
|
callCnt = 0;
|
||||||
|
}
|
||||||
4
Tools/VscodeExtension/fig-languague-syntax/.vscodeignore
Normal file
4
Tools/VscodeExtension/fig-languague-syntax/.vscodeignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.vscode/**
|
||||||
|
.vscode-test/**
|
||||||
|
.gitignore
|
||||||
|
vsc-extension-quickstart.md
|
||||||
9
Tools/VscodeExtension/fig-languague-syntax/CHANGELOG.md
Normal file
9
Tools/VscodeExtension/fig-languague-syntax/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to the "fig-languague-syntax" extension will be documented in this file.
|
||||||
|
|
||||||
|
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Initial release
|
||||||
3
Tools/VscodeExtension/fig-languague-syntax/README.md
Normal file
3
Tools/VscodeExtension/fig-languague-syntax/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# fig-languague-syntax README
|
||||||
|
|
||||||
|
Nothing
|
||||||
Binary file not shown.
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"comments": {
|
||||||
|
"lineComment": "//",
|
||||||
|
"blockComment": [
|
||||||
|
"/*",
|
||||||
|
"*/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"brackets": [
|
||||||
|
[
|
||||||
|
"{",
|
||||||
|
"}"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"[",
|
||||||
|
"]"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"(",
|
||||||
|
")"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"\"",
|
||||||
|
"\""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/\"",
|
||||||
|
"\"/"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"autoClosingPairs": [
|
||||||
|
{
|
||||||
|
"open": "{",
|
||||||
|
"close": "}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"open": "[",
|
||||||
|
"close": "]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"open": "(",
|
||||||
|
"close": ")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"open": "\"",
|
||||||
|
"close": "\"",
|
||||||
|
"notIn": [
|
||||||
|
"string"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"open": "/\"",
|
||||||
|
"close": "\"/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"surroundingPairs": [
|
||||||
|
[
|
||||||
|
"{",
|
||||||
|
"}"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"[",
|
||||||
|
"]"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"(",
|
||||||
|
")"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"\"",
|
||||||
|
"\""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/\"",
|
||||||
|
"\"/"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"indentationRules": {
|
||||||
|
"increaseIndentPattern": "^(?=.*(\\{|\\()|.*/\\\").*$",
|
||||||
|
"decreaseIndentPattern": "^\\s*[}\\)]|\\s*\"/"
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Tools/VscodeExtension/fig-languague-syntax/package.json
Normal file
33
Tools/VscodeExtension/fig-languague-syntax/package.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "fig-languague-syntax",
|
||||||
|
"displayName": "Fig Languague Syntax",
|
||||||
|
"description": ":)",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"engines": {
|
||||||
|
"vscode": "^1.96.0"
|
||||||
|
},
|
||||||
|
"categories": [
|
||||||
|
"Programming Languages"
|
||||||
|
],
|
||||||
|
"contributes": {
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"id": "fig",
|
||||||
|
"extensions": [
|
||||||
|
".fl"
|
||||||
|
],
|
||||||
|
"aliases": [
|
||||||
|
"Fig"
|
||||||
|
],
|
||||||
|
"configuration": "./language-configuration.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grammars": [
|
||||||
|
{
|
||||||
|
"language": "fig",
|
||||||
|
"scopeName": "source.fig",
|
||||||
|
"path": "./syntaxes/fig.tmLanguage.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||||
|
"name": "Fig",
|
||||||
|
"scopeName": "source.fig",
|
||||||
|
"fileTypes": [
|
||||||
|
".fl"
|
||||||
|
],
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "comment.line.double-slash",
|
||||||
|
"match": "//.*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "comment.block",
|
||||||
|
"begin": "/\\*",
|
||||||
|
"end": "\\*/",
|
||||||
|
"captures": {
|
||||||
|
"0": {
|
||||||
|
"name": "comment.block.fig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.control",
|
||||||
|
"match": "\\b(if|else|for|while|continue|break|return|or|not)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.declaration",
|
||||||
|
"match": "\\b(var|val|func|module)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "storage.type",
|
||||||
|
"match": "\\b(Int32|Int64|Float|Double|Map|Bool|Null|String)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "entity.name.function",
|
||||||
|
"match": "(?<=\\bfunc\\b)\\s+\\w+"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator.arrow",
|
||||||
|
"match": "->"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "keyword.operator",
|
||||||
|
"match": "[+\\-*/%&|><!]=?|&&|\\|\\||=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.quoted.double",
|
||||||
|
"begin": "\"",
|
||||||
|
"end": "\"",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "constant.character.escape",
|
||||||
|
"match": "\\\\."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.quoted.multiline",
|
||||||
|
"begin": "\\/\\\"",
|
||||||
|
"end": "\\\"/",
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"name": "constant.character.escape",
|
||||||
|
"match": "\\\\."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "constant.numeric.integer",
|
||||||
|
"match": "\\b\\d+\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "constant.numeric.float",
|
||||||
|
"match": "\\b\\d+\\.\\d+([eE][+-]?\\d+)?[f]?\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "punctuation.curly",
|
||||||
|
"match": "[{}]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "punctuation.parenthesis",
|
||||||
|
"match": "[()]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "punctuation.square",
|
||||||
|
"match": "[\\[\\]]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Welcome to your VS Code Extension
|
||||||
|
|
||||||
|
## What's in the folder
|
||||||
|
|
||||||
|
* This folder contains all of the files necessary for your extension.
|
||||||
|
* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.
|
||||||
|
* `syntaxes/figlang.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization.
|
||||||
|
* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets.
|
||||||
|
|
||||||
|
## Get up and running straight away
|
||||||
|
|
||||||
|
* Make sure the language configuration settings in `language-configuration.json` are accurate.
|
||||||
|
* Press `F5` to open a new window with your extension loaded.
|
||||||
|
* Create a new file with a file name suffix matching your language.
|
||||||
|
* Verify that syntax highlighting works and that the language configuration settings are working.
|
||||||
|
|
||||||
|
## Make changes
|
||||||
|
|
||||||
|
* You can relaunch the extension from the debug toolbar after making changes to the files listed above.
|
||||||
|
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||||
|
|
||||||
|
## Add more language features
|
||||||
|
|
||||||
|
* To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs
|
||||||
|
|
||||||
|
## Install your extension
|
||||||
|
|
||||||
|
* To start using your extension with Visual Studio Code copy it into the `<user home>/.vscode/extensions` folder and restart Code.
|
||||||
|
* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.
|
||||||
3
compile_flags.txt
Normal file
3
compile_flags.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-std=c++2b
|
||||||
|
-static
|
||||||
|
-stdlib=libc++
|
||||||
248
docs/FigDesignDocument.md
Normal file
248
docs/FigDesignDocument.md
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
## `Fig Programming Language` <font face="Consolas"> DESIGN DOC </font>
|
||||||
|
|
||||||
|
---
|
||||||
|
### 关键词解释 Token
|
||||||
|
|
||||||
|
``` cpp
|
||||||
|
enum class TokenType : int8_t
|
||||||
|
{
|
||||||
|
Illegal = -1,
|
||||||
|
EndOfFile = 0,
|
||||||
|
|
||||||
|
Comments,
|
||||||
|
|
||||||
|
Identifier,
|
||||||
|
|
||||||
|
/* Keywords */
|
||||||
|
And, // and
|
||||||
|
Or, // or
|
||||||
|
Not, // not
|
||||||
|
Import, // import
|
||||||
|
Function, // fun
|
||||||
|
Variable, // var
|
||||||
|
Const, // const
|
||||||
|
Final, // final
|
||||||
|
While, // while
|
||||||
|
For, // for
|
||||||
|
Struct, // struct
|
||||||
|
Interface, // interface
|
||||||
|
Implement, // implement
|
||||||
|
Public, // public
|
||||||
|
|
||||||
|
// TypeNull, // Null
|
||||||
|
// TypeInt, // Int
|
||||||
|
// TypeString, // String
|
||||||
|
// TypeBool, // Bool
|
||||||
|
// TypeDouble, // Double
|
||||||
|
|
||||||
|
/* Literal Types (not keyword)*/
|
||||||
|
LiteralNumber, // number (int,float...)
|
||||||
|
LiteralString, // FString
|
||||||
|
LiteralBool, // bool (true/false)
|
||||||
|
LiteralNull, // null (Null的唯一实例)
|
||||||
|
|
||||||
|
/* Punct */
|
||||||
|
Plus, // +
|
||||||
|
Minus, // -
|
||||||
|
Asterisk, // *
|
||||||
|
Slash, // /
|
||||||
|
Percent, // %
|
||||||
|
Caret, // ^
|
||||||
|
Ampersand, // &
|
||||||
|
Pipe, // |
|
||||||
|
Tilde, // ~
|
||||||
|
ShiftLeft, // <<
|
||||||
|
ShiftRight, // >>
|
||||||
|
// Exclamation, // !
|
||||||
|
Question, // ?
|
||||||
|
Assign, // =
|
||||||
|
Less, // <
|
||||||
|
Greater, // >
|
||||||
|
Dot, // .
|
||||||
|
Comma, // ,
|
||||||
|
Colon, // :
|
||||||
|
Semicolon, // ;
|
||||||
|
SingleQuote, // '
|
||||||
|
DoubleQuote, // "
|
||||||
|
// Backtick, // `
|
||||||
|
// At, // @
|
||||||
|
// Hash, // #
|
||||||
|
// Dollar, // $
|
||||||
|
// Backslash, // '\'
|
||||||
|
// Underscore, // _
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftBracket, // [
|
||||||
|
RightBracket, // ]
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
// LeftArrow, // <-
|
||||||
|
RightArrow, // ->
|
||||||
|
// DoubleArrow, // =>
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
LessEqual, // <=
|
||||||
|
GreaterEqual, // >=
|
||||||
|
PlusEqual, // +=
|
||||||
|
MinusEqual, // -=
|
||||||
|
AsteriskEqual, // *=
|
||||||
|
SlashEqual, // /=
|
||||||
|
PercentEqual, // %=
|
||||||
|
CaretEqual, // ^=
|
||||||
|
DoublePlus, // ++
|
||||||
|
DoubleMinus, // --
|
||||||
|
DoubleAmpersand, // &&
|
||||||
|
DoublePipe, // ||
|
||||||
|
Walrus, // :=
|
||||||
|
Power, // **
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Illegal`
|
||||||
|
非法Token:无法解析或语法错误
|
||||||
|
|
||||||
|
* `EndOfFile`
|
||||||
|
即:
|
||||||
|
```cpp
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
文件终止符
|
||||||
|
|
||||||
|
* `Comments`
|
||||||
|
注释Token,包括单行和多行
|
||||||
|
|
||||||
|
* `Identifier`
|
||||||
|
标识符,用户定义的‘名字’
|
||||||
|
|
||||||
|
* `And` -> `&&` 或 `and`
|
||||||
|
逻辑与
|
||||||
|
|
||||||
|
* `Or` -> `||` 或 `or`
|
||||||
|
逻辑或
|
||||||
|
|
||||||
|
* `Not` -> `!` 或 `!`
|
||||||
|
逻辑非
|
||||||
|
|
||||||
|
* `Import` -> `import`
|
||||||
|
导入关键字,用于导入包。 e.g
|
||||||
|
``` python
|
||||||
|
import std.io
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Function` -> `function`
|
||||||
|
定义函数,匿名也可
|
||||||
|
``` javascript
|
||||||
|
function greeting() -> Null public
|
||||||
|
{
|
||||||
|
std.io.println("Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function intAdder() -> Function public
|
||||||
|
{
|
||||||
|
return function(n1: Int, n2: Int) => n1 + n2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
此处的 `public` 为公开标识
|
||||||
|
不进行显示声明 `public` 默认为私有,即对象仅能在当前作用域访问
|
||||||
|
|
||||||
|
* `Variable` -> `var`
|
||||||
|
定义变量
|
||||||
|
``` dart
|
||||||
|
var foobar;
|
||||||
|
var defaultVal = 1145;
|
||||||
|
var numberSpecific: Int;
|
||||||
|
var numberDefault: Int = 91;
|
||||||
|
|
||||||
|
foobar = "hello, world!";
|
||||||
|
foobar = 13;
|
||||||
|
|
||||||
|
defaultVal = "it can be any value";
|
||||||
|
|
||||||
|
numberSpecific = 78;
|
||||||
|
numberDefault = 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Const` -> `const`
|
||||||
|
定义`全过程`常量: 从词法分析到求值器内的生命周期都为常量,仅能**在生命周期内**赋值一次,使用时也只有一个唯一对象
|
||||||
|
|
||||||
|
必须在源码中指定值
|
||||||
|
|
||||||
|
``` dart
|
||||||
|
const Pi = 3.1415926; // recommended
|
||||||
|
|
||||||
|
const name; // ❌ 错误
|
||||||
|
```
|
||||||
|
|
||||||
|
定义后的常量,其值及类型均不可改变,故可省略类型标识。这是推荐的写法
|
||||||
|
同时,也可作为结构体成员的修饰
|
||||||
|
``` cpp
|
||||||
|
struct MathConstants
|
||||||
|
{
|
||||||
|
const Pi = 3.1415926;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Final` -> `final`
|
||||||
|
定义`结构体运行时`常量:从运行期开始的常量,仅能**在运行时**被赋值一次, **仅修饰结构体成员**
|
||||||
|
不存在 **final** 类型的外部常量
|
||||||
|
|
||||||
|
定义后的常量,其值及类型均不可改变,故可省略类型标识。这是推荐的写法
|
||||||
|
``` cpp
|
||||||
|
struct Person
|
||||||
|
{
|
||||||
|
final name: String
|
||||||
|
final age: Int
|
||||||
|
|
||||||
|
final sex: String = "gender" // ❌ 请使用 const 代替
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `While` -> `while`
|
||||||
|
while循环,满足一个布尔类型条件循环执行语句
|
||||||
|
|
||||||
|
``` cpp
|
||||||
|
while (ans != 27.19236)
|
||||||
|
{
|
||||||
|
ans = Int.parse(std.io.readline());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `For` -> `for`
|
||||||
|
for循环,拥有初始语句、条件、增长语句
|
||||||
|
``` cpp
|
||||||
|
for (init; condition; increment)
|
||||||
|
{
|
||||||
|
statements...;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* `Struct` -> `struct`
|
||||||
|
结构体,面对对象
|
||||||
|
|
||||||
|
``` cpp
|
||||||
|
struct Person
|
||||||
|
{
|
||||||
|
public final name: String; // public, final
|
||||||
|
public age: Int; // public
|
||||||
|
sex: String; // private normally;
|
||||||
|
|
||||||
|
const ADULT_AGE = 18; // private, const
|
||||||
|
|
||||||
|
fun printInfo()
|
||||||
|
{
|
||||||
|
std.io.println("name: {}, age: {}, sex: {}", name, age, sex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var person = Person {"Fig", 1, "IDK"};
|
||||||
|
// or
|
||||||
|
var person = Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
|
||||||
|
|
||||||
|
var name = "Fig";
|
||||||
|
var age = 1;
|
||||||
|
var sex = "IDK";
|
||||||
|
|
||||||
|
var person = Person {name, age, sex};
|
||||||
|
// = `var person = Person {name: name, age: age, sex: sex}`
|
||||||
|
```
|
||||||
|
|
||||||
14
include/Ast/AccessModifier.hpp
Normal file
14
include/Ast/AccessModifier.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class AccessModifier
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Const,
|
||||||
|
Final,
|
||||||
|
Public,
|
||||||
|
PublicConst,
|
||||||
|
PublicFinal,
|
||||||
|
};
|
||||||
|
};
|
||||||
29
include/Ast/BinaryExpr.hpp
Normal file
29
include/Ast/BinaryExpr.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class BinaryExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Operator op;
|
||||||
|
Expression lexp, rexp;
|
||||||
|
|
||||||
|
BinaryExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::BinaryExpr;
|
||||||
|
}
|
||||||
|
BinaryExprAst(Expression _lexp, Operator _op, Expression _rexp)
|
||||||
|
{
|
||||||
|
type = AstType::BinaryExpr;
|
||||||
|
|
||||||
|
lexp = _lexp;
|
||||||
|
op = _op;
|
||||||
|
rexp = _rexp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using BinaryExpr = std::shared_ptr<BinaryExprAst>;
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
65
include/Ast/ContainerInitExprs.hpp
Normal file
65
include/Ast/ContainerInitExprs.hpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
// Container Data Types --- Tuple/List/Map...
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class ListExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<Expression> val;
|
||||||
|
|
||||||
|
ListExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::ListExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListExprAst(std::vector<Expression> _val) :
|
||||||
|
val(std::move(_val))
|
||||||
|
{
|
||||||
|
type = AstType::ListExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ListExpr = std::shared_ptr<ListExprAst>;
|
||||||
|
|
||||||
|
class TupleExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<Expression> val;
|
||||||
|
|
||||||
|
TupleExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::TupleExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TupleExprAst(std::vector<Expression> _val) :
|
||||||
|
val(std::move(_val))
|
||||||
|
{
|
||||||
|
type = AstType::TupleExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using TupleExpr = std::shared_ptr<TupleExprAst>;
|
||||||
|
|
||||||
|
class MapExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::map<FString, Expression> val;
|
||||||
|
|
||||||
|
MapExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::MapExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapExprAst(std::map<FString, Expression> _val) :
|
||||||
|
val(std::move(_val))
|
||||||
|
{
|
||||||
|
type = AstType::MapExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using MapExpr = std::shared_ptr<MapExprAst>;
|
||||||
|
}; // namespace Fig::Ast
|
||||||
46
include/Ast/ControlSt.hpp
Normal file
46
include/Ast/ControlSt.hpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class ReturnSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression retValue;
|
||||||
|
|
||||||
|
ReturnSt()
|
||||||
|
{
|
||||||
|
type = AstType::ReturnSt;
|
||||||
|
}
|
||||||
|
ReturnSt(Expression _retValue) :
|
||||||
|
retValue(_retValue)
|
||||||
|
{
|
||||||
|
type = AstType::ReturnSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Return = std::shared_ptr<ReturnSt>;
|
||||||
|
|
||||||
|
class BreakSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BreakSt()
|
||||||
|
{
|
||||||
|
type = AstType::BreakSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Break = std::shared_ptr<BreakSt>;
|
||||||
|
|
||||||
|
class ContinueSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContinueSt()
|
||||||
|
{
|
||||||
|
type = AstType::ContinueSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Continue = std::shared_ptr<ContinueSt>;
|
||||||
|
};
|
||||||
21
include/Ast/ExpressionStmt.hpp
Normal file
21
include/Ast/ExpressionStmt.hpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class ExpressionStmtAst final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression exp;
|
||||||
|
ExpressionStmtAst()
|
||||||
|
{
|
||||||
|
type = AstType::ExpressionStmt;
|
||||||
|
}
|
||||||
|
ExpressionStmtAst(Expression _exp) : exp(std::move(_exp))
|
||||||
|
{
|
||||||
|
type = AstType::ExpressionStmt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ExpressionStmt = std::shared_ptr<ExpressionStmtAst>;
|
||||||
|
}
|
||||||
54
include/Ast/FunctionCall.hpp
Normal file
54
include/Ast/FunctionCall.hpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// include/Ast/FunctionCall.hpp
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <value.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
struct FunctionArguments
|
||||||
|
{
|
||||||
|
std::vector<Expression> argv;
|
||||||
|
size_t getLength() const { return argv.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionCallArgs final
|
||||||
|
{
|
||||||
|
std::vector<Value> argv;
|
||||||
|
size_t getLength() const { return argv.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionCallExpr final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FString name;
|
||||||
|
FunctionArguments arg;
|
||||||
|
|
||||||
|
FunctionCallExpr()
|
||||||
|
{
|
||||||
|
type = AstType::FunctionCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionCallExpr(FString _name, FunctionArguments _arg) :
|
||||||
|
name(std::move(_name)), arg(std::move(_arg))
|
||||||
|
{
|
||||||
|
type = AstType::FunctionCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() override
|
||||||
|
{
|
||||||
|
FString s = name;
|
||||||
|
s += u8"(";
|
||||||
|
for (size_t i = 0; i < arg.argv.size(); ++i)
|
||||||
|
{
|
||||||
|
s += arg.argv[i]->toString();
|
||||||
|
if (i + 1 < arg.argv.size())
|
||||||
|
s += u8", ";
|
||||||
|
}
|
||||||
|
s += u8")";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using FunctionCall = std::shared_ptr<FunctionCallExpr>;
|
||||||
|
}; // namespace Fig
|
||||||
46
include/Ast/FunctionDefSt.hpp
Normal file
46
include/Ast/FunctionDefSt.hpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Ast/functionParameters.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <value.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
fun greet(greeting, name:String, age:Int, split:String=":") public -> Null
|
||||||
|
{
|
||||||
|
io.println("{}, {}{}{}", greeting, name, split, age);
|
||||||
|
}
|
||||||
|
|
||||||
|
`greeting`, `name`, `age` -> positional parameters
|
||||||
|
`split` -> default parameter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FunctionDefSt final : public StatementAst // for define
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FString name;
|
||||||
|
FunctionParameters paras;
|
||||||
|
bool isPublic;
|
||||||
|
FString retType;
|
||||||
|
BlockStatement body;
|
||||||
|
FunctionDefSt() :
|
||||||
|
retType(ValueType::Null.name)
|
||||||
|
{
|
||||||
|
type = AstType::FunctionDefSt;
|
||||||
|
}
|
||||||
|
FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, FString _retType, BlockStatement _body)
|
||||||
|
{
|
||||||
|
type = AstType::FunctionDefSt;
|
||||||
|
|
||||||
|
name = std::move(_name);
|
||||||
|
paras = std::move(_paras);
|
||||||
|
isPublic = _isPublic;
|
||||||
|
retType = std::move(_retType);
|
||||||
|
body = std::move(_body);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using FunctionDef = std::shared_ptr<FunctionDefSt>;
|
||||||
|
}; // namespace Fig
|
||||||
68
include/Ast/IfSt.hpp
Normal file
68
include/Ast/IfSt.hpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class ElseSt final : public StatementAst
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using Else = std::shared_ptr<ElseSt>;
|
||||||
|
class ElseIfSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression condition;
|
||||||
|
BlockStatement body;
|
||||||
|
ElseIfSt()
|
||||||
|
{
|
||||||
|
type = AstType::ElseIfSt;
|
||||||
|
}
|
||||||
|
ElseIfSt(Expression _condition,
|
||||||
|
BlockStatement _body) :
|
||||||
|
condition(_condition), body(_body)
|
||||||
|
{
|
||||||
|
type = AstType::ElseIfSt;
|
||||||
|
}
|
||||||
|
virtual FString toString() override
|
||||||
|
{
|
||||||
|
return FString(std::format("<ElseIf Ast at {}:{}>", aai.line, aai.column));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ElseIf = std::shared_ptr<ElseIfSt>;
|
||||||
|
class IfSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
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
|
||||||
0
include/Ast/ImplementSt.hpp
Normal file
0
include/Ast/ImplementSt.hpp
Normal file
28
include/Ast/InitExpr.hpp
Normal file
28
include/Ast/InitExpr.hpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class InitExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FString structName;
|
||||||
|
|
||||||
|
std::vector<std::pair<FString, Expression>> args;
|
||||||
|
|
||||||
|
InitExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::InitExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitExprAst(FString _structName, std::vector<std::pair<FString, Expression>> _args) :
|
||||||
|
structName(std::move(_structName)), args(std::move(_args))
|
||||||
|
{
|
||||||
|
type = AstType::InitExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using InitExpr = std::shared_ptr<InitExprAst>;
|
||||||
|
|
||||||
|
}; // namespace Fig::Ast
|
||||||
43
include/Ast/LambdaExpr.hpp
Normal file
43
include/Ast/LambdaExpr.hpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Ast/functionParameters.hpp>
|
||||||
|
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class LambdaExprAst : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
Lambda:
|
||||||
|
fun (greeting) -> Null {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
FunctionParameters paras;
|
||||||
|
FString retType;
|
||||||
|
BlockStatement body;
|
||||||
|
LambdaExprAst() :
|
||||||
|
retType(ValueType::Null.name)
|
||||||
|
{
|
||||||
|
type = AstType::LambdaExpr;
|
||||||
|
}
|
||||||
|
LambdaExprAst(FunctionParameters _paras, FString _retType, BlockStatement _body) :
|
||||||
|
retType(ValueType::Null.name)
|
||||||
|
{
|
||||||
|
paras = std::move(_paras);
|
||||||
|
retType = std::move(_retType);
|
||||||
|
body = std::move(_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString typeName() override
|
||||||
|
{
|
||||||
|
return FString(std::format("LambdaExprAst<{}>", retType.toBasicString()));
|
||||||
|
}
|
||||||
|
virtual ~LambdaExprAst() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
using LambdaExpr = std::shared_ptr<LambdaExprAst>;
|
||||||
|
}; // namespace Fig
|
||||||
45
include/Ast/StructDefSt.hpp
Normal file
45
include/Ast/StructDefSt.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <Ast/AccessModifier.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
|
||||||
|
struct StructDefField
|
||||||
|
{
|
||||||
|
AccessModifier am;
|
||||||
|
FString fieldName;
|
||||||
|
FString tiName;
|
||||||
|
Expression defaultValueExpr;
|
||||||
|
|
||||||
|
StructDefField() {}
|
||||||
|
StructDefField(AccessModifier _am, FString _fieldName, FString _tiName, Expression _defaultValueExpr) :
|
||||||
|
am(std::move(_am)), fieldName(std::move(_fieldName)), tiName(std::move(_tiName)), defaultValueExpr(std::move(_defaultValueExpr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class StructDefSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isPublic;
|
||||||
|
const FString 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) :
|
||||||
|
isPublic(std::move(_isPublic)), name(std::move(_name)), fields(std::move(_fields)), body(std::move(_body))
|
||||||
|
{
|
||||||
|
type = AstType::StructSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using StructDef = std::shared_ptr<StructDefSt>;
|
||||||
|
}; // namespace Fig
|
||||||
29
include/Ast/TernaryExpr.hpp
Normal file
29
include/Ast/TernaryExpr.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
// condition ? val_true : val_false
|
||||||
|
class TernaryExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression condition;
|
||||||
|
Expression valueT;
|
||||||
|
Expression valueF;
|
||||||
|
|
||||||
|
TernaryExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::TernaryExpr;
|
||||||
|
}
|
||||||
|
TernaryExprAst(Expression _condition, Expression _valueT, Expression _valueF)
|
||||||
|
{
|
||||||
|
type = AstType::TernaryExpr;
|
||||||
|
|
||||||
|
condition = std::move(_condition);
|
||||||
|
valueT = std::move(_valueT);
|
||||||
|
valueF = std::move(_valueF);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using TernaryExpr = std::shared_ptr<TernaryExprAst>;
|
||||||
|
} // namespace Fig
|
||||||
27
include/Ast/UnaryExpr.hpp
Normal file
27
include/Ast/UnaryExpr.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class UnaryExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Operator op;
|
||||||
|
Expression exp;
|
||||||
|
|
||||||
|
UnaryExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::UnaryExpr;
|
||||||
|
}
|
||||||
|
UnaryExprAst(Operator _op, Expression _exp)
|
||||||
|
{
|
||||||
|
type = AstType::UnaryExpr;
|
||||||
|
|
||||||
|
op = _op;
|
||||||
|
exp = std::move(_exp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using UnaryExpr = std::shared_ptr<UnaryExprAst>;
|
||||||
|
} // namespace Fig
|
||||||
26
include/Ast/ValueExpr.hpp
Normal file
26
include/Ast/ValueExpr.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
#include <value.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class ValueExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Value val;
|
||||||
|
|
||||||
|
ValueExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::ValueExpr;
|
||||||
|
}
|
||||||
|
ValueExprAst(Value _val)
|
||||||
|
{
|
||||||
|
type = AstType::ValueExpr;
|
||||||
|
val = std::move(_val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ValueExpr = std::shared_ptr<ValueExprAst>;
|
||||||
|
};
|
||||||
26
include/Ast/VarAssignSt.hpp
Normal file
26
include/Ast/VarAssignSt.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class VarAssignSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const FString varName;
|
||||||
|
const Expression valueExpr;
|
||||||
|
|
||||||
|
VarAssignSt()
|
||||||
|
{
|
||||||
|
type = AstType::VarAssignSt;
|
||||||
|
}
|
||||||
|
|
||||||
|
VarAssignSt(FString _varName, Expression _valueExpr) :
|
||||||
|
varName(std::move(_varName)), valueExpr(std::move(_valueExpr))
|
||||||
|
{
|
||||||
|
type = AstType::VarAssignSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using VarAssign = std::shared_ptr<VarAssignSt>;
|
||||||
|
}; // namespace Fig
|
||||||
34
include/Ast/VarDef.hpp
Normal file
34
include/Ast/VarDef.hpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class VarDefAst final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool isPublic;
|
||||||
|
bool isConst;
|
||||||
|
FString name;
|
||||||
|
FString typeName;
|
||||||
|
Expression expr;
|
||||||
|
|
||||||
|
VarDefAst() :
|
||||||
|
typeName(ValueType::Any.name)
|
||||||
|
{
|
||||||
|
type = AstType::VarDefSt;
|
||||||
|
}
|
||||||
|
VarDefAst(bool _isPublic, bool _isConst, FString _name, FString _info, Expression _expr) :
|
||||||
|
typeName(std::move(_info))
|
||||||
|
{
|
||||||
|
type = AstType::VarDefSt;
|
||||||
|
isPublic = _isPublic;
|
||||||
|
isConst = _isConst;
|
||||||
|
name = std::move(_name);
|
||||||
|
expr = std::move(_expr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using VarDef = std::shared_ptr<VarDefAst>;
|
||||||
|
} // namespace Fig
|
||||||
24
include/Ast/VarExpr.hpp
Normal file
24
include/Ast/VarExpr.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using VarExpr = std::shared_ptr<VarExprAst>;
|
||||||
|
}; // namespace Fig
|
||||||
26
include/Ast/WhileSt.hpp
Normal file
26
include/Ast/WhileSt.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
class WhileSt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression condition;
|
||||||
|
BlockStatement body;
|
||||||
|
|
||||||
|
WhileSt()
|
||||||
|
{
|
||||||
|
type = AstType::WhileSt;
|
||||||
|
}
|
||||||
|
|
||||||
|
WhileSt(Expression _condition, BlockStatement _body)
|
||||||
|
: condition(_condition), body(_body)
|
||||||
|
{
|
||||||
|
type = AstType::WhileSt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using While = std::shared_ptr<WhileSt>;
|
||||||
|
};
|
||||||
338
include/Ast/astBase.hpp
Normal file
338
include/Ast/astBase.hpp
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <token.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
enum class AstType : uint8_t
|
||||||
|
{
|
||||||
|
/* Base Class */
|
||||||
|
_AstBase,
|
||||||
|
StatementBase,
|
||||||
|
ExpressionBase,
|
||||||
|
/* Expression */
|
||||||
|
ValueExpr,
|
||||||
|
VarExpr,
|
||||||
|
FunctionCall,
|
||||||
|
LambdaExpr,
|
||||||
|
UnaryExpr,
|
||||||
|
BinaryExpr,
|
||||||
|
TernaryExpr,
|
||||||
|
|
||||||
|
ListExpr, // []
|
||||||
|
TupleExpr, // ()
|
||||||
|
MapExpr, // {}
|
||||||
|
InitExpr, // struct{}
|
||||||
|
|
||||||
|
/* Statement */
|
||||||
|
BlockStatement,
|
||||||
|
ExpressionStmt,
|
||||||
|
|
||||||
|
VarDefSt,
|
||||||
|
FunctionDefSt,
|
||||||
|
StructSt,
|
||||||
|
ImplementSt,
|
||||||
|
|
||||||
|
IfSt,
|
||||||
|
ElseSt,
|
||||||
|
ElseIfSt,
|
||||||
|
|
||||||
|
VarAssignSt,
|
||||||
|
WhileSt,
|
||||||
|
ReturnSt,
|
||||||
|
BreakSt,
|
||||||
|
ContinueSt,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_map<AstType, FString> astTypeToString{
|
||||||
|
/* Base Class */
|
||||||
|
{AstType::_AstBase, FString(u8"Ast")},
|
||||||
|
{AstType::StatementBase, FString(u8"Statement")},
|
||||||
|
{AstType::ExpressionBase, FString(u8"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::InitExpr, FString(u8"InitExpr")},
|
||||||
|
|
||||||
|
/* Statement */
|
||||||
|
{AstType::BlockStatement, FString(u8"BlockStatement")},
|
||||||
|
|
||||||
|
{AstType::VarDefSt, FString(u8"VarSt")},
|
||||||
|
{AstType::FunctionDefSt, FString(u8"FunctionDefSt")},
|
||||||
|
{AstType::StructSt, FString(u8"StructSt")},
|
||||||
|
{AstType::ImplementSt, FString(u8"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")},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AstAddressInfo
|
||||||
|
{
|
||||||
|
size_t line, column;
|
||||||
|
};
|
||||||
|
|
||||||
|
class _AstBase
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
AstType type;
|
||||||
|
AstAddressInfo aai;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_AstBase(const _AstBase &) = default;
|
||||||
|
_AstBase(_AstBase &&) = default;
|
||||||
|
|
||||||
|
_AstBase &operator=(const _AstBase &) = default;
|
||||||
|
_AstBase &operator=(_AstBase &&) = default;
|
||||||
|
|
||||||
|
_AstBase() {}
|
||||||
|
|
||||||
|
void setAAI(AstAddressInfo _aai)
|
||||||
|
{
|
||||||
|
aai = std::move(_aai);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString typeName()
|
||||||
|
{
|
||||||
|
return astTypeToString.at(type);
|
||||||
|
}
|
||||||
|
virtual FString toString()
|
||||||
|
{
|
||||||
|
return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||||
|
}
|
||||||
|
|
||||||
|
AstAddressInfo getAAI()
|
||||||
|
{
|
||||||
|
return aai;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstType getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatementAst : public _AstBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using _AstBase::_AstBase;
|
||||||
|
using _AstBase::operator=;
|
||||||
|
StatementAst()
|
||||||
|
{
|
||||||
|
type = AstType::StatementBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() override
|
||||||
|
{
|
||||||
|
return FString(std::format("<Stmt Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class EofStmt final : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EofStmt()
|
||||||
|
{
|
||||||
|
type = AstType::StatementBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() override
|
||||||
|
{
|
||||||
|
return FString(std::format("<EOF Stmt at {}:{}>", aai.line, aai.column));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExpressionAst : public _AstBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using _AstBase::_AstBase;
|
||||||
|
using _AstBase::operator=;
|
||||||
|
ExpressionAst()
|
||||||
|
{
|
||||||
|
type = AstType::ExpressionBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() override
|
||||||
|
{
|
||||||
|
return FString(std::format("<Expr Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
enum class Operator : uint8_t
|
||||||
|
{
|
||||||
|
LeftParen,
|
||||||
|
RightParen,
|
||||||
|
|
||||||
|
// 算术
|
||||||
|
Add, // +
|
||||||
|
Subtract, // -
|
||||||
|
Multiply, // *
|
||||||
|
Divide, // /
|
||||||
|
Modulo, // %
|
||||||
|
Power, // **
|
||||||
|
|
||||||
|
// 逻辑
|
||||||
|
And, // and / &&
|
||||||
|
Or, // or / ||
|
||||||
|
Not, // not / !
|
||||||
|
|
||||||
|
// 比较
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
Less, // <
|
||||||
|
LessEqual, // <=
|
||||||
|
Greater, // >
|
||||||
|
GreaterEqual, // >=
|
||||||
|
|
||||||
|
// 三目
|
||||||
|
TernaryCond,
|
||||||
|
|
||||||
|
// 位运算
|
||||||
|
BitAnd, // &
|
||||||
|
BitOr, // |
|
||||||
|
BitXor, // ^
|
||||||
|
BitNot, // ~
|
||||||
|
ShiftLeft, // <<
|
||||||
|
ShiftRight, // >>
|
||||||
|
|
||||||
|
// 赋值表达式
|
||||||
|
Walrus, // :=
|
||||||
|
|
||||||
|
// 点运算符 .
|
||||||
|
Dot,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::unordered_set<Operator> unaryOps{
|
||||||
|
Operator::Not,
|
||||||
|
Operator::Subtract,
|
||||||
|
Operator::BitNot,
|
||||||
|
};
|
||||||
|
static const std::unordered_set<Operator> binaryOps{
|
||||||
|
Operator::Add,
|
||||||
|
Operator::Subtract,
|
||||||
|
Operator::Multiply,
|
||||||
|
Operator::Divide,
|
||||||
|
Operator::Modulo,
|
||||||
|
Operator::Power,
|
||||||
|
Operator::And,
|
||||||
|
Operator::Or,
|
||||||
|
|
||||||
|
Operator::Equal,
|
||||||
|
Operator::NotEqual,
|
||||||
|
Operator::Less,
|
||||||
|
Operator::LessEqual,
|
||||||
|
Operator::Greater,
|
||||||
|
Operator::GreaterEqual,
|
||||||
|
|
||||||
|
Operator::BitAnd,
|
||||||
|
Operator::BitOr,
|
||||||
|
Operator::BitXor,
|
||||||
|
Operator::BitNot,
|
||||||
|
Operator::ShiftLeft,
|
||||||
|
Operator::ShiftRight,
|
||||||
|
|
||||||
|
Operator::Walrus,
|
||||||
|
Operator::Dot};
|
||||||
|
static const std::unordered_set<Operator> ternaryOps{Operator::TernaryCond};
|
||||||
|
|
||||||
|
static const std::unordered_map<TokenType, Operator> TokenToOp{
|
||||||
|
// 算术
|
||||||
|
{TokenType::Plus, Operator::Add},
|
||||||
|
{TokenType::Minus, Operator::Subtract},
|
||||||
|
{TokenType::Asterisk, Operator::Multiply},
|
||||||
|
{TokenType::Slash, Operator::Divide},
|
||||||
|
{TokenType::Percent, Operator::Modulo},
|
||||||
|
{TokenType::Power, Operator::Power},
|
||||||
|
|
||||||
|
// 逻辑
|
||||||
|
{TokenType::And, Operator::And},
|
||||||
|
{TokenType::DoubleAmpersand, Operator::And},
|
||||||
|
{TokenType::Or, Operator::Or},
|
||||||
|
{TokenType::DoublePipe, Operator::Or},
|
||||||
|
{TokenType::Not, Operator::Not},
|
||||||
|
|
||||||
|
// 比较
|
||||||
|
{TokenType::Equal, Operator::Equal},
|
||||||
|
{TokenType::NotEqual, Operator::NotEqual},
|
||||||
|
{TokenType::Less, Operator::Less},
|
||||||
|
{TokenType::LessEqual, Operator::LessEqual},
|
||||||
|
{TokenType::Greater, Operator::Greater},
|
||||||
|
{TokenType::GreaterEqual, Operator::GreaterEqual},
|
||||||
|
|
||||||
|
// 三目
|
||||||
|
{TokenType::Question, Operator::TernaryCond},
|
||||||
|
|
||||||
|
// 位运算
|
||||||
|
{TokenType::Ampersand, Operator::BitAnd},
|
||||||
|
{TokenType::Pipe, Operator::BitOr},
|
||||||
|
{TokenType::Caret, Operator::BitXor},
|
||||||
|
{TokenType::Tilde, Operator::BitNot},
|
||||||
|
{TokenType::ShiftLeft, Operator::ShiftLeft},
|
||||||
|
{TokenType::ShiftRight, Operator::ShiftRight},
|
||||||
|
|
||||||
|
// 赋值表达式
|
||||||
|
{TokenType::Walrus, Operator::Walrus},
|
||||||
|
// 点运算符
|
||||||
|
{TokenType::Dot, Operator::Dot},
|
||||||
|
}; // :=
|
||||||
|
|
||||||
|
inline bool isOpUnary(Operator op)
|
||||||
|
{
|
||||||
|
return unaryOps.contains(op);
|
||||||
|
}
|
||||||
|
inline bool isOpBinary(Operator op)
|
||||||
|
{
|
||||||
|
return binaryOps.contains(op);
|
||||||
|
}
|
||||||
|
inline bool isOpTernary(Operator op)
|
||||||
|
{
|
||||||
|
return ternaryOps.contains(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
using AstBase = std::shared_ptr<_AstBase>;
|
||||||
|
using Statement = std::shared_ptr<StatementAst>;
|
||||||
|
using Expression = std::shared_ptr<ExpressionAst>;
|
||||||
|
using Eof = std::shared_ptr<EofStmt>;
|
||||||
|
|
||||||
|
class BlockStatementAst : public StatementAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const 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
|
||||||
|
{
|
||||||
|
return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||||
|
}
|
||||||
|
virtual ~BlockStatementAst() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
using BlockStatement = std::shared_ptr<BlockStatementAst>;
|
||||||
|
// static BlockStatement builtinEmptyBlockSt(new BlockStatementAst());
|
||||||
|
}; // namespace Fig::Ast
|
||||||
39
include/Ast/functionParameters.hpp
Normal file
39
include/Ast/functionParameters.hpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
struct FunctionParameters // for define
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Positional Parameters:
|
||||||
|
fun test(pp1, pp2: Int)
|
||||||
|
Default Parameters:
|
||||||
|
fun 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>>>;
|
||||||
|
|
||||||
|
PosParasType posParas;
|
||||||
|
DefParasType defParas; // default parameters
|
||||||
|
|
||||||
|
FunctionParameters()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
FunctionParameters(PosParasType _posParas, DefParasType _defParas)
|
||||||
|
{
|
||||||
|
posParas = std::move(_posParas);
|
||||||
|
defParas = std::move(_defParas);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return posParas.size() + defParas.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
200
include/AstPrinter.hpp
Normal file
200
include/AstPrinter.hpp
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <ast.hpp>
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
|
using namespace Fig;
|
||||||
|
using namespace Fig::Ast;
|
||||||
|
class AstPrinter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void print(const AstBase &node, int indent = 0)
|
||||||
|
{
|
||||||
|
if (!node) return;
|
||||||
|
switch (node->getType())
|
||||||
|
{
|
||||||
|
case AstType::BinaryExpr:
|
||||||
|
printBinaryExpr(std::static_pointer_cast<BinaryExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::UnaryExpr:
|
||||||
|
printUnaryExpr(std::static_pointer_cast<UnaryExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::ValueExpr:
|
||||||
|
printValueExpr(std::static_pointer_cast<ValueExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::VarDefSt:
|
||||||
|
printVarDef(std::static_pointer_cast<VarDefAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::VarExpr:
|
||||||
|
printVarExpr(std::static_pointer_cast<VarExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::BlockStatement:
|
||||||
|
printBlockStatement(std::static_pointer_cast<BlockStatementAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::FunctionCall:
|
||||||
|
printFunctionCall(std::static_pointer_cast<FunctionCallExpr>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::FunctionDefSt:
|
||||||
|
printFunctionSt(std::static_pointer_cast<FunctionDefSt>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::IfSt:
|
||||||
|
printIfSt(std::static_pointer_cast<IfSt>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::LambdaExpr:
|
||||||
|
printLambdaExpr(std::static_pointer_cast<LambdaExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
case AstType::TernaryExpr:
|
||||||
|
printTernaryExpr(std::static_pointer_cast<TernaryExprAst>(node), indent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "Unknown AST Node\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void printIndent(int indent)
|
||||||
|
{
|
||||||
|
std::cout << std::string(indent, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
void printFString(const Fig::FString &fstr, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "FString: \"";
|
||||||
|
std::cout.write(reinterpret_cast<const char *>(fstr.data()), fstr.size());
|
||||||
|
std::cout << "\"\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename EnumT>
|
||||||
|
void printEnum(const EnumT &value, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "Enum: " << magic_enum::enum_name(value) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void printBinaryExpr(const std::shared_ptr<BinaryExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "BinaryExpr\n";
|
||||||
|
printEnum(node->op, indent + 2);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Left:\n";
|
||||||
|
print(node->lexp, indent + 4);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Right:\n";
|
||||||
|
print(node->rexp, indent + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printUnaryExpr(const std::shared_ptr<UnaryExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "UnaryExpr\n";
|
||||||
|
printEnum(node->op, indent + 2);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Expr:\n";
|
||||||
|
print(node->exp, indent + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printValueExpr(const std::shared_ptr<ValueExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "ValueExpr\n";
|
||||||
|
printFString(node->val.toString(), indent + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printVarDef(const std::shared_ptr<VarDefAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "VarDef\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Name: ";
|
||||||
|
printFString(node->name, 0);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Type: ";
|
||||||
|
printFString(node->typeName, 0);
|
||||||
|
if (node->expr)
|
||||||
|
{
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "InitExpr:\n";
|
||||||
|
print(node->expr, indent + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printVarExpr(const std::shared_ptr<VarExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "VarExpr\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Name: ";
|
||||||
|
printFString(node->name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printBlockStatement(const std::shared_ptr<BlockStatementAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "BlockStatement\n";
|
||||||
|
for (const auto &stmt : node->stmts)
|
||||||
|
{
|
||||||
|
print(stmt, indent + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printFunctionCall(const std::shared_ptr<FunctionCallExpr> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "FunctionCall\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "FuncName: ";
|
||||||
|
printFString(node->name, 0);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printFunctionSt(const std::shared_ptr<FunctionDefSt> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "FunctionSt\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Name: ";
|
||||||
|
printFString(node->name, 0);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Body:\n";
|
||||||
|
print(node->body, indent + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printIfSt(const std::shared_ptr<IfSt> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "IfSt\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Condition:\n";
|
||||||
|
print(node->condition, indent + 4);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printLambdaExpr(const std::shared_ptr<LambdaExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "LambdaExpr\n"
|
||||||
|
<< node->toString().toBasicString();
|
||||||
|
printIndent(indent + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printTernaryExpr(const std::shared_ptr<TernaryExprAst> &node, int indent)
|
||||||
|
{
|
||||||
|
printIndent(indent);
|
||||||
|
std::cout << "TernaryExpr\n";
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "Condition:\n";
|
||||||
|
print(node->condition, indent + 4);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "TrueExpr:\n";
|
||||||
|
print(node->valueT, indent + 4);
|
||||||
|
printIndent(indent + 2);
|
||||||
|
std::cout << "FalseExpr:\n";
|
||||||
|
print(node->valueF, indent + 4);
|
||||||
|
}
|
||||||
|
};
|
||||||
190
include/Value/BaseValue.hpp
Normal file
190
include/Value/BaseValue.hpp
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class __ValueWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const TypeInfo ti;
|
||||||
|
std::unique_ptr<T> data;
|
||||||
|
|
||||||
|
__ValueWrapper(const __ValueWrapper &other) :
|
||||||
|
ti(other.ti)
|
||||||
|
{
|
||||||
|
if (other.data)
|
||||||
|
data = std::make_unique<T>(*other.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
__ValueWrapper(__ValueWrapper &&other) noexcept
|
||||||
|
:
|
||||||
|
ti(other.ti), data(std::move(other.data)) {}
|
||||||
|
|
||||||
|
__ValueWrapper &operator=(const __ValueWrapper &other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
if (other.data)
|
||||||
|
data = std::make_unique<T>(*other.data);
|
||||||
|
else
|
||||||
|
data.reset();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
__ValueWrapper &operator=(__ValueWrapper &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
data = std::move(other.data);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &getValue() const
|
||||||
|
{
|
||||||
|
if (!data) throw std::runtime_error("Accessing null Value data");
|
||||||
|
return *data;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t getSize() const
|
||||||
|
{
|
||||||
|
return sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isNull() const
|
||||||
|
{
|
||||||
|
return !data;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() const
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return FString(std::format("<{} object (null)>", ti.name.toBasicString()));
|
||||||
|
|
||||||
|
return FString(std::format(
|
||||||
|
"<{} object @{:p}>",
|
||||||
|
ti.name.toBasicString(),
|
||||||
|
static_cast<const void *>(data.get())));
|
||||||
|
}
|
||||||
|
|
||||||
|
__ValueWrapper(const TypeInfo &_ti) :
|
||||||
|
ti(_ti) {}
|
||||||
|
|
||||||
|
__ValueWrapper(const T &x, const TypeInfo &_ti) :
|
||||||
|
ti(_ti)
|
||||||
|
{
|
||||||
|
data = std::make_unique<T>(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Int final : public __ValueWrapper<ValueType::IntClass>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Int(const ValueType::IntClass &x) :
|
||||||
|
__ValueWrapper(x, ValueType::Int) {}
|
||||||
|
|
||||||
|
Int(const Int &) = default;
|
||||||
|
Int(Int &&) noexcept = default;
|
||||||
|
|
||||||
|
Int &operator=(const Int &) = default;
|
||||||
|
Int &operator=(Int &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const Int &other) const noexcept
|
||||||
|
{
|
||||||
|
return getValue() == other.getValue();
|
||||||
|
}
|
||||||
|
bool operator!=(const Int &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Double final : public __ValueWrapper<ValueType::DoubleClass>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Double(const ValueType::DoubleClass &x) :
|
||||||
|
__ValueWrapper(x, ValueType::Double) {}
|
||||||
|
Double(const Double &) = default;
|
||||||
|
Double(Double &&) noexcept = default;
|
||||||
|
|
||||||
|
Double &operator=(const Double &) = default;
|
||||||
|
Double &operator=(Double &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const Double &other) const noexcept
|
||||||
|
{
|
||||||
|
return getValue() == other.getValue();
|
||||||
|
}
|
||||||
|
bool operator!=(const Double &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Null final : public __ValueWrapper<ValueType::NullClass>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Null() :
|
||||||
|
__ValueWrapper(ValueType::NullClass{}, ValueType::Null) {}
|
||||||
|
|
||||||
|
Null(const Null &) = default;
|
||||||
|
Null(Null &&) noexcept = default;
|
||||||
|
|
||||||
|
Null &operator=(const Null &) = default;
|
||||||
|
Null &operator=(Null &&) noexcept = default;
|
||||||
|
|
||||||
|
bool isNull() const override { return true; }
|
||||||
|
bool operator==(const Null &) const noexcept { return true; }
|
||||||
|
bool operator!=(const Null &) const noexcept { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class String final : public __ValueWrapper<ValueType::StringClass>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
String(const ValueType::StringClass &x) :
|
||||||
|
__ValueWrapper(x, ValueType::String) {}
|
||||||
|
String(const String &) = default;
|
||||||
|
String(String &&) noexcept = default;
|
||||||
|
|
||||||
|
String &operator=(const String &) = default;
|
||||||
|
String &operator=(String &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const String &other) const noexcept
|
||||||
|
{
|
||||||
|
return getValue() == other.getValue();
|
||||||
|
}
|
||||||
|
bool operator!=(const String &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Bool final : public __ValueWrapper<ValueType::BoolClass>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Bool(const ValueType::BoolClass &x) :
|
||||||
|
__ValueWrapper(x, ValueType::Bool) {}
|
||||||
|
|
||||||
|
Bool(const Bool &) = default;
|
||||||
|
Bool(Bool &&) noexcept = default;
|
||||||
|
|
||||||
|
Bool &operator=(const Bool &) = default;
|
||||||
|
Bool &operator=(Bool &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const Bool &other) const noexcept
|
||||||
|
{
|
||||||
|
return getValue() == other.getValue();
|
||||||
|
}
|
||||||
|
bool operator!=(const Bool &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
79
include/Value/Type.hpp
Normal file
79
include/Value/Type.hpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
class TypeInfo final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
size_t id;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FString name;
|
||||||
|
|
||||||
|
FString toString() const
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::map<FString, size_t> typeMap;
|
||||||
|
|
||||||
|
static size_t getID(FString _name)
|
||||||
|
{
|
||||||
|
return typeMap.at(_name);
|
||||||
|
}
|
||||||
|
size_t getInstanceID(FString _name) const
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInfo();
|
||||||
|
TypeInfo(FString _name, bool reg = false);
|
||||||
|
TypeInfo(const TypeInfo &other) = default;
|
||||||
|
|
||||||
|
bool operator==(const TypeInfo &other) const
|
||||||
|
{
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// class Value;
|
||||||
|
namespace ValueType
|
||||||
|
{
|
||||||
|
extern const TypeInfo Any;
|
||||||
|
extern const TypeInfo Null;
|
||||||
|
extern const TypeInfo Int;
|
||||||
|
extern const TypeInfo String;
|
||||||
|
extern const TypeInfo Bool;
|
||||||
|
extern const TypeInfo Double;
|
||||||
|
extern const TypeInfo Function;
|
||||||
|
extern const TypeInfo StructType;
|
||||||
|
extern const TypeInfo StructInstance;
|
||||||
|
extern const TypeInfo List;
|
||||||
|
extern const TypeInfo Map;
|
||||||
|
extern const TypeInfo Tuple;
|
||||||
|
|
||||||
|
using IntClass = int64_t;
|
||||||
|
using DoubleClass = double;
|
||||||
|
using BoolClass = bool;
|
||||||
|
using NullClass = std::monostate;
|
||||||
|
using StringClass = FString;
|
||||||
|
|
||||||
|
/* complex types */
|
||||||
|
struct FunctionStruct;
|
||||||
|
using FunctionClass = FunctionStruct;
|
||||||
|
|
||||||
|
struct StructT;
|
||||||
|
using StructTypeClass = StructT;
|
||||||
|
|
||||||
|
struct StructInstanceT;
|
||||||
|
using StructInstanceClass = StructInstanceT;
|
||||||
|
}; // namespace ValueType
|
||||||
|
}; // namespace Fig
|
||||||
74
include/Value/function.hpp
Normal file
74
include/Value/function.hpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Value/BaseValue.hpp>
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
#include <Ast/functionParameters.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
/* complex objects */
|
||||||
|
struct FunctionStruct
|
||||||
|
{
|
||||||
|
std::size_t id;
|
||||||
|
Ast::FunctionParameters paras;
|
||||||
|
TypeInfo retType;
|
||||||
|
Ast::BlockStatement body;
|
||||||
|
|
||||||
|
FunctionStruct(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body) :
|
||||||
|
id(nextId()), // 分配唯一 ID
|
||||||
|
paras(std::move(_paras)),
|
||||||
|
retType(std::move(_retType)),
|
||||||
|
body(std::move(_body))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionStruct(const FunctionStruct &other) :
|
||||||
|
id(other.id), paras(other.paras), retType(other.retType), body(other.body) {}
|
||||||
|
|
||||||
|
FunctionStruct &operator=(const FunctionStruct &other) = default;
|
||||||
|
FunctionStruct(FunctionStruct &&) noexcept = default;
|
||||||
|
FunctionStruct &operator=(FunctionStruct &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const FunctionStruct &other) const noexcept
|
||||||
|
{
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
bool operator!=(const FunctionStruct &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::size_t nextId()
|
||||||
|
{
|
||||||
|
static std::atomic<std::size_t> counter{1};
|
||||||
|
return counter++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Function final : public __ValueWrapper<FunctionStruct>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Function(const FunctionStruct &x) :
|
||||||
|
__ValueWrapper(ValueType::Function)
|
||||||
|
{
|
||||||
|
data = std::make_unique<FunctionStruct>(x);
|
||||||
|
}
|
||||||
|
Function(Ast::FunctionParameters paras, TypeInfo ret, Ast::BlockStatement body) :
|
||||||
|
__ValueWrapper(ValueType::Function)
|
||||||
|
{
|
||||||
|
data = std::make_unique<FunctionStruct>(
|
||||||
|
std::move(paras), std::move(ret), std::move(body));
|
||||||
|
}
|
||||||
|
bool operator==(const Function &other) const noexcept
|
||||||
|
{
|
||||||
|
if (!data || !other.data) return false;
|
||||||
|
return *data == *other.data; // call -> FunctionStruct::operator== (based on ID comparing)
|
||||||
|
}
|
||||||
|
Function(const Function &) = default;
|
||||||
|
Function(Function &&) noexcept = default;
|
||||||
|
Function &operator=(const Function &) = default;
|
||||||
|
Function &operator=(Function &&) noexcept = default;
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
50
include/Value/structInstance.hpp
Normal file
50
include/Value/structInstance.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Value/BaseValue.hpp>
|
||||||
|
|
||||||
|
#include <context_forward.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct StructInstanceT final
|
||||||
|
{
|
||||||
|
FString structName; // 类的名字 (StructType), 非变量名。用于获取所属类
|
||||||
|
ContextPtr localContext;
|
||||||
|
|
||||||
|
StructInstanceT(FString _structName, ContextPtr _localContext) :
|
||||||
|
structName(std::move(_structName)), localContext(std::move(_localContext)) {}
|
||||||
|
|
||||||
|
StructInstanceT(const StructInstanceT &other) :
|
||||||
|
structName(other.structName), localContext(other.localContext) {}
|
||||||
|
StructInstanceT &operator=(const StructInstanceT &) = default;
|
||||||
|
StructInstanceT(StructInstanceT &&) = default;
|
||||||
|
StructInstanceT &operator=(StructInstanceT &&) = default;
|
||||||
|
|
||||||
|
bool operator==(const StructInstanceT &) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StructInstance final : public __ValueWrapper<StructInstanceT>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StructInstance(const StructInstanceT &x) :
|
||||||
|
__ValueWrapper(ValueType::StructInstance)
|
||||||
|
{
|
||||||
|
data = std::make_unique<StructInstanceT>(x);
|
||||||
|
}
|
||||||
|
StructInstance(FString _structName, ContextPtr _localContext) :
|
||||||
|
__ValueWrapper(ValueType::StructInstance)
|
||||||
|
{
|
||||||
|
data = std::make_unique<StructInstanceT>(std::move(_structName), std::move(_localContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const StructInstance &other) const noexcept
|
||||||
|
{
|
||||||
|
return data == other.data;
|
||||||
|
}
|
||||||
|
StructInstance(const StructInstance &) = default;
|
||||||
|
StructInstance(StructInstance &&) = default;
|
||||||
|
StructInstance &operator=(const StructInstance &) = default;
|
||||||
|
StructInstance &operator=(StructInstance &&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
95
include/Value/structType.hpp
Normal file
95
include/Value/structType.hpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <Ast/StructDefSt.hpp>
|
||||||
|
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
#include <Value/BaseValue.hpp>
|
||||||
|
|
||||||
|
#include <context_forward.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct Field
|
||||||
|
{
|
||||||
|
bool isPublic() const
|
||||||
|
{
|
||||||
|
return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal;
|
||||||
|
}
|
||||||
|
bool isConst() const
|
||||||
|
{
|
||||||
|
return am == AccessModifier::Const or am == AccessModifier::PublicConst;
|
||||||
|
}
|
||||||
|
bool isFinal() const
|
||||||
|
{
|
||||||
|
return am == AccessModifier::Final or am == AccessModifier::PublicFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessModifier am;
|
||||||
|
FString name;
|
||||||
|
TypeInfo type;
|
||||||
|
Ast::Expression defaultValue;
|
||||||
|
|
||||||
|
Field(AccessModifier _am, FString _name, TypeInfo _type, Ast::Expression _defaultValue) :
|
||||||
|
am(std::move(_am)), name(std::move(_name)), type(std::move(_type)), defaultValue(std::move(_defaultValue)) {}
|
||||||
|
};
|
||||||
|
struct StructT final// = StructType 结构体定义
|
||||||
|
{
|
||||||
|
std::size_t id;
|
||||||
|
ContextPtr defContext; // 定义时的上下文
|
||||||
|
std::vector<Field> fields;
|
||||||
|
StructT(ContextPtr _defContext, std::vector<Field> fieldsMap) :
|
||||||
|
defContext(std::move(_defContext)),
|
||||||
|
fields(std::move(fieldsMap))
|
||||||
|
{
|
||||||
|
id = nextId();
|
||||||
|
}
|
||||||
|
StructT(const StructT &other) :
|
||||||
|
id(other.id), fields(other.fields) {}
|
||||||
|
StructT &operator=(const StructT &other) = default;
|
||||||
|
StructT(StructT &&) noexcept = default;
|
||||||
|
StructT &operator=(StructT &&) noexcept = default;
|
||||||
|
|
||||||
|
bool operator==(const StructT &other) const noexcept
|
||||||
|
{
|
||||||
|
return id == other.id;
|
||||||
|
}
|
||||||
|
bool operator!=(const StructT &other) const noexcept
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::size_t nextId()
|
||||||
|
{
|
||||||
|
static std::atomic<std::size_t> counter{1};
|
||||||
|
return counter++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StructType final : public __ValueWrapper<StructT>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StructType(const StructT &x) :
|
||||||
|
__ValueWrapper(ValueType::StructType)
|
||||||
|
{
|
||||||
|
data = std::make_unique<StructT>(x);
|
||||||
|
}
|
||||||
|
StructType(ContextPtr _defContext, std::vector<Field> fieldsMap) :
|
||||||
|
__ValueWrapper(ValueType::StructType)
|
||||||
|
{
|
||||||
|
data = std::make_unique<StructT>(std::move(_defContext), std::move(fieldsMap));
|
||||||
|
}
|
||||||
|
bool operator==(const StructType &other) const noexcept
|
||||||
|
{
|
||||||
|
return data == other.data;
|
||||||
|
}
|
||||||
|
StructType(const StructType &) = default;
|
||||||
|
StructType(StructType &&) noexcept = default;
|
||||||
|
StructType &operator=(const StructType &) = default;
|
||||||
|
StructType &operator=(StructType &&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
17
include/Value/valueError.hpp
Normal file
17
include/Value/valueError.hpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <error.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class ValueError : public UnaddressableError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using UnaddressableError::UnaddressableError;
|
||||||
|
virtual FString 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
2589
include/argparse/argparse.hpp
Normal file
2589
include/argparse/argparse.hpp
Normal file
File diff suppressed because it is too large
Load Diff
23
include/ast.hpp
Normal file
23
include/ast.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Ast/AccessModifier.hpp>
|
||||||
|
#include <Ast/BinaryExpr.hpp>
|
||||||
|
#include <Ast/ContainerInitExprs.hpp>
|
||||||
|
#include <Ast/ControlSt.hpp>
|
||||||
|
#include <Ast/ExpressionStmt.hpp>
|
||||||
|
#include <Ast/FunctionCall.hpp>
|
||||||
|
#include <Ast/functionParameters.hpp>
|
||||||
|
#include <Ast/FunctionDefSt.hpp>
|
||||||
|
#include <Ast/IfSt.hpp>
|
||||||
|
#include <Ast/ImplementSt.hpp>
|
||||||
|
#include <Ast/InitExpr.hpp>
|
||||||
|
#include <Ast/LambdaExpr.hpp>
|
||||||
|
#include <Ast/StructDefSt.hpp>
|
||||||
|
#include <Ast/TernaryExpr.hpp>
|
||||||
|
#include <Ast/UnaryExpr.hpp>
|
||||||
|
#include <Ast/ValueExpr.hpp>
|
||||||
|
#include <Ast/VarAssignSt.hpp>
|
||||||
|
#include <Ast/VarDef.hpp>
|
||||||
|
#include <Ast/VarExpr.hpp>
|
||||||
|
#include <Ast/WhileSt.hpp>
|
||||||
152
include/builtins.hpp
Normal file
152
include/builtins.hpp
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <value.hpp>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <print>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace Builtins
|
||||||
|
{
|
||||||
|
const std::unordered_map<FString, Value> builtinValues = {
|
||||||
|
{u8"null", Value::getNullInstance()},
|
||||||
|
{u8"true", Value(true)},
|
||||||
|
{u8"false", Value(false)},
|
||||||
|
};
|
||||||
|
|
||||||
|
using BuiltinFunction = std::function<Value(const std::vector<Value> &)>;
|
||||||
|
|
||||||
|
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},
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
|
||||||
|
{u8"__fstdout_print", [](const std::vector<Value> &args) -> Value {
|
||||||
|
for (auto arg : args)
|
||||||
|
{
|
||||||
|
std::print("{}", arg.toString().toBasicString());
|
||||||
|
}
|
||||||
|
return Value(Int(args.size()));
|
||||||
|
}},
|
||||||
|
{u8"__fstdout_println", [](const std::vector<Value> &args) -> Value {
|
||||||
|
for (auto arg : args)
|
||||||
|
{
|
||||||
|
std::print("{}", arg.toString().toBasicString());
|
||||||
|
}
|
||||||
|
std::print("\n");
|
||||||
|
return Value(Int(args.size()));
|
||||||
|
}},
|
||||||
|
{u8"__fstdin_read", [](const std::vector<Value> &args) -> Value {
|
||||||
|
std::string input;
|
||||||
|
std::cin >> input;
|
||||||
|
return Value(FString::fromBasicString(input));
|
||||||
|
}},
|
||||||
|
{u8"__fstdin_readln", [](const std::vector<Value> &args) -> Value {
|
||||||
|
std::string line;
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
return Value(FString::fromBasicString(line));
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_type", [](const std::vector<Value> &args) -> Value {
|
||||||
|
return Value(args[0].getTypeInfo().toString());
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_int_parse", [](const std::vector<Value> &args) -> Value {
|
||||||
|
FString str = args[0].as<String>().getValue();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ValueType::IntClass val = std::stoi(str.toBasicString());
|
||||||
|
return Value(Int(val));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Invalid int string for parsing", str.toBasicString())));
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_int_from", [](const std::vector<Value> &args) -> Value {
|
||||||
|
Value val = args[0];
|
||||||
|
if (val.is<Double>())
|
||||||
|
{
|
||||||
|
return Value(Int(static_cast<ValueType::IntClass>(val.as<Double>().getValue())));
|
||||||
|
}
|
||||||
|
else if (val.is<Bool>())
|
||||||
|
{
|
||||||
|
return Value(Int(val.as<Bool>().getValue() ? 1 : 0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to int", val.getTypeInfo().toString().toBasicString())));
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_double_parse", [](const std::vector<Value> &args) -> Value {
|
||||||
|
FString str = args[0].as<String>().getValue();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ValueType::DoubleClass val = std::stod(str.toBasicString());
|
||||||
|
return Value(Double(val));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Invalid double string for parsing", str.toBasicString())));
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_double_from", [](const std::vector<Value> &args) -> Value {
|
||||||
|
Value val = args[0];
|
||||||
|
if (val.is<Int>())
|
||||||
|
{
|
||||||
|
return Value(Double(static_cast<ValueType::DoubleClass>(val.as<Int>().getValue())));
|
||||||
|
}
|
||||||
|
else if (val.is<Bool>())
|
||||||
|
{
|
||||||
|
return Value(Double(val.as<Bool>().getValue() ? 1.0 : 0.0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to double", val.getTypeInfo().toString().toBasicString())));
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{u8"__fvalue_string_from", [](const std::vector<Value> &args) -> Value {
|
||||||
|
Value val = args[0];
|
||||||
|
return Value(val.toString());
|
||||||
|
}},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isBuiltinFunction(const FString &name)
|
||||||
|
{
|
||||||
|
return builtinFunctions.find(name) != builtinFunctions.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline BuiltinFunction getBuiltinFunction(const FString &name)
|
||||||
|
{
|
||||||
|
auto it = builtinFunctions.find(name);
|
||||||
|
if (it == builtinFunctions.end())
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getBuiltinFunctionParamCount(const FString &name)
|
||||||
|
{
|
||||||
|
auto it = builtinFunctionArgCounts.find(name);
|
||||||
|
if (it == builtinFunctionArgCounts.end())
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}; // namespace Builtins
|
||||||
|
}; // namespace Fig
|
||||||
168
include/context.hpp
Normal file
168
include/context.hpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <context_forward.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <value.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct Context
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
FString scopeName;
|
||||||
|
std::unordered_map<FString, TypeInfo> varTypes;
|
||||||
|
std::unordered_map<FString, Value> variables;
|
||||||
|
std::unordered_map<FString, AccessModifier> ams;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ContextPtr parent;
|
||||||
|
|
||||||
|
Context(const Context &) = default;
|
||||||
|
Context(const FString &name, ContextPtr p = nullptr) :
|
||||||
|
scopeName(name), parent(p) {}
|
||||||
|
Context(const FString &name, std::unordered_map<FString, TypeInfo> types, std::unordered_map<FString, Value> vars, std::unordered_map<FString, AccessModifier> _ams) :
|
||||||
|
scopeName(std::move(name)), varTypes(std::move(types)), variables(std::move(vars)), ams(std::move(_ams)) {}
|
||||||
|
|
||||||
|
void setParent(ContextPtr _parent)
|
||||||
|
{
|
||||||
|
parent = _parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScopeName(FString _name)
|
||||||
|
{
|
||||||
|
scopeName = std::move(_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString getScopeName() const
|
||||||
|
{
|
||||||
|
return scopeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Value> get(const FString &name)
|
||||||
|
{
|
||||||
|
auto it = variables.find(name);
|
||||||
|
if (it != variables.end())
|
||||||
|
return it->second;
|
||||||
|
if (parent)
|
||||||
|
return parent->get(name);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
AccessModifier getAccessModifier(const FString &name)
|
||||||
|
{
|
||||||
|
if (variables.contains(name))
|
||||||
|
{
|
||||||
|
return ams[name];
|
||||||
|
}
|
||||||
|
else if (parent != nullptr)
|
||||||
|
{
|
||||||
|
return parent->getAccessModifier(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContextPtr createCopyWithPublicVariables()
|
||||||
|
{
|
||||||
|
std::unordered_map<FString, TypeInfo> _varTypes;
|
||||||
|
std::unordered_map<FString, Value> _variables;
|
||||||
|
std::unordered_map<FString, AccessModifier> _ams;
|
||||||
|
for (const auto &p : this->variables)
|
||||||
|
{
|
||||||
|
if (isVariablePublic(p.first))
|
||||||
|
{
|
||||||
|
_variables[p.first] = p.second;
|
||||||
|
_varTypes[p.first] = varTypes[p.first];
|
||||||
|
_ams[p.first] = ams[p.first];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_shared<Context>(this->scopeName, _varTypes, _variables, _ams);
|
||||||
|
}
|
||||||
|
bool isVariableMutable(const FString &name)
|
||||||
|
{
|
||||||
|
AccessModifier am = getAccessModifier(name); // may throw
|
||||||
|
return am == AccessModifier::Normal or am == AccessModifier::Public;
|
||||||
|
}
|
||||||
|
bool isVariablePublic(const FString &name)
|
||||||
|
{
|
||||||
|
AccessModifier am = getAccessModifier(name); // may throw
|
||||||
|
return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal;
|
||||||
|
}
|
||||||
|
void set(const FString &name, const Value &value)
|
||||||
|
{
|
||||||
|
if (variables.contains(name))
|
||||||
|
{
|
||||||
|
if (!isVariableMutable(name))
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString())));
|
||||||
|
}
|
||||||
|
variables[name] = value;
|
||||||
|
}
|
||||||
|
else if (parent != nullptr)
|
||||||
|
{
|
||||||
|
parent->set(name, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void def(const FString &name, const TypeInfo &ti, AccessModifier am, const Value &value = Any())
|
||||||
|
{
|
||||||
|
if (variables.contains(name))
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
||||||
|
}
|
||||||
|
variables[name] = value;
|
||||||
|
varTypes[name] = ti;
|
||||||
|
ams[name] = am;
|
||||||
|
}
|
||||||
|
bool contains(const FString &name)
|
||||||
|
{
|
||||||
|
if (variables.contains(name))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (parent != nullptr)
|
||||||
|
{
|
||||||
|
return parent->contains(name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TypeInfo getTypeInfo(const FString &name)
|
||||||
|
{
|
||||||
|
if (varTypes.contains(name))
|
||||||
|
{
|
||||||
|
return varTypes[name];
|
||||||
|
}
|
||||||
|
else if (parent != nullptr)
|
||||||
|
{
|
||||||
|
return parent->getTypeInfo(name);
|
||||||
|
}
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
|
}
|
||||||
|
void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const
|
||||||
|
{
|
||||||
|
const Context *ctx = this;
|
||||||
|
std::vector<const Context *> chain;
|
||||||
|
|
||||||
|
while (ctx)
|
||||||
|
{
|
||||||
|
chain.push_back(ctx);
|
||||||
|
ctx = ctx->parent.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
os << "[STACK TRACE]\n";
|
||||||
|
for (int i = static_cast<int>(chain.size()) - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
os << " #" << (chain.size() - 1 - i)
|
||||||
|
<< " " << chain[i]->scopeName.toBasicString()
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
9
include/context_forward.hpp
Normal file
9
include/context_forward.hpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct Context;
|
||||||
|
using ContextPtr = std::shared_ptr<Context>;
|
||||||
|
};
|
||||||
59
include/core.hpp
Normal file
59
include/core.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#define __FCORE_VERSION "0.3.1"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define __FCORE_PLATFORM "Windows"
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define __FCORE_PLATFORM "Apple"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define __FCORE_PLATFORM "Linux"
|
||||||
|
#elif defined(__unix__)
|
||||||
|
#define __FCORE_PLATFORM "Unix"
|
||||||
|
#else
|
||||||
|
#define __FCORE_PLATFORM "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "llvm-mingw"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "MinGW"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "GCC"
|
||||||
|
#endif
|
||||||
|
#elif defined(__clang__)
|
||||||
|
#define __FCORE_COMPILER "Clang"
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define __FCORE_COMPILER "MSVC"
|
||||||
|
#else
|
||||||
|
#define __FCORE_COMPILER "Unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SIZE_MAX == 18446744073709551615ull
|
||||||
|
#define __FCORE_ARCH "64"
|
||||||
|
#else
|
||||||
|
#define __FCORE_ARCH "86"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
||||||
|
inline constexpr std::string_view LICENSE = "MIT";
|
||||||
|
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
||||||
|
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
||||||
|
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||||
|
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||||
|
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||||
|
inline constexpr FString MAIN_FUNCTION = u8"main";
|
||||||
|
}; // namespace Core
|
||||||
|
}; // namespace Fig
|
||||||
128
include/error.hpp
Normal file
128
include/error.hpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <format>
|
||||||
|
#include <source_location>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class AddressableError : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AddressableError() {}
|
||||||
|
explicit AddressableError(FStringView _msg,
|
||||||
|
size_t _line,
|
||||||
|
size_t _column,
|
||||||
|
std::source_location loc = std::source_location::current()) :
|
||||||
|
src_loc(loc), line(_line), column(_column)
|
||||||
|
{
|
||||||
|
message = _msg;
|
||||||
|
}
|
||||||
|
virtual FString 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);
|
||||||
|
}
|
||||||
|
const char *what() const noexcept override
|
||||||
|
{
|
||||||
|
static std::string msg = toString().toBasicString();
|
||||||
|
return msg.c_str();
|
||||||
|
}
|
||||||
|
std::source_location src_loc;
|
||||||
|
|
||||||
|
size_t getLine() const { return line; }
|
||||||
|
size_t getColumn() const { return column; }
|
||||||
|
FStringView getMessage() const { return message; }
|
||||||
|
|
||||||
|
virtual FString getErrorType() const
|
||||||
|
{
|
||||||
|
return FString(u8"AddressableError");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
size_t line, column;
|
||||||
|
FStringView message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnaddressableError : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit UnaddressableError() {}
|
||||||
|
explicit UnaddressableError(FStringView _msg,
|
||||||
|
std::source_location loc = std::source_location::current()) :
|
||||||
|
src_loc(loc)
|
||||||
|
{
|
||||||
|
message = _msg;
|
||||||
|
}
|
||||||
|
virtual FString toString() const
|
||||||
|
{
|
||||||
|
std::string msg = std::format("[UnaddressableError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||||
|
return FString(msg);
|
||||||
|
}
|
||||||
|
const char *what() const noexcept override
|
||||||
|
{
|
||||||
|
static std::string msg = toString().toBasicString();
|
||||||
|
return msg.c_str();
|
||||||
|
}
|
||||||
|
std::source_location src_loc;
|
||||||
|
FStringView getMessage() const { return message; }
|
||||||
|
|
||||||
|
virtual FString getErrorType() const
|
||||||
|
{
|
||||||
|
return FString(u8"UnaddressableError");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FStringView message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SyntaxError : public AddressableError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using AddressableError::AddressableError;
|
||||||
|
|
||||||
|
explicit SyntaxError(FStringView _msg,
|
||||||
|
size_t _line,
|
||||||
|
size_t _column,
|
||||||
|
std::source_location loc = std::source_location::current()) :
|
||||||
|
AddressableError(_msg, _line, _column, loc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString toString() const override
|
||||||
|
{
|
||||||
|
std::string msg = std::format("[SyntaxError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||||
|
return FString(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString getErrorType() const override
|
||||||
|
{
|
||||||
|
return FString(u8"SyntaxError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RuntimeError final : public UnaddressableError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using UnaddressableError::UnaddressableError;
|
||||||
|
explicit RuntimeError(FStringView _msg,
|
||||||
|
std::source_location loc = std::source_location::current()) :
|
||||||
|
UnaddressableError(_msg, loc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual FString toString() const override
|
||||||
|
{
|
||||||
|
std::string msg = std::format("[RuntimeError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||||
|
return FString(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString getErrorType() const override
|
||||||
|
{
|
||||||
|
return FString(u8"RuntimeError");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Fig
|
||||||
140
include/errorLog.hpp
Normal file
140
include/errorLog.hpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <error.hpp>
|
||||||
|
#include <core.hpp>
|
||||||
|
|
||||||
|
#include <print>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
namespace ErrorLog
|
||||||
|
{
|
||||||
|
namespace TerminalColors
|
||||||
|
{
|
||||||
|
constexpr const char *Reset = "\033[0m";
|
||||||
|
constexpr const char *Bold = "\033[1m";
|
||||||
|
constexpr const char *Dim = "\033[2m";
|
||||||
|
constexpr const char *Italic = "\033[3m";
|
||||||
|
constexpr const char *Underline = "\033[4m";
|
||||||
|
constexpr const char *Blink = "\033[5m";
|
||||||
|
constexpr const char *Reverse = "\033[7m"; // 前背景反色
|
||||||
|
constexpr const char *Hidden = "\033[8m"; // 隐藏文本
|
||||||
|
constexpr const char *Strike = "\033[9m"; // 删除线
|
||||||
|
|
||||||
|
constexpr const char *Black = "\033[30m";
|
||||||
|
constexpr const char *Red = "\033[31m";
|
||||||
|
constexpr const char *Green = "\033[32m";
|
||||||
|
constexpr const char *Yellow = "\033[33m";
|
||||||
|
constexpr const char *Blue = "\033[34m";
|
||||||
|
constexpr const char *Magenta = "\033[35m";
|
||||||
|
constexpr const char *Cyan = "\033[36m";
|
||||||
|
constexpr const char *White = "\033[37m";
|
||||||
|
|
||||||
|
constexpr const char *LightBlack = "\033[90m";
|
||||||
|
constexpr const char *LightRed = "\033[91m";
|
||||||
|
constexpr const char *LightGreen = "\033[92m";
|
||||||
|
constexpr const char *LightYellow = "\033[93m";
|
||||||
|
constexpr const char *LightBlue = "\033[94m";
|
||||||
|
constexpr const char *LightMagenta = "\033[95m";
|
||||||
|
constexpr const char *LightCyan = "\033[96m";
|
||||||
|
constexpr const char *LightWhite = "\033[97m";
|
||||||
|
|
||||||
|
constexpr const char *DarkRed = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *DarkGreen = "\033[38;2;0;100;0m";
|
||||||
|
constexpr const char *DarkYellow = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *DarkBlue = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *DarkMagenta = "\033[38;2;100;0;100m";
|
||||||
|
constexpr const char *DarkCyan = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *DarkGray = "\033[38;2;64;64;64m";
|
||||||
|
constexpr const char *Gray = "\033[38;2;128;128;128m";
|
||||||
|
constexpr const char *Silver = "\033[38;2;192;192;192m";
|
||||||
|
|
||||||
|
constexpr const char *Navy = "\033[38;2;0;0;128m";
|
||||||
|
constexpr const char *RoyalBlue = "\033[38;2;65;105;225m";
|
||||||
|
constexpr const char *ForestGreen = "\033[38;2;34;139;34m";
|
||||||
|
constexpr const char *Olive = "\033[38;2;128;128;0m";
|
||||||
|
constexpr const char *Teal = "\033[38;2;0;128;128m";
|
||||||
|
constexpr const char *Maroon = "\033[38;2;128;0;0m";
|
||||||
|
constexpr const char *Purple = "\033[38;2;128;0;128m";
|
||||||
|
constexpr const char *Orange = "\033[38;2;255;165;0m";
|
||||||
|
constexpr const char *Gold = "\033[38;2;255;215;0m";
|
||||||
|
constexpr const char *Pink = "\033[38;2;255;192;203m";
|
||||||
|
constexpr const char *Crimson = "\033[38;2;220;20;60m";
|
||||||
|
|
||||||
|
constexpr const char *OnBlack = "\033[40m";
|
||||||
|
constexpr const char *OnRed = "\033[41m";
|
||||||
|
constexpr const char *OnGreen = "\033[42m";
|
||||||
|
constexpr const char *OnYellow = "\033[43m";
|
||||||
|
constexpr const char *OnBlue = "\033[44m";
|
||||||
|
constexpr const char *OnMagenta = "\033[45m";
|
||||||
|
constexpr const char *OnCyan = "\033[46m";
|
||||||
|
constexpr const char *OnWhite = "\033[47m";
|
||||||
|
|
||||||
|
constexpr const char *OnLightBlack = "\033[100m";
|
||||||
|
constexpr const char *OnLightRed = "\033[101m";
|
||||||
|
constexpr const char *OnLightGreen = "\033[102m";
|
||||||
|
constexpr const char *OnLightYellow = "\033[103m";
|
||||||
|
constexpr const char *OnLightBlue = "\033[104m";
|
||||||
|
constexpr const char *OnLightMagenta = "\033[105m";
|
||||||
|
constexpr const char *OnLightCyan = "\033[106m";
|
||||||
|
constexpr const char *OnLightWhite = "\033[107m";
|
||||||
|
|
||||||
|
constexpr const char *OnDarkBlue = "\033[48;2;0;0;128m";
|
||||||
|
constexpr const char *OnGreenYellow = "\033[48;2;173;255;47m";
|
||||||
|
constexpr const char *OnOrange = "\033[48;2;255;165;0m";
|
||||||
|
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
||||||
|
}; // namespace TerminalColors
|
||||||
|
|
||||||
|
inline void coloredPrint(const char *colorCode, FString msg)
|
||||||
|
{
|
||||||
|
std::print("{}{}{}", colorCode, msg.toBasicString(), TerminalColors::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void coloredPrint(const char *colorCode, std::string msg)
|
||||||
|
{
|
||||||
|
std::print("{}{}{}", colorCode, msg, TerminalColors::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void logAddressableError(const AddressableError &err, FString fileName, std::vector<FString> sourceLines)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
for (size_t i = 1; i < err.getColumn(); ++i)
|
||||||
|
{
|
||||||
|
if (lineContent[i - 1] == U'\t')
|
||||||
|
{
|
||||||
|
pointerLine += U'\t';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointerLine += U' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pointerLine += U'^';
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}; // namespace ErrorLog
|
||||||
|
}; // namespace Fig
|
||||||
103
include/evaluator.hpp
Normal file
103
include/evaluator.hpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <error.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <ast.hpp>
|
||||||
|
#include <value.hpp>
|
||||||
|
#include <context.hpp>
|
||||||
|
#include <parser.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
template <const char *errName>
|
||||||
|
class EvaluatorError final : public AddressableError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual FString toString() const override
|
||||||
|
{
|
||||||
|
std::string msg = std::format("[Eve: {}] {} in [{}] {}", errName, std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||||
|
return FString(msg);
|
||||||
|
}
|
||||||
|
using AddressableError::AddressableError;
|
||||||
|
explicit EvaluatorError(FStringView _msg,
|
||||||
|
Ast::AstAddressInfo aai,
|
||||||
|
std::source_location loc = std::source_location::current()) :
|
||||||
|
AddressableError(_msg, aai.line, aai.column, loc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct StatementResult
|
||||||
|
{
|
||||||
|
Value result;
|
||||||
|
enum class Flow
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Return,
|
||||||
|
Break,
|
||||||
|
Continue
|
||||||
|
} flow;
|
||||||
|
|
||||||
|
StatementResult(Value val, Flow f = Flow::Normal) :
|
||||||
|
result(val), flow(f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static StatementResult normal(Value val = Value::getNullInstance())
|
||||||
|
{
|
||||||
|
return StatementResult(val, Flow::Normal);
|
||||||
|
}
|
||||||
|
static StatementResult returnFlow(Value val)
|
||||||
|
{
|
||||||
|
return StatementResult(val, Flow::Return);
|
||||||
|
}
|
||||||
|
static StatementResult breakFlow()
|
||||||
|
{
|
||||||
|
return StatementResult(Value::getNullInstance(), Flow::Break);
|
||||||
|
}
|
||||||
|
static StatementResult continueFlow()
|
||||||
|
{
|
||||||
|
return StatementResult(Value::getNullInstance(), Flow::Continue);
|
||||||
|
}
|
||||||
|
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Evaluator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<Ast::AstBase> asts;
|
||||||
|
std::shared_ptr<Context> globalContext;
|
||||||
|
std::shared_ptr<Context> currentContext;
|
||||||
|
|
||||||
|
Ast::AstAddressInfo currentAddressInfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Evaluator(const std::vector<Ast::AstBase> &a) :
|
||||||
|
asts(a)
|
||||||
|
{
|
||||||
|
globalContext = std::make_shared<Context>(FString(u8"global"));
|
||||||
|
currentContext = globalContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Context> getCurrentContext() { return currentContext; }
|
||||||
|
std::shared_ptr<Context> getGlobalContext() { return globalContext; }
|
||||||
|
|
||||||
|
Value __evalOp(Ast::Operator, const Value &, const Value & = Value::getNullInstance());
|
||||||
|
Value evalBinary(const Ast::BinaryExpr &);
|
||||||
|
Value evalUnary(const Ast::UnaryExpr &);
|
||||||
|
|
||||||
|
StatementResult evalStatement(const Ast::Statement &);
|
||||||
|
|
||||||
|
Value eval(Ast::Expression);
|
||||||
|
void run();
|
||||||
|
void printStackTrace() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Fig
|
||||||
3
include/fassert.hpp
Normal file
3
include/fassert.hpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define fassert(exp,msg) (if(!exp) throw msg;)
|
||||||
100
include/fig_string.hpp
Normal file
100
include/fig_string.hpp
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
// using String = std::u8string;
|
||||||
|
// using StringView = std::u8string_view;
|
||||||
|
|
||||||
|
class FStringView : public std::u8string_view
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::u8string_view::u8string_view;
|
||||||
|
|
||||||
|
static FStringView fromBasicStringView(std::string_view sv)
|
||||||
|
{
|
||||||
|
return FStringView(reinterpret_cast<const char8_t*>(sv.data()), sv.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit FStringView(std::string_view sv)
|
||||||
|
{
|
||||||
|
*this = fromBasicStringView(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit FStringView()
|
||||||
|
{
|
||||||
|
*this = fromBasicStringView(std::string_view(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class FString : public std::u8string
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::u8string::u8string;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
static FString fromBasicString(const std::string &str)
|
||||||
|
{
|
||||||
|
return FString(str.begin(), str.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
static FString fromStringView(FStringView sv)
|
||||||
|
{
|
||||||
|
return FString(reinterpret_cast<const char*> (sv.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static FString fromU8String(const std::u8string &str)
|
||||||
|
{
|
||||||
|
return FString(str.begin(), str.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length()
|
||||||
|
{
|
||||||
|
// get UTF8-String real length
|
||||||
|
size_t len = 0;
|
||||||
|
for (auto it = this->begin(); it != this->end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it & 0xC0) != 0x80)
|
||||||
|
{
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace Fig
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<Fig::FString>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const Fig::FString &s) const
|
||||||
|
{
|
||||||
|
return std::hash<std::u8string>{}(static_cast<const std::u8string &>(s));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
95
include/lexer.hpp
Normal file
95
include/lexer.hpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <corecrt.h>
|
||||||
|
#include <cuchar>
|
||||||
|
#include <cwctype>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <token.hpp>
|
||||||
|
#include <error.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <utf8_iterator.hpp>
|
||||||
|
#include <warning.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
class Lexer final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
size_t line;
|
||||||
|
const FString source;
|
||||||
|
SyntaxError error;
|
||||||
|
UTF8Iterator it;
|
||||||
|
|
||||||
|
std::vector<Warning> warnings;
|
||||||
|
|
||||||
|
size_t last_line, last_column, column = 1;
|
||||||
|
|
||||||
|
bool hasNext()
|
||||||
|
{
|
||||||
|
return !this->it.isEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void skipLine();
|
||||||
|
inline void next()
|
||||||
|
{
|
||||||
|
if (*it == U'\n')
|
||||||
|
{
|
||||||
|
++this->line;
|
||||||
|
this->column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++this->column;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushWarning(size_t id, FString msg)
|
||||||
|
{
|
||||||
|
warnings.push_back(Warning(id, std::move(msg), getCurrentLine(), getCurrentColumn()));
|
||||||
|
}
|
||||||
|
void pushWarning(size_t id, FString 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;
|
||||||
|
|
||||||
|
inline Lexer(const FString &_source) :
|
||||||
|
source(_source), it(source)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token nextToken();
|
||||||
|
|
||||||
|
Token scanNumber();
|
||||||
|
Token scanString();
|
||||||
|
Token scanRawString();
|
||||||
|
Token scanMultilineString();
|
||||||
|
Token scanIdentifier();
|
||||||
|
Token scanSymbol();
|
||||||
|
Token scanComments();
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
1508
include/magic_enum/magic_enum.hpp
Normal file
1508
include/magic_enum/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load Diff
44
include/magic_enum/magic_enum_all.hpp
Normal file
44
include/magic_enum/magic_enum_all.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_containers.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
#include "magic_enum_format.hpp"
|
||||||
|
#include "magic_enum_fuse.hpp"
|
||||||
|
#include "magic_enum_iostream.hpp"
|
||||||
|
#include "magic_enum_switch.hpp"
|
||||||
|
#include "magic_enum_utility.hpp"
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||||
1174
include/magic_enum/magic_enum_containers.hpp
Normal file
1174
include/magic_enum/magic_enum_containers.hpp
Normal file
File diff suppressed because it is too large
Load Diff
222
include/magic_enum/magic_enum_flags.hpp
Normal file
222
include/magic_enum/magic_enum_flags.hpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(push)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
|
||||||
|
constexpr U values_ors() noexcept {
|
||||||
|
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
|
||||||
|
|
||||||
|
auto ors = U{0};
|
||||||
|
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
|
||||||
|
ors |= static_cast<U>(values_v<E, S>[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ors;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
// Returns name from enum-flags value.
|
||||||
|
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] auto enum_flags_name(E value, char_type sep = static_cast<char_type>('|')) -> detail::enable_if_t<E, string> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
string name;
|
||||||
|
auto check_value = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (static_cast<U>(value) & v) != 0) {
|
||||||
|
if (const auto n = detail::names_v<D, S>[i]; !n.empty()) {
|
||||||
|
check_value |= v;
|
||||||
|
if (!name.empty()) {
|
||||||
|
name.append(1, sep);
|
||||||
|
}
|
||||||
|
name.append(n.data(), n.size());
|
||||||
|
} else {
|
||||||
|
return {}; // Value out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_value != 0 && check_value == static_cast<U>(value)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains enum-flags value from integer value.
|
||||||
|
// Returns optional with enum-flags value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
if constexpr (detail::count_v<D, S> == 0) {
|
||||||
|
static_cast<void>(value);
|
||||||
|
return {}; // Empty enum.
|
||||||
|
} else {
|
||||||
|
if constexpr (detail::is_sparse_v<D, S>) {
|
||||||
|
auto check_value = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (value & v) != 0) {
|
||||||
|
check_value |= v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_value != 0 && check_value == value) {
|
||||||
|
return static_cast<D>(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
constexpr auto min = detail::min_v<D, S>;
|
||||||
|
constexpr auto max = detail::values_ors<D, S>();
|
||||||
|
|
||||||
|
if (value >= min && value <= max) {
|
||||||
|
return static_cast<D>(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains enum-flags value from name.
|
||||||
|
// Returns optional with enum-flags value.
|
||||||
|
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
constexpr auto S = detail::enum_subtype::flags;
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
if constexpr (detail::count_v<D, S> == 0) {
|
||||||
|
static_cast<void>(value);
|
||||||
|
return {}; // Empty enum.
|
||||||
|
} else {
|
||||||
|
auto result = U{0};
|
||||||
|
while (!value.empty()) {
|
||||||
|
const auto d = detail::find(value, '|');
|
||||||
|
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
|
||||||
|
auto f = U{0};
|
||||||
|
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||||
|
if (detail::cmp_equal(s, detail::names_v<D, S>[i], p)) {
|
||||||
|
f = static_cast<U>(enum_value<D, S>(i));
|
||||||
|
result |= f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f == U{0}) {
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != U{0}) {
|
||||||
|
return static_cast<D>(result);
|
||||||
|
}
|
||||||
|
return {}; // Invalid value or out of range.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains value with such value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(static_cast<U>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains value with such integer value.
|
||||||
|
template <typename E>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether enum-flags contains enumerator with such name.
|
||||||
|
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||||
|
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
return static_cast<bool>(enum_flags_cast<D>(value, std::move(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether `flags set` contains `flag`.
|
||||||
|
// Note: If `flag` equals 0, it returns false, as 0 is not a flag.
|
||||||
|
template <typename E>
|
||||||
|
constexpr auto enum_flags_test(E flags, E flag) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using U = underlying_type_t<E>;
|
||||||
|
|
||||||
|
return static_cast<U>(flag) && ((static_cast<U>(flags) & static_cast<U>(flag)) == static_cast<U>(flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether `lhs flags set` and `rhs flags set` have common flags.
|
||||||
|
// Note: If `lhs flags set` or `rhs flags set` equals 0, it returns false, as 0 is not a flag, and therfore cannot have any matching flag.
|
||||||
|
template <typename E>
|
||||||
|
constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t<E, bool> {
|
||||||
|
using U = underlying_type_t<E>;
|
||||||
|
|
||||||
|
return (static_cast<U>(lhs) & static_cast<U>(rhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||||
114
include/magic_enum/magic_enum_format.hpp
Normal file
114
include/magic_enum/magic_enum_format.hpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
|
||||||
|
#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT)
|
||||||
|
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT 1
|
||||||
|
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum::customize {
|
||||||
|
// customize enum to enable/disable automatic std::format
|
||||||
|
template <typename E>
|
||||||
|
constexpr bool enum_format_enabled() noexcept {
|
||||||
|
return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT;
|
||||||
|
}
|
||||||
|
} // magic_enum::customize
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_format)
|
||||||
|
|
||||||
|
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||||
|
#include <format>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
struct std::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
|
||||||
|
template <class FormatContext>
|
||||||
|
auto format(E e, FormatContext& ctx) const {
|
||||||
|
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||||
|
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FMT_VERSION)
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : fmt::formatter<std::string_view> {
|
||||||
|
template <class FormatContext>
|
||||||
|
auto format(E e, FormatContext& ctx) const {
|
||||||
|
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||||
|
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||||
|
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE)
|
||||||
|
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
|
||||||
|
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||||
89
include/magic_enum/magic_enum_fuse.hpp
Normal file
89
include/magic_enum/magic_enum_fuse.hpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
|
||||||
|
if (hash) {
|
||||||
|
if (const auto index = enum_index(value)) {
|
||||||
|
return (*hash << log2((enum_count<E>() << 1) - 1)) | *index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
|
||||||
|
return fuse_one_enum(0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, typename... Es>
|
||||||
|
constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
|
||||||
|
return fuse_one_enum(fuse_enum(tail...), head);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Es>
|
||||||
|
constexpr auto typesafe_fuse_enum(Es... values) noexcept {
|
||||||
|
enum class enum_fuse_t : std::uintmax_t;
|
||||||
|
const auto fuse = fuse_enum(values...);
|
||||||
|
if (fuse) {
|
||||||
|
return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
|
||||||
|
}
|
||||||
|
return optional<enum_fuse_t>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
|
||||||
|
template <typename... Es>
|
||||||
|
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
|
||||||
|
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
|
||||||
|
static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
|
||||||
|
static_assert((detail::log2(enum_count<std::decay_t<Es>>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
|
||||||
|
#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
|
||||||
|
const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
|
||||||
|
#else
|
||||||
|
const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
|
||||||
|
#endif
|
||||||
|
return MAGIC_ENUM_ASSERT(fuse), fuse;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||||
117
include/magic_enum/magic_enum_iostream.hpp
Normal file
117
include/magic_enum/magic_enum_iostream.hpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
#include "magic_enum_flags.hpp"
|
||||||
|
|
||||||
|
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||||
|
#include <iosfwd>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace ostream_operators {
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
using U = underlying_type_t<D>;
|
||||||
|
|
||||||
|
if constexpr (detail::supported<D>::value) {
|
||||||
|
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||||
|
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
|
||||||
|
for (const auto c : name) {
|
||||||
|
os.put(c);
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto name = enum_name<D>(value); !name.empty()) {
|
||||||
|
for (const auto c : name) {
|
||||||
|
os.put(c);
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (os << static_cast<U>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||||
|
return value ? (os << *value) : os;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::ostream_operators
|
||||||
|
|
||||||
|
namespace istream_operators {
|
||||||
|
|
||||||
|
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||||
|
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
|
||||||
|
std::basic_string<Char, Traits> s;
|
||||||
|
is >> s;
|
||||||
|
if constexpr (detail::supported<D>::value) {
|
||||||
|
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||||
|
if (const auto v = enum_flags_cast<D>(s)) {
|
||||||
|
value = *v;
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (const auto v = enum_cast<D>(s)) {
|
||||||
|
value = *v;
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
is.setstate(std::basic_ios<Char>::failbit);
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::istream_operators
|
||||||
|
|
||||||
|
namespace iostream_operators {
|
||||||
|
|
||||||
|
using magic_enum::ostream_operators::operator<<;
|
||||||
|
using magic_enum::istream_operators::operator>>;
|
||||||
|
|
||||||
|
} // namespace magic_enum::iostream_operators
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||||
195
include/magic_enum/magic_enum_switch.hpp
Normal file
195
include/magic_enum/magic_enum_switch.hpp
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct default_result_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct identity {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nonesuch {};
|
||||||
|
|
||||||
|
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
|
||||||
|
struct invoke_result : identity<nonesuch> {};
|
||||||
|
|
||||||
|
template <typename F, typename V>
|
||||||
|
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
|
||||||
|
|
||||||
|
template <typename F, typename V>
|
||||||
|
using invoke_result_t = typename invoke_result<F, V>::type;
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||||
|
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
|
||||||
|
static_assert(std::is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
|
||||||
|
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return identity<nonesuch>{};
|
||||||
|
} else {
|
||||||
|
return std::common_type<invoke_result_t<F, enum_constant<values_v<E, S>[I]>>...>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename Result, typename F>
|
||||||
|
constexpr auto result_type() noexcept {
|
||||||
|
static_assert(std::is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
|
||||||
|
|
||||||
|
constexpr auto seq = std::make_index_sequence<count_v<E, S>>{};
|
||||||
|
using R = typename decltype(common_invocable<E, S, F>(seq))::type;
|
||||||
|
if constexpr (std::is_same_v<Result, default_result_type>) {
|
||||||
|
if constexpr (std::is_same_v<R, nonesuch>) {
|
||||||
|
return identity<void>{};
|
||||||
|
} else {
|
||||||
|
return identity<R>{};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (std::is_convertible_v<R, Result>) {
|
||||||
|
return identity<Result>{};
|
||||||
|
} else if constexpr (std::is_convertible_v<Result, R>) {
|
||||||
|
return identity<R>{};
|
||||||
|
} else {
|
||||||
|
return identity<nonesuch>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename Result, typename F, typename D = std::decay_t<E>, typename R = typename decltype(result_type<D, S, Result, F>())::type>
|
||||||
|
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
|
||||||
|
|
||||||
|
#if !defined(MAGIC_ENUM_ENABLE_HASH) && !defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
|
||||||
|
template <typename T = void>
|
||||||
|
inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
|
||||||
|
|
||||||
|
template <std::size_t I, std::size_t End, typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||||
|
constexpr decltype(auto) constexpr_switch_impl(F&& f, E value, Def&& def) {
|
||||||
|
if constexpr(I < End) {
|
||||||
|
constexpr auto v = enum_constant<enum_value<E, I, S>()>{};
|
||||||
|
if (value == v) {
|
||||||
|
if constexpr (std::is_invocable_r_v<R, F, decltype(v)>) {
|
||||||
|
return static_cast<R>(std::forward<F>(f)(v));
|
||||||
|
} else {
|
||||||
|
return def();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return constexpr_switch_impl<I + 1, End, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||||
|
constexpr decltype(auto) constexpr_switch(F&& f, E value, Def&& def) {
|
||||||
|
static_assert(is_enum_v<E>, "magic_enum::detail::constexpr_switch requires enum type.");
|
||||||
|
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return def();
|
||||||
|
} else {
|
||||||
|
return constexpr_switch_impl<0, count_v<E, S>, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
template <typename Result = detail::default_result_type, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
detail::default_result_type_lambda<R>);
|
||||||
|
#else
|
||||||
|
return detail::constexpr_switch<R, D, S>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
detail::default_result_type_lambda<R>);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result = detail::default_result_type, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||||
|
return enum_switch<Result, E, S>(std::forward<F>(f), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
|
||||||
|
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||||
|
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
[&result]() -> R { return std::forward<Result>(result); });
|
||||||
|
#else
|
||||||
|
return detail::constexpr_switch<R, D, S>(
|
||||||
|
std::forward<F>(f),
|
||||||
|
value,
|
||||||
|
[&result]() -> R { return std::forward<Result>(result); });
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Result, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||||
|
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||||
|
return enum_switch<Result, E, S>(std::forward<F>(f), value, std::forward<Result>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||||
138
include/magic_enum/magic_enum_utility.hpp
Normal file
138
include/magic_enum/magic_enum_utility.hpp
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// __ __ _ ______ _____
|
||||||
|
// | \/ | (_) | ____| / ____|_ _
|
||||||
|
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||||
|
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||||
|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||||
|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||||
|
// __/ | https://github.com/Neargye/magic_enum
|
||||||
|
// |___/ version 0.9.7
|
||||||
|
//
|
||||||
|
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
|
#define NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
|
|
||||||
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
|
namespace magic_enum {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||||
|
constexpr auto for_each(F&& f, std::index_sequence<I...>) {
|
||||||
|
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> || ...);
|
||||||
|
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> && ...);
|
||||||
|
|
||||||
|
if constexpr (has_void_return) {
|
||||||
|
(f(enum_constant<values_v<E, S>[I]>{}), ...);
|
||||||
|
} else if constexpr (all_same_return) {
|
||||||
|
return std::array{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||||
|
} else {
|
||||||
|
return std::tuple{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, enum_subtype S, typename F,std::size_t... I>
|
||||||
|
constexpr bool all_invocable(std::index_sequence<I...>) {
|
||||||
|
if constexpr (count_v<E, S> == 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return (std::is_invocable_v<F, enum_constant<values_v<E, S>[I]>> && ...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum::detail
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, detail::enable_if_t<E, int> = 0>
|
||||||
|
constexpr auto enum_for_each(F&& f) {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
|
||||||
|
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||||
|
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};
|
||||||
|
|
||||||
|
if constexpr (detail::all_invocable<D, S, F>(sep)) {
|
||||||
|
return detail::for_each<D, S>(std::forward<F>(f), sep);
|
||||||
|
} else {
|
||||||
|
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_next_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) + n);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_next_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) + n) % count) + count) % count);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MAGIC_ENUM_ASSERT(false), value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_prev_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) - n);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||||
|
[[nodiscard]] constexpr auto enum_prev_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||||
|
using D = std::decay_t<E>;
|
||||||
|
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||||
|
|
||||||
|
if (const auto i = enum_index<D, S>(value)) {
|
||||||
|
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) - n) % count) + count) % count);
|
||||||
|
if (index >= 0 && index < count) {
|
||||||
|
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MAGIC_ENUM_ASSERT(false), value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace magic_enum
|
||||||
|
|
||||||
|
#endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||||
47
include/module.hpp
Normal file
47
include/module.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <value.hpp>
|
||||||
|
#include <context.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const FString name;
|
||||||
|
const FString spec;
|
||||||
|
const FString path;
|
||||||
|
|
||||||
|
std::shared_ptr<Context> context; // module-level context
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
import module -> automatically create a module context and call function `init` if exists
|
||||||
|
all global functions, variables, structs, etc will be stored in module context
|
||||||
|
then module context will be linked to the current context
|
||||||
|
|
||||||
|
*/
|
||||||
|
Module(const FString &moduleName, const FString &moduleSpec, const FString &modulePath) :
|
||||||
|
name(moduleName), spec(moduleSpec), path(modulePath)
|
||||||
|
{
|
||||||
|
context = std::make_shared<Context>(FString(std::format("<Module {}>", name.toBasicString())), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasSymbol(const FString &symbolName)
|
||||||
|
{
|
||||||
|
return context->contains(symbolName);
|
||||||
|
}
|
||||||
|
Value getSymbol(const FString &symbolName)
|
||||||
|
{
|
||||||
|
auto valOpt = context->get(symbolName);
|
||||||
|
if (!valOpt.has_value())
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(std::format("Symbol '{}' not found in module '{}'", symbolName.toBasicString(), name.toBasicString())));
|
||||||
|
}
|
||||||
|
return valOpt.value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
265
include/parser.hpp
Normal file
265
include/parser.hpp
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <ast.hpp>
|
||||||
|
#include <lexer.hpp>
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <error.hpp>
|
||||||
|
|
||||||
|
#include <print>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
class Parser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Lexer lexer;
|
||||||
|
std::vector<Ast::AstBase> output;
|
||||||
|
std::vector<Token> previousTokens;
|
||||||
|
|
||||||
|
size_t tokenPruduced = 0;
|
||||||
|
size_t currentTokenIndex = 0;
|
||||||
|
|
||||||
|
std::unique_ptr<AddressableError> error;
|
||||||
|
|
||||||
|
Ast::AstAddressInfo currentAAI;
|
||||||
|
|
||||||
|
std::stack<Ast::Expression> exprStack;
|
||||||
|
|
||||||
|
void pushNode(const Ast::AstBase &_node)
|
||||||
|
{
|
||||||
|
Ast::AstBase node = _node;
|
||||||
|
node->setAAI(currentAAI);
|
||||||
|
output.push_back(std::move(node));
|
||||||
|
}
|
||||||
|
void pushNode(const Ast::AstBase &_node, Ast::AstAddressInfo _aai)
|
||||||
|
{
|
||||||
|
Ast::AstBase node = _node;
|
||||||
|
node->setAAI(_aai);
|
||||||
|
output.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTokenSymbol(Token tok)
|
||||||
|
{
|
||||||
|
return Lexer::symbol_map.contains(tok.getValue());
|
||||||
|
}
|
||||||
|
bool isTokenOp(Token tok)
|
||||||
|
{
|
||||||
|
return Ast::TokenToOp.contains(tok.getType());
|
||||||
|
}
|
||||||
|
bool isEOF()
|
||||||
|
{
|
||||||
|
if (tokenPruduced == 0) return false;
|
||||||
|
return currentToken() == EOFTok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Precedence = uint32_t;
|
||||||
|
static const std::unordered_map<Ast::Operator, std::pair<Precedence, Precedence>> opPrecedence;
|
||||||
|
Parser(const Lexer &_lexer) :
|
||||||
|
lexer(_lexer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressableError* getError() const
|
||||||
|
{
|
||||||
|
return error.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _ErrT, typename = AddressableError>
|
||||||
|
void throwAddressableError(FStringView msg, size_t line, size_t column, 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, line, column, loc);
|
||||||
|
error = std::make_unique<_ErrT>(spError);
|
||||||
|
throw spError;
|
||||||
|
}
|
||||||
|
template <class _ErrT, typename = AddressableError>
|
||||||
|
void throwAddressableError(FStringView 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`
|
||||||
|
_ErrT spError(msg, currentAAI.line, currentAAI.column, loc);
|
||||||
|
error = std::make_unique<_ErrT>(spError);
|
||||||
|
throw spError;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _ErrT, typename = UnaddressableError>
|
||||||
|
void throwUnaddressableError(FStringView 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);
|
||||||
|
error = std::make_unique<_ErrT>(spError);
|
||||||
|
throw spError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCurrentAAI(Ast::AstAddressInfo _aai)
|
||||||
|
{
|
||||||
|
currentAAI = std::move(_aai);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::AstAddressInfo getCurrentAAI() const
|
||||||
|
{
|
||||||
|
return currentAAI;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token nextToken()
|
||||||
|
{
|
||||||
|
// 没有Rollback时, 存在 currentTokenIndex = tokenPruduced - 1
|
||||||
|
next();
|
||||||
|
return currentToken();
|
||||||
|
}
|
||||||
|
inline void rollback()
|
||||||
|
{
|
||||||
|
if (int64_t(currentTokenIndex - 1) < int64_t(0))
|
||||||
|
// 同下 next注释
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Internal Error in Parser::rollbackToken, trying to rollback but it's already on the begin");
|
||||||
|
}
|
||||||
|
currentTokenIndex--;
|
||||||
|
}
|
||||||
|
inline void next()
|
||||||
|
{
|
||||||
|
if (int64_t(currentTokenIndex) < (int64_t(tokenPruduced) - 1))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
必须两个都显示转换为int64_t.否则,负数时会超出范围,变成int64_t max, 并且 CTI也需要显示转换,否则转换完的pruduced又会被转回去,变为 int64_t max
|
||||||
|
*/
|
||||||
|
currentTokenIndex++;
|
||||||
|
setCurrentAAI(Ast::AstAddressInfo{.line = currentToken().line, .column = currentToken().column});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isEOF()) return;
|
||||||
|
const Token &tok = lexer.nextToken();
|
||||||
|
tokenPruduced++;
|
||||||
|
if (tok == IllegalTok) throw lexer.getError();
|
||||||
|
currentTokenIndex = tokenPruduced - 1;
|
||||||
|
setCurrentAAI(Ast::AstAddressInfo{.line = tok.line, .column = tok.column});
|
||||||
|
previousTokens.push_back(tok);
|
||||||
|
}
|
||||||
|
inline Token currentToken()
|
||||||
|
{
|
||||||
|
if (tokenPruduced == 0) return nextToken();
|
||||||
|
return previousTokens.at(currentTokenIndex);
|
||||||
|
}
|
||||||
|
inline Token rollbackToken()
|
||||||
|
{
|
||||||
|
rollback();
|
||||||
|
return previousTokens.at(currentTokenIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Token peekToken()
|
||||||
|
{
|
||||||
|
Token tok = nextToken();
|
||||||
|
rollback();
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<Precedence, Precedence> getBindingPower(Ast::Operator op)
|
||||||
|
{
|
||||||
|
return opPrecedence.at(op);
|
||||||
|
}
|
||||||
|
Precedence getLeftBindingPower(Ast::Operator op)
|
||||||
|
{
|
||||||
|
return getBindingPower(op).first;
|
||||||
|
}
|
||||||
|
Precedence getRightBindingPower(Ast::Operator op)
|
||||||
|
{
|
||||||
|
return getBindingPower(op).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class _Tp, class... Args>
|
||||||
|
std::shared_ptr<_Tp> makeAst(Args &&...args)
|
||||||
|
{
|
||||||
|
_Tp node(args...);
|
||||||
|
node.setAAI(currentAAI);
|
||||||
|
return std::shared_ptr<_Tp>(new _Tp(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
void expectPeek(TokenType type)
|
||||||
|
{
|
||||||
|
if (peekToken().getType() != type)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||||
|
magic_enum::enum_name(type),
|
||||||
|
magic_enum::enum_name(peekToken().getType()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void expect(TokenType type)
|
||||||
|
{
|
||||||
|
if (currentToken().getType() != type)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||||
|
magic_enum::enum_name(type),
|
||||||
|
magic_enum::enum_name(currentToken().getType()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void expectPeek(TokenType type, FString expected)
|
||||||
|
{
|
||||||
|
if (peekToken().getType() != type)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||||
|
expected.toBasicString(),
|
||||||
|
magic_enum::enum_name(peekToken().getType()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void expect(TokenType type, FString expected)
|
||||||
|
{
|
||||||
|
if (currentToken().getType() != type)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||||
|
expected.toBasicString(),
|
||||||
|
magic_enum::enum_name(currentToken().getType()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
Value __parseValue();
|
||||||
|
Ast::ValueExpr __parseValueExpr();
|
||||||
|
Ast::FunctionCall __parseFunctionCall(FString);
|
||||||
|
Ast::FunctionParameters __parseFunctionParameters(); // entry: current is Token::LeftParen
|
||||||
|
Ast::Statement __parseStatement(); // entry: (idk)
|
||||||
|
Ast::BlockStatement __parseBlockStatement(); // entry: current is Token::LeftBrace
|
||||||
|
Ast::VarAssign __parseVarAssign(FString); // entry: current is Token::Assign, para1 is var name
|
||||||
|
Ast::If __parseIf(); // entry: current is Token::If
|
||||||
|
Ast::While __parseWhile(); // entry: current is Token::While
|
||||||
|
Ast::Return __parseReturn(); // entry: current is Token::Return
|
||||||
|
|
||||||
|
Ast::VarExpr __parseVarExpr(FString);
|
||||||
|
Ast::LambdaExpr __parseLambdaExpr();
|
||||||
|
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::BinaryExpr __parseInfix(Ast::Expression, Ast::Operator, Precedence);
|
||||||
|
Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence);
|
||||||
|
|
||||||
|
Ast::ListExpr __parseListExpr(); // entry: current is `[`
|
||||||
|
Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`
|
||||||
|
Ast::MapExpr __parseMapExpr(); // entry: current is `{`
|
||||||
|
|
||||||
|
Ast::InitExpr __parseInitExpr(FString); // entry: current is `{`, ahead is struct name. arg (struct name : FString)
|
||||||
|
|
||||||
|
Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
|
||||||
|
std::vector<Ast::AstBase> parseAll();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
167
include/token.hpp
Normal file
167
include/token.hpp
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class TokenType : int8_t
|
||||||
|
{
|
||||||
|
Illegal = -1,
|
||||||
|
EndOfFile = 0,
|
||||||
|
|
||||||
|
Comments,
|
||||||
|
|
||||||
|
Identifier,
|
||||||
|
|
||||||
|
/* Keywords */
|
||||||
|
And, // and
|
||||||
|
Or, // or
|
||||||
|
Not, // not
|
||||||
|
Import, // import
|
||||||
|
Function, // fun
|
||||||
|
Variable, // var
|
||||||
|
Const, // const
|
||||||
|
Final, // final
|
||||||
|
While, // while
|
||||||
|
For, // for
|
||||||
|
If, // if
|
||||||
|
Else, // else
|
||||||
|
Struct, // struct
|
||||||
|
Interface, // interface
|
||||||
|
Implement, // implement
|
||||||
|
Public, // public
|
||||||
|
Return, // return
|
||||||
|
|
||||||
|
// TypeNull, // Null
|
||||||
|
// TypeInt, // Int
|
||||||
|
// TypeString, // String
|
||||||
|
// TypeBool, // Bool
|
||||||
|
// TypeDouble, // Double
|
||||||
|
|
||||||
|
/* Literal Types (not keyword)*/
|
||||||
|
LiteralNumber, // number (int,float...)
|
||||||
|
LiteralString, // FString
|
||||||
|
LiteralBool, // bool (true/false)
|
||||||
|
LiteralNull, // null (Null的唯一实例)
|
||||||
|
|
||||||
|
/* Punct */
|
||||||
|
Plus, // +
|
||||||
|
Minus, // -
|
||||||
|
Asterisk, // *
|
||||||
|
Slash, // /
|
||||||
|
Percent, // %
|
||||||
|
Caret, // ^
|
||||||
|
Ampersand, // &
|
||||||
|
Pipe, // |
|
||||||
|
Tilde, // ~
|
||||||
|
ShiftLeft, // <<
|
||||||
|
ShiftRight, // >>
|
||||||
|
// Exclamation, // !
|
||||||
|
Question, // ?
|
||||||
|
Assign, // =
|
||||||
|
Less, // <
|
||||||
|
Greater, // >
|
||||||
|
Dot, // .
|
||||||
|
Comma, // ,
|
||||||
|
Colon, // :
|
||||||
|
Semicolon, // ;
|
||||||
|
SingleQuote, // '
|
||||||
|
DoubleQuote, // "
|
||||||
|
// Backtick, // `
|
||||||
|
// At, // @
|
||||||
|
// Hash, // #
|
||||||
|
// Dollar, // $
|
||||||
|
// Backslash, // '\'
|
||||||
|
// Underscore, // _
|
||||||
|
LeftParen, // (
|
||||||
|
RightParen, // )
|
||||||
|
LeftBracket, // [
|
||||||
|
RightBracket, // ]
|
||||||
|
LeftBrace, // {
|
||||||
|
RightBrace, // }
|
||||||
|
// LeftArrow, // <-
|
||||||
|
RightArrow, // ->
|
||||||
|
// DoubleArrow, // =>
|
||||||
|
Equal, // ==
|
||||||
|
NotEqual, // !=
|
||||||
|
LessEqual, // <=
|
||||||
|
GreaterEqual, // >=
|
||||||
|
PlusEqual, // +=
|
||||||
|
MinusEqual, // -=
|
||||||
|
AsteriskEqual, // *=
|
||||||
|
SlashEqual, // /=
|
||||||
|
PercentEqual, // %=
|
||||||
|
CaretEqual, // ^=
|
||||||
|
DoublePlus, // ++
|
||||||
|
DoubleMinus, // --
|
||||||
|
DoubleAmpersand, // &&
|
||||||
|
DoublePipe, // ||
|
||||||
|
Walrus, // :=
|
||||||
|
Power, // **
|
||||||
|
};
|
||||||
|
|
||||||
|
class Token final
|
||||||
|
{
|
||||||
|
friend bool operator==(const Token &l, const Token &r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FString 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)
|
||||||
|
{
|
||||||
|
line = _line;
|
||||||
|
column = _column;
|
||||||
|
}
|
||||||
|
Token setPos(size_t _line, size_t _column)
|
||||||
|
{
|
||||||
|
line = _line;
|
||||||
|
column = _column;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
FString getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
inline FString toString() const
|
||||||
|
{
|
||||||
|
return FString(std::format(
|
||||||
|
"Token('{}',{})",
|
||||||
|
this->value.toBasicString(),
|
||||||
|
magic_enum::enum_name(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isIdentifier()
|
||||||
|
{
|
||||||
|
return type == TokenType::Identifier;
|
||||||
|
}
|
||||||
|
bool isLiteral()
|
||||||
|
{
|
||||||
|
return type == TokenType::LiteralNull || type == TokenType::LiteralBool || type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const Token &l, const Token &r)
|
||||||
|
{
|
||||||
|
return l.type == r.type and l.value == r.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token IllegalTok(u8"ILLEGAL", TokenType::Illegal);
|
||||||
|
static Token EOFTok(u8"EOF", TokenType::EndOfFile);
|
||||||
|
} // namespace Fig
|
||||||
260
include/utf8_iterator.hpp
Normal file
260
include/utf8_iterator.hpp
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
#include <corecrt.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
#include <cwctype>
|
||||||
|
// fuckyou C++
|
||||||
|
// i don't know how to deal with unicode string in cpp
|
||||||
|
// fuck
|
||||||
|
// generate by Qwen3-Coder:
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class UTF8Char
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::u8string char_data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UTF8Char(const std::u8string &data) :
|
||||||
|
char_data_(data) {}
|
||||||
|
|
||||||
|
// 获取UTF-8字符的字节长度
|
||||||
|
static size_t getUTF8CharLength(char8_t first_byte)
|
||||||
|
{
|
||||||
|
if ((first_byte & 0x80) == 0x00) return 1;
|
||||||
|
if ((first_byte & 0xE0) == 0xC0) return 2;
|
||||||
|
if ((first_byte & 0xF0) == 0xE0) return 3;
|
||||||
|
if ((first_byte & 0xF8) == 0xF0) return 4;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为Unicode码点
|
||||||
|
char32_t toCodePoint() const
|
||||||
|
{
|
||||||
|
if (char_data_.empty()) return 0;
|
||||||
|
|
||||||
|
size_t len = getUTF8CharLength(char_data_[0]);
|
||||||
|
if (len > char_data_.length()) return 0;
|
||||||
|
|
||||||
|
char32_t code_point = 0;
|
||||||
|
|
||||||
|
switch (len)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
code_point = char_data_[0];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
code_point = ((char_data_[0] & 0x1F) << 6) | (char_data_[1] & 0x3F);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
code_point = ((char_data_[0] & 0x0F) << 12) | ((char_data_[1] & 0x3F) << 6) | (char_data_[2] & 0x3F);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
code_point = ((char_data_[0] & 0x07) << 18) | ((char_data_[1] & 0x3F) << 12) | ((char_data_[2] & 0x3F) << 6) | (char_data_[3] & 0x3F);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return code_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(char32_t ch)
|
||||||
|
{
|
||||||
|
return this->toCodePoint() == ch;
|
||||||
|
}
|
||||||
|
// 字符分类函数
|
||||||
|
bool isAlpha() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswalpha(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDigit() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswdigit(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAlnum() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswalnum(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSpace() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswspace(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isUpper() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswupper(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLower() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswlower(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isPunct() const
|
||||||
|
{
|
||||||
|
char32_t cp = toCodePoint();
|
||||||
|
return std::iswpunct(static_cast<wint_t>(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取底层数据
|
||||||
|
const std::u8string &getString() const { return char_data_; }
|
||||||
|
|
||||||
|
// 获取字符长度(字节数)
|
||||||
|
size_t length() const { return char_data_.length(); }
|
||||||
|
|
||||||
|
// 是否为空
|
||||||
|
bool empty() const { return char_data_.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class UTF8Iterator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const std::u8string *str_;
|
||||||
|
size_t pos_;
|
||||||
|
|
||||||
|
// 获取UTF-8字符的字节长度
|
||||||
|
static size_t getUTF8CharLength(char8_t first_byte)
|
||||||
|
{
|
||||||
|
if ((first_byte & 0x80) == 0x00) return 1;
|
||||||
|
if ((first_byte & 0xE0) == 0xC0) return 2;
|
||||||
|
if ((first_byte & 0xF0) == 0xE0) return 3;
|
||||||
|
if ((first_byte & 0xF8) == 0xF0) return 4;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取下一个字符的起始位置
|
||||||
|
size_t getNextCharPos(size_t current_pos) const
|
||||||
|
{
|
||||||
|
if (current_pos >= str_->length()) return current_pos;
|
||||||
|
size_t char_len = getUTF8CharLength((*str_)[current_pos]);
|
||||||
|
return current_pos + char_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取前一个字符的起始位置
|
||||||
|
size_t getPrevCharPos(size_t current_pos) const
|
||||||
|
{
|
||||||
|
if (current_pos == 0) return 0;
|
||||||
|
|
||||||
|
size_t pos = current_pos - 1;
|
||||||
|
while (pos > 0 && (str_->at(pos) & 0xC0) == 0x80)
|
||||||
|
{
|
||||||
|
--pos;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||||||
|
using value_type = UTF8Char;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = const UTF8Char *;
|
||||||
|
using reference = const UTF8Char &;
|
||||||
|
|
||||||
|
// 构造函数
|
||||||
|
UTF8Iterator(const std::u8string &str, size_t pos = 0) :
|
||||||
|
str_(&str), pos_(pos)
|
||||||
|
{
|
||||||
|
if (pos_ > str_->length()) pos_ = str_->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前置递增
|
||||||
|
UTF8Iterator &operator++()
|
||||||
|
{
|
||||||
|
pos_ = getNextCharPos(pos_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后置递增
|
||||||
|
UTF8Iterator operator++(int)
|
||||||
|
{
|
||||||
|
UTF8Iterator temp = *this;
|
||||||
|
pos_ = getNextCharPos(pos_);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前置递减
|
||||||
|
UTF8Iterator &operator--()
|
||||||
|
{
|
||||||
|
pos_ = getPrevCharPos(pos_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后置递减
|
||||||
|
UTF8Iterator operator--(int)
|
||||||
|
{
|
||||||
|
UTF8Iterator temp = *this;
|
||||||
|
pos_ = getPrevCharPos(pos_);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解引用操作符 - 返回当前字符
|
||||||
|
UTF8Char operator*() const
|
||||||
|
{
|
||||||
|
if (pos_ >= str_->length())
|
||||||
|
{
|
||||||
|
return UTF8Char(std::u8string());
|
||||||
|
}
|
||||||
|
size_t char_len = getUTF8CharLength((*str_)[pos_]);
|
||||||
|
size_t end_pos = pos_ + char_len;
|
||||||
|
if (end_pos > str_->length())
|
||||||
|
{
|
||||||
|
end_pos = str_->length();
|
||||||
|
}
|
||||||
|
return UTF8Char(str_->substr(pos_, end_pos - pos_));
|
||||||
|
}
|
||||||
|
UTF8Char peek() const
|
||||||
|
{
|
||||||
|
if (pos_ >= str_->length())
|
||||||
|
{
|
||||||
|
return UTF8Char(std::u8string());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t next_pos = getNextCharPos(pos_);
|
||||||
|
if (next_pos >= str_->length())
|
||||||
|
{
|
||||||
|
return UTF8Char(std::u8string());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t char_len = getUTF8CharLength((*str_)[next_pos]);
|
||||||
|
size_t end_pos = next_pos + char_len;
|
||||||
|
if (end_pos > str_->length())
|
||||||
|
{
|
||||||
|
end_pos = str_->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return UTF8Char(str_->substr(next_pos, end_pos - next_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 窥探前一个字符
|
||||||
|
UTF8Char peekPrev() const
|
||||||
|
{
|
||||||
|
if (pos_ == 0)
|
||||||
|
{
|
||||||
|
return UTF8Char(std::u8string());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t prev_pos = getPrevCharPos(pos_);
|
||||||
|
size_t char_len = getUTF8CharLength((*str_)[prev_pos]);
|
||||||
|
size_t end_pos = prev_pos + char_len;
|
||||||
|
if (end_pos > str_->length())
|
||||||
|
{
|
||||||
|
end_pos = str_->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return UTF8Char(str_->substr(prev_pos, end_pos - prev_pos));
|
||||||
|
}
|
||||||
|
// 获取当前位置
|
||||||
|
size_t position() const { return pos_; }
|
||||||
|
size_t column() const { return pos_ + 1; }
|
||||||
|
// 检查是否到达末尾
|
||||||
|
bool isEnd() const { return pos_ >= str_->length(); }
|
||||||
|
};
|
||||||
|
} // namespace Fig
|
||||||
109
include/utils.hpp
Normal file
109
include/utils.hpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
#pragma once
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <locale>
|
||||||
|
#include <cwctype>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace Fig::Utils
|
||||||
|
{
|
||||||
|
inline std::u32string utf8ToUtf32(const FString &s)
|
||||||
|
{
|
||||||
|
std::u32string result;
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < s.size())
|
||||||
|
{
|
||||||
|
char32_t codepoint = 0;
|
||||||
|
unsigned char c = static_cast<unsigned char>(s[i]);
|
||||||
|
|
||||||
|
if (c < 0x80)
|
||||||
|
{
|
||||||
|
codepoint = c;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else if ((c >> 5) == 0x6)
|
||||||
|
{
|
||||||
|
codepoint = ((c & 0x1F) << 6) | (static_cast<unsigned char>(s[i + 1]) & 0x3F);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if ((c >> 4) == 0xE)
|
||||||
|
{
|
||||||
|
codepoint = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(s[i + 1]) & 0x3F) << 6) | (static_cast<unsigned char>(s[i + 2]) & 0x3F);
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if ((c >> 3) == 0x1E)
|
||||||
|
{
|
||||||
|
codepoint = ((c & 0x07) << 18) | ((static_cast<unsigned char>(s[i + 1]) & 0x3F) << 12) | ((static_cast<unsigned char>(s[i + 2]) & 0x3F) << 6) | (static_cast<unsigned char>(s[i + 3]) & 0x3F);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i += 1; // 跳过非法字节
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.push_back(codepoint);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FString utf32ToUtf8(const std::u32string &s)
|
||||||
|
{
|
||||||
|
FString result;
|
||||||
|
for (char32_t cp : s)
|
||||||
|
{
|
||||||
|
if (cp < 0x80)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char8_t>(cp));
|
||||||
|
}
|
||||||
|
else if (cp < 0x800)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char8_t>((cp >> 6) | 0xC0));
|
||||||
|
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||||
|
}
|
||||||
|
else if (cp < 0x10000)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char8_t>((cp >> 12) | 0xE0));
|
||||||
|
result.push_back(static_cast<char8_t>(((cp >> 6) & 0x3F) | 0x80));
|
||||||
|
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char8_t>((cp >> 18) | 0xF0));
|
||||||
|
result.push_back(static_cast<char8_t>(((cp >> 12) & 0x3F) | 0x80));
|
||||||
|
result.push_back(static_cast<char8_t>(((cp >> 6) & 0x3F) | 0x80));
|
||||||
|
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FString toLower(const FString &s)
|
||||||
|
{
|
||||||
|
std::u32string u32 = utf8ToUtf32(s);
|
||||||
|
std::locale loc("");
|
||||||
|
for (auto &ch : u32)
|
||||||
|
{
|
||||||
|
ch = std::towlower(ch);
|
||||||
|
}
|
||||||
|
return utf32ToUtf8(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline FString toUpper(const FString &s)
|
||||||
|
{
|
||||||
|
std::u32string u32 = utf8ToUtf32(s);
|
||||||
|
std::locale loc("");
|
||||||
|
for (auto &ch : u32)
|
||||||
|
{
|
||||||
|
ch = std::towupper(ch);
|
||||||
|
}
|
||||||
|
return utf32ToUtf8(u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool vectorContains(const T &t, const std::vector<T> v)
|
||||||
|
{
|
||||||
|
return std::find(v.begin(), v.end(), t) != v.end();
|
||||||
|
}
|
||||||
|
} // namespace Fig::Utils
|
||||||
373
include/value.hpp
Normal file
373
include/value.hpp
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Value/BaseValue.hpp>
|
||||||
|
#include <Value/valueError.hpp>
|
||||||
|
#include <Value/function.hpp>
|
||||||
|
#include <Value/structType.hpp>
|
||||||
|
#include <Value/structInstance.hpp>
|
||||||
|
|
||||||
|
#include <Value/Type.hpp>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using VariantType = std::variant<Null, Int, Double, String, Bool, Function, StructType, StructInstance>;
|
||||||
|
VariantType data;
|
||||||
|
|
||||||
|
Value() :
|
||||||
|
data(Null{}) {}
|
||||||
|
Value(const Null &n) :
|
||||||
|
data(std::in_place_type<Null>, n) {}
|
||||||
|
Value(const Int &i) :
|
||||||
|
data(std::in_place_type<Int>, i) {}
|
||||||
|
Value(const Double &d) :
|
||||||
|
data(std::in_place_type<Double>, d)
|
||||||
|
{
|
||||||
|
ValueType::IntClass casted = static_cast<ValueType::IntClass>(d.getValue());
|
||||||
|
if (casted == d.getValue())
|
||||||
|
{
|
||||||
|
data.emplace<Int>(casted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value(const String &s) :
|
||||||
|
data(std::in_place_type<String>, s) {}
|
||||||
|
Value(const Bool &b) :
|
||||||
|
data(std::in_place_type<Bool>, b) {}
|
||||||
|
Value(const Function &f) :
|
||||||
|
data(std::in_place_type<Function>, f) {}
|
||||||
|
Value(const StructType &s) :
|
||||||
|
data(std::in_place_type<StructType>, s) {}
|
||||||
|
Value(const StructInstance &s) :
|
||||||
|
data(std::in_place_type<StructInstance>, s) {}
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
typename = std::enable_if_t<
|
||||||
|
std::is_same_v<T, ValueType::IntClass>
|
||||||
|
|| std::is_same_v<T, ValueType::DoubleClass>
|
||||||
|
|| std::is_same_v<T, ValueType::StringClass>
|
||||||
|
|| std::is_same_v<T, ValueType::BoolClass>
|
||||||
|
|| std::is_same_v<T, ValueType::FunctionClass>
|
||||||
|
|| std::is_same_v<T, ValueType::StructTypeClass>>>
|
||||||
|
Value(const T &val)
|
||||||
|
{
|
||||||
|
// 不可以用 data = 的形式
|
||||||
|
// __ValueWrapper 构造、拷贝有限制
|
||||||
|
if constexpr (std::is_same_v<T, ValueType::IntClass>)
|
||||||
|
data.emplace<Int>(val);
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
|
||||||
|
{
|
||||||
|
ValueType::IntClass casted = static_cast<ValueType::IntClass>(val);
|
||||||
|
if (casted == val)
|
||||||
|
{
|
||||||
|
data.emplace<Int>(casted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.emplace<Double>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::StringClass>)
|
||||||
|
data.emplace<String>(val);
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
|
||||||
|
data.emplace<Bool>(val);
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::FunctionClass>)
|
||||||
|
data.emplace<Function>(val);
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::StructTypeClass>)
|
||||||
|
data.emplace<StructType>(val);
|
||||||
|
else if constexpr (std::is_same_v<T, ValueType::StructInstanceClass>)
|
||||||
|
data.emplace<StructInstance>(val);
|
||||||
|
}
|
||||||
|
Value(const Value &) = default;
|
||||||
|
Value(Value &&) noexcept = default;
|
||||||
|
Value &operator=(const Value &) = default;
|
||||||
|
Value &operator=(Value &&) noexcept = default;
|
||||||
|
|
||||||
|
static Value defaultValue(TypeInfo ti)
|
||||||
|
{
|
||||||
|
if (ti == ValueType::Int)
|
||||||
|
return Value(Int(0));
|
||||||
|
else if (ti == ValueType::Double)
|
||||||
|
return Value(Double(0.0));
|
||||||
|
else if (ti == ValueType::String)
|
||||||
|
return Value(String(u8""));
|
||||||
|
else if (ti == ValueType::Bool)
|
||||||
|
return Value(Bool(false));
|
||||||
|
else if (ti == ValueType::Function)
|
||||||
|
return getNullInstance();
|
||||||
|
else if (ti == ValueType::StructType)
|
||||||
|
return getNullInstance();
|
||||||
|
else if (ti == ValueType::StructInstance)
|
||||||
|
return getNullInstance();
|
||||||
|
else
|
||||||
|
return getNullInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool is() const
|
||||||
|
{
|
||||||
|
return std::holds_alternative<T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T &as()
|
||||||
|
{
|
||||||
|
return std::get<T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T &as() const
|
||||||
|
{
|
||||||
|
return std::get<T>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value getNullInstance()
|
||||||
|
{
|
||||||
|
static Value v(Null{});
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInfo getTypeInfo() const
|
||||||
|
{
|
||||||
|
return std::visit([](auto &&val) { return val.ti; }, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNull() const { return is<Null>(); }
|
||||||
|
bool isNumeric() const { return is<Int>() || is<Double>(); }
|
||||||
|
|
||||||
|
ValueType::DoubleClass getNumericValue() const
|
||||||
|
{
|
||||||
|
if (is<Int>())
|
||||||
|
return static_cast<ValueType::DoubleClass>(as<Int>().getValue());
|
||||||
|
else if (is<Double>())
|
||||||
|
return as<Double>().getValue();
|
||||||
|
else
|
||||||
|
throw RuntimeError(u8"getNumericValue: Not a numeric value");
|
||||||
|
}
|
||||||
|
|
||||||
|
FString toString() const
|
||||||
|
{
|
||||||
|
if (is<Null>()) return FString(u8"null");
|
||||||
|
if (is<Int>()) return FString(std::to_string(as<Int>().getValue()));
|
||||||
|
if (is<Double>()) return FString(std::to_string(as<Double>().getValue()));
|
||||||
|
if (is<String>()) return as<String>().getValue();
|
||||||
|
if (is<Bool>()) return as<Bool>().getValue() ? FString(u8"true") : FString(u8"false");
|
||||||
|
if (is<Function>())
|
||||||
|
{
|
||||||
|
return FString(std::format("<Function {} at {:p}>",
|
||||||
|
as<Function>().getValue().id,
|
||||||
|
static_cast<const void *>(as<Function>().data.get())));
|
||||||
|
}
|
||||||
|
if (is<StructType>())
|
||||||
|
{
|
||||||
|
return FString(std::format("<StructType {} at {:p}>",
|
||||||
|
as<StructType>().getValue().id,
|
||||||
|
static_cast<const void *>(as<StructType>().data.get())));
|
||||||
|
}
|
||||||
|
if (is<StructInstance>())
|
||||||
|
{
|
||||||
|
return FString(std::format("<Struct Instance('{}') at {:p}",
|
||||||
|
as<StructInstance>().getValue().structName.toBasicString(),
|
||||||
|
static_cast<const void *>(as<StructInstance>().data.get())));
|
||||||
|
}
|
||||||
|
return FString(u8"<error>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::string makeTypeErrorMessage(const char *prefix, const char *op,
|
||||||
|
const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
auto lhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, lhs.data);
|
||||||
|
auto rhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, rhs.data);
|
||||||
|
return std::format("{}: {} '{}' {}", prefix, lhs_type, op, rhs_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// math
|
||||||
|
friend Value operator+(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNull() || rhs.isNull())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
return lhs.getNumericValue() + rhs.getNumericValue();
|
||||||
|
if (lhs.is<String>() && rhs.is<String>())
|
||||||
|
return Value(ValueType::StringClass(lhs.as<String>().getValue() + rhs.as<String>().getValue()));
|
||||||
|
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator-(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNull() || rhs.isNull())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
return lhs.getNumericValue() - rhs.getNumericValue();
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator*(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNull() || rhs.isNull())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs)));
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
return lhs.getNumericValue() * rhs.getNumericValue();
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator/(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNull() || rhs.isNull())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs)));
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
{
|
||||||
|
auto rnv = rhs.getNumericValue();
|
||||||
|
if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
|
||||||
|
return lhs.getNumericValue() / rnv;
|
||||||
|
}
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator%(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNull() || rhs.isNull())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
{
|
||||||
|
auto rnv = rhs.getNumericValue();
|
||||||
|
if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Modulo by zero", "%", lhs, rhs)));
|
||||||
|
return fmod(lhs.getNumericValue(), rhs.getNumericValue());
|
||||||
|
}
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// logic
|
||||||
|
friend Value operator&&(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Bool>() || !rhs.is<Bool>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Bool>().getValue() && rhs.as<Bool>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator||(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Bool>() || !rhs.is<Bool>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Bool>().getValue() || rhs.as<Bool>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator!(const Value &v)
|
||||||
|
{
|
||||||
|
if (!v.is<Bool>())
|
||||||
|
throw ValueError(FStringView(std::format("Logical NOT requires bool: '{}'",
|
||||||
|
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
|
||||||
|
return Value(!v.as<Bool>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value operator-(const Value &v)
|
||||||
|
{
|
||||||
|
if (v.isNull())
|
||||||
|
throw ValueError(FStringView(std::format("Unary minus cannot be applied to null")));
|
||||||
|
if (v.is<Int>())
|
||||||
|
return Value(-v.as<Int>().getValue());
|
||||||
|
if (v.is<Double>())
|
||||||
|
return Value(-v.as<Double>().getValue());
|
||||||
|
throw ValueError(FStringView(std::format("Unary minus requires int or double: '{}'",
|
||||||
|
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
|
||||||
|
}
|
||||||
|
friend Value operator~(const Value &v)
|
||||||
|
{
|
||||||
|
if (!v.is<Int>())
|
||||||
|
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'",
|
||||||
|
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
|
||||||
|
return Value(~v.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare → now returns bool
|
||||||
|
friend bool operator==(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
return lhs.data == rhs.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
return !(lhs.data == rhs.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
return lhs.getNumericValue() < rhs.getNumericValue();
|
||||||
|
if (lhs.is<String>() && rhs.is<String>()) return lhs.as<String>().getValue() < rhs.as<String>().getValue();
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator<=(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
return lhs == rhs or lhs < rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (lhs.isNumeric() and rhs.isNumeric())
|
||||||
|
return lhs.getNumericValue() > rhs.getNumericValue();
|
||||||
|
if (lhs.is<String>() && rhs.is<String>()) return lhs.as<String>().getValue() > rhs.as<String>().getValue();
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator>=(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
return lhs == rhs or lhs > rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bitwise
|
||||||
|
friend Value bit_and(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Int>() || !rhs.is<Int>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Int>().getValue() & rhs.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value bit_or(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Int>() || !rhs.is<Int>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Int>().getValue() | rhs.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value bit_xor(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Int>() || !rhs.is<Int>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Int>().getValue() ^ rhs.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value bit_not(const Value &v)
|
||||||
|
{
|
||||||
|
if (!v.is<Int>())
|
||||||
|
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'",
|
||||||
|
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
|
||||||
|
return Value(~v.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value shift_left(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Int>() || !rhs.is<Int>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Int>().getValue() << rhs.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Value shift_right(const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
if (!lhs.is<Int>() || !rhs.is<Int>())
|
||||||
|
throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
|
||||||
|
return Value(lhs.as<Int>().getValue() >> rhs.as<Int>().getValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using Any = Value;
|
||||||
|
} // namespace Fig
|
||||||
55
include/warning.hpp
Normal file
55
include/warning.hpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
|
||||||
|
#include <magic_enum/magic_enum.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class Warning
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
size_t id; // the id (standard) of warning
|
||||||
|
FString msg;
|
||||||
|
size_t line, column;
|
||||||
|
public:
|
||||||
|
static const std::unordered_map<size_t, FString> standardWarnings;
|
||||||
|
Warning(size_t _id, FString _msg)
|
||||||
|
{
|
||||||
|
id = _id;
|
||||||
|
msg = std::move(_msg);
|
||||||
|
}
|
||||||
|
Warning(size_t _id, FString _msg, size_t _line, size_t _column)
|
||||||
|
{
|
||||||
|
id = _id;
|
||||||
|
msg = std::move(_msg);
|
||||||
|
line = _line;
|
||||||
|
column = _column;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getIDName()
|
||||||
|
{
|
||||||
|
return standardWarnings.at(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getID()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
auto getMsg()
|
||||||
|
{
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
auto getLine()
|
||||||
|
{
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
auto getColumn()
|
||||||
|
{
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
488
src/evaluator.cpp
Normal file
488
src/evaluator.cpp
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
#include <evaluator.hpp>
|
||||||
|
#include <builtins.hpp>
|
||||||
|
#include <utils.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
Value Evaluator::__evalOp(Ast::Operator op, const Value &lhs, const Value &rhs)
|
||||||
|
{
|
||||||
|
using Fig::Ast::Operator;
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case Operator::Add: return lhs + rhs;
|
||||||
|
case Operator::Subtract: return lhs - rhs;
|
||||||
|
case Operator::Multiply: return lhs * rhs;
|
||||||
|
case Operator::Divide: return lhs / rhs;
|
||||||
|
case Operator::Modulo: return lhs % rhs;
|
||||||
|
|
||||||
|
case Operator::And: return lhs && rhs;
|
||||||
|
case Operator::Or: return lhs || rhs;
|
||||||
|
case Operator::Not: return !lhs;
|
||||||
|
|
||||||
|
case Operator::Equal: return Value(lhs == rhs);
|
||||||
|
case Operator::NotEqual: return Value(lhs != rhs);
|
||||||
|
case Operator::Less: return lhs < rhs;
|
||||||
|
case Operator::LessEqual: return lhs <= rhs;
|
||||||
|
case Operator::Greater: return lhs > rhs;
|
||||||
|
case Operator::GreaterEqual: return lhs >= rhs;
|
||||||
|
|
||||||
|
case Operator::BitAnd: return bit_and(lhs, rhs);
|
||||||
|
case Operator::BitOr: return bit_or(lhs, rhs);
|
||||||
|
case Operator::BitXor: return bit_xor(lhs, rhs);
|
||||||
|
case Operator::BitNot: return bit_not(lhs);
|
||||||
|
case Operator::ShiftLeft: return shift_left(lhs, rhs);
|
||||||
|
case Operator::ShiftRight: return shift_right(lhs, rhs);
|
||||||
|
|
||||||
|
case Operator::Walrus: {
|
||||||
|
static constexpr char WalrusErrorName[] = "WalrusError";
|
||||||
|
throw EvaluatorError<WalrusErrorName>(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw RuntimeError(FStringView(u8"Unsupported operator"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value Evaluator::evalBinary(const Ast::BinaryExpr &binExp)
|
||||||
|
{
|
||||||
|
return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp));
|
||||||
|
}
|
||||||
|
Value Evaluator::evalUnary(const Ast::UnaryExpr &unExp)
|
||||||
|
{
|
||||||
|
using Fig::Ast::Operator;
|
||||||
|
switch (unExp->op)
|
||||||
|
{
|
||||||
|
case Operator::Not:
|
||||||
|
return !eval(unExp->exp);
|
||||||
|
case Operator::Subtract:
|
||||||
|
return -eval(unExp->exp);
|
||||||
|
case Operator::BitNot:
|
||||||
|
return bit_not(eval(unExp->exp));
|
||||||
|
default:
|
||||||
|
throw RuntimeError(FStringView(std::format("Unsupported unary operator: {}", magic_enum::enum_name(unExp->op))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value Evaluator::eval(Ast::Expression exp)
|
||||||
|
{
|
||||||
|
using Fig::Ast::AstType;
|
||||||
|
switch (exp->getType())
|
||||||
|
{
|
||||||
|
case AstType::ValueExpr: {
|
||||||
|
auto valExp = std::dynamic_pointer_cast<Ast::ValueExprAst>(exp);
|
||||||
|
return valExp->val;
|
||||||
|
}
|
||||||
|
case AstType::VarExpr: {
|
||||||
|
auto varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(exp);
|
||||||
|
auto val = currentContext->get(varExp->name);
|
||||||
|
if (val.has_value())
|
||||||
|
{
|
||||||
|
return val.value();
|
||||||
|
}
|
||||||
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", varExp->name.toBasicString())));
|
||||||
|
}
|
||||||
|
case AstType::BinaryExpr: {
|
||||||
|
auto binExp = std::dynamic_pointer_cast<Ast::BinaryExprAst>(exp);
|
||||||
|
return evalBinary(binExp);
|
||||||
|
}
|
||||||
|
case AstType::UnaryExpr: {
|
||||||
|
auto unExp = std::dynamic_pointer_cast<Ast::UnaryExprAst>(exp);
|
||||||
|
return evalUnary(unExp);
|
||||||
|
}
|
||||||
|
case AstType::FunctionCall: {
|
||||||
|
// std::cerr << "Eval: function call...\n";
|
||||||
|
auto fnCall = std::dynamic_pointer_cast<Ast::FunctionCallExpr>(exp);
|
||||||
|
FString fnName = fnCall->name;
|
||||||
|
if (Builtins::isBuiltinFunction(fnName))
|
||||||
|
{
|
||||||
|
std::vector<Value> callArgs;
|
||||||
|
if (fnCall->arg.getLength() != Builtins::getBuiltinFunctionParamCount(fnName) and Builtins::getBuiltinFunctionParamCount(fnName) != -1) // -1 means variadic
|
||||||
|
{
|
||||||
|
static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError";
|
||||||
|
throw EvaluatorError<BuiltinArgumentMismatchErrorName>(FStringView(std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), Builtins::getBuiltinFunctionParamCount(fnName), callArgs.size())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
for (const auto &argExp : fnCall->arg.argv)
|
||||||
|
{
|
||||||
|
callArgs.push_back(eval(argExp));
|
||||||
|
}
|
||||||
|
return Builtins::getBuiltinFunction(fnName)(callArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fnValOpt = currentContext->get(fnName);
|
||||||
|
if (!fnValOpt.has_value())
|
||||||
|
{
|
||||||
|
static constexpr char FunctionNotFoundErrorName[] = "FunctionNotFoundError";
|
||||||
|
throw EvaluatorError<FunctionNotFoundErrorName>(FStringView(std::format("Function '{}' not defined", fnName.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
Value fnVal = fnValOpt.value();
|
||||||
|
if (!fnVal.is<Function>())
|
||||||
|
{
|
||||||
|
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
|
||||||
|
throw EvaluatorError<NotAFunctionErrorName>(FStringView(std::format("'{}' is not a function or callable", fnName.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
FunctionStruct fnStruct = fnVal.as<Function>().getValue();
|
||||||
|
// check argument, all types of parameters
|
||||||
|
Ast::FunctionParameters fnParas = fnStruct.paras;
|
||||||
|
Ast::FunctionArguments fnArgs = fnCall->arg;
|
||||||
|
if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
|
||||||
|
{
|
||||||
|
static constexpr char ArgumentMismatchErrorName[] = "ArgumentMismatchError";
|
||||||
|
throw EvaluatorError<ArgumentMismatchErrorName>(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::FunctionCallArgs evaluatedArgs;
|
||||||
|
|
||||||
|
// positional parameters type check
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < fnParas.posParas.size(); i++)
|
||||||
|
{
|
||||||
|
TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the name, use it, else throw
|
||||||
|
Value argVal = eval(fnArgs.argv[i]);
|
||||||
|
TypeInfo actualType = argVal.getTypeInfo();
|
||||||
|
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||||
|
{
|
||||||
|
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
||||||
|
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.posParas[i].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
evaluatedArgs.argv.push_back(argVal);
|
||||||
|
}
|
||||||
|
// default parameters type check
|
||||||
|
for (; i < fnArgs.getLength(); i++)
|
||||||
|
{
|
||||||
|
size_t defParamIndex = i - fnParas.posParas.size();
|
||||||
|
TypeInfo expectedType = fnParas.defParas[defParamIndex].second.first;
|
||||||
|
|
||||||
|
Value defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
||||||
|
if (expectedType != defaultVal.getTypeInfo() and expectedType != ValueType::Any)
|
||||||
|
{
|
||||||
|
static constexpr char DefaultParameterTypeErrorName[] = "DefaultParameterTypeError";
|
||||||
|
throw EvaluatorError<DefaultParameterTypeErrorName>(FStringView(std::format("In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), defaultVal.getTypeInfo().toString().toBasicString(), expectedType.toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value argVal = eval(fnArgs.argv[i]);
|
||||||
|
TypeInfo actualType = argVal.getTypeInfo();
|
||||||
|
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||||
|
{
|
||||||
|
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
||||||
|
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
evaluatedArgs.argv.push_back(argVal);
|
||||||
|
}
|
||||||
|
// default parameters filling
|
||||||
|
for (; i < fnParas.size(); i++)
|
||||||
|
{
|
||||||
|
size_t defParamIndex = i - fnParas.posParas.size();
|
||||||
|
Value defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
||||||
|
evaluatedArgs.argv.push_back(defaultVal);
|
||||||
|
}
|
||||||
|
// create new context for function call
|
||||||
|
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())), currentContext);
|
||||||
|
auto previousContext = currentContext;
|
||||||
|
currentContext = newContext;
|
||||||
|
// define parameters in new context
|
||||||
|
for (size_t j = 0; j < fnParas.size(); j++)
|
||||||
|
{
|
||||||
|
FString paramName;
|
||||||
|
TypeInfo paramType;
|
||||||
|
if (j < fnParas.posParas.size())
|
||||||
|
{
|
||||||
|
paramName = fnParas.posParas[j].first;
|
||||||
|
paramType = fnParas.posParas[j].second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t defParamIndex = j - fnParas.posParas.size();
|
||||||
|
paramName = fnParas.defParas[defParamIndex].first;
|
||||||
|
paramType = fnParas.defParas[defParamIndex].second.first;
|
||||||
|
}
|
||||||
|
AccessModifier argAm = AccessModifier::Const;
|
||||||
|
currentContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
|
||||||
|
}
|
||||||
|
// execute function body
|
||||||
|
Value retVal = Value::getNullInstance();
|
||||||
|
for (const auto &stmt : fnStruct.body->stmts)
|
||||||
|
{
|
||||||
|
StatementResult sr = evalStatement(stmt);
|
||||||
|
if (sr.shouldReturn())
|
||||||
|
{
|
||||||
|
retVal = sr.result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentContext = previousContext;
|
||||||
|
if (fnStruct.retType != retVal.getTypeInfo() and fnStruct.retType != ValueType::Any)
|
||||||
|
{
|
||||||
|
static constexpr char ReturnTypeMismatchErrorName[] = "ReturnTypeMismatchError";
|
||||||
|
throw EvaluatorError<ReturnTypeMismatchErrorName>(FStringView(std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), fnStruct.retType.toString().toBasicString(), retVal.getTypeInfo().toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
case AstType::ListExpr: {
|
||||||
|
auto listexpr = std::dynamic_pointer_cast<Ast::ListExprAst>(exp);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast<int>(exp->getType()))));
|
||||||
|
return Value::getNullInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatementResult Evaluator::evalStatement(const Ast::Statement &stmt)
|
||||||
|
{
|
||||||
|
using Fig::Ast::AstType;
|
||||||
|
switch (stmt->getType())
|
||||||
|
{
|
||||||
|
case AstType::VarDefSt: {
|
||||||
|
auto varDef = std::dynamic_pointer_cast<Ast::VarDefAst>(stmt);
|
||||||
|
if (currentContext->contains(varDef->name))
|
||||||
|
{
|
||||||
|
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||||
|
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Variable '{}' already defined in this scope", varDef->name.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
Value val;
|
||||||
|
TypeInfo varTypeInfo;
|
||||||
|
if (varDef->typeName == Parser::varDefTypeFollowed)
|
||||||
|
{
|
||||||
|
// has expr
|
||||||
|
val = eval(varDef->expr);
|
||||||
|
varTypeInfo = val.getTypeInfo();
|
||||||
|
}
|
||||||
|
else if (varDef->expr)
|
||||||
|
{
|
||||||
|
val = eval(varDef->expr);
|
||||||
|
if (varDef->typeName != ValueType::Any.name)
|
||||||
|
{
|
||||||
|
TypeInfo expectedType(varDef->typeName);
|
||||||
|
TypeInfo actualType = val.getTypeInfo();
|
||||||
|
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||||
|
{
|
||||||
|
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||||
|
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("Variable '{}' expects type '{}', but got type '{}'", varDef->name.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!varDef->typeName.empty())
|
||||||
|
{
|
||||||
|
varTypeInfo = TypeInfo(varDef->typeName); // may throw
|
||||||
|
val = Value::defaultValue(varTypeInfo);
|
||||||
|
}
|
||||||
|
AccessModifier am = (varDef->isPublic ? (varDef->isConst ? AccessModifier::PublicConst : AccessModifier::Public) : (varDef->isConst ? AccessModifier::Const : AccessModifier::Normal));
|
||||||
|
currentContext->def(varDef->name, varTypeInfo, am, val);
|
||||||
|
return StatementResult::normal();
|
||||||
|
}
|
||||||
|
case AstType::ExpressionStmt: {
|
||||||
|
auto exprSt = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(stmt);
|
||||||
|
eval(exprSt->exp);
|
||||||
|
return StatementResult::normal();
|
||||||
|
};
|
||||||
|
case AstType::BlockStatement: {
|
||||||
|
auto blockSt = std::dynamic_pointer_cast<Ast::BlockStatementAst>(stmt);
|
||||||
|
auto newContext = std::make_shared<Context>(FString(std::format("<Block {}:{}>", blockSt->getAAI().line, blockSt->getAAI().column)), currentContext);
|
||||||
|
auto previousContext = currentContext;
|
||||||
|
currentContext = newContext;
|
||||||
|
StatementResult lstResult = StatementResult::normal();
|
||||||
|
for (const auto &s : blockSt->stmts)
|
||||||
|
{
|
||||||
|
StatementResult sr = evalStatement(s);
|
||||||
|
if (!sr.isNormal())
|
||||||
|
{
|
||||||
|
lstResult = sr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentContext = previousContext;
|
||||||
|
return lstResult;
|
||||||
|
};
|
||||||
|
case AstType::FunctionDefSt: {
|
||||||
|
auto fnDef = std::dynamic_pointer_cast<Ast::FunctionDefSt>(stmt);
|
||||||
|
if (currentContext->contains(fnDef->name))
|
||||||
|
{
|
||||||
|
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||||
|
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Function '{}' already defined in this scope", fnDef->name.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
AccessModifier am = (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||||
|
currentContext->def(
|
||||||
|
fnDef->name,
|
||||||
|
ValueType::Function,
|
||||||
|
am,
|
||||||
|
Value(Function(
|
||||||
|
fnDef->paras,
|
||||||
|
TypeInfo(fnDef->retType),
|
||||||
|
fnDef->body)));
|
||||||
|
return StatementResult::normal();
|
||||||
|
};
|
||||||
|
case AstType::StructSt: {
|
||||||
|
auto stDef = std::dynamic_pointer_cast<Ast::StructDefSt>(stmt);
|
||||||
|
if (currentContext->contains(stDef->name))
|
||||||
|
{
|
||||||
|
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||||
|
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
std::vector<Field> fields;
|
||||||
|
std::vector<FString> _fieldNames;
|
||||||
|
for (Ast::StructDefField field : stDef->fields)
|
||||||
|
{
|
||||||
|
if (Utils::vectorContains(field.fieldName, _fieldNames))
|
||||||
|
{
|
||||||
|
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||||
|
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Field '{}' already defined in structure '{}'", field.fieldName.toBasicString(), stDef->name.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr));
|
||||||
|
}
|
||||||
|
ContextPtr defContext(currentContext);
|
||||||
|
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||||
|
currentContext->def(
|
||||||
|
stDef->name,
|
||||||
|
ValueType::StructType,
|
||||||
|
am,
|
||||||
|
Value(StructType(
|
||||||
|
defContext,
|
||||||
|
fields)));
|
||||||
|
return StatementResult::normal();
|
||||||
|
}
|
||||||
|
case AstType::VarAssignSt: {
|
||||||
|
auto varAssign = std::dynamic_pointer_cast<Ast::VarAssignSt>(stmt);
|
||||||
|
if (!currentContext->contains(varAssign->varName))
|
||||||
|
{
|
||||||
|
static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError";
|
||||||
|
throw EvaluatorError<VariableNotFoundErrorName>(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
if (!currentContext->isVariableMutable(varAssign->varName))
|
||||||
|
{
|
||||||
|
static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError";
|
||||||
|
throw EvaluatorError<ConstAssignmentErrorName>(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
Value val = eval(varAssign->valueExpr);
|
||||||
|
if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any)
|
||||||
|
{
|
||||||
|
TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName);
|
||||||
|
TypeInfo actualType = val.getTypeInfo();
|
||||||
|
if (expectedType != actualType)
|
||||||
|
{
|
||||||
|
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||||
|
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentContext->set(varAssign->varName, val);
|
||||||
|
return StatementResult::normal();
|
||||||
|
};
|
||||||
|
case AstType::IfSt: {
|
||||||
|
auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt);
|
||||||
|
Value condVal = eval(ifSt->condition);
|
||||||
|
if (condVal.getTypeInfo() != ValueType::Bool)
|
||||||
|
{
|
||||||
|
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||||
|
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"If condition must be boolean"), currentAddressInfo);
|
||||||
|
}
|
||||||
|
if (condVal.as<Bool>().getValue())
|
||||||
|
{
|
||||||
|
return evalStatement(ifSt->body);
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
for (const auto &elif : ifSt->elifs)
|
||||||
|
{
|
||||||
|
Value elifCondVal = eval(elif->condition);
|
||||||
|
if (elifCondVal.getTypeInfo() != ValueType::Bool)
|
||||||
|
{
|
||||||
|
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||||
|
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"Else-if condition must be boolean"), currentAddressInfo);
|
||||||
|
}
|
||||||
|
if (elifCondVal.as<Bool>().getValue())
|
||||||
|
{
|
||||||
|
return evalStatement(elif->body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ifSt->els)
|
||||||
|
{
|
||||||
|
return evalStatement(ifSt->els->body);
|
||||||
|
}
|
||||||
|
return StatementResult::normal();
|
||||||
|
};
|
||||||
|
case AstType::WhileSt: {
|
||||||
|
auto whileSt = std::dynamic_pointer_cast<Ast::WhileSt>(stmt);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Value condVal = eval(whileSt->condition);
|
||||||
|
if (condVal.getTypeInfo() != ValueType::Bool)
|
||||||
|
{
|
||||||
|
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||||
|
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"While condition must be boolean"), currentAddressInfo);
|
||||||
|
}
|
||||||
|
if (!condVal.as<Bool>().getValue())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
StatementResult sr = evalStatement(whileSt->body);
|
||||||
|
if (sr.shouldReturn())
|
||||||
|
{
|
||||||
|
return sr;
|
||||||
|
}
|
||||||
|
if (sr.shouldBreak())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (sr.shouldContinue())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StatementResult::normal();
|
||||||
|
};
|
||||||
|
case AstType::ReturnSt: {
|
||||||
|
if (!currentContext->parent)
|
||||||
|
{
|
||||||
|
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||||
|
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||||
|
}
|
||||||
|
std::shared_ptr<Context> fc = currentContext;
|
||||||
|
while (fc->parent)
|
||||||
|
{
|
||||||
|
if (fc->getScopeName().find(u8"<Function ") == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fc = fc->parent;
|
||||||
|
}
|
||||||
|
if (fc->getScopeName().find(u8"<Function ") != 0)
|
||||||
|
{
|
||||||
|
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||||
|
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||||
|
}
|
||||||
|
auto returnSt = std::dynamic_pointer_cast<Ast::ReturnSt>(stmt);
|
||||||
|
return StatementResult::returnFlow(eval(returnSt->retValue));
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
throw RuntimeError(FStringView(std::string("Unknown statement type:") + magic_enum::enum_name(stmt->getType()).data()));
|
||||||
|
}
|
||||||
|
return StatementResult::normal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Evaluator::run()
|
||||||
|
{
|
||||||
|
for (auto ast : asts)
|
||||||
|
{
|
||||||
|
currentAddressInfo = ast->getAAI();
|
||||||
|
if (std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast))
|
||||||
|
{
|
||||||
|
auto exprAst = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast);
|
||||||
|
Ast::Expression exp = exprAst->exp;
|
||||||
|
eval(exp);
|
||||||
|
}
|
||||||
|
else if (dynamic_cast<Ast::StatementAst *>(ast.get()))
|
||||||
|
{
|
||||||
|
auto stmtAst = std::dynamic_pointer_cast<Ast::StatementAst>(ast);
|
||||||
|
evalStatement(stmtAst);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(u8"Unknown AST type"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Evaluator::printStackTrace() const
|
||||||
|
{
|
||||||
|
if (currentContext)
|
||||||
|
currentContext->printStackTrace();
|
||||||
|
else
|
||||||
|
std::cerr << "[STACK TRACE] (No context has been loaded)\n";
|
||||||
|
}
|
||||||
|
} // namespace Fig
|
||||||
524
src/lexer.cpp
Normal file
524
src/lexer.cpp
Normal file
@@ -0,0 +1,524 @@
|
|||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <error.hpp>
|
||||||
|
#include <token.hpp>
|
||||||
|
#include <lexer.hpp>
|
||||||
|
|
||||||
|
#include <fig_string.hpp>
|
||||||
|
#include <utils.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
const std::unordered_map<FString, TokenType> Lexer::symbol_map{
|
||||||
|
// 双字符
|
||||||
|
{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::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}};
|
||||||
|
|
||||||
|
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"fun"), 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"struct"), TokenType::Struct},
|
||||||
|
{FString(u8"interface"), TokenType::Interface},
|
||||||
|
{FString(u8"implement"), TokenType::Implement},
|
||||||
|
{FString(u8"public"), TokenType::Public},
|
||||||
|
{FString(u8"return"), TokenType::Return},
|
||||||
|
|
||||||
|
|
||||||
|
// {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},
|
||||||
|
};
|
||||||
|
void Lexer::skipLine()
|
||||||
|
{
|
||||||
|
while (*it != U'\n' and hasNext())
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
next(); // skip '\n'
|
||||||
|
++line;
|
||||||
|
}
|
||||||
|
Token Lexer::scanIdentifier()
|
||||||
|
{
|
||||||
|
FString identifier;
|
||||||
|
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
UTF8Char c = *it;
|
||||||
|
if (c.isAlnum() || c == U'_')
|
||||||
|
{
|
||||||
|
identifier += c.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
if (identifier.length() <= 1)
|
||||||
|
{
|
||||||
|
pushWarning(2, identifier); // The identifier is too abstract
|
||||||
|
}
|
||||||
|
return Token(identifier, TokenType::Identifier);
|
||||||
|
}
|
||||||
|
Token Lexer::scanString()
|
||||||
|
{
|
||||||
|
FString str;
|
||||||
|
bool unterminated = true;
|
||||||
|
size_t str_start_col = it.column() - 1;
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
UTF8Char c = *it;
|
||||||
|
if (c == U'"' || c == U'\n')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
unterminated = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (c == U'\\') // c is '\'
|
||||||
|
{
|
||||||
|
if (it.isEnd())
|
||||||
|
{
|
||||||
|
error = SyntaxError(u8"Unterminated FString", this->line, it.column());
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
UTF8Char ec = *it;
|
||||||
|
if (ec == U'n')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\n";
|
||||||
|
}
|
||||||
|
else if (ec == U't')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\t";
|
||||||
|
}
|
||||||
|
else if (ec == U'v')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\v";
|
||||||
|
}
|
||||||
|
else if (ec == U'b')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\b";
|
||||||
|
}
|
||||||
|
else if (ec == U'"')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\"";
|
||||||
|
}
|
||||||
|
else if (ec == U'\'')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"'";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = SyntaxError(FStringView(
|
||||||
|
std::format(
|
||||||
|
"Unsupported escape character: {}",
|
||||||
|
FString(ec.getString()).toBasicString())),
|
||||||
|
this->line,
|
||||||
|
it.column());
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str += c.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unterminated)
|
||||||
|
{
|
||||||
|
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
return Token(str, TokenType::LiteralString);
|
||||||
|
}
|
||||||
|
Token Lexer::scanRawString()
|
||||||
|
{
|
||||||
|
FString str;
|
||||||
|
bool unterminated = true;
|
||||||
|
size_t str_start_col = it.column() - 1;
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
UTF8Char c = *it;
|
||||||
|
if (c == U'"' || c == U'\n')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
unterminated = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str += c.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unterminated)
|
||||||
|
{
|
||||||
|
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
return Token(str, TokenType::LiteralString);
|
||||||
|
}
|
||||||
|
Token Lexer::scanMultilineString()
|
||||||
|
{
|
||||||
|
FString str;
|
||||||
|
bool unterminated = true;
|
||||||
|
|
||||||
|
uint8_t end = 0;
|
||||||
|
size_t str_start_col = it.column() - 1;
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
UTF8Char c = *it;
|
||||||
|
if (c == U'"')
|
||||||
|
{
|
||||||
|
if (end == 3)
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
unterminated = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
end++;
|
||||||
|
next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (c == U'\\') // c is '\'
|
||||||
|
{
|
||||||
|
if (it.isEnd())
|
||||||
|
{
|
||||||
|
error = SyntaxError(u8"Unterminated FString", this->line, it.column());
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
UTF8Char ec = *it;
|
||||||
|
if (ec == U'n')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\n";
|
||||||
|
}
|
||||||
|
else if (ec == U't')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\t";
|
||||||
|
}
|
||||||
|
else if (ec == U'v')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\v";
|
||||||
|
}
|
||||||
|
else if (ec == U'b')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\b";
|
||||||
|
}
|
||||||
|
else if (ec == U'"')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\"";
|
||||||
|
}
|
||||||
|
else if (ec == U'\'')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"'";
|
||||||
|
}
|
||||||
|
else if (ec == U'\\')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
str += u8"\\";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = SyntaxError(FStringView(
|
||||||
|
std::format(
|
||||||
|
"Unsupported escape character: {}",
|
||||||
|
FString(ec.getString()).toBasicString())),
|
||||||
|
this->line,
|
||||||
|
it.column());
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
str += c.getString();
|
||||||
|
}
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
if (unterminated)
|
||||||
|
{
|
||||||
|
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
return Token(str, TokenType::LiteralString);
|
||||||
|
}
|
||||||
|
Token Lexer::scanNumber()
|
||||||
|
{
|
||||||
|
FString numStr;
|
||||||
|
bool hasPoint = false;
|
||||||
|
// 负号(减号) 直接交由 scanSymbol处理,在parser中被分类->与数字结合/变为操作数
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
UTF8Char ch = *it;
|
||||||
|
if (ch.isDigit() or ch == U'e') // . / e / - for scientific counting
|
||||||
|
{
|
||||||
|
numStr += ch.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else if (ch == U'-' and numStr.ends_with(U'-'))
|
||||||
|
{
|
||||||
|
numStr += ch.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else if (ch == U'.' and not hasPoint)
|
||||||
|
{
|
||||||
|
hasPoint = true;
|
||||||
|
numStr += ch.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Numbers in Fig-lang
|
||||||
|
/*
|
||||||
|
114514
|
||||||
|
1145.14
|
||||||
|
1.14e3 -> 1140
|
||||||
|
1.14e-3 -> 0.00114
|
||||||
|
.3 -> 0.3
|
||||||
|
*/
|
||||||
|
// checking legality
|
||||||
|
if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数
|
||||||
|
{
|
||||||
|
error = SyntaxError(FStringView(
|
||||||
|
std::format("Ellegal number literal: {}", numStr.toBasicString())),
|
||||||
|
this->line, it.column());
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
return Token(numStr, TokenType::LiteralNumber);
|
||||||
|
}
|
||||||
|
Token Lexer::scanSymbol()
|
||||||
|
{
|
||||||
|
FString sym;
|
||||||
|
UTF8Char ch = *it;
|
||||||
|
|
||||||
|
sym += ch.getString();
|
||||||
|
UTF8Char peek = UTF8Char(u8"");
|
||||||
|
if (hasNext() and (peek = it.peek()).isPunct()) // 窥探下一个操作符
|
||||||
|
{
|
||||||
|
FString symd = FString(sym + peek.getString());
|
||||||
|
if (this->symbol_map.contains(symd))
|
||||||
|
{
|
||||||
|
// Operator length is 2
|
||||||
|
next();
|
||||||
|
sym = symd;
|
||||||
|
}
|
||||||
|
// Operator length is 1
|
||||||
|
else if (!this->symbol_map.contains(sym))
|
||||||
|
{
|
||||||
|
// check legality
|
||||||
|
error = SyntaxError(FStringView(
|
||||||
|
std::format("No such a operator: {}", sym.toBasicString())),
|
||||||
|
this->line, it.column());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
return Token(sym, this->symbol_map.at(sym)); // const object 'symbol_map', operator[] call is invalid
|
||||||
|
}
|
||||||
|
Token Lexer::scanComments()
|
||||||
|
{
|
||||||
|
// entry: when iterator current char is '/' and peek is '/' or '*'
|
||||||
|
// current char is '/'
|
||||||
|
FString comment;
|
||||||
|
if (it.peek() == U'/')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
next();
|
||||||
|
UTF8Char c = *it;
|
||||||
|
while (c != U'\n' and hasNext())
|
||||||
|
{
|
||||||
|
comment += c.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
next();
|
||||||
|
UTF8Char c = *it;
|
||||||
|
bool terminated = false;
|
||||||
|
while (hasNext())
|
||||||
|
{
|
||||||
|
if (c == U'*' and hasNext() and it.peek() == U'/')
|
||||||
|
{
|
||||||
|
next(); // skip '*'
|
||||||
|
next(); // skip '/'
|
||||||
|
next(); // to next char
|
||||||
|
terminated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
comment += c.getString();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!terminated)
|
||||||
|
{
|
||||||
|
error = SyntaxError(FStringView(u8"Unterminated multiline comment"), this->line, it.column());
|
||||||
|
next();
|
||||||
|
return IllegalTok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Token(comment, TokenType::Comments);
|
||||||
|
}
|
||||||
|
Token Lexer::nextToken()
|
||||||
|
{
|
||||||
|
if (!hasNext())
|
||||||
|
{
|
||||||
|
return EOFTok;
|
||||||
|
}
|
||||||
|
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'r' and hasNext() and it.peek() == U'"')
|
||||||
|
{
|
||||||
|
// r""
|
||||||
|
// raw FString
|
||||||
|
next();
|
||||||
|
next();
|
||||||
|
return scanRawString().setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
if (ch.isAlpha() || 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 == U'/')
|
||||||
|
{
|
||||||
|
UTF8Char c{u8""};
|
||||||
|
if (!hasNext())
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
c = it.peek();
|
||||||
|
if (c != U'/' and c != U'*')
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
return scanComments().setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
else if (ch.isPunct())
|
||||||
|
{
|
||||||
|
return scanSymbol().setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = SyntaxError(FStringView(
|
||||||
|
std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())),
|
||||||
|
this->line, it.column());
|
||||||
|
if (hasNext())
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
return IllegalTok.setPos(last_line, last_column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Fig
|
||||||
187
src/main.cpp
Normal file
187
src/main.cpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
███████████ █████ █████ ██████████ ███████████ █████ █████████ █████ █████████ ██████ █████ █████████ █████ █████ █████████ █████████ ██████████
|
||||||
|
░█░░░███░░░█░░███ ░░███ ░░███░░░░░█ ░░███░░░░░░█░░███ ███░░░░░███ ░░███ ███░░░░░███ ░░██████ ░░███ ███░░░░░███░░███ ░░███ ███░░░░░███ ███░░░░░███░░███░░░░░█
|
||||||
|
░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ ███ ░░░ ░███ ░███ ░███ ░███░███ ░███ ███ ░░░ ░███ ░███ ░███ ░███ ███ ░░░ ░███ █ ░
|
||||||
|
░███ ░███████████ ░██████ ░███████ ░███ ░███ ░███ ░███████████ ░███░░███░███ ░███ ░███ ░███ ░███████████ ░███ ░██████
|
||||||
|
░███ ░███░░░░░███ ░███░░█ ░███░░░█ ░███ ░███ █████ ░███ ░███░░░░░███ ░███ ░░██████ ░███ █████ ░███ ░███ ░███░░░░░███ ░███ █████ ░███░░█
|
||||||
|
░███ ░███ ░███ ░███ ░ █ ░███ ░ ░███ ░░███ ░░███ ░███ █ ░███ ░███ ░███ ░░█████ ░░███ ░░███ ░███ ░███ ░███ ░███ ░░███ ░░███ ░███ ░ █
|
||||||
|
█████ █████ █████ ██████████ █████ █████ ░░█████████ ███████████ █████ █████ █████ ░░█████ ░░█████████ ░░████████ █████ █████ ░░█████████ ██████████
|
||||||
|
░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░
|
||||||
|
|
||||||
|
|
||||||
|
.---.
|
||||||
|
. __.....__ .--. | | _..._ __.....__
|
||||||
|
.'| .-'' '. _.._ |__| .--./) | | .' '. .--./) .--./) .-'' '.
|
||||||
|
.| < | / .-''"'-. `. .' .._|.--. /.''\\ | | . .-. . /.''\\ /.''\\ / .-''"'-. `.
|
||||||
|
.' |_ | | / /________\ \ | ' | || | | | | | __ | ' ' || | | | __ | | | |/ /________\ \
|
||||||
|
.' || | .'''-. | | __| |__ | | \`-' / | | .:--.'. | | | | \`-' / _ _ .:--.'. \`-' / | |
|
||||||
|
'--. .-'| |/.'''. \\ .-------------' |__ __| | | /("'` | |/ | \ | | | | | /("'` | ' / | / | \ | /("'` \ .-------------'
|
||||||
|
| | | / | | \ '-.____...---. | | | | \ '---. | |`" __ | | | | | | \ '---. .' | .' | `" __ | | \ '---. \ '-.____...---.
|
||||||
|
| | | | | | `. .' | | |__| /'""'.\ | | .'.''| | | | | | /'""'.\ / | / | .'.''| | /'""'.\ `. .'
|
||||||
|
| '.'| | | | `''-...... -' | | || || '---'/ / | |_| | | | || ||| `'. | / / | |_|| || `''-...... -'
|
||||||
|
| / | '. | '. | | \'. __// \ \._,\ '/| | | | \'. __// ' .'| '/\ \._,\ '/\'. __//
|
||||||
|
`'-' '---' '---' |_| `'---' `--' `" '--' '--' `'---' `-' `--' `--' `" `'---'
|
||||||
|
|
||||||
|
Copyright (C) 2020-2025 PuqiAR
|
||||||
|
|
||||||
|
This software is licensed under the MIT License. See LICENSE.txt for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <argparse/argparse.hpp>
|
||||||
|
#include <print>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <core.hpp>
|
||||||
|
#include <lexer.hpp>
|
||||||
|
#include <parser.hpp>
|
||||||
|
#include <evaluator.hpp>
|
||||||
|
#include <AstPrinter.hpp>
|
||||||
|
#include <errorLog.hpp>
|
||||||
|
|
||||||
|
static size_t addressableErrorCount = 0;
|
||||||
|
static size_t unaddressableErrorCount = 0;
|
||||||
|
|
||||||
|
std::vector<FString> splitSource(FString source)
|
||||||
|
{
|
||||||
|
UTF8Iterator it(source);
|
||||||
|
std::vector<FString> lines;
|
||||||
|
FString currentLine;
|
||||||
|
while (!it.isEnd())
|
||||||
|
{
|
||||||
|
UTF8Char c = *it;
|
||||||
|
if (c == U'\n')
|
||||||
|
{
|
||||||
|
lines.push_back(currentLine);
|
||||||
|
currentLine = FString(u8"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentLine += c.getString();
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
if (!currentLine.empty())
|
||||||
|
{
|
||||||
|
lines.push_back(currentLine);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
argparse::ArgumentParser program("Fig Interpreter", Fig::Core::VERSION.data());
|
||||||
|
program.add_argument("source")
|
||||||
|
.help("source file to be interpreted");
|
||||||
|
// interpreter
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
program.parse_args(argc, argv);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Fig::FString sourcePath(program.get<std::string>("source"));
|
||||||
|
std::ifstream file(sourcePath.toBasicString());
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "Could not open file: " << sourcePath.toBasicString() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string source((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
Fig::Lexer lexer((Fig::FString(source)));
|
||||||
|
Fig::Parser parser(lexer);
|
||||||
|
std::vector<Fig::Ast::AstBase> ast;
|
||||||
|
|
||||||
|
std::vector<FString> sourceLines = splitSource(Fig::FString(source));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ast = parser.parseAll();
|
||||||
|
}
|
||||||
|
catch (const Fig::AddressableError &e)
|
||||||
|
{
|
||||||
|
addressableErrorCount++;
|
||||||
|
ErrorLog::logAddressableError(e, sourcePath, sourceLines);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const Fig::UnaddressableError &e)
|
||||||
|
{
|
||||||
|
unaddressableErrorCount++;
|
||||||
|
ErrorLog::logUnaddressableError(e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << "uncaught exception of: " << e.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token tok;
|
||||||
|
// while ((tok = lexer.nextToken()).getType() != TokenType::EndOfFile)
|
||||||
|
// {
|
||||||
|
// std::println("{}", tok.toString().toBasicString());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// AstPrinter printer;
|
||||||
|
// std::print("<Debug> AST:\n");
|
||||||
|
// for (const auto &node : ast)
|
||||||
|
// {
|
||||||
|
// printer.print(node);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Fig::Evaluator evaluator(ast);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
evaluator.run();
|
||||||
|
}
|
||||||
|
catch (const Fig::AddressableError &e)
|
||||||
|
{
|
||||||
|
addressableErrorCount++;
|
||||||
|
ErrorLog::logAddressableError(e, sourcePath, sourceLines);
|
||||||
|
evaluator.printStackTrace();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const Fig::UnaddressableError &e)
|
||||||
|
{
|
||||||
|
unaddressableErrorCount++;
|
||||||
|
ErrorLog::logUnaddressableError(e);
|
||||||
|
evaluator.printStackTrace();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// std::vector<Fig::Ast> ast = parser.parseAll();
|
||||||
|
// AstPrinter printer;
|
||||||
|
|
||||||
|
// std::print("<Debug> AST:\n");
|
||||||
|
// for (const auto &node : ast)
|
||||||
|
// {
|
||||||
|
// printer.print(node);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Fig::Evaluator evaluator(ast);
|
||||||
|
// evaluator.run();
|
||||||
|
// }
|
||||||
|
// catch (const Fig::AddressableError &e)
|
||||||
|
// {
|
||||||
|
// std::cerr << e.what() << '\n';
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
// catch (const Fig::UnaddressableError &e)
|
||||||
|
// {
|
||||||
|
// std::cerr << e.what() << '\n';
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
// catch (const std::exception &e)
|
||||||
|
// {
|
||||||
|
// std::cerr << e.what() << '\n';
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
}
|
||||||
848
src/parser.cpp
Normal file
848
src/parser.cpp
Normal file
@@ -0,0 +1,848 @@
|
|||||||
|
#include <parser.hpp>
|
||||||
|
|
||||||
|
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::And, {5, 6}},
|
||||||
|
{Ast::Operator::Or, {4, 5}},
|
||||||
|
{Ast::Operator::Not, {30, 31}}, // 一元
|
||||||
|
|
||||||
|
// 比较
|
||||||
|
{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::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::Walrus, {2, 1}}, // 右结合
|
||||||
|
|
||||||
|
// 点运算符
|
||||||
|
{Ast::Operator::Dot, {40, 41}},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
||||||
|
{
|
||||||
|
// entry: current is keyword `var` or `const`
|
||||||
|
bool isConst = (currentToken().getType() == TokenType::Const ? true : false);
|
||||||
|
next();
|
||||||
|
expect(TokenType::Identifier);
|
||||||
|
FString name = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
FString tiName = ValueType::Any.name;
|
||||||
|
bool hasSpecificType = false;
|
||||||
|
if (isThis(TokenType::Colon)) // :
|
||||||
|
{
|
||||||
|
expectPeek(TokenType::Identifier, FString(u8"Type name"));
|
||||||
|
next();
|
||||||
|
tiName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
hasSpecificType = true;
|
||||||
|
}
|
||||||
|
if (isThis(TokenType::Semicolon))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, nullptr);
|
||||||
|
}
|
||||||
|
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus");
|
||||||
|
if (isThis(TokenType::Walrus))
|
||||||
|
{
|
||||||
|
if (hasSpecificType) throwAddressableError<SyntaxError>(FStringView(u8""));
|
||||||
|
tiName = Parser::varDefTypeFollowed;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
Ast::Expression exp = parseExpression(0);
|
||||||
|
expect(TokenType::Semicolon);
|
||||||
|
next();
|
||||||
|
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value Parser::__parseValue()
|
||||||
|
{
|
||||||
|
FString _val = currentToken().getValue();
|
||||||
|
if (currentToken().getType() == TokenType::LiteralNumber)
|
||||||
|
{
|
||||||
|
if (_val.contains(u8'.') || _val.contains(u8'e'))
|
||||||
|
{
|
||||||
|
// 非整数
|
||||||
|
ValueType::DoubleClass d;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
d = std::stod(_val.toBasicString());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
|
||||||
|
}
|
||||||
|
return Value(d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 整数
|
||||||
|
ValueType::IntClass i;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
i = std::stoi(_val.toBasicString());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
|
||||||
|
}
|
||||||
|
return Value(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (currentToken().getType() == TokenType::LiteralString)
|
||||||
|
{
|
||||||
|
return Value(_val);
|
||||||
|
}
|
||||||
|
else if (currentToken().getType() == TokenType::LiteralBool)
|
||||||
|
{
|
||||||
|
return Value((_val == u8"true" ? true : false));
|
||||||
|
}
|
||||||
|
else if (currentToken().getType() == TokenType::LiteralNull)
|
||||||
|
{
|
||||||
|
return Value::getNullInstance();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Internal Error at: ") + std::string(__func__));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::ValueExpr Parser::__parseValueExpr()
|
||||||
|
{
|
||||||
|
return Ast::ValueExpr(new Ast::ValueExprAst(__parseValue()));
|
||||||
|
}
|
||||||
|
Ast::FunctionParameters Parser::__parseFunctionParameters()
|
||||||
|
{
|
||||||
|
// entry: current is Token::LeftParen
|
||||||
|
// stop: current is `)` next one
|
||||||
|
// *note: must called when parsing function
|
||||||
|
|
||||||
|
next(); // skip `(`
|
||||||
|
|
||||||
|
Ast::FunctionParameters::PosParasType pp;
|
||||||
|
Ast::FunctionParameters::DefParasType dp;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (isThis(TokenType::RightParen))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return Ast::FunctionParameters(pp, dp);
|
||||||
|
}
|
||||||
|
expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current
|
||||||
|
FString pname = currentToken().getValue();
|
||||||
|
next(); // skip pname
|
||||||
|
if (isThis(TokenType::Assign)) // =
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
dp.push_back({pname, {ValueType::Any.name, parseExpression(0, TokenType::Comma)}});
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // only skip `,` when it's there
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Colon)) // :
|
||||||
|
{
|
||||||
|
next(); // skip `:`
|
||||||
|
expect(TokenType::Identifier, FString(u8"Type name"));
|
||||||
|
FString ti(currentToken().getValue());
|
||||||
|
next(); // skip type name
|
||||||
|
if (isThis(TokenType::Assign)) // =
|
||||||
|
{
|
||||||
|
next(); // skip `=`
|
||||||
|
dp.push_back({pname, {ti, parseExpression(0, TokenType::Comma)}});
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // only skip `,` when it's there
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pp.push_back({pname, ti});
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // only skip `,` when it's there
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pp.push_back({pname, ValueType::Any.name});
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // only skip `,` when it's there
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ast::FunctionDef Parser::__parseFunctionDef(bool isPublic)
|
||||||
|
{
|
||||||
|
FString funcName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
expect(TokenType::LeftParen);
|
||||||
|
Ast::FunctionParameters params = __parseFunctionParameters();
|
||||||
|
FString retTiName = ValueType::Any.name;
|
||||||
|
if (isThis(TokenType::RightArrow)) // ->
|
||||||
|
{
|
||||||
|
next(); // skip `->`
|
||||||
|
expect(TokenType::Identifier);
|
||||||
|
retTiName = currentToken().getValue();
|
||||||
|
next(); // skip return type
|
||||||
|
}
|
||||||
|
expect(TokenType::LeftBrace);
|
||||||
|
Ast::BlockStatement body = __parseBlockStatement();
|
||||||
|
return makeAst<Ast::FunctionDefSt>(funcName, params, isPublic, retTiName, body);
|
||||||
|
}
|
||||||
|
Ast::StructDef Parser::__parseStructDef(bool isPublic)
|
||||||
|
{
|
||||||
|
// entry: current is struct name
|
||||||
|
FString structName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
expect(TokenType::LeftBrace, u8"struct body");
|
||||||
|
next();
|
||||||
|
bool braceClosed = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
public name
|
||||||
|
public const name
|
||||||
|
public final name
|
||||||
|
|
||||||
|
const name
|
||||||
|
final name
|
||||||
|
|
||||||
|
name
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto __parseStructField = [this](bool isPublic) -> Ast::StructDefField {
|
||||||
|
AccessModifier am = AccessModifier::Normal;
|
||||||
|
FString fieldName;
|
||||||
|
if (isThis(TokenType::Identifier))
|
||||||
|
{
|
||||||
|
fieldName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
am = (isPublic ? AccessModifier::Public : AccessModifier::Normal);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Final))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
expect(TokenType::Identifier, u8"field name");
|
||||||
|
fieldName = currentToken().getValue();
|
||||||
|
am = (isPublic ? AccessModifier::PublicFinal : AccessModifier::Final);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Const))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
expect(TokenType::Identifier, u8"field name");
|
||||||
|
fieldName = currentToken().getValue();
|
||||||
|
am = (isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(std::format("expect field name or field attribute")));
|
||||||
|
}
|
||||||
|
FString tiName = ValueType::Any.name;
|
||||||
|
if (isThis(TokenType::Colon))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
expect(TokenType::Identifier, u8"type name");
|
||||||
|
tiName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
Ast::Expression initExpr = nullptr;
|
||||||
|
if (isThis(TokenType::Assign))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
if (isEOF()) throwAddressableError<SyntaxError>(FStringView(u8"expect an expression"));
|
||||||
|
initExpr = parseExpression(0);
|
||||||
|
}
|
||||||
|
expect(TokenType::Semicolon);
|
||||||
|
next(); // consume `;`
|
||||||
|
return Ast::StructDefField(am, fieldName, tiName, initExpr);
|
||||||
|
};
|
||||||
|
std::vector<Ast::Statement> stmts;
|
||||||
|
std::vector<Ast::StructDefField> fields;
|
||||||
|
|
||||||
|
while (!isEOF())
|
||||||
|
{
|
||||||
|
if (isThis(TokenType::RightBrace))
|
||||||
|
{
|
||||||
|
braceClosed = true;
|
||||||
|
next(); // consume `}`
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isThis(TokenType::Identifier))
|
||||||
|
{
|
||||||
|
fields.push_back(__parseStructField(false));
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Public))
|
||||||
|
{
|
||||||
|
if (isNext(TokenType::Const) or isNext(TokenType::Final))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
fields.push_back(__parseStructField(true));
|
||||||
|
}
|
||||||
|
else if (isNext(TokenType::Function))
|
||||||
|
{
|
||||||
|
next(); // consume `public`
|
||||||
|
next(); // consume `function`
|
||||||
|
stmts.push_back(__parseFunctionDef(true));
|
||||||
|
}
|
||||||
|
else if (isNext(TokenType::Struct))
|
||||||
|
{
|
||||||
|
next(); // consume `public`
|
||||||
|
next(); // consume `struct`
|
||||||
|
stmts.push_back(__parseStructDef(true));
|
||||||
|
}
|
||||||
|
else if (isNext(TokenType::Identifier))
|
||||||
|
{
|
||||||
|
next(); // consume `public`
|
||||||
|
fields.push_back(__parseStructField(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Function))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
stmts.push_back(__parseFunctionDef(false));
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Struct))
|
||||||
|
{
|
||||||
|
next(); // consume `struct`
|
||||||
|
stmts.push_back(__parseStructDef(false));
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Const) or isThis(TokenType::Final))
|
||||||
|
{
|
||||||
|
fields.push_back(__parseStructField(false));
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Variable))
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView("Variables are not allowed to be defined within a structure."));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!braceClosed)
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView("braces are not closed"));
|
||||||
|
}
|
||||||
|
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts));
|
||||||
|
}
|
||||||
|
Ast::Statement Parser::__parseStatement()
|
||||||
|
{
|
||||||
|
Ast::Statement stmt;
|
||||||
|
if (isThis(TokenType::EndOfFile)) { return makeAst<Ast::EofStmt>(); }
|
||||||
|
if (isThis(TokenType::Public))
|
||||||
|
{
|
||||||
|
// stmt = __parseVarDef();
|
||||||
|
// expect(TokenType::Semicolon);
|
||||||
|
// next();
|
||||||
|
if (isNext(TokenType::Variable) || isNext(TokenType::Const))
|
||||||
|
{
|
||||||
|
next(); // consume `public`
|
||||||
|
stmt = __parseVarDef(true);
|
||||||
|
}
|
||||||
|
else if (isNext(TokenType::Function))
|
||||||
|
{
|
||||||
|
next(); // consume `public`
|
||||||
|
expectPeek(TokenType::Identifier);
|
||||||
|
next();
|
||||||
|
stmt = __parseFunctionDef(true);
|
||||||
|
}
|
||||||
|
else if (isNext(TokenType::Struct))
|
||||||
|
{
|
||||||
|
stmt = __parseStructDef(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Expected `var`, `const`, `function` or `struct` after `public`"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Variable) || isThis(TokenType::Const))
|
||||||
|
{
|
||||||
|
stmt = __parseVarDef(false);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Function))
|
||||||
|
{
|
||||||
|
expectPeek(TokenType::Identifier, u8"function name");
|
||||||
|
next();
|
||||||
|
stmt = __parseFunctionDef(false);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Struct))
|
||||||
|
{
|
||||||
|
expectPeek(TokenType::Identifier, u8"struct name");
|
||||||
|
next();
|
||||||
|
stmt = __parseStructDef(false);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Identifier) and isNext(TokenType::Assign))
|
||||||
|
{
|
||||||
|
FString varName = currentToken().getValue();
|
||||||
|
next(); // consume identifier
|
||||||
|
stmt = __parseVarAssign(varName);
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::If))
|
||||||
|
{
|
||||||
|
stmt = __parseIf();
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Else))
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"`else` without matching `if`"));
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::LeftBrace))
|
||||||
|
{
|
||||||
|
stmt = __parseBlockStatement();
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::While))
|
||||||
|
{
|
||||||
|
stmt = __parseWhile();
|
||||||
|
}
|
||||||
|
else if (isThis(TokenType::Return))
|
||||||
|
{
|
||||||
|
stmt = __parseReturn();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// expression statement
|
||||||
|
Ast::Expression exp = parseExpression(0);
|
||||||
|
expect(TokenType::Semicolon);
|
||||||
|
next();
|
||||||
|
stmt = makeAst<Ast::ExpressionStmtAst>(exp);
|
||||||
|
}
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
Ast::BlockStatement Parser::__parseBlockStatement()
|
||||||
|
{
|
||||||
|
// entry: current is `{`
|
||||||
|
// stop: current is `}` next one
|
||||||
|
next(); // consume `{`
|
||||||
|
std::vector<Ast::Statement> stmts;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (isThis(TokenType::RightBrace))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return makeAst<Ast::BlockStatementAst>(stmts);
|
||||||
|
}
|
||||||
|
stmts.push_back(__parseStatement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ast::VarAssign Parser::__parseVarAssign(FString varName)
|
||||||
|
{
|
||||||
|
// entry: current is `=`
|
||||||
|
next(); // consume `=`
|
||||||
|
Ast::Expression exp = parseExpression(0);
|
||||||
|
expect(TokenType::Semicolon);
|
||||||
|
next(); // consume `;`
|
||||||
|
return makeAst<Ast::VarAssignSt>(varName, exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::If Parser::__parseIf()
|
||||||
|
{
|
||||||
|
// entry: current is `if`
|
||||||
|
next(); // consume `if`
|
||||||
|
Ast::Expression condition;
|
||||||
|
if (isThis( TokenType::LeftParen))
|
||||||
|
{
|
||||||
|
next(); // consume `(`
|
||||||
|
condition = parseExpression(0, TokenType::RightParen);
|
||||||
|
expect(TokenType::RightParen);
|
||||||
|
next(); // consume `)`
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
condition = parseExpression(0);
|
||||||
|
}
|
||||||
|
// parenthesis is not required
|
||||||
|
expect(TokenType::LeftBrace); // {
|
||||||
|
Ast::BlockStatement body = __parseBlockStatement();
|
||||||
|
std::vector<Ast::ElseIf> elifs;
|
||||||
|
Ast::Else els = nullptr;
|
||||||
|
while (isThis(TokenType::Else))
|
||||||
|
{
|
||||||
|
next(); // consume `else`
|
||||||
|
if (isThis(TokenType::If))
|
||||||
|
{
|
||||||
|
// else if
|
||||||
|
next(); // consume `if`
|
||||||
|
Ast::Expression elifCondition = parseExpression(0);
|
||||||
|
expect(TokenType::LeftBrace); // {
|
||||||
|
Ast::BlockStatement elifBody = __parseBlockStatement();
|
||||||
|
elifs.push_back(makeAst<Ast::ElseIfSt>(elifCondition, elifBody));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
expect(TokenType::LeftBrace); // {
|
||||||
|
Ast::BlockStatement elseBody = __parseBlockStatement();
|
||||||
|
els = makeAst<Ast::ElseSt>(elseBody);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return makeAst<Ast::IfSt>(condition, body, elifs, els);
|
||||||
|
}
|
||||||
|
Ast::While Parser::__parseWhile()
|
||||||
|
{
|
||||||
|
// entry: current is `while`
|
||||||
|
next(); // consume `while`
|
||||||
|
Ast::Expression condition = parseExpression(0);
|
||||||
|
expect(TokenType::LeftBrace); // {
|
||||||
|
Ast::BlockStatement body = __parseBlockStatement();
|
||||||
|
return makeAst<Ast::WhileSt>(condition, body);
|
||||||
|
}
|
||||||
|
Ast::Return Parser::__parseReturn()
|
||||||
|
{
|
||||||
|
// entry: current is `return`
|
||||||
|
next(); // consume `return`
|
||||||
|
Ast::Expression retValue = parseExpression(0);
|
||||||
|
expect(TokenType::Semicolon);
|
||||||
|
next(); // consume `;`
|
||||||
|
return makeAst<Ast::ReturnSt>(retValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::FunctionCall Parser::__parseFunctionCall(FString funcName)
|
||||||
|
{
|
||||||
|
// entry: current at '('
|
||||||
|
next(); // consume '('
|
||||||
|
std::vector<Ast::Expression> args;
|
||||||
|
if (!isThis(TokenType::RightParen))
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
args.push_back(parseExpression(0, TokenType::Comma, TokenType::RightParen));
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // consume ','
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(TokenType::RightParen);
|
||||||
|
next(); // consume ')'
|
||||||
|
return makeAst<Ast::FunctionCallExpr>(funcName, Ast::FunctionArguments(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::VarExpr Parser::__parseVarExpr(FString name)
|
||||||
|
{
|
||||||
|
return makeAst<Ast::VarExprAst>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::LambdaExpr Parser::__parseLambdaExpr()
|
||||||
|
{
|
||||||
|
// entry: current tok Token::LeftParen and last is Token::Function
|
||||||
|
/*
|
||||||
|
Lambda in Fig like:
|
||||||
|
fun (params) -> <return type> {...}
|
||||||
|
*/
|
||||||
|
Ast::FunctionParameters params = __parseFunctionParameters();
|
||||||
|
// if OK, the current token is `)` next one
|
||||||
|
FString tiName = ValueType::Any.name;
|
||||||
|
if (isThis(TokenType::RightArrow)) // ->
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
expect(TokenType::Identifier);
|
||||||
|
tiName = currentToken().getValue();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
expect(TokenType::LeftBrace); // `{`
|
||||||
|
return makeAst<Ast::LambdaExprAst>(params, tiName, __parseBlockStatement());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::UnaryExpr Parser::__parsePrefix(Ast::Operator op, Precedence bp)
|
||||||
|
{
|
||||||
|
return makeAst<Ast::UnaryExprAst>(op, parseExpression(bp));
|
||||||
|
}
|
||||||
|
Ast::BinaryExpr Parser::__parseInfix(Ast::Expression lhs, Ast::Operator op, Precedence bp)
|
||||||
|
{
|
||||||
|
return makeAst<Ast::BinaryExprAst>(lhs, op, parseExpression(bp));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::ListExpr Parser::__parseListExpr()
|
||||||
|
{
|
||||||
|
// entry: current is `[`
|
||||||
|
next(); // consume `[`
|
||||||
|
std::vector<Ast::Expression> val;
|
||||||
|
while (!isThis(TokenType::RightBracket))
|
||||||
|
{
|
||||||
|
val.push_back(parseExpression(0, TokenType::RightBracket, TokenType::Comma));
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // consume `,`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(TokenType::RightBracket);
|
||||||
|
next(); // consume `]`
|
||||||
|
return makeAst<Ast::ListExprAst>(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::MapExpr Parser::__parseMapExpr()
|
||||||
|
{
|
||||||
|
// entry: current is `{`
|
||||||
|
next(); // consume `{`
|
||||||
|
std::map<FString, Ast::Expression> val;
|
||||||
|
while (!isThis(TokenType::RightBrace))
|
||||||
|
{
|
||||||
|
expect(TokenType::Identifier, FString(u8"key (identifier)"));
|
||||||
|
FString key = currentToken().getValue();
|
||||||
|
if (val.contains(key)) throwAddressableError<SyntaxError>(FStringView(std::format(
|
||||||
|
"Redefinition of immutable key {} in mapping literal",
|
||||||
|
key.toBasicString())));
|
||||||
|
next(); // consume key
|
||||||
|
expect(TokenType::Colon);
|
||||||
|
next(); // consume `:`
|
||||||
|
val[key] = parseExpression(0, TokenType::RightBrace, TokenType::Comma);
|
||||||
|
if (isThis(TokenType::Comma))
|
||||||
|
{
|
||||||
|
next(); // consume `,`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(TokenType::RightBrace);
|
||||||
|
next(); // consume `}`
|
||||||
|
return makeAst<Ast::MapExprAst>(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::InitExpr Parser::__parseInitExpr(FString structName)
|
||||||
|
{
|
||||||
|
// entry: current is `{`
|
||||||
|
next(); // consume `{`
|
||||||
|
std::vector<std::pair<FString, Ast::Expression>> args;
|
||||||
|
/*
|
||||||
|
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};
|
||||||
|
*/
|
||||||
|
uint8_t mode = 0; // 0=undetermined, 1=positional, 2=named, 3=shorthand
|
||||||
|
|
||||||
|
while (!isThis(TokenType::RightBrace))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == 1)
|
||||||
|
{
|
||||||
|
// 1 Person {"Fig", 1, "IDK"};
|
||||||
|
Ast::Expression expr = parseExpression(0);
|
||||||
|
args.push_back({FString(), std::move(expr)});
|
||||||
|
}
|
||||||
|
else if (mode == 2)
|
||||||
|
{
|
||||||
|
// 2 Person {name: "Fig", age: 1, sex: "IDK"};
|
||||||
|
expect(TokenType::Identifier);
|
||||||
|
FString fieldName = currentToken().getValue();
|
||||||
|
next(); // consume identifier
|
||||||
|
expect(TokenType::Colon);
|
||||||
|
next(); // consume colon
|
||||||
|
Ast::Expression expr = parseExpression(0);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
else if (!isThis(TokenType::RightBrace))
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(u8"Expected comma or right brace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(TokenType::RightBrace);
|
||||||
|
next(); // consume `}`
|
||||||
|
return makeAst<Ast::InitExprAst>(structName, args);
|
||||||
|
}
|
||||||
|
Ast::Expression Parser::__parseTupleOrParenExpr()
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
|
||||||
|
if (currentToken().getType() == TokenType::RightParen)
|
||||||
|
{
|
||||||
|
next(); // consume ')'
|
||||||
|
return makeAst<Ast::TupleExprAst>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ast::Expression firstExpr = parseExpression(0);
|
||||||
|
|
||||||
|
if (currentToken().getType() == TokenType::Comma)
|
||||||
|
{
|
||||||
|
std::vector<Ast::Expression> elements;
|
||||||
|
elements.push_back(firstExpr);
|
||||||
|
|
||||||
|
while (currentToken().getType() == TokenType::Comma)
|
||||||
|
{
|
||||||
|
next(); // consume ','
|
||||||
|
|
||||||
|
if (currentToken().getType() == TokenType::RightParen)
|
||||||
|
break;
|
||||||
|
|
||||||
|
elements.push_back(parseExpression(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(TokenType::RightParen);
|
||||||
|
next(); // consume ')'
|
||||||
|
|
||||||
|
return makeAst<Ast::TupleExprAst>(std::move(elements));
|
||||||
|
}
|
||||||
|
else if (currentToken().getType() == TokenType::RightParen)
|
||||||
|
{
|
||||||
|
next(); // consume ')'
|
||||||
|
return firstExpr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Expect ')' or ',' after expression in parentheses"));
|
||||||
|
}
|
||||||
|
return nullptr; // to suppress compiler warning
|
||||||
|
}
|
||||||
|
Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2)
|
||||||
|
{
|
||||||
|
Ast::Expression lhs;
|
||||||
|
Ast::Operator op;
|
||||||
|
|
||||||
|
Token tok = currentToken();
|
||||||
|
if (tok == EOFTok)
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected end of expression"));
|
||||||
|
if (tok.getType() == stop || tok.getType() == stop2)
|
||||||
|
{
|
||||||
|
if (lhs == nullptr) throwAddressableError<SyntaxError>(FStringView(u8"Expected expression"));
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
if (tok.getType() == TokenType::LeftBracket)
|
||||||
|
{
|
||||||
|
lhs = __parseListExpr(); // auto consume
|
||||||
|
}
|
||||||
|
else if (tok.getType() == TokenType::LeftParen)
|
||||||
|
{
|
||||||
|
lhs = __parseTupleOrParenExpr(); // auto consume
|
||||||
|
}
|
||||||
|
else if (tok.getType() == TokenType::LeftBrace)
|
||||||
|
{
|
||||||
|
lhs = __parseMapExpr(); // auto consume
|
||||||
|
}
|
||||||
|
else if (tok.isLiteral())
|
||||||
|
{
|
||||||
|
lhs = __parseValueExpr();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
else if (tok.isIdentifier())
|
||||||
|
{
|
||||||
|
FString id = tok.getValue();
|
||||||
|
next();
|
||||||
|
if (currentToken().getType() == TokenType::LeftParen)
|
||||||
|
{
|
||||||
|
lhs = __parseFunctionCall(id); // foo(...)
|
||||||
|
}
|
||||||
|
else if (currentToken().getType() == TokenType::LeftBrace)
|
||||||
|
{
|
||||||
|
lhs = __parseInitExpr(id); // a_struct{init...}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lhs = __parseVarExpr(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isTokenOp(tok) && isOpUnary((op = Ast::TokenToOp.at(tok.getType()))))
|
||||||
|
{
|
||||||
|
// prefix
|
||||||
|
next();
|
||||||
|
lhs = __parsePrefix(op, getRightBindingPower(op));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected token in expression"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// infix / (postfix) ?
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
tok = currentToken();
|
||||||
|
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
|
||||||
|
|
||||||
|
// ternary
|
||||||
|
if (tok.getType() == TokenType::Question)
|
||||||
|
{
|
||||||
|
next(); // consume ?
|
||||||
|
Ast::Expression trueExpr = parseExpression(0, TokenType::Colon);
|
||||||
|
expect(TokenType::Colon);
|
||||||
|
next(); // consume :
|
||||||
|
Ast::Expression falseExpr = parseExpression(0, TokenType::Semicolon, stop2);
|
||||||
|
lhs = makeAst<Ast::TernaryExprAst>(lhs, trueExpr, falseExpr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTokenOp(tok)) break;
|
||||||
|
|
||||||
|
op = Ast::TokenToOp.at(tok.getType());
|
||||||
|
Precedence lbp = getLeftBindingPower(op);
|
||||||
|
if (bp >= lbp) break;
|
||||||
|
|
||||||
|
next(); // consume op
|
||||||
|
lhs = __parseInfix(lhs, op, getRightBindingPower(op));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Ast::AstBase> Parser::parseAll()
|
||||||
|
{
|
||||||
|
output.clear();
|
||||||
|
Token tok = currentToken();
|
||||||
|
if (tok == EOFTok)
|
||||||
|
{
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Package/Module Import Support
|
||||||
|
while (!isEOF())
|
||||||
|
{
|
||||||
|
pushNode(__parseStatement());
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Fig
|
||||||
39
src/value.cpp
Normal file
39
src/value.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#include <value.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")) {}
|
||||||
|
TypeInfo::TypeInfo(FString _name, bool reg)
|
||||||
|
{
|
||||||
|
static size_t id_count = 0;
|
||||||
|
name = std::move(_name);
|
||||||
|
// std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n";
|
||||||
|
if (reg)
|
||||||
|
{
|
||||||
|
typeMap[name] = ++id_count;
|
||||||
|
id = id_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id = typeMap.at(name); // may throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Tuple(FString(u8"Tuple"), true); // id: 12
|
||||||
|
} // namespace Fig
|
||||||
9
src/waring.cpp
Normal file
9
src/waring.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <warning.hpp>
|
||||||
|
|
||||||
|
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")}
|
||||||
|
};
|
||||||
|
};
|
||||||
21
xmake.lua
Normal file
21
xmake.lua
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
add_rules("mode.debug", "mode.release")
|
||||||
|
add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"})
|
||||||
|
|
||||||
|
set_policy("run.autobuild", false)
|
||||||
|
|
||||||
|
target("Fig")
|
||||||
|
set_kind("binary")
|
||||||
|
set_languages("c++2b")
|
||||||
|
|
||||||
|
set_plat("mingw")
|
||||||
|
--set_toolchains("clang")
|
||||||
|
|
||||||
|
add_cxxflags("-static")
|
||||||
|
add_cxxflags("-stdlib=libc++")
|
||||||
|
|
||||||
|
add_files("src/*.cpp")
|
||||||
|
add_includedirs("include")
|
||||||
|
|
||||||
|
set_warnings("all")
|
||||||
|
|
||||||
|
add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"")
|
||||||
Reference in New Issue
Block a user