diff --git a/docs/FigDesignDocument.md b/docs/FigDesignDocument.md
deleted file mode 100644
index 5645676..0000000
--- a/docs/FigDesignDocument.md
+++ /dev/null
@@ -1,248 +0,0 @@
-## `Fig Programming Language` DESIGN DOC
-
----
-### 关键词解释 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}`
- ```
-
diff --git a/function.py b/function.py
new file mode 100644
index 0000000..a1c343e
--- /dev/null
+++ b/function.py
@@ -0,0 +1,2 @@
+print = 123
+print()
\ No newline at end of file
diff --git a/include/Ast/FunctionCall.hpp b/include/Ast/FunctionCall.hpp
index 2c0067f..24b03d9 100644
--- a/include/Ast/FunctionCall.hpp
+++ b/include/Ast/FunctionCall.hpp
@@ -21,7 +21,7 @@ namespace Fig::Ast
class FunctionCallExpr final : public ExpressionAst
{
public:
- FString name;
+ Expression callee; // 不是 name 了!!
FunctionArguments arg;
FunctionCallExpr()
@@ -29,15 +29,15 @@ namespace Fig::Ast
type = AstType::FunctionCall;
}
- FunctionCallExpr(FString _name, FunctionArguments _arg) :
- name(std::move(_name)), arg(std::move(_arg))
+ FunctionCallExpr(Expression _callee, FunctionArguments _arg) :
+ callee(std::move(_callee)), arg(std::move(_arg))
{
type = AstType::FunctionCall;
}
- virtual FString toString() override
+ virtual FString toString() override
{
- FString s = name;
+ FString s = callee->toString();
s += u8"(";
for (size_t i = 0; i < arg.argv.size(); ++i)
{
diff --git a/include/Ast/FunctionDefSt.hpp b/include/Ast/FunctionDefSt.hpp
index 9b1d6f6..c0105b6 100644
--- a/include/Ast/FunctionDefSt.hpp
+++ b/include/Ast/FunctionDefSt.hpp
@@ -9,7 +9,7 @@
namespace Fig::Ast
{
/*
- fun greet(greeting, name:String, age:Int, split:String=":") public -> Null
+ func greet(greeting, name:String, age:Int, split:String=":") -> Null
{
io.println("{}, {}{}{}", greeting, name, split, age);
}
@@ -18,7 +18,7 @@ namespace Fig::Ast
`split` -> default parameter
*/
- class FunctionDefSt final : public StatementAst // for define
+ class FunctionDefSt final : public StatementAst // for definition
{
public:
FString name;
diff --git a/include/Ast/FunctionLiteralExpr.hpp b/include/Ast/FunctionLiteralExpr.hpp
new file mode 100644
index 0000000..014f4e3
--- /dev/null
+++ b/include/Ast/FunctionLiteralExpr.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace Fig::Ast
+{
+ class FunctionLiteralExprAst final : public ExpressionAst
+ {
+ public:
+ FunctionParameters paras;
+ std::variant body;
+
+ FunctionLiteralExprAst(FunctionParameters _paras, BlockStatement _body) :
+ paras(std::move(_paras)), body(std::move(_body))
+ {
+ type = AstType::FunctionLiteralExpr;
+ }
+
+ FunctionLiteralExprAst(FunctionParameters _paras, Expression _exprBody) :
+ paras(std::move(_paras)), body(std::move(_exprBody))
+ {
+ type = AstType::FunctionLiteralExpr;
+ }
+
+ bool isExprMode() const
+ {
+ return std::holds_alternative(body);
+ }
+
+ BlockStatement &getBlockBody()
+ {
+ return std::get(body);
+ }
+
+ Expression &getExprBody()
+ {
+ return std::get(body);
+ }
+
+ ~FunctionLiteralExprAst() = default;
+ };
+
+ using FunctionLiteralExpr = std::shared_ptr;
+} // namespace Fig::Ast
\ No newline at end of file
diff --git a/include/Ast/LambdaExpr.hpp b/include/Ast/LambdaExpr.hpp
deleted file mode 100644
index 3eaac5c..0000000
--- a/include/Ast/LambdaExpr.hpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-
-#include
-#include
-
-#include
-#include
-
-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;
-}; // namespace Fig
\ No newline at end of file
diff --git a/include/Ast/astBase.hpp b/include/Ast/astBase.hpp
index 2dcc335..871f185 100644
--- a/include/Ast/astBase.hpp
+++ b/include/Ast/astBase.hpp
@@ -30,6 +30,7 @@ namespace Fig::Ast
TupleExpr, // ()
MapExpr, // {}
InitExpr, // struct{}
+ FunctionLiteralExpr,
/* Statement */
BlockStatement,
@@ -312,7 +313,7 @@ namespace Fig::Ast
class BlockStatementAst : public StatementAst
{
public:
- const std::vector stmts;
+ std::vector stmts;
BlockStatementAst()
{
type = AstType::BlockStatement;
diff --git a/include/AstPrinter.hpp b/include/AstPrinter.hpp
index 9d69245..8927d5b 100644
--- a/include/AstPrinter.hpp
+++ b/include/AstPrinter.hpp
@@ -42,9 +42,6 @@ public:
case AstType::IfSt:
printIfSt(std::static_pointer_cast(node), indent);
break;
- case AstType::LambdaExpr:
- printLambdaExpr(std::static_pointer_cast(node), indent);
- break;
case AstType::TernaryExpr:
printTernaryExpr(std::static_pointer_cast(node), indent);
break;
@@ -147,8 +144,6 @@ private:
printIndent(indent);
std::cout << "FunctionCall\n";
printIndent(indent + 2);
- std::cout << "FuncName: ";
- printFString(node->name, 0);
printIndent(indent + 2);
}
@@ -175,14 +170,6 @@ private:
printIndent(indent + 2);
}
- void printLambdaExpr(const std::shared_ptr &node, int indent)
- {
- printIndent(indent);
- std::cout << "LambdaExpr\n"
- << node->toString().toBasicString();
- printIndent(indent + 2);
- }
-
void printTernaryExpr(const std::shared_ptr &node, int indent)
{
printIndent(indent);
diff --git a/include/Value/function.hpp b/include/Value/function.hpp
index fd34632..6fcac8d 100644
--- a/include/Value/function.hpp
+++ b/include/Value/function.hpp
@@ -1,12 +1,14 @@
#pragma once
#include
-#include
#include
#include
+
namespace Fig
{
+ class Value;
+
/* complex objects */
struct FunctionStruct
{
@@ -15,6 +17,10 @@ namespace Fig
TypeInfo retType;
Ast::BlockStatement body;
+ bool isBuiltin = false;
+ std::function &)> builtin;
+ int builtinParamCount = -1;
+
FunctionStruct(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body) :
id(nextId()), // 分配唯一 ID
paras(std::move(_paras)),
@@ -23,8 +29,10 @@ namespace Fig
{
}
+ FunctionStruct(std::function &)> fn, int argc);
+
FunctionStruct(const FunctionStruct &other) :
- id(other.id), paras(other.paras), retType(other.retType), body(other.body) {}
+ id(other.id), paras(other.paras), retType(other.retType), body(other.body), isBuiltin(other.isBuiltin), builtin(other.builtin), builtinParamCount(other.builtinParamCount) {}
FunctionStruct &operator=(const FunctionStruct &other) = default;
FunctionStruct(FunctionStruct &&) noexcept = default;
@@ -61,6 +69,8 @@ namespace Fig
data = std::make_unique(
std::move(paras), std::move(ret), std::move(body));
}
+ Function(std::function &)> fn, int argc);
+
bool operator==(const Function &other) const noexcept
{
if (!data || !other.data) return false;
diff --git a/include/ast.hpp b/include/ast.hpp
index 8bfa67d..0ae6cc8 100644
--- a/include/ast.hpp
+++ b/include/ast.hpp
@@ -9,10 +9,10 @@
#include
#include
#include
+#include
#include
#include
#include
-#include
#include
#include
#include
diff --git a/include/evaluator.hpp b/include/evaluator.hpp
index 9c6a5f1..af27a4d 100644
--- a/include/evaluator.hpp
+++ b/include/evaluator.hpp
@@ -1,8 +1,6 @@
#pragma once
-#include
-#include
-
+#include
#include
#include
#include
@@ -84,6 +82,26 @@ namespace Fig
{
globalContext = std::make_shared(FString(u8"global"));
currentContext = globalContext;
+
+ for (auto &[name, fn] : Builtins::builtinFunctions)
+ {
+ int argc = Builtins::getBuiltinFunctionParamCount(name);
+ Function f(fn, argc);
+ globalContext->def(
+ name,
+ ValueType::Function,
+ AccessModifier::PublicConst,
+ Value(f));
+ }
+
+ for (auto &[name, val] : Builtins::builtinValues)
+ {
+ globalContext->def(
+ name,
+ val.getTypeInfo(),
+ AccessModifier::PublicConst,
+ val);
+ }
}
std::shared_ptr getCurrentContext() { return currentContext; }
@@ -95,6 +113,8 @@ namespace Fig
StatementResult evalStatement(const Ast::Statement &);
+ Value evalFunctionCall(const Function &, const Ast::FunctionArguments &, FString fnName = u8"");
+
Value eval(Ast::Expression);
void run();
void printStackTrace() const;
diff --git a/include/parser.hpp b/include/parser.hpp
index 04f5ee2..666f40f 100644
--- a/include/parser.hpp
+++ b/include/parser.hpp
@@ -174,12 +174,19 @@ namespace Fig
return getBindingPower(op).second;
}
+ // template
+ // std::shared_ptr<_Tp> makeAst(Args &&...args)
+ // {
+ // _Tp node(std::forward(args)...);
+ // node.setAAI(currentAAI);
+ // return std::shared_ptr<_Tp>(new _Tp(node));
+ // }
template
std::shared_ptr<_Tp> makeAst(Args &&...args)
{
- _Tp node(args...);
- node.setAAI(currentAAI);
- return std::shared_ptr<_Tp>(new _Tp(node));
+ std::shared_ptr<_Tp> ptr = std::make_shared<_Tp>(std::forward(args)...);
+ ptr->setAAI(currentAAI);
+ return ptr;
}
void expectPeek(TokenType type)
@@ -236,7 +243,6 @@ namespace Fig
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
@@ -246,18 +252,20 @@ namespace Fig
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::Expression __parseCall(Ast::Expression);
+
Ast::ListExpr __parseListExpr(); // entry: current is `[`
- Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`
- Ast::MapExpr __parseMapExpr(); // 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 __parseTupleOrParenExpr(); // entry: current is `(`
+
+ Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function
Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
std::vector parseAll();
diff --git a/include/token.hpp b/include/token.hpp
index f3f1e9b..3cef360 100644
--- a/include/token.hpp
+++ b/include/token.hpp
@@ -22,7 +22,7 @@ namespace Fig
Or, // or
Not, // not
Import, // import
- Function, // fun
+ Function, // func
Variable, // var
Const, // const
Final, // final
@@ -85,7 +85,7 @@ namespace Fig
RightBrace, // }
// LeftArrow, // <-
RightArrow, // ->
- // DoubleArrow, // =>
+ DoubleArrow, // =>
Equal, // ==
NotEqual, // !=
LessEqual, // <=
diff --git a/src/Value/function.cpp b/src/Value/function.cpp
new file mode 100644
index 0000000..f01b747
--- /dev/null
+++ b/src/Value/function.cpp
@@ -0,0 +1,19 @@
+#include
+#include
+
+namespace Fig
+{
+ FunctionStruct::FunctionStruct(std::function &)> fn,
+ int argc) :
+ id(nextId()),
+ isBuiltin(true),
+ builtin(std::move(fn)),
+ builtinParamCount(argc) {}
+
+ Function::Function(std::function &)> fn,
+ int argc) :
+ __ValueWrapper(ValueType::Function)
+ {
+ data = std::make_unique(std::move(fn), argc);
+ }
+}; // namespace Fig
\ No newline at end of file
diff --git a/src/evaluator.cpp b/src/evaluator.cpp
index 4c380fa..14db024 100644
--- a/src/evaluator.cpp
+++ b/src/evaluator.cpp
@@ -62,6 +62,118 @@ namespace Fig
}
}
+ Value Evaluator::evalFunctionCall(const Function &fn, const Ast::FunctionArguments &fnArgs, FString fnName)
+ {
+ FunctionStruct fnStruct = fn.getValue();
+ Ast::FunctionCallArgs evaluatedArgs;
+ if (fnStruct.isBuiltin)
+ {
+ for (const auto &argExpr : fnArgs.argv)
+ {
+ evaluatedArgs.argv.push_back(eval(argExpr));
+ }
+ if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength())
+ {
+ static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError";
+ throw EvaluatorError(FStringView(std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), fnStruct.builtinParamCount, evaluatedArgs.getLength())), currentAddressInfo);
+ }
+ return fnStruct.builtin(evaluatedArgs.argv);
+ }
+
+ // check argument, all types of parameters
+ Ast::FunctionParameters fnParas = fnStruct.paras;
+ if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
+ {
+ static constexpr char ArgumentMismatchErrorName[] = "ArgumentMismatchError";
+ throw EvaluatorError(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo);
+ }
+
+ // 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(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(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(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(FString(std::format("", 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(FStringView(std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), fnStruct.retType.toString().toBasicString(), retVal.getTypeInfo().toString().toBasicString())), currentAddressInfo);
+ }
+ return retVal;
+ }
+
Value Evaluator::eval(Ast::Expression exp)
{
using Fig::Ast::AstType;
@@ -78,7 +190,8 @@ namespace Fig
{
return val.value();
}
- throw RuntimeError(FStringView(std::format("Variable '{}' not defined", varExp->name.toBasicString())));
+ static constexpr char UndefinedVariableErrorName[] = "UndefinedVariableError";
+ throw EvaluatorError(FStringView(std::format("Variable '{}' is not defined in the current scope", varExp->name.toBasicString())), varExp->getAAI());
}
case AstType::BinaryExpr: {
auto binExp = std::dynamic_pointer_cast(exp);
@@ -89,132 +202,54 @@ namespace Fig
return evalUnary(unExp);
}
case AstType::FunctionCall: {
- // std::cerr << "Eval: function call...\n";
auto fnCall = std::dynamic_pointer_cast(exp);
- FString fnName = fnCall->name;
- if (Builtins::isBuiltinFunction(fnName))
- {
- std::vector callArgs;
- if (fnCall->arg.getLength() != Builtins::getBuiltinFunctionParamCount(fnName) and Builtins::getBuiltinFunctionParamCount(fnName) != -1) // -1 means variadic
- {
- static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError";
- throw EvaluatorError(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(FStringView(std::format("Function '{}' not defined", fnName.toBasicString())), currentAddressInfo);
- }
- Value fnVal = fnValOpt.value();
- if (!fnVal.is())
+ Value calleeVal = eval(fnCall->callee);
+
+ if (!calleeVal.is())
{
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
- throw EvaluatorError(FStringView(std::format("'{}' is not a function or callable", fnName.toBasicString())), currentAddressInfo);
- }
- FunctionStruct fnStruct = fnVal.as().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(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo);
+ throw EvaluatorError(
+ FStringView(std::format(
+ "'{}' is not a function or callable",
+ calleeVal.toString().toBasicString())),
+ currentAddressInfo);
}
- Ast::FunctionCallArgs evaluatedArgs;
+ Function fn = calleeVal.as();
- // 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(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;
+ FString fnName = u8"";
+ if (auto var = std::dynamic_pointer_cast(fnCall->callee))
+ fnName = var->name; // try to get function name
- Value defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
- if (expectedType != defaultVal.getTypeInfo() and expectedType != ValueType::Any)
- {
- static constexpr char DefaultParameterTypeErrorName[] = "DefaultParameterTypeError";
- throw EvaluatorError(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);
- }
+ return evalFunctionCall(fn, fnCall->arg, fnName);
+ }
- Value argVal = eval(fnArgs.argv[i]);
- TypeInfo actualType = argVal.getTypeInfo();
- if (expectedType != actualType and expectedType != ValueType::Any)
- {
- static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
- throw EvaluatorError(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++)
+ case AstType::FunctionLiteralExpr: {
+ auto fn = std::dynamic_pointer_cast(exp);
+
+ if (fn->isExprMode())
{
- size_t defParamIndex = i - fnParas.posParas.size();
- Value defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
- evaluatedArgs.argv.push_back(defaultVal);
+ Ast::BlockStatement body = std::make_shared();
+ body->setAAI(fn->getExprBody()->getAAI());
+ Ast::Statement retSt = std::make_shared(fn->getExprBody());
+ retSt->setAAI(fn->getExprBody()->getAAI());
+ body->stmts.push_back(retSt);
+ return Function(
+ fn->paras,
+ ValueType::Any,
+ body
+ );
}
- // create new context for function call
- auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), currentContext);
- auto previousContext = currentContext;
- currentContext = newContext;
- // define parameters in new context
- for (size_t j = 0; j < fnParas.size(); j++)
+ else
{
- 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]);
+ Ast::BlockStatement body = fn->getBlockBody();
+ return Function(
+ fn->paras,
+ ValueType::Any,
+ body
+ );
}
- // 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(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(exp);
diff --git a/src/lexer.cpp b/src/lexer.cpp
index 2e22e1d..0999ab2 100644
--- a/src/lexer.cpp
+++ b/src/lexer.cpp
@@ -30,6 +30,7 @@ namespace Fig
{FString(u8":="), TokenType::Walrus},
{FString(u8"**"), TokenType::Power},
{FString(u8"->"), TokenType::RightArrow},
+ {FString(u8"=>"), TokenType::DoubleArrow},
// 单字符
{FString(u8"+"), TokenType::Plus},
@@ -62,7 +63,7 @@ namespace Fig
{FString(u8"or"), TokenType::Or},
{FString(u8"not"), TokenType::Not},
{FString(u8"import"), TokenType::Import},
- {FString(u8"fun"), TokenType::Function},
+ {FString(u8"func"), TokenType::Function},
{FString(u8"var"), TokenType::Variable},
{FString(u8"const"), TokenType::Const},
{FString(u8"final"), TokenType::Final},
diff --git a/src/parser.cpp b/src/parser.cpp
index 2f2d382..9321794 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -361,20 +361,17 @@ namespace Fig
{
// stmt = __parseVarDef();
// expect(TokenType::Semicolon);
- // next();
- if (isNext(TokenType::Variable) || isNext(TokenType::Const))
+ next(); // consume `public`
+ if (isThis(TokenType::Variable) || isThis(TokenType::Const))
{
- next(); // consume `public`
stmt = __parseVarDef(true);
}
- else if (isNext(TokenType::Function))
+ else if (isThis(TokenType::Function) and isNext(TokenType::Identifier))
{
- next(); // consume `public`
- expectPeek(TokenType::Identifier);
next();
stmt = __parseFunctionDef(true);
}
- else if (isNext(TokenType::Struct))
+ else if (isThis(TokenType::Struct))
{
stmt = __parseStructDef(true);
}
@@ -387,9 +384,8 @@ namespace Fig
{
stmt = __parseVarDef(false);
}
- else if (isThis(TokenType::Function))
+ else if (isThis(TokenType::Function) and isNext(TokenType::Identifier))
{
- expectPeek(TokenType::Identifier, u8"function name");
next();
stmt = __parseFunctionDef(false);
}
@@ -523,55 +519,11 @@ namespace Fig
return makeAst(retValue);
}
- Ast::FunctionCall Parser::__parseFunctionCall(FString funcName)
- {
- // entry: current at '('
- next(); // consume '('
- std::vector 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(funcName, Ast::FunctionArguments(args));
- }
-
Ast::VarExpr Parser::__parseVarExpr(FString name)
{
return makeAst(name);
}
- Ast::LambdaExpr Parser::__parseLambdaExpr()
- {
- // entry: current tok Token::LeftParen and last is Token::Function
- /*
- Lambda in Fig like:
- fun (params) -> {...}
- */
- 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(params, tiName, __parseBlockStatement());
- }
-
Ast::UnaryExpr Parser::__parsePrefix(Ast::Operator op, Precedence bp)
{
return makeAst(op, parseExpression(bp));
@@ -581,6 +533,32 @@ namespace Fig
return makeAst(lhs, op, parseExpression(bp));
}
+ Ast::Expression Parser::__parseCall(Ast::Expression callee)
+ {
+ next(); // consume '('
+ std::vector args;
+
+ if (!isThis(TokenType::RightParen))
+ {
+ while (true)
+ {
+ args.push_back(parseExpression(0, TokenType::Comma, TokenType::RightParen));
+
+ if (isThis(TokenType::Comma))
+ {
+ next();
+ continue;
+ }
+ break;
+ }
+ }
+
+ expect(TokenType::RightParen);
+ next(); // consume ')'
+
+ return makeAst(callee, Ast::FunctionArguments(args));
+ }
+
Ast::ListExpr Parser::__parseListExpr()
{
// entry: current is `[`
@@ -739,6 +717,27 @@ namespace Fig
}
return nullptr; // to suppress compiler warning
}
+
+ Ast::FunctionLiteralExpr Parser::__parseFunctionLiteralExpr()
+ {
+ // entry: current is Token::LeftParen and last is Token::Function
+ /*
+ Function literal:
+ func (params){...}
+ or
+ func (params) =>
+ */
+ Ast::FunctionParameters params = __parseFunctionParameters();
+ if (isThis(TokenType::DoubleArrow)) // =>
+ {
+ next();
+ Ast::Expression bodyExpr = parseExpression(0);
+ return makeAst(params, bodyExpr);
+ }
+ expect(TokenType::LeftBrace); // `{`
+ return makeAst(params, __parseBlockStatement());
+ }
+
Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2)
{
Ast::Expression lhs;
@@ -764,6 +763,17 @@ namespace Fig
{
lhs = __parseMapExpr(); // auto consume
}
+ else if (tok.getType() == TokenType::Function)
+ {
+ next(); // consume `function`
+ if (currentToken().getType() == TokenType::Identifier)
+ {
+ // err
+ throwAddressableError(FStringView(u8"Function literal should not have a name"));
+ }
+ expect(TokenType::LeftParen);
+ lhs = __parseFunctionLiteralExpr();
+ }
else if (tok.isLiteral())
{
lhs = __parseValueExpr();
@@ -773,11 +783,7 @@ namespace Fig
{
FString id = tok.getValue();
next();
- if (currentToken().getType() == TokenType::LeftParen)
- {
- lhs = __parseFunctionCall(id); // foo(...)
- }
- else if (currentToken().getType() == TokenType::LeftBrace)
+ if (currentToken().getType() == TokenType::LeftBrace)
{
lhs = __parseInitExpr(id); // a_struct{init...}
}
@@ -803,6 +809,12 @@ namespace Fig
tok = currentToken();
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
+ if (tok.getType() == TokenType::LeftParen)
+ {
+ lhs = __parseCall(lhs);
+ continue;
+ }
+
// ternary
if (tok.getType() == TokenType::Question)
{
diff --git a/test.fig b/test.fig
index 7db07aa..3634dfb 100644
--- a/test.fig
+++ b/test.fig
@@ -1 +1,8 @@
-var x = 10
\ No newline at end of file
+func build_adder(x) -> Function
+{
+ return func(a) => a + x;
+}
+
+var add2 = build_adder(2);
+var println = __fstdout_println;
+println(add2(1));
\ No newline at end of file
diff --git a/xmake.lua b/xmake.lua
index 433a66c..307c88f 100644
--- a/xmake.lua
+++ b/xmake.lua
@@ -14,6 +14,8 @@ target("Fig")
add_cxxflags("-stdlib=libc++")
add_files("src/*.cpp")
+ add_files("src/Value/function.cpp")
+
add_includedirs("include")
set_warnings("all")