forked from PuqiAR/Fig-TreeWalker
v0.3.1
This commit is contained in:
488
src/evaluator.cpp
Normal file
488
src/evaluator.cpp
Normal file
@@ -0,0 +1,488 @@
|
||||
#include <evaluator.hpp>
|
||||
#include <builtins.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
Value Evaluator::__evalOp(Ast::Operator op, const Value &lhs, const Value &rhs)
|
||||
{
|
||||
using Fig::Ast::Operator;
|
||||
switch (op)
|
||||
{
|
||||
case Operator::Add: return lhs + rhs;
|
||||
case Operator::Subtract: return lhs - rhs;
|
||||
case Operator::Multiply: return lhs * rhs;
|
||||
case Operator::Divide: return lhs / rhs;
|
||||
case Operator::Modulo: return lhs % rhs;
|
||||
|
||||
case Operator::And: return lhs && rhs;
|
||||
case Operator::Or: return lhs || rhs;
|
||||
case Operator::Not: return !lhs;
|
||||
|
||||
case Operator::Equal: return Value(lhs == rhs);
|
||||
case Operator::NotEqual: return Value(lhs != rhs);
|
||||
case Operator::Less: return lhs < rhs;
|
||||
case Operator::LessEqual: return lhs <= rhs;
|
||||
case Operator::Greater: return lhs > rhs;
|
||||
case Operator::GreaterEqual: return lhs >= rhs;
|
||||
|
||||
case Operator::BitAnd: return bit_and(lhs, rhs);
|
||||
case Operator::BitOr: return bit_or(lhs, rhs);
|
||||
case Operator::BitXor: return bit_xor(lhs, rhs);
|
||||
case Operator::BitNot: return bit_not(lhs);
|
||||
case Operator::ShiftLeft: return shift_left(lhs, rhs);
|
||||
case Operator::ShiftRight: return shift_right(lhs, rhs);
|
||||
|
||||
case Operator::Walrus: {
|
||||
static constexpr char WalrusErrorName[] = "WalrusError";
|
||||
throw EvaluatorError<WalrusErrorName>(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now
|
||||
}
|
||||
default:
|
||||
throw RuntimeError(FStringView(u8"Unsupported operator"));
|
||||
}
|
||||
}
|
||||
|
||||
Value Evaluator::evalBinary(const Ast::BinaryExpr &binExp)
|
||||
{
|
||||
return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp));
|
||||
}
|
||||
Value Evaluator::evalUnary(const Ast::UnaryExpr &unExp)
|
||||
{
|
||||
using Fig::Ast::Operator;
|
||||
switch (unExp->op)
|
||||
{
|
||||
case Operator::Not:
|
||||
return !eval(unExp->exp);
|
||||
case Operator::Subtract:
|
||||
return -eval(unExp->exp);
|
||||
case Operator::BitNot:
|
||||
return bit_not(eval(unExp->exp));
|
||||
default:
|
||||
throw RuntimeError(FStringView(std::format("Unsupported unary operator: {}", magic_enum::enum_name(unExp->op))));
|
||||
}
|
||||
}
|
||||
|
||||
Value Evaluator::eval(Ast::Expression exp)
|
||||
{
|
||||
using Fig::Ast::AstType;
|
||||
switch (exp->getType())
|
||||
{
|
||||
case AstType::ValueExpr: {
|
||||
auto valExp = std::dynamic_pointer_cast<Ast::ValueExprAst>(exp);
|
||||
return valExp->val;
|
||||
}
|
||||
case AstType::VarExpr: {
|
||||
auto varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(exp);
|
||||
auto val = currentContext->get(varExp->name);
|
||||
if (val.has_value())
|
||||
{
|
||||
return val.value();
|
||||
}
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", varExp->name.toBasicString())));
|
||||
}
|
||||
case AstType::BinaryExpr: {
|
||||
auto binExp = std::dynamic_pointer_cast<Ast::BinaryExprAst>(exp);
|
||||
return evalBinary(binExp);
|
||||
}
|
||||
case AstType::UnaryExpr: {
|
||||
auto unExp = std::dynamic_pointer_cast<Ast::UnaryExprAst>(exp);
|
||||
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>())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
Ast::FunctionCallArgs evaluatedArgs;
|
||||
|
||||
// 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;
|
||||
}
|
||||
case AstType::ListExpr: {
|
||||
auto listexpr = std::dynamic_pointer_cast<Ast::ListExprAst>(exp);
|
||||
}
|
||||
default:
|
||||
throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast<int>(exp->getType()))));
|
||||
return Value::getNullInstance();
|
||||
}
|
||||
}
|
||||
|
||||
StatementResult Evaluator::evalStatement(const Ast::Statement &stmt)
|
||||
{
|
||||
using Fig::Ast::AstType;
|
||||
switch (stmt->getType())
|
||||
{
|
||||
case AstType::VarDefSt: {
|
||||
auto varDef = std::dynamic_pointer_cast<Ast::VarDefAst>(stmt);
|
||||
if (currentContext->contains(varDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Variable '{}' already defined in this scope", varDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
Value val;
|
||||
TypeInfo varTypeInfo;
|
||||
if (varDef->typeName == Parser::varDefTypeFollowed)
|
||||
{
|
||||
// has expr
|
||||
val = eval(varDef->expr);
|
||||
varTypeInfo = val.getTypeInfo();
|
||||
}
|
||||
else if (varDef->expr)
|
||||
{
|
||||
val = eval(varDef->expr);
|
||||
if (varDef->typeName != ValueType::Any.name)
|
||||
{
|
||||
TypeInfo expectedType(varDef->typeName);
|
||||
TypeInfo actualType = val.getTypeInfo();
|
||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("Variable '{}' expects type '{}', but got type '{}'", varDef->name.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!varDef->typeName.empty())
|
||||
{
|
||||
varTypeInfo = TypeInfo(varDef->typeName); // may throw
|
||||
val = Value::defaultValue(varTypeInfo);
|
||||
}
|
||||
AccessModifier am = (varDef->isPublic ? (varDef->isConst ? AccessModifier::PublicConst : AccessModifier::Public) : (varDef->isConst ? AccessModifier::Const : AccessModifier::Normal));
|
||||
currentContext->def(varDef->name, varTypeInfo, am, val);
|
||||
return StatementResult::normal();
|
||||
}
|
||||
case AstType::ExpressionStmt: {
|
||||
auto exprSt = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(stmt);
|
||||
eval(exprSt->exp);
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::BlockStatement: {
|
||||
auto blockSt = std::dynamic_pointer_cast<Ast::BlockStatementAst>(stmt);
|
||||
auto newContext = std::make_shared<Context>(FString(std::format("<Block {}:{}>", blockSt->getAAI().line, blockSt->getAAI().column)), currentContext);
|
||||
auto previousContext = currentContext;
|
||||
currentContext = newContext;
|
||||
StatementResult lstResult = StatementResult::normal();
|
||||
for (const auto &s : blockSt->stmts)
|
||||
{
|
||||
StatementResult sr = evalStatement(s);
|
||||
if (!sr.isNormal())
|
||||
{
|
||||
lstResult = sr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentContext = previousContext;
|
||||
return lstResult;
|
||||
};
|
||||
case AstType::FunctionDefSt: {
|
||||
auto fnDef = std::dynamic_pointer_cast<Ast::FunctionDefSt>(stmt);
|
||||
if (currentContext->contains(fnDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Function '{}' already defined in this scope", fnDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
AccessModifier am = (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
currentContext->def(
|
||||
fnDef->name,
|
||||
ValueType::Function,
|
||||
am,
|
||||
Value(Function(
|
||||
fnDef->paras,
|
||||
TypeInfo(fnDef->retType),
|
||||
fnDef->body)));
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::StructSt: {
|
||||
auto stDef = std::dynamic_pointer_cast<Ast::StructDefSt>(stmt);
|
||||
if (currentContext->contains(stDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
std::vector<Field> fields;
|
||||
std::vector<FString> _fieldNames;
|
||||
for (Ast::StructDefField field : stDef->fields)
|
||||
{
|
||||
if (Utils::vectorContains(field.fieldName, _fieldNames))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Field '{}' already defined in structure '{}'", field.fieldName.toBasicString(), stDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr));
|
||||
}
|
||||
ContextPtr defContext(currentContext);
|
||||
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
currentContext->def(
|
||||
stDef->name,
|
||||
ValueType::StructType,
|
||||
am,
|
||||
Value(StructType(
|
||||
defContext,
|
||||
fields)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
case AstType::VarAssignSt: {
|
||||
auto varAssign = std::dynamic_pointer_cast<Ast::VarAssignSt>(stmt);
|
||||
if (!currentContext->contains(varAssign->varName))
|
||||
{
|
||||
static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError";
|
||||
throw EvaluatorError<VariableNotFoundErrorName>(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
if (!currentContext->isVariableMutable(varAssign->varName))
|
||||
{
|
||||
static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError";
|
||||
throw EvaluatorError<ConstAssignmentErrorName>(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
Value val = eval(varAssign->valueExpr);
|
||||
if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any)
|
||||
{
|
||||
TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName);
|
||||
TypeInfo actualType = val.getTypeInfo();
|
||||
if (expectedType != actualType)
|
||||
{
|
||||
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
}
|
||||
currentContext->set(varAssign->varName, val);
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::IfSt: {
|
||||
auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt);
|
||||
Value condVal = eval(ifSt->condition);
|
||||
if (condVal.getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"If condition must be boolean"), currentAddressInfo);
|
||||
}
|
||||
if (condVal.as<Bool>().getValue())
|
||||
{
|
||||
return evalStatement(ifSt->body);
|
||||
}
|
||||
// else
|
||||
for (const auto &elif : ifSt->elifs)
|
||||
{
|
||||
Value elifCondVal = eval(elif->condition);
|
||||
if (elifCondVal.getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"Else-if condition must be boolean"), currentAddressInfo);
|
||||
}
|
||||
if (elifCondVal.as<Bool>().getValue())
|
||||
{
|
||||
return evalStatement(elif->body);
|
||||
}
|
||||
}
|
||||
if (ifSt->els)
|
||||
{
|
||||
return evalStatement(ifSt->els->body);
|
||||
}
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::WhileSt: {
|
||||
auto whileSt = std::dynamic_pointer_cast<Ast::WhileSt>(stmt);
|
||||
while (true)
|
||||
{
|
||||
Value condVal = eval(whileSt->condition);
|
||||
if (condVal.getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"While condition must be boolean"), currentAddressInfo);
|
||||
}
|
||||
if (!condVal.as<Bool>().getValue())
|
||||
{
|
||||
break;
|
||||
}
|
||||
StatementResult sr = evalStatement(whileSt->body);
|
||||
if (sr.shouldReturn())
|
||||
{
|
||||
return sr;
|
||||
}
|
||||
if (sr.shouldBreak())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sr.shouldContinue())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::ReturnSt: {
|
||||
if (!currentContext->parent)
|
||||
{
|
||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||
}
|
||||
std::shared_ptr<Context> fc = currentContext;
|
||||
while (fc->parent)
|
||||
{
|
||||
if (fc->getScopeName().find(u8"<Function ") == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fc = fc->parent;
|
||||
}
|
||||
if (fc->getScopeName().find(u8"<Function ") != 0)
|
||||
{
|
||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||
}
|
||||
auto returnSt = std::dynamic_pointer_cast<Ast::ReturnSt>(stmt);
|
||||
return StatementResult::returnFlow(eval(returnSt->retValue));
|
||||
};
|
||||
default:
|
||||
throw RuntimeError(FStringView(std::string("Unknown statement type:") + magic_enum::enum_name(stmt->getType()).data()));
|
||||
}
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
void Evaluator::run()
|
||||
{
|
||||
for (auto ast : asts)
|
||||
{
|
||||
currentAddressInfo = ast->getAAI();
|
||||
if (std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast))
|
||||
{
|
||||
auto exprAst = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast);
|
||||
Ast::Expression exp = exprAst->exp;
|
||||
eval(exp);
|
||||
}
|
||||
else if (dynamic_cast<Ast::StatementAst *>(ast.get()))
|
||||
{
|
||||
auto stmtAst = std::dynamic_pointer_cast<Ast::StatementAst>(ast);
|
||||
evalStatement(stmtAst);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(u8"Unknown AST type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Evaluator::printStackTrace() const
|
||||
{
|
||||
if (currentContext)
|
||||
currentContext->printStackTrace();
|
||||
else
|
||||
std::cerr << "[STACK TRACE] (No context has been loaded)\n";
|
||||
}
|
||||
} // namespace Fig
|
||||
524
src/lexer.cpp
Normal file
524
src/lexer.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
#include <fig_string.hpp>
|
||||
#include <error.hpp>
|
||||
#include <token.hpp>
|
||||
#include <lexer.hpp>
|
||||
|
||||
#include <fig_string.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
const std::unordered_map<FString, TokenType> Lexer::symbol_map{
|
||||
// 双字符
|
||||
{FString(u8"=="), TokenType::Equal},
|
||||
{FString(u8"!="), TokenType::NotEqual},
|
||||
{FString(u8"<="), TokenType::LessEqual},
|
||||
{FString(u8">="), TokenType::GreaterEqual},
|
||||
{FString(u8"<<"), TokenType::ShiftLeft},
|
||||
{FString(u8">>"), TokenType::ShiftRight},
|
||||
{FString(u8"+="), TokenType::PlusEqual},
|
||||
{FString(u8"-="), TokenType::MinusEqual},
|
||||
{FString(u8"*="), TokenType::AsteriskEqual},
|
||||
{FString(u8"/="), TokenType::SlashEqual},
|
||||
{FString(u8"%="), TokenType::PercentEqual},
|
||||
{FString(u8"^="), TokenType::CaretEqual},
|
||||
{FString(u8"++"), TokenType::DoublePlus},
|
||||
{FString(u8"--"), TokenType::DoubleMinus},
|
||||
{FString(u8"&&"), TokenType::DoubleAmpersand},
|
||||
{FString(u8"||"), TokenType::DoublePipe},
|
||||
{FString(u8":="), TokenType::Walrus},
|
||||
{FString(u8"**"), TokenType::Power},
|
||||
{FString(u8"->"), TokenType::RightArrow},
|
||||
|
||||
// 单字符
|
||||
{FString(u8"+"), TokenType::Plus},
|
||||
{FString(u8"-"), TokenType::Minus},
|
||||
{FString(u8"*"), TokenType::Asterisk},
|
||||
{FString(u8"/"), TokenType::Slash},
|
||||
{FString(u8"%"), TokenType::Percent},
|
||||
{FString(u8"^"), TokenType::Caret},
|
||||
{FString(u8"&"), TokenType::Ampersand},
|
||||
{FString(u8"|"), TokenType::Pipe},
|
||||
{FString(u8"~"), TokenType::Tilde},
|
||||
{FString(u8"="), TokenType::Assign},
|
||||
{FString(u8"<"), TokenType::Less},
|
||||
{FString(u8">"), TokenType::Greater},
|
||||
{FString(u8"."), TokenType::Dot},
|
||||
{FString(u8","), TokenType::Comma},
|
||||
{FString(u8":"), TokenType::Colon},
|
||||
{FString(u8";"), TokenType::Semicolon},
|
||||
{FString(u8"'"), TokenType::SingleQuote},
|
||||
{FString(u8"\""), TokenType::DoubleQuote},
|
||||
{FString(u8"("), TokenType::LeftParen},
|
||||
{FString(u8")"), TokenType::RightParen},
|
||||
{FString(u8"["), TokenType::LeftBracket},
|
||||
{FString(u8"]"), TokenType::RightBracket},
|
||||
{FString(u8"{"), TokenType::LeftBrace},
|
||||
{FString(u8"}"), TokenType::RightBrace}};
|
||||
|
||||
const std::unordered_map<FString, TokenType> Lexer::keyword_map{
|
||||
{FString(u8"and"), TokenType::And},
|
||||
{FString(u8"or"), TokenType::Or},
|
||||
{FString(u8"not"), TokenType::Not},
|
||||
{FString(u8"import"), TokenType::Import},
|
||||
{FString(u8"fun"), TokenType::Function},
|
||||
{FString(u8"var"), TokenType::Variable},
|
||||
{FString(u8"const"), TokenType::Const},
|
||||
{FString(u8"final"), TokenType::Final},
|
||||
{FString(u8"while"), TokenType::While},
|
||||
{FString(u8"for"), TokenType::For},
|
||||
{FString(u8"if"), TokenType::If},
|
||||
{FString(u8"else"), TokenType::Else},
|
||||
{FString(u8"struct"), TokenType::Struct},
|
||||
{FString(u8"interface"), TokenType::Interface},
|
||||
{FString(u8"implement"), TokenType::Implement},
|
||||
{FString(u8"public"), TokenType::Public},
|
||||
{FString(u8"return"), TokenType::Return},
|
||||
|
||||
|
||||
// {FString(u8"Null"), TokenType::TypeNull},
|
||||
// {FString(u8"Int"), TokenType::TypeInt},
|
||||
// {FString(u8"String"), TokenType::TypeString},
|
||||
// {FString(u8"Bool"), TokenType::TypeBool},
|
||||
// {FString(u8"Double"), TokenType::TypeDouble},
|
||||
};
|
||||
void Lexer::skipLine()
|
||||
{
|
||||
while (*it != U'\n' and hasNext())
|
||||
{
|
||||
next();
|
||||
}
|
||||
next(); // skip '\n'
|
||||
++line;
|
||||
}
|
||||
Token Lexer::scanIdentifier()
|
||||
{
|
||||
FString identifier;
|
||||
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c.isAlnum() || c == U'_')
|
||||
{
|
||||
identifier += c.getString();
|
||||
next();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this->keyword_map.contains(identifier))
|
||||
{
|
||||
return Token(identifier, this->keyword_map.at(identifier));
|
||||
}
|
||||
else if (identifier == u8"true" || identifier == u8"false")
|
||||
{
|
||||
return Token(identifier, TokenType::LiteralBool);
|
||||
}
|
||||
else if (identifier == u8"null")
|
||||
{
|
||||
// null instance
|
||||
return Token(identifier, TokenType::LiteralNull);
|
||||
}
|
||||
if (keyword_map.contains(Utils::toLower(identifier)))
|
||||
{
|
||||
pushWarning(1, identifier); // Identifier is too similar to a keyword or a primitive type
|
||||
}
|
||||
if (identifier.length() <= 1)
|
||||
{
|
||||
pushWarning(2, identifier); // The identifier is too abstract
|
||||
}
|
||||
return Token(identifier, TokenType::Identifier);
|
||||
}
|
||||
Token Lexer::scanString()
|
||||
{
|
||||
FString str;
|
||||
bool unterminated = true;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c == U'"' || c == U'\n')
|
||||
{
|
||||
next();
|
||||
unterminated = false;
|
||||
break;
|
||||
}
|
||||
else if (c == U'\\') // c is '\'
|
||||
{
|
||||
if (it.isEnd())
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, it.column());
|
||||
return IllegalTok;
|
||||
}
|
||||
next();
|
||||
UTF8Char ec = *it;
|
||||
if (ec == U'n')
|
||||
{
|
||||
next();
|
||||
str += u8"\n";
|
||||
}
|
||||
else if (ec == U't')
|
||||
{
|
||||
next();
|
||||
str += u8"\t";
|
||||
}
|
||||
else if (ec == U'v')
|
||||
{
|
||||
next();
|
||||
str += u8"\v";
|
||||
}
|
||||
else if (ec == U'b')
|
||||
{
|
||||
next();
|
||||
str += u8"\b";
|
||||
}
|
||||
else if (ec == U'"')
|
||||
{
|
||||
next();
|
||||
str += u8"\"";
|
||||
}
|
||||
else if (ec == U'\'')
|
||||
{
|
||||
next();
|
||||
str += u8"'";
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FStringView(
|
||||
std::format(
|
||||
"Unsupported escape character: {}",
|
||||
FString(ec.getString()).toBasicString())),
|
||||
this->line,
|
||||
it.column());
|
||||
return IllegalTok;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
next();
|
||||
}
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanRawString()
|
||||
{
|
||||
FString str;
|
||||
bool unterminated = true;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c == U'"' || c == U'\n')
|
||||
{
|
||||
next();
|
||||
unterminated = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
next();
|
||||
}
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanMultilineString()
|
||||
{
|
||||
FString str;
|
||||
bool unterminated = true;
|
||||
|
||||
uint8_t end = 0;
|
||||
size_t str_start_col = it.column() - 1;
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c == U'"')
|
||||
{
|
||||
if (end == 3)
|
||||
{
|
||||
next();
|
||||
unterminated = false;
|
||||
break;
|
||||
}
|
||||
end++;
|
||||
next();
|
||||
continue;
|
||||
}
|
||||
else if (c == U'\\') // c is '\'
|
||||
{
|
||||
if (it.isEnd())
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, it.column());
|
||||
return IllegalTok;
|
||||
}
|
||||
next();
|
||||
UTF8Char ec = *it;
|
||||
if (ec == U'n')
|
||||
{
|
||||
next();
|
||||
str += u8"\n";
|
||||
}
|
||||
else if (ec == U't')
|
||||
{
|
||||
next();
|
||||
str += u8"\t";
|
||||
}
|
||||
else if (ec == U'v')
|
||||
{
|
||||
next();
|
||||
str += u8"\v";
|
||||
}
|
||||
else if (ec == U'b')
|
||||
{
|
||||
next();
|
||||
str += u8"\b";
|
||||
}
|
||||
else if (ec == U'"')
|
||||
{
|
||||
next();
|
||||
str += u8"\"";
|
||||
}
|
||||
else if (ec == U'\'')
|
||||
{
|
||||
next();
|
||||
str += u8"'";
|
||||
}
|
||||
else if (ec == U'\\')
|
||||
{
|
||||
next();
|
||||
str += u8"\\";
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FStringView(
|
||||
std::format(
|
||||
"Unsupported escape character: {}",
|
||||
FString(ec.getString()).toBasicString())),
|
||||
this->line,
|
||||
it.column());
|
||||
return IllegalTok;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str += c.getString();
|
||||
}
|
||||
end = 0;
|
||||
}
|
||||
if (unterminated)
|
||||
{
|
||||
error = SyntaxError(u8"Unterminated FString", this->line, str_start_col);
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(str, TokenType::LiteralString);
|
||||
}
|
||||
Token Lexer::scanNumber()
|
||||
{
|
||||
FString numStr;
|
||||
bool hasPoint = false;
|
||||
// 负号(减号) 直接交由 scanSymbol处理,在parser中被分类->与数字结合/变为操作数
|
||||
while (hasNext())
|
||||
{
|
||||
UTF8Char ch = *it;
|
||||
if (ch.isDigit() or ch == U'e') // . / e / - for scientific counting
|
||||
{
|
||||
numStr += ch.getString();
|
||||
next();
|
||||
}
|
||||
else if (ch == U'-' and numStr.ends_with(U'-'))
|
||||
{
|
||||
numStr += ch.getString();
|
||||
next();
|
||||
}
|
||||
else if (ch == U'.' and not hasPoint)
|
||||
{
|
||||
hasPoint = true;
|
||||
numStr += ch.getString();
|
||||
next();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Numbers in Fig-lang
|
||||
/*
|
||||
114514
|
||||
1145.14
|
||||
1.14e3 -> 1140
|
||||
1.14e-3 -> 0.00114
|
||||
.3 -> 0.3
|
||||
*/
|
||||
// checking legality
|
||||
if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数
|
||||
{
|
||||
error = SyntaxError(FStringView(
|
||||
std::format("Ellegal number literal: {}", numStr.toBasicString())),
|
||||
this->line, it.column());
|
||||
return IllegalTok;
|
||||
}
|
||||
return Token(numStr, TokenType::LiteralNumber);
|
||||
}
|
||||
Token Lexer::scanSymbol()
|
||||
{
|
||||
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))
|
||||
{
|
||||
// Operator length is 2
|
||||
next();
|
||||
sym = symd;
|
||||
}
|
||||
// Operator length is 1
|
||||
else if (!this->symbol_map.contains(sym))
|
||||
{
|
||||
// check legality
|
||||
error = SyntaxError(FStringView(
|
||||
std::format("No such a operator: {}", sym.toBasicString())),
|
||||
this->line, it.column());
|
||||
}
|
||||
}
|
||||
next();
|
||||
return Token(sym, this->symbol_map.at(sym)); // const object 'symbol_map', operator[] call is invalid
|
||||
}
|
||||
Token Lexer::scanComments()
|
||||
{
|
||||
// entry: when iterator current char is '/' and peek is '/' or '*'
|
||||
// current char is '/'
|
||||
FString comment;
|
||||
if (it.peek() == U'/')
|
||||
{
|
||||
next();
|
||||
next();
|
||||
UTF8Char c = *it;
|
||||
while (c != U'\n' and hasNext())
|
||||
{
|
||||
comment += c.getString();
|
||||
next();
|
||||
}
|
||||
next();
|
||||
}
|
||||
else
|
||||
{
|
||||
next();
|
||||
next();
|
||||
UTF8Char c = *it;
|
||||
bool terminated = false;
|
||||
while (hasNext())
|
||||
{
|
||||
if (c == U'*' and hasNext() and it.peek() == U'/')
|
||||
{
|
||||
next(); // skip '*'
|
||||
next(); // skip '/'
|
||||
next(); // to next char
|
||||
terminated = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
comment += c.getString();
|
||||
next();
|
||||
}
|
||||
}
|
||||
if (!terminated)
|
||||
{
|
||||
error = SyntaxError(FStringView(u8"Unterminated multiline comment"), this->line, it.column());
|
||||
next();
|
||||
return IllegalTok;
|
||||
}
|
||||
}
|
||||
return Token(comment, TokenType::Comments);
|
||||
}
|
||||
Token Lexer::nextToken()
|
||||
{
|
||||
if (!hasNext())
|
||||
{
|
||||
return EOFTok;
|
||||
}
|
||||
UTF8Char ch = *it;
|
||||
while (ch.isSpace())
|
||||
{
|
||||
next();
|
||||
ch = *it;
|
||||
if (!hasNext())
|
||||
{
|
||||
return EOFTok.setPos(getCurrentLine(), getCurrentColumn());
|
||||
}
|
||||
}
|
||||
last_line = getCurrentLine();
|
||||
last_column = getCurrentColumn();
|
||||
if (ch == U'r' and hasNext() and it.peek() == U'"')
|
||||
{
|
||||
// r""
|
||||
// raw FString
|
||||
next();
|
||||
next();
|
||||
return scanRawString().setPos(last_line, last_column);
|
||||
}
|
||||
if (ch.isAlpha() || ch == U'_')
|
||||
{
|
||||
return scanIdentifier().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch == U'"')
|
||||
{
|
||||
next();
|
||||
return scanString().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch.isDigit())
|
||||
{
|
||||
return scanNumber().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch == U'/')
|
||||
{
|
||||
UTF8Char c{u8""};
|
||||
if (!hasNext())
|
||||
{
|
||||
next();
|
||||
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||
}
|
||||
c = it.peek();
|
||||
if (c != U'/' and c != U'*')
|
||||
{
|
||||
next();
|
||||
return Token(u8"/", this->symbol_map.at(u8"/")).setPos(last_line, last_column);
|
||||
}
|
||||
return scanComments().setPos(last_line, last_column);
|
||||
}
|
||||
else if (ch.isPunct())
|
||||
{
|
||||
return scanSymbol().setPos(last_line, last_column);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = SyntaxError(FStringView(
|
||||
std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())),
|
||||
this->line, it.column());
|
||||
if (hasNext())
|
||||
{
|
||||
next();
|
||||
}
|
||||
return IllegalTok.setPos(last_line, last_column);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fig
|
||||
187
src/main.cpp
Normal file
187
src/main.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
███████████ █████ █████ ██████████ ███████████ █████ █████████ █████ █████████ ██████ █████ █████████ █████ █████ █████████ █████████ ██████████
|
||||
░█░░░███░░░█░░███ ░░███ ░░███░░░░░█ ░░███░░░░░░█░░███ ███░░░░░███ ░░███ ███░░░░░███ ░░██████ ░░███ ███░░░░░███░░███ ░░███ ███░░░░░███ ███░░░░░███░░███░░░░░█
|
||||
░ ░███ ░ ░███ ░███ ░███ █ ░ ░███ █ ░ ░███ ███ ░░░ ░███ ░███ ░███ ░███░███ ░███ ███ ░░░ ░███ ░███ ░███ ░███ ███ ░░░ ░███ █ ░
|
||||
░███ ░███████████ ░██████ ░███████ ░███ ░███ ░███ ░███████████ ░███░░███░███ ░███ ░███ ░███ ░███████████ ░███ ░██████
|
||||
░███ ░███░░░░░███ ░███░░█ ░███░░░█ ░███ ░███ █████ ░███ ░███░░░░░███ ░███ ░░██████ ░███ █████ ░███ ░███ ░███░░░░░███ ░███ █████ ░███░░█
|
||||
░███ ░███ ░███ ░███ ░ █ ░███ ░ ░███ ░░███ ░░███ ░███ █ ░███ ░███ ░███ ░░█████ ░░███ ░░███ ░███ ░███ ░███ ░███ ░░███ ░░███ ░███ ░ █
|
||||
█████ █████ █████ ██████████ █████ █████ ░░█████████ ███████████ █████ █████ █████ ░░█████ ░░█████████ ░░████████ █████ █████ ░░█████████ ██████████
|
||||
░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░░ ░░░░░░░░░░
|
||||
|
||||
|
||||
.---.
|
||||
. __.....__ .--. | | _..._ __.....__
|
||||
.'| .-'' '. _.._ |__| .--./) | | .' '. .--./) .--./) .-'' '.
|
||||
.| < | / .-''"'-. `. .' .._|.--. /.''\\ | | . .-. . /.''\\ /.''\\ / .-''"'-. `.
|
||||
.' |_ | | / /________\ \ | ' | || | | | | | __ | ' ' || | | | __ | | | |/ /________\ \
|
||||
.' || | .'''-. | | __| |__ | | \`-' / | | .:--.'. | | | | \`-' / _ _ .:--.'. \`-' / | |
|
||||
'--. .-'| |/.'''. \\ .-------------' |__ __| | | /("'` | |/ | \ | | | | | /("'` | ' / | / | \ | /("'` \ .-------------'
|
||||
| | | / | | \ '-.____...---. | | | | \ '---. | |`" __ | | | | | | \ '---. .' | .' | `" __ | | \ '---. \ '-.____...---.
|
||||
| | | | | | `. .' | | |__| /'""'.\ | | .'.''| | | | | | /'""'.\ / | / | .'.''| | /'""'.\ `. .'
|
||||
| '.'| | | | `''-...... -' | | || || '---'/ / | |_| | | | || ||| `'. | / / | |_|| || `''-...... -'
|
||||
| / | '. | '. | | \'. __// \ \._,\ '/| | | | \'. __// ' .'| '/\ \._,\ '/\'. __//
|
||||
`'-' '---' '---' |_| `'---' `--' `" '--' '--' `'---' `-' `--' `--' `" `'---'
|
||||
|
||||
Copyright (C) 2020-2025 PuqiAR
|
||||
|
||||
This software is licensed under the MIT License. See LICENSE.txt for details.
|
||||
*/
|
||||
|
||||
#include <argparse/argparse.hpp>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
|
||||
#include <core.hpp>
|
||||
#include <lexer.hpp>
|
||||
#include <parser.hpp>
|
||||
#include <evaluator.hpp>
|
||||
#include <AstPrinter.hpp>
|
||||
#include <errorLog.hpp>
|
||||
|
||||
static size_t addressableErrorCount = 0;
|
||||
static size_t unaddressableErrorCount = 0;
|
||||
|
||||
std::vector<FString> splitSource(FString source)
|
||||
{
|
||||
UTF8Iterator it(source);
|
||||
std::vector<FString> lines;
|
||||
FString currentLine;
|
||||
while (!it.isEnd())
|
||||
{
|
||||
UTF8Char c = *it;
|
||||
if (c == U'\n')
|
||||
{
|
||||
lines.push_back(currentLine);
|
||||
currentLine = FString(u8"");
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLine += c.getString();
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (!currentLine.empty())
|
||||
{
|
||||
lines.push_back(currentLine);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
argparse::ArgumentParser program("Fig Interpreter", Fig::Core::VERSION.data());
|
||||
program.add_argument("source")
|
||||
.help("source file to be interpreted");
|
||||
// interpreter
|
||||
|
||||
try
|
||||
{
|
||||
program.parse_args(argc, argv);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
Fig::FString sourcePath(program.get<std::string>("source"));
|
||||
std::ifstream file(sourcePath.toBasicString());
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cerr << "Could not open file: " << sourcePath.toBasicString() << '\n';
|
||||
return 1;
|
||||
}
|
||||
std::string source((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
file.close();
|
||||
|
||||
Fig::Lexer lexer((Fig::FString(source)));
|
||||
Fig::Parser parser(lexer);
|
||||
std::vector<Fig::Ast::AstBase> ast;
|
||||
|
||||
std::vector<FString> sourceLines = splitSource(Fig::FString(source));
|
||||
|
||||
try
|
||||
{
|
||||
ast = parser.parseAll();
|
||||
}
|
||||
catch (const Fig::AddressableError &e)
|
||||
{
|
||||
addressableErrorCount++;
|
||||
ErrorLog::logAddressableError(e, sourcePath, sourceLines);
|
||||
return 1;
|
||||
}
|
||||
catch (const Fig::UnaddressableError &e)
|
||||
{
|
||||
unaddressableErrorCount++;
|
||||
ErrorLog::logUnaddressableError(e);
|
||||
return 1;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "uncaught exception of: " << e.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Token tok;
|
||||
// while ((tok = lexer.nextToken()).getType() != TokenType::EndOfFile)
|
||||
// {
|
||||
// std::println("{}", tok.toString().toBasicString());
|
||||
// }
|
||||
|
||||
// AstPrinter printer;
|
||||
// std::print("<Debug> AST:\n");
|
||||
// for (const auto &node : ast)
|
||||
// {
|
||||
// printer.print(node);
|
||||
// }
|
||||
|
||||
Fig::Evaluator evaluator(ast);
|
||||
try
|
||||
{
|
||||
evaluator.run();
|
||||
}
|
||||
catch (const Fig::AddressableError &e)
|
||||
{
|
||||
addressableErrorCount++;
|
||||
ErrorLog::logAddressableError(e, sourcePath, sourceLines);
|
||||
evaluator.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
catch (const Fig::UnaddressableError &e)
|
||||
{
|
||||
unaddressableErrorCount++;
|
||||
ErrorLog::logUnaddressableError(e);
|
||||
evaluator.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// try
|
||||
// {
|
||||
// std::vector<Fig::Ast> ast = parser.parseAll();
|
||||
// AstPrinter printer;
|
||||
|
||||
// std::print("<Debug> AST:\n");
|
||||
// for (const auto &node : ast)
|
||||
// {
|
||||
// printer.print(node);
|
||||
// }
|
||||
|
||||
// Fig::Evaluator evaluator(ast);
|
||||
// evaluator.run();
|
||||
// }
|
||||
// catch (const Fig::AddressableError &e)
|
||||
// {
|
||||
// std::cerr << e.what() << '\n';
|
||||
// return 1;
|
||||
// }
|
||||
// catch (const Fig::UnaddressableError &e)
|
||||
// {
|
||||
// std::cerr << e.what() << '\n';
|
||||
// return 1;
|
||||
// }
|
||||
// catch (const std::exception &e)
|
||||
// {
|
||||
// std::cerr << e.what() << '\n';
|
||||
// return 1;
|
||||
// }
|
||||
}
|
||||
848
src/parser.cpp
Normal file
848
src/parser.cpp
Normal file
@@ -0,0 +1,848 @@
|
||||
#include <parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// Operator : pair<LeftBindingPower, RightBindingPower>
|
||||
|
||||
const std::unordered_map<Ast::Operator, std::pair<Parser::Precedence, Parser::Precedence>> Parser::opPrecedence = {
|
||||
// 算术
|
||||
{Ast::Operator::Add, {10, 11}},
|
||||
{Ast::Operator::Subtract, {10, 11}},
|
||||
{Ast::Operator::Multiply, {20, 21}},
|
||||
{Ast::Operator::Divide, {20, 21}},
|
||||
{Ast::Operator::Modulo, {20, 21}},
|
||||
{Ast::Operator::Power, {30, 29}},
|
||||
|
||||
// 逻辑
|
||||
{Ast::Operator::And, {5, 6}},
|
||||
{Ast::Operator::Or, {4, 5}},
|
||||
{Ast::Operator::Not, {30, 31}}, // 一元
|
||||
|
||||
// 比较
|
||||
{Ast::Operator::Equal, {7, 8}},
|
||||
{Ast::Operator::NotEqual, {7, 8}},
|
||||
{Ast::Operator::Less, {8, 9}},
|
||||
{Ast::Operator::LessEqual, {8, 9}},
|
||||
{Ast::Operator::Greater, {8, 9}},
|
||||
{Ast::Operator::GreaterEqual, {8, 9}},
|
||||
|
||||
// 位运算
|
||||
{Ast::Operator::BitAnd, {6, 7}},
|
||||
{Ast::Operator::BitOr, {4, 5}},
|
||||
{Ast::Operator::BitXor, {5, 6}},
|
||||
{Ast::Operator::BitNot, {30, 31}}, // 一元
|
||||
{Ast::Operator::ShiftLeft, {15, 16}},
|
||||
{Ast::Operator::ShiftRight, {15, 16}},
|
||||
|
||||
// 海象运算符
|
||||
{Ast::Operator::Walrus, {2, 1}}, // 右结合
|
||||
|
||||
// 点运算符
|
||||
{Ast::Operator::Dot, {40, 41}},
|
||||
};
|
||||
|
||||
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
||||
{
|
||||
// entry: current is keyword `var` or `const`
|
||||
bool isConst = (currentToken().getType() == TokenType::Const ? true : false);
|
||||
next();
|
||||
expect(TokenType::Identifier);
|
||||
FString name = currentToken().getValue();
|
||||
next();
|
||||
FString tiName = ValueType::Any.name;
|
||||
bool hasSpecificType = false;
|
||||
if (isThis(TokenType::Colon)) // :
|
||||
{
|
||||
expectPeek(TokenType::Identifier, FString(u8"Type name"));
|
||||
next();
|
||||
tiName = currentToken().getValue();
|
||||
next();
|
||||
hasSpecificType = true;
|
||||
}
|
||||
if (isThis(TokenType::Semicolon))
|
||||
{
|
||||
next();
|
||||
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, nullptr);
|
||||
}
|
||||
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus");
|
||||
if (isThis(TokenType::Walrus))
|
||||
{
|
||||
if (hasSpecificType) throwAddressableError<SyntaxError>(FStringView(u8""));
|
||||
tiName = Parser::varDefTypeFollowed;
|
||||
}
|
||||
next();
|
||||
Ast::Expression exp = parseExpression(0);
|
||||
expect(TokenType::Semicolon);
|
||||
next();
|
||||
return makeAst<Ast::VarDefAst>(isPublic, isConst, name, tiName, exp);
|
||||
}
|
||||
|
||||
Value Parser::__parseValue()
|
||||
{
|
||||
FString _val = currentToken().getValue();
|
||||
if (currentToken().getType() == TokenType::LiteralNumber)
|
||||
{
|
||||
if (_val.contains(u8'.') || _val.contains(u8'e'))
|
||||
{
|
||||
// 非整数
|
||||
ValueType::DoubleClass d;
|
||||
try
|
||||
{
|
||||
d = std::stod(_val.toBasicString());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
|
||||
}
|
||||
return Value(d);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 整数
|
||||
ValueType::IntClass i;
|
||||
try
|
||||
{
|
||||
i = std::stoi(_val.toBasicString());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
|
||||
}
|
||||
return Value(i);
|
||||
}
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::LiteralString)
|
||||
{
|
||||
return Value(_val);
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::LiteralBool)
|
||||
{
|
||||
return Value((_val == u8"true" ? true : false));
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::LiteralNull)
|
||||
{
|
||||
return Value::getNullInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("Internal Error at: ") + std::string(__func__));
|
||||
}
|
||||
}
|
||||
|
||||
Ast::ValueExpr Parser::__parseValueExpr()
|
||||
{
|
||||
return Ast::ValueExpr(new Ast::ValueExprAst(__parseValue()));
|
||||
}
|
||||
Ast::FunctionParameters Parser::__parseFunctionParameters()
|
||||
{
|
||||
// entry: current is Token::LeftParen
|
||||
// stop: current is `)` next one
|
||||
// *note: must called when parsing function
|
||||
|
||||
next(); // skip `(`
|
||||
|
||||
Ast::FunctionParameters::PosParasType pp;
|
||||
Ast::FunctionParameters::DefParasType dp;
|
||||
while (true)
|
||||
{
|
||||
if (isThis(TokenType::RightParen))
|
||||
{
|
||||
next();
|
||||
return Ast::FunctionParameters(pp, dp);
|
||||
}
|
||||
expect(TokenType::Identifier, FString(u8"Identifier or `)`")); // check current
|
||||
FString pname = currentToken().getValue();
|
||||
next(); // skip pname
|
||||
if (isThis(TokenType::Assign)) // =
|
||||
{
|
||||
next();
|
||||
dp.push_back({pname, {ValueType::Any.name, parseExpression(0, TokenType::Comma)}});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
}
|
||||
}
|
||||
else if (isThis(TokenType::Colon)) // :
|
||||
{
|
||||
next(); // skip `:`
|
||||
expect(TokenType::Identifier, FString(u8"Type name"));
|
||||
FString ti(currentToken().getValue());
|
||||
next(); // skip type name
|
||||
if (isThis(TokenType::Assign)) // =
|
||||
{
|
||||
next(); // skip `=`
|
||||
dp.push_back({pname, {ti, parseExpression(0, TokenType::Comma)}});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.push_back({pname, ti});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pp.push_back({pname, ValueType::Any.name});
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // only skip `,` when it's there
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ast::FunctionDef Parser::__parseFunctionDef(bool isPublic)
|
||||
{
|
||||
FString funcName = currentToken().getValue();
|
||||
next();
|
||||
expect(TokenType::LeftParen);
|
||||
Ast::FunctionParameters params = __parseFunctionParameters();
|
||||
FString retTiName = ValueType::Any.name;
|
||||
if (isThis(TokenType::RightArrow)) // ->
|
||||
{
|
||||
next(); // skip `->`
|
||||
expect(TokenType::Identifier);
|
||||
retTiName = currentToken().getValue();
|
||||
next(); // skip return type
|
||||
}
|
||||
expect(TokenType::LeftBrace);
|
||||
Ast::BlockStatement body = __parseBlockStatement();
|
||||
return makeAst<Ast::FunctionDefSt>(funcName, params, isPublic, retTiName, body);
|
||||
}
|
||||
Ast::StructDef Parser::__parseStructDef(bool isPublic)
|
||||
{
|
||||
// entry: current is struct name
|
||||
FString structName = currentToken().getValue();
|
||||
next();
|
||||
expect(TokenType::LeftBrace, u8"struct body");
|
||||
next();
|
||||
bool braceClosed = false;
|
||||
|
||||
/*
|
||||
public name
|
||||
public const name
|
||||
public final name
|
||||
|
||||
const name
|
||||
final name
|
||||
|
||||
name
|
||||
*/
|
||||
|
||||
auto __parseStructField = [this](bool isPublic) -> Ast::StructDefField {
|
||||
AccessModifier am = AccessModifier::Normal;
|
||||
FString fieldName;
|
||||
if (isThis(TokenType::Identifier))
|
||||
{
|
||||
fieldName = currentToken().getValue();
|
||||
next();
|
||||
am = (isPublic ? AccessModifier::Public : AccessModifier::Normal);
|
||||
}
|
||||
else if (isThis(TokenType::Final))
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"field name");
|
||||
fieldName = currentToken().getValue();
|
||||
am = (isPublic ? AccessModifier::PublicFinal : AccessModifier::Final);
|
||||
}
|
||||
else if (isThis(TokenType::Const))
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"field name");
|
||||
fieldName = currentToken().getValue();
|
||||
am = (isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(std::format("expect field name or field attribute")));
|
||||
}
|
||||
FString tiName = ValueType::Any.name;
|
||||
if (isThis(TokenType::Colon))
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"type name");
|
||||
tiName = currentToken().getValue();
|
||||
next();
|
||||
}
|
||||
Ast::Expression initExpr = nullptr;
|
||||
if (isThis(TokenType::Assign))
|
||||
{
|
||||
next();
|
||||
if (isEOF()) throwAddressableError<SyntaxError>(FStringView(u8"expect an expression"));
|
||||
initExpr = parseExpression(0);
|
||||
}
|
||||
expect(TokenType::Semicolon);
|
||||
next(); // consume `;`
|
||||
return Ast::StructDefField(am, fieldName, tiName, initExpr);
|
||||
};
|
||||
std::vector<Ast::Statement> stmts;
|
||||
std::vector<Ast::StructDefField> fields;
|
||||
|
||||
while (!isEOF())
|
||||
{
|
||||
if (isThis(TokenType::RightBrace))
|
||||
{
|
||||
braceClosed = true;
|
||||
next(); // consume `}`
|
||||
break;
|
||||
}
|
||||
if (isThis(TokenType::Identifier))
|
||||
{
|
||||
fields.push_back(__parseStructField(false));
|
||||
}
|
||||
else if (isThis(TokenType::Public))
|
||||
{
|
||||
if (isNext(TokenType::Const) or isNext(TokenType::Final))
|
||||
{
|
||||
next();
|
||||
fields.push_back(__parseStructField(true));
|
||||
}
|
||||
else if (isNext(TokenType::Function))
|
||||
{
|
||||
next(); // consume `public`
|
||||
next(); // consume `function`
|
||||
stmts.push_back(__parseFunctionDef(true));
|
||||
}
|
||||
else if (isNext(TokenType::Struct))
|
||||
{
|
||||
next(); // consume `public`
|
||||
next(); // consume `struct`
|
||||
stmts.push_back(__parseStructDef(true));
|
||||
}
|
||||
else if (isNext(TokenType::Identifier))
|
||||
{
|
||||
next(); // consume `public`
|
||||
fields.push_back(__parseStructField(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
|
||||
}
|
||||
}
|
||||
else if (isThis(TokenType::Function))
|
||||
{
|
||||
next();
|
||||
stmts.push_back(__parseFunctionDef(false));
|
||||
}
|
||||
else if (isThis(TokenType::Struct))
|
||||
{
|
||||
next(); // consume `struct`
|
||||
stmts.push_back(__parseStructDef(false));
|
||||
}
|
||||
else if (isThis(TokenType::Const) or isThis(TokenType::Final))
|
||||
{
|
||||
fields.push_back(__parseStructField(false));
|
||||
}
|
||||
else if (isThis(TokenType::Variable))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView("Variables are not allowed to be defined within a structure."));
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
|
||||
}
|
||||
}
|
||||
if (!braceClosed)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView("braces are not closed"));
|
||||
}
|
||||
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts));
|
||||
}
|
||||
Ast::Statement Parser::__parseStatement()
|
||||
{
|
||||
Ast::Statement stmt;
|
||||
if (isThis(TokenType::EndOfFile)) { return makeAst<Ast::EofStmt>(); }
|
||||
if (isThis(TokenType::Public))
|
||||
{
|
||||
// stmt = __parseVarDef();
|
||||
// expect(TokenType::Semicolon);
|
||||
// next();
|
||||
if (isNext(TokenType::Variable) || isNext(TokenType::Const))
|
||||
{
|
||||
next(); // consume `public`
|
||||
stmt = __parseVarDef(true);
|
||||
}
|
||||
else if (isNext(TokenType::Function))
|
||||
{
|
||||
next(); // consume `public`
|
||||
expectPeek(TokenType::Identifier);
|
||||
next();
|
||||
stmt = __parseFunctionDef(true);
|
||||
}
|
||||
else if (isNext(TokenType::Struct))
|
||||
{
|
||||
stmt = __parseStructDef(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Expected `var`, `const`, `function` or `struct` after `public`"));
|
||||
}
|
||||
}
|
||||
else if (isThis(TokenType::Variable) || isThis(TokenType::Const))
|
||||
{
|
||||
stmt = __parseVarDef(false);
|
||||
}
|
||||
else if (isThis(TokenType::Function))
|
||||
{
|
||||
expectPeek(TokenType::Identifier, u8"function name");
|
||||
next();
|
||||
stmt = __parseFunctionDef(false);
|
||||
}
|
||||
else if (isThis(TokenType::Struct))
|
||||
{
|
||||
expectPeek(TokenType::Identifier, u8"struct name");
|
||||
next();
|
||||
stmt = __parseStructDef(false);
|
||||
}
|
||||
else if (isThis(TokenType::Identifier) and isNext(TokenType::Assign))
|
||||
{
|
||||
FString varName = currentToken().getValue();
|
||||
next(); // consume identifier
|
||||
stmt = __parseVarAssign(varName);
|
||||
}
|
||||
else if (isThis(TokenType::If))
|
||||
{
|
||||
stmt = __parseIf();
|
||||
}
|
||||
else if (isThis(TokenType::Else))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"`else` without matching `if`"));
|
||||
}
|
||||
else if (isThis(TokenType::LeftBrace))
|
||||
{
|
||||
stmt = __parseBlockStatement();
|
||||
}
|
||||
else if (isThis(TokenType::While))
|
||||
{
|
||||
stmt = __parseWhile();
|
||||
}
|
||||
else if (isThis(TokenType::Return))
|
||||
{
|
||||
stmt = __parseReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
// expression statement
|
||||
Ast::Expression exp = parseExpression(0);
|
||||
expect(TokenType::Semicolon);
|
||||
next();
|
||||
stmt = makeAst<Ast::ExpressionStmtAst>(exp);
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
Ast::BlockStatement Parser::__parseBlockStatement()
|
||||
{
|
||||
// entry: current is `{`
|
||||
// stop: current is `}` next one
|
||||
next(); // consume `{`
|
||||
std::vector<Ast::Statement> stmts;
|
||||
while (true)
|
||||
{
|
||||
if (isThis(TokenType::RightBrace))
|
||||
{
|
||||
next();
|
||||
return makeAst<Ast::BlockStatementAst>(stmts);
|
||||
}
|
||||
stmts.push_back(__parseStatement());
|
||||
}
|
||||
}
|
||||
Ast::VarAssign Parser::__parseVarAssign(FString varName)
|
||||
{
|
||||
// entry: current is `=`
|
||||
next(); // consume `=`
|
||||
Ast::Expression exp = parseExpression(0);
|
||||
expect(TokenType::Semicolon);
|
||||
next(); // consume `;`
|
||||
return makeAst<Ast::VarAssignSt>(varName, exp);
|
||||
}
|
||||
|
||||
Ast::If Parser::__parseIf()
|
||||
{
|
||||
// entry: current is `if`
|
||||
next(); // consume `if`
|
||||
Ast::Expression condition;
|
||||
if (isThis( TokenType::LeftParen))
|
||||
{
|
||||
next(); // consume `(`
|
||||
condition = parseExpression(0, TokenType::RightParen);
|
||||
expect(TokenType::RightParen);
|
||||
next(); // consume `)`
|
||||
}
|
||||
else
|
||||
{
|
||||
condition = parseExpression(0);
|
||||
}
|
||||
// parenthesis is not required
|
||||
expect(TokenType::LeftBrace); // {
|
||||
Ast::BlockStatement body = __parseBlockStatement();
|
||||
std::vector<Ast::ElseIf> elifs;
|
||||
Ast::Else els = nullptr;
|
||||
while (isThis(TokenType::Else))
|
||||
{
|
||||
next(); // consume `else`
|
||||
if (isThis(TokenType::If))
|
||||
{
|
||||
// else if
|
||||
next(); // consume `if`
|
||||
Ast::Expression elifCondition = parseExpression(0);
|
||||
expect(TokenType::LeftBrace); // {
|
||||
Ast::BlockStatement elifBody = __parseBlockStatement();
|
||||
elifs.push_back(makeAst<Ast::ElseIfSt>(elifCondition, elifBody));
|
||||
}
|
||||
else
|
||||
{
|
||||
expect(TokenType::LeftBrace); // {
|
||||
Ast::BlockStatement elseBody = __parseBlockStatement();
|
||||
els = makeAst<Ast::ElseSt>(elseBody);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return makeAst<Ast::IfSt>(condition, body, elifs, els);
|
||||
}
|
||||
Ast::While Parser::__parseWhile()
|
||||
{
|
||||
// entry: current is `while`
|
||||
next(); // consume `while`
|
||||
Ast::Expression condition = parseExpression(0);
|
||||
expect(TokenType::LeftBrace); // {
|
||||
Ast::BlockStatement body = __parseBlockStatement();
|
||||
return makeAst<Ast::WhileSt>(condition, body);
|
||||
}
|
||||
Ast::Return Parser::__parseReturn()
|
||||
{
|
||||
// entry: current is `return`
|
||||
next(); // consume `return`
|
||||
Ast::Expression retValue = parseExpression(0);
|
||||
expect(TokenType::Semicolon);
|
||||
next(); // consume `;`
|
||||
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));
|
||||
}
|
||||
Ast::BinaryExpr Parser::__parseInfix(Ast::Expression lhs, Ast::Operator op, Precedence bp)
|
||||
{
|
||||
return makeAst<Ast::BinaryExprAst>(lhs, op, parseExpression(bp));
|
||||
}
|
||||
|
||||
Ast::ListExpr Parser::__parseListExpr()
|
||||
{
|
||||
// entry: current is `[`
|
||||
next(); // consume `[`
|
||||
std::vector<Ast::Expression> val;
|
||||
while (!isThis(TokenType::RightBracket))
|
||||
{
|
||||
val.push_back(parseExpression(0, TokenType::RightBracket, TokenType::Comma));
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // consume `,`
|
||||
}
|
||||
}
|
||||
expect(TokenType::RightBracket);
|
||||
next(); // consume `]`
|
||||
return makeAst<Ast::ListExprAst>(val);
|
||||
}
|
||||
|
||||
Ast::MapExpr Parser::__parseMapExpr()
|
||||
{
|
||||
// entry: current is `{`
|
||||
next(); // consume `{`
|
||||
std::map<FString, Ast::Expression> val;
|
||||
while (!isThis(TokenType::RightBrace))
|
||||
{
|
||||
expect(TokenType::Identifier, FString(u8"key (identifier)"));
|
||||
FString key = currentToken().getValue();
|
||||
if (val.contains(key)) throwAddressableError<SyntaxError>(FStringView(std::format(
|
||||
"Redefinition of immutable key {} in mapping literal",
|
||||
key.toBasicString())));
|
||||
next(); // consume key
|
||||
expect(TokenType::Colon);
|
||||
next(); // consume `:`
|
||||
val[key] = parseExpression(0, TokenType::RightBrace, TokenType::Comma);
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // consume `,`
|
||||
}
|
||||
}
|
||||
expect(TokenType::RightBrace);
|
||||
next(); // consume `}`
|
||||
return makeAst<Ast::MapExprAst>(val);
|
||||
}
|
||||
|
||||
Ast::InitExpr Parser::__parseInitExpr(FString structName)
|
||||
{
|
||||
// entry: current is `{`
|
||||
next(); // consume `{`
|
||||
std::vector<std::pair<FString, Ast::Expression>> args;
|
||||
/*
|
||||
3 ways of calling constructor
|
||||
.1 Person {"Fig", 1, "IDK"};
|
||||
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
|
||||
.3 Person {name, age, sex};
|
||||
*/
|
||||
uint8_t mode = 0; // 0=undetermined, 1=positional, 2=named, 3=shorthand
|
||||
|
||||
while (!isThis(TokenType::RightBrace))
|
||||
{
|
||||
if (mode == 0)
|
||||
{
|
||||
if (isThis(TokenType::Identifier) && isNext(TokenType::Colon))
|
||||
{
|
||||
mode = 2;
|
||||
}
|
||||
else if (isThis(TokenType::Identifier) && (isNext(TokenType::Comma) || isNext(TokenType::RightBrace)))
|
||||
{
|
||||
mode = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == 1)
|
||||
{
|
||||
// 1 Person {"Fig", 1, "IDK"};
|
||||
Ast::Expression expr = parseExpression(0);
|
||||
args.push_back({FString(), std::move(expr)});
|
||||
}
|
||||
else if (mode == 2)
|
||||
{
|
||||
// 2 Person {name: "Fig", age: 1, sex: "IDK"};
|
||||
expect(TokenType::Identifier);
|
||||
FString fieldName = currentToken().getValue();
|
||||
next(); // consume identifier
|
||||
expect(TokenType::Colon);
|
||||
next(); // consume colon
|
||||
Ast::Expression expr = parseExpression(0);
|
||||
args.push_back({fieldName, std::move(expr)});
|
||||
}
|
||||
else if (mode == 3)
|
||||
{
|
||||
// 3 Person {name, age, sex};
|
||||
expect(TokenType::Identifier);
|
||||
FString fieldName = currentToken().getValue();
|
||||
Ast::Expression expr = makeAst<Ast::VarExprAst>(fieldName);
|
||||
args.push_back({fieldName, std::move(expr)});
|
||||
next(); // consume identifier
|
||||
}
|
||||
|
||||
if (isThis(TokenType::Comma))
|
||||
{
|
||||
next(); // consume comma
|
||||
}
|
||||
else if (!isThis(TokenType::RightBrace))
|
||||
{
|
||||
throwAddressableError<SyntaxError>(u8"Expected comma or right brace");
|
||||
}
|
||||
}
|
||||
expect(TokenType::RightBrace);
|
||||
next(); // consume `}`
|
||||
return makeAst<Ast::InitExprAst>(structName, args);
|
||||
}
|
||||
Ast::Expression Parser::__parseTupleOrParenExpr()
|
||||
{
|
||||
next();
|
||||
|
||||
if (currentToken().getType() == TokenType::RightParen)
|
||||
{
|
||||
next(); // consume ')'
|
||||
return makeAst<Ast::TupleExprAst>();
|
||||
}
|
||||
|
||||
Ast::Expression firstExpr = parseExpression(0);
|
||||
|
||||
if (currentToken().getType() == TokenType::Comma)
|
||||
{
|
||||
std::vector<Ast::Expression> elements;
|
||||
elements.push_back(firstExpr);
|
||||
|
||||
while (currentToken().getType() == TokenType::Comma)
|
||||
{
|
||||
next(); // consume ','
|
||||
|
||||
if (currentToken().getType() == TokenType::RightParen)
|
||||
break;
|
||||
|
||||
elements.push_back(parseExpression(0));
|
||||
}
|
||||
|
||||
expect(TokenType::RightParen);
|
||||
next(); // consume ')'
|
||||
|
||||
return makeAst<Ast::TupleExprAst>(std::move(elements));
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::RightParen)
|
||||
{
|
||||
next(); // consume ')'
|
||||
return firstExpr;
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Expect ')' or ',' after expression in parentheses"));
|
||||
}
|
||||
return nullptr; // to suppress compiler warning
|
||||
}
|
||||
Ast::Expression Parser::parseExpression(Precedence bp, TokenType stop, TokenType stop2)
|
||||
{
|
||||
Ast::Expression lhs;
|
||||
Ast::Operator op;
|
||||
|
||||
Token tok = currentToken();
|
||||
if (tok == EOFTok)
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected end of expression"));
|
||||
if (tok.getType() == stop || tok.getType() == stop2)
|
||||
{
|
||||
if (lhs == nullptr) throwAddressableError<SyntaxError>(FStringView(u8"Expected expression"));
|
||||
return lhs;
|
||||
}
|
||||
if (tok.getType() == TokenType::LeftBracket)
|
||||
{
|
||||
lhs = __parseListExpr(); // auto consume
|
||||
}
|
||||
else if (tok.getType() == TokenType::LeftParen)
|
||||
{
|
||||
lhs = __parseTupleOrParenExpr(); // auto consume
|
||||
}
|
||||
else if (tok.getType() == TokenType::LeftBrace)
|
||||
{
|
||||
lhs = __parseMapExpr(); // auto consume
|
||||
}
|
||||
else if (tok.isLiteral())
|
||||
{
|
||||
lhs = __parseValueExpr();
|
||||
next();
|
||||
}
|
||||
else if (tok.isIdentifier())
|
||||
{
|
||||
FString id = tok.getValue();
|
||||
next();
|
||||
if (currentToken().getType() == TokenType::LeftParen)
|
||||
{
|
||||
lhs = __parseFunctionCall(id); // foo(...)
|
||||
}
|
||||
else if (currentToken().getType() == TokenType::LeftBrace)
|
||||
{
|
||||
lhs = __parseInitExpr(id); // a_struct{init...}
|
||||
}
|
||||
else
|
||||
{
|
||||
lhs = __parseVarExpr(id);
|
||||
}
|
||||
}
|
||||
else if (isTokenOp(tok) && isOpUnary((op = Ast::TokenToOp.at(tok.getType()))))
|
||||
{
|
||||
// prefix
|
||||
next();
|
||||
lhs = __parsePrefix(op, getRightBindingPower(op));
|
||||
}
|
||||
else
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected token in expression"));
|
||||
}
|
||||
|
||||
// infix / (postfix) ?
|
||||
while (true)
|
||||
{
|
||||
tok = currentToken();
|
||||
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
|
||||
|
||||
// ternary
|
||||
if (tok.getType() == TokenType::Question)
|
||||
{
|
||||
next(); // consume ?
|
||||
Ast::Expression trueExpr = parseExpression(0, TokenType::Colon);
|
||||
expect(TokenType::Colon);
|
||||
next(); // consume :
|
||||
Ast::Expression falseExpr = parseExpression(0, TokenType::Semicolon, stop2);
|
||||
lhs = makeAst<Ast::TernaryExprAst>(lhs, trueExpr, falseExpr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isTokenOp(tok)) break;
|
||||
|
||||
op = Ast::TokenToOp.at(tok.getType());
|
||||
Precedence lbp = getLeftBindingPower(op);
|
||||
if (bp >= lbp) break;
|
||||
|
||||
next(); // consume op
|
||||
lhs = __parseInfix(lhs, op, getRightBindingPower(op));
|
||||
}
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
std::vector<Ast::AstBase> Parser::parseAll()
|
||||
{
|
||||
output.clear();
|
||||
Token tok = currentToken();
|
||||
if (tok == EOFTok)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
// TODO: Package/Module Import Support
|
||||
while (!isEOF())
|
||||
{
|
||||
pushNode(__parseStatement());
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace Fig
|
||||
39
src/value.cpp
Normal file
39
src/value.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <value.hpp>
|
||||
|
||||
// #include <iostream>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
std::map<FString, size_t> TypeInfo::typeMap = {};
|
||||
|
||||
TypeInfo::TypeInfo() : // only allow use in evaluate time !! <---- dynamic type system requirement
|
||||
id(1), name(FString(u8"Any")) {}
|
||||
TypeInfo::TypeInfo(FString _name, bool reg)
|
||||
{
|
||||
static size_t id_count = 0;
|
||||
name = std::move(_name);
|
||||
// std::cerr << "TypeInfo constructor called for type name: " << name.toBasicString() << "\n";
|
||||
if (reg)
|
||||
{
|
||||
typeMap[name] = ++id_count;
|
||||
id = id_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
id = typeMap.at(name); // may throw
|
||||
}
|
||||
}
|
||||
|
||||
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
||||
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
|
||||
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
|
||||
const TypeInfo ValueType::String(FString(u8"String"), true); // id: 4
|
||||
const TypeInfo ValueType::Bool(FString(u8"Bool"), true); // id: 5
|
||||
const TypeInfo ValueType::Double(FString(u8"Double"), true); // id: 6
|
||||
const TypeInfo ValueType::Function(FString(u8"Function"), true); // id: 7
|
||||
const TypeInfo ValueType::StructType(FString(u8"StructType"), true); // id: 8
|
||||
const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9
|
||||
const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10
|
||||
const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11
|
||||
const TypeInfo ValueType::Tuple(FString(u8"Tuple"), true); // id: 12
|
||||
} // namespace Fig
|
||||
9
src/waring.cpp
Normal file
9
src/waring.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <warning.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
const std::unordered_map<std::size_t, FString> Warning::standardWarnings = {
|
||||
{1, FString(u8"Identifier is too similar to a keyword or a primitive type")},
|
||||
{2, FString(u8"The identifier is too abstract")}
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user