[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
{
public:
std::map<FString, Expression> val;
std::map<Expression, Expression> val;
MapExprAst()
{
type = AstType::MapExpr;
}
MapExprAst(std::map<FString, Expression> _val) :
MapExprAst(std::map<Expression, Expression> _val) :
val(std::move(_val))
{
type = AstType::MapExpr;

View File

@@ -7,6 +7,7 @@
#include <Context/context_forward.hpp>
#include <Core/fig_string.hpp>
#include <Value/value.hpp>
#include <Value/VariableSlot.hpp>
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<VariableSlot>(
name,

View File

@@ -15,7 +15,7 @@ namespace Fig
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)
@@ -28,12 +28,22 @@ namespace Fig
*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
{
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;

View File

@@ -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);
}

View File

@@ -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<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(
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<StructInstance>();
@@ -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<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(
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<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:
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()))));
}

View File

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

View File

@@ -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;

View File

@@ -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())

View File

@@ -36,16 +36,24 @@ namespace Fig
const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
{u8"__fstdout_print", [](const std::vector<ObjectPtr> &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<Object>(ValueType::IntClass(args.size()));
}},
{u8"__fstdout_println", [](const std::vector<ObjectPtr> &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<Object>(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<ObjectPtr> &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<ObjectPtr> &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<ObjectPtr> &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<ObjectPtr> &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;
}

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::Walrus))
{
if (hasSpecificType) throwAddressableError<SyntaxError>(FStringView(u8""));
if (hasSpecificType) throwAddressableError<SyntaxError>(FString(u8""));
tiName = Parser::varDefTypeFollowed;
}
next();
@@ -93,7 +93,7 @@ namespace Fig
}
catch (...)
{
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
}
return std::make_shared<Object>(d);
}
@@ -107,7 +107,7 @@ namespace Fig
}
catch (...)
{
throwAddressableError<SyntaxError>(FStringView(u8"Illegal number literal"));
throwAddressableError<SyntaxError>(FString(u8"Illegal number literal"));
}
return std::make_shared<Object>(i);
}
@@ -253,7 +253,7 @@ namespace Fig
}
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;
if (isThis(TokenType::Colon))
@@ -267,7 +267,7 @@ namespace Fig
if (isThis(TokenType::Assign))
{
next();
if (isEOF()) throwAddressableError<SyntaxError>(FStringView(u8"expect an expression"));
if (isEOF()) throwAddressableError<SyntaxError>(FString(u8"expect an expression"));
initExpr = parseExpression(0);
}
expectSemicolon();
@@ -314,7 +314,7 @@ namespace Fig
}
else
{
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
throwAddressableError<SyntaxError>(FString("Invalid syntax"));
}
}
else if (isThis(TokenType::Function))
@@ -333,16 +333,16 @@ namespace Fig
}
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
{
throwAddressableError<SyntaxError>(FStringView("Invalid syntax"));
throwAddressableError<SyntaxError>(FString("Invalid syntax"));
}
}
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));
}
@@ -368,7 +368,7 @@ namespace Fig
}
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))
@@ -392,7 +392,7 @@ namespace Fig
}
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))
{
@@ -625,15 +625,10 @@ namespace Fig
{
// entry: current is `{`
next(); // consume `{`
std::map<FString, Ast::Expression> val;
std::map<Ast::Expression, Ast::Expression> val;
while (!isThis(TokenType::RightBrace))
{
expect(TokenType::Identifier, FString(u8"key (identifier)"));
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
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<SyntaxError>(FStringView(
throwAddressableError<SyntaxError>(FString(
std::format("Expect `,` or `}}` in struct initialization expression, got {}",
currentToken().toString().toBasicString())
));
@@ -762,7 +757,7 @@ namespace Fig
}
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
}
@@ -794,10 +789,10 @@ namespace Fig
Token tok = currentToken();
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 (lhs == nullptr) throwAddressableError<SyntaxError>(FStringView(u8"Expected expression"));
if (lhs == nullptr) throwAddressableError<SyntaxError>(FString(u8"Expected expression"));
return lhs;
}
if (tok.getType() == TokenType::LeftBracket)
@@ -818,7 +813,7 @@ namespace Fig
if (currentToken().getType() == TokenType::Identifier)
{
// 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);
lhs = __parseFunctionLiteralExpr();
@@ -849,7 +844,7 @@ namespace Fig
}
else
{
throwAddressableError<SyntaxError>(FStringView(u8"Unexpected token in expression"));
throwAddressableError<SyntaxError>(FString(u8"Unexpected token in expression"));
}
// infix / (postfix) ?
@@ -872,7 +867,7 @@ namespace Fig
next(); // consume '.'
Token idTok = currentToken();
if (!idTok.isIdentifier())
throwAddressableError<SyntaxError>(FStringView(u8"Expected identifier after '.'"));
throwAddressableError<SyntaxError>(FString(u8"Expected identifier after '.'"));
FString member = idTok.getValue();
next(); // consume identifier

View File

@@ -92,7 +92,7 @@ namespace Fig
}
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>,
"_ErrT must derive from AddressableError");
@@ -101,7 +101,7 @@ namespace Fig
throw spError;
}
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>,
"_ErrT must derive from AddressableError");
@@ -112,7 +112,7 @@ namespace Fig
}
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>,
"_ErrT must derive from AddressableError");
@@ -215,7 +215,7 @@ namespace Fig
{
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(peekToken().getType()))));
}
@@ -225,7 +225,7 @@ namespace Fig
{
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(currentToken().getType()))));
}
@@ -235,7 +235,7 @@ namespace Fig
{
if (peekToken().getType() != type)
{
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
throwAddressableError<SyntaxError>(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<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
throwAddressableError<SyntaxError>(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 `(`

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;
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;

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;
}
};
struct StructType
@@ -65,3 +64,15 @@ 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::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

View File

@@ -9,6 +9,8 @@
#include <cmath>
#include <string>
#include <format>
#include <functional>
#include <unordered_map>
namespace Fig
{
@@ -26,6 +28,22 @@ namespace Fig
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
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
{
@@ -38,7 +56,104 @@ namespace Fig
ValueType::BoolClass,
Function,
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;
@@ -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<decltype(val)>;
if constexpr (std::is_same_v<T, ValueType::NullClass>)
return ValueType::Null;
else if constexpr (std::is_same_v<T, ValueType::IntClass>)
return ValueType::Int;
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
return ValueType::Double;
else if constexpr (std::is_same_v<T, ValueType::StringClass>)
return ValueType::String;
else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
return ValueType::Bool;
else if constexpr (std::is_same_v<T, Function>)
return ValueType::Function;
else if constexpr (std::is_same_v<T, StructType>)
return ValueType::StructType;
else if constexpr (std::is_same_v<T, 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
return ValueType::Any;
},
@@ -155,12 +291,18 @@ namespace Fig
throw RuntimeError(u8"getNumericValue: Not a numeric value");
}
FString toStringIO() const
{
if (is<ValueType::StringClass>()) return as<ValueType::StringClass>();
return toString();
}
FString toString() const
{
if (is<ValueType::NullClass>()) return FString(u8"null");
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::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<Function>())
return FString(std::format("<Function {} at {:p}>",
@@ -174,6 +316,36 @@ namespace Fig
return FString(std::format("<StructInstance '{}' at {:p}>",
as<StructInstance>().parentId,
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>");
}
@@ -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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -202,13 +374,13 @@ namespace Fig
}
if (lhs.is<ValueType::StringClass>() && rhs.is<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)
{
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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -217,13 +389,13 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
@@ -232,82 +404,82 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = lhs.getNumericValue() / rnv;
if (bothInt && !isNumberExceededIntLimit(result))
return Object(static_cast<ValueType::IntClass>(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<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
auto result = std::fmod(lhs.getNumericValue(), rnv);
if (bothInt && !isNumberExceededIntLimit(result))
return Object(static_cast<ValueType::IntClass>(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<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>());
}
friend Object operator||(const Object &lhs, const Object &rhs)
{
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>());
}
friend Object operator!(const Object &v)
{
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>());
}
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<ValueType::IntClass>())
return Object(-v.as<ValueType::IntClass>());
if (v.is<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)
{
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>());
}
@@ -319,7 +491,7 @@ namespace Fig
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() < rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<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)
@@ -327,7 +499,7 @@ namespace Fig
if (lhs.isNumeric() && rhs.isNumeric()) return lhs.getNumericValue() > rhs.getNumericValue();
if (lhs.is<ValueType::StringClass>() && rhs.is<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; }
@@ -335,49 +507,49 @@ namespace Fig
friend Object bit_and(const Object &lhs, const Object &rhs)
{
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>());
}
friend Object bit_or(const Object &lhs, const Object &rhs)
{
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>());
}
friend Object bit_xor(const Object &lhs, const Object &rhs)
{
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>());
}
friend Object bit_not(const Object &v)
{
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>());
}
friend Object shift_left(const Object &lhs, const Object &rhs)
{
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>());
}
friend Object shift_right(const Object &lhs, const Object &rhs)
{
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>());
}
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<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
@@ -386,68 +558,17 @@ namespace Fig
return Object(static_cast<ValueType::IntClass>(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 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<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;
}
};
return *l.value == *r.value;
}
} // namespace Fig