From e7ca714a89b48d670fd6fb6b2bc7deec0a014173 Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Sat, 20 Dec 2025 20:27:36 +0800 Subject: [PATCH] support function literal, set builtins to global context. great! --- docs/FigDesignDocument.md | 248 -------------------------- function.py | 2 + include/Ast/FunctionCall.hpp | 10 +- include/Ast/FunctionDefSt.hpp | 4 +- include/Ast/FunctionLiteralExpr.hpp | 47 +++++ include/Ast/LambdaExpr.hpp | 43 ----- include/Ast/astBase.hpp | 3 +- include/AstPrinter.hpp | 13 -- include/Value/function.hpp | 14 +- include/ast.hpp | 2 +- include/evaluator.hpp | 26 ++- include/parser.hpp | 24 ++- include/token.hpp | 4 +- src/Value/function.cpp | 19 ++ src/evaluator.cpp | 263 ++++++++++++++++------------ src/lexer.cpp | 3 +- src/parser.cpp | 128 ++++++++------ test.fig | 9 +- xmake.lua | 2 + 19 files changed, 362 insertions(+), 502 deletions(-) delete mode 100644 docs/FigDesignDocument.md create mode 100644 function.py create mode 100644 include/Ast/FunctionLiteralExpr.hpp delete mode 100644 include/Ast/LambdaExpr.hpp create mode 100644 src/Value/function.cpp 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")