diff --git a/ExampleCodes/1-Variables.fig b/ExampleCodes/1-Variables.fig index 260183e..1cad678 100644 --- a/ExampleCodes/1-Variables.fig +++ b/ExampleCodes/1-Variables.fig @@ -10,4 +10,70 @@ invalid! */ const Pi := 3.14; // recommended, auto detect type -// equal -> const Pi: Double = 3.14; \ No newline at end of file +// equal -> const Pi: Double = 3.14; + +/* +In fig, we have 13 builtin-type + +01 Any +02 Null +03 Int +04 String +05 Bool +06 Double +07 Function +08 StructType +09 StructInstance +10 List +11 Map +12 Module +13 InterfaceType + +3, 4, 5, 6, 10, 11 are initable + +value system: + object is immutable + (included basic types: Int, String...) + + `variable` is a name, refers to an object + assignment is to bind name to value + + Example: var a := 10; + + [name] 'a' ---> variable slot (name, declared type, access modifier, [value) ---> ObjectPtr ---> raw Object class + bind bind (shared_ptr) + + For example: + var a := 10; + var b := 10; + + `a` and `b` reference to the same object in memory + + a = 20; + + now a refers to a new object (20, Int) + + what about complex types? + they actually have same behaviors with basic types + + var a := [1, 2, 3, 4]; + var b := a; + + > a + [1, 2, 3, 4] + > b + [1, 2, 3, 4] + + set a[0] to 5 + + > a + [5, 2, 3, 4] + > b + [5, 2, 3, 4] + + Why did such a result occur? + + " `a` and `b` reference to the same object in memory " + + If you wish to obtain a copy, use List {a} to deeply copy it +*/ \ No newline at end of file diff --git a/ExampleCodes/SpeedTest/fibBenchmark.fig b/ExampleCodes/SpeedTest/fibBenchmark.fig new file mode 100644 index 0000000..28ab6cb --- /dev/null +++ b/ExampleCodes/SpeedTest/fibBenchmark.fig @@ -0,0 +1,65 @@ +import std.io; +import std.time; + +func benchmark(fn: Function, arg: Any) -> Null +{ + io.println("Testing fn:", fn, "with arg:", arg); + const start := time.now(); + + const result := fn(arg); + + const end := time.now(); + const duration := new time.Time{ + end.since(start) + }; + io.println("=" * 50); + io.println("fn returns:", result, "\n"); + io.println("Cost:", duration.toSeconds(), "s"); + io.println(" ", duration.toMillis(), "ms"); +} + +var memo := {}; + +func fib_memo(x) +{ + if memo.contains(x) + { + return memo.get(x); + } + if x <= 1 + { + memo[x] = x; + return x; + } + var result := fib_memo(x - 1) + fib_memo(x - 2); + memo[x] = result; + return result; +} + +func fib_iter(n) +{ + var a := 0; + var b := 1; + for var i := 0; i < n; i = i + 1 + { + var temp := a + b; + a = b; + b = temp; + } + return a; +} + +func fib(x) +{ + if x <= 1 + { + return x; + } + return fib(x - 1) + fib(x - 2); +} + +const n := 28; + +benchmark(fib, n); +benchmark(fib_memo, n); +benchmark(fib_iter, n); \ No newline at end of file diff --git a/fig-vscode/package.json b/fig-vscode/package.json index ee53153..f5eb4e5 100644 --- a/fig-vscode/package.json +++ b/fig-vscode/package.json @@ -2,7 +2,7 @@ "name": "fig-vscode", "displayName": "Fig Language", "description": "VSCode extension for Fig language with syntax highlighting", - "version": "0.0.2", + "version": "0.4.2", "publisher": "PuqiAR", "engines": { "vscode": "^1.90.0" diff --git a/fig-vscode/syntaxes/fig.tmLanguage.json b/fig-vscode/syntaxes/fig.tmLanguage.json index 011a4e5..2404f07 100644 --- a/fig-vscode/syntaxes/fig.tmLanguage.json +++ b/fig-vscode/syntaxes/fig.tmLanguage.json @@ -57,7 +57,7 @@ "patterns": [ { "name": "keyword.control.fig", - "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b" + "match": "\\b(and|or|not|import|func|var|const|final|while|for|if|else|new|struct|interface|impl|public|return|break|continue|try|catch|throw)\\b" }, { "name": "constant.language.fig", "match": "\\b(true|false|null)\\b" } ] diff --git a/src/Ast/Statements/FunctionDefSt.hpp b/src/Ast/Statements/FunctionDefSt.hpp index 7d6674e..e668062 100644 --- a/src/Ast/Statements/FunctionDefSt.hpp +++ b/src/Ast/Statements/FunctionDefSt.hpp @@ -23,14 +23,14 @@ namespace Fig::Ast FString name; FunctionParameters paras; bool isPublic; - FString retType; + Expression retType; BlockStatement body; - FunctionDefSt() : - retType(ValueType::Null.name) + + FunctionDefSt() { type = AstType::FunctionDefSt; } - FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, FString _retType, BlockStatement _body) + FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, Expression _retType, BlockStatement _body) { type = AstType::FunctionDefSt; diff --git a/src/Ast/Statements/InterfaceDefSt.hpp b/src/Ast/Statements/InterfaceDefSt.hpp index d062fa4..e0fb141 100644 --- a/src/Ast/Statements/InterfaceDefSt.hpp +++ b/src/Ast/Statements/InterfaceDefSt.hpp @@ -25,7 +25,7 @@ namespace Fig::Ast { FString name; FunctionParameters paras; - FString returnType; + Expression returnType; BlockStatement defaultBody = nullptr; // nullptr is non-default func diff --git a/src/Ast/Statements/StructDefSt.hpp b/src/Ast/Statements/StructDefSt.hpp index 9a81bdf..d04d467 100644 --- a/src/Ast/Statements/StructDefSt.hpp +++ b/src/Ast/Statements/StructDefSt.hpp @@ -12,12 +12,12 @@ namespace Fig::Ast { AccessModifier am; FString fieldName; - FString tiName; + Expression declaredType; Expression defaultValueExpr; StructDefField() {} - StructDefField(AccessModifier _am, FString _fieldName, FString _tiName, Expression _defaultValueExpr) : - am(std::move(_am)), fieldName(std::move(_fieldName)), tiName(std::move(_tiName)), defaultValueExpr(std::move(_defaultValueExpr)) + StructDefField(AccessModifier _am, FString _fieldName, Expression _declaredType, Expression _defaultValueExpr) : + am(std::move(_am)), fieldName(std::move(_fieldName)), declaredType(std::move(_declaredType)), defaultValueExpr(std::move(_defaultValueExpr)) { } }; diff --git a/src/Ast/Statements/VarDef.hpp b/src/Ast/Statements/VarDef.hpp index a5ad13e..d53d879 100644 --- a/src/Ast/Statements/VarDef.hpp +++ b/src/Ast/Statements/VarDef.hpp @@ -11,22 +11,28 @@ namespace Fig::Ast bool isPublic; bool isConst; FString name; - FString typeName; + // FString typeName; + Expression declaredType; Expression expr; - VarDefAst() : - typeName(ValueType::Any.name) + bool followupType; + + VarDefAst() { type = AstType::VarDefSt; + declaredType = nullptr; + expr = nullptr; + followupType = false; } - VarDefAst(bool _isPublic, bool _isConst, FString _name, FString _info, Expression _expr) : - typeName(std::move(_info)) + VarDefAst(bool _isPublic, bool _isConst, FString _name, Expression _declaredType, Expression _expr, bool _followupType) { type = AstType::VarDefSt; isPublic = _isPublic; isConst = _isConst; name = std::move(_name); + declaredType = std::move(_declaredType); expr = std::move(_expr); + followupType = _followupType; } }; diff --git a/src/Ast/astBase.hpp b/src/Ast/astBase.hpp index 0e0ec29..a406b44 100644 --- a/src/Ast/astBase.hpp +++ b/src/Ast/astBase.hpp @@ -26,14 +26,15 @@ namespace Fig::Ast TernaryExpr, /* Postfix */ - MemberExpr, // a.b - IndexExpr, // a[b] + 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, @@ -119,30 +120,20 @@ namespace Fig::Ast _AstBase() {} - void setAAI(AstAddressInfo _aai) - { - aai = std::move(_aai); - } + void setAAI(AstAddressInfo _aai) { aai = std::move(_aai); } virtual FString typeName() { - return FString::fromStringView( - FStringView::fromBasicStringView(magic_enum::enum_name(type))); + return FString::fromStringView(FStringView::fromBasicStringView(magic_enum::enum_name(type))); } virtual FString toString() { return FString(std::format("", typeName().toBasicString(), aai.line, aai.column)); } - AstAddressInfo getAAI() - { - return aai; - } + AstAddressInfo getAAI() { return aai; } - AstType getType() - { - return type; - } + AstType getType() { return type; } }; class StatementAst : public _AstBase @@ -150,10 +141,7 @@ namespace Fig::Ast public: using _AstBase::_AstBase; using _AstBase::operator=; - StatementAst() - { - type = AstType::StatementBase; - } + StatementAst() { type = AstType::StatementBase; } virtual FString toString() override { @@ -164,10 +152,7 @@ namespace Fig::Ast class EofStmt final : public StatementAst { public: - EofStmt() - { - type = AstType::StatementBase; - } + EofStmt() { type = AstType::StatementBase; } virtual FString toString() override { @@ -180,10 +165,7 @@ namespace Fig::Ast public: using _AstBase::_AstBase; using _AstBase::operator=; - ExpressionAst() - { - type = AstType::ExpressionBase; - } + ExpressionAst() { type = AstType::ExpressionBase; } virtual FString toString() override { @@ -242,41 +224,22 @@ namespace Fig::Ast static const std::unordered_set unaryOps{ Operator::Not, // ! Operator::Subtract, // - - Operator::BitNot, // ~ + Operator::BitNot, // ~ Operator::BitAnd, // reference operator & }; static const std::unordered_set binaryOps{ - Operator::Add, - Operator::Subtract, - Operator::Multiply, - Operator::Divide, - Operator::Modulo, - Operator::Power, - Operator::And, - Operator::Or, + Operator::Add, Operator::Subtract, Operator::Multiply, Operator::Divide, + Operator::Modulo, Operator::Power, Operator::And, Operator::Or, - Operator::Equal, - Operator::NotEqual, - Operator::Less, - Operator::LessEqual, - Operator::Greater, - Operator::GreaterEqual, - Operator::Is, + Operator::Equal, Operator::NotEqual, Operator::Less, Operator::LessEqual, + Operator::Greater, Operator::GreaterEqual, Operator::Is, - Operator::BitAnd, - Operator::BitOr, - Operator::BitXor, - Operator::BitNot, - Operator::ShiftLeft, - Operator::ShiftRight, + Operator::BitAnd, Operator::BitOr, Operator::BitXor, Operator::BitNot, + Operator::ShiftLeft, Operator::ShiftRight, - Operator::Assign, - Operator::PlusAssign, - Operator::MinusAssign, - Operator::AsteriskAssign, - Operator::SlashAssign, - Operator::CaretAssign + Operator::Assign, Operator::PlusAssign, Operator::MinusAssign, Operator::AsteriskAssign, + Operator::SlashAssign, Operator::CaretAssign // Operator::Walrus, // Operator::Dot @@ -352,19 +315,9 @@ namespace Fig::Ast { public: std::vector stmts; - BlockStatementAst() - { - type = AstType::BlockStatement; - } - BlockStatementAst(std::vector _stmts) : - stmts(std::move(_stmts)) - { - type = AstType::BlockStatement; - } - virtual FString typeName() override - { - return FString(u8"BlockStatement"); - } + BlockStatementAst() { type = AstType::BlockStatement; } + BlockStatementAst(std::vector _stmts) : stmts(std::move(_stmts)) { type = AstType::BlockStatement; } + virtual FString typeName() override { return FString(u8"BlockStatement"); } virtual FString toString() override { return FString(std::format("", typeName().toBasicString(), aai.line, aai.column)); diff --git a/src/Core/core.hpp b/src/Core/core.hpp index 769991e..a49dec2 100644 --- a/src/Core/core.hpp +++ b/src/Core/core.hpp @@ -4,7 +4,7 @@ #include #include -#define __FCORE_VERSION "0.4.1-alpha" +#define __FCORE_VERSION "0.4.2-alpha" #if defined(_WIN32) #define __FCORE_PLATFORM "Windows" diff --git a/src/Evaluator/Context/context.hpp b/src/Evaluator/Context/context.hpp index 9709ac0..903876e 100644 --- a/src/Evaluator/Context/context.hpp +++ b/src/Evaluator/Context/context.hpp @@ -1,5 +1,6 @@ #pragma once +#include "Ast/Statements/InterfaceDefSt.hpp" #include "Value/interface.hpp" #include #include @@ -303,7 +304,7 @@ namespace Fig return false; } - Function getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) + Ast::InterfaceMethod getDefaultImplementedMethod(const TypeInfo &structType, const FString &functionName) { // O(N²) // SLOW @@ -336,15 +337,12 @@ namespace Fig if (method.name == functionName) { if (!method.hasDefaultBody()) assert(false); - - return Function( - method.paras, TypeInfo(method.returnType), method.defaultBody, shared_from_this()); + return method; } } } assert(false); - return Function(); // ignore warning } const Function &getImplementedMethod(const TypeInfo &structType, const FString &functionName) const diff --git a/src/Evaluator/Value/IntPool.hpp b/src/Evaluator/Value/IntPool.hpp new file mode 100644 index 0000000..203f323 --- /dev/null +++ b/src/Evaluator/Value/IntPool.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "Value/value.hpp" +#include +#include +#include +#include +#include + +namespace Fig +{ + class IntPool + { + private: + static constexpr ValueType::IntClass CACHE_MIN = -128; + static constexpr ValueType::IntClass CACHE_MAX = 127; + + std::array cache; + + public: + IntPool() + { + for (ValueType::IntClass i = CACHE_MIN; i <= CACHE_MAX; ++i) + { + cache[i - CACHE_MIN] = std::make_shared(i); + } + } + ObjectPtr createInt(ValueType::IntClass val) const + { + if (val >= CACHE_MIN && val <= CACHE_MAX) { return cache[val - CACHE_MIN]; } + return std::make_shared(val); + } + + static const IntPool &getInstance() + { + static IntPool pool; + return pool; + } + }; +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Value/LvObject.hpp b/src/Evaluator/Value/LvObject.hpp index e5b07c1..4636c34 100644 --- a/src/Evaluator/Value/LvObject.hpp +++ b/src/Evaluator/Value/LvObject.hpp @@ -87,7 +87,7 @@ namespace Fig std::format("Variable `{}` expects type `{}`, but got '{}'", s->name.toBasicString(), s->declaredType.toString().toBasicString(), - v->getTypeInfo().toString().toBasicString()))); + prettyType(v).toBasicString()))); } if (isAccessConst(s->am)) { diff --git a/src/Evaluator/Value/VariableSlot.hpp b/src/Evaluator/Value/VariableSlot.hpp index 984c40c..f65cb17 100644 --- a/src/Evaluator/Value/VariableSlot.hpp +++ b/src/Evaluator/Value/VariableSlot.hpp @@ -4,11 +4,12 @@ #include #include #include +#include + #include namespace Fig { - class Object; - using ObjectPtr = std::shared_ptr; + struct VariableSlot { FString name; diff --git a/src/Evaluator/Value/value.cpp b/src/Evaluator/Value/value.cpp index 110535c..16cbbd5 100644 --- a/src/Evaluator/Value/value.cpp +++ b/src/Evaluator/Value/value.cpp @@ -1,3 +1,4 @@ +#include "Value/structType.hpp" #include #include #include @@ -26,9 +27,7 @@ namespace Fig { if (!typeMap.contains(_name)) { - throw RuntimeError(FString(std::format( - "No type named '{}'", - _name.toBasicString()))); + throw RuntimeError(FString(std::format("No type named '{}'", _name.toBasicString()))); // *this = ValueType::String; } id = typeMap.at(name); // may throw @@ -41,10 +40,7 @@ namespace Fig ObjectPtr value = key.value; const TypeInfo &type = value->getTypeInfo(); - if (type == ValueType::Int) - { - return std::hash{}(value->as()); - } + if (type == ValueType::Int) { return std::hash{}(value->as()); } if (type == ValueType::Double) { return std::hash{}(value->as()); @@ -61,10 +57,7 @@ namespace Fig { auto HashFields = [](std::vector fields) { size_t r = 0; - for (auto &f : fields) - { - r += std::hash{}(f); - } + for (auto &f : fields) { r += std::hash{}(f); } return r; }; const StructType &st = value->as(); @@ -73,19 +66,30 @@ namespace Fig if (type == ValueType::StructInstance) { const StructInstance &si = value->as(); - return std::hash{}(si.parentType) + std::hash{}(reinterpret_cast(std::addressof(*si.localContext))); + return std::hash{}(si.parentType) + + std::hash{}(reinterpret_cast(std::addressof(*si.localContext))); } assert(false); throw ""; // ignore warning } } - FString prettyType(std::shared_ptr obj) + TypeInfo actualType(std::shared_ptr obj) { auto t = obj->getTypeInfo(); - if (t == ValueType::StructInstance) - return obj->as().parentType.toString(); - return t.toString(); + + // dispatch builtin struct types (like Int{}, List{} e.g...) + if (t == ValueType::StructType) + { + return obj->as().type; + } + + if (t == ValueType::StructInstance) return obj->as().parentType; + return t; + } + FString prettyType(std::shared_ptr obj) + { + return actualType(obj).toString(); } const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 @@ -102,8 +106,6 @@ namespace Fig const TypeInfo ValueType::Module(FString(u8"Module"), true); // id: 12 const TypeInfo ValueType::InterfaceType(FString(u8"InterfaceType"), true); // id: 13 - - bool implements(const TypeInfo &structType, const TypeInfo &interfaceType, ContextPtr ctx) { return ctx->hasImplRegisted(structType, interfaceType); @@ -111,28 +113,25 @@ namespace Fig bool isTypeMatch(const TypeInfo &expected, ObjectPtr obj, ContextPtr ctx) { - if (expected == ValueType::Any) - return true; + if (expected == ValueType::Any) return true; TypeInfo actual = obj->getTypeInfo(); - if (obj->is()) + if (obj->is()) + { + const StructType &t = obj->as(); + if (expected == t.type) // the StructType typeinfo + { + return true; + } + } + else if (obj->is()) { const StructInstance &si = obj->as(); - if (si.parentType == expected) - { - return true; - } - if (implements(si.parentType, expected, ctx)) - { - return true; - } - return false; - } - else - { - return expected == actual; + if (si.parentType == expected) { return true; } + if (implements(si.parentType, expected, ctx)) { return true; } } + return expected == actual; } } // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/Value/value.hpp b/src/Evaluator/Value/value.hpp index 891eea3..feaa44f 100644 --- a/src/Evaluator/Value/value.hpp +++ b/src/Evaluator/Value/value.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -31,10 +32,8 @@ namespace Fig static_cast(std::numeric_limits::min()); return d > intMaxAsDouble || d < intMinAsDouble; } - class Object; - using ObjectPtr = std::shared_ptr; - + TypeInfo actualType(std::shared_ptr obj); FString prettyType(std::shared_ptr obj); bool operator==(const Object &, const Object &); @@ -509,7 +508,7 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() + rhs.getNumericValue(); - if (bothInt && !isNumberExceededIntLimit(result)) + if (bothInt) return Object(static_cast(result)); return Object(result); } @@ -526,7 +525,7 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() - rhs.getNumericValue(); - if (bothInt && !isNumberExceededIntLimit(result)) + if (bothInt) return Object(static_cast(result)); return Object(result); } @@ -541,10 +540,20 @@ namespace Fig { bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() * rhs.getNumericValue(); - if (bothInt && !isNumberExceededIntLimit(result)) + if (bothInt) return Object(static_cast(result)); return Object(result); } + if (lhs.is() && rhs.is()) + { + FString result; + const FString &l = lhs.as(); + for (size_t i=0; i < rhs.getNumericValue(); ++i) + { + result += l; + } + return Object(result); + } throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); } @@ -559,7 +568,7 @@ namespace Fig throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); // bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() / rnv; - // if (bothInt && !isNumberExceededIntLimit(result)) + // if (bothInt) // return Object(static_cast(result)); // int / int maybe decimals @@ -573,15 +582,24 @@ namespace Fig { if (lhs.isNull() || rhs.isNull()) throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs))); + if (lhs.is() && rhs.is()) + { + ValueType::IntClass lv = lhs.as(); + ValueType::IntClass rv = lhs.as(); + if (rv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); + + ValueType::IntClass q = lv / rv; + ValueType::IntClass r = lv % rv; + if (r != 0 && ((lv < 0) != (rv < 0))) { q -= 1; } + return q; + } + if (lhs.isNumeric() && rhs.isNumeric()) { auto rnv = rhs.getNumericValue(); if (rnv == 0) throw ValueError(FString(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); - bool bothInt = lhs.is() && rhs.is(); auto result = std::fmod(lhs.getNumericValue(), rnv); - if (bothInt && !isNumberExceededIntLimit(result)) - return Object(static_cast(result)); return Object(result); } throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); @@ -698,7 +716,7 @@ namespace Fig { bool bothInt = base.is() && exp.is(); auto result = std::pow(base.getNumericValue(), exp.getNumericValue()); - if (bothInt && !isNumberExceededIntLimit(result)) + if (bothInt) return Object(static_cast(result)); return Object(result); } @@ -706,7 +724,6 @@ namespace Fig } }; - using ObjectPtr = std::shared_ptr; using RvObject = ObjectPtr; inline bool operator==(const ValueKey &l, const ValueKey &r) diff --git a/src/Evaluator/Value/value_forward.hpp b/src/Evaluator/Value/value_forward.hpp new file mode 100644 index 0000000..f9dfbee --- /dev/null +++ b/src/Evaluator/Value/value_forward.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace Fig +{ + class Object; + + using ObjectPtr = std::shared_ptr; +}; // namespace Fig \ No newline at end of file diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index e7d6746..8073f6d 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -1,3 +1,7 @@ +#include +#include +#include +#include #include #include #include @@ -120,11 +124,11 @@ namespace Fig } else if (ctx->hasDefaultImplementedMethod(si.parentType, member)) { + const auto &ifm = ctx->getDefaultImplementedMethod(si.parentType, member); + Function fn(ifm.paras, actualType(eval(ifm.returnType, ctx)), ifm.defaultBody, ctx); + return LvObject(std::make_shared( - member, - std::make_shared(ctx->getDefaultImplementedMethod(si.parentType, member)), - ValueType::Function, - AccessModifier::PublicConst), + member, std::make_shared(fn), ValueType::Function, AccessModifier::PublicConst), ctx); } else @@ -222,6 +226,362 @@ namespace Fig } } + RvObject Evaluator::evalInitExpr(Ast::InitExpr initExpr, ContextPtr ctx) + { + LvObject structeLv = evalLv(initExpr->structe, ctx); + ObjectPtr structTypeVal = structeLv.get(); + const FString &structName = structeLv.name(); + if (!structTypeVal->is()) + { + throw EvaluatorError(u8"NotAStructTypeError", + std::format("'{}' is not a structure type", structName.toBasicString()), + initExpr); + } + const StructType &structT = structTypeVal->as(); + + if (structT.builtin) + { + const TypeInfo &type = structT.type; + auto &args = initExpr->args; + size_t argSize = args.size(); + + if (argSize > 1) + { + throw EvaluatorError(u8"StructInitArgumentMismatchError", + std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided", + type.toString().toBasicString(), + argSize), + initExpr); + } + + // default value + if (argSize == 0) + { + if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function) + { + throw EvaluatorError( + u8"BuiltinNotConstructibleError", + std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), + initExpr); + } + return std::make_shared(Object::defaultValue(type)); + } + + ObjectPtr val = eval(args[0].second, ctx); + + auto err = [&](const char *msg) { + throw EvaluatorError(u8"BuiltinInitTypeMismatchError", + std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg), + initExpr); + }; + + // ===================== Int ===================== + if (type == ValueType::Int) + { + if (!val->is()) err("expects Int"); + return std::make_shared(val->as()); + } + + // ===================== Double ===================== + if (type == ValueType::Double) + { + if (!val->is()) err("expects Double"); + return std::make_shared(val->as()); + } + + // ===================== Bool ===================== + if (type == ValueType::Bool) + { + if (!val->is()) err("expects Bool"); + return std::make_shared(val->as()); + } + + // ===================== String ===================== + if (type == ValueType::String) + { + if (!val->is()) err("expects String"); + return std::make_shared(val->as()); + } + + // ===================== Null ===================== + if (type == ValueType::Null) + { + // Null basically ignores input but keep invariant strict: + if (!val->is()) err("expects Null"); + return Object::getNullInstance(); + } + + // ===================== List ===================== + if (type == ValueType::List) + { + if (!val->is()) err("expects List"); + + const auto &src = val->as(); + auto copied = std::make_shared(List{}); + + auto &dst = copied->as(); + dst.reserve(src.size()); + for (auto &e : src) dst.push_back(e); // shallow element copy, but new container + + return copied; + } + + // ===================== Map ===================== + if (type == ValueType::Map) + { + if (!val->is()) err("expects Map"); + + const auto &src = val->as(); + auto copied = std::make_shared(Map{}); + + auto &dst = copied->as(); + for (auto &[k, v] : src) dst.emplace(k, v); + + return copied; + } + + throw EvaluatorError( + u8"BuiltinNotConstructibleError", + std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), + initExpr); + } + + 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", + structName.toBasicString(), + minArgs, + maxArgs, + initExpr->args.size()), + initExpr); + } + + std::vector> evaluatedArgs; + + auto evalArguments = [&evaluatedArgs, initExpr, ctx, this]() { + for (const auto &[argName, argExpr] : initExpr->args) + { + evaluatedArgs.push_back({argName, eval(argExpr, ctx)}); + } + }; + + ContextPtr instanceCtx = + std::make_shared(FString(std::format("", 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) + { + evalArguments(); + + 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 (!isTypeMatch(expectedType, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, expectedType, field.am, defaultVal); + continue; + } + + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (!isTypeMatch(expectedType, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + instanceCtx->def(fieldName, expectedType, field.am, argVal); + } + } + else if (initExpr->initMode == Named) + { + evalArguments(); + + // named + 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(), + 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 (!isTypeMatch(expectedType, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + expectedType.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(fieldName, field.type, field.am, defaultVal); + continue; + } + const ObjectPtr &argVal = evaluatedArgs[i].second; + if (!isTypeMatch(field.type, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + fieldName.toBasicString(), + field.type.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + instanceCtx->def(fieldName, field.type, field.am, argVal); + } + } + else + { + // shorthand, can be unordered + // in this mode, initExpr args are all VarExpr + // field name is the variable name + for (const auto &[argName, argExpr] : initExpr->args) + { + // assert(argExpr->getType() == Ast::AstType::VarExpr); + // argName is var name + const ObjectPtr &argVal = eval(argExpr, ctx); // get the value + // find field + auto fieldIt = std::find_if( + structT.fields.begin(), + structT.fields.end(), + [&argName](const Field &f) { return f.name == argName; }); + if (fieldIt == structT.fields.end()) + { + throw EvaluatorError(u8"StructFieldNotFoundError", + std::format("Field '{}' not found in structure '{}'", + argName.toBasicString(), + structName.toBasicString()), + initExpr); + } + const Field &field = *fieldIt; + if (!isTypeMatch(field.type, argVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + field.name.toBasicString(), + field.type.toString().toBasicString(), + prettyType(argVal).toBasicString()), + initExpr); + } + // field.name is argName (var name) + // Point{x=x, y=y} --> Point{x, y} + + instanceCtx->def(field.name, field.type, field.am, argVal); + } + // fill default values + size_t currentFieldCount = initExpr->args.size(); // we have already check argument count, min <= got <= max + // so remain fields start from currentFieldCount to maxArgs + for (size_t i = currentFieldCount; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + + // evaluate default value in definition context + ObjectPtr defaultVal = eval(field.defaultValue, + defContext); // it can't be null here + + // type check + if (!isTypeMatch(field.type, defaultVal, ctx)) + { + throw EvaluatorError( + u8"StructFieldTypeMismatchError", + std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", + structName.toBasicString(), + field.name.toBasicString(), + field.type.toString().toBasicString(), + prettyType(defaultVal).toBasicString()), + initExpr); + } + + instanceCtx->def(field.name, field.type, field.am, defaultVal); + } + } + } + ContextPtr stDefCtx = structT.defContext; + + // load struct method + for (auto &[id, fn] : stDefCtx->getFunctions()) + { + auto funcNameOpt = stDefCtx->getFunctionName(id); + assert(funcNameOpt.has_value()); + + const FString &funcName = *funcNameOpt; + auto funcSlot = stDefCtx->get(funcName); + + instanceCtx->def(funcName, + ValueType::Function, + funcSlot->am, + std::make_shared(Function(fn.paras, fn.retType, fn.body, instanceCtx))); + } + + return std::make_shared(StructInstance(structT.type, instanceCtx)); + } + RvObject Evaluator::evalBinary(Ast::BinaryExpr bin, ContextPtr ctx) { using Ast::Operator; @@ -232,16 +592,37 @@ namespace Fig case Operator::Add: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() + rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(*lhs + *rhs); } case Operator::Subtract: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() - rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared(*lhs - *rhs); }; case Operator::Multiply: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() * rhs->as(); + return IntPool::getInstance().createInt(result); + } + return std::make_shared((*lhs) * (*rhs)); }; case Operator::Divide: { @@ -252,11 +633,40 @@ namespace Fig case Operator::Modulo: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass lv = lhs->as(); + ValueType::IntClass rv = lhs->as(); + if (rv == 0) + { + throw ValueError( + FString( + std::format( + "Modulo by zero: {} % {}", + lv, + rv + ) + ) + ); + } + ValueType::IntClass result = lv / rv; + ValueType::IntClass r = lv % rv; + if (r != 0 && ((lv < 0) != (rv < 0))) { result -= 1; } + return IntPool::getInstance().createInt(result); + } + return std::make_shared(*lhs % *rhs); }; case Operator::Power: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = std::pow(lhs->as(),rhs->as()); + return IntPool::getInstance().createInt(result); + } return std::make_shared(power(*lhs, *rhs)); } case Operator::And: { @@ -343,34 +753,64 @@ namespace Fig throw EvaluatorError(u8"TypeError", std::format("Unsupported operator `is` for '{}' && '{}'", - lhsType.toString().toBasicString(), - rhsType.toString().toBasicString()), + prettyType(lhs).toBasicString(), + prettyType(rhs).toBasicString()), bin->lexp); } case Operator::BitAnd: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() & rhs->as(); + return IntPool::getInstance().createInt(result); + } return std::make_shared(bit_and(*lhs, *rhs)); } case Operator::BitOr: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() | rhs->as(); + return IntPool::getInstance().createInt(result); + } return std::make_shared(bit_or(*lhs, *rhs)); } case Operator::BitXor: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() ^ rhs->as(); + return IntPool::getInstance().createInt(result); + } return std::make_shared(bit_xor(*lhs, *rhs)); } case Operator::ShiftLeft: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() << rhs->as(); + return IntPool::getInstance().createInt(result); + } return std::make_shared(shift_left(*lhs, *rhs)); } case Operator::ShiftRight: { ObjectPtr lhs = eval(lexp, ctx); ObjectPtr rhs = eval(rexp, ctx); + + if (lhs->is() && rhs->is()) + { + ValueType::IntClass result = lhs->as() >> rhs->as(); + return IntPool::getInstance().createInt(result); + } return std::make_shared(shift_right(*lhs, *rhs)); } @@ -439,7 +879,6 @@ namespace Fig 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)), @@ -721,306 +1160,8 @@ namespace Fig } case AstType::InitExpr: { auto initExpr = std::static_pointer_cast(exp); - LvObject structeLv = evalLv(initExpr->structe, ctx); - ObjectPtr structTypeVal = structeLv.get(); - const FString &structName = structeLv.name(); - if (!structTypeVal->is()) - { - throw EvaluatorError(u8"NotAStructTypeError", - std::format("'{}' is not a structure type", structName.toBasicString()), - initExpr); - } - const StructType &structT = structTypeVal->as(); - - if (structT.builtin) - { - const TypeInfo &type = structT.type; - auto &args = initExpr->args; - size_t argSize = args.size(); - - if (argSize > 1) - { - throw EvaluatorError( - u8"StructInitArgumentMismatchError", - std::format("Builtin class `{}` expects 0 or 1 argument, but {} were provided", - type.toString().toBasicString(), - argSize), - initExpr); - } - - // default value - if (argSize == 0) - { - if (type == ValueType::Any || type == ValueType::Null || type == ValueType::Function) - { - throw EvaluatorError( - u8"BuiltinNotConstructibleError", - std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), - initExpr); - } - return std::make_shared(Object::defaultValue(type)); - } - - ObjectPtr val = eval(args[0].second, ctx); - - auto err = [&](const char *msg) { - throw EvaluatorError( - u8"BuiltinInitTypeMismatchError", - std::format("Builtin `{}` constructor {}", type.toString().toBasicString(), msg), - initExpr); - }; - - // ===================== Int ===================== - if (type == ValueType::Int) - { - if (!val->is()) err("expects Int"); - return std::make_shared(val->as()); - } - - // ===================== Double ===================== - if (type == ValueType::Double) - { - if (!val->is()) err("expects Double"); - return std::make_shared(val->as()); - } - - // ===================== Bool ===================== - if (type == ValueType::Bool) - { - if (!val->is()) err("expects Bool"); - return std::make_shared(val->as()); - } - - // ===================== String ===================== - if (type == ValueType::String) - { - if (!val->is()) err("expects String"); - return std::make_shared(val->as()); - } - - // ===================== Null ===================== - if (type == ValueType::Null) - { - // Null basically ignores input but keep invariant strict: - if (!val->is()) err("expects Null"); - return Object::getNullInstance(); - } - - // ===================== List ===================== - if (type == ValueType::List) - { - if (!val->is()) err("expects List"); - - const auto &src = val->as(); - auto copied = std::make_shared(List{}); - - auto &dst = copied->as(); - dst.reserve(src.size()); - for (auto &e : src) dst.push_back(e); // shallow element copy, but new container - - return copied; - } - - // ===================== Map ===================== - if (type == ValueType::Map) - { - if (!val->is()) err("expects Map"); - - const auto &src = val->as(); - auto copied = std::make_shared(Map{}); - - auto &dst = copied->as(); - for (auto &[k, v] : src) dst.emplace(k, v); - - return copied; - } - - throw EvaluatorError( - u8"BuiltinNotConstructibleError", - std::format("Builtin type `{}` cannot be constructed", type.toString().toBasicString()), - initExpr); - } - - 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", - 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("", 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 (!isTypeMatch(expectedType, defaultVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format( - "In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), - initExpr); - } - - instanceCtx->def(fieldName, expectedType, field.am, defaultVal); - continue; - } - - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(expectedType, argVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(argVal).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(), - 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 (!isTypeMatch(expectedType, defaultVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format( - "In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - expectedType.toString().toBasicString(), - prettyType(defaultVal).toBasicString()), - initExpr); - } - - instanceCtx->def(fieldName, field.type, field.am, defaultVal); - continue; - } - const ObjectPtr &argVal = evaluatedArgs[i].second; - if (!isTypeMatch(field.type, argVal, ctx)) - { - throw EvaluatorError( - u8"StructFieldTypeMismatchError", - std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", - structName.toBasicString(), - fieldName.toBasicString(), - field.type.toString().toBasicString(), - prettyType(argVal).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 - // )); - // } - - ContextPtr stDefCtx = structT.defContext; - - // load struct method - for (auto &[id, fn] : stDefCtx->getFunctions()) - { - auto funcNameOpt = stDefCtx->getFunctionName(id); - assert(funcNameOpt.has_value()); - - const FString &funcName = *funcNameOpt; - auto funcSlot = stDefCtx->get(funcName); - - instanceCtx->def( - funcName, - ValueType::Function, - funcSlot->am, - std::make_shared(Function( - fn.paras, - fn.retType, - fn.body, - instanceCtx - )) - ); - } - - return std::make_shared(StructInstance(structT.type, instanceCtx)); + assert(initExpr != nullptr); + return evalInitExpr(initExpr, ctx); } case AstType::ListExpr: { @@ -1081,18 +1222,22 @@ namespace Fig 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()) + const Ast::Expression &declaredTypeExp = varDef->declaredType; + + if (varDef->followupType) { declaredType = actualType(value); } + else if (declaredTypeExp) { - declaredType = TypeInfo(declaredTypeName); + ObjectPtr declaredTypeValue = eval(declaredTypeExp, ctx); + declaredType = actualType(declaredTypeValue); + if (value != nullptr && !isTypeMatch(declaredType, value, ctx)) { throw EvaluatorError(u8"TypeError", std::format("Variable `{}` expects init-value type `{}`, but got '{}'", varDef->name.toBasicString(), - declaredTypeName.toBasicString(), + prettyType(declaredTypeValue).toBasicString(), prettyType(value).toBasicString()), varDef->expr); } @@ -1120,7 +1265,14 @@ namespace Fig std::format("Function `{}` already declared in this scope", fnName.toBasicString()), fnDef); } - Function fn(fnDef->paras, TypeInfo(fnDef->retType), fnDef->body, ctx); + TypeInfo returnType = ValueType::Any; + if (fnDef->retType) + { + ObjectPtr returnTypeValue = eval(fnDef->retType, ctx); + returnType = actualType(returnTypeValue); + } + + Function fn(fnDef->paras, returnType, fnDef->body, ctx); ctx->def(fnName, ValueType::Function, (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const), @@ -1151,7 +1303,14 @@ namespace Fig stDef->name.toBasicString()), stDef); } - fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr)); + TypeInfo fieldType = ValueType::Any; + if (field.declaredType) + { + ObjectPtr declaredTypeValue = eval(field.declaredType, ctx); + fieldType = actualType(declaredTypeValue); + } + + fields.push_back(Field(field.am, field.fieldName, fieldType, field.defaultValueExpr)); } ContextPtr defContext = std::make_shared(FString(std::format("", stDef->name.toBasicString(), @@ -1322,8 +1481,10 @@ namespace Fig implemented.insert(name); + ObjectPtr returnTypeValue = eval(ifMethod.returnType, ctx); + record.implMethods[name] = - Function(implMethod.paras, TypeInfo(ifMethod.returnType), implMethod.body, ctx); + Function(implMethod.paras, actualType(returnTypeValue), implMethod.body, ctx); } for (auto &m : interface.methods) @@ -1499,13 +1660,11 @@ namespace Fig case BreakSt: { if (!ctx->parent) { - throw EvaluatorError( - u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); + throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); } if (!ctx->isInLoopContext()) { - throw EvaluatorError( - u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); + throw EvaluatorError(u8"BreakOutsideLoopError", u8"`break` statement outside loop", stmt); } return StatementResult::breakFlow(); } @@ -1513,13 +1672,11 @@ namespace Fig case ContinueSt: { if (!ctx->parent) { - throw EvaluatorError( - u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); + throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); } if (!ctx->isInLoopContext()) { - throw EvaluatorError( - u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); + throw EvaluatorError(u8"ContinueOutsideLoopError", u8"`continue` statement outside loop", stmt); } return StatementResult::continueFlow(); } @@ -1531,6 +1688,15 @@ namespace Fig return StatementResult::normal(eval(exprStmt->exp, ctx)); } + case BlockStatement: { + auto block = std::static_pointer_cast(stmt); + assert(block != nullptr); + + ContextPtr blockCtx = std::make_shared( + FString(std::format("", block->getAAI().line, block->getAAI().column)), ctx); + return evalBlockStatement(block, blockCtx); + } + default: throw RuntimeError( FString(std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); @@ -1690,9 +1856,8 @@ namespace Fig if (ctx->containsInThisScope(modName)) { - throw EvaluatorError(u8"RedeclarationError", - std::format("{} has already been declared.", modName.toBasicString()), - i); + throw EvaluatorError( + u8"RedeclarationError", std::format("{} has already been declared.", modName.toBasicString()), i); } ctx->def( modName, ValueType::Module, AccessModifier::PublicConst, std::make_shared(Module(modName, modCtx))); diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index f42c420..b6c6172 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -1,3 +1,4 @@ +#include "Ast/Expressions/InitExpr.hpp" #include #include #include @@ -127,7 +128,7 @@ namespace Fig LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b] /* Right-value eval*/ - + RvObject evalInitExpr(Ast::InitExpr, ContextPtr); // only allows evalUnary to call RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *.... RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr diff --git a/src/Lexer/lexer.cpp b/src/Lexer/lexer.cpp index 45399ed..db4cd3f 100644 --- a/src/Lexer/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -66,7 +66,10 @@ namespace Fig {FString(u8"["), TokenType::LeftBracket}, {FString(u8"]"), TokenType::RightBracket}, {FString(u8"{"), TokenType::LeftBrace}, - {FString(u8"}"), TokenType::RightBrace}}; + {FString(u8"}"), TokenType::RightBrace}, + {FString(u8"?"), TokenType::Question}, + {FString(u8"!"), TokenType::Not}, + }; const std::unordered_map Lexer::keyword_map{ {FString(u8"and"), TokenType::And}, @@ -81,6 +84,7 @@ namespace Fig {FString(u8"for"), TokenType::For}, {FString(u8"if"), TokenType::If}, {FString(u8"else"), TokenType::Else}, + {FString(u8"new"), TokenType::New}, {FString(u8"struct"), TokenType::Struct}, {FString(u8"interface"), TokenType::Interface}, {FString(u8"impl"), TokenType::Implement}, diff --git a/src/Module/Library/std/formater/formater.fig b/src/Module/Library/std/formater/formater.fig index ef4b321..41e89e7 100644 --- a/src/Module/Library/std/formater/formater.fig +++ b/src/Module/Library/std/formater/formater.fig @@ -33,14 +33,14 @@ public func format(objects ...) -> Any { if objects.length() < 1 { - throw FormatError{"Require format string"}; + throw new FormatError{"Require format string"}; } var fmt := objects[0]; var fmtType := value.type(fmt); if fmtType != "String" { - throw FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; + throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; } var result := ""; @@ -56,7 +56,7 @@ public func format(objects ...) -> Any { if (i + 1 >= length) { - throw FormatError{"unclosed brace"}; + throw new FormatError{"unclosed brace"}; } var nextChar = fmt[i + 1]; @@ -80,12 +80,12 @@ public func format(objects ...) -> Any if endIndex == -1 { - throw FormatError{"unclosed brace"}; + throw new FormatError{"unclosed brace"}; } if argIndex >= objects.length() { - throw FormatError{"require enough format expression"}; + throw new FormatError{"require enough format expression"}; } result += value.string_from(objects[argIndex]); @@ -102,7 +102,7 @@ public func format(objects ...) -> Any continue; } - throw FormatError{"invalid format syntax"}; + throw new FormatError{"invalid format syntax"}; } else { @@ -122,14 +122,14 @@ public func formatByListArgs(objects) -> Any } if objects.length() < 1 { - throw FormatError{"Require format string"}; + throw new FormatError{"Require format string"}; } var fmt := objects[0]; var fmtType := value.type(fmt); if fmtType != "String" { - throw FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; + throw new FormatError{"arg 0 (fmt) must be String type, got " + fmtType}; } var result := ""; @@ -145,7 +145,7 @@ if objects.length() < 1 { if (i + 1 >= length) { - throw FormatError{"unclosed brace"}; + throw new FormatError{"unclosed brace"}; } var nextChar = fmt[i + 1]; @@ -169,12 +169,12 @@ if objects.length() < 1 if endIndex == -1 { - throw FormatError{"unclosed brace"}; + throw new FormatError{"unclosed brace"}; } if argIndex >= objects.length() { - throw FormatError{"require enough format expression"}; + throw new FormatError{"require enough format expression"}; } result += value.string_from(objects[argIndex]); @@ -191,7 +191,7 @@ if objects.length() < 1 continue; } - throw FormatError{"invalid format syntax"}; + throw new FormatError{"invalid format syntax"}; } else { diff --git a/src/Module/Library/std/time/time.fig b/src/Module/Library/std/time/time.fig index 1f62cfd..e1346a0 100644 --- a/src/Module/Library/std/time/time.fig +++ b/src/Module/Library/std/time/time.fig @@ -47,5 +47,5 @@ public struct Time public func now() -> Time { - return Time{__ftime_now_ns()}; + return new Time{__ftime_now_ns()}; } \ No newline at end of file diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index aad22a0..c15e7d8 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -1,5 +1,8 @@ #pragma once +#include "Ast/Expressions/VarExpr.hpp" +#include "Ast/Statements/VarDef.hpp" +#include "Ast/astBase.hpp" #include #include #include @@ -35,11 +38,19 @@ namespace Fig {u8"true", Object::getTrueInstance()}, {u8"false", Object::getFalseInstance()}, {u8"Error", - std::make_shared(InterfaceType( - ErrorInterfaceTypeInfo, - {Ast::InterfaceMethod(u8"toString", Ast::FunctionParameters({}, {}), u8"String", nullptr), - Ast::InterfaceMethod(u8"getErrorClass", Ast::FunctionParameters({}, {}), u8"String", nullptr), - Ast::InterfaceMethod(u8"getErrorMessage", Ast::FunctionParameters({}, {}), u8"String", nullptr)}))}, + std::make_shared(InterfaceType(ErrorInterfaceTypeInfo, + {Ast::InterfaceMethod(u8"toString", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr), + Ast::InterfaceMethod(u8"getErrorClass", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr), + Ast::InterfaceMethod(u8"getErrorMessage", + Ast::FunctionParameters({}, {}), + std::make_shared(u8"String"), + nullptr)}))}, {u8"Any", std::make_shared(StructType(ValueType::Any, nullptr, {}, true))}, {u8"Int", std::make_shared(StructType(ValueType::Int, nullptr, {}, true))}, diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index 6f0841e..8b2025c 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -1,9 +1,9 @@ -#include "Ast/Statements/ErrorFlow.hpp" -#include "Ast/Statements/ImplementSt.hpp" -#include "Ast/astBase.hpp" -#include "Ast/functionParameters.hpp" -#include "Error/error.hpp" -#include "Token/token.hpp" +#include +#include +#include +#include +#include +#include #include namespace Fig @@ -54,17 +54,17 @@ namespace Fig // // 点运算符 // {Ast::Operator::Dot, {40, 41}}, + {Ast::Operator::TernaryCond, {3, 2}}, }; const std::unordered_map Parser::unaryOpPrecedence = { {Ast::Operator::Subtract, 150}, // - - {Ast::Operator::BitAnd, 150}, // & - {Ast::Operator::BitNot, 150}, // ~ - {Ast::Operator::Not, 150}, // ! + {Ast::Operator::BitAnd, 150}, // & + {Ast::Operator::BitNot, 150}, // ~ + {Ast::Operator::Not, 150}, // ! }; - Ast::VarDef - Parser::__parseVarDef(bool isPublic) + Ast::VarDef Parser::__parseVarDef(bool isPublic) { // entry: current is keyword `var` or `const` bool isConst = (currentToken().getType() == TokenType::Const ? true : false); @@ -72,31 +72,31 @@ namespace Fig expect(TokenType::Identifier); FString name = currentToken().getValue(); next(); - FString tiName = ValueType::Any.name; + Ast::Expression declaredType = nullptr; bool hasSpecificType = false; if (isThis(TokenType::Colon)) // : { - expectPeek(TokenType::Identifier, FString(u8"Type name")); - next(); - tiName = currentToken().getValue(); - next(); + next(); // consume `:` + declaredType = parseExpression(0, TokenType::Assign, TokenType::Semicolon); hasSpecificType = true; } if (isThis(TokenType::Semicolon)) { next(); // consume `;`, no using expectConsume here cause we don't need to check again - return makeAst(isPublic, isConst, name, tiName, nullptr); + return makeAst(isPublic, isConst, name, declaredType, nullptr, false); } if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus"); + bool followupType = false; + if (isThis(TokenType::Walrus)) { if (hasSpecificType) throwAddressableError(FString(u8"")); - tiName = Parser::varDefTypeFollowed; + followupType = true; } next(); Ast::Expression exp = parseExpression(0); expectSemicolon(); - return makeAst(isPublic, isConst, name, tiName, exp); + return makeAst(isPublic, isConst, name, declaredType, exp, followupType); } ObjectPtr Parser::__parseValue() @@ -133,18 +133,12 @@ namespace Fig return std::make_shared(i); } } - else if (currentToken().getType() == TokenType::LiteralString) - { - return std::make_shared(_val); - } + else if (currentToken().getType() == TokenType::LiteralString) { return std::make_shared(_val); } else if (currentToken().getType() == TokenType::LiteralBool) { return std::make_shared((_val == u8"true" ? true : false)); } - else if (currentToken().getType() == TokenType::LiteralNull) - { - return Object::getNullInstance(); - } + else if (currentToken().getType() == TokenType::LiteralNull) { return Object::getNullInstance(); } else { throw std::runtime_error(std::string("Internal Error at: ") + std::string(__func__)); @@ -237,17 +231,17 @@ namespace Fig next(); expect(TokenType::LeftParen); Ast::FunctionParameters params = __parseFunctionParameters(); - FString retTiName = ValueType::Any.name; + + Ast::Expression returnType; + if (isThis(TokenType::RightArrow)) // -> { next(); // skip `->` - expect(TokenType::Identifier); - retTiName = currentToken().getValue(); - next(); // skip return type + returnType = parseExpression(0, TokenType::LeftBrace, TokenType::Semicolon); } expect(TokenType::LeftBrace); Ast::BlockStatement body = __parseBlockStatement(); - return makeAst(funcName, params, isPublic, retTiName, body); + return makeAst(funcName, params, isPublic, returnType, body); } Ast::StructDef Parser::__parseStructDef(bool isPublic) { @@ -289,13 +283,11 @@ namespace Fig { throwAddressableError(FString(std::format("expect field name or field attribute"))); } - FString tiName = ValueType::Any.name; + Ast::Expression fieldType = nullptr; if (isThis(TokenType::Colon)) { - next(); - expect(TokenType::Identifier, u8"type name"); - tiName = currentToken().getValue(); - next(); + next(); // consume `:` + fieldType = parseExpression(0, TokenType::Assign, TokenType::Semicolon); } Ast::Expression initExpr = nullptr; if (isThis(TokenType::Assign)) @@ -305,7 +297,7 @@ namespace Fig initExpr = parseExpression(0); } expectSemicolon(); - return Ast::StructDefField(am, fieldName, tiName, initExpr); + return Ast::StructDefField(am, fieldName, fieldType, initExpr); }; std::vector stmts; std::vector fields; @@ -318,10 +310,7 @@ namespace Fig next(); // consume `}` break; } - if (isThis(TokenType::Identifier)) - { - fields.push_back(__parseStructField(false)); - } + if (isThis(TokenType::Identifier)) { fields.push_back(__parseStructField(false)); } else if (isThis(TokenType::Public)) { if (isNext(TokenType::Const)) @@ -361,23 +350,18 @@ namespace Fig next(); // consume `struct` stmts.push_back(__parseStructDef(false)); } - else if (isThis(TokenType::Const)) - { - fields.push_back(__parseStructField(false)); - } + else if (isThis(TokenType::Const)) { fields.push_back(__parseStructField(false)); } else if (isThis(TokenType::Variable)) { - throwAddressableError(FString("Variables are not allowed to be defined within a structure.")); + throwAddressableError( + FString("Variables are not allowed to be defined within a structure.")); } else { throwAddressableError(FString("Invalid syntax")); } } - if (!braceClosed) - { - throwAddressableError(FString("braces are not closed")); - } + if (!braceClosed) { throwAddressableError(FString("braces are not closed")); } return makeAst(isPublic, structName, fields, makeAst(stmts)); } @@ -409,27 +393,18 @@ namespace Fig expect(TokenType::RightArrow); // -> next(); // consume `->` - expect(TokenType::Identifier, u8"return type"); - FString returnType = currentToken().getValue(); - next(); // consume return type + Ast::Expression returnType = parseExpression(0, TokenType::LeftBrace, TokenType::Semicolon); if (isThis(TokenType::LeftBrace)) { Ast::BlockStatement block = __parseBlockStatement(); - methods.push_back(Ast::InterfaceMethod( - funcName, - paras, - returnType, - block)); + methods.push_back(Ast::InterfaceMethod(funcName, paras, returnType, block)); continue; } expectSemicolon(); - methods.push_back(Ast::InterfaceMethod( - funcName, - paras, - returnType)); + methods.push_back(Ast::InterfaceMethod(funcName, paras, returnType)); } else { @@ -454,7 +429,7 @@ namespace Fig FString structName = currentToken().getValue(); next(); // consume name expect(TokenType::LeftBrace); // { - next(); // consume `{` + next(); // consume `{` std::vector methods; @@ -473,10 +448,7 @@ namespace Fig Ast::FunctionParameters paras = __parseFunctionParameters(); expect(TokenType::LeftBrace); Ast::BlockStatement body = __parseBlockStatement(); - methods.push_back(Ast::ImplementMethod( - funcName, - paras, - body)); + methods.push_back(Ast::ImplementMethod(funcName, paras, body)); } else { @@ -501,18 +473,18 @@ namespace Fig { // entry: current is `try` next(); // consume `try` - + /* try { - ... + ... } catch(e: IOError) { } catch(e: TimeOutError) { - } + } */ expect(TokenType::LeftBrace); Ast::BlockStatement body = __parseBlockStatement(); @@ -540,15 +512,13 @@ namespace Fig hasType = true; } expect(TokenType::RightParen); // ) - next(); // consume `)` - expect(TokenType::LeftBrace); // { + next(); // consume `)` + expect(TokenType::LeftBrace); // { Ast::BlockStatement catchBody = __parseBlockStatement(); - if (hasType) + if (hasType) { catches.push_back(Ast::Catch(errVarName, errVarType, catchBody)); } + else { - catches.push_back(Ast::Catch(errVarName, errVarType, catchBody)); - } - else { catches.push_back(Ast::Catch(errVarName, catchBody)); } } @@ -563,7 +533,7 @@ namespace Fig expect(TokenType::LeftBrace); finallyBlock = __parseBlockStatement(); } - else + else { break; } @@ -575,17 +545,11 @@ namespace Fig { Ast::Statement stmt; if (isThis(TokenType::EndOfFile)) { return makeAst(); } - else if (isThis(TokenType::Import)) - { - stmt = __parseImport(); - } + else if (isThis(TokenType::Import)) { stmt = __parseImport(); } else if (isThis(TokenType::Public)) { next(); // consume `public` - if (isThis(TokenType::Variable) || isThis(TokenType::Const)) - { - stmt = __parseVarDef(true); - } + if (isThis(TokenType::Variable) || isThis(TokenType::Const)) { stmt = __parseVarDef(true); } else if (isThis(TokenType::Function) and isNext(TokenType::Identifier)) { next(); @@ -596,19 +560,14 @@ namespace Fig next(); stmt = __parseStructDef(true); } - else if (isThis(TokenType::Interface)) - { - stmt = __parseInterfaceDef(true); - } + else if (isThis(TokenType::Interface)) { stmt = __parseInterfaceDef(true); } else { - throwAddressableError(FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`")); + throwAddressableError( + FString(u8"Expected `var`, `const`, `function`, `struct` or `interface` after `public`")); } } - else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) - { - stmt = __parseVarDef(false); - } + else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) { stmt = __parseVarDef(false); } else if (isThis(TokenType::Function) and isNext(TokenType::Identifier)) { next(); @@ -626,50 +585,20 @@ namespace Fig next(); stmt = __parseInterfaceDef(false); } - else if (isThis(TokenType::Implement)) - { - stmt = __parseImplement(); - } - else if (isThis(TokenType::If)) - { - stmt = __parseIf(); - } + else if (isThis(TokenType::Implement)) { stmt = __parseImplement(); } + else if (isThis(TokenType::If)) { stmt = __parseIf(); } else if (isThis(TokenType::Else)) { throwAddressableError(FString(u8"`else` without matching `if`")); } - else if (isThis(TokenType::LeftBrace)) - { - stmt = __parseBlockStatement(); - } - else if (isThis(TokenType::While)) - { - stmt = __parseWhile(); - } - else if (isThis(TokenType::For)) - { - stmt = __parseFor(); - } - else if (isThis(TokenType::Return)) - { - stmt = __parseReturn(); - } - else if (isThis(TokenType::Break)) - { - stmt = __parseBreak(); - } - else if (isThis(TokenType::Continue)) - { - stmt = __parseContinue(); - } - else if (isThis(TokenType::Throw)) - { - stmt = __parseThrow(); - } - else if (isThis(TokenType::Try)) - { - stmt = __parseTry(); - } + else if (isThis(TokenType::LeftBrace)) { stmt = __parseBlockStatement(); } + else if (isThis(TokenType::While)) { stmt = __parseWhile(); } + else if (isThis(TokenType::For)) { stmt = __parseFor(); } + else if (isThis(TokenType::Return)) { stmt = __parseReturn(); } + else if (isThis(TokenType::Break)) { stmt = __parseBreak(); } + else if (isThis(TokenType::Continue)) { stmt = __parseContinue(); } + else if (isThis(TokenType::Throw)) { stmt = __parseThrow(); } + else if (isThis(TokenType::Try)) { stmt = __parseTry(); } else if (allowExp) { // expression statement @@ -677,7 +606,7 @@ namespace Fig expectSemicolon(); stmt = makeAst(exp); } - else + else { throwAddressableError(u8"invalid syntax", currentAAI.line, currentAAI.column); } @@ -766,7 +695,7 @@ namespace Fig } else { - condition = parseExpression(0); + condition = parseExpression(0, TokenType::LeftBrace); } expect(TokenType::LeftBrace); // { Ast::BlockStatement body = __parseBlockStatement(); @@ -784,7 +713,8 @@ namespace Fig throwAddressableError(u8"BlockStatement cannot be used as for loop increment"); } - if (isThis(TokenType::If) || isThis(TokenType::While) || isThis(TokenType::For) || isThis(TokenType::Return) || isThis(TokenType::Break) || isThis(TokenType::Continue)) + if (isThis(TokenType::If) || isThis(TokenType::While) || isThis(TokenType::For) || isThis(TokenType::Return) + || isThis(TokenType::Break) || isThis(TokenType::Continue)) { throwAddressableError(u8"Control flow statements cannot be used as for loop increment"); } @@ -799,8 +729,7 @@ namespace Fig // TODO: support enumeration next(); // consume `for` bool paren = isThis(TokenType::LeftParen); - if (paren) - next(); // consume `(` + if (paren) next(); // consume `(` // support 3-part for loop // for init; condition; increment {} Ast::Statement initStmt = __parseStatement(false); // auto check `` @@ -813,8 +742,7 @@ namespace Fig // auto guard = disableSemicolon(); incrementStmt = __parseIncrementStatement(); } // after parse increment, semicolon check state restored - if (paren) - expectConsume(TokenType::RightParen); // consume `)` if has `(` + if (paren) expectConsume(TokenType::RightParen); // consume `)` if has `(` expect(TokenType::LeftBrace); // { Ast::BlockStatement body = __parseBlockStatement(); // auto consume `}` return makeAst(initStmt, condition, incrementStmt, body); @@ -938,10 +866,7 @@ namespace Fig { if (mode == 0) { - if (isThis(TokenType::Identifier) && isNext(TokenType::Colon)) - { - mode = 2; - } + if (isThis(TokenType::Identifier) && isNext(TokenType::Colon)) { mode = 2; } else if (isThis(TokenType::Identifier) && (isNext(TokenType::Comma) || isNext(TokenType::RightBrace))) { mode = 3; @@ -985,17 +910,19 @@ namespace Fig } else if (!isThis(TokenType::RightBrace)) { - throwAddressableError(FString( - std::format("Expect `,` or `}}` in struct initialization expression, got {}", - currentToken().toString().toBasicString()))); + throwAddressableError( + FString(std::format("Expect `,` or `}}` in struct initialization expression, got {}", + currentToken().toString().toBasicString()))); } } expect(TokenType::RightBrace); next(); // consume `}` - return makeAst(structe, args, - (mode == 1 ? Ast::InitExprAst::InitMode::Positional : - (mode == 2 ? Ast::InitExprAst::InitMode::Named : Ast::InitExprAst::InitMode::Shorthand))); + return makeAst( + structe, + args, + static_cast(mode)); } + Ast::Expression Parser::__parseTupleOrParenExpr() { next(); @@ -1017,8 +944,7 @@ namespace Fig { next(); // consume ',' - if (currentToken().getType() == TokenType::RightParen) - break; + if (currentToken().getType() == TokenType::RightParen) break; elements.push_back(parseExpression(0)); } @@ -1069,10 +995,7 @@ namespace Fig expect(TokenType::Identifier, u8"package name"); path.push_back(currentToken().getValue()); next(); // consume package name - if (isThis(TokenType::Semicolon)) - { - break; - } + if (isThis(TokenType::Semicolon)) { break; } else if (isThis(TokenType::Dot)) { next(); // consume `.` @@ -1092,8 +1015,7 @@ namespace Fig Ast::Operator op; Token tok = currentToken(); - if (tok == EOFTok) - throwAddressableError(FString(u8"Unexpected end of expression")); + if (tok == EOFTok) throwAddressableError(FString(u8"Unexpected end of expression")); if (tok.getType() == stop || tok.getType() == stop2) { if (lhs == nullptr) throwAddressableError(FString(u8"Expected expression")); @@ -1139,6 +1061,14 @@ namespace Fig next(); lhs = makeAst(op, parseExpression(bp, stop, stop2)); } + else if (tok.getType() == TokenType::New) + { + // `new` now is an independent syntax + next(); + Ast::Expression operand = parseExpression(bp, TokenType::LeftBrace); + expect(TokenType::LeftBrace); + lhs = __parseInitExpr(operand); + } else { throwAddressableError(FString(u8"Unexpected token in expression:") + tok.toString()); @@ -1148,21 +1078,31 @@ namespace Fig while (true) { tok = currentToken(); - if (tok.getType() == stop || tok.getType() == stop2|| tok == EOFTok) break; + if (tok.getType() == stop || tok.getType() == stop2 || tok == EOFTok) break; /* Postfix */ + if (tok.getType() == TokenType::LeftBrace) + { + throwAddressableError( + FString(u8"Since Fig v0.4.2, please use new struct{} to avoid syntax ambiguity")); + } + if (tok.getType() == TokenType::LeftParen) { lhs = __parseCall(lhs); continue; } + // else if (tok.getType() == TokenType::LeftBrace) { lhs = __parseInitExpr(lhs); } + /* + since Fig v0.4.2, use new struct{}; - if (tok.getType() == TokenType::LeftBrace) - { - lhs = __parseInitExpr(lhs); - continue; - } + if a == A{} + is A{} struct init? + or A a variable, {} is the body? + + fuck. + */ // member access: a.b if (tok.getType() == TokenType::Dot) @@ -1189,15 +1129,17 @@ namespace Fig lhs = makeAst(lhs, indexExpr); continue; } - // ternary if (tok.getType() == TokenType::Question) { + auto [lbp, rbp] = getBindingPower(Ast::Operator::TernaryCond); + if (bp >= lbp) break; + next(); // consume ? - Ast::Expression trueExpr = parseExpression(0, TokenType::Colon, stop2); - expect(TokenType::Colon); - next(); // consume : - Ast::Expression falseExpr = parseExpression(0, TokenType::Semicolon, stop2); + Ast::Expression trueExpr = parseExpression(0, TokenType::Colon); + expectConsume(TokenType::Colon); + + Ast::Expression falseExpr = parseExpression(0); lhs = makeAst(lhs, trueExpr, falseExpr); continue; } @@ -1219,10 +1161,7 @@ namespace Fig { output.clear(); Token tok = currentToken(); - if (tok == EOFTok) - { - return output; - } + if (tok == EOFTok) { return output; } while (!isEOF()) { diff --git a/src/Parser/parser.hpp b/src/Parser/parser.hpp index 7cad4d5..5d42a5d 100644 --- a/src/Parser/parser.hpp +++ b/src/Parser/parser.hpp @@ -1,13 +1,12 @@ #pragma once -#include "Ast/astBase.hpp" +#include #include #include #include #include #include -#include #include #include @@ -248,7 +247,7 @@ namespace Fig [[nodiscard]] SemicolonDisabler disableSemicolon() { return SemicolonDisabler(this); } - void expectSemicolon() + void expectSemicolon(std::source_location loc = std::source_location::current()) { // if need semicolon and stream has `;`, consume it. if not need semicolon, do nothing @@ -263,18 +262,18 @@ namespace Fig } // normal semicolon check - expectConsume(TokenType::Semicolon); + expectConsume(TokenType::Semicolon, loc); } - void expectConsume(TokenType type, FString expected) + void expectConsume(TokenType type, FString expected, std::source_location loc = std::source_location::current()) { - expect(type, expected); + expect(type, expected, loc); next(); } - void expectConsume(TokenType type) + void expectConsume(TokenType type, std::source_location loc = std::source_location::current()) { - expect(type); + expect(type, loc); next(); } @@ -314,6 +313,8 @@ namespace Fig Ast::MapExpr __parseMapExpr(); // entry: current is `{` Ast::InitExpr __parseInitExpr(Ast::Expression); // entry: current is `{`, ahead is struct type exp. + + Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(` Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function diff --git a/src/Token/token.hpp b/src/Token/token.hpp index 551ca65..575d460 100644 --- a/src/Token/token.hpp +++ b/src/Token/token.hpp @@ -31,6 +31,7 @@ namespace Fig For, // for If, // if Else, // else + New, // new Struct, // struct Interface, // interface Implement, // impl diff --git a/src/Utils/AstPrinter.hpp b/src/Utils/AstPrinter.hpp index aaedf0a..ccf024f 100644 --- a/src/Utils/AstPrinter.hpp +++ b/src/Utils/AstPrinter.hpp @@ -111,7 +111,7 @@ private: printFString(node->name, 0); printIndent(indent + 2); std::cout << "Type: "; - printFString(node->typeName, 0); + printFString(node->declaredType->toString(), 0); if (node->expr) { printIndent(indent + 2);