diff --git a/src/Ast/Expressions/ContainerInitExprs.hpp b/src/Ast/Expressions/ContainerInitExprs.hpp index 3072434..0012c5b 100644 --- a/src/Ast/Expressions/ContainerInitExprs.hpp +++ b/src/Ast/Expressions/ContainerInitExprs.hpp @@ -48,14 +48,14 @@ namespace Fig::Ast class MapExprAst final : public ExpressionAst { public: - std::map val; + std::map val; MapExprAst() { type = AstType::MapExpr; } - MapExprAst(std::map _val) : + MapExprAst(std::map _val) : val(std::move(_val)) { type = AstType::MapExpr; diff --git a/src/Context/context.hpp b/src/Context/context.hpp index 6ba5aa1..d72fec2 100644 --- a/src/Context/context.hpp +++ b/src/Context/context.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Fig { @@ -65,7 +66,7 @@ namespace Fig return it->second; if (parent) return parent->get(name); - throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString()))); } AccessModifier getAccessModifier(const FString &name) { @@ -79,7 +80,7 @@ namespace Fig } else { - throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString()))); } } bool isVariableMutable(const FString &name) @@ -98,7 +99,7 @@ namespace Fig { if (!isVariableMutable(name)) { - throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' is immutable", name.toBasicString()))); } variables[name]->value = value; } @@ -108,7 +109,7 @@ namespace Fig } else { - throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString()))); } } void _update(const FString &name, ObjectPtr value) @@ -123,14 +124,14 @@ namespace Fig } else { - throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' not defined", name.toBasicString()))); } } void def(const FString &name, const TypeInfo &ti, AccessModifier am, const ObjectPtr &value = Object::getNullInstance()) { if (containsInThisScope(name)) { - throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString()))); + throw RuntimeError(FString(std::format("Variable '{}' already defined in this scope", name.toBasicString()))); } variables[name] = std::make_shared( name, diff --git a/src/Core/fig_string.hpp b/src/Core/fig_string.hpp index 4084e29..5391a24 100644 --- a/src/Core/fig_string.hpp +++ b/src/Core/fig_string.hpp @@ -15,7 +15,7 @@ namespace Fig static FStringView fromBasicStringView(std::string_view sv) { - return FStringView(reinterpret_cast(sv.data()), sv.size()); + return FStringView(reinterpret_cast(sv.data())); } explicit FStringView(std::string_view sv) @@ -28,12 +28,22 @@ namespace Fig *this = fromBasicStringView(std::string_view("")); } + std::string_view toBasicStringView() const + { + return std::string_view(reinterpret_cast(data()), size()); + } }; class FString : public std::u8string { public: using std::u8string::u8string; + + FString operator+(const FString& x) + { + return FString(toBasicString() + x.toBasicString()); + } + explicit FString(const std::u8string &str) { *this = fromU8String(str); @@ -70,7 +80,7 @@ namespace Fig return FString(str.begin(), str.end()); } - size_t length() + size_t length() const { // get UTF8-String real length size_t len = 0; diff --git a/src/Error/error.hpp b/src/Error/error.hpp index cace7d7..ffe2317 100644 --- a/src/Error/error.hpp +++ b/src/Error/error.hpp @@ -13,7 +13,7 @@ namespace Fig { public: explicit AddressableError() {} - explicit AddressableError(FStringView _msg, + explicit AddressableError(FString _msg, size_t _line, size_t _column, std::source_location loc = std::source_location::current()) : @@ -35,7 +35,7 @@ namespace Fig size_t getLine() const { return line; } size_t getColumn() const { return column; } - FStringView getMessage() const { return message; } + FString getMessage() const { return message; } virtual FString getErrorType() const { @@ -44,14 +44,14 @@ namespace Fig protected: size_t line, column; - FStringView message; + FString message; }; class UnaddressableError : public std::exception { public: explicit UnaddressableError() {} - explicit UnaddressableError(FStringView _msg, + explicit UnaddressableError(FString _msg, std::source_location loc = std::source_location::current()) : src_loc(loc) { @@ -59,7 +59,7 @@ namespace Fig } virtual FString toString() const { - std::string msg = std::format("[UnaddressableError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name()); + std::string msg = std::format("[UnaddressableError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name()); return FString(msg); } const char *what() const noexcept override @@ -68,7 +68,7 @@ namespace Fig return msg.c_str(); } std::source_location src_loc; - FStringView getMessage() const { return message; } + FString getMessage() const { return message; } virtual FString getErrorType() const { @@ -76,7 +76,7 @@ namespace Fig } protected: - FStringView message; + FString message; }; class SyntaxError : public AddressableError @@ -84,7 +84,7 @@ namespace Fig public: using AddressableError::AddressableError; - explicit SyntaxError(FStringView _msg, + explicit SyntaxError(FString _msg, size_t _line, size_t _column, std::source_location loc = std::source_location::current()) : @@ -94,7 +94,7 @@ namespace Fig virtual FString toString() const override { - std::string msg = std::format("[SyntaxError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name()); + std::string msg = std::format("[SyntaxError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name()); return FString(msg); } @@ -108,14 +108,14 @@ namespace Fig { public: using UnaddressableError::UnaddressableError; - explicit RuntimeError(FStringView _msg, + explicit RuntimeError(FString _msg, std::source_location loc = std::source_location::current()) : UnaddressableError(_msg, loc) { } virtual FString toString() const override { - std::string msg = std::format("[RuntimeError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name()); + std::string msg = std::format("[RuntimeError] {} in [{}] {}", this->message.toBasicString(), this->src_loc.file_name(), this->src_loc.function_name()); return FString(msg); } diff --git a/src/Evaluator/evaluator.cpp b/src/Evaluator/evaluator.cpp index 1acecfc..fb0dcb0 100644 --- a/src/Evaluator/evaluator.cpp +++ b/src/Evaluator/evaluator.cpp @@ -22,13 +22,25 @@ namespace Fig LvObject base = evalLv(me->base, ctx); RvObject baseVal = base.get(); const FString &member = me->member; - if (baseVal->getTypeInfo() != ValueType::StructInstance) + if (baseVal->hasMemberFunction(member)) + { + return LvObject(std::make_shared( + member, + std::make_shared( + Function( + baseVal->getMemberFunction(member), + baseVal->getMemberFunctionParaCount(member))), + ValueType::Function, + AccessModifier::PublicConst)); // fake l-value + } + if (baseVal->getTypeInfo() != ValueType::StructInstance) // and not member function found { throw EvaluatorError( - u8"TypeError", + u8"NoAttributeError", std::format( - "`{}` isn't a struct", - base.name().toBasicString()), + "`{}` has not attribute '{}'", + baseVal->toString().toBasicString(), + member.toBasicString()), me->base); } const StructInstance &si = baseVal->as(); @@ -49,11 +61,48 @@ namespace Fig LvObject base = evalLv(ie->base, ctx); RvObject index = eval(ie->index, ctx); - const TypeInfo &type = base.declaredType(); + const TypeInfo &type = base.get()->getTypeInfo(); - if (type != ValueType::List - && type != ValueType::Tuple - && type != ValueType::Map) + if (type == ValueType::List) + { + if (index->getTypeInfo() != ValueType::Int) + { + throw EvaluatorError( + u8"TypeError", + std::format( + "Type `List` indices must be `Int`, got '{}'", + index->getTypeInfo().toString().toBasicString() + ), + ie->index + ); + } + List &list = base.get()->as(); + ValueType::IntClass indexVal = index->as(); + if (indexVal >= list.size()) + { + throw EvaluatorError( + u8"IndexOutOfRangeError", + std::format( + "Index {} out of list `{}` range", + indexVal, + base.get()->toString().toBasicString() + ), + ie->index + ); + } + return LvObject( + base.get(), + indexVal + ); + } + else if (type == ValueType::Map) + { + return LvObject( + base.get(), + index + ); + } + else { throw EvaluatorError( u8"NoSubscriptableError", @@ -62,8 +111,7 @@ namespace Fig base.declaredType().toString().toBasicString()), ie->base); } - // TODO - return LvObject(); + } LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx) { @@ -651,6 +699,30 @@ namespace Fig return std::make_shared(StructInstance(structT.id, instanceCtx)); } + case AstType::ListExpr: { + auto lstExpr = std::dynamic_pointer_cast(exp); + assert(lstExpr != nullptr); + + List list; + for (auto &exp : lstExpr->val) + { + list.push_back(eval(exp, ctx)); + } + return std::make_shared(std::move(list)); + } + + case AstType::MapExpr: { + auto mapExpr = std::dynamic_pointer_cast(exp); + assert(mapExpr != nullptr); + + Map map; + for (auto &[key, value] : mapExpr->val) + { + map[eval(key, ctx)] = eval(value, ctx); + } + return std::make_shared(std::move(map)); + } + default: assert(false); } @@ -990,7 +1062,7 @@ namespace Fig } default: - throw RuntimeError(FStringView( + throw RuntimeError(FString( std::format("Feature stmt {} unsupported yet", magic_enum::enum_name(stmt->getType())))); } diff --git a/src/Evaluator/evaluator.hpp b/src/Evaluator/evaluator.hpp index c8565de..9e1c6ed 100644 --- a/src/Evaluator/evaluator.hpp +++ b/src/Evaluator/evaluator.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace Fig diff --git a/src/Evaluator/evaluator_error.hpp b/src/Evaluator/evaluator_error.hpp index 14fabca..2998c25 100644 --- a/src/Evaluator/evaluator_error.hpp +++ b/src/Evaluator/evaluator_error.hpp @@ -12,7 +12,7 @@ namespace Fig using AddressableError::AddressableError; EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) { - message = FStringView::fromBasicStringView(msg.toBasicString()); + message = msg; line = ast->getAAI().line; column = ast->getAAI().column; @@ -23,7 +23,7 @@ namespace Fig } EvaluatorError(FString _typeName, std::string_view msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) { - message = FStringView::fromBasicStringView(msg); + message = FString::fromBasicString(std::string(msg.data())); line = ast->getAAI().line; column = ast->getAAI().column; diff --git a/src/Lexer/lexer.cpp b/src/Lexer/lexer.cpp index 7571b4e..b499d95 100644 --- a/src/Lexer/lexer.cpp +++ b/src/Lexer/lexer.cpp @@ -190,7 +190,7 @@ namespace Fig } else { - error = SyntaxError(FStringView( + error = SyntaxError(FString( std::format( "Unsupported escape character: {}", FString(ec.getString()).toBasicString())), @@ -307,7 +307,7 @@ namespace Fig } else { - error = SyntaxError(FStringView( + error = SyntaxError(FString( std::format( "Unsupported escape character: {}", FString(ec.getString()).toBasicString())), @@ -369,7 +369,7 @@ namespace Fig // checking legality if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数 { - error = SyntaxError(FStringView( + error = SyntaxError(FString( std::format("Ellegal number literal: {}", numStr.toBasicString())), this->line, it.column()); return IllegalTok; @@ -396,7 +396,7 @@ namespace Fig else if (!this->symbol_map.contains(sym)) { // check legality - error = SyntaxError(FStringView( + error = SyntaxError(FString( std::format("No such a operator: {}", sym.toBasicString())), this->line, it.column()); } @@ -455,7 +455,7 @@ namespace Fig if (!terminated) { - error = SyntaxError(FStringView(u8"Unterminated multiline comment"), this->line, it.column()); + error = SyntaxError(FString(u8"Unterminated multiline comment"), this->line, it.column()); next(); return IllegalTok; } @@ -526,7 +526,7 @@ namespace Fig } else { - error = SyntaxError(FStringView( + error = SyntaxError(FString( std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())), this->line, it.column()); if (hasNext()) diff --git a/src/Module/builtins.hpp b/src/Module/builtins.hpp index cea9fca..6f7f1ff 100644 --- a/src/Module/builtins.hpp +++ b/src/Module/builtins.hpp @@ -36,16 +36,24 @@ namespace Fig const std::unordered_map builtinFunctions{ {u8"__fstdout_print", [](const std::vector &args) -> ObjectPtr { + bool first_flag = true; for (auto arg : args) { - std::print("{}", arg->toString().toBasicString()); + if (!first_flag) + std::print(" "); + std::print("{}", arg->toStringIO().toBasicString()); + first_flag = false; } return std::make_shared(ValueType::IntClass(args.size())); }}, {u8"__fstdout_println", [](const std::vector &args) -> ObjectPtr { + bool first_flag = true; for (auto arg : args) { - std::print("{}", arg->toString().toBasicString()); + if (!first_flag) + std::print(" "); + std::print("{}", arg->toStringIO().toBasicString()); + first_flag = false; } std::print("\n"); return std::make_shared(ValueType::IntClass(args.size())); @@ -72,7 +80,7 @@ namespace Fig } catch (...) { - throw RuntimeError(FStringView(std::format("Invalid int string for parsing", str.toBasicString()))); + throw RuntimeError(FString(std::format("Invalid int string for parsing", str.toBasicString()))); } }}, {u8"__fvalue_int_from", [](const std::vector &args) -> ObjectPtr { @@ -87,7 +95,7 @@ namespace Fig } else { - throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to int", val->getTypeInfo().toString().toBasicString()))); + throw RuntimeError(FString(std::format("Type '{}' cannot be converted to int", val->getTypeInfo().toString().toBasicString()))); } }}, {u8"__fvalue_double_parse", [](const std::vector &args) -> ObjectPtr { @@ -99,7 +107,7 @@ namespace Fig } catch (...) { - throw RuntimeError(FStringView(std::format("Invalid double string for parsing", str.toBasicString()))); + throw RuntimeError(FString(std::format("Invalid double string for parsing", str.toBasicString()))); } }}, {u8"__fvalue_double_from", [](const std::vector &args) -> ObjectPtr { @@ -114,7 +122,7 @@ namespace Fig } else { - throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to double", val->getTypeInfo().toString().toBasicString()))); + throw RuntimeError(FString(std::format("Type '{}' cannot be converted to double", val->getTypeInfo().toString().toBasicString()))); } }}, {u8"__fvalue_string_from", [](const std::vector &args) -> ObjectPtr { @@ -134,7 +142,7 @@ namespace Fig auto it = builtinFunctions.find(name); if (it == builtinFunctions.end()) { - throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString()))); + throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); } return it->second; } @@ -144,7 +152,7 @@ namespace Fig auto it = builtinFunctionArgCounts.find(name); if (it == builtinFunctionArgCounts.end()) { - throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString()))); + throw RuntimeError(FString(std::format("Builtin function '{}' not found", name.toBasicString()))); } return it->second; } diff --git a/src/Parser/parser.cpp b/src/Parser/parser.cpp index 4c3f56a..de4f15f 100644 --- a/src/Parser/parser.cpp +++ b/src/Parser/parser.cpp @@ -69,7 +69,7 @@ namespace Fig if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus"); if (isThis(TokenType::Walrus)) { - if (hasSpecificType) throwAddressableError(FStringView(u8"")); + if (hasSpecificType) throwAddressableError(FString(u8"")); tiName = Parser::varDefTypeFollowed; } next(); @@ -93,7 +93,7 @@ namespace Fig } catch (...) { - throwAddressableError(FStringView(u8"Illegal number literal")); + throwAddressableError(FString(u8"Illegal number literal")); } return std::make_shared(d); } @@ -107,7 +107,7 @@ namespace Fig } catch (...) { - throwAddressableError(FStringView(u8"Illegal number literal")); + throwAddressableError(FString(u8"Illegal number literal")); } return std::make_shared(i); } @@ -253,7 +253,7 @@ namespace Fig } else { - throwAddressableError(FStringView(std::format("expect field name or field attribute"))); + throwAddressableError(FString(std::format("expect field name or field attribute"))); } FString tiName = ValueType::Any.name; if (isThis(TokenType::Colon)) @@ -267,7 +267,7 @@ namespace Fig if (isThis(TokenType::Assign)) { next(); - if (isEOF()) throwAddressableError(FStringView(u8"expect an expression")); + if (isEOF()) throwAddressableError(FString(u8"expect an expression")); initExpr = parseExpression(0); } expectSemicolon(); @@ -314,7 +314,7 @@ namespace Fig } else { - throwAddressableError(FStringView("Invalid syntax")); + throwAddressableError(FString("Invalid syntax")); } } else if (isThis(TokenType::Function)) @@ -333,16 +333,16 @@ namespace Fig } else if (isThis(TokenType::Variable)) { - throwAddressableError(FStringView("Variables are not allowed to be defined within a structure.")); + throwAddressableError(FString("Variables are not allowed to be defined within a structure.")); } else { - throwAddressableError(FStringView("Invalid syntax")); + throwAddressableError(FString("Invalid syntax")); } } if (!braceClosed) { - throwAddressableError(FStringView("braces are not closed")); + throwAddressableError(FString("braces are not closed")); } return makeAst(isPublic, structName, fields, makeAst(stmts)); } @@ -368,7 +368,7 @@ namespace Fig } else { - throwAddressableError(FStringView(u8"Expected `var`, `const`, `function` or `struct` after `public`")); + throwAddressableError(FString(u8"Expected `var`, `const`, `function` or `struct` after `public`")); } } else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) @@ -392,7 +392,7 @@ namespace Fig } else if (isThis(TokenType::Else)) { - throwAddressableError(FStringView(u8"`else` without matching `if`")); + throwAddressableError(FString(u8"`else` without matching `if`")); } else if (isThis(TokenType::LeftBrace)) { @@ -625,15 +625,10 @@ namespace Fig { // entry: current is `{` next(); // consume `{` - std::map val; + std::map val; while (!isThis(TokenType::RightBrace)) { - expect(TokenType::Identifier, FString(u8"key (identifier)")); - FString key = currentToken().getValue(); - if (val.contains(key)) throwAddressableError(FStringView(std::format( - "Redefinition of immutable key {} in mapping literal", - key.toBasicString()))); - next(); // consume key + Ast::Expression key = parseExpression(0, TokenType::Colon); expect(TokenType::Colon); next(); // consume `:` val[key] = parseExpression(0, TokenType::RightBrace, TokenType::Comma); @@ -711,7 +706,7 @@ namespace Fig } else if (!isThis(TokenType::RightBrace)) { - throwAddressableError(FStringView( + throwAddressableError(FString( std::format("Expect `,` or `}}` in struct initialization expression, got {}", currentToken().toString().toBasicString()) )); @@ -762,7 +757,7 @@ namespace Fig } else { - throwAddressableError(FStringView(u8"Expect ')' or ',' after expression in parentheses")); + throwAddressableError(FString(u8"Expect ')' or ',' after expression in parentheses")); } return nullptr; // to suppress compiler warning } @@ -794,10 +789,10 @@ namespace Fig Token tok = currentToken(); if (tok == EOFTok) - throwAddressableError(FStringView(u8"Unexpected end of expression")); + throwAddressableError(FString(u8"Unexpected end of expression")); if (tok.getType() == stop || tok.getType() == stop2) { - if (lhs == nullptr) throwAddressableError(FStringView(u8"Expected expression")); + if (lhs == nullptr) throwAddressableError(FString(u8"Expected expression")); return lhs; } if (tok.getType() == TokenType::LeftBracket) @@ -818,7 +813,7 @@ namespace Fig if (currentToken().getType() == TokenType::Identifier) { // err - throwAddressableError(FStringView(u8"Function literal should not have a name")); + throwAddressableError(FString(u8"Function literal should not have a name")); } expect(TokenType::LeftParen); lhs = __parseFunctionLiteralExpr(); @@ -849,7 +844,7 @@ namespace Fig } else { - throwAddressableError(FStringView(u8"Unexpected token in expression")); + throwAddressableError(FString(u8"Unexpected token in expression")); } // infix / (postfix) ? @@ -872,7 +867,7 @@ namespace Fig next(); // consume '.' Token idTok = currentToken(); if (!idTok.isIdentifier()) - throwAddressableError(FStringView(u8"Expected identifier after '.'")); + throwAddressableError(FString(u8"Expected identifier after '.'")); FString member = idTok.getValue(); next(); // consume identifier diff --git a/src/Parser/parser.hpp b/src/Parser/parser.hpp index a0e49fa..146f3dd 100644 --- a/src/Parser/parser.hpp +++ b/src/Parser/parser.hpp @@ -92,7 +92,7 @@ namespace Fig } template - void throwAddressableError(FStringView msg, size_t line, size_t column, std::source_location loc = std::source_location::current()) + void throwAddressableError(FString msg, size_t line, size_t column, std::source_location loc = std::source_location::current()) { static_assert(std::is_base_of_v, "_ErrT must derive from AddressableError"); @@ -101,7 +101,7 @@ namespace Fig throw spError; } template - void throwAddressableError(FStringView msg, std::source_location loc = std::source_location::current()) + void throwAddressableError(FString msg, std::source_location loc = std::source_location::current()) { static_assert(std::is_base_of_v, "_ErrT must derive from AddressableError"); @@ -112,7 +112,7 @@ namespace Fig } template - void throwUnaddressableError(FStringView msg, std::source_location loc = std::source_location::current()) + void throwUnaddressableError(FString msg, std::source_location loc = std::source_location::current()) { static_assert(std::is_base_of_v, "_ErrT must derive from AddressableError"); @@ -215,7 +215,7 @@ namespace Fig { if (peekToken().getType() != type) { - throwAddressableError(FStringView(std::format("Expected `{}`, but got `{}`", + throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", magic_enum::enum_name(type), magic_enum::enum_name(peekToken().getType())))); } @@ -225,7 +225,7 @@ namespace Fig { if (currentToken().getType() != type) { - throwAddressableError(FStringView(std::format("Expected `{}`, but got `{}`", + throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", magic_enum::enum_name(type), magic_enum::enum_name(currentToken().getType())))); } @@ -235,7 +235,7 @@ namespace Fig { if (peekToken().getType() != type) { - throwAddressableError(FStringView(std::format("Expected `{}`, but got `{}`", + throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", expected.toBasicString(), magic_enum::enum_name(peekToken().getType())))); } @@ -245,7 +245,7 @@ namespace Fig { if (currentToken().getType() != type) { - throwAddressableError(FStringView(std::format("Expected `{}`, but got `{}`", + throwAddressableError(FString(std::format("Expected `{}`, but got `{}`", expected.toBasicString(), magic_enum::enum_name(currentToken().getType())))); } @@ -319,8 +319,8 @@ namespace Fig Ast::Expression __parseCall(Ast::Expression); Ast::ListExpr __parseListExpr(); // entry: current is `[` - Ast::MapExpr __parseMapExpr(); // entry: current is `{` + Ast::InitExpr __parseInitExpr(FString); // entry: current is `{`, ahead is struct name. arg (struct name : FString) Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(` diff --git a/src/Value/LvObject.hpp b/src/Value/LvObject.hpp new file mode 100644 index 0000000..54e5cbe --- /dev/null +++ b/src/Value/LvObject.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include +#include + +namespace Fig +{ + + struct LvObject + { + enum class Kind + { + Variable, + ListElement, + MapElement + } kind; + std::shared_ptr slot; + + ObjectPtr listOrMap = nullptr; + size_t listIndex; + + ObjectPtr mapIndex; + + LvObject(std::shared_ptr _slot) : + slot(std::move(_slot)) + { + kind = Kind::Variable; + } + LvObject(ObjectPtr _v, size_t _index) : + listOrMap(_v), listIndex(_index) + { + assert(_v->getTypeInfo() == ValueType::List); + kind = Kind::ListElement; + } + LvObject(ObjectPtr _v, ObjectPtr _index) : + listOrMap(_v), mapIndex(_index) + { + assert(_v->getTypeInfo() == ValueType::Map); + kind = Kind::MapElement; + } + + const ObjectPtr &get() const + { + if (kind == Kind::Variable) + { + auto s = resolve(slot); + return s->value; + } + else if (kind == Kind::ListElement) + { + List &list = listOrMap->as(); + if (listIndex >= list.size()) + throw RuntimeError(FString( + std::format("Index {} out of range", listIndex))); + return list.at(listIndex); + } + else // map + { + Map &map = listOrMap->as(); + if (!map.contains(mapIndex)) + throw RuntimeError(FString( + std::format("Key {} not found", mapIndex->toString().toBasicString()))); + return map.at(mapIndex); + } + } + + void set(const ObjectPtr &v) + { + if (kind == Kind::Variable) + { + auto s = resolve(slot); + if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo()) + { + throw RuntimeError( + FString( + std::format("Variable `{}` expects type `{}`, but got '{}'", + s->name.toBasicString(), + s->declaredType.toString().toBasicString(), + v->getTypeInfo().toString().toBasicString()))); + } + if (isAccessConst(s->am)) + { + throw RuntimeError(FString( + std::format("Variable `{}` is immutable", s->name.toBasicString()))); + } + s->value = v; + } + else if (kind == Kind::ListElement) + { + List &list = listOrMap->as(); + if (listIndex >= list.size()) + throw RuntimeError(FString( + std::format("Index {} out of range", listIndex))); + list[listIndex] = v; + } + else // map + { + Map &map = listOrMap->as(); + map[mapIndex] = 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; + } + }; +} \ No newline at end of file diff --git a/src/Value/Type.hpp b/src/Value/Type.hpp index 3b3ac82..adfa3c0 100644 --- a/src/Value/Type.hpp +++ b/src/Value/Type.hpp @@ -14,6 +14,8 @@ namespace Fig size_t id; public: + friend class TypeInfoHash; + FString name; FString toString() const @@ -27,7 +29,7 @@ namespace Fig { return typeMap.at(_name); } - size_t getInstanceID(FString _name) const + size_t getInstanceID() const { return id; } @@ -42,6 +44,15 @@ namespace Fig } }; + class TypeInfoHash + { + public: + std::size_t operator()(const TypeInfo &ti) const + { + return ti.id; + } + }; + // class Value; namespace ValueType { @@ -56,7 +67,7 @@ namespace Fig extern const TypeInfo StructInstance; extern const TypeInfo List; extern const TypeInfo Map; - extern const TypeInfo Tuple; + // extern const TypeInfo Tuple; using IntClass = int64_t; using DoubleClass = double; diff --git a/src/Value/VariableSlot.hpp b/src/Value/VariableSlot.hpp new file mode 100644 index 0000000..984c40c --- /dev/null +++ b/src/Value/VariableSlot.hpp @@ -0,0 +1,22 @@ +#pragma once + + +#include +#include +#include +#include +namespace Fig +{ + class Object; + using ObjectPtr = std::shared_ptr; + struct VariableSlot + { + FString name; + ObjectPtr value; + TypeInfo declaredType; + AccessModifier am; + + bool isRef = false; + std::shared_ptr refTarget; + }; +} \ No newline at end of file diff --git a/src/Value/containers.hpp b/src/Value/containers.hpp deleted file mode 100644 index 71ba15c..0000000 --- a/src/Value/containers.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace Fig -{ - -}; \ No newline at end of file diff --git a/src/Value/structType.hpp b/src/Value/structType.hpp index 78b1ff8..fe5b79c 100644 --- a/src/Value/structType.hpp +++ b/src/Value/structType.hpp @@ -29,7 +29,6 @@ namespace Fig { return am == AccessModifier::Const || am == AccessModifier::PublicConst; } - }; struct StructType @@ -65,3 +64,15 @@ namespace Fig } }; } // namespace Fig + +namespace std +{ + template <> + struct hash + { + size_t operator()(const Fig::Field &f) + { + return std::hash{}(f.name); + } + }; +}; // namespace std \ No newline at end of file diff --git a/src/Value/value.cpp b/src/Value/value.cpp index c0b9f43..f53e14b 100644 --- a/src/Value/value.cpp +++ b/src/Value/value.cpp @@ -25,6 +25,50 @@ namespace Fig } } + size_t ValueKeyHash::operator()(const ValueKey &key) const + { + { + ObjectPtr value = key.value; + const TypeInfo &type = value->getTypeInfo(); + + if (type == ValueType::Int) + { + return std::hash{}(value->as()); + } + if (type == ValueType::Double) + { + return std::hash{}(value->as()); + } + if (type == ValueType::String) + { + return std::hash{}(value->as()); + } + if (type == ValueType::Bool) + { + return std::hash{}(value->as()); + } + if (type == ValueType::StructType) + { + auto HashFields = [](std::vector fields) { + size_t r = 0; + for (auto &f : fields) + { + r += std::hash{}(f); + } + return r; + }; + const StructType &st = value->as(); + return std::hash{}(st.id) + HashFields(st.fields); + } + if (type == ValueType::StructInstance) + { + const StructInstance &si = value->as(); + return std::hash{}(si.parentId) + std::hash{}(reinterpret_cast(std::addressof(*si.localContext))); + } + assert(false); + } + } + 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 @@ -36,5 +80,5 @@ namespace Fig const TypeInfo ValueType::StructInstance(FString(u8"StructInstance"), true); // id: 9 const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10 const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11 - const TypeInfo ValueType::Tuple(FString(u8"Tuple"), true); // id: 12 + // const TypeInfo ValueType::Tuple(FString(u8"Tuple"), true); // id: 12 } // namespace Fig \ No newline at end of file diff --git a/src/Value/value.hpp b/src/Value/value.hpp index 73fb8d3..064d7e9 100644 --- a/src/Value/value.hpp +++ b/src/Value/value.hpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace Fig { @@ -26,6 +28,22 @@ namespace Fig static_cast(std::numeric_limits::min()); return d > intMaxAsDouble || d < intMinAsDouble; } + class Object; + using ObjectPtr = std::shared_ptr; + using List = std::vector; + + struct ValueKey + { + ObjectPtr value; + ValueKey(ObjectPtr _value) : + value(_value) {} + }; + + struct ValueKeyHash + { + size_t operator()(const ValueKey &key) const; + }; + using Map = std::unordered_map; class Object { @@ -38,7 +56,104 @@ namespace Fig ValueType::BoolClass, Function, StructType, - StructInstance>; + StructInstance, + List, + Map>; + + std::unordered_map)>>, + TypeInfoHash> + memberTypeFunctions{ + {ValueType::Null, {}}, + {ValueType::Int, {}}, + {ValueType::Double, {}}, + {ValueType::String, { + {u8"length", [this](std::vector args) -> ObjectPtr { + if (args.size() != 0) + throw RuntimeError(FString( + std::format("`length` expects 0 arguments, {} got", args.size()))); + const FString &str = as(); + return std::make_shared(static_cast(str.length())); + }}, + + }}, + {ValueType::Function, {}}, + {ValueType::StructType, {}}, + {ValueType::StructInstance, {}}, + {ValueType::List, {{u8"length", [this](std::vector args) -> ObjectPtr { + if (args.size() != 0) + throw RuntimeError(FString( + std::format("`length` expects 0 arguments, {} got", args.size()))); + const List &list = as(); + return std::make_shared(static_cast(list.size())); + }}, + {u8"get", [this](std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError(FString( + std::format("`get` expects 1 arguments, {} got", args.size()))); + ObjectPtr arg = args[0]; + if (arg->getTypeInfo() != ValueType::Int) + throw RuntimeError(FString( + std::format("`get` argument 1 expects Int, {} got", arg->getTypeInfo().toString().toBasicString()))); + ValueType::IntClass i = arg->as(); + const List &list = as(); + if (i >= list.size()) + return Object::getNullInstance(); + return list[i]; + }}}}, + {ValueType::Map, { + {u8"get", [this](std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError(FString( + std::format("`get` expects 1 arguments, {} got", args.size()))); + ObjectPtr index = args[0]; + const Map &map = as(); + if (!map.contains(index)) + return Object::getNullInstance(); + return map.at(index); + }}, + {u8"contains", [this](std::vector args) -> ObjectPtr { + if (args.size() != 1) + throw RuntimeError(FString( + std::format("`contains` expects 1 arguments, {} got", args.size()))); + ObjectPtr index = args[0]; + const Map &map = as(); + return std::make_shared( + map.contains(index)); + }}, + }}, + }; + std::unordered_map, TypeInfoHash> memberTypeFunctionsParas{ + {ValueType::Null, {}}, + {ValueType::Int, {}}, + {ValueType::Double, {}}, + {ValueType::String, { + {u8"length", 0}, + + }}, + {ValueType::Function, {}}, + {ValueType::StructType, {}}, + {ValueType::StructInstance, {}}, + {ValueType::List, {{u8"length", 0}, {u8"get", 1}}}, + {ValueType::Map, { + {u8"get", 1}, + {u8"contains", 1}, + }}, + + }; + bool hasMemberFunction(const FString &name) const + { + return memberTypeFunctions.at(getTypeInfo()).contains(name); + } + std::function)> getMemberFunction(const FString &name) const + { + return memberTypeFunctions.at(getTypeInfo()).at(name); + } + int getMemberFunctionParaCount(const FString &name) const + { + return memberTypeFunctionsParas.at(getTypeInfo()).at(name); + } VariantType data; @@ -49,9 +164,7 @@ namespace Fig Object(const ValueType::IntClass &i) : data(i) {} explicit Object(const ValueType::DoubleClass &d) : - data(d) - { - } + data(d) {} Object(const ValueType::StringClass &s) : data(s) {} Object(const ValueType::BoolClass &b) : @@ -62,6 +175,10 @@ namespace Fig data(s) {} Object(const StructInstance &s) : data(s) {} + Object(const List &l) : + data(l) {} + Object(const Map &m) : + data(m) {} Object(const Object &) = default; Object(Object &&) noexcept = default; @@ -78,6 +195,10 @@ namespace Fig return Object(ValueType::StringClass(u8"")); else if (ti == ValueType::Bool) return Object(ValueType::BoolClass(false)); + else if (ti == ValueType::List) + return Object(List{}); + else if (ti == ValueType::Map) + return Object(Map{}); else return *getNullInstance(); } @@ -120,22 +241,37 @@ namespace Fig { return std::visit([](auto &&val) -> TypeInfo { using T = std::decay_t; + if constexpr (std::is_same_v) return ValueType::Null; + else if constexpr (std::is_same_v) return ValueType::Int; + else if constexpr (std::is_same_v) return ValueType::Double; + else if constexpr (std::is_same_v) return ValueType::String; + else if constexpr (std::is_same_v) return ValueType::Bool; + else if constexpr (std::is_same_v) return ValueType::Function; + else if constexpr (std::is_same_v) return ValueType::StructType; + else if constexpr (std::is_same_v) return ValueType::StructInstance; + + else if constexpr (std::is_same_v) + return ValueType::List; + + else if constexpr (std::is_same_v) + return ValueType::Map; + else return ValueType::Any; }, @@ -155,12 +291,18 @@ namespace Fig throw RuntimeError(u8"getNumericValue: Not a numeric value"); } + FString toStringIO() const + { + if (is()) return as(); + return toString(); + } + FString toString() const { if (is()) return FString(u8"null"); if (is()) return FString(std::to_string(as())); if (is()) return FString(std::format("{}", as())); - if (is()) return as(); + if (is()) return FString(u8"() + FString(u8"\" >"); if (is()) return as() ? FString(u8"true") : FString(u8"false"); if (is()) return FString(std::format("", @@ -174,6 +316,36 @@ namespace Fig return FString(std::format("", as().parentId, static_cast(&as()))); + if (is()) + { + FString output(u8"["); + const List &list = as(); + bool first_flag = true; + for (auto &ele : list) + { + if (!first_flag) + output += u8", "; + output += ele->toString(); + first_flag = false; + } + output += u8"]"; + return output; + } + if (is()) + { + FString output(u8"{"); + const Map &map = as(); + bool first_flag = true; + for (auto &[key, value] : map) + { + if (!first_flag) + output += u8", "; + output += key.value->toString() + FString(u8" : ") + value->toString(); + first_flag = false; + } + output += u8"}"; + return output; + } return FString(u8""); } @@ -191,7 +363,7 @@ namespace Fig friend Object operator+(const Object &lhs, const Object &rhs) { if (lhs.isNull() || rhs.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Cannot add", "+", lhs, rhs))); if (lhs.isNumeric() && rhs.isNumeric()) { bool bothInt = lhs.is() && rhs.is(); @@ -202,13 +374,13 @@ namespace Fig } if (lhs.is() && rhs.is()) return Object(FString(lhs.as() + rhs.as())); - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs))); } friend Object operator-(const Object &lhs, const Object &rhs) { if (lhs.isNull() || rhs.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs))); if (lhs.isNumeric() && rhs.isNumeric()) { bool bothInt = lhs.is() && rhs.is(); @@ -217,13 +389,13 @@ namespace Fig return Object(static_cast(result)); return Object(result); } - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs))); } friend Object operator*(const Object &lhs, const Object &rhs) { if (lhs.isNull() || rhs.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs))); if (lhs.isNumeric() && rhs.isNumeric()) { bool bothInt = lhs.is() && rhs.is(); @@ -232,82 +404,82 @@ namespace Fig return Object(static_cast(result)); return Object(result); } - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs))); } friend Object operator/(const Object &lhs, const Object &rhs) { if (lhs.isNull() || rhs.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs))); if (lhs.isNumeric() && rhs.isNumeric()) { auto rnv = rhs.getNumericValue(); if (rnv == 0) - throw ValueError(FStringView(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Division by zero", "/", lhs, rhs))); bool bothInt = lhs.is() && rhs.is(); auto result = lhs.getNumericValue() / rnv; if (bothInt && !isNumberExceededIntLimit(result)) return Object(static_cast(result)); return Object(result); } - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs))); } friend Object operator%(const Object &lhs, const Object &rhs) { if (lhs.isNull() || rhs.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs))); if (lhs.isNumeric() && rhs.isNumeric()) { auto rnv = rhs.getNumericValue(); if (rnv == 0) - throw ValueError(FStringView(makeTypeErrorMessage("Modulo by zero", "/", lhs, rhs))); + 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(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); } // logic friend Object operator&&(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs))); return Object(lhs.as() && rhs.as()); } friend Object operator||(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs))); return Object(lhs.as() || rhs.as()); } friend Object operator!(const Object &v) { if (!v.is()) - throw ValueError(FStringView(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError(FString(std::format("Logical NOT requires bool: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(!v.as()); } friend Object operator-(const Object &v) { if (v.isNull()) - throw ValueError(FStringView(u8"Unary minus cannot be applied to null")); + throw ValueError(FString(u8"Unary minus cannot be applied to null")); if (v.is()) return Object(-v.as()); if (v.is()) return Object(-v.as()); - throw ValueError(FStringView(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError(FString(std::format("Unary minus requires int or double: '{}'", v.getTypeInfo().name.toBasicString()))); } friend Object operator~(const Object &v) { if (!v.is()) - throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(~v.as()); } @@ -319,7 +491,7 @@ namespace Fig if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue(); if (lhs.is() && rhs.is()) return lhs.as() < rhs.as(); - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs))); } friend bool operator<=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs < rhs; } friend bool operator>(const Object &lhs, const Object &rhs) @@ -327,7 +499,7 @@ namespace Fig if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue(); if (lhs.is() && rhs.is()) return lhs.as() > rhs.as(); - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs))); } friend bool operator>=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs > rhs; } @@ -335,49 +507,49 @@ namespace Fig friend Object bit_and(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs))); return Object(lhs.as() & rhs.as()); } friend Object bit_or(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs))); return Object(lhs.as() | rhs.as()); } friend Object bit_xor(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs))); return Object(lhs.as() ^ rhs.as()); } friend Object bit_not(const Object &v) { if (!v.is()) - throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); + throw ValueError(FString(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString()))); return Object(~v.as()); } friend Object shift_left(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs))); return Object(lhs.as() << rhs.as()); } friend Object shift_right(const Object &lhs, const Object &rhs) { if (!lhs.is() || !rhs.is()) - throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs))); + throw ValueError(FString(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs))); return Object(lhs.as() >> rhs.as()); } friend Object power(const Object &base, const Object &exp) { if (base.isNull() || exp.isNull()) - throw ValueError(FStringView(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp))); + throw ValueError(FString(makeTypeErrorMessage("Cannot exponentiate", "**", base, exp))); if (base.isNumeric() && exp.isNumeric()) { bool bothInt = base.is() && exp.is(); @@ -386,68 +558,17 @@ namespace Fig return Object(static_cast(result)); return Object(result); } - throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "**", base, exp))); + throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "**", base, exp))); } }; using ObjectPtr = std::shared_ptr; using RvObject = ObjectPtr; - struct VariableSlot + + inline bool operator==(const ValueKey &l, const ValueKey &r) { - 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; - } - }; - + return *l.value == *r.value; + } } // namespace Fig