forked from PuqiAR/Fig-TreeWalker
[Feat] 函数支持可变参数!! func (x...) 获取到的x类型为List
[Impl] Lexer现在贪心检测符号,拓展时可以直接添加到symbol_map
This commit is contained in:
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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());
|
|
||||||
if (this->symbol_map.contains(symd))
|
|
||||||
{
|
{
|
||||||
// Operator length is 2
|
const FString &op = p.first;
|
||||||
next();
|
if (op.starts_with(prefix))
|
||||||
sym = symd;
|
return true;
|
||||||
}
|
}
|
||||||
// Operator length is 1
|
return false;
|
||||||
else if (!this->symbol_map.contains(sym))
|
};
|
||||||
|
|
||||||
|
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
|
next();
|
||||||
error = SyntaxError(FString(
|
sym = candidate;
|
||||||
std::format("No such a operator: {}", sym.toBasicString())),
|
}
|
||||||
this->line, it.column());
|
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();
|
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()
|
Token Lexer::scanComments()
|
||||||
{
|
{
|
||||||
// entry: when iterator current char is '/' and peek is '/' or '*'
|
// entry: when iterator current char is '/' and peek is '/' or '*'
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -153,7 +154,7 @@ namespace Fig
|
|||||||
}
|
}
|
||||||
expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current
|
expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current
|
||||||
FString pname = currentToken().getValue();
|
FString pname = currentToken().getValue();
|
||||||
next(); // skip pname
|
next(); // skip pname
|
||||||
if (isThis(TokenType::Assign)) // =
|
if (isThis(TokenType::Assign)) // =
|
||||||
{
|
{
|
||||||
next();
|
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
|
else
|
||||||
{
|
{
|
||||||
pp.push_back({pname, ValueType::Any.name});
|
pp.push_back({pname, ValueType::Any.name});
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ namespace Fig
|
|||||||
DoublePipe, // ||
|
DoublePipe, // ||
|
||||||
Walrus, // :=
|
Walrus, // :=
|
||||||
Power, // **
|
Power, // **
|
||||||
|
|
||||||
|
TripleDot, // ... for variadic parameter
|
||||||
};
|
};
|
||||||
|
|
||||||
class Token final
|
class Token final
|
||||||
|
|||||||
Reference in New Issue
Block a user