support function literal, set builtins to global context. great!

This commit is contained in:
2025-12-20 20:27:36 +08:00
parent b9a98150ae
commit e7ca714a89
19 changed files with 362 additions and 502 deletions

View File

@@ -1,248 +0,0 @@
## `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无法解析或语法错误
&nbsp;
* `EndOfFile`
即:
```cpp
EOF
```
文件终止符
&nbsp;
* `Comments`
注释Token包括单行和多行
&nbsp;
* `Identifier`
标识符,用户定义的‘名字’
&nbsp;
* `And` -> `&&` 或 `and`
逻辑与
&nbsp;
* `Or` -> `||` 或 `or`
逻辑或
&nbsp;
* `Not` -> `!` 或 `!`
逻辑非
&nbsp;
* `Import` -> `import`
导入关键字,用于导入包。 e.g
``` python
import std.io
```
&nbsp;
* `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` 默认为私有,即对象仅能在当前作用域访问
&nbsp;
* `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;
```
&nbsp;
* `Const` -> `const`
定义`全过程`常量: 从词法分析到求值器内的生命周期都为常量,仅能**在生命周期内**赋值一次,使用时也只有一个唯一对象
&nbsp;
必须在源码中指定值
``` dart
const Pi = 3.1415926; // recommended
const name; // ❌ 错误
```
定义后的常量,其值及类型均不可改变,故可省略类型标识。这是推荐的写法
同时,也可作为结构体成员的修饰
``` cpp
struct MathConstants
{
const Pi = 3.1415926;
};
```
&nbsp;
* `Final` -> `final`
定义`结构体运行时`常量:从运行期开始的常量,仅能**在运行时**被赋值一次, **仅修饰结构体成员**
不存在 **final** 类型的外部常量
&nbsp;
定义后的常量,其值及类型均不可改变,故可省略类型标识。这是推荐的写法
``` cpp
struct Person
{
final name: String
final age: Int
final sex: String = "gender" // ❌ 请使用 const 代替
}
```
&nbsp;
* `While` -> `while`
while循环满足一个布尔类型条件循环执行语句
``` cpp
while (ans != 27.19236)
{
ans = Int.parse(std.io.readline());
}
```
&nbsp;
* `For` -> `for`
for循环拥有初始语句、条件、增长语句
``` cpp
for (init; condition; increment)
{
statements...;
}
```
&nbsp;
* `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}`
```

2
function.py Normal file
View File

@@ -0,0 +1,2 @@
print = 123
print()

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -0,0 +1,47 @@
#pragma once
#include <Ast/astBase.hpp>
#include <Ast/functionParameters.hpp>
#include <fig_string.hpp>
#include <variant>
namespace Fig::Ast
{
class FunctionLiteralExprAst final : public ExpressionAst
{
public:
FunctionParameters paras;
std::variant<BlockStatement, Expression> 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<Expression>(body);
}
BlockStatement &getBlockBody()
{
return std::get<BlockStatement>(body);
}
Expression &getExprBody()
{
return std::get<Expression>(body);
}
~FunctionLiteralExprAst() = default;
};
using FunctionLiteralExpr = std::shared_ptr<FunctionLiteralExprAst>;
} // namespace Fig::Ast

View File

@@ -1,43 +0,0 @@
#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

View File

@@ -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<Statement> stmts;
std::vector<Statement> stmts;
BlockStatementAst()
{
type = AstType::BlockStatement;

View File

@@ -42,9 +42,6 @@ public:
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;
@@ -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<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);

View File

@@ -1,12 +1,14 @@
#pragma once
#include <Value/BaseValue.hpp>
#include <Value/Type.hpp>
#include <Ast/functionParameters.hpp>
#include <atomic>
namespace Fig
{
class Value;
/* complex objects */
struct FunctionStruct
{
@@ -15,6 +17,10 @@ namespace Fig
TypeInfo retType;
Ast::BlockStatement body;
bool isBuiltin = false;
std::function<Value(const std::vector<Value> &)> 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<Value(const std::vector<Value> &)> 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<FunctionStruct>(
std::move(paras), std::move(ret), std::move(body));
}
Function(std::function<Value(const std::vector<Value> &)> fn, int argc);
bool operator==(const Function &other) const noexcept
{
if (!data || !other.data) return false;

View File

@@ -9,10 +9,10 @@
#include <Ast/FunctionCall.hpp>
#include <Ast/functionParameters.hpp>
#include <Ast/FunctionDefSt.hpp>
#include <Ast/FunctionLiteralExpr.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>

View File

@@ -1,8 +1,6 @@
#pragma once
#include <unordered_map>
#include <optional>
#include <builtins.hpp>
#include <error.hpp>
#include <fig_string.hpp>
#include <ast.hpp>
@@ -84,6 +82,26 @@ namespace Fig
{
globalContext = std::make_shared<Context>(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<Context> getCurrentContext() { return currentContext; }
@@ -95,6 +113,8 @@ namespace Fig
StatementResult evalStatement(const Ast::Statement &);
Value evalFunctionCall(const Function &, const Ast::FunctionArguments &, FString fnName = u8"<anonymous>");
Value eval(Ast::Expression);
void run();
void printStackTrace() const;

View File

@@ -174,12 +174,19 @@ namespace Fig
return getBindingPower(op).second;
}
// template <class _Tp, class... Args>
// std::shared_ptr<_Tp> makeAst(Args &&...args)
// {
// _Tp node(std::forward<Args>(args)...);
// node.setAAI(currentAAI);
// return std::shared_ptr<_Tp>(new _Tp(node));
// }
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));
std::shared_ptr<_Tp> ptr = std::make_shared<_Tp>(std::forward<Args>(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<Ast::AstBase> parseAll();

View File

@@ -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, // <=

19
src/Value/function.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include <value.hpp>
#include <Value/function.hpp>
namespace Fig
{
FunctionStruct::FunctionStruct(std::function<Value(const std::vector<Value> &)> fn,
int argc) :
id(nextId()),
isBuiltin(true),
builtin(std::move(fn)),
builtinParamCount(argc) {}
Function::Function(std::function<Value(const std::vector<Value> &)> fn,
int argc) :
__ValueWrapper(ValueType::Function)
{
data = std::make_unique<FunctionStruct>(std::move(fn), argc);
}
}; // namespace Fig

View File

@@ -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<BuiltinArgumentMismatchErrorName>(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<ArgumentMismatchErrorName>(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<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;
}
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<UndefinedVariableErrorName>(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<Ast::BinaryExprAst>(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<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>())
Value calleeVal = eval(fnCall->callee);
if (!calleeVal.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);
throw EvaluatorError<NotAFunctionErrorName>(
FStringView(std::format(
"'{}' is not a function or callable",
calleeVal.toString().toBasicString())),
currentAddressInfo);
}
Ast::FunctionCallArgs evaluatedArgs;
Function fn = calleeVal.as<Function>();
// 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;
FString fnName = u8"<anonymous>";
if (auto var = std::dynamic_pointer_cast<Ast::VarExprAst>(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<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);
}
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<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++)
case AstType::FunctionLiteralExpr: {
auto fn = std::dynamic_pointer_cast<Ast::FunctionLiteralExprAst>(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<Ast::BlockStatementAst>();
body->setAAI(fn->getExprBody()->getAAI());
Ast::Statement retSt = std::make_shared<Ast::ReturnSt>(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<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++)
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<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);

View File

@@ -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},

View File

@@ -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<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));
@@ -581,6 +533,32 @@ namespace Fig
return makeAst<Ast::BinaryExprAst>(lhs, op, parseExpression(bp));
}
Ast::Expression Parser::__parseCall(Ast::Expression callee)
{
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();
continue;
}
break;
}
}
expect(TokenType::RightParen);
next(); // consume ')'
return makeAst<Ast::FunctionCallExpr>(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) => <expression>
*/
Ast::FunctionParameters params = __parseFunctionParameters();
if (isThis(TokenType::DoubleArrow)) // =>
{
next();
Ast::Expression bodyExpr = parseExpression(0);
return makeAst<Ast::FunctionLiteralExprAst>(params, bodyExpr);
}
expect(TokenType::LeftBrace); // `{`
return makeAst<Ast::FunctionLiteralExprAst>(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<SyntaxError>(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)
{

View File

@@ -1 +1,8 @@
var x = 10
func build_adder(x) -> Function
{
return func(a) => a + x;
}
var add2 = build_adder(2);
var println = __fstdout_println;
println(add2(1));

View File

@@ -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")