diff --git a/include/Ast/AccessModifier.hpp b/include/Ast/AccessModifier.hpp deleted file mode 100644 index 7547332..0000000 --- a/include/Ast/AccessModifier.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -namespace Fig -{ - enum class AccessModifier - { - Normal, - Const, - Final, - Public, - PublicConst, - PublicFinal, - }; -}; \ No newline at end of file diff --git a/include/ast.hpp b/include/ast.hpp deleted file mode 100644 index 6eede29..0000000 --- a/include/ast.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include \ No newline at end of file diff --git a/include/evaluator.hpp b/include/evaluator.hpp deleted file mode 100644 index 04cee6d..0000000 --- a/include/evaluator.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Fig -{ - - template - class EvaluatorError final : public AddressableError - { - public: - const char* errorName = errName; - virtual FString toString() const override - { - std::string msg = std::format("[Eve: {}] {} in [{}] {}", errName, std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name()); - return FString(msg); - } - using AddressableError::AddressableError; - explicit EvaluatorError(FStringView _msg, - Ast::AstAddressInfo aai, - std::source_location loc = std::source_location::current()) : - AddressableError(_msg, aai.line, aai.column, loc) - { - } - - virtual FString getErrorType() const override - { - return FString("[Eval]" + std::string(errorName)); - } - }; - struct StatementResult - { - ObjectPtr result; - enum class Flow - { - Normal, - Return, - Break, - Continue - } flow; - - StatementResult(ObjectPtr val, Flow f = Flow::Normal) : - result(val), flow(f) - { - } - - static StatementResult normal(ObjectPtr val = Object::getNullInstance()) - { - return StatementResult(val, Flow::Normal); - } - static StatementResult returnFlow(ObjectPtr val) - { - return StatementResult(val, Flow::Return); - } - static StatementResult breakFlow() - { - return StatementResult(Object::getNullInstance(), Flow::Break); - } - static StatementResult continueFlow() - { - return StatementResult(Object::getNullInstance(), Flow::Continue); - } - - 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; } - }; - - class Evaluator - { - private: - std::vector asts; - std::shared_ptr globalContext; - std::shared_ptr currentContext; - - Ast::AstAddressInfo currentAddressInfo; - - public: - Evaluator(const std::vector &a) : - asts(a) - { - globalContext = std::make_shared(FString(u8"global")); - currentContext = globalContext; - - for (auto &[name, fn] : Builtins::builtinFunctions) - { - int argc = Builtins::getBuiltinFunctionParamCount(name); - Function f(fn, argc); - globalContext->def( - name, - ValueType::Function, - AccessModifier::PublicConst, - std::make_shared(f)); - } - - for (auto &[name, val] : Builtins::builtinValues) - { - globalContext->def( - name, - val->getTypeInfo(), - AccessModifier::PublicConst, - val); - } - } - - std::shared_ptr getCurrentContext() { return currentContext; } - std::shared_ptr getGlobalContext() { return globalContext; } - - ObjectPtr __evalOp(Ast::Operator, const ObjectPtr &, const ObjectPtr & = Object::getNullInstance()); - ObjectPtr evalBinary(const Ast::BinaryExpr &); - ObjectPtr evalUnary(const Ast::UnaryExpr &); - - StatementResult evalBlockStatement(const Ast::BlockStatement &, ContextPtr = nullptr); - StatementResult evalStatement(const Ast::Statement &); - - ObjectPtr evalFunctionCall(const Function &, const Ast::FunctionArguments &, FString fnName = u8""); - - ObjectPtr eval(Ast::Expression); - void run(); - void printStackTrace() const; - }; - -} // namespace Fig diff --git a/include/module.hpp b/include/module.hpp deleted file mode 100644 index f0f92b5..0000000 --- a/include/module.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace Fig -{ - class Module - { - public: - const FString name; - const FString spec; - const FString path; - - std::shared_ptr context; // module-level context - - /* - - import module -> automatically create a module context and call function `init` if exists - all global functions, variables, structs, etc will be stored in module context - then module context will be linked to the current context - - */ - Module(const FString &moduleName, const FString &moduleSpec, const FString &modulePath) : - name(moduleName), spec(moduleSpec), path(modulePath) - { - context = std::make_shared(FString(std::format("", name.toBasicString())), nullptr); - } - - bool hasSymbol(const FString &symbolName) - { - return context->contains(symbolName); - } - Value getSymbol(const FString &symbolName) - { - auto valOpt = context->get(symbolName); - if (!valOpt.has_value()) - { - throw RuntimeError(FStringView(std::format("Symbol '{}' not found in module '{}'", symbolName.toBasicString(), name.toBasicString()))); - } - return valOpt.value(); - } - }; -}; \ No newline at end of file diff --git a/src/Ast/AccessModifier.hpp b/src/Ast/AccessModifier.hpp new file mode 100644 index 0000000..1eac50d --- /dev/null +++ b/src/Ast/AccessModifier.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +namespace Fig +{ + enum class AccessModifier : uint8_t + { + Normal, + Const, + Public, + PublicConst, + }; + + inline bool isAccessPublic(AccessModifier am) + { + return am == AccessModifier::Public || am == AccessModifier::PublicConst; + } + + inline bool isAccessConst(AccessModifier am) + { + return am == AccessModifier::Const || am == AccessModifier::PublicConst; + } +}; \ No newline at end of file diff --git a/include/Ast/BinaryExpr.hpp b/src/Ast/Expressions/BinaryExpr.hpp similarity index 100% rename from include/Ast/BinaryExpr.hpp rename to src/Ast/Expressions/BinaryExpr.hpp diff --git a/include/Ast/ContainerInitExprs.hpp b/src/Ast/Expressions/ContainerInitExprs.hpp similarity index 100% rename from include/Ast/ContainerInitExprs.hpp rename to src/Ast/Expressions/ContainerInitExprs.hpp diff --git a/include/Ast/FunctionCall.hpp b/src/Ast/Expressions/FunctionCall.hpp similarity index 94% rename from include/Ast/FunctionCall.hpp rename to src/Ast/Expressions/FunctionCall.hpp index d6a8a63..e29dc1a 100644 --- a/include/Ast/FunctionCall.hpp +++ b/src/Ast/Expressions/FunctionCall.hpp @@ -2,7 +2,7 @@ #pragma once #include -#include +#include namespace Fig::Ast { @@ -21,7 +21,7 @@ namespace Fig::Ast class FunctionCallExpr final : public ExpressionAst { public: - Expression callee; // 不是 name 了!! + Expression callee; FunctionArguments arg; FunctionCallExpr() diff --git a/include/Ast/FunctionLiteralExpr.hpp b/src/Ast/Expressions/FunctionLiteralExpr.hpp similarity index 97% rename from include/Ast/FunctionLiteralExpr.hpp rename to src/Ast/Expressions/FunctionLiteralExpr.hpp index 014f4e3..6742ee5 100644 --- a/include/Ast/FunctionLiteralExpr.hpp +++ b/src/Ast/Expressions/FunctionLiteralExpr.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace Fig::Ast diff --git a/include/Ast/InitExpr.hpp b/src/Ast/Expressions/InitExpr.hpp similarity index 100% rename from include/Ast/InitExpr.hpp rename to src/Ast/Expressions/InitExpr.hpp diff --git a/src/Ast/Expressions/PostfixExprs.hpp b/src/Ast/Expressions/PostfixExprs.hpp new file mode 100644 index 0000000..6edcecc --- /dev/null +++ b/src/Ast/Expressions/PostfixExprs.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace Fig::Ast +{ + // actually, function call is postfix, too + // but it's too long, so use a single file (FunctionCall.hpp) + + class MemberExprAst final : public ExpressionAst + { + public: + Expression base; + FString member; + + MemberExprAst() + { + type = AstType::MemberExpr; + } + + MemberExprAst(Expression _base, FString _member) : + base(std::move(_base)), member(std::move(_member)) + { + type = AstType::MemberExpr; + } + }; + + using MemberExpr = std::shared_ptr; + + class IndexExprAst final : public ExpressionAst + { + public: + Expression base; + Expression index; + + IndexExprAst() + { + type = AstType::IndexExpr; + } + + IndexExprAst(Expression _base, Expression _index) : + base(std::move(_base)), index(std::move(_index)) + { + type = AstType::IndexExpr; + } + }; + + using IndexExpr = std::shared_ptr; +}; // namespace Fig::Ast \ No newline at end of file diff --git a/include/Ast/TernaryExpr.hpp b/src/Ast/Expressions/TernaryExpr.hpp similarity index 100% rename from include/Ast/TernaryExpr.hpp rename to src/Ast/Expressions/TernaryExpr.hpp diff --git a/include/Ast/UnaryExpr.hpp b/src/Ast/Expressions/UnaryExpr.hpp similarity index 100% rename from include/Ast/UnaryExpr.hpp rename to src/Ast/Expressions/UnaryExpr.hpp diff --git a/include/Ast/ValueExpr.hpp b/src/Ast/Expressions/ValueExpr.hpp similarity index 94% rename from include/Ast/ValueExpr.hpp rename to src/Ast/Expressions/ValueExpr.hpp index 31adf45..5e2ec3f 100644 --- a/include/Ast/ValueExpr.hpp +++ b/src/Ast/Expressions/ValueExpr.hpp @@ -2,7 +2,7 @@ #include -#include +#include namespace Fig::Ast { diff --git a/include/Ast/VarExpr.hpp b/src/Ast/Expressions/VarExpr.hpp similarity index 100% rename from include/Ast/VarExpr.hpp rename to src/Ast/Expressions/VarExpr.hpp diff --git a/include/Ast/ControlSt.hpp b/src/Ast/Statements/ControlSt.hpp similarity index 100% rename from include/Ast/ControlSt.hpp rename to src/Ast/Statements/ControlSt.hpp diff --git a/include/Ast/ExpressionStmt.hpp b/src/Ast/Statements/ExpressionStmt.hpp similarity index 100% rename from include/Ast/ExpressionStmt.hpp rename to src/Ast/Statements/ExpressionStmt.hpp diff --git a/include/Ast/ForSt.hpp b/src/Ast/Statements/ForSt.hpp similarity index 100% rename from include/Ast/ForSt.hpp rename to src/Ast/Statements/ForSt.hpp diff --git a/include/Ast/FunctionDefSt.hpp b/src/Ast/Statements/FunctionDefSt.hpp similarity index 96% rename from include/Ast/FunctionDefSt.hpp rename to src/Ast/Statements/FunctionDefSt.hpp index c0105b6..7d6674e 100644 --- a/include/Ast/FunctionDefSt.hpp +++ b/src/Ast/Statements/FunctionDefSt.hpp @@ -2,9 +2,8 @@ #include #include -#include -#include +#include namespace Fig::Ast { diff --git a/include/Ast/IfSt.hpp b/src/Ast/Statements/IfSt.hpp similarity index 100% rename from include/Ast/IfSt.hpp rename to src/Ast/Statements/IfSt.hpp diff --git a/include/Ast/ImplementSt.hpp b/src/Ast/Statements/ImplementSt.hpp similarity index 100% rename from include/Ast/ImplementSt.hpp rename to src/Ast/Statements/ImplementSt.hpp diff --git a/include/Ast/StructDefSt.hpp b/src/Ast/Statements/StructDefSt.hpp similarity index 98% rename from include/Ast/StructDefSt.hpp rename to src/Ast/Statements/StructDefSt.hpp index 48761c5..9a81bdf 100644 --- a/include/Ast/StructDefSt.hpp +++ b/src/Ast/Statements/StructDefSt.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/include/Ast/VarDef.hpp b/src/Ast/Statements/VarDef.hpp similarity index 100% rename from include/Ast/VarDef.hpp rename to src/Ast/Statements/VarDef.hpp diff --git a/include/Ast/WhileSt.hpp b/src/Ast/Statements/WhileSt.hpp similarity index 100% rename from include/Ast/WhileSt.hpp rename to src/Ast/Statements/WhileSt.hpp diff --git a/src/Ast/ast.hpp b/src/Ast/ast.hpp new file mode 100644 index 0000000..fac714b --- /dev/null +++ b/src/Ast/ast.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/include/Ast/astBase.hpp b/src/Ast/astBase.hpp similarity index 95% rename from include/Ast/astBase.hpp rename to src/Ast/astBase.hpp index 056f4c6..f378e36 100644 --- a/include/Ast/astBase.hpp +++ b/src/Ast/astBase.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include @@ -20,15 +20,21 @@ namespace Fig::Ast /* Expression */ ValueExpr, VarExpr, - FunctionCall, LambdaExpr, UnaryExpr, BinaryExpr, TernaryExpr, - ListExpr, // [] - TupleExpr, // () - MapExpr, // {} - InitExpr, // struct{} + + /* Postfix */ + MemberExpr, // a.b + IndexExpr, // a[b] + FunctionCall, // a() + + /* Literals */ + ListExpr, // [1, "2", 3 + TupleExpr, // (1, 2, 3) + MapExpr, // {a: 1} + InitExpr, // struct{"123", 456} FunctionLiteralExpr, /* Statement */ @@ -63,7 +69,7 @@ namespace Fig::Ast // {AstType::UnaryExpr, FString(u8"UnaryExpr")}, // {AstType::BinaryExpr, FString(u8"BinaryExpr")}, // {AstType::TernaryExpr, FString(u8"TernaryExpr")}, - + // {AstType::InitExpr, FString(u8"InitExpr")}, // /* Statement */ @@ -112,8 +118,7 @@ namespace Fig::Ast virtual FString typeName() { return FString::fromStringView( - FStringView::fromBasicStringView(magic_enum::enum_name(type)) - ); + FStringView::fromBasicStringView(magic_enum::enum_name(type))); } virtual FString toString() { @@ -216,9 +221,6 @@ namespace Fig::Ast // 赋值表达式 Assign, // = // Walrus, // := - - // 点运算符 . - Dot, }; static const std::unordered_set unaryOps{ @@ -251,7 +253,8 @@ namespace Fig::Ast Operator::ShiftRight, // Operator::Walrus, - Operator::Dot}; + // Operator::Dot + }; static const std::unordered_set ternaryOps{Operator::TernaryCond}; static const std::unordered_map TokenToOp{ @@ -291,9 +294,7 @@ namespace Fig::Ast // 赋值表达式 {TokenType::Assign, Operator::Assign}, - // {TokenType::Walrus, Operator::Walrus}, - // 点运算符 - {TokenType::Dot, Operator::Dot}, + // {TokenType::Walrus, Operator::Walrus}, }; // := inline bool isOpUnary(Operator op) diff --git a/include/Ast/functionParameters.hpp b/src/Ast/functionParameters.hpp similarity index 96% rename from include/Ast/functionParameters.hpp rename to src/Ast/functionParameters.hpp index 376bf46..1592527 100644 --- a/include/Ast/functionParameters.hpp +++ b/src/Ast/functionParameters.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include namespace Fig::Ast { diff --git a/include/context.hpp b/src/Context/context.hpp similarity index 75% rename from include/context.hpp rename to src/Context/context.hpp index 5a40366..6ba5aa1 100644 --- a/include/context.hpp +++ b/src/Context/context.hpp @@ -4,32 +4,31 @@ #include #include -#include -#include -#include +#include +#include +#include namespace Fig { + class Context : public std::enable_shared_from_this { private: FString scopeName; - std::unordered_map varTypes; - std::unordered_map variables; - std::unordered_map ams; + std::unordered_map> variables; std::unordered_map functions; std::unordered_map functionNames; - std::unordered_map structTypeNames; + public: ContextPtr parent; Context(const Context &) = default; Context(const FString &name, ContextPtr p = nullptr) : scopeName(name), parent(p) {} - Context(const FString &name, std::unordered_map types, std::unordered_map vars, std::unordered_map _ams) : - scopeName(std::move(name)), varTypes(std::move(types)), variables(std::move(vars)), ams(std::move(_ams)) {} + Context(const FString &name, std::unordered_map types, std::unordered_map> vars, std::unordered_map _ams) : + scopeName(std::move(name)), variables(std::move(vars)) {} void setParent(ContextPtr _parent) { @@ -46,11 +45,9 @@ namespace Fig return scopeName; } - void merge(const Context& c) + void merge(const Context &c) { - varTypes.insert(c.varTypes.begin(), c.varTypes.end()); variables.insert(c.variables.begin(), c.variables.end()); - ams.insert(c.ams.begin(), c.ams.end()); functions.insert(c.functions.begin(), c.functions.end()); functionNames.insert(c.functionNames.begin(), c.functionNames.end()); structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end()); @@ -61,20 +58,20 @@ namespace Fig return functions; } - std::optional get(const FString &name) + std::shared_ptr get(const FString &name) { auto it = variables.find(name); if (it != variables.end()) return it->second; if (parent) return parent->get(name); - return std::nullopt; + throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); } AccessModifier getAccessModifier(const FString &name) { if (variables.contains(name)) { - return ams[name]; + return variables[name]->am; } else if (parent != nullptr) { @@ -85,33 +82,17 @@ namespace Fig throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); } } - ContextPtr createCopyWithPublicVariables() - { - std::unordered_map _varTypes; - std::unordered_map _variables; - std::unordered_map _ams; - for (const auto &p : this->variables) - { - if (isVariablePublic(p.first)) - { - _variables[p.first] = std::make_shared(*p.second); // copy - _varTypes[p.first] = varTypes[p.first]; - _ams[p.first] = ams[p.first]; - } - } - return std::make_shared(this->scopeName, _varTypes, _variables, _ams); - } bool isVariableMutable(const FString &name) { AccessModifier am = getAccessModifier(name); // may throw - return am == AccessModifier::Normal or am == AccessModifier::Public; + return !isAccessConst(am); } bool isVariablePublic(const FString &name) { AccessModifier am = getAccessModifier(name); // may throw - return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal; + return isAccessPublic(am); } - void set(const FString &name, const Object &value) + void set(const FString &name, ObjectPtr value) { if (variables.contains(name)) { @@ -119,7 +100,7 @@ namespace Fig { throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString()))); } - variables[name] = std::make_shared(value); + variables[name]->value = value; } else if (parent != nullptr) { @@ -130,11 +111,11 @@ namespace Fig throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); } } - void _update(const FString &name, const Object &value) + void _update(const FString &name, ObjectPtr value) { if (variables.contains(name)) { - variables[name] = std::make_shared(value); + variables[name]->value = value; } else if (parent != nullptr) { @@ -151,10 +132,11 @@ namespace Fig { throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString()))); } - variables[name] = std::make_shared(*value); - varTypes[name] = ti; - ams[name] = am; - + variables[name] = std::make_shared( + name, + value, + ti, + am); if (ti == ValueType::Function and value->getTypeInfo() == ValueType::Function) { auto &fn = value->as(); @@ -234,15 +216,7 @@ namespace Fig TypeInfo getTypeInfo(const FString &name) { - if (varTypes.contains(name)) - { - return varTypes[name]; - } - else if (parent != nullptr) - { - return parent->getTypeInfo(name); - } - throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); + return get(name)->declaredType; } bool isInFunctionContext() { @@ -262,8 +236,7 @@ namespace Fig ContextPtr ctx = shared_from_this(); while (ctx) { - if (ctx->getScopeName().find(u8"getScopeName().find(u8"getScopeName().find(u8"getScopeName().find(u8" +#include #include #include diff --git a/include/fig_string.hpp b/src/Core/fig_string.hpp similarity index 100% rename from include/fig_string.hpp rename to src/Core/fig_string.hpp diff --git a/include/utf8_iterator.hpp b/src/Core/utf8_iterator.hpp similarity index 100% rename from include/utf8_iterator.hpp rename to src/Core/utf8_iterator.hpp diff --git a/src/waring.cpp b/src/Core/warning.cpp similarity index 89% rename from src/waring.cpp rename to src/Core/warning.cpp index 93693a8..b1fa626 100644 --- a/src/waring.cpp +++ b/src/Core/warning.cpp @@ -1,4 +1,4 @@ -#include +#include namespace Fig { diff --git a/include/warning.hpp b/src/Core/warning.hpp similarity index 92% rename from include/warning.hpp rename to src/Core/warning.hpp index a71d316..bf63520 100644 --- a/include/warning.hpp +++ b/src/Core/warning.hpp @@ -1,8 +1,8 @@ #pragma once -#include +#include -#include +#include #include namespace Fig diff --git a/include/error.hpp b/src/Error/error.hpp similarity index 99% rename from include/error.hpp rename to src/Error/error.hpp index 2471b30..cace7d7 100644 --- a/include/error.hpp +++ b/src/Error/error.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/errorLog.hpp b/src/Error/errorLog.hpp similarity index 99% rename from include/errorLog.hpp rename to src/Error/errorLog.hpp index 355c648..84f8e61 100644 --- a/include/errorLog.hpp +++ b/src/Error/errorLog.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp new file mode 100644 index 0000000..1acecfc --- /dev/null +++ b/src/Evaluator/evaluator.cpp @@ -0,0 +1,1029 @@ +#include +#include +#include +#include +#include + +#include + +namespace Fig +{ + LvObject Evaluator::evalVarExpr(Ast::VarExpr var, ContextPtr ctx) + { + const FString &name = var->name; + if (!ctx->contains(name)) + { + throw EvaluatorError(u8"UndeclaredIdentifierError", name, var); + } + return LvObject(ctx->get(name)); + } + LvObject Evaluator::evalMemberExpr(Ast::MemberExpr me, ContextPtr ctx) + { + LvObject base = evalLv(me->base, ctx); + RvObject baseVal = base.get(); + const FString &member = me->member; + if (baseVal->getTypeInfo() != ValueType::StructInstance) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "`{}` isn't a struct", + base.name().toBasicString()), + me->base); + } + const StructInstance &si = baseVal->as(); + if (!si.localContext->containsInThisScope(member) || !si.localContext->isVariablePublic(member)) + { + throw EvaluatorError( + u8"NoAttributeError", + std::format( + "`{}` has not attribute '{}'", + baseVal->toString().toBasicString(), + member.toBasicString()), + me->base); + } + return LvObject(si.localContext->get(member)); + } + LvObject Evaluator::evalIndexExpr(Ast::IndexExpr ie, ContextPtr ctx) + { + LvObject base = evalLv(ie->base, ctx); + RvObject index = eval(ie->index, ctx); + + const TypeInfo &type = base.declaredType(); + + if (type != ValueType::List + && type != ValueType::Tuple + && type != ValueType::Map) + { + throw EvaluatorError( + u8"NoSubscriptableError", + std::format( + "`{}` object is not subscriptable", + base.declaredType().toString().toBasicString()), + ie->base); + } + // TODO + return LvObject(); + } + LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx) + { + using Ast::Operator; + using Ast::AstType; + switch (exp->getType()) + { + case AstType::VarExpr: { + 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); + assert(me != nullptr); + return evalMemberExpr(me, ctx); + } + case AstType::IndexExpr: { + 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); + } + } + } + + RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) + { + using Ast::Operator; + Operator op = bin->op; + Ast::Expression lexp = bin->lexp, + rexp = bin->rexp; + switch (op) + { + case Operator::Add: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs + *rhs); + } + case Operator::Subtract: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs - *rhs); + }; + case Operator::Multiply: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared((*lhs) * (*rhs)); + }; + case Operator::Divide: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs / *rhs); + }; + case Operator::Modulo: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs % *rhs); + }; + case Operator::Power: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(power(*lhs, *rhs)); + } + case Operator::And: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs && *rhs); + }; + case Operator::Or: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs || *rhs); + }; + case Operator::Equal: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs == *rhs); + } + case Operator::NotEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs != *rhs); + } + case Operator::Less: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs < *rhs); + } + case Operator::LessEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs <= *rhs); + } + case Operator::Greater: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs > *rhs); + } + case Operator::GreaterEqual: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(*lhs >= *rhs); + } + + case Operator::BitAnd: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(bit_and(*lhs, *rhs)); + } + case Operator::BitOr: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(bit_or(*lhs, *rhs)); + } + case Operator::BitXor: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(bit_xor(*lhs, *rhs)); + } + case Operator::ShiftLeft: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(shift_left(*lhs, *rhs)); + } + case Operator::ShiftRight: { + ObjectPtr lhs = eval(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + return std::make_shared(shift_right(*lhs, *rhs)); + } + case Operator::Assign: { + LvObject lv = evalLv(lexp, ctx); + ObjectPtr rhs = eval(rexp, ctx); + lv.set(rhs); + return rhs; + } + default: + throw EvaluatorError(u8"UnsupportedOp", std::format("Unsupport operator '{}' for binary", magic_enum::enum_name(op)), bin); + } + } + RvObject Evaluator::evalUnary(Ast::UnaryExpr un, ContextPtr ctx) + { + using Ast::Operator; + Operator op = un->op; + Ast::Expression exp = un->exp; + ObjectPtr value = eval(exp, ctx); + switch (op) + { + case Operator::Not: { + return std::make_shared(!(*value)); + } + case Operator::Subtract: { + return std::make_shared(-(*value)); + } + case Operator::BitNot: { + return std::make_shared(bit_not((*value))); + } + + default: { + throw EvaluatorError( + u8"UnsupportedOpError", + std::format("Unsupported op '{}' for unary expression", + magic_enum::enum_name(op)), + un); + } + } + } + RvObject Evaluator::evalTernary(Ast::TernaryExpr te, ContextPtr ctx) + { + RvObject condVal = eval(te->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format("Condition must be boolean, got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + te->condition); + } + ValueType::BoolClass cond = condVal->as(); + 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) + { + const Function &fnStruct = fn; + Ast::FunctionCallArgs evaluatedArgs; + if (fnStruct.isBuiltin) + { + for (const auto &argExpr : fnArgs.argv) + { + evaluatedArgs.argv.push_back(eval(argExpr, ctx)); + } + 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()); + } + return fnStruct.builtin(evaluatedArgs.argv); + } + + // check argument, all types of parameters + Ast::FunctionParameters fnParas = fnStruct.paras; + if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) + { + throw EvaluatorError( + u8"ArgumentMismatchError", + std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength()), fnArgs.argv.back()); + } + + // 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 + ObjectPtr argVal = eval(fnArgs.argv[i], ctx); + TypeInfo actualType = argVal->getTypeInfo(); + if (expectedType != actualType and expectedType != ValueType::Any) + { + 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()), + fnArgs.argv[i]); + } + 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; + + ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + if (expectedType != defaultVal->getTypeInfo() and expectedType != ValueType::Any) + { + 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(), + defaultVal->getTypeInfo().toString().toBasicString(), + expectedType.toString().toBasicString()), + fnArgs.argv[i]); + } + + ObjectPtr argVal = eval(fnArgs.argv[i], ctx); + TypeInfo actualType = argVal->getTypeInfo(); + if (expectedType != actualType and expectedType != ValueType::Any) + { + 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()), + fnArgs.argv[i]); + } + evaluatedArgs.argv.push_back(argVal); + } + // default parameters filling + for (; i < fnParas.size(); i++) + { + size_t defParamIndex = i - fnParas.posParas.size(); + ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second, ctx); + evaluatedArgs.argv.push_back(defaultVal); + } + // create new context for function call + auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), + fnStruct.closureContext); + // 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; + newContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]); + } + // execute function body + ObjectPtr retVal = Object::getNullInstance(); + for (const auto &stmt : fnStruct.body->stmts) + { + StatementResult sr = evalStatement(stmt, newContext); + if (sr.shouldReturn()) + { + retVal = sr.result; + break; + } + } + if (fnStruct.retType != retVal->getTypeInfo() and fnStruct.retType != ValueType::Any) + { + throw EvaluatorError( + u8"ReturnTypeMismatchError", + std::format("Function '{}' expects return type '{}', but got type '{}'", + fnName.toBasicString(), + fnStruct.retType.toString().toBasicString(), + retVal->getTypeInfo().toString().toBasicString()), + fnStruct.body); + } + return retVal; + } + + RvObject Evaluator::eval(Ast::Expression exp, ContextPtr ctx) + { + using Ast::AstType; + AstType type = exp->getType(); + switch (type) + { + case AstType::ValueExpr: { + auto val = std::dynamic_pointer_cast(exp); + assert(val != nullptr); + return val->val; + } + case AstType::VarExpr: { + auto varExpr = std::dynamic_pointer_cast(exp); + assert(varExpr != nullptr); + return evalVarExpr(varExpr, ctx).get(); // LvObject -> RvObject + } + case AstType::BinaryExpr: { + auto bin = std::dynamic_pointer_cast(exp); + assert(bin != nullptr); + return evalBinary(bin, ctx); + } + case AstType::UnaryExpr: { + auto un = std::dynamic_pointer_cast(exp); + assert(un != nullptr); + return evalUnary(un, ctx); + } + case AstType::TernaryExpr: { + auto te = std::dynamic_pointer_cast(exp); + assert(te != nullptr); + return evalTernary(te, ctx); + } + case AstType::MemberExpr: + case AstType::IndexExpr: + return evalLv(exp, ctx).get(); + + case AstType::FunctionCall: { + auto fnCall = std::dynamic_pointer_cast(exp); + assert(fnCall != nullptr); + + Ast::Expression callee = fnCall->callee; + ObjectPtr fnObj = eval(callee, ctx); + if (fnObj->getTypeInfo() != ValueType::Function) + { + throw EvaluatorError( + u8"ObjectNotCallable", + std::format("Object `{}` isn't callable", + fnObj->toString().toBasicString()), + callee); + } + const Function &fn = fnObj->as(); + size_t fnId = fn.id; + const auto &fnNameOpt = ctx->getFunctionName(fnId); + const FString &fnName = (fnNameOpt ? *fnNameOpt : u8""); + return evalFunctionCall(fn, fnCall->arg, fnName, ctx); + } + case AstType::FunctionLiteralExpr: { + auto fnLiteral = std::dynamic_pointer_cast(exp); + assert(fnLiteral != nullptr); + + Ast::BlockStatement body = nullptr; + if (fnLiteral->isExprMode()) + { + Ast::Expression exprBody = fnLiteral->getExprBody(); + assert(exprBody != nullptr); + + const Ast::AstAddressInfo &aai = exprBody->getAAI(); + Ast::Return st = std::make_shared(exprBody); + st->setAAI(aai); + + body = std::make_shared(); + body->stmts.push_back(st); // convert to Ast::Statement + body->setAAI(aai); + } + else + { + body = fnLiteral->getBlockBody(); + assert(body != nullptr); + } + Function fn( + fnLiteral->paras, + ValueType::Any, + body, + ctx + /* + pass the ctx(fnLiteral eval context) as closure context + */ + ); + return std::make_shared(std::move(fn)); + } + case AstType::InitExpr: { + 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()), + initExpr); + } + ObjectPtr structTypeVal = ctx->get(initExpr->structName)->value; + if (!structTypeVal->is()) + { + throw EvaluatorError( + u8"NotAStructTypeError", + std::format( + "'{}' is not a structure type", + initExpr->structName.toBasicString()), + initExpr); + } + const StructType &structT = structTypeVal->as(); + ContextPtr defContext = structT.defContext; // definition context + // check init args + + size_t minArgs = 0; + size_t maxArgs = structT.fields.size(); + + for (auto &f : structT.fields) + { + if (f.defaultValue == nullptr) minArgs++; + } + + size_t got = initExpr->args.size(); + if (got > maxArgs || got < minArgs) + { + throw EvaluatorError( + u8"StructInitArgumentMismatchError", + std::format( + "Structure '{}' expects {} to {} fields, but {} were provided", + initExpr->structName.toBasicString(), + minArgs, + maxArgs, + initExpr->args.size()), + initExpr); + } + + std::vector> evaluatedArgs; + for (const auto &[argName, argExpr] : initExpr->args) + { + evaluatedArgs.push_back({argName, eval(argExpr, ctx)}); + } + ContextPtr instanceCtx = std::make_shared( + 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}; + */ + { + using enum Ast::InitExprAst::InitMode; + if (initExpr->initMode == Positional) + { + for (size_t i = 0; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + const FString &fieldName = field.name; + const TypeInfo &expectedType = field.type; + if (i >= evaluatedArgs.size()) + { + // 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 + + // type check + if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format( + "In structure '{}', field '{}' expects type '{}', but got type '{}'", + initExpr->structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + defaultVal->getTypeInfo().toString().toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, expectedType, field.am, defaultVal); + continue; + } + + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (expectedType != argVal->getTypeInfo() && expectedType != ValueType::Any) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + initExpr->structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + argVal->getTypeInfo().toString().toBasicString()), + initExpr); + } + instanceCtx->def(fieldName, expectedType, field.am, argVal); + } + } + else + { + // named / shorthand init + 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); + if (instanceCtx->containsInThisScope(fieldName)) + { + throw EvaluatorError( + u8"StructFieldRedeclarationError", + std::format( + "Field '{}' already initialized in structure '{}'", + fieldName.toBasicString(), + initExpr->structName.toBasicString()), + initExpr); + } + 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 + + // type check + const TypeInfo &expectedType = field.type; + if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format( + "In structure '{}', field '{}' expects type '{}', but got type '{}'", + initExpr->structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + defaultVal->getTypeInfo().toString().toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, field.type, field.am, defaultVal); + continue; + } + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (field.type != argVal->getTypeInfo() && field.type != ValueType::Any) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format( + "In structure '{}', field '{}' expects type '{}', but got type '{}'", + initExpr->structName.toBasicString(), + fieldName.toBasicString(), + field.type.toString().toBasicString(), + argVal->getTypeInfo().toString().toBasicString()), + initExpr); + } + 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 + )); + } + return std::make_shared(StructInstance(structT.id, instanceCtx)); + } + + default: + assert(false); + } + } + 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; + } + } + return sr; + } + StatementResult Evaluator::evalStatement(Ast::Statement stmt, ContextPtr ctx) + { + using enum Ast::AstType; + switch (stmt->getType()) + { + case VarDefSt: { + auto varDef = std::dynamic_pointer_cast(stmt); + assert(varDef != nullptr); + + if (ctx->containsInThisScope(varDef->name)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Variable `{}` already declared in this scope", + varDef->name.toBasicString()), + varDef); + } + + RvObject value = nullptr; + if (varDef->expr) + { + value = eval(varDef->expr, ctx); + } + TypeInfo declaredType; // default is Any + const FString &declaredTypeName = varDef->typeName; + if (declaredTypeName == Parser::varDefTypeFollowed) + { + declaredType = value->getTypeInfo(); + } + else if (!declaredTypeName.empty()) + { + declaredType = TypeInfo(declaredTypeName); + if (value != nullptr && value->getTypeInfo() != declaredType && declaredType != ValueType::Any) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Variable `{}` expects init-value type `{}`, but got '{}'", + varDef->name.toBasicString(), + declaredTypeName.toBasicString(), + value->getTypeInfo().toString().toBasicString()), + varDef->expr); + } + else if (value == nullptr) + { + value = std::make_shared( + 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); + return StatementResult::normal(); + } + + case FunctionDefSt: { + auto fnDef = std::dynamic_pointer_cast(stmt); + assert(fnDef != nullptr); + + const FString &fnName = fnDef->name; + if (ctx->containsInThisScope(fnName)) + { + throw EvaluatorError( + u8"RedeclarationError", + 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)); + return StatementResult::normal(); + } + + case StructSt: { + auto stDef = std::dynamic_pointer_cast(stmt); + assert(stDef != nullptr); + + if (ctx->containsInThisScope(stDef->name)) + { + throw EvaluatorError( + u8"RedeclarationError", + std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString()), + stDef); + } + std::vector fields; + std::vector _fieldNames; + for (Ast::StructDefField field : stDef->fields) + { + if (Utils::vectorContains(field.fieldName, _fieldNames)) + { + throw EvaluatorError( + u8"RedeclarationError", + 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)); + } + 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); + } + evalStatement(st, defContext); // function def st + } + + AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); + TypeInfo _(stDef->name, true); // register type name + ctx->def( + stDef->name, + ValueType::StructType, + am, + std::make_shared(StructType( + defContext, + fields))); + return StatementResult::normal(); + } + + case IfSt: { + auto ifSt = std::dynamic_pointer_cast(stmt); + ObjectPtr condVal = eval(ifSt->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Condition must be boolean, but got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + ifSt->condition); + } + if (condVal->as()) + { + return evalBlockStatement(ifSt->body, ctx); + } + // else + for (const auto &elif : ifSt->elifs) + { + ObjectPtr elifCondVal = eval(elif->condition, ctx); + throw EvaluatorError( + u8"TypeError", + std::format( + "Condition must be boolean, but got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + ifSt->condition); + if (elifCondVal->as()) + { + return evalBlockStatement(elif->body, ctx); + } + } + if (ifSt->els) + { + return evalBlockStatement(ifSt->els->body, ctx); + } + return StatementResult::normal(); + }; + case WhileSt: { + auto whileSt = std::dynamic_pointer_cast(stmt); + while (true) + { + ObjectPtr condVal = eval(whileSt->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Condition must be boolean, but got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + whileSt->condition); + } + if (!condVal->as()) + { + break; + } + ContextPtr loopContext = std::make_shared( + FString(std::format("", + 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; + } + } + return StatementResult::normal(); + }; + case ForSt: { + auto forSt = std::dynamic_pointer_cast(stmt); + ContextPtr loopContext = std::make_shared( + FString(std::format("", + forSt->getAAI().line, forSt->getAAI().column)), + ctx); // for loop has its own context + + 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 + { + ObjectPtr condVal = eval(forSt->condition, ctx); + if (condVal->getTypeInfo() != ValueType::Bool) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Condition must be boolean, but got '{}'", + condVal->getTypeInfo().toString().toBasicString()), + forSt->condition); + } + if (!condVal->as()) + { + break; + } + iteration++; + ContextPtr iterationContext = std::make_shared( + FString(std::format("", + 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; + } + if (sr.shouldContinue()) + { + // continue to next iteration + continue; + } + evalStatement(forSt->incrementSt, loopContext); // ignore increment statement result + } + return StatementResult::normal(); + } + case ReturnSt: { + auto returnSt = std::dynamic_pointer_cast(stmt); + assert(returnSt != nullptr); + + ObjectPtr returnValue = Object::getNullInstance(); // default is null + if (returnSt->retValue) + returnValue = eval(returnSt->retValue, ctx); + return StatementResult::returnFlow(returnValue); + } + + case BreakSt: { + if (!ctx->parent) + { + throw EvaluatorError( + u8"BreakOutsideLoopError", + u8"`break` statement outside loop", + stmt); + } + if (!ctx->isInLoopContext()) + { + throw EvaluatorError( + u8"BreakOutsideLoopError", + u8"`break` statement outside loop", + stmt); + } + return StatementResult::breakFlow(); + } + + case ContinueSt: { + if (!ctx->parent) + { + throw EvaluatorError( + u8"ContinueOutsideLoopError", + u8"`continue` statement outside loop", + stmt); + } + if (!ctx->isInLoopContext()) + { + throw EvaluatorError( + u8"ContinueOutsideLoopError", + u8"`continue` statement outside loop", + stmt); + } + return StatementResult::continueFlow(); + } + + case ExpressionStmt: { + auto exprStmt = std::dynamic_pointer_cast(stmt); + assert(exprStmt != nullptr); + + return StatementResult::normal(eval(exprStmt->exp, ctx)); + } + + default: + throw RuntimeError(FStringView( + std::format("Feature stmt {} unsupported yet", + magic_enum::enum_name(stmt->getType())))); + } + } + StatementResult Evaluator::Run(std::vector asts) + { + using Ast::AstType; + StatementResult sr = StatementResult::normal(); + for (auto &ast : asts) + { + Ast::Expression exp; + if ((exp = std::dynamic_pointer_cast(ast))) + { + sr = StatementResult::normal(eval(exp, global)); + } + else + { + // statement + Ast::Statement stmt = std::dynamic_pointer_cast(ast); + assert(stmt != nullptr); + sr = evalStatement(stmt, global); + if (!sr.isNormal()) + { + return sr; + } + } + } + return sr; + } + + void Evaluator::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 new file mode 100644 index 0000000..c8565de --- /dev/null +++ b/src/Evaluator/evaluator.hpp @@ -0,0 +1,116 @@ +#include + +#include +#include +#include + + +namespace Fig +{ + struct StatementResult + { + ObjectPtr result; + enum class Flow + { + Normal, + Return, + Break, + Continue + } flow; + + StatementResult(ObjectPtr val, Flow f = Flow::Normal) : + result(val), flow(f) + { + } + + static StatementResult normal(ObjectPtr val = Object::getNullInstance()) + { + return StatementResult(val, Flow::Normal); + } + static StatementResult returnFlow(ObjectPtr val) + { + return StatementResult(val, Flow::Return); + } + static StatementResult breakFlow() + { + return StatementResult(Object::getNullInstance(), Flow::Break); + } + static StatementResult continueFlow() + { + return StatementResult(Object::getNullInstance(), Flow::Continue); + } + + 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; } + }; + + class Evaluator + { + private: + ContextPtr global; + public: + + void SetGlobalContext(ContextPtr ctx) + { + assert(ctx != nullptr); + global = ctx; + } + + void CreateGlobalContext() + { + global = std::make_shared( + FString(u8"")); + } + + void RegisterBuiltins() + { + assert(global != nullptr); + + for (auto &[name, fn] : Builtins::builtinFunctions) + { + int argc = Builtins::getBuiltinFunctionParamCount(name); + Function f(fn, argc); + global->def( + name, + ValueType::Function, + AccessModifier::Const, + std::make_shared(f) + ); + } + + for (auto &[name, val] : Builtins::builtinValues) + { + global->def( + name, + val->getTypeInfo(), + AccessModifier::Const, + val + ); + } + } + /* Left-value eval*/ + LvObject evalVarExpr(Ast::VarExpr, ContextPtr); + LvObject evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b + LvObject evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b] + + LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b] + + /* Right-value eval*/ + + RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *.... + RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr + RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr + + RvObject evalFunctionCall(const Function&, const Ast::FunctionArguments&, const FString& ,ContextPtr); // function call + RvObject eval(Ast::Expression, ContextPtr); + + StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block + StatementResult evalStatement(Ast::Statement, ContextPtr); // statement + + StatementResult Run(std::vector); // Entry + + void printStackTrace(); + }; +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/evaluator_error.hpp b/src/Evaluator/evaluator_error.hpp new file mode 100644 index 0000000..14fabca --- /dev/null +++ b/src/Evaluator/evaluator_error.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +namespace Fig +{ + class EvaluatorError final : public AddressableError + { + public: + FString typeName; + using AddressableError::AddressableError; + EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) + { + message = FStringView::fromBasicStringView(msg.toBasicString()); + line = ast->getAAI().line; + column = ast->getAAI().column; + + src_loc = std::move(loc); + + typeName = std::move(_typeName); + + } + EvaluatorError(FString _typeName, std::string_view msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) + { + message = FStringView::fromBasicStringView(msg); + line = ast->getAAI().line; + column = ast->getAAI().column; + + src_loc = std::move(loc); + + typeName = std::move(_typeName); + } + + virtual FString getErrorType() const override + { + return typeName; + } + + + }; +}; \ No newline at end of file diff --git a/src/lexer.cpp b/src/Lexer/lexer.cpp similarity index 98% rename from src/lexer.cpp rename to src/Lexer/lexer.cpp index a3c2c64..7571b4e 100644 --- a/src/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -1,10 +1,10 @@ -#include -#include -#include -#include +#include +#include +#include +#include -#include -#include +#include +#include namespace Fig { @@ -66,7 +66,7 @@ namespace Fig {FString(u8"func"), TokenType::Function}, {FString(u8"var"), TokenType::Variable}, {FString(u8"const"), TokenType::Const}, - {FString(u8"final"), TokenType::Final}, + // {FString(u8"final"), TokenType::Final}, {FString(u8"while"), TokenType::While}, {FString(u8"for"), TokenType::For}, {FString(u8"if"), TokenType::If}, diff --git a/include/lexer.hpp b/src/Lexer/lexer.hpp similarity index 93% rename from include/lexer.hpp rename to src/Lexer/lexer.hpp index 6d656fc..bf348a9 100644 --- a/include/lexer.hpp +++ b/src/Lexer/lexer.hpp @@ -6,11 +6,11 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include namespace Fig { diff --git a/include/builtins.hpp b/src/Module/builtins.hpp similarity index 99% rename from include/builtins.hpp rename to src/Module/builtins.hpp index 4e1442b..cea9fca 100644 --- a/include/builtins.hpp +++ b/src/Module/builtins.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/src/Module/module.hpp b/src/Module/module.hpp new file mode 100644 index 0000000..1824d7b --- /dev/null +++ b/src/Module/module.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include +#include + +namespace Fig +{ + // class Module + // { + // public: + // const FString name; + // const FString spec; + // const FString path; + + // std::shared_ptr context; // module-level context + + // /* + + // import module -> automatically create a module context and call function `init` if exists + // all global functions, variables, structs, etc will be stored in module context + // then module context will be linked to the current context + + // */ + // Module(const FString &moduleName, const FString &moduleSpec, const FString &modulePath) : + // name(moduleName), spec(moduleSpec), path(modulePath) + // { + // context = std::make_shared(FString(std::format("", name.toBasicString())), nullptr); + // } + + // bool hasSymbol(const FString &symbolName) + // { + // return context->contains(symbolName); + // } + // Object getSymbol(const FString &symbolName) + // { + // auto valOpt = context->get(symbolName); + // if (!valOpt.has_value()) + // { + // throw RuntimeError(FStringView(std::format("Symbol '{}' not found in module '{}'", symbolName.toBasicString(), name.toBasicString()))); + // } + // return valOpt.value(); + // } + // }; +}; \ No newline at end of file diff --git a/src/parser.cpp b/src/Parser/parser.cpp similarity index 96% rename from src/parser.cpp rename to src/Parser/parser.cpp index b3e1ad9..4c3f56a 100644 --- a/src/parser.cpp +++ b/src/Parser/parser.cpp @@ -1,4 +1,4 @@ -#include +#include namespace Fig { @@ -39,8 +39,8 @@ namespace Fig // 海象运算符 // {Ast::Operator::Walrus, {2, 1}}, // 右结合 - // 点运算符 - {Ast::Operator::Dot, {40, 41}}, + // // 点运算符 + // {Ast::Operator::Dot, {40, 41}}, }; Ast::VarDef Parser::__parseVarDef(bool isPublic) @@ -244,13 +244,6 @@ namespace Fig 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(); @@ -297,7 +290,7 @@ namespace Fig } else if (isThis(TokenType::Public)) { - if (isNext(TokenType::Const) or isNext(TokenType::Final)) + if (isNext(TokenType::Const)) { next(); fields.push_back(__parseStructField(true)); @@ -334,7 +327,7 @@ namespace Fig next(); // consume `struct` stmts.push_back(__parseStructDef(false)); } - else if (isThis(TokenType::Const) or isThis(TokenType::Final)) + else if (isThis(TokenType::Const)) { fields.push_back(__parseStructField(false)); } @@ -865,12 +858,40 @@ namespace Fig tok = currentToken(); if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break; + /* Postfix */ + if (tok.getType() == TokenType::LeftParen) { lhs = __parseCall(lhs); continue; } + // member access: a.b + if (tok.getType() == TokenType::Dot) + { + next(); // consume '.' + Token idTok = currentToken(); + if (!idTok.isIdentifier()) + throwAddressableError(FStringView(u8"Expected identifier after '.'")); + + FString member = idTok.getValue(); + next(); // consume identifier + + lhs = makeAst(lhs, member); + continue; + } + // index: x[expr] + if (tok.getType() == TokenType::LeftBracket) + { + next(); // consume '[' + auto indexExpr = parseExpression(0, TokenType::RightBracket); + expect(TokenType::RightBracket); + next(); // consume ']' + + lhs = makeAst(lhs, indexExpr); + continue; + } + // ternary if (tok.getType() == TokenType::Question) { diff --git a/include/parser.hpp b/src/Parser/parser.hpp similarity index 99% rename from include/parser.hpp rename to src/Parser/parser.hpp index 4bc2914..a0e49fa 100644 --- a/include/parser.hpp +++ b/src/Parser/parser.hpp @@ -1,9 +1,9 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include #include #include diff --git a/include/token.hpp b/src/Token/token.hpp similarity index 97% rename from include/token.hpp rename to src/Token/token.hpp index fae6465..aa61f39 100644 --- a/include/token.hpp +++ b/src/Token/token.hpp @@ -2,9 +2,9 @@ #include #include -#include +#include -#include +#include namespace Fig { @@ -25,7 +25,7 @@ namespace Fig Function, // func Variable, // var Const, // const - Final, // final + // Final, // final While, // while For, // for If, // if diff --git a/include/AstPrinter.hpp b/src/Utils/AstPrinter.hpp similarity index 98% rename from include/AstPrinter.hpp rename to src/Utils/AstPrinter.hpp index e7ea357..aaedf0a 100644 --- a/include/AstPrinter.hpp +++ b/src/Utils/AstPrinter.hpp @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include using namespace Fig; using namespace Fig::Ast; diff --git a/include/argparse/argparse.hpp b/src/Utils/argparse/argparse.hpp similarity index 100% rename from include/argparse/argparse.hpp rename to src/Utils/argparse/argparse.hpp diff --git a/include/magic_enum/magic_enum.hpp b/src/Utils/magic_enum/magic_enum.hpp similarity index 100% rename from include/magic_enum/magic_enum.hpp rename to src/Utils/magic_enum/magic_enum.hpp diff --git a/include/magic_enum/magic_enum_all.hpp b/src/Utils/magic_enum/magic_enum_all.hpp similarity index 100% rename from include/magic_enum/magic_enum_all.hpp rename to src/Utils/magic_enum/magic_enum_all.hpp diff --git a/include/magic_enum/magic_enum_containers.hpp b/src/Utils/magic_enum/magic_enum_containers.hpp similarity index 100% rename from include/magic_enum/magic_enum_containers.hpp rename to src/Utils/magic_enum/magic_enum_containers.hpp diff --git a/include/magic_enum/magic_enum_flags.hpp b/src/Utils/magic_enum/magic_enum_flags.hpp similarity index 100% rename from include/magic_enum/magic_enum_flags.hpp rename to src/Utils/magic_enum/magic_enum_flags.hpp diff --git a/include/magic_enum/magic_enum_format.hpp b/src/Utils/magic_enum/magic_enum_format.hpp similarity index 100% rename from include/magic_enum/magic_enum_format.hpp rename to src/Utils/magic_enum/magic_enum_format.hpp diff --git a/include/magic_enum/magic_enum_fuse.hpp b/src/Utils/magic_enum/magic_enum_fuse.hpp similarity index 100% rename from include/magic_enum/magic_enum_fuse.hpp rename to src/Utils/magic_enum/magic_enum_fuse.hpp diff --git a/include/magic_enum/magic_enum_iostream.hpp b/src/Utils/magic_enum/magic_enum_iostream.hpp similarity index 100% rename from include/magic_enum/magic_enum_iostream.hpp rename to src/Utils/magic_enum/magic_enum_iostream.hpp diff --git a/include/magic_enum/magic_enum_switch.hpp b/src/Utils/magic_enum/magic_enum_switch.hpp similarity index 100% rename from include/magic_enum/magic_enum_switch.hpp rename to src/Utils/magic_enum/magic_enum_switch.hpp diff --git a/include/magic_enum/magic_enum_utility.hpp b/src/Utils/magic_enum/magic_enum_utility.hpp similarity index 100% rename from include/magic_enum/magic_enum_utility.hpp rename to src/Utils/magic_enum/magic_enum_utility.hpp diff --git a/include/utils.hpp b/src/Utils/utils.hpp similarity index 99% rename from include/utils.hpp rename to src/Utils/utils.hpp index 03743b2..e5b10f2 100644 --- a/include/utils.hpp +++ b/src/Utils/utils.hpp @@ -1,6 +1,6 @@ #pragma once #pragma once -#include +#include #include #include #include diff --git a/include/Value/Type.hpp b/src/Value/Type.hpp similarity index 97% rename from include/Value/Type.hpp rename to src/Value/Type.hpp index 943782e..3b3ac82 100644 --- a/include/Value/Type.hpp +++ b/src/Value/Type.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/src/Value/containers.hpp b/src/Value/containers.hpp new file mode 100644 index 0000000..71ba15c --- /dev/null +++ b/src/Value/containers.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace Fig +{ + +}; \ No newline at end of file diff --git a/include/Value/function.hpp b/src/Value/function.hpp similarity index 97% rename from include/Value/function.hpp rename to src/Value/function.hpp index 908dd87..98b15e2 100644 --- a/include/Value/function.hpp +++ b/src/Value/function.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include diff --git a/include/Value/structInstance.hpp b/src/Value/structInstance.hpp similarity index 95% rename from include/Value/structInstance.hpp rename to src/Value/structInstance.hpp index 8f55ac0..29f5c31 100644 --- a/include/Value/structInstance.hpp +++ b/src/Value/structInstance.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace Fig { diff --git a/include/Value/structType.hpp b/src/Value/structType.hpp similarity index 86% rename from include/Value/structType.hpp rename to src/Value/structType.hpp index f831379..78b1ff8 100644 --- a/include/Value/structType.hpp +++ b/src/Value/structType.hpp @@ -1,11 +1,11 @@ #pragma once -#include -#include +#include +#include #include -#include +#include #include #include @@ -23,16 +23,13 @@ namespace Fig bool isPublic() const { - return am == AccessModifier::Public || am == AccessModifier::PublicConst || am == AccessModifier::PublicFinal; + return am == AccessModifier::Public || am == AccessModifier::PublicConst; } bool isConst() const { return am == AccessModifier::Const || am == AccessModifier::PublicConst; } - bool isFinal() const - { - return am == AccessModifier::Final || am == AccessModifier::PublicFinal; - } + }; struct StructType diff --git a/src/value.cpp b/src/Value/value.cpp similarity index 96% rename from src/value.cpp rename to src/Value/value.cpp index de82964..c0b9f43 100644 --- a/src/value.cpp +++ b/src/Value/value.cpp @@ -1,4 +1,5 @@ -#include +#include +#include // #include @@ -23,7 +24,7 @@ namespace Fig 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 diff --git a/include/value.hpp b/src/Value/value.hpp similarity index 89% rename from include/value.hpp rename to src/Value/value.hpp index cce99df..73fb8d3 100644 --- a/include/value.hpp +++ b/src/Value/value.hpp @@ -19,8 +19,11 @@ namespace Fig inline bool isNumberExceededIntLimit(ValueType::DoubleClass d) { - static constexpr ValueType::DoubleClass intMaxAsDouble = static_cast(std::numeric_limits::max()); - static constexpr ValueType::DoubleClass intMinAsDouble = static_cast(std::numeric_limits::min()); + static constexpr auto intMaxAsDouble = + static_cast(std::numeric_limits::max()); + + static constexpr auto intMinAsDouble = + static_cast(std::numeric_limits::min()); return d > intMaxAsDouble || d < intMinAsDouble; } @@ -45,13 +48,9 @@ namespace Fig data(n) {} Object(const ValueType::IntClass &i) : data(i) {} - Object(const ValueType::DoubleClass &d) + explicit Object(const ValueType::DoubleClass &d) : + data(d) { - ValueType::IntClass casted = static_cast(d); - if (casted == d) - data = casted; - else - data = d; } Object(const ValueType::StringClass &s) : data(s) {} @@ -392,5 +391,63 @@ namespace Fig }; using ObjectPtr = std::shared_ptr; + using RvObject = ObjectPtr; + + struct VariableSlot + { + FString name; + ObjectPtr value; + TypeInfo declaredType; + AccessModifier am; + + bool isRef = false; + std::shared_ptr refTarget; + }; + + struct LvObject + { + std::shared_ptr slot; + + const ObjectPtr& get() const + { + auto s = resolve(slot); + return s->value; + } + + void set(const ObjectPtr& v) + { + auto s = resolve(slot); + if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo()) + { + throw RuntimeError( + FStringView( + std::format("Variable `{}` expects type `{}`, but got '{}'", + s->name.toBasicString(), + s->declaredType.toString().toBasicString(), + v->getTypeInfo().toString().toBasicString()) + ) + ); + } + if (isAccessConst(s->am)) + { + throw RuntimeError(FStringView( + std::format("Variable `{}` is immutable", s->name.toBasicString()) + )); + } + s->value = v; + } + + FString name() const { return resolve(slot)->name; } + TypeInfo declaredType() const { return resolve(slot)->declaredType; } + AccessModifier access() const { return resolve(slot)->am; } + + private: + std::shared_ptr resolve(std::shared_ptr s) const + { + while (s->isRef) s = s->refTarget; + return s; + } + }; + } // namespace Fig diff --git a/include/Value/valueError.hpp b/src/Value/valueError.hpp similarity index 94% rename from include/Value/valueError.hpp rename to src/Value/valueError.hpp index 6851a3c..ccbba06 100644 --- a/include/Value/valueError.hpp +++ b/src/Value/valueError.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace Fig { diff --git a/src/evaluator.cpp b/src/evaluator.cpp deleted file mode 100644 index bfc8ede..0000000 --- a/src/evaluator.cpp +++ /dev/null @@ -1,854 +0,0 @@ -#include -#include -#include - -namespace Fig -{ - ObjectPtr Evaluator::__evalOp(Ast::Operator op, const ObjectPtr &lhs, const ObjectPtr &rhs) - { - using Fig::Ast::Operator; - switch (op) - { - case Operator::Add: return std::make_shared(*lhs + *rhs); - case Operator::Subtract: return std::make_shared(*lhs - *rhs); - case Operator::Multiply: return std::make_shared((*lhs) * (*rhs)); - case Operator::Divide: return std::make_shared(*lhs / *rhs); - case Operator::Modulo: return std::make_shared(*lhs % *rhs); - case Operator::Power: return std::make_shared(power(*lhs, *rhs)); - - case Operator::And: return std::make_shared(*lhs && *rhs); - case Operator::Or: return std::make_shared(*lhs || *rhs); - case Operator::Not: return std::make_shared(!*lhs); - - case Operator::Equal: return std::make_shared(*lhs == *rhs); - case Operator::NotEqual: return std::make_shared(*lhs != *rhs); - case Operator::Less: return std::make_shared(*lhs < *rhs); - case Operator::LessEqual: return std::make_shared(*lhs <= *rhs); - case Operator::Greater: return std::make_shared(*lhs > *rhs); - case Operator::GreaterEqual: return std::make_shared(*lhs >= *rhs); - - case Operator::BitAnd: return std::make_shared(bit_and(*lhs, *rhs)); - case Operator::BitOr: return std::make_shared(bit_or(*lhs, *rhs)); - case Operator::BitXor: return std::make_shared(bit_xor(*lhs, *rhs)); - case Operator::BitNot: return std::make_shared(bit_not(*lhs)); - case Operator::ShiftLeft: return std::make_shared(shift_left(*lhs, *rhs)); - case Operator::ShiftRight: return std::make_shared(shift_right(*lhs, *rhs)); - - case Operator::Assign: { - *lhs = *rhs; - return Object::getNullInstance(); - } - - // case Operator::Walrus: { - // static constexpr char WalrusErrorName[] = "WalrusError"; - // throw EvaluatorError(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now - // } - default: - throw RuntimeError(FStringView(u8"Unsupported operator")); - } - } - - ObjectPtr Evaluator::evalBinary(const Ast::BinaryExpr &binExp) - { - if (binExp->op == Ast::Operator::Dot) - { - const ObjectPtr &lhs = eval(binExp->lexp); - if (!lhs->is()) - { - static constexpr char AccessOpObjectNotStructError[] = "AccessOpObjectNotStructError"; - throw EvaluatorError(FStringView( - std::format("Object not a struct")), - binExp->lexp->getAAI()); - } - const StructInstance &st = lhs->as(); - Ast::VarExpr varExp; - Ast::FunctionCall fnCall; - if ((varExp = std::dynamic_pointer_cast(binExp->rexp))) - { - FString member = varExp->name; - auto structTypeNameOpt = currentContext->getStructName(st.parentId); - if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name")); - FString structTypeName = *structTypeNameOpt; - if (!st.localContext->containsInThisScope(member)) - { - static constexpr char NoAttributeError[] = "NoAttributeError"; - throw EvaluatorError(FStringView( - std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), member.toBasicString())), - binExp->rexp->getAAI()); - } - if (!st.localContext->isVariablePublic(member)) - { - static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError"; - throw EvaluatorError(FStringView( - std::format("Attribute '{}' of class `{}` is private", - structTypeName.toBasicString(), - member.toBasicString())), - binExp->rexp->getAAI()); - } - return *st.localContext->get(member); // safe - } - else if ((fnCall = std::dynamic_pointer_cast(binExp->rexp))) - { - auto structTypeNameOpt = currentContext->getStructName(st.parentId); - if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name")); - FString structTypeName = *structTypeNameOpt; - - FString fnName = u8""; - if (auto var = std::dynamic_pointer_cast(fnCall->callee)) - fnName = var->name; // function in struct has its name, so we can get the name - if (!st.localContext->containsInThisScope(fnName)) - { - static constexpr char NoAttributeError[] = "NoAttributeError"; - throw EvaluatorError(FStringView( - std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), fnName.toBasicString())), - binExp->rexp->getAAI()); - } - if (!st.localContext->isVariablePublic(fnName)) - { - static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError"; - throw EvaluatorError(FStringView( - std::format("Attribute '{}' of class `{}` is private", - structTypeName.toBasicString(), - fnName.toBasicString())), - binExp->rexp->getAAI()); - } - auto calleeValOpt = st.localContext->get(fnName); - ObjectPtr calleeVal = *calleeValOpt; - - if (!calleeVal->is()) - { - static constexpr char NotAFunctionErrorName[] = "NotAFunctionError"; - throw EvaluatorError( - FStringView(std::format( - "'{}' is not a function or callable", - calleeVal->toString().toBasicString())), - currentAddressInfo); - } - - Function fn = calleeVal->as(); - - return evalFunctionCall(fn, fnCall->arg, fnName); - } - else - { - static constexpr char AccessOpNotAFieldNameError[] = "AccessOpNotAFieldNameError"; - throw EvaluatorError(FStringView( - std::format("{} is not a field", binExp->rexp->toString().toBasicString())), - binExp->rexp->getAAI()); - } - } - return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp)); - } - ObjectPtr Evaluator::evalUnary(const Ast::UnaryExpr &unExp) - { - using Fig::Ast::Operator; - switch (unExp->op) - { - case Operator::Not: - return std::make_shared(!*eval(unExp->exp)); - case Operator::Subtract: - return std::make_shared(-*eval(unExp->exp)); - case Operator::BitNot: - return std::make_shared(bit_not(*eval(unExp->exp))); - default: - throw RuntimeError(FStringView(std::format("Unsupported unary operator: {}", magic_enum::enum_name(unExp->op)))); - } - } - - ObjectPtr Evaluator::evalFunctionCall(const Function &fn, const Ast::FunctionArguments &fnArgs, FString fnName) - { - const Function &fnStruct = fn; - Ast::FunctionCallArgs evaluatedArgs; - if (fnStruct.isBuiltin) - { - for (const auto &argExpr : fnArgs.argv) - { - evaluatedArgs.argv.push_back(eval(argExpr)); - } - if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength()) - { - static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError"; - throw EvaluatorError(FStringView(std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), fnStruct.builtinParamCount, evaluatedArgs.getLength())), currentAddressInfo); - } - return fnStruct.builtin(evaluatedArgs.argv); - } - - // check argument, all types of parameters - Ast::FunctionParameters fnParas = fnStruct.paras; - if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size()) - { - static constexpr char ArgumentMismatchErrorName[] = "ArgumentMismatchError"; - throw EvaluatorError(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo); - } - - // 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 - ObjectPtr argVal = eval(fnArgs.argv[i]); - TypeInfo actualType = argVal->getTypeInfo(); - if (expectedType != actualType and expectedType != ValueType::Any) - { - static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError"; - throw EvaluatorError(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; - - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second); - if (expectedType != defaultVal->getTypeInfo() and expectedType != ValueType::Any) - { - static constexpr char DefaultParameterTypeErrorName[] = "DefaultParameterTypeError"; - throw EvaluatorError(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); - } - - ObjectPtr argVal = eval(fnArgs.argv[i]); - TypeInfo actualType = argVal->getTypeInfo(); - if (expectedType != actualType and expectedType != ValueType::Any) - { - static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError"; - throw EvaluatorError(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(); - ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second); - evaluatedArgs.argv.push_back(defaultVal); - } - // create new context for function call - auto newContext = std::make_shared(FString(std::format("", fnName.toBasicString())), - fnStruct.closureContext); - 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 - ObjectPtr retVal = Object::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(FStringView(std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), fnStruct.retType.toString().toBasicString(), retVal->getTypeInfo().toString().toBasicString())), currentAddressInfo); - } - return retVal; - } - - ObjectPtr Evaluator::eval(Ast::Expression exp) - { - using Fig::Ast::AstType; - switch (exp->getType()) - { - case AstType::ValueExpr: { - auto valExp = std::dynamic_pointer_cast(exp); - return valExp->val; - } - case AstType::VarExpr: { - auto varExp = std::dynamic_pointer_cast(exp); - auto val = currentContext->get(varExp->name); - if (val.has_value()) - { - return val.value(); - } - static constexpr char UndefinedVariableErrorName[] = "UndefinedVariableError"; - throw EvaluatorError(FStringView(std::format("Variable '{}' is not defined in the current scope", varExp->name.toBasicString())), varExp->getAAI()); - } - case AstType::BinaryExpr: { - auto binExp = std::dynamic_pointer_cast(exp); - return evalBinary(binExp); - } - case AstType::UnaryExpr: { - auto unExp = std::dynamic_pointer_cast(exp); - return evalUnary(unExp); - } - case AstType::FunctionCall: { - auto fnCall = std::dynamic_pointer_cast(exp); - - ObjectPtr calleeVal = eval(fnCall->callee); - - if (!calleeVal->is()) - { - static constexpr char NotAFunctionErrorName[] = "NotAFunctionError"; - throw EvaluatorError( - FStringView(std::format( - "'{}' is not a function or callable", - calleeVal->toString().toBasicString())), - currentAddressInfo); - } - - Function fn = calleeVal->as(); - - FString fnName = u8""; - if (auto var = std::dynamic_pointer_cast(fnCall->callee)) - fnName = var->name; // try to get function name - - return evalFunctionCall(fn, fnCall->arg, fnName); - } - - case AstType::FunctionLiteralExpr: { - auto fn = std::dynamic_pointer_cast(exp); - - if (fn->isExprMode()) - { - Ast::BlockStatement body = std::make_shared(); - body->setAAI(fn->getExprBody()->getAAI()); - Ast::Statement retSt = std::make_shared(fn->getExprBody()); - retSt->setAAI(fn->getExprBody()->getAAI()); - body->stmts.push_back(retSt); - return std::make_shared(Function( - fn->paras, - ValueType::Any, - body, - currentContext)); - } - else - { - Ast::BlockStatement body = fn->getBlockBody(); - return std::make_shared(Function( - fn->paras, - ValueType::Any, - body, - currentContext)); - } - } - case AstType::InitExpr: { - auto initExpr = std::dynamic_pointer_cast(exp); - if (!currentContext->contains(initExpr->structName)) - { - static constexpr char StructNotFoundErrorName[] = "StructNotFoundError"; - throw EvaluatorError(FStringView(std::format("Structure type '{}' not found", initExpr->structName.toBasicString())), initExpr->getAAI()); - } - ObjectPtr structTypeVal = currentContext->get(initExpr->structName).value(); - if (!structTypeVal->is()) - { - static constexpr char NotAStructTypeErrorName[] = "NotAStructTypeError"; - throw EvaluatorError(FStringView(std::format("'{}' is not a structure type", initExpr->structName.toBasicString())), initExpr->getAAI()); - } - const StructType &structT = structTypeVal->as(); - ContextPtr defContext = structT.defContext; // definition context - // check init args - - size_t minArgs = 0; - size_t maxArgs = structT.fields.size(); - - for (auto &f : structT.fields) - { - if (f.defaultValue == nullptr) minArgs++; - } - - size_t got = initExpr->args.size(); - if (got > maxArgs || got < minArgs) - { - static constexpr char StructInitArgumentMismatchErrorName[] = "StructInitArgumentMismatchError"; - throw EvaluatorError(FStringView(std::format("Structure '{}' expects {} to {} fields, but {} were provided", initExpr->structName.toBasicString(), minArgs, maxArgs, initExpr->args.size())), initExpr->getAAI()); - } - - std::vector> evaluatedArgs; - for (const auto &[argName, argExpr] : initExpr->args) - { - evaluatedArgs.push_back({argName, eval(argExpr)}); - } - ContextPtr instanceCtx = std::make_shared( - FString(std::format("", initExpr->structName.toBasicString())), - currentContext); - /* - 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}; - */ - { - using enum Ast::InitExprAst::InitMode; - if (initExpr->initMode == Positional) - { - for (size_t i = 0; i < maxArgs; ++i) - { - const Field &field = structT.fields[i]; - const FString &fieldName = field.name; - const TypeInfo &expectedType = field.type; - if (i >= evaluatedArgs.size()) - { - // we've checked argument count before, so here must be a default value - ContextPtr previousContext = currentContext; - currentContext = defContext; // evaluate default value in definition context - - ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here - - currentContext = previousContext; - - // type check - if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any) - { - static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; - throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI()); - } - - instanceCtx->def(fieldName, expectedType, field.am, defaultVal); - continue; - } - - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (expectedType != argVal->getTypeInfo() && expectedType != ValueType::Any) - { - static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; - throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI()); - } - instanceCtx->def(fieldName, expectedType, field.am, argVal); - } - } - else - { - // named / shorthand init - 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); - if (instanceCtx->containsInThisScope(fieldName)) - { - static constexpr char StructFieldRedeclarationErrorName[] = "StructFieldRedeclarationError"; - throw EvaluatorError(FStringView(std::format("Field '{}' already initialized in structure '{}'", fieldName.toBasicString(), initExpr->structName.toBasicString())), initExpr->getAAI()); - } - if (i + 1 > got) - { - // use default value - ContextPtr previousContext = currentContext; - currentContext = defContext; // evaluate default value in definition context - ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here - currentContext = previousContext; - - // type check - const TypeInfo &expectedType = field.type; - if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any) - { - static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; - throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI()); - } - - instanceCtx->def(fieldName, field.type, field.am, defaultVal); - continue; - } - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (field.type != argVal->getTypeInfo() && field.type != ValueType::Any) - { - static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; - throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), field.type.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI()); - } - instanceCtx->def(fieldName, field.type, field.am, argVal); - } - } - } - instanceCtx->merge(*structT.defContext); - for (auto &[id, fn] : instanceCtx->getFunctions()) - { - instanceCtx->_update(*instanceCtx->getFunctionName(id), Object( - Function(fn.paras, fn.retType, fn.body, instanceCtx) // change its closureContext to struct instance's context - )); - } - return std::make_shared(StructInstance(structT.id, instanceCtx)); - } - default: - throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast(exp->getType())))); - return Object::getNullInstance(); - } - } - StatementResult Evaluator::evalBlockStatement(const Ast::BlockStatement &blockSt, ContextPtr context) - { - auto previousContext = currentContext; - if (context) - { - currentContext = context; - } - else - { - currentContext = std::make_shared(FString(std::format("", blockSt->getAAI().line, blockSt->getAAI().column)), currentContext); - } - StatementResult lstResult = StatementResult::normal(); - for (const auto &s : blockSt->stmts) - { - StatementResult sr = evalStatement(s); - if (!sr.isNormal()) - { - lstResult = sr; - break; - } - } - currentContext = previousContext; - return lstResult; - } - StatementResult Evaluator::evalStatement(const Ast::Statement &stmt) - { - using Fig::Ast::AstType; - currentAddressInfo = stmt->getAAI(); - switch (stmt->getType()) - { - case AstType::VarDefSt: { - auto varDef = std::dynamic_pointer_cast(stmt); - if (currentContext->contains(varDef->name)) - { - static constexpr char RedeclarationErrorName[] = "RedeclarationError"; - throw EvaluatorError(FStringView(std::format("Variable '{}' already defined in this scope", varDef->name.toBasicString())), currentAddressInfo); - } - ObjectPtr 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(FStringView(std::format("Variable '{}' expects type '{}', but got type '{}'", varDef->name.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), varDef->getAAI()); - } - } - } - else if (!varDef->typeName.empty()) - { - varTypeInfo = TypeInfo(varDef->typeName); // may throw - val = std::make_shared(Object::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(stmt); - eval(exprSt->exp); - return StatementResult::normal(); - }; - case AstType::BlockStatement: { - auto blockSt = std::dynamic_pointer_cast(stmt); - return evalBlockStatement(blockSt); // auto create new context in block statement - }; - case AstType::FunctionDefSt: { - auto fnDef = std::dynamic_pointer_cast(stmt); - if (currentContext->containsInThisScope(fnDef->name)) - { - static constexpr char RedeclarationErrorName[] = "RedeclarationError"; - throw EvaluatorError(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, - std::make_shared(Function( - fnDef->paras, - TypeInfo(fnDef->retType), - fnDef->body, - currentContext))); - return StatementResult::normal(); - }; - case AstType::StructSt: { - auto stDef = std::dynamic_pointer_cast(stmt); - if (currentContext->containsInThisScope(stDef->name)) - { - static constexpr char RedeclarationErrorName[] = "RedeclarationError"; - throw EvaluatorError(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo); - } - std::vector fields; - std::vector _fieldNames; - for (Ast::StructDefField field : stDef->fields) - { - if (Utils::vectorContains(field.fieldName, _fieldNames)) - { - static constexpr char RedeclarationErrorName[] = "RedeclarationError"; - throw EvaluatorError(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 = std::make_shared(FString(std::format("", - stDef->name.toBasicString(), - stDef->getAAI().line, - stDef->getAAI().column)), - currentContext); - ContextPtr previousContext = currentContext; - currentContext = defContext; - - const Ast::BlockStatement &body = stDef->body; - for (auto &st : body->stmts) - { - if (st->getType() != Ast::AstType::FunctionDefSt) - { - static constexpr char UnexpectedStatementInStructError[] = "UnexpectedStatementInStructError"; - throw EvaluatorError(FStringView( - std::format("Unexpected statement `{}` in struct declaration", - st->toString().toBasicString())), - st->getAAI()); - } - evalStatement(st); // function def st - } - currentContext = previousContext; - - AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); - TypeInfo _(stDef->name, true); // register type name - currentContext->def( - stDef->name, - ValueType::StructType, - am, - std::make_shared(StructType( - defContext, - fields))); - return StatementResult::normal(); - } - // case AstType::VarAssignSt: { - // auto varAssign = std::dynamic_pointer_cast(stmt); - // if (!currentContext->contains(varAssign->varName)) - // { - // static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError"; - // throw EvaluatorError(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo); - // } - // if (!currentContext->isVariableMutable(varAssign->varName)) - // { - // static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError"; - // throw EvaluatorError(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo); - // } - // Object 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(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(stmt); - ObjectPtr condVal = eval(ifSt->condition); - if (condVal->getTypeInfo() != ValueType::Bool) - { - static constexpr char ConditionTypeErrorName[] = "ConditionTypeError"; - throw EvaluatorError(FStringView(u8"If condition must be boolean"), currentAddressInfo); - } - if (condVal->as()) - { - return evalBlockStatement(ifSt->body); - } - // else - for (const auto &elif : ifSt->elifs) - { - ObjectPtr elifCondVal = eval(elif->condition); - if (elifCondVal->getTypeInfo() != ValueType::Bool) - { - static constexpr char ConditionTypeErrorName[] = "ConditionTypeError"; - throw EvaluatorError(FStringView(u8"Else-if condition must be boolean"), currentAddressInfo); - } - if (elifCondVal->as()) - { - return evalBlockStatement(elif->body); - } - } - if (ifSt->els) - { - return evalBlockStatement(ifSt->els->body); - } - return StatementResult::normal(); - }; - case AstType::WhileSt: { - auto whileSt = std::dynamic_pointer_cast(stmt); - while (true) - { - ObjectPtr condVal = eval(whileSt->condition); - if (condVal->getTypeInfo() != ValueType::Bool) - { - static constexpr char ConditionTypeErrorName[] = "ConditionTypeError"; - throw EvaluatorError(FStringView(u8"While condition must be boolean"), whileSt->condition->getAAI()); - } - if (!condVal->as()) - { - break; - } - ContextPtr loopContext = std::make_shared( - FString(std::format("", - whileSt->getAAI().line, whileSt->getAAI().column)), - currentContext); // 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; - } - } - return StatementResult::normal(); - }; - case AstType::ForSt: { - auto forSt = std::dynamic_pointer_cast(stmt); - ContextPtr loopContext = std::make_shared( - FString(std::format("", - forSt->getAAI().line, forSt->getAAI().column)), - currentContext); // for loop has its own context - ContextPtr previousContext = currentContext; - currentContext = loopContext; - - evalStatement(forSt->initSt); // 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 - { - ObjectPtr condVal = eval(forSt->condition); - if (condVal->getTypeInfo() != ValueType::Bool) - { - static constexpr char ConditionTypeErrorName[] = "ConditionTypeError"; - throw EvaluatorError(FStringView(u8"For condition must be boolean"), forSt->condition->getAAI()); - } - if (!condVal->as()) - { - break; - } - iteration++; - ContextPtr iterationContext = std::make_shared( - FString(std::format("", - forSt->getAAI().line, forSt->getAAI().column, iteration)), - loopContext); // every loop has its own context - StatementResult sr = evalBlockStatement(forSt->body, iterationContext); - if (sr.shouldReturn()) - { - currentContext = previousContext; // restore context before return - return sr; - } - if (sr.shouldBreak()) - { - break; - } - if (sr.shouldContinue()) - { - // continue to next iteration - continue; - } - currentContext = loopContext; // let increment statement be in loop context - evalStatement(forSt->incrementSt); // ignore increment statement result - } - currentContext = previousContext; // restore context - return StatementResult::normal(); - } - case AstType::ReturnSt: { - if (!currentContext->parent) - { - static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError"; - throw EvaluatorError(FStringView(u8"'return' statement outside function"), currentAddressInfo); - } - if (!currentContext->isInFunctionContext()) - { - static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError"; - throw EvaluatorError(FStringView(u8"'return' statement outside function"), currentAddressInfo); - } - auto returnSt = std::dynamic_pointer_cast(stmt); - return StatementResult::returnFlow(eval(returnSt->retValue)); - }; - case AstType::BreakSt: { - if (!currentContext->parent) - { - static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError"; - throw EvaluatorError(FStringView(u8"'break' statement outside loop"), currentAddressInfo); - } - if (!currentContext->isInLoopContext()) - { - static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError"; - throw EvaluatorError(FStringView(u8"'break' statement outside loop"), currentAddressInfo); - } - return StatementResult::breakFlow(); - }; - case AstType::ContinueSt: { - if (!currentContext->parent) - { - static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError"; - throw EvaluatorError(FStringView(u8"'continue' statement outside loop"), currentAddressInfo); - } - if (!currentContext->isInLoopContext()) - { - static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError"; - throw EvaluatorError(FStringView(u8"'continue' statement outside loop"), currentAddressInfo); - } - return StatementResult::continueFlow(); - }; - 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)) - { - auto exprAst = std::dynamic_pointer_cast(ast); - Ast::Expression exp = exprAst->exp; - eval(exp); - } - else if (dynamic_cast(ast.get())) - { - auto stmtAst = std::dynamic_pointer_cast(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 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ea09af2..0249fb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,16 +27,16 @@ Copyright (C) 2020-2025 PuqiAR This software is licensed under the MIT License. See LICENSE.txt for details. */ -#include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include static size_t addressableErrorCount = 0; static size_t unaddressableErrorCount = 0; @@ -117,13 +117,13 @@ int main(int argc, char **argv) // } Fig::Parser parser(lexer); - std::vector ast; + std::vector asts; std::vector sourceLines = splitSource(Fig::FString(source)); try { - ast = parser.parseAll(); + asts = parser.parseAll(); } catch (const Fig::AddressableError &e) { @@ -150,10 +150,12 @@ int main(int argc, char **argv) // printer.print(node); // } - Fig::Evaluator evaluator(ast); + Fig::Evaluator evaluator; + evaluator.CreateGlobalContext(); + evaluator.RegisterBuiltins(); try { - evaluator.run(); + evaluator.Run(asts); } catch (const Fig::AddressableError &e) { diff --git a/test.fig b/test.fig index c14d824..7cf413e 100644 --- a/test.fig +++ b/test.fig @@ -1,14 +1,15 @@ -struct test +struct Person { - bar:String = "1233"; - func foo() + name: String; + + func getName() -> String { - __fstdout_println(bar); + return name; } } -var x := test{}; -x.foo(); +var person := Person{"PuqiAR"}; -x.bar = "555"; -x.foo(); \ No newline at end of file +const print := __fstdout_println; + +print(person.getName()); \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 9a41ae1..2fee10a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -13,10 +13,15 @@ target("Fig") add_cxxflags("-static") add_cxxflags("-stdlib=libc++") - add_files("src/*.cpp") - - add_includedirs("include") + add_files("src/main.cpp") + add_files("src/Core/warning.cpp") + add_files("src/Evaluator/evaluator.cpp") + add_files("src/Lexer/lexer.cpp") + add_files("src/Parser/parser.cpp") + add_files("src/Value/value.cpp") + add_includedirs("src") + set_warnings("all") add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"") \ No newline at end of file