[Feat] 增加容器 List, Map, 以及对应Hash

[Impl] Addressable/Unaddressable Error现在内部存储FString而非View
This commit is contained in:
2025-12-25 17:10:12 +08:00
parent ab4024c2bf
commit f056b0ffbe
18 changed files with 583 additions and 179 deletions

View File

@@ -48,14 +48,14 @@ namespace Fig::Ast
class MapExprAst final : public ExpressionAst class MapExprAst final : public ExpressionAst
{ {
public: public:
std::map<FString, Expression> val; std::map<Expression, Expression> val;
MapExprAst() MapExprAst()
{ {
type = AstType::MapExpr; type = AstType::MapExpr;
} }
MapExprAst(std::map<FString, Expression> _val) : MapExprAst(std::map<Expression, Expression> _val) :
val(std::move(_val)) val(std::move(_val))
{ {
type = AstType::MapExpr; type = AstType::MapExpr;

View File

@@ -7,6 +7,7 @@
#include <Context/context_forward.hpp> #include <Context/context_forward.hpp>
#include <Core/fig_string.hpp> #include <Core/fig_string.hpp>
#include <Value/value.hpp> #include <Value/value.hpp>
#include <Value/VariableSlot.hpp>
namespace Fig namespace Fig
{ {
@@ -65,7 +66,7 @@ namespace Fig
return it->second; return it->second;
if (parent) if (parent)
return parent->get(name); 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) AccessModifier getAccessModifier(const FString &name)
{ {
@@ -79,7 +80,7 @@ namespace Fig
} }
else 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) bool isVariableMutable(const FString &name)
@@ -98,7 +99,7 @@ namespace Fig
{ {
if (!isVariableMutable(name)) 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; variables[name]->value = value;
} }
@@ -108,7 +109,7 @@ namespace Fig
} }
else 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) void _update(const FString &name, ObjectPtr value)
@@ -123,14 +124,14 @@ namespace Fig
} }
else 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()) void def(const FString &name, const TypeInfo &ti, AccessModifier am, const ObjectPtr &value = Object::getNullInstance())
{ {
if (containsInThisScope(name)) 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<VariableSlot>( variables[name] = std::make_shared<VariableSlot>(
name, name,

View File

@@ -15,7 +15,7 @@ namespace Fig
static FStringView fromBasicStringView(std::string_view sv) static FStringView fromBasicStringView(std::string_view sv)
{ {
return FStringView(reinterpret_cast<const char8_t*>(sv.data()), sv.size()); return FStringView(reinterpret_cast<const char8_t*>(sv.data()));
} }
explicit FStringView(std::string_view sv) explicit FStringView(std::string_view sv)
@@ -28,12 +28,22 @@ namespace Fig
*this = fromBasicStringView(std::string_view("")); *this = fromBasicStringView(std::string_view(""));
} }
std::string_view toBasicStringView() const
{
return std::string_view(reinterpret_cast<const char *>(data()), size());
}
}; };
class FString : public std::u8string class FString : public std::u8string
{ {
public: public:
using std::u8string::u8string; using std::u8string::u8string;
FString operator+(const FString& x)
{
return FString(toBasicString() + x.toBasicString());
}
explicit FString(const std::u8string &str) explicit FString(const std::u8string &str)
{ {
*this = fromU8String(str); *this = fromU8String(str);
@@ -70,7 +80,7 @@ namespace Fig
return FString(str.begin(), str.end()); return FString(str.begin(), str.end());
} }
size_t length() size_t length() const
{ {
// get UTF8-String real length // get UTF8-String real length
size_t len = 0; size_t len = 0;

View File

@@ -13,7 +13,7 @@ namespace Fig
{ {
public: public:
explicit AddressableError() {} explicit AddressableError() {}
explicit AddressableError(FStringView _msg, explicit AddressableError(FString _msg,
size_t _line, size_t _line,
size_t _column, size_t _column,
std::source_location loc = std::source_location::current()) : std::source_location loc = std::source_location::current()) :
@@ -35,7 +35,7 @@ namespace Fig
size_t getLine() const { return line; } size_t getLine() const { return line; }
size_t getColumn() const { return column; } size_t getColumn() const { return column; }
FStringView getMessage() const { return message; } FString getMessage() const { return message; }
virtual FString getErrorType() const virtual FString getErrorType() const
{ {
@@ -44,14 +44,14 @@ namespace Fig
protected: protected:
size_t line, column; size_t line, column;
FStringView message; FString message;
}; };
class UnaddressableError : public std::exception class UnaddressableError : public std::exception
{ {
public: public:
explicit UnaddressableError() {} explicit UnaddressableError() {}
explicit UnaddressableError(FStringView _msg, explicit UnaddressableError(FString _msg,
std::source_location loc = std::source_location::current()) : std::source_location loc = std::source_location::current()) :
src_loc(loc) src_loc(loc)
{ {
@@ -59,7 +59,7 @@ namespace Fig
} }
virtual FString toString() const 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); return FString(msg);
} }
const char *what() const noexcept override const char *what() const noexcept override
@@ -68,7 +68,7 @@ namespace Fig
return msg.c_str(); return msg.c_str();
} }
std::source_location src_loc; std::source_location src_loc;
FStringView getMessage() const { return message; } FString getMessage() const { return message; }
virtual FString getErrorType() const virtual FString getErrorType() const
{ {
@@ -76,7 +76,7 @@ namespace Fig
} }
protected: protected:
FStringView message; FString message;
}; };
class SyntaxError : public AddressableError class SyntaxError : public AddressableError
@@ -84,7 +84,7 @@ namespace Fig
public: public:
using AddressableError::AddressableError; using AddressableError::AddressableError;
explicit SyntaxError(FStringView _msg, explicit SyntaxError(FString _msg,
size_t _line, size_t _line,
size_t _column, size_t _column,
std::source_location loc = std::source_location::current()) : std::source_location loc = std::source_location::current()) :
@@ -94,7 +94,7 @@ namespace Fig
virtual FString toString() const override 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); return FString(msg);
} }
@@ -108,14 +108,14 @@ namespace Fig
{ {
public: public:
using UnaddressableError::UnaddressableError; using UnaddressableError::UnaddressableError;
explicit RuntimeError(FStringView _msg, explicit RuntimeError(FString _msg,
std::source_location loc = std::source_location::current()) : std::source_location loc = std::source_location::current()) :
UnaddressableError(_msg, loc) UnaddressableError(_msg, loc)
{ {
} }
virtual FString toString() const override 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); return FString(msg);
} }

View File

@@ -22,13 +22,25 @@ namespace Fig
LvObject base = evalLv(me->base, ctx); LvObject base = evalLv(me->base, ctx);
RvObject baseVal = base.get(); RvObject baseVal = base.get();
const FString &member = me->member; const FString &member = me->member;
if (baseVal->getTypeInfo() != ValueType::StructInstance) if (baseVal->hasMemberFunction(member))
{
return LvObject(std::make_shared<VariableSlot>(
member,
std::make_shared<Object>(
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( throw EvaluatorError(
u8"TypeError", u8"NoAttributeError",
std::format( std::format(
"`{}` isn't a struct", "`{}` has not attribute '{}'",
base.name().toBasicString()), baseVal->toString().toBasicString(),
member.toBasicString()),
me->base); me->base);
} }
const StructInstance &si = baseVal->as<StructInstance>(); const StructInstance &si = baseVal->as<StructInstance>();
@@ -49,11 +61,48 @@ namespace Fig
LvObject base = evalLv(ie->base, ctx); LvObject base = evalLv(ie->base, ctx);
RvObject index = eval(ie->index, ctx); RvObject index = eval(ie->index, ctx);
const TypeInfo &type = base.declaredType(); const TypeInfo &type = base.get()->getTypeInfo();
if (type != ValueType::List if (type == ValueType::List)
&& type != ValueType::Tuple {
&& type != ValueType::Map) 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<List>();
ValueType::IntClass indexVal = index->as<ValueType::IntClass>();
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( throw EvaluatorError(
u8"NoSubscriptableError", u8"NoSubscriptableError",
@@ -62,8 +111,7 @@ namespace Fig
base.declaredType().toString().toBasicString()), base.declaredType().toString().toBasicString()),
ie->base); ie->base);
} }
// TODO
return LvObject();
} }
LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx) LvObject Evaluator::evalLv(Ast::Expression exp, ContextPtr ctx)
{ {
@@ -651,6 +699,30 @@ namespace Fig
return std::make_shared<Object>(StructInstance(structT.id, instanceCtx)); return std::make_shared<Object>(StructInstance(structT.id, instanceCtx));
} }
case AstType::ListExpr: {
auto lstExpr = std::dynamic_pointer_cast<Ast::ListExprAst>(exp);
assert(lstExpr != nullptr);
List list;
for (auto &exp : lstExpr->val)
{
list.push_back(eval(exp, ctx));
}
return std::make_shared<Object>(std::move(list));
}
case AstType::MapExpr: {
auto mapExpr = std::dynamic_pointer_cast<Ast::MapExprAst>(exp);
assert(mapExpr != nullptr);
Map map;
for (auto &[key, value] : mapExpr->val)
{
map[eval(key, ctx)] = eval(value, ctx);
}
return std::make_shared<Object>(std::move(map));
}
default: default:
assert(false); assert(false);
} }
@@ -990,7 +1062,7 @@ namespace Fig
} }
default: default:
throw RuntimeError(FStringView( throw RuntimeError(FString(
std::format("Feature stmt {} unsupported yet", std::format("Feature stmt {} unsupported yet",
magic_enum::enum_name(stmt->getType())))); magic_enum::enum_name(stmt->getType()))));
} }

View File

@@ -3,6 +3,7 @@
#include <Context/context.hpp> #include <Context/context.hpp>
#include <Error/error.hpp> #include <Error/error.hpp>
#include <Module/builtins.hpp> #include <Module/builtins.hpp>
#include <Value/LvObject.hpp>
namespace Fig namespace Fig

View File

@@ -12,7 +12,7 @@ namespace Fig
using AddressableError::AddressableError; using AddressableError::AddressableError;
EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current()) 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; line = ast->getAAI().line;
column = ast->getAAI().column; 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()) 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; line = ast->getAAI().line;
column = ast->getAAI().column; column = ast->getAAI().column;

View File

@@ -190,7 +190,7 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FStringView( error = SyntaxError(FString(
std::format( std::format(
"Unsupported escape character: {}", "Unsupported escape character: {}",
FString(ec.getString()).toBasicString())), FString(ec.getString()).toBasicString())),
@@ -307,7 +307,7 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FStringView( error = SyntaxError(FString(
std::format( std::format(
"Unsupported escape character: {}", "Unsupported escape character: {}",
FString(ec.getString()).toBasicString())), FString(ec.getString()).toBasicString())),
@@ -369,7 +369,7 @@ namespace Fig
// checking legality // checking legality
if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数 if ((*numStr.end()) == u'e') // e 后面必须跟整数表示科学计数
{ {
error = SyntaxError(FStringView( error = SyntaxError(FString(
std::format("Ellegal number literal: {}", numStr.toBasicString())), std::format("Ellegal number literal: {}", numStr.toBasicString())),
this->line, it.column()); this->line, it.column());
return IllegalTok; return IllegalTok;
@@ -396,7 +396,7 @@ namespace Fig
else if (!this->symbol_map.contains(sym)) else if (!this->symbol_map.contains(sym))
{ {
// check legality // check legality
error = SyntaxError(FStringView( error = SyntaxError(FString(
std::format("No such a operator: {}", sym.toBasicString())), std::format("No such a operator: {}", sym.toBasicString())),
this->line, it.column()); this->line, it.column());
} }
@@ -455,7 +455,7 @@ namespace Fig
if (!terminated) 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(); next();
return IllegalTok; return IllegalTok;
} }
@@ -526,7 +526,7 @@ namespace Fig
} }
else else
{ {
error = SyntaxError(FStringView( error = SyntaxError(FString(
std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())), std::format("Cannot tokenize char: '{}'", FString(ch.getString()).toBasicString())),
this->line, it.column()); this->line, it.column());
if (hasNext()) if (hasNext())

View File

@@ -36,16 +36,24 @@ namespace Fig
const std::unordered_map<FString, BuiltinFunction> builtinFunctions{ const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
{u8"__fstdout_print", [](const std::vector<ObjectPtr> &args) -> ObjectPtr { {u8"__fstdout_print", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
bool first_flag = true;
for (auto arg : args) 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<Object>(ValueType::IntClass(args.size())); return std::make_shared<Object>(ValueType::IntClass(args.size()));
}}, }},
{u8"__fstdout_println", [](const std::vector<ObjectPtr> &args) -> ObjectPtr { {u8"__fstdout_println", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
bool first_flag = true;
for (auto arg : args) 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"); std::print("\n");
return std::make_shared<Object>(ValueType::IntClass(args.size())); return std::make_shared<Object>(ValueType::IntClass(args.size()));
@@ -72,7 +80,7 @@ namespace Fig
} }
catch (...) 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<ObjectPtr> &args) -> ObjectPtr { {u8"__fvalue_int_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
@@ -87,7 +95,7 @@ namespace Fig
} }
else 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<ObjectPtr> &args) -> ObjectPtr { {u8"__fvalue_double_parse", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
@@ -99,7 +107,7 @@ namespace Fig
} }
catch (...) 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<ObjectPtr> &args) -> ObjectPtr { {u8"__fvalue_double_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
@@ -114,7 +122,7 @@ namespace Fig
} }
else 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<ObjectPtr> &args) -> ObjectPtr { {u8"__fvalue_string_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
@@ -134,7 +142,7 @@ namespace Fig
auto it = builtinFunctions.find(name); auto it = builtinFunctions.find(name);
if (it == builtinFunctions.end()) 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; return it->second;
} }
@@ -144,7 +152,7 @@ namespace Fig
auto it = builtinFunctionArgCounts.find(name); auto it = builtinFunctionArgCounts.find(name);
if (it == builtinFunctionArgCounts.end()) 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; return it->second;
} }

View File

@@ -69,7 +69,7 @@ namespace Fig
if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus"); if (!isThis(TokenType::Assign) and !isThis(TokenType::Walrus)) expect(TokenType::Assign, u8"assign or walrus");
if (isThis(TokenType::Walrus)) if (isThis(TokenType::Walrus))
{ {
if (hasSpecificType) throwAddressableError<SyntaxError>(FStringView(u8"")); if (hasSpecificType) throwAddressableError<SyntaxError>(FString(u8""));
tiName = Parser::varDefTypeFollowed; tiName = Parser::varDefTypeFollowed;
} }
next(); next();
@@ -93,7 +93,7 @@ namespace Fig
} }
catch (...) catch (...)
{ {
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal")); throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
} }
return std::make_shared<Object>(d); return std::make_shared<Object>(d);
} }
@@ -107,7 +107,7 @@ namespace Fig
} }
catch (...) catch (...)
{ {
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal")); throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
} }
return std::make_shared<Object>(i); return std::make_shared<Object>(i);
} }
@@ -253,7 +253,7 @@ namespace Fig
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView(std::format("expect field name or field attribute"))); throwAddressableError<SyntaxError>(FString(std::format("expect field name or field attribute")));
} }
FString tiName = ValueType::Any.name; FString tiName = ValueType::Any.name;
if (isThis(TokenType::Colon)) if (isThis(TokenType::Colon))
@@ -267,7 +267,7 @@ namespace Fig
if (isThis(TokenType::Assign)) if (isThis(TokenType::Assign))
{ {
next(); next();
if (isEOF()) throwAddressableError<SyntaxError>(FStringView(u8"expect an expression")); if (isEOF()) throwAddressableError<SyntaxError>(FString(u8"expect an expression"));
initExpr = parseExpression(0); initExpr = parseExpression(0);
} }
expectSemicolon(); expectSemicolon();
@@ -314,7 +314,7 @@ namespace Fig
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView("Invalid syntax")); throwAddressableError<SyntaxError>(FString("Invalid syntax"));
} }
} }
else if (isThis(TokenType::Function)) else if (isThis(TokenType::Function))
@@ -333,16 +333,16 @@ namespace Fig
} }
else if (isThis(TokenType::Variable)) else if (isThis(TokenType::Variable))
{ {
throwAddressableError<SyntaxError>(FStringView("Variables are not allowed to be defined within a structure.")); throwAddressableError<SyntaxError>(FString("Variables are not allowed to be defined within a structure."));
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView("Invalid syntax")); throwAddressableError<SyntaxError>(FString("Invalid syntax"));
} }
} }
if (!braceClosed) if (!braceClosed)
{ {
throwAddressableError<SyntaxError>(FStringView("braces are not closed")); throwAddressableError<SyntaxError>(FString("braces are not closed"));
} }
return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts)); return makeAst<Ast::StructDefSt>(isPublic, structName, fields, makeAst<Ast::BlockStatementAst>(stmts));
} }
@@ -368,7 +368,7 @@ namespace Fig
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView(u8"Expected `var`, `const`, `function` or `struct` after `public`")); throwAddressableError<SyntaxError>(FString(u8"Expected `var`, `const`, `function` or `struct` after `public`"));
} }
} }
else if (isThis(TokenType::Variable) || isThis(TokenType::Const)) else if (isThis(TokenType::Variable) || isThis(TokenType::Const))
@@ -392,7 +392,7 @@ namespace Fig
} }
else if (isThis(TokenType::Else)) else if (isThis(TokenType::Else))
{ {
throwAddressableError<SyntaxError>(FStringView(u8"`else` without matching `if`")); throwAddressableError<SyntaxError>(FString(u8"`else` without matching `if`"));
} }
else if (isThis(TokenType::LeftBrace)) else if (isThis(TokenType::LeftBrace))
{ {
@@ -625,15 +625,10 @@ namespace Fig
{ {
// entry: current is `{` // entry: current is `{`
next(); // consume `{` next(); // consume `{`
std::map<FString, Ast::Expression> val; std::map<Ast::Expression, Ast::Expression> val;
while (!isThis(TokenType::RightBrace)) while (!isThis(TokenType::RightBrace))
{ {
expect(TokenType::Identifier, FString(u8"key (identifier)")); Ast::Expression key = parseExpression(0, TokenType::Colon);
FString key = currentToken().getValue();
if (val.contains(key)) throwAddressableError<SyntaxError>(FStringView(std::format(
"Redefinition of immutable key {} in mapping literal",
key.toBasicString())));
next(); // consume key
expect(TokenType::Colon); expect(TokenType::Colon);
next(); // consume `:` next(); // consume `:`
val[key] = parseExpression(0, TokenType::RightBrace, TokenType::Comma); val[key] = parseExpression(0, TokenType::RightBrace, TokenType::Comma);
@@ -711,7 +706,7 @@ namespace Fig
} }
else if (!isThis(TokenType::RightBrace)) else if (!isThis(TokenType::RightBrace))
{ {
throwAddressableError<SyntaxError>(FStringView( throwAddressableError<SyntaxError>(FString(
std::format("Expect `,` or `}}` in struct initialization expression, got {}", std::format("Expect `,` or `}}` in struct initialization expression, got {}",
currentToken().toString().toBasicString()) currentToken().toString().toBasicString())
)); ));
@@ -762,7 +757,7 @@ namespace Fig
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView(u8"Expect ')' or ',' after expression in parentheses")); throwAddressableError<SyntaxError>(FString(u8"Expect ')' or ',' after expression in parentheses"));
} }
return nullptr; // to suppress compiler warning return nullptr; // to suppress compiler warning
} }
@@ -794,10 +789,10 @@ namespace Fig
Token tok = currentToken(); Token tok = currentToken();
if (tok == EOFTok) if (tok == EOFTok)
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected end of expression")); throwAddressableError<SyntaxError>(FString(u8"Unexpected end of expression"));
if (tok.getType() == stop || tok.getType() == stop2) if (tok.getType() == stop || tok.getType() == stop2)
{ {
if (lhs == nullptr) throwAddressableError<SyntaxError>(FStringView(u8"Expected expression")); if (lhs == nullptr) throwAddressableError<SyntaxError>(FString(u8"Expected expression"));
return lhs; return lhs;
} }
if (tok.getType() == TokenType::LeftBracket) if (tok.getType() == TokenType::LeftBracket)
@@ -818,7 +813,7 @@ namespace Fig
if (currentToken().getType() == TokenType::Identifier) if (currentToken().getType() == TokenType::Identifier)
{ {
// err // err
throwAddressableError<SyntaxError>(FStringView(u8"Function literal should not have a name")); throwAddressableError<SyntaxError>(FString(u8"Function literal should not have a name"));
} }
expect(TokenType::LeftParen); expect(TokenType::LeftParen);
lhs = __parseFunctionLiteralExpr(); lhs = __parseFunctionLiteralExpr();
@@ -849,7 +844,7 @@ namespace Fig
} }
else else
{ {
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected token in expression")); throwAddressableError<SyntaxError>(FString(u8"Unexpected token in expression"));
} }
// infix / (postfix) ? // infix / (postfix) ?
@@ -872,7 +867,7 @@ namespace Fig
next(); // consume '.' next(); // consume '.'
Token idTok = currentToken(); Token idTok = currentToken();
if (!idTok.isIdentifier()) if (!idTok.isIdentifier())
throwAddressableError<SyntaxError>(FStringView(u8"Expected identifier after '.'")); throwAddressableError<SyntaxError>(FString(u8"Expected identifier after '.'"));
FString member = idTok.getValue(); FString member = idTok.getValue();
next(); // consume identifier next(); // consume identifier

View File

@@ -92,7 +92,7 @@ namespace Fig
} }
template <class _ErrT, typename = AddressableError> template <class _ErrT, typename = AddressableError>
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<AddressableError, _ErrT>, static_assert(std::is_base_of_v<AddressableError, _ErrT>,
"_ErrT must derive from AddressableError"); "_ErrT must derive from AddressableError");
@@ -101,7 +101,7 @@ namespace Fig
throw spError; throw spError;
} }
template <class _ErrT, typename = AddressableError> template <class _ErrT, typename = AddressableError>
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<AddressableError, _ErrT>, static_assert(std::is_base_of_v<AddressableError, _ErrT>,
"_ErrT must derive from AddressableError"); "_ErrT must derive from AddressableError");
@@ -112,7 +112,7 @@ namespace Fig
} }
template <class _ErrT, typename = UnaddressableError> template <class _ErrT, typename = UnaddressableError>
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<AddressableError, _ErrT>, static_assert(std::is_base_of_v<AddressableError, _ErrT>,
"_ErrT must derive from AddressableError"); "_ErrT must derive from AddressableError");
@@ -215,7 +215,7 @@ namespace Fig
{ {
if (peekToken().getType() != type) if (peekToken().getType() != type)
{ {
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`", throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
magic_enum::enum_name(type), magic_enum::enum_name(type),
magic_enum::enum_name(peekToken().getType())))); magic_enum::enum_name(peekToken().getType()))));
} }
@@ -225,7 +225,7 @@ namespace Fig
{ {
if (currentToken().getType() != type) if (currentToken().getType() != type)
{ {
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`", throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
magic_enum::enum_name(type), magic_enum::enum_name(type),
magic_enum::enum_name(currentToken().getType())))); magic_enum::enum_name(currentToken().getType()))));
} }
@@ -235,7 +235,7 @@ namespace Fig
{ {
if (peekToken().getType() != type) if (peekToken().getType() != type)
{ {
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`", throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
expected.toBasicString(), expected.toBasicString(),
magic_enum::enum_name(peekToken().getType())))); magic_enum::enum_name(peekToken().getType()))));
} }
@@ -245,7 +245,7 @@ namespace Fig
{ {
if (currentToken().getType() != type) if (currentToken().getType() != type)
{ {
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`", throwAddressableError<SyntaxError>(FString(std::format("Expected `{}`, but got `{}`",
expected.toBasicString(), expected.toBasicString(),
magic_enum::enum_name(currentToken().getType())))); magic_enum::enum_name(currentToken().getType()))));
} }
@@ -319,8 +319,8 @@ namespace Fig
Ast::Expression __parseCall(Ast::Expression); Ast::Expression __parseCall(Ast::Expression);
Ast::ListExpr __parseListExpr(); // entry: current is `[` Ast::ListExpr __parseListExpr(); // entry: current is `[`
Ast::MapExpr __parseMapExpr(); // 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::InitExpr __parseInitExpr(FString); // entry: current is `{`, ahead is struct name. arg (struct name : FString)
Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(` Ast::Expression __parseTupleOrParenExpr(); // entry: current is `(`

114
src/Value/LvObject.hpp Normal file
View File

@@ -0,0 +1,114 @@
#pragma once
#include <Value/VariableSlot.hpp>
#include <Value/value.hpp>
namespace Fig
{
struct LvObject
{
enum class Kind
{
Variable,
ListElement,
MapElement
} kind;
std::shared_ptr<VariableSlot> slot;
ObjectPtr listOrMap = nullptr;
size_t listIndex;
ObjectPtr mapIndex;
LvObject(std::shared_ptr<VariableSlot> _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<List>();
if (listIndex >= list.size())
throw RuntimeError(FString(
std::format("Index {} out of range", listIndex)));
return list.at(listIndex);
}
else // map
{
Map &map = listOrMap->as<Map>();
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<List>();
if (listIndex >= list.size())
throw RuntimeError(FString(
std::format("Index {} out of range", listIndex)));
list[listIndex] = v;
}
else // map
{
Map &map = listOrMap->as<Map>();
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<VariableSlot> resolve(std::shared_ptr<VariableSlot> s) const
{
while (s->isRef) s = s->refTarget;
return s;
}
};
}

View File

@@ -14,6 +14,8 @@ namespace Fig
size_t id; size_t id;
public: public:
friend class TypeInfoHash;
FString name; FString name;
FString toString() const FString toString() const
@@ -27,7 +29,7 @@ namespace Fig
{ {
return typeMap.at(_name); return typeMap.at(_name);
} }
size_t getInstanceID(FString _name) const size_t getInstanceID() const
{ {
return id; return id;
} }
@@ -42,6 +44,15 @@ namespace Fig
} }
}; };
class TypeInfoHash
{
public:
std::size_t operator()(const TypeInfo &ti) const
{
return ti.id;
}
};
// class Value; // class Value;
namespace ValueType namespace ValueType
{ {
@@ -56,7 +67,7 @@ namespace Fig
extern const TypeInfo StructInstance; extern const TypeInfo StructInstance;
extern const TypeInfo List; extern const TypeInfo List;
extern const TypeInfo Map; extern const TypeInfo Map;
extern const TypeInfo Tuple; // extern const TypeInfo Tuple;
using IntClass = int64_t; using IntClass = int64_t;
using DoubleClass = double; using DoubleClass = double;

View File

@@ -0,0 +1,22 @@
#pragma once
#include <Ast/AccessModifier.hpp>
#include <Core/fig_string.hpp>
#include <Value/Type.hpp>
#include <memory>
namespace Fig
{
class Object;
using ObjectPtr = std::shared_ptr<Object>;
struct VariableSlot
{
FString name;
ObjectPtr value;
TypeInfo declaredType;
AccessModifier am;
bool isRef = false;
std::shared_ptr<VariableSlot> refTarget;
};
}

View File

@@ -1,6 +0,0 @@
#pragma once
namespace Fig
{
};

View File

@@ -29,7 +29,6 @@ namespace Fig
{ {
return am == AccessModifier::Const || am == AccessModifier::PublicConst; return am == AccessModifier::Const || am == AccessModifier::PublicConst;
} }
}; };
struct StructType struct StructType
@@ -65,3 +64,15 @@ namespace Fig
} }
}; };
} // namespace Fig } // namespace Fig
namespace std
{
template <>
struct hash<Fig::Field>
{
size_t operator()(const Fig::Field &f)
{
return std::hash<Fig::FString>{}(f.name);
}
};
}; // namespace std

View File

@@ -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<ValueType::IntClass>{}(value->as<ValueType::IntClass>());
}
if (type == ValueType::Double)
{
return std::hash<ValueType::DoubleClass>{}(value->as<ValueType::DoubleClass>());
}
if (type == ValueType::String)
{
return std::hash<ValueType::StringClass>{}(value->as<ValueType::StringClass>());
}
if (type == ValueType::Bool)
{
return std::hash<ValueType::BoolClass>{}(value->as<ValueType::BoolClass>());
}
if (type == ValueType::StructType)
{
auto HashFields = [](std::vector<Field> fields) {
size_t r = 0;
for (auto &f : fields)
{
r += std::hash<Field>{}(f);
}
return r;
};
const StructType &st = value->as<StructType>();
return std::hash<std::size_t>{}(st.id) + HashFields(st.fields);
}
if (type == ValueType::StructInstance)
{
const StructInstance &si = value->as<StructInstance>();
return std::hash<std::size_t>{}(si.parentId) + std::hash<uint64_t>{}(reinterpret_cast<uint64_t>(std::addressof(*si.localContext)));
}
assert(false);
}
}
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1 const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2 const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3 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::StructInstance(FString(u8"StructInstance"), true); // id: 9
const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10 const TypeInfo ValueType::List(FString(u8"List"), true); // id: 10
const TypeInfo ValueType::Map(FString(u8"Map"), true); // id: 11 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 } // namespace Fig

View File

@@ -9,6 +9,8 @@
#include <cmath> #include <cmath>
#include <string> #include <string>
#include <format> #include <format>
#include <functional>
#include <unordered_map>
namespace Fig namespace Fig
{ {
@@ -26,6 +28,22 @@ namespace Fig
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min()); static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
return d > intMaxAsDouble || d < intMinAsDouble; return d > intMaxAsDouble || d < intMinAsDouble;
} }
class Object;
using ObjectPtr = std::shared_ptr<Object>;
using List = std::vector<ObjectPtr>;
struct ValueKey
{
ObjectPtr value;
ValueKey(ObjectPtr _value) :
value(_value) {}
};
struct ValueKeyHash
{
size_t operator()(const ValueKey &key) const;
};
using Map = std::unordered_map<ValueKey, ObjectPtr, ValueKeyHash>;
class Object class Object
{ {
@@ -38,7 +56,104 @@ namespace Fig
ValueType::BoolClass, ValueType::BoolClass,
Function, Function,
StructType, StructType,
StructInstance>; StructInstance,
List,
Map>;
std::unordered_map<TypeInfo,
std::unordered_map<FString,
std::function<ObjectPtr(std::vector<ObjectPtr>)>>,
TypeInfoHash>
memberTypeFunctions{
{ValueType::Null, {}},
{ValueType::Int, {}},
{ValueType::Double, {}},
{ValueType::String, {
{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(FString(
std::format("`length` expects 0 arguments, {} got", args.size())));
const FString &str = as<ValueType::StringClass>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(str.length()));
}},
}},
{ValueType::Function, {}},
{ValueType::StructType, {}},
{ValueType::StructInstance, {}},
{ValueType::List, {{u8"length", [this](std::vector<ObjectPtr> args) -> ObjectPtr {
if (args.size() != 0)
throw RuntimeError(FString(
std::format("`length` expects 0 arguments, {} got", args.size())));
const List &list = as<List>();
return std::make_shared<Object>(static_cast<ValueType::IntClass>(list.size()));
}},
{u8"get", [this](std::vector<ObjectPtr> 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<ValueType::IntClass>();
const List &list = as<List>();
if (i >= list.size())
return Object::getNullInstance();
return list[i];
}}}},
{ValueType::Map, {
{u8"get", [this](std::vector<ObjectPtr> 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<Map>();
if (!map.contains(index))
return Object::getNullInstance();
return map.at(index);
}},
{u8"contains", [this](std::vector<ObjectPtr> 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<Map>();
return std::make_shared<Object>(
map.contains(index));
}},
}},
};
std::unordered_map<TypeInfo, std::unordered_map<FString, int>, 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<ObjectPtr(std::vector<ObjectPtr>)> 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; VariantType data;
@@ -49,9 +164,7 @@ namespace Fig
Object(const ValueType::IntClass &i) : Object(const ValueType::IntClass &i) :
data(i) {} data(i) {}
explicit Object(const ValueType::DoubleClass &d) : explicit Object(const ValueType::DoubleClass &d) :
data(d) data(d) {}
{
}
Object(const ValueType::StringClass &s) : Object(const ValueType::StringClass &s) :
data(s) {} data(s) {}
Object(const ValueType::BoolClass &b) : Object(const ValueType::BoolClass &b) :
@@ -62,6 +175,10 @@ namespace Fig
data(s) {} data(s) {}
Object(const StructInstance &s) : Object(const StructInstance &s) :
data(s) {} data(s) {}
Object(const List &l) :
data(l) {}
Object(const Map &m) :
data(m) {}
Object(const Object &) = default; Object(const Object &) = default;
Object(Object &&) noexcept = default; Object(Object &&) noexcept = default;
@@ -78,6 +195,10 @@ namespace Fig
return Object(ValueType::StringClass(u8"")); return Object(ValueType::StringClass(u8""));
else if (ti == ValueType::Bool) else if (ti == ValueType::Bool)
return Object(ValueType::BoolClass(false)); return Object(ValueType::BoolClass(false));
else if (ti == ValueType::List)
return Object(List{});
else if (ti == ValueType::Map)
return Object(Map{});
else else
return *getNullInstance(); return *getNullInstance();
} }
@@ -120,22 +241,37 @@ namespace Fig
{ {
return std::visit([](auto &&val) -> TypeInfo { return std::visit([](auto &&val) -> TypeInfo {
using T = std::decay_t<decltype(val)>; using T = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, ValueType::NullClass>) if constexpr (std::is_same_v<T, ValueType::NullClass>)
return ValueType::Null; return ValueType::Null;
else if constexpr (std::is_same_v<T, ValueType::IntClass>) else if constexpr (std::is_same_v<T, ValueType::IntClass>)
return ValueType::Int; return ValueType::Int;
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>) else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
return ValueType::Double; return ValueType::Double;
else if constexpr (std::is_same_v<T, ValueType::StringClass>) else if constexpr (std::is_same_v<T, ValueType::StringClass>)
return ValueType::String; return ValueType::String;
else if constexpr (std::is_same_v<T, ValueType::BoolClass>) else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
return ValueType::Bool; return ValueType::Bool;
else if constexpr (std::is_same_v<T, Function>) else if constexpr (std::is_same_v<T, Function>)
return ValueType::Function; return ValueType::Function;
else if constexpr (std::is_same_v<T, StructType>) else if constexpr (std::is_same_v<T, StructType>)
return ValueType::StructType; return ValueType::StructType;
else if constexpr (std::is_same_v<T, StructInstance>) else if constexpr (std::is_same_v<T, StructInstance>)
return ValueType::StructInstance; return ValueType::StructInstance;
else if constexpr (std::is_same_v<T, List>)
return ValueType::List;
else if constexpr (std::is_same_v<T, Map>)
return ValueType::Map;
else else
return ValueType::Any; return ValueType::Any;
}, },
@@ -155,12 +291,18 @@ namespace Fig
throw RuntimeError(u8"getNumericValue: Not a numeric value"); throw RuntimeError(u8"getNumericValue: Not a numeric value");
} }
FString toStringIO() const
{
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
return toString();
}
FString toString() const FString toString() const
{ {
if (is<ValueType::NullClass>()) return FString(u8"null"); if (is<ValueType::NullClass>()) return FString(u8"null");
if (is<ValueType::IntClass>()) return FString(std::to_string(as<ValueType::IntClass>())); if (is<ValueType::IntClass>()) return FString(std::to_string(as<ValueType::IntClass>()));
if (is<ValueType::DoubleClass>()) return FString(std::format("{}", as<ValueType::DoubleClass>())); if (is<ValueType::DoubleClass>()) return FString(std::format("{}", as<ValueType::DoubleClass>()));
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>(); if (is<ValueType::StringClass>()) return FString(u8"<String \"") + as<ValueType::StringClass>() + FString(u8"\" >");
if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false"); if (is<ValueType::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
if (is<Function>()) if (is<Function>())
return FString(std::format("<Function {} at {:p}>", return FString(std::format("<Function {} at {:p}>",
@@ -174,6 +316,36 @@ namespace Fig
return FString(std::format("<StructInstance '{}' at {:p}>", return FString(std::format("<StructInstance '{}' at {:p}>",
as<StructInstance>().parentId, as<StructInstance>().parentId,
static_cast<const void *>(&as<StructInstance>()))); static_cast<const void *>(&as<StructInstance>())));
if (is<List>())
{
FString output(u8"[");
const List &list = as<List>();
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<Map>())
{
FString output(u8"{");
const Map &map = as<Map>();
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"<error>"); return FString(u8"<error>");
} }
@@ -191,7 +363,7 @@ namespace Fig
friend Object operator+(const Object &lhs, const Object &rhs) friend Object operator+(const Object &lhs, const Object &rhs)
{ {
if (lhs.isNull() || rhs.isNull()) 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()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -202,13 +374,13 @@ namespace Fig
} }
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>()) if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return Object(FString(lhs.as<ValueType::StringClass>() + rhs.as<ValueType::StringClass>())); return Object(FString(lhs.as<ValueType::StringClass>() + rhs.as<ValueType::StringClass>()));
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) friend Object operator-(const Object &lhs, const Object &rhs)
{ {
if (lhs.isNull() || rhs.isNull()) 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()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -217,13 +389,13 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(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) friend Object operator*(const Object &lhs, const Object &rhs)
{ {
if (lhs.isNull() || rhs.isNull()) 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()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -232,82 +404,82 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(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) friend Object operator/(const Object &lhs, const Object &rhs)
{ {
if (lhs.isNull() || rhs.isNull()) 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()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
auto rnv = rhs.getNumericValue(); auto rnv = rhs.getNumericValue();
if (rnv == 0) 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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() / rnv; auto result = lhs.getNumericValue() / rnv;
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt && !isNumberExceededIntLimit(result))
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(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) friend Object operator%(const Object &lhs, const Object &rhs)
{ {
if (lhs.isNull() || rhs.isNull()) 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()) if (lhs.isNumeric() && rhs.isNumeric())
{ {
auto rnv = rhs.getNumericValue(); auto rnv = rhs.getNumericValue();
if (rnv == 0) 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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>(); bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = std::fmod(lhs.getNumericValue(), rnv); auto result = std::fmod(lhs.getNumericValue(), rnv);
if (bothInt && !isNumberExceededIntLimit(result)) if (bothInt && !isNumberExceededIntLimit(result))
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(result); return Object(result);
} }
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
} }
// logic // logic
friend Object operator&&(const Object &lhs, const Object &rhs) friend Object operator&&(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>()) if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() && rhs.as<ValueType::BoolClass>()); return Object(lhs.as<ValueType::BoolClass>() && rhs.as<ValueType::BoolClass>());
} }
friend Object operator||(const Object &lhs, const Object &rhs) friend Object operator||(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>()) if (!lhs.is<ValueType::BoolClass>() || !rhs.is<ValueType::BoolClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
return Object(lhs.as<ValueType::BoolClass>() || rhs.as<ValueType::BoolClass>()); return Object(lhs.as<ValueType::BoolClass>() || rhs.as<ValueType::BoolClass>());
} }
friend Object operator!(const Object &v) friend Object operator!(const Object &v)
{ {
if (!v.is<ValueType::BoolClass>()) if (!v.is<ValueType::BoolClass>())
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<ValueType::BoolClass>()); return Object(!v.as<ValueType::BoolClass>());
} }
friend Object operator-(const Object &v) friend Object operator-(const Object &v)
{ {
if (v.isNull()) 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<ValueType::IntClass>()) if (v.is<ValueType::IntClass>())
return Object(-v.as<ValueType::IntClass>()); return Object(-v.as<ValueType::IntClass>());
if (v.is<ValueType::DoubleClass>()) if (v.is<ValueType::DoubleClass>())
return Object(-v.as<ValueType::DoubleClass>()); return Object(-v.as<ValueType::DoubleClass>());
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) friend Object operator~(const Object &v)
{ {
if (!v.is<ValueType::IntClass>()) if (!v.is<ValueType::IntClass>())
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<ValueType::IntClass>()); return Object(~v.as<ValueType::IntClass>());
} }
@@ -319,7 +491,7 @@ namespace Fig
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue(); if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>()) if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() < rhs.as<ValueType::StringClass>(); return lhs.as<ValueType::StringClass>() < rhs.as<ValueType::StringClass>();
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) { return lhs == rhs || lhs < rhs; }
friend bool operator>(const Object &lhs, const Object &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.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>()) if (lhs.is<ValueType::StringClass>() && rhs.is<ValueType::StringClass>())
return lhs.as<ValueType::StringClass>() > rhs.as<ValueType::StringClass>(); return lhs.as<ValueType::StringClass>() > rhs.as<ValueType::StringClass>();
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) { return lhs == rhs || lhs > rhs; }
@@ -335,49 +507,49 @@ namespace Fig
friend Object bit_and(const Object &lhs, const Object &rhs) friend Object bit_and(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>()) if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() & rhs.as<ValueType::IntClass>()); return Object(lhs.as<ValueType::IntClass>() & rhs.as<ValueType::IntClass>());
} }
friend Object bit_or(const Object &lhs, const Object &rhs) friend Object bit_or(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>()) if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() | rhs.as<ValueType::IntClass>()); return Object(lhs.as<ValueType::IntClass>() | rhs.as<ValueType::IntClass>());
} }
friend Object bit_xor(const Object &lhs, const Object &rhs) friend Object bit_xor(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>()) if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() ^ rhs.as<ValueType::IntClass>()); return Object(lhs.as<ValueType::IntClass>() ^ rhs.as<ValueType::IntClass>());
} }
friend Object bit_not(const Object &v) friend Object bit_not(const Object &v)
{ {
if (!v.is<ValueType::IntClass>()) if (!v.is<ValueType::IntClass>())
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<ValueType::IntClass>()); return Object(~v.as<ValueType::IntClass>());
} }
friend Object shift_left(const Object &lhs, const Object &rhs) friend Object shift_left(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>()) if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() << rhs.as<ValueType::IntClass>()); return Object(lhs.as<ValueType::IntClass>() << rhs.as<ValueType::IntClass>());
} }
friend Object shift_right(const Object &lhs, const Object &rhs) friend Object shift_right(const Object &lhs, const Object &rhs)
{ {
if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>()) if (!lhs.is<ValueType::IntClass>() || !rhs.is<ValueType::IntClass>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs))); throw ValueError(FString(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
return Object(lhs.as<ValueType::IntClass>() >> rhs.as<ValueType::IntClass>()); return Object(lhs.as<ValueType::IntClass>() >> rhs.as<ValueType::IntClass>());
} }
friend Object power(const Object &base, const Object &exp) friend Object power(const Object &base, const Object &exp)
{ {
if (base.isNull() || exp.isNull()) 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()) if (base.isNumeric() && exp.isNumeric())
{ {
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>(); bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
@@ -386,68 +558,17 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(result)); return Object(static_cast<ValueType::IntClass>(result));
return Object(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<Object>; using ObjectPtr = std::shared_ptr<Object>;
using RvObject = ObjectPtr; using RvObject = ObjectPtr;
struct VariableSlot
inline bool operator==(const ValueKey &l, const ValueKey &r)
{ {
FString name; return *l.value == *r.value;
ObjectPtr value; }
TypeInfo declaredType;
AccessModifier am;
bool isRef = false;
std::shared_ptr<VariableSlot> refTarget;
};
struct LvObject
{
std::shared_ptr<VariableSlot> 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<VariableSlot> resolve(std::shared_ptr<VariableSlot> s) const
{
while (s->isRef) s = s->refTarget;
return s;
}
};
} // namespace Fig } // namespace Fig