[Feat] 函数支持可变参数!! func (x...) 获取到的x类型为List

[Impl] Lexer现在贪心检测符号,拓展时可以直接添加到symbol_map
This commit is contained in:
2025-12-26 21:43:29 +08:00
parent 00240f1ed1
commit 8a047de1c7
5 changed files with 108 additions and 25 deletions

View File

@@ -21,6 +21,9 @@ namespace Fig::Ast
PosParasType posParas; PosParasType posParas;
DefParasType defParas; // default parameters DefParasType defParas; // default parameters
FString variadicPara;
bool variadic = false;
FunctionParameters() FunctionParameters()
{ {
@@ -30,6 +33,11 @@ namespace Fig::Ast
posParas = std::move(_posParas); posParas = std::move(_posParas);
defParas = std::move(_defParas); defParas = std::move(_defParas);
} }
FunctionParameters(FString _variadicPara)
{
variadicPara = std::move(_variadicPara);
variadic = true;
}
size_t size() const size_t size() const
{ {

View File

@@ -31,10 +31,10 @@ namespace Fig
if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member))
{ {
return LvObject( return LvObject(
mod.ctx->get(member) mod.ctx->get(member));
);
} }
else { else
{
throw EvaluatorError( throw EvaluatorError(
u8"VariableNotFoundError", u8"VariableNotFoundError",
std::format( std::format(
@@ -146,8 +146,7 @@ namespace Fig
return LvObject( return LvObject(
base.get(), base.get(),
indexVal, indexVal,
LvObject::Kind::StringElement LvObject::Kind::StringElement);
);
} }
else else
{ {
@@ -366,6 +365,17 @@ namespace Fig
// check argument, all types of parameters // check argument, all types of parameters
Ast::FunctionParameters fnParas = fnStruct.paras; Ast::FunctionParameters fnParas = fnStruct.paras;
// create new context for function call
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())),
fnStruct.closureContext);
if (fnParas.variadic)
goto VariadicFilling;
else
goto NormalFilling;
NormalFilling: {
if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
{ {
throw RuntimeError(FString( throw RuntimeError(FString(
@@ -437,9 +447,7 @@ namespace Fig
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx);
evaluatedArgs.argv.push_back(defaultVal); evaluatedArgs.argv.push_back(defaultVal);
} }
// create new context for function call
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())),
fnStruct.closureContext);
// define parameters in new context // define parameters in new context
for (size_t j = 0; j < fnParas.size(); j++) for (size_t j = 0; j < fnParas.size(); j++)
{ {
@@ -459,6 +467,25 @@ namespace Fig
AccessModifier argAm = AccessModifier::Const; AccessModifier argAm = AccessModifier::Const;
newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); 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<Object>(
list));
goto ExecuteBody;
}
ExecuteBody: {
// execute function body // execute function body
ObjectPtr retVal = Object::getNullInstance(); ObjectPtr retVal = Object::getNullInstance();
for (const auto &stmt : fnStruct.body->stmts) for (const auto &stmt : fnStruct.body->stmts)
@@ -482,6 +509,7 @@ namespace Fig
} }
return retVal; return retVal;
} }
}
RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx) RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx)
{ {

View File

@@ -10,6 +10,8 @@ namespace Fig
{ {
const std::unordered_map<FString, TokenType> Lexer::symbol_map{ const std::unordered_map<FString, TokenType> Lexer::symbol_map{
// 三字符
{FString(u8"..."), TokenType::TripleDot},
// 双字符 // 双字符
{FString(u8"=="), TokenType::Equal}, {FString(u8"=="), TokenType::Equal},
{FString(u8"!="), TokenType::NotEqual}, {FString(u8"!="), TokenType::NotEqual},
@@ -380,30 +382,59 @@ namespace Fig
{ {
FString sym; FString sym;
UTF8Char ch = *it; UTF8Char ch = *it;
sym += ch.getString(); sym += ch.getString();
UTF8Char peek = UTF8Char(u8"");
if (hasNext() and (peek = it.peek()).isPunct()) // 窥探下一个操作符 auto startsWith = [&](const FString &prefix) -> bool {
for (const auto &p : symbol_map)
{ {
FString symd = FString(sym + peek.getString()); const FString &op = p.first;
if (this->symbol_map.contains(symd)) if (op.starts_with(prefix))
{ return true;
// Operator length is 2
next();
sym = symd;
} }
// Operator length is 1 return false;
else if (!this->symbol_map.contains(sym)) };
if (!startsWith(sym))
{ {
// check legality error = SyntaxError(
error = SyntaxError(FString( FString(std::format("No such operator: {}", sym.toBasicString())),
std::format("No such a operator: {}", sym.toBasicString())),
this->line, it.column()); this->line, it.column());
}
}
next(); next();
return Token(sym, this->symbol_map.at(sym)); // const object 'symbol_map', operator[] call is invalid return IllegalTok;
} }
while (hasNext())
{
UTF8Char peek = it.peek();
if (!peek.isPunct())
break;
FString candidate = sym + FString(peek.getString());
if (startsWith(candidate))
{
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, symbol_map.at(sym));
}
Token Lexer::scanComments() Token Lexer::scanComments()
{ {
// entry: when iterator current char is '/' and peek is '/' or '*' // entry: when iterator current char is '/' and peek is '/' or '*'

View File

@@ -144,6 +144,7 @@ namespace Fig
Ast::FunctionParameters::PosParasType pp; Ast::FunctionParameters::PosParasType pp;
Ast::FunctionParameters::DefParasType dp; Ast::FunctionParameters::DefParasType dp;
FString variaPara;
while (true) while (true)
{ {
if (isThis(TokenType::RightParen)) if (isThis(TokenType::RightParen))
@@ -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 else
{ {
pp.push_back({pname, ValueType::Any.name}); pp.push_back({pname, ValueType::Any.name});

View File

@@ -105,6 +105,8 @@ namespace Fig
DoublePipe, // || DoublePipe, // ||
Walrus, // := Walrus, // :=
Power, // ** Power, // **
TripleDot, // ... for variadic parameter
}; };
class Token final class Token final