This commit is contained in:
2025-12-19 20:38:40 +08:00
commit 73c828d99b
83 changed files with 13068 additions and 0 deletions

488
src/evaluator.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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")}
};
};