diff --git a/src/Ast/functionParameters.hpp b/src/Ast/functionParameters.hpp index 1592527..a251d63 100644 --- a/src/Ast/functionParameters.hpp +++ b/src/Ast/functionParameters.hpp @@ -21,6 +21,9 @@ namespace Fig::Ast PosParasType posParas; DefParasType defParas; // default parameters + FString variadicPara; + bool variadic = false; + FunctionParameters() { @@ -30,6 +33,11 @@ namespace Fig::Ast posParas = std::move(_posParas); defParas = std::move(_defParas); } + FunctionParameters(FString _variadicPara) + { + variadicPara = std::move(_variadicPara); + variadic = true; + } size_t size() const { diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index 5d57ee0..1dc7bd4 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -31,10 +31,10 @@ namespace Fig if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) { return LvObject( - mod.ctx->get(member) - ); + mod.ctx->get(member)); } - else { + else + { throw EvaluatorError( u8"VariableNotFoundError", std::format( @@ -146,8 +146,7 @@ namespace Fig return LvObject( base.get(), indexVal, - LvObject::Kind::StringElement - ); + LvObject::Kind::StringElement); } else { @@ -366,6 +365,17 @@ namespace Fig // check argument, all types of parameters Ast::FunctionParameters fnParas = fnStruct.paras; + + // create new context for function call + auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), + fnStruct.closureContext); + + if (fnParas.variadic) + goto VariadicFilling; + else + goto NormalFilling; + + NormalFilling: { if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) { throw RuntimeError(FString( @@ -437,9 +447,7 @@ namespace Fig ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); evaluatedArgs.argv.push_back(defaultVal); } - // create new context for function call - auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), - fnStruct.closureContext); + // define parameters in new context for (size_t j = 0; j < fnParas.size(); j++) { @@ -459,6 +467,25 @@ namespace Fig AccessModifier argAm = AccessModifier::Const; newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); } + goto ExecuteBody; + } + + VariadicFilling: { + List list; + for (auto &exp : fnArgs.argv) + { + list.push_back(eval(exp, ctx)); // eval arguments in current scope + } + newContext->def( + fnParas.variadicPara, + ValueType::List, + AccessModifier::Const, + std::make_shared( + list)); + goto ExecuteBody; + } + + ExecuteBody: { // execute function body ObjectPtr retVal = Object::getNullInstance(); for (const auto &stmt : fnStruct.body->stmts) @@ -482,6 +509,7 @@ namespace Fig } return retVal; } + } RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx) { diff --git a/src/Lexer/lexer.cpp b/src/Lexer/lexer.cpp index b499d95..52fa2a0 100644 --- a/src/Lexer/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -10,6 +10,8 @@ namespace Fig { const std::unordered_map Lexer::symbol_map{ + // 三字符 + {FString(u8"..."), TokenType::TripleDot}, // 双字符 {FString(u8"=="), TokenType::Equal}, {FString(u8"!="), TokenType::NotEqual}, @@ -380,30 +382,59 @@ namespace Fig { FString sym; UTF8Char ch = *it; - sym += ch.getString(); - UTF8Char peek = UTF8Char(u8""); - if (hasNext() and (peek = it.peek()).isPunct()) // 窥探下一个操作符 - { - FString symd = FString(sym + peek.getString()); - if (this->symbol_map.contains(symd)) + + auto startsWith = [&](const FString &prefix) -> bool { + for (const auto &p : symbol_map) { - // Operator length is 2 - next(); - sym = symd; + const FString &op = p.first; + if (op.starts_with(prefix)) + return true; } - // Operator length is 1 - else if (!this->symbol_map.contains(sym)) + return false; + }; + + if (!startsWith(sym)) + { + error = SyntaxError( + FString(std::format("No such operator: {}", sym.toBasicString())), + this->line, it.column()); + next(); + return IllegalTok; + } + + while (hasNext()) + { + UTF8Char peek = it.peek(); + if (!peek.isPunct()) + break; + + FString candidate = sym + FString(peek.getString()); + + if (startsWith(candidate)) { - // check legality - error = SyntaxError(FString( - std::format("No such a operator: {}", sym.toBasicString())), - this->line, it.column()); + next(); + sym = candidate; + } + else + { + break; } } + + if (!symbol_map.contains(sym)) + { + error = SyntaxError( + FString(std::format("No such operator: {}", sym.toBasicString())), + this->line, it.column()); + next(); + return IllegalTok; + } + next(); - return Token(sym, this->symbol_map.at(sym)); // const object 'symbol_map', operator[] call is invalid + return Token(sym, symbol_map.at(sym)); } + Token Lexer::scanComments() { // entry: when iterator current char is '/' and peek is '/' or '*' diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index 4e6019c..9991206 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -144,6 +144,7 @@ namespace Fig Ast::FunctionParameters::PosParasType pp; Ast::FunctionParameters::DefParasType dp; + FString variaPara; while (true) { if (isThis(TokenType::RightParen)) @@ -153,7 +154,7 @@ namespace Fig } expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current FString pname = currentToken().getValue(); - next(); // skip pname + next(); // skip pname if (isThis(TokenType::Assign)) // = { next(); @@ -187,6 +188,19 @@ namespace Fig } } } + else if (isThis(TokenType::TripleDot)) + { + variaPara = pname; + next(); // skip `...` + if (!isThis(TokenType::RightParen)) + throw SyntaxError( + u8"Expects right paren, variable parameter function can only have one parameter", + currentAAI.line, + currentAAI.column + ); + next(); // skip `)` + return Ast::FunctionParameters(variaPara); + } else { pp.push_back({pname, ValueType::Any.name}); diff --git a/src/Token/token.hpp b/src/Token/token.hpp index b10abf0..5a02277 100644 --- a/src/Token/token.hpp +++ b/src/Token/token.hpp @@ -105,6 +105,8 @@ namespace Fig DoublePipe, // || Walrus, // := Power, // ** + + TripleDot, // ... for variadic parameter }; class Token final