From 9f24392034e32f83c18dc3ddedd4d0645e56e2f4 Mon Sep 17 00:00:00 2001 From: PuqiAR Date: Tue, 30 Dec 2025 17:42:41 +0800 Subject: [PATCH] =?UTF-8?q?[VER]=200.3.4-alpha=20[FEAT]=20=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=B3=BB=E7=BB=9F,=20try/catch/finally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 8 +- fig-vscode/package.json | 2 +- fig-vscode/syntaxes/fig.tmLanguage.json | 4 +- src/Ast/Statements/ErrorFlow.hpp | 64 ++ src/Ast/ast.hpp | 3 +- src/Ast/astBase.hpp | 6 + src/Evaluator/evaluator.cpp | 891 +++++++++++++----------- src/Evaluator/evaluator.hpp | 8 +- src/Lexer/lexer.cpp | 5 +- src/Module/builtins.hpp | 219 ++++-- src/Parser/parser.cpp | 93 +++ src/Parser/parser.hpp | 20 +- src/Token/token.hpp | 4 + 13 files changed, 857 insertions(+), 470 deletions(-) create mode 100644 src/Ast/Statements/ErrorFlow.hpp diff --git a/.clang-format b/.clang-format index 901fe52..3d55335 100644 --- a/.clang-format +++ b/.clang-format @@ -52,10 +52,10 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true # false表示函数实参要么都在同一行,要么都各自一行 -BinPackArguments: true +BinPackArguments: false # false表示所有形参要么都在同一行,要么都各自一行 -BinPackParameters: true +BinPackParameters: false # 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效 BraceWrapping: @@ -108,12 +108,12 @@ BreakConstructorInitializers: AfterColon BreakStringLiterals: false # 每行字符的限制,0表示没有限制 -ColumnLimit: 0 +ColumnLimit: 80 CompactNamespaces: true # 构造函数的初始化列表要么都在同一行,要么都各自一行 -ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true # 构造函数的初始化列表的缩进宽度 ConstructorInitializerIndentWidth: 4 diff --git a/fig-vscode/package.json b/fig-vscode/package.json index abdf9df..ee53153 100644 --- a/fig-vscode/package.json +++ b/fig-vscode/package.json @@ -2,7 +2,7 @@ "name": "fig-vscode", "displayName": "Fig Language", "description": "VSCode extension for Fig language with syntax highlighting", - "version": "0.0.1", + "version": "0.0.2", "publisher": "PuqiAR", "engines": { "vscode": "^1.90.0" diff --git a/fig-vscode/syntaxes/fig.tmLanguage.json b/fig-vscode/syntaxes/fig.tmLanguage.json index 4265174..011a4e5 100644 --- a/fig-vscode/syntaxes/fig.tmLanguage.json +++ b/fig-vscode/syntaxes/fig.tmLanguage.json @@ -57,7 +57,7 @@ "patterns": [ { "name": "keyword.control.fig", - "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|struct|interface|implement|public|return|break|continue)\\b" + "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b" }, { "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" } ] @@ -78,7 +78,7 @@ }, { "name": "keyword.operator.comparison.fig", - "match": "(==|!=|<=|>=|<|>)" + "match": "(==|!=|<=|>=|<|>|is)" }, { "name": "punctuation.separator.fig", diff --git a/src/Ast/Statements/ErrorFlow.hpp b/src/Ast/Statements/ErrorFlow.hpp new file mode 100644 index 0000000..cc7cadc --- /dev/null +++ b/src/Ast/Statements/ErrorFlow.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include + +namespace Fig::Ast +{ + class ThrowSt final : public StatementAst + { + public: + Expression value; + + ThrowSt() + { + type = AstType::ThrowSt; + } + + ThrowSt(Expression _value) : + value(std::move(_value)) + { + type = AstType::ThrowSt; + } + }; + using Throw = std::shared_ptr; + + struct Catch + { + FString errVarName; + bool hasType = false; + FString errVarType; + BlockStatement body; + + Catch() {} + Catch(FString _errVarName, FString _errVarType, BlockStatement _body) : + errVarName(std::move(_errVarName)), errVarType(std::move(_errVarType)), body(std::move(_body)) + { + hasType = true; + } + Catch(FString _errVarName, BlockStatement _body) : + errVarName(std::move(_errVarName)), body(std::move(_body)) + { + hasType = false; + } + }; + + class TrySt final : public StatementAst + { + public: + BlockStatement body; + std::vector catches; + BlockStatement finallyBlock = nullptr; + + TrySt() + { + type = AstType::TrySt; + } + TrySt(BlockStatement _body, std::vector _catches, BlockStatement _finallyBlock) : + body(std::move(_body)), catches(std::move(_catches)), finallyBlock(std::move(_finallyBlock)) + { + type = AstType::TrySt; + } + }; + + using Try = std::shared_ptr; +} // namespace Fig::Ast \ No newline at end of file diff --git a/src/Ast/ast.hpp b/src/Ast/ast.hpp index c2b7741..0000da7 100644 --- a/src/Ast/ast.hpp +++ b/src/Ast/ast.hpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include @@ -23,6 +25,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/Ast/astBase.hpp b/src/Ast/astBase.hpp index 05e8f51..76aceb1 100644 --- a/src/Ast/astBase.hpp +++ b/src/Ast/astBase.hpp @@ -60,6 +60,9 @@ namespace Fig::Ast PackageSt, ImportSt, + + TrySt, + ThrowSt, }; // static const std::unordered_map astTypeToString{ @@ -210,6 +213,7 @@ namespace Fig::Ast LessEqual, // <= Greater, // > GreaterEqual, // >= + Is, // a is b // 三目 TernaryCond, @@ -254,6 +258,7 @@ namespace Fig::Ast Operator::LessEqual, Operator::Greater, Operator::GreaterEqual, + Operator::Is, Operator::BitAnd, Operator::BitOr, @@ -297,6 +302,7 @@ namespace Fig::Ast {TokenType::LessEqual, Operator::LessEqual}, {TokenType::Greater, Operator::Greater}, {TokenType::GreaterEqual, Operator::GreaterEqual}, + {TokenType::Is, Operator::Is}, // 三目 {TokenType::Question, Operator::TernaryCond}, diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index 2ad7b10..109c9e1 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -1,3 +1,4 @@ +#include "Ast/Statements/ErrorFlow.hpp" #include "Value/VariableSlot.hpp" #include "Value/value.hpp" #include @@ -24,9 +25,12 @@ namespace Fig { - bool Evaluator::isInterfaceSignatureMatch(const Ast::ImplementMethod &implMethod, const Ast::InterfaceMethod &ifMethod) + bool + Evaluator::isInterfaceSignatureMatch(const Ast::ImplementMethod &implMethod, + const Ast::InterfaceMethod &ifMethod) { - return implMethod.name == ifMethod.name && implMethod.paras == ifMethod.paras; + return implMethod.name == ifMethod.name + && implMethod.paras == ifMethod.paras; } LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) @@ -48,9 +52,7 @@ namespace Fig const Module &mod = baseVal->as(); if (mod.ctx->contains(member) && mod.ctx->isVariablePublic(member)) { - return LvObject( - mod.ctx->get(member), - ctx); + return LvObject(mod.ctx->get(member), ctx); } else { @@ -65,50 +67,59 @@ namespace Fig } if (baseVal->hasMemberFunction(member)) { - return LvObject(std::make_shared( - member, - std::make_shared( - Function( - baseVal->getMemberFunction(member), - baseVal->getMemberFunctionParaCount(member))), - ValueType::Function, - AccessModifier::PublicConst), - ctx); // fake l-value + return LvObject( + std::make_shared( + member, + std::make_shared( + Function(baseVal->getMemberFunction(member), + baseVal->getMemberFunctionParaCount(member))), + ValueType::Function, + AccessModifier::PublicConst), + ctx); // fake l-value } - if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found + if (baseVal->getTypeInfo() + != ValueType::StructInstance) // and not member function found { throw EvaluatorError( u8"NoAttributeError", - std::format( - "`{}` has not attribute '{}'", - baseVal->toString().toBasicString(), - member.toBasicString()), + std::format("`{}` has not attribute '{}'", + baseVal->toString().toBasicString(), + member.toBasicString()), me->base); } const StructInstance &si = baseVal->as(); if (ctx->hasMethodImplemented(si.parentType, member)) { + auto &fn = ctx->getImplementedMethod(si.parentType, member); + Function boundFn( + fn.paras, + fn.retType, + fn.body, + si.localContext // create a new function and set closure context + // to struct instance context + ); return LvObject(std::make_shared( member, - std::make_shared( - ctx->getImplementedMethod(si.parentType, member)), + std::make_shared(boundFn), ValueType::Function, AccessModifier::PublicConst), ctx); } - else if (si.localContext->containsInThisScope(member) && si.localContext->isVariablePublic(member)) + else if (si.localContext->containsInThisScope(member) + && si.localContext->isVariablePublic(member)) { return LvObject(si.localContext->get(member), ctx); } else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) { - return LvObject(std::make_shared( - member, - std::make_shared( - ctx->getDefaultImplementedMethod(si.parentType, member)), - ValueType::Function, - AccessModifier::PublicConst), - ctx); + return LvObject( + std::make_shared( + member, + std::make_shared(ctx->getDefaultImplementedMethod( + si.parentType, member)), + ValueType::Function, + AccessModifier::PublicConst), + ctx); } else { @@ -134,9 +145,8 @@ namespace Fig { throw EvaluatorError( u8"TypeError", - std::format( - "Type `List` indices must be `Int`, got '{}'", - prettyType(index).toBasicString()), + std::format("Type `List` indices must be `Int`, got '{}'", + prettyType(index).toBasicString()), ie->index); } List &list = base.get()->as(); @@ -145,25 +155,17 @@ namespace Fig { throw EvaluatorError( u8"IndexOutOfRangeError", - std::format( - "Index {} out of list `{}` range", - indexVal, - base.get()->toString().toBasicString()), + std::format("Index {} out of list `{}` range", + indexVal, + base.get()->toString().toBasicString()), ie->index); } return LvObject( - base.get(), - indexVal, - LvObject::Kind::ListElement, - ctx); + base.get(), indexVal, LvObject::Kind::ListElement, ctx); } else if (type == ValueType::Map) { - return LvObject( - base.get(), - index, - LvObject::Kind::MapElement, - ctx); + return LvObject(base.get(), index, LvObject::Kind::MapElement, ctx); } else if (type == ValueType::String) { @@ -171,9 +173,8 @@ namespace Fig { throw EvaluatorError( u8"TypeError", - std::format( - "Type `String` indices must be `Int`, got '{}'", - prettyType(index).toBasicString()), + std::format("Type `String` indices must be `Int`, got '{}'", + prettyType(index).toBasicString()), ie->index); } FString &string = base.get()->as(); @@ -182,25 +183,20 @@ namespace Fig { throw EvaluatorError( u8"IndexOutOfRangeError", - std::format( - "Index {} out of string `{}` range", - indexVal, - base.get()->toString().toBasicString()), + std::format("Index {} out of string `{}` range", + indexVal, + base.get()->toString().toBasicString()), ie->index); } return LvObject( - base.get(), - indexVal, - LvObject::Kind::StringElement, - ctx); + base.get(), indexVal, LvObject::Kind::StringElement, ctx); } else { throw EvaluatorError( u8"NoSubscriptableError", - std::format( - "`{}` object is not subscriptable", - base.declaredType().toString().toBasicString()), + std::format("`{}` object is not subscriptable", + base.declaredType().toString().toBasicString()), ie->base); } } @@ -211,22 +207,29 @@ namespace Fig switch (exp->getType()) { case AstType::VarExpr: { - Ast::VarExpr var = std::dynamic_pointer_cast(exp); + Ast::VarExpr var = + std::dynamic_pointer_cast(exp); assert(var != nullptr); return evalVarExpr(var, ctx); } case AstType::MemberExpr: { - Ast::MemberExpr me = std::dynamic_pointer_cast(exp); + Ast::MemberExpr me = + std::dynamic_pointer_cast(exp); assert(me != nullptr); return evalMemberExpr(me, ctx); } case AstType::IndexExpr: { - Ast::IndexExpr ie = std::dynamic_pointer_cast(exp); + Ast::IndexExpr ie = + std::dynamic_pointer_cast(exp); assert(ie != nullptr); return evalIndexExpr(ie, ctx); } default: { - throw EvaluatorError(u8"TypeError", std::format("Expression '{}' doesn't refer to a lvalue", exp->typeName().toBasicString()), exp); + throw EvaluatorError( + u8"TypeError", + std::format("Expression '{}' doesn't refer to a lvalue", + exp->typeName().toBasicString()), + exp); } } } @@ -235,8 +238,7 @@ namespace Fig { using Ast::Operator; Operator op = bin->op; - Ast::Expression lexp = bin->lexp, - rexp = bin->rexp; + Ast::Expression lexp = bin->lexp, rexp = bin->rexp; switch (op) { case Operator::Add: { @@ -309,6 +311,30 @@ namespace Fig ObjectPtr rhs = eval(rexp, ctx); return std::make_shared(*lhs >= *rhs); } + case Operator::Is: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + const StructInstance &si = lhs->as(); + const StructType &st = rhs->as(); + return std::make_shared(si.parentType == st.type); + } + if (lhs->is() && rhs->is()) + { + const StructInstance &si = lhs->as(); + const InterfaceType &it = rhs->as(); + return std::make_shared( + implements(si.parentType, it.type, ctx)); + } + throw EvaluatorError( + u8"TypeError", + std::format( + "Operator `is` requires an struct instance on left-hand side, got '{}'", + lhs->getTypeInfo().toString().toBasicString()), + bin->lexp); + } case Operator::BitAnd: { ObjectPtr lhs = eval(lexp, ctx); @@ -345,36 +371,31 @@ namespace Fig case Operator::PlusAssign: { LvObject lv = evalLv(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared( - *(lv.get()) + *rhs)); + lv.set(std::make_shared(*(lv.get()) + *rhs)); return rhs; } case Operator::MinusAssign: { LvObject lv = evalLv(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared( - *(lv.get()) - *rhs)); + lv.set(std::make_shared(*(lv.get()) - *rhs)); return rhs; } case Operator::AsteriskAssign: { LvObject lv = evalLv(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared( - *(lv.get()) * (*rhs))); + lv.set(std::make_shared(*(lv.get()) * (*rhs))); return rhs; } case Operator::SlashAssign: { LvObject lv = evalLv(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared( - *(lv.get()) / *rhs)); + lv.set(std::make_shared(*(lv.get()) / *rhs)); return rhs; } case Operator::PercentAssign: { LvObject lv = evalLv(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); - lv.set(std::make_shared( - *(lv.get()) / *rhs)); + lv.set(std::make_shared(*(lv.get()) / *rhs)); return rhs; } // case Operator::CaretAssign: { @@ -384,7 +405,11 @@ namespace Fig // *(lv.get()) ^ *rhs)); // } default: - throw EvaluatorError(u8"UnsupportedOp", std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)), bin); + throw EvaluatorError( + u8"UnsupportedOp", + std::format("Unsupport operator '{}' for binary", + magic_enum::enum_name(op)), + bin); } } RvObject Evaluator::evalUnary(Ast::UnaryExpr un, ContextPtr ctx) @@ -429,7 +454,10 @@ namespace Fig return (cond ? eval(te->valueT, ctx) : eval(te->valueF, ctx)); } - RvObject Evaluator::evalFunctionCall(const Function &fn, const Ast::FunctionArguments &fnArgs, const FString &fnName, ContextPtr ctx) + RvObject Evaluator::evalFunctionCall(const Function &fn, + const Ast::FunctionArguments &fnArgs, + const FString &fnName, + ContextPtr ctx) { const Function &fnStruct = fn; Ast::FunctionCallArgs evaluatedArgs; @@ -439,14 +467,17 @@ namespace Fig { evaluatedArgs.argv.push_back(eval(argExpr, ctx)); } - if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength()) + if (fnStruct.builtinParamCount != -1 + && fnStruct.builtinParamCount != evaluatedArgs.getLength()) { - throw EvaluatorError(u8"BuiltinArgumentMismatchError", - std::format("Builtin function '{}' expects {} arguments, but {} were provided", - fnName.toBasicString(), - fnStruct.builtinParamCount, - evaluatedArgs.getLength()), - fnArgs.argv.back()); + throw EvaluatorError( + u8"BuiltinArgumentMismatchError", + std::format( + "Builtin function '{}' expects {} arguments, but {} were provided", + fnName.toBasicString(), + fnStruct.builtinParamCount, + evaluatedArgs.getLength()), + fnArgs.argv.back()); } return fnStruct.builtin(evaluatedArgs.argv); } @@ -455,8 +486,9 @@ namespace Fig Ast::FunctionParameters fnParas = fnStruct.paras; // create new context for function call - auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), - fnStruct.closureContext); + auto newContext = std::make_shared( + FString(std::format("", fnName.toBasicString())), + fnStruct.closureContext); if (fnParas.variadic) goto VariadicFilling; @@ -464,33 +496,37 @@ namespace Fig goto NormalFilling; NormalFilling: { - if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) + if (fnArgs.getLength() < fnParas.posParas.size() + || fnArgs.getLength() > fnParas.size()) { - throw RuntimeError(FString( - std::format( - "Function '{}' expects {} to {} arguments, but {} were provided", - fnName.toBasicString(), - fnParas.posParas.size(), - fnParas.size(), - fnArgs.getLength()))); + throw RuntimeError(FString(std::format( + "Function '{}' expects {} to {} arguments, but {} were provided", + fnName.toBasicString(), + fnParas.posParas.size(), + fnParas.size(), + fnArgs.getLength()))); } // 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 + TypeInfo expectedType( + fnParas.posParas[i] + .second); // look up type info, if exists a type with the + // name, use it, else throw ObjectPtr argVal = eval(fnArgs.argv[i], ctx); TypeInfo actualType = argVal->getTypeInfo(); if (!isTypeMatch(expectedType, argVal, ctx)) { throw EvaluatorError( u8"ArgumentTypeMismatchError", - std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", - fnName.toBasicString(), - fnParas.posParas[i].first.toBasicString(), - expectedType.toString().toBasicString(), - actualType.toString().toBasicString()), + std::format( + "In function '{}', argument '{}' expects type '{}', but got type '{}'", + fnName.toBasicString(), + fnParas.posParas[i].first.toBasicString(), + expectedType.toString().toBasicString(), + actualType.toString().toBasicString()), fnArgs.argv[i]); } evaluatedArgs.argv.push_back(argVal); @@ -501,16 +537,18 @@ namespace Fig size_t defParamIndex = i - fnParas.posParas.size(); TypeInfo expectedType(fnParas.defParas[defParamIndex].second.first); - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + ObjectPtr defaultVal = + eval(fnParas.defParas[defParamIndex].second.second, ctx); if (!isTypeMatch(expectedType, defaultVal, ctx)) { throw EvaluatorError( u8"DefaultParameterTypeError", - std::format("In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", - fnName.toBasicString(), - fnParas.defParas[defParamIndex].first.toBasicString(), - prettyType(defaultVal).toBasicString(), - expectedType.toString().toBasicString()), + std::format( + "In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", + fnName.toBasicString(), + fnParas.defParas[defParamIndex].first.toBasicString(), + prettyType(defaultVal).toBasicString(), + expectedType.toString().toBasicString()), fnArgs.argv[i]); } @@ -520,10 +558,12 @@ namespace Fig { throw EvaluatorError( u8"ArgumentTypeMismatchError", - std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", - fnName.toBasicString(), - fnParas.defParas[defParamIndex].first.toBasicString(), - expectedType.toString().toBasicString(), actualType.toString().toBasicString()), + std::format( + "In function '{}', argument '{}' expects type '{}', but got type '{}'", + fnName.toBasicString(), + fnParas.defParas[defParamIndex].first.toBasicString(), + expectedType.toString().toBasicString(), + actualType.toString().toBasicString()), fnArgs.argv[i]); } evaluatedArgs.argv.push_back(argVal); @@ -532,7 +572,8 @@ namespace Fig for (; i < fnParas.size(); i++) { size_t defParamIndex = i - fnParas.posParas.size(); - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + ObjectPtr defaultVal = + eval(fnParas.defParas[defParamIndex].second.second, ctx); evaluatedArgs.argv.push_back(defaultVal); } @@ -550,7 +591,8 @@ namespace Fig { size_t defParamIndex = j - fnParas.posParas.size(); paramName = fnParas.defParas[defParamIndex].first; - paramType = TypeInfo(fnParas.defParas[defParamIndex].second.first); + paramType = + TypeInfo(fnParas.defParas[defParamIndex].second.first); } AccessModifier argAm = AccessModifier::Const; newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); @@ -564,12 +606,10 @@ namespace Fig { list.push_back(eval(exp, ctx)); // eval arguments in current scope } - newContext->def( - fnParas.variadicPara, - ValueType::List, - AccessModifier::Const, - std::make_shared( - list)); + newContext->def(fnParas.variadicPara, + ValueType::List, + AccessModifier::Const, + std::make_shared(list)); goto ExecuteBody; } @@ -579,7 +619,7 @@ namespace Fig for (const auto &stmt : fnStruct.body->stmts) { StatementResult sr = evalStatement(stmt, newContext); - if (sr.shouldReturn()) + if (!sr.isNormal()) { retVal = sr.result; break; @@ -589,10 +629,11 @@ namespace Fig { throw EvaluatorError( u8"ReturnTypeMismatchError", - std::format("Function '{}' expects return type '{}', but got type '{}'", - fnName.toBasicString(), - fnStruct.retType.toString().toBasicString(), - prettyType(retVal).toBasicString()), + std::format( + "Function '{}' expects return type '{}', but got type '{}'", + fnName.toBasicString(), + fnStruct.retType.toString().toBasicString(), + prettyType(retVal).toBasicString()), fnStruct.body); } return retVal; @@ -631,11 +672,11 @@ namespace Fig return evalTernary(te, ctx); } case AstType::MemberExpr: - case AstType::IndexExpr: - return evalLv(exp, ctx).get(); + case AstType::IndexExpr: return evalLv(exp, ctx).get(); case AstType::FunctionCall: { - auto fnCall = std::dynamic_pointer_cast(exp); + auto fnCall = + std::dynamic_pointer_cast(exp); assert(fnCall != nullptr); Ast::Expression callee = fnCall->callee; @@ -651,18 +692,21 @@ namespace Fig const Function &fn = fnObj->as(); size_t fnId = fn.id; // const auto &fnNameOpt = ctx->getFunctionName(fnId); - // const FString &fnName = (fnNameOpt ? *fnNameOpt : u8""); + // const FString &fnName = (fnNameOpt ? *fnNameOpt : + // u8""); auto fnNameOpt = ctx->getFunctionName(fnId); if (!fnNameOpt && fn.closureContext) fnNameOpt = fn.closureContext->getFunctionName(fnId); - const FString &fnName = (fnNameOpt ? *fnNameOpt : u8""); + const FString &fnName = + (fnNameOpt ? *fnNameOpt : u8""); return evalFunctionCall(fn, fnCall->arg, fnName, ctx); } case AstType::FunctionLiteralExpr: { - auto fnLiteral = std::dynamic_pointer_cast(exp); + auto fnLiteral = + std::dynamic_pointer_cast(exp); assert(fnLiteral != nullptr); Ast::BlockStatement body = nullptr; @@ -685,10 +729,7 @@ namespace Fig assert(body != nullptr); } Function fn( - fnLiteral->paras, - ValueType::Any, - body, - ctx + fnLiteral->paras, ValueType::Any, body, ctx /* pass the ctx(fnLiteral eval context) as closure context */ @@ -696,14 +737,14 @@ namespace Fig return std::make_shared(std::move(fn)); } case AstType::InitExpr: { - auto initExpr = std::dynamic_pointer_cast(exp); + auto initExpr = + std::dynamic_pointer_cast(exp); if (!ctx->contains(initExpr->structName)) { throw EvaluatorError( u8"StructNotFoundError", - std::format( - "Structure type '{}' not found", - initExpr->structName.toBasicString()), + std::format("Structure type '{}' not found", + initExpr->structName.toBasicString()), initExpr); } ObjectPtr structTypeVal = ctx->get(initExpr->structName)->value; @@ -711,13 +752,13 @@ namespace Fig { throw EvaluatorError( u8"NotAStructTypeError", - std::format( - "'{}' is not a structure type", - initExpr->structName.toBasicString()), + std::format("'{}' is not a structure type", + initExpr->structName.toBasicString()), initExpr); } const StructType &structT = structTypeVal->as(); - ContextPtr defContext = structT.defContext; // definition context + ContextPtr defContext = + structT.defContext; // definition context // check init args size_t minArgs = 0; @@ -748,13 +789,14 @@ namespace Fig evaluatedArgs.push_back({argName, eval(argExpr, ctx)}); } ContextPtr instanceCtx = std::make_shared( - FString(std::format("", initExpr->structName.toBasicString())), + FString(std::format("", + initExpr->structName.toBasicString())), ctx); /* 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}; + .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be + unordered .3 Person {name, age, sex}; */ { using enum Ast::InitExprAst::InitMode; @@ -767,10 +809,13 @@ namespace Fig const TypeInfo &expectedType = field.type; if (i >= evaluatedArgs.size()) { - // we've checked argument count before, so here must be a default value + // we've checked argument count before, so here + // must be a default value // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue, ctx); // it can't be null here + ObjectPtr defaultVal = + eval(field.defaultValue, + ctx); // it can't be null here // type check if (!isTypeMatch(expectedType, defaultVal, ctx)) @@ -779,14 +824,20 @@ namespace Fig u8"StructFieldTypeMismatchError", std::format( "In structure '{}', field '{}' expects type '{}', but got type '{}'", - initExpr->structName.toBasicString(), + initExpr->structName + .toBasicString(), fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), + expectedType.toString() + .toBasicString(), + prettyType(defaultVal) + .toBasicString()), initExpr); } - instanceCtx->def(fieldName, expectedType, field.am, defaultVal); + instanceCtx->def(fieldName, + expectedType, + field.am, + defaultVal); continue; } @@ -795,14 +846,16 @@ namespace Fig { throw EvaluatorError( u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - initExpr->structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(argVal).toBasicString()), + std::format( + "In structure '{}', field '{}' expects type '{}', but got type '{}'", + initExpr->structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(argVal).toBasicString()), initExpr); } - instanceCtx->def(fieldName, expectedType, field.am, argVal); + instanceCtx->def( + fieldName, expectedType, field.am, argVal); } } else @@ -811,7 +864,9 @@ namespace Fig for (size_t i = 0; i < maxArgs; ++i) { const Field &field = structT.fields[i]; - const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name); + const FString &fieldName = + (field.name.empty() ? evaluatedArgs[i].first : + field.name); if (instanceCtx->containsInThisScope(fieldName)) { throw EvaluatorError( @@ -824,8 +879,11 @@ namespace Fig } if (i + 1 > got) { - // use default value // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue, defContext); // it can't be null here + // use default value // + // evaluate default value in definition context + ObjectPtr defaultVal = + eval(field.defaultValue, + defContext); // it can't be null here // type check const TypeInfo &expectedType = field.type; @@ -835,14 +893,20 @@ namespace Fig u8"StructFieldTypeMismatchError", std::format( "In structure '{}', field '{}' expects type '{}', but got type '{}'", - initExpr->structName.toBasicString(), + initExpr->structName + .toBasicString(), fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), + expectedType.toString() + .toBasicString(), + prettyType(defaultVal) + .toBasicString()), initExpr); } - instanceCtx->def(fieldName, field.type, field.am, defaultVal); + instanceCtx->def(fieldName, + field.type, + field.am, + defaultVal); continue; } const ObjectPtr &argVal = evaluatedArgs[i].second; @@ -858,18 +922,26 @@ namespace Fig prettyType(argVal).toBasicString()), initExpr); } - instanceCtx->def(fieldName, field.type, field.am, argVal); + instanceCtx->def( + fieldName, field.type, field.am, argVal); } } } instanceCtx->merge(*structT.defContext); for (auto &[id, fn] : instanceCtx->getFunctions()) { - instanceCtx->_update(*instanceCtx->getFunctionName(id), std::make_shared( - Function(fn.paras, fn.retType, fn.body, instanceCtx) // change its closureContext to struct instance's context - )); + instanceCtx->_update( + *instanceCtx->getFunctionName(id), + std::make_shared(Function( + fn.paras, + fn.retType, + fn.body, + instanceCtx) // change its closureContext to struct + // instance's context + )); } - return std::make_shared(StructInstance(structT.type, instanceCtx)); + return std::make_shared( + StructInstance(structT.type, instanceCtx)); } case AstType::ListExpr: { @@ -896,24 +968,22 @@ namespace Fig return std::make_shared(std::move(map)); } - default: - assert(false); + default: assert(false); } } - StatementResult Evaluator::evalBlockStatement(Ast::BlockStatement block, ContextPtr ctx) + StatementResult Evaluator::evalBlockStatement(Ast::BlockStatement block, + ContextPtr ctx) { StatementResult sr = StatementResult::normal(); for (const Ast::Statement &stmt : block->stmts) { sr = evalStatement(stmt, ctx); - if (!sr.isNormal()) - { - return sr; - } + if (!sr.isNormal()) { return sr; } } return sr; } - StatementResult Evaluator::evalStatement(Ast::Statement stmt, ContextPtr ctx) + StatementResult Evaluator::evalStatement(Ast::Statement stmt, + ContextPtr ctx) { using enum Ast::AstType; switch (stmt->getType()) @@ -931,16 +1001,14 @@ namespace Fig { throw EvaluatorError( u8"RedeclarationError", - std::format("Variable `{}` already declared in this scope", - varDef->name.toBasicString()), + std::format( + "Variable `{}` already declared in this scope", + varDef->name.toBasicString()), varDef); } RvObject value = nullptr; - if (varDef->expr) - { - value = eval(varDef->expr, ctx); - } + if (varDef->expr) { value = eval(varDef->expr, ctx); } TypeInfo declaredType; // default is Any const FString &declaredTypeName = varDef->typeName; if (declaredTypeName == Parser::varDefTypeFollowed) @@ -950,7 +1018,8 @@ namespace Fig else if (!declaredTypeName.empty()) { declaredType = TypeInfo(declaredTypeName); - if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) + if (value != nullptr + && !isTypeMatch(declaredType, value, ctx)) { throw EvaluatorError( u8"TypeError", @@ -967,22 +1036,19 @@ namespace Fig Object::defaultValue(declaredType)); } // else -> Ok } // else -> type is Any (default) - AccessModifier am = (varDef->isConst ? ( - varDef->isPublic ? AccessModifier::PublicConst : - AccessModifier::Const) : - ( - varDef->isPublic ? AccessModifier::Public : - AccessModifier::Normal)); - ctx->def( - varDef->name, - declaredType, - am, - value); + AccessModifier am = + (varDef->isConst ? + (varDef->isPublic ? AccessModifier::PublicConst : + AccessModifier::Const) : + (varDef->isPublic ? AccessModifier::Public : + AccessModifier::Normal)); + ctx->def(varDef->name, declaredType, am, value); return StatementResult::normal(); } case FunctionDefSt: { - auto fnDef = std::dynamic_pointer_cast(stmt); + auto fnDef = + std::dynamic_pointer_cast(stmt); assert(fnDef != nullptr); const FString &fnName = fnDef->name; @@ -990,20 +1056,18 @@ namespace Fig { throw EvaluatorError( u8"RedeclarationError", - std::format("Function `{}` already declared in this scope", - fnName.toBasicString()), + std::format( + "Function `{}` already declared in this scope", + fnName.toBasicString()), fnDef); } Function fn( - fnDef->paras, - TypeInfo(fnDef->retType), - fnDef->body, - ctx); - ctx->def( - fnName, - ValueType::Function, - (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), - std::make_shared(fn)); + fnDef->paras, TypeInfo(fnDef->retType), fnDef->body, ctx); + ctx->def(fnName, + ValueType::Function, + (fnDef->isPublic ? AccessModifier::PublicConst : + AccessModifier::Const), + std::make_shared(fn)); return StatementResult::normal(); } @@ -1015,7 +1079,9 @@ namespace Fig { throw EvaluatorError( u8"RedeclarationError", - std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()), + std::format( + "Structure '{}' already defined in this scope", + stDef->name.toBasicString()), stDef); } std::vector fields; @@ -1026,45 +1092,53 @@ namespace Fig { throw EvaluatorError( u8"RedeclarationError", - std::format("Field '{}' already defined in structure '{}'", - field.fieldName.toBasicString(), stDef->name.toBasicString()), + std::format( + "Field '{}' already defined in structure '{}'", + field.fieldName.toBasicString(), + stDef->name.toBasicString()), stDef); } - fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr)); + fields.push_back(Field(field.am, + field.fieldName, + TypeInfo(field.tiName), + field.defaultValueExpr)); } - ContextPtr defContext = std::make_shared(FString(std::format("", - stDef->name.toBasicString(), - stDef->getAAI().line, - stDef->getAAI().column)), - ctx); + ContextPtr defContext = std::make_shared( + FString(std::format("", + stDef->name.toBasicString(), + stDef->getAAI().line, + stDef->getAAI().column)), + ctx); const Ast::BlockStatement &body = stDef->body; for (auto &st : body->stmts) { if (st->getType() != Ast::AstType::FunctionDefSt) { - throw EvaluatorError(u8"UnexpectedStatementInStructError", - std::format("Unexpected statement `{}` in struct declaration", - st->toString().toBasicString()), - st); + throw EvaluatorError( + u8"UnexpectedStatementInStructError", + std::format( + "Unexpected statement `{}` in struct declaration", + st->toString().toBasicString()), + st); } evalStatement(st, defContext); // function def st } - AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); + AccessModifier am = + (stDef->isPublic ? AccessModifier::PublicConst : + AccessModifier::Const); TypeInfo type(stDef->name, true); // register type name - ctx->def( - stDef->name, - ValueType::StructType, - am, - std::make_shared(StructType( - type, - defContext, - fields))); + ctx->def(stDef->name, + ValueType::StructType, + am, + std::make_shared( + StructType(type, defContext, fields))); return StatementResult::normal(); } case InterfaceDefSt: { - auto ifd = std::dynamic_pointer_cast(stmt); + auto ifd = + std::dynamic_pointer_cast(stmt); assert(ifd != nullptr); const FString &interfaceName = ifd->name; @@ -1073,18 +1147,18 @@ namespace Fig { throw EvaluatorError( u8"RedeclarationError", - std::format("Interface `{}` already declared in this scope", - interfaceName.toBasicString()), + std::format( + "Interface `{}` already declared in this scope", + interfaceName.toBasicString()), ifd); } TypeInfo type(interfaceName, true); // register interface - ctx->def( - interfaceName, - type, - (ifd->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), - std::make_shared(InterfaceType( - type, - ifd->methods))); + ctx->def(interfaceName, + type, + (ifd->isPublic ? AccessModifier::PublicConst : + AccessModifier::Const), + std::make_shared( + InterfaceType(type, ifd->methods))); return StatementResult::normal(); } @@ -1098,24 +1172,25 @@ namespace Fig { throw EvaluatorError( u8"DuplicateImplError", - std::format( - "Duplicate implement `{}` for `{}`", - interfaceType.toString().toBasicString(), - structType.toString().toBasicString()), + std::format("Duplicate implement `{}` for `{}`", + interfaceType.toString().toBasicString(), + structType.toString().toBasicString()), ip); } if (!ctx->contains(ip->interfaceName)) { throw EvaluatorError( u8"InterfaceNotFoundError", - std::format("Interface '{}' not found", ip->interfaceName.toBasicString()), + std::format("Interface '{}' not found", + ip->interfaceName.toBasicString()), ip); } if (!ctx->contains(ip->structName)) { throw EvaluatorError( u8"StructNotFoundError", - std::format("Struct '{}' not found", ip->structName.toBasicString()), + std::format("Struct '{}' not found", + ip->structName.toBasicString()), ip); } auto interfaceSlot = ctx->get(ip->interfaceName); @@ -1131,14 +1206,16 @@ namespace Fig { throw EvaluatorError( u8"NotAInterfaceError", - std::format("Variable `{}` is not a interface", ip->interfaceName.toBasicString()), + std::format("Variable `{}` is not a interface", + ip->interfaceName.toBasicString()), ip); } if (!structTypeObj->is()) { throw EvaluatorError( u8"NotAStructType", - std::format("Variable `{}` is not a struct type", ip->structName.toBasicString()), + std::format("Variable `{}` is not a struct type", + ip->structName.toBasicString()), ip); } auto &implementMethods = ip->methods; @@ -1154,9 +1231,10 @@ namespace Fig { throw EvaluatorError( u8"InterfaceDuplicateMethodError", - std::format("Interface '{}' has duplicate method '{}'", - interfaceType.toString().toBasicString(), - m.name.toBasicString()), + std::format( + "Interface '{}' has duplicate method '{}'", + interfaceType.toString().toBasicString(), + m.name.toBasicString()), ip); } ifaceMethods[m.name] = m; @@ -1173,11 +1251,12 @@ namespace Fig { throw EvaluatorError( u8"RedundantImplementationError", - std::format("Struct '{}' implements extra method '{}' " - "which is not required by interface '{}'", - structType.toString().toBasicString(), - name.toBasicString(), - interfaceType.toString().toBasicString()), + std::format( + "Struct '{}' implements extra method '{}' " + "which is not required by interface '{}'", + structType.toString().toBasicString(), + name.toBasicString(), + interfaceType.toString().toBasicString()), ip); } @@ -1221,20 +1300,18 @@ namespace Fig implemented.insert(name); - record.implMethods[name] = Function( - implMethod.paras, - TypeInfo(ifMethod.returnType), - implMethod.body, - ctx); + record.implMethods[name] = + Function(implMethod.paras, + TypeInfo(ifMethod.returnType), + implMethod.body, + ctx); } for (auto &m : interface.methods) { - if (implemented.contains(m.name)) - continue; + if (implemented.contains(m.name)) continue; - if (m.hasDefaultBody()) - continue; + if (m.hasDefaultBody()) continue; throw EvaluatorError( u8"MissingImplementationError", @@ -1258,9 +1335,8 @@ namespace Fig { throw EvaluatorError( u8"TypeError", - std::format( - "Condition must be boolean, but got '{}'", - prettyType(condVal).toBasicString()), + std::format("Condition must be boolean, but got '{}'", + prettyType(condVal).toBasicString()), ifSt->condition); } if (condVal->as()) @@ -1305,27 +1381,17 @@ namespace Fig prettyType(condVal).toBasicString()), whileSt->condition); } - if (!condVal->as()) - { - break; - } + if (!condVal->as()) { break; } ContextPtr loopContext = std::make_shared( FString(std::format("", - whileSt->getAAI().line, whileSt->getAAI().column)), + whileSt->getAAI().line, + whileSt->getAAI().column)), ctx); // every loop has its own context - StatementResult sr = evalBlockStatement(whileSt->body, loopContext); - if (sr.shouldReturn()) - { - return sr; - } - if (sr.shouldBreak()) - { - break; - } - if (sr.shouldContinue()) - { - continue; - } + StatementResult sr = + evalBlockStatement(whileSt->body, loopContext); + if (sr.shouldReturn()) { return sr; } + if (sr.shouldBreak()) { break; } + if (sr.shouldContinue()) { continue; } } return StatementResult::normal(); }; @@ -1333,13 +1399,16 @@ namespace Fig auto forSt = std::dynamic_pointer_cast(stmt); ContextPtr loopContext = std::make_shared( FString(std::format("", - forSt->getAAI().line, forSt->getAAI().column)), + forSt->getAAI().line, + forSt->getAAI().column)), ctx); // for loop has its own context - evalStatement(forSt->initSt, loopContext); // ignore init statement result + evalStatement(forSt->initSt, + loopContext); // ignore init statement result size_t iteration = 0; - while (true) // use while loop to simulate for loop, cause we need to check condition type every iteration + while (true) // use while loop to simulate for loop, cause we + // need to check condition type every iteration { ObjectPtr condVal = eval(forSt->condition, loopContext); if (condVal->getTypeInfo() != ValueType::Bool) @@ -1351,38 +1420,102 @@ namespace Fig prettyType(condVal).toBasicString()), forSt->condition); } - if (!condVal->as()) - { - break; - } + if (!condVal->as()) { break; } iteration++; ContextPtr iterationContext = std::make_shared( FString(std::format("", - forSt->getAAI().line, forSt->getAAI().column, iteration)), + forSt->getAAI().line, + forSt->getAAI().column, + iteration)), loopContext); // every loop has its own context - StatementResult sr = evalBlockStatement(forSt->body, iterationContext); - if (sr.shouldReturn()) - { - return sr; - } - if (sr.shouldBreak()) - { - break; - } + StatementResult sr = + evalBlockStatement(forSt->body, iterationContext); + if (sr.shouldReturn()) { return sr; } + if (sr.shouldBreak()) { break; } if (sr.shouldContinue()) { // continue to next iteration continue; } - evalStatement(forSt->incrementSt, loopContext); // ignore increment statement result + evalStatement( + forSt->incrementSt, + loopContext); // ignore increment statement result } return StatementResult::normal(); } + + case TrySt: { + auto tryst = std::dynamic_pointer_cast(stmt); + assert(tryst != nullptr); + + ContextPtr tryCtx = std::make_shared( + FString(std::format("", + tryst->getAAI().line, + tryst->getAAI().column)), + ctx); + StatementResult sr = StatementResult::normal(); + for (auto &stmt : tryst->body->stmts) + { + sr = evalStatement(stmt, tryCtx); // eval in try context + if (sr.isError()) { break; } + } + bool catched = false; + for (auto &cat : tryst->catches) + { + const FString &errVarName = cat.errVarName; + TypeInfo errVarType = + (cat.hasType ? TypeInfo(cat.errVarType) : + ValueType::Any); + if (isTypeMatch(errVarType, sr.result, ctx)) + { + ContextPtr catchCtx = std::make_shared( + FString(std::format("", + cat.body->getAAI().line, + cat.body->getAAI().column)), + ctx); + catchCtx->def(errVarName, + errVarType, + AccessModifier::Normal, + sr.result); + sr = evalBlockStatement(cat.body, catchCtx); + catched = true; + break; + } + } + if (!catched) + { + throw EvaluatorError( + u8"UncaughtExceptionError", + std::format("Uncaught exception: {}", + sr.result->toString().toBasicString()), + tryst); + } + if (tryst->finallyBlock) + { + sr = evalBlockStatement(tryst->finallyBlock, ctx); + } + return sr; + } + + case ThrowSt: { + auto ts = std::dynamic_pointer_cast(stmt); + assert(ts != nullptr); + + ObjectPtr value = eval(ts->value, ctx); + if (value->is()) + { + throw EvaluatorError( + u8"TypeError", u8"Why did you throw a null?", ts); + } + return StatementResult::errorFlow(value); + } + case ReturnSt: { auto returnSt = std::dynamic_pointer_cast(stmt); assert(returnSt != nullptr); - ObjectPtr returnValue = Object::getNullInstance(); // default is null + ObjectPtr returnValue = + Object::getNullInstance(); // default is null if (returnSt->retValue) returnValue = eval(returnSt->retValue, ctx); return StatementResult::returnFlow(returnValue); @@ -1391,17 +1524,15 @@ namespace Fig case BreakSt: { if (!ctx->parent) { - throw EvaluatorError( - u8"BreakOutsideLoopError", - u8"`break` statement outside loop", - stmt); + throw EvaluatorError(u8"BreakOutsideLoopError", + u8"`break` statement outside loop", + stmt); } if (!ctx->isInLoopContext()) { - throw EvaluatorError( - u8"BreakOutsideLoopError", - u8"`break` statement outside loop", - stmt); + throw EvaluatorError(u8"BreakOutsideLoopError", + u8"`break` statement outside loop", + stmt); } return StatementResult::breakFlow(); } @@ -1409,23 +1540,22 @@ namespace Fig case ContinueSt: { if (!ctx->parent) { - throw EvaluatorError( - u8"ContinueOutsideLoopError", - u8"`continue` statement outside loop", - stmt); + throw EvaluatorError(u8"ContinueOutsideLoopError", + u8"`continue` statement outside loop", + stmt); } if (!ctx->isInLoopContext()) { - throw EvaluatorError( - u8"ContinueOutsideLoopError", - u8"`continue` statement outside loop", - stmt); + throw EvaluatorError(u8"ContinueOutsideLoopError", + u8"`continue` statement outside loop", + stmt); } return StatementResult::continueFlow(); } case ExpressionStmt: { - auto exprStmt = std::dynamic_pointer_cast(stmt); + auto exprStmt = + std::dynamic_pointer_cast(stmt); assert(exprStmt != nullptr); return StatementResult::normal(eval(exprStmt->exp, ctx)); @@ -1438,19 +1568,18 @@ namespace Fig } } - std::filesystem::path Evaluator::resolveModulePath(const std::vector &pathVec) + std::filesystem::path + Evaluator::resolveModulePath(const std::vector &pathVec) { namespace fs = std::filesystem; - static const std::vector defaultLibraryPath{ - "Library", - "Library/fpm"}; + static const std::vector defaultLibraryPath{"Library", + "Library/fpm"}; std::vector pathToFind(defaultLibraryPath); pathToFind.insert( pathToFind.begin(), - fs::path( - this->sourcePath.toBasicString()) + fs::path(this->sourcePath.toBasicString()) .parent_path()); // first search module at the source file path fs::path path; @@ -1466,7 +1595,8 @@ namespace Fig bool found = false; for (auto &parentFolder : pathToFind) { - modPath = parentFolder / FString(modPathStrTop + u8".fig").toBasicString(); + modPath = parentFolder + / FString(modPathStrTop + u8".fig").toBasicString(); if (fs::exists(modPath)) { path = modPath; @@ -1478,17 +1608,20 @@ namespace Fig modPath = parentFolder / modPathStrTop.toBasicString(); if (fs::is_directory(modPath)) // comp is a directory { - modPath = modPath / FString(modPathStrTop + u8".fig").toBasicString(); + modPath = + modPath + / FString(modPathStrTop + u8".fig").toBasicString(); /* - if module name is a directory, we require [module name].fig at the directory + if module name is a directory, we require [module + name].fig at the directory */ if (!fs::exists(modPath)) { throw RuntimeError(FString( - std::format( - "requires module file, {}\\{}", - modPathStrTop.toBasicString(), - FString(modPathStrTop + u8".fig").toBasicString()))); + std::format("requires module file, {}\\{}", + modPathStrTop.toBasicString(), + FString(modPathStrTop + u8".fig") + .toBasicString()))); } found = true; path = modPath; @@ -1498,10 +1631,8 @@ namespace Fig } if (!found) - throw RuntimeError( - FString(std::format( - "Could not find module `{}`", - modPathStrTop.toBasicString()))); + throw RuntimeError(FString(std::format( + "Could not find module `{}`", modPathStrTop.toBasicString()))); bool found2 = false; @@ -1513,10 +1644,9 @@ namespace Fig if (fs::exists(modPath)) { if (i != pathVec.size() - 1) - throw RuntimeError(FString( - std::format( - "expects {} as parent directory and find next module, but got a file", - next.toBasicString()))); + throw RuntimeError(FString(std::format( + "expects {} as parent directory and find next module, but got a file", + next.toBasicString()))); // it's the last module found2 = true; path = modPath; @@ -1525,18 +1655,17 @@ namespace Fig // `next` is a folder modPath = modPath.parent_path() / next.toBasicString(); if (!fs::exists(modPath)) - throw RuntimeError(FString( - std::format("Could not find module `{}`", next.toBasicString()))); + throw RuntimeError(FString(std::format( + "Could not find module `{}`", next.toBasicString()))); if (i == pathVec.size() - 1) { // `next` is the last module modPath = modPath / FString(next + u8".fig").toBasicString(); if (!fs::exists(modPath)) { - throw RuntimeError(FString( - std::format( - "expects {} as parent directory and find next module, but got a file", - next.toBasicString()))); + throw RuntimeError(FString(std::format( + "expects {} as parent directory and find next module, but got a file", + next.toBasicString()))); } found2 = true; path = modPath; @@ -1544,8 +1673,8 @@ namespace Fig } if (!found2 && !fs::exists(modPath)) - throw RuntimeError(FString( - std::format("Could not find module `{}`", pathVec.end()->toBasicString()))); + throw RuntimeError(FString(std::format( + "Could not find module `{}`", pathVec.end()->toBasicString()))); return path; } @@ -1555,7 +1684,8 @@ namespace Fig std::ifstream file(path); assert(file.is_open()); - std::string source((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + std::string source((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); file.close(); Lexer lexer((FString(source))); @@ -1564,29 +1694,13 @@ namespace Fig std::vector sourceLines = Utils::splitSource(FString(source)); - try - { - asts = parser.parseAll(); - } - catch (const AddressableError &e) - { - ErrorLog::logAddressableError(e, sourcePath, sourceLines); - } - catch (const UnaddressableError &e) - { - ErrorLog::logUnaddressableError(e); - } - catch (const std::exception &e) - { - std::cerr << "uncaught exception of: " << e.what() << '\n'; - } + asts = parser.parseAll(); Evaluator evaluator; evaluator.SetSourcePath(FString(path.string())); ContextPtr modctx = std::make_shared( - FString(std::format("", path.string())), - nullptr); + FString(std::format("", path.string())), nullptr); evaluator.SetGlobalContext(modctx); evaluator.RegisterBuiltinsValue(); @@ -1599,7 +1713,8 @@ namespace Fig { const std::vector &pathVec = i->path; - const FString &modName = pathVec.at(pathVec.size() - 1); // pathVec at least has 1 element + const FString &modName = + pathVec.at(pathVec.size() - 1); // pathVec at least has 1 element if (modName == u8"_builtins") { RegisterBuiltins(); @@ -1612,21 +1727,15 @@ namespace Fig if (ctx->containsInThisScope(modName)) { - throw EvaluatorError( - u8"RedeclarationError", - std::format( - "{} has already been declared.", - modName.toBasicString()), - i); + throw EvaluatorError(u8"RedeclarationError", + std::format("{} has already been declared.", + modName.toBasicString()), + i); } - ctx->def( - modName, - ValueType::Module, - AccessModifier::PublicConst, - std::make_shared( - Module( - modName, - modCtx))); + ctx->def(modName, + ValueType::Module, + AccessModifier::PublicConst, + std::make_shared(Module(modName, modCtx))); return StatementResult::normal(); } @@ -1644,21 +1753,27 @@ namespace Fig else { // statement - Ast::Statement stmt = std::dynamic_pointer_cast(ast); + Ast::Statement stmt = + std::dynamic_pointer_cast(ast); assert(stmt != nullptr); sr = evalStatement(stmt, global); - if (!sr.isNormal()) + if (sr.isError()) { - return sr; + throw EvaluatorError( + u8"UncaughtExceptionError", + std::format("Uncaught exception: {}", + sr.result->toString().toBasicString()), + stmt); } + if (!sr.isNormal()) { return sr; } } } + return sr; } void Evaluator::printStackTrace() { - if (global) - global->printStackTrace(); + if (global) global->printStackTrace(); } }; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index 5b0cc9b..1ab53ab 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -19,7 +19,8 @@ namespace Fig Normal, Return, Break, - Continue + Continue, + Error } flow; StatementResult(ObjectPtr val, Flow f = Flow::Normal) : @@ -43,11 +44,16 @@ namespace Fig { return StatementResult(Object::getNullInstance(), Flow::Continue); } + static StatementResult errorFlow(ObjectPtr val) + { + return StatementResult(val, Flow::Error); + } bool isNormal() const { return flow == Flow::Normal; } bool shouldReturn() const { return flow == Flow::Return; } bool shouldBreak() const { return flow == Flow::Break; } bool shouldContinue() const { return flow == Flow::Continue; } + bool isError() const { return flow == Flow::Error; } }; class Evaluator diff --git a/src/Lexer/lexer.cpp b/src/Lexer/lexer.cpp index 5f65532..4336661 100644 --- a/src/Lexer/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -81,7 +81,10 @@ namespace Fig {FString(u8"return"), TokenType::Return}, {FString(u8"break"), TokenType::Break}, {FString(u8"continue"), TokenType::Continue}, - + {FString(u8"try"), TokenType::Try}, + {FString(u8"catch"), TokenType::Catch}, + {FString(u8"throw"), TokenType::Throw}, + {FString(u8"Finally"), TokenType::Finally}, // {FString(u8"Null"), TokenType::TypeNull}, // {FString(u8"Int"), TokenType::TypeInt}, diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index 36bf62d..d986b5b 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -1,5 +1,7 @@ #pragma once +#include "Ast/Statements/InterfaceDefSt.hpp" +#include "Ast/functionParameters.hpp" #include #include @@ -15,13 +17,40 @@ namespace Fig { namespace Builtins { + const TypeInfo ErrorInterfaceTypeInfo(u8"Error", true); + /* + // error's interface like: + interface Error + { + toString() -> String; + getErrorClass() -> String; + getErrorMessage() -> String; + } + */ + const std::unordered_map builtinValues = { {u8"null", Object::getNullInstance()}, {u8"true", Object::getTrueInstance()}, {u8"false", Object::getFalseInstance()}, + {u8"Error", + std::make_shared(InterfaceType( + ErrorInterfaceTypeInfo, + {Ast::InterfaceMethod(u8"toString", + Ast::FunctionParameters({}, {}), + u8"String", + nullptr), + Ast::InterfaceMethod(u8"getErrorClass", + Ast::FunctionParameters({}, {}), + u8"String", + nullptr), + Ast::InterfaceMethod(u8"getErrorMessage", + Ast::FunctionParameters({}, {}), + u8"String", + nullptr)}))}, }; - using BuiltinFunction = std::function &)>; + using BuiltinFunction = + std::function &)>; const std::unordered_map builtinFunctionArgCounts = { {u8"__fstdout_print", -1}, // variadic @@ -67,35 +96,46 @@ namespace Fig }; const std::unordered_map builtinFunctions{ - {u8"__fstdout_print", [](const std::vector &args) -> ObjectPtr { + {u8"__fstdout_print", + [](const std::vector &args) -> ObjectPtr { for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } - return std::make_shared(ValueType::IntClass(args.size())); + return std::make_shared( + ValueType::IntClass(args.size())); }}, - {u8"__fstdout_println", [](const std::vector &args) -> ObjectPtr { + {u8"__fstdout_println", + [](const std::vector &args) -> ObjectPtr { for (auto arg : args) { std::print("{}", arg->toStringIO().toBasicString()); } std::print("\n"); - return std::make_shared(ValueType::IntClass(args.size())); + return std::make_shared( + ValueType::IntClass(args.size())); }}, - {u8"__fstdin_read", [](const std::vector &args) -> ObjectPtr { + {u8"__fstdin_read", + [](const std::vector &args) -> ObjectPtr { std::string input; std::cin >> input; - return std::make_shared(FString::fromBasicString(input)); + return std::make_shared( + FString::fromBasicString(input)); }}, - {u8"__fstdin_readln", [](const std::vector &args) -> ObjectPtr { + {u8"__fstdin_readln", + [](const std::vector &args) -> ObjectPtr { std::string line; std::getline(std::cin, line); - return std::make_shared(FString::fromBasicString(line)); + return std::make_shared( + FString::fromBasicString(line)); }}, - {u8"__fvalue_type", [](const std::vector &args) -> ObjectPtr { - return std::make_shared(args[0]->getTypeInfo().toString()); + {u8"__fvalue_type", + [](const std::vector &args) -> ObjectPtr { + return std::make_shared( + args[0]->getTypeInfo().toString()); }}, - {u8"__fvalue_int_parse", [](const std::vector &args) -> ObjectPtr { + {u8"__fvalue_int_parse", + [](const std::vector &args) -> ObjectPtr { FString str = args[0]->as(); try { @@ -104,203 +144,252 @@ namespace Fig } catch (...) { - throw RuntimeError(FString(std::format("Invalid int string for parsing", str.toBasicString()))); + throw RuntimeError( + FString(std::format("Invalid int string for parsing", + str.toBasicString()))); } }}, - {u8"__fvalue_int_from", [](const std::vector &args) -> ObjectPtr { + {u8"__fvalue_int_from", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; if (val->is()) { - return std::make_shared(static_cast(val->as())); + return std::make_shared( + static_cast( + val->as())); } else if (val->is()) { - return std::make_shared(static_cast(val->as() ? 1 : 0)); + return std::make_shared( + static_cast( + val->as() ? 1 : 0)); } else { - throw RuntimeError(FString(std::format("Type '{}' cannot be converted to int", val->getTypeInfo().toString().toBasicString()))); + throw RuntimeError(FString(std::format( + "Type '{}' cannot be converted to int", + val->getTypeInfo().toString().toBasicString()))); } }}, - {u8"__fvalue_double_parse", [](const std::vector &args) -> ObjectPtr { + {u8"__fvalue_double_parse", + [](const std::vector &args) -> ObjectPtr { FString str = args[0]->as(); try { - ValueType::DoubleClass val = std::stod(str.toBasicString()); - return std::make_shared(ValueType::DoubleClass(val)); + ValueType::DoubleClass val = + std::stod(str.toBasicString()); + return std::make_shared( + ValueType::DoubleClass(val)); } catch (...) { - throw RuntimeError(FString(std::format("Invalid double string for parsing", str.toBasicString()))); + throw RuntimeError(FString( + std::format("Invalid double string for parsing", + str.toBasicString()))); } }}, - {u8"__fvalue_double_from", [](const std::vector &args) -> ObjectPtr { + {u8"__fvalue_double_from", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; if (val->is()) { - return std::make_shared(static_cast(val->as())); + return std::make_shared( + static_cast( + val->as())); } else if (val->is()) { - return std::make_shared(ValueType::DoubleClass(val->as() ? 1.0 : 0.0)); + return std::make_shared(ValueType::DoubleClass( + val->as() ? 1.0 : 0.0)); } else { - throw RuntimeError(FString(std::format("Type '{}' cannot be converted to double", val->getTypeInfo().toString().toBasicString()))); + throw RuntimeError(FString(std::format( + "Type '{}' cannot be converted to double", + val->getTypeInfo().toString().toBasicString()))); } }}, - {u8"__fvalue_string_from", [](const std::vector &args) -> ObjectPtr { + {u8"__fvalue_string_from", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; return std::make_shared(val->toStringIO()); }}, - /* math start */ - {u8"__fmath_acos", [](const std::vector &args) -> ObjectPtr { + /* math start */ + {u8"__fmath_acos", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(acos(d)); }}, - {u8"__fmath_acosh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_acosh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(acosh(d)); }}, - {u8"__fmath_asin", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_asin", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(asin(d)); }}, - {u8"__fmath_asinh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_asinh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(asinh(d)); }}, - {u8"__fmath_atan", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_atan", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(atan(d)); }}, - {u8"__fmath_atan2", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_atan2", + [](const std::vector &args) -> ObjectPtr { ValueType::DoubleClass y = args[0]->getNumericValue(); ValueType::DoubleClass x = args[1]->getNumericValue(); return std::make_shared(atan2(y, x)); }}, - {u8"__fmath_atanh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_atanh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(atanh(d)); }}, - {u8"__fmath_ceil", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_ceil", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(ceil(d)); }}, - {u8"__fmath_cos", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_cos", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(cos(d)); }}, - {u8"__fmath_cosh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_cosh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(cosh(d)); }}, - {u8"__fmath_exp", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_exp", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(exp(d)); }}, - {u8"__fmath_expm1", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_expm1", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(expm1(d)); }}, - {u8"__fmath_fabs", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_fabs", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(fabs(d)); }}, - {u8"__fmath_floor", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_floor", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(floor(d)); }}, - {u8"__fmath_fmod", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_fmod", + [](const std::vector &args) -> ObjectPtr { ValueType::DoubleClass x = args[0]->getNumericValue(); ValueType::DoubleClass y = args[1]->getNumericValue(); return std::make_shared(fmod(x, y)); }}, - {u8"__fmath_frexp", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_frexp", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); int e; - return std::make_shared(List( - {std::make_shared(frexp(d, &e)), - std::make_shared(static_cast(e))})); + return std::make_shared( + List({std::make_shared(frexp(d, &e)), + std::make_shared( + static_cast(e))})); }}, - {u8"__fmath_gcd", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_gcd", + [](const std::vector &args) -> ObjectPtr { ValueType::IntClass x = args[0]->as(); ValueType::IntClass y = args[1]->as(); return std::make_shared(std::gcd(x, y)); }}, - {u8"__fmath_hypot", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_hypot", + [](const std::vector &args) -> ObjectPtr { ValueType::DoubleClass x = args[0]->getNumericValue(); ValueType::DoubleClass y = args[1]->getNumericValue(); return std::make_shared(hypot(x, y)); }}, - {u8"__fmath_isequal", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_isequal", + [](const std::vector &args) -> ObjectPtr { ValueType::DoubleClass x = args[0]->getNumericValue(); ValueType::DoubleClass y = args[1]->getNumericValue(); static const double epsilon = 1e-9; - return std::make_shared( - fabs(x - y) < epsilon - ); + return std::make_shared(fabs(x - y) < epsilon); }}, - {u8"__fmath_log", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_log", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(log(d)); }}, - {u8"__fmath_log10", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_log10", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(log10(d)); }}, - {u8"__fmath_log1p", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_log1p", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(log1p(d)); }}, - {u8"__fmath_log2", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_log2", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(log2(d)); }}, - {u8"__fmath_sin", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_sin", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(sin(d)); }}, - {u8"__fmath_sinh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_sinh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(sinh(d)); }}, - {u8"__fmath_sqrt", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_sqrt", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(sqrt(d)); }}, - {u8"__fmath_tan", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_tan", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(tan(d)); }}, - {u8"__fmath_tanh", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_tanh", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(tanh(d)); }}, - {u8"__fmath_trunc", [](const std::vector &args) -> ObjectPtr { + {u8"__fmath_trunc", + [](const std::vector &args) -> ObjectPtr { ObjectPtr val = args[0]; ValueType::DoubleClass d = val->getNumericValue(); return std::make_shared(trunc(d)); @@ -317,7 +406,8 @@ namespace Fig auto it = builtinFunctions.find(name); if (it == builtinFunctions.end()) { - throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); + throw RuntimeError(FString(std::format( + "Builtin function '{}' not found", name.toBasicString()))); } return it->second; } @@ -327,7 +417,8 @@ namespace Fig auto it = builtinFunctionArgCounts.find(name); if (it == builtinFunctionArgCounts.end()) { - throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); + throw RuntimeError(FString(std::format( + "Builtin function '{}' not found", name.toBasicString()))); } return it->second; } diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index 695d051..33a2343 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -1,3 +1,4 @@ +#include "Ast/Statements/ErrorFlow.hpp" #include "Ast/Statements/ImplementSt.hpp" #include "Ast/astBase.hpp" #include "Ast/functionParameters.hpp" @@ -30,6 +31,7 @@ namespace Fig {Ast::Operator::LessEqual, {8, 9}}, {Ast::Operator::Greater, {8, 9}}, {Ast::Operator::GreaterEqual, {8, 9}}, + {Ast::Operator::Is, {8, 9}}, // 位运算 {Ast::Operator::BitAnd, {6, 7}}, @@ -478,6 +480,89 @@ namespace Fig return makeAst(interfaceName, structName, methods); } + Ast::Throw Parser::__parseThrow() + { + // entry: current is `throw` + next(); // consume `throw` + Ast::Expression exp = parseExpression(0); + expect(TokenType::Semicolon); + next(); // consume `;` + return makeAst(exp); + } + + Ast::Try Parser::__parseTry() + { + // entry: current is `try` + next(); // consume `try` + + /* + try + { + ... + } + catch(e: IOError) + { + } + catch(e: TimeOutError) + { + } + */ + expect(TokenType::LeftBrace); + Ast::BlockStatement body = __parseBlockStatement(); + std::vector catches; + Ast::BlockStatement finallyBlock = nullptr; + while (true) + { + if (isThis(TokenType::Catch)) + { + next(); // consume `catch` + expect(TokenType::LeftParen); + next(); // consume `(` + expect(TokenType::Identifier, u8"error receive var name"); + FString errVarName = currentToken().getValue(); + next(); // consume name + + bool hasType = false; + FString errVarType; + if (isThis(TokenType::Colon)) // : + { + next(); + expect(TokenType::Identifier, u8"error type"); + errVarType = currentToken().getValue(); + next(); // consume var type + hasType = true; + } + expect(TokenType::RightParen); // ) + next(); // consume `)` + expect(TokenType::LeftBrace); // { + Ast::BlockStatement catchBody = __parseBlockStatement(); + + if (hasType) + { + catches.push_back(Ast::Catch(errVarName, errVarType, catchBody)); + } + else { + catches.push_back(Ast::Catch(errVarName, catchBody)); + } + } + else if (isThis(TokenType::Finally)) + { + if (finallyBlock != nullptr) + { + throw SyntaxError(u8"Duplicate try finally-block", currentAAI.line, currentAAI.column); + } + next(); // consume `finally` + expect(TokenType::LeftBrace); + finallyBlock = __parseBlockStatement(); + } + else + { + break; + } + } + return makeAst(body, catches, finallyBlock); + } + Ast::Statement Parser::__parseStatement() { Ast::Statement stmt; @@ -568,6 +653,14 @@ namespace Fig { stmt = __parseContinue(); } + else if (isThis(TokenType::Throw)) + { + stmt = __parseThrow(); + } + else if (isThis(TokenType::Try)) + { + stmt = __parseTry(); + } else { // expression statement diff --git a/src/Parser/parser.hpp b/src/Parser/parser.hpp index eeddc49..6e69317 100644 --- a/src/Parser/parser.hpp +++ b/src/Parser/parser.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -211,43 +212,43 @@ namespace Fig return ptr; } - void expectPeek(TokenType type) + void expectPeek(TokenType type, std::source_location loc = std::source_location::current()) { if (peekToken().getType() != type) { throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", magic_enum::enum_name(type), - magic_enum::enum_name(peekToken().getType())))); + magic_enum::enum_name(peekToken().getType()))), loc); } } - void expect(TokenType type) + void expect(TokenType type, std::source_location loc = std::source_location::current()) { if (currentToken().getType() != type) { throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", magic_enum::enum_name(type), - magic_enum::enum_name(currentToken().getType())))); + magic_enum::enum_name(currentToken().getType()))), loc); } } - void expectPeek(TokenType type, FString expected) + void expectPeek(TokenType type, FString expected, std::source_location loc = std::source_location::current()) { if (peekToken().getType() != type) { throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", expected.toBasicString(), - magic_enum::enum_name(peekToken().getType())))); + magic_enum::enum_name(peekToken().getType()))), loc); } } - void expect(TokenType type, FString expected) + void expect(TokenType type, FString expected, std::source_location loc = std::source_location::current()) { if (currentToken().getType() != type) { throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", expected.toBasicString(), - magic_enum::enum_name(currentToken().getType())))); + magic_enum::enum_name(currentToken().getType()))), loc); } } @@ -316,6 +317,9 @@ namespace Fig Ast::InterfaceDef __parseInterfaceDef(bool); // entry: current is Token::Identifier (interface name) arg(isPublic: bool) Ast::Implement __parseImplement(); // entry: current is `impl` + Ast::Throw __parseThrow(); // entry: current is `throw` + Ast::Try __parseTry(); // entry: current is `try` + Ast::BinaryExpr __parseInfix(Ast::Expression, Ast::Operator, Precedence); Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence); Ast::Expression __parseCall(Ast::Expression); diff --git a/src/Token/token.hpp b/src/Token/token.hpp index 012c8ee..551ca65 100644 --- a/src/Token/token.hpp +++ b/src/Token/token.hpp @@ -39,6 +39,10 @@ namespace Fig Return, // return Break, // break Continue, // continue + Try, // try + Catch, // catch + Throw, // throw + Finally, // finally // TypeNull, // Null // TypeInt, // Int