forked from PuqiAR/Fig-TreeWalker
[Feat] 详细区分左值(LvObject)与右值(RvObject -> ObjectPtr)
[Impl] 重构evaluator.cpp + hpp 全部 [Feat] 增加对于IndexExpr的解析 [Fix][Impl] 现在点运算符不由BinaryExpr负责,增加MemberExpr,单独实现解析 [Impl] 项目目录全部翻修, src/目录下单独文件夹放置每一个模块
This commit is contained in:
23
src/Ast/AccessModifier.hpp
Normal file
23
src/Ast/AccessModifier.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
namespace Fig
|
||||
{
|
||||
enum class AccessModifier : uint8_t
|
||||
{
|
||||
Normal,
|
||||
Const,
|
||||
Public,
|
||||
PublicConst,
|
||||
};
|
||||
|
||||
inline bool isAccessPublic(AccessModifier am)
|
||||
{
|
||||
return am == AccessModifier::Public || am == AccessModifier::PublicConst;
|
||||
}
|
||||
|
||||
inline bool isAccessConst(AccessModifier am)
|
||||
{
|
||||
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
||||
}
|
||||
};
|
||||
29
src/Ast/Expressions/BinaryExpr.hpp
Normal file
29
src/Ast/Expressions/BinaryExpr.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class BinaryExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Operator op;
|
||||
Expression lexp, rexp;
|
||||
|
||||
BinaryExprAst()
|
||||
{
|
||||
type = AstType::BinaryExpr;
|
||||
}
|
||||
BinaryExprAst(Expression _lexp, Operator _op, Expression _rexp)
|
||||
{
|
||||
type = AstType::BinaryExpr;
|
||||
|
||||
lexp = _lexp;
|
||||
op = _op;
|
||||
rexp = _rexp;
|
||||
}
|
||||
};
|
||||
|
||||
using BinaryExpr = std::shared_ptr<BinaryExprAst>;
|
||||
|
||||
}; // namespace Fig
|
||||
65
src/Ast/Expressions/ContainerInitExprs.hpp
Normal file
65
src/Ast/Expressions/ContainerInitExprs.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
// Container Data Types --- Tuple/List/Map...
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ListExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
std::vector<Expression> val;
|
||||
|
||||
ListExprAst()
|
||||
{
|
||||
type = AstType::ListExpr;
|
||||
}
|
||||
|
||||
ListExprAst(std::vector<Expression> _val) :
|
||||
val(std::move(_val))
|
||||
{
|
||||
type = AstType::ListExpr;
|
||||
}
|
||||
};
|
||||
using ListExpr = std::shared_ptr<ListExprAst>;
|
||||
|
||||
class TupleExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
std::vector<Expression> val;
|
||||
|
||||
TupleExprAst()
|
||||
{
|
||||
type = AstType::TupleExpr;
|
||||
}
|
||||
|
||||
TupleExprAst(std::vector<Expression> _val) :
|
||||
val(std::move(_val))
|
||||
{
|
||||
type = AstType::TupleExpr;
|
||||
}
|
||||
};
|
||||
using TupleExpr = std::shared_ptr<TupleExprAst>;
|
||||
|
||||
class MapExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
std::map<FString, Expression> val;
|
||||
|
||||
MapExprAst()
|
||||
{
|
||||
type = AstType::MapExpr;
|
||||
}
|
||||
|
||||
MapExprAst(std::map<FString, Expression> _val) :
|
||||
val(std::move(_val))
|
||||
{
|
||||
type = AstType::MapExpr;
|
||||
}
|
||||
};
|
||||
using MapExpr = std::shared_ptr<MapExprAst>;
|
||||
}; // namespace Fig::Ast
|
||||
54
src/Ast/Expressions/FunctionCall.hpp
Normal file
54
src/Ast/Expressions/FunctionCall.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// include/Ast/FunctionCall.hpp
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
struct FunctionArguments
|
||||
{
|
||||
std::vector<Expression> argv;
|
||||
size_t getLength() const { return argv.size(); }
|
||||
};
|
||||
|
||||
struct FunctionCallArgs final
|
||||
{
|
||||
std::vector<ObjectPtr> argv;
|
||||
size_t getLength() const { return argv.size(); }
|
||||
};
|
||||
|
||||
class FunctionCallExpr final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Expression callee;
|
||||
FunctionArguments arg;
|
||||
|
||||
FunctionCallExpr()
|
||||
{
|
||||
type = AstType::FunctionCall;
|
||||
}
|
||||
|
||||
FunctionCallExpr(Expression _callee, FunctionArguments _arg) :
|
||||
callee(std::move(_callee)), arg(std::move(_arg))
|
||||
{
|
||||
type = AstType::FunctionCall;
|
||||
}
|
||||
|
||||
virtual FString toString() override
|
||||
{
|
||||
FString s = callee->toString();
|
||||
s += u8"(";
|
||||
for (size_t i = 0; i < arg.argv.size(); ++i)
|
||||
{
|
||||
s += arg.argv[i]->toString();
|
||||
if (i + 1 < arg.argv.size())
|
||||
s += u8", ";
|
||||
}
|
||||
s += u8")";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
using FunctionCall = std::shared_ptr<FunctionCallExpr>;
|
||||
}; // namespace Fig
|
||||
47
src/Ast/Expressions/FunctionLiteralExpr.hpp
Normal file
47
src/Ast/Expressions/FunctionLiteralExpr.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <variant>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class FunctionLiteralExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
FunctionParameters paras;
|
||||
std::variant<BlockStatement, Expression> body;
|
||||
|
||||
FunctionLiteralExprAst(FunctionParameters _paras, BlockStatement _body) :
|
||||
paras(std::move(_paras)), body(std::move(_body))
|
||||
{
|
||||
type = AstType::FunctionLiteralExpr;
|
||||
}
|
||||
|
||||
FunctionLiteralExprAst(FunctionParameters _paras, Expression _exprBody) :
|
||||
paras(std::move(_paras)), body(std::move(_exprBody))
|
||||
{
|
||||
type = AstType::FunctionLiteralExpr;
|
||||
}
|
||||
|
||||
bool isExprMode() const
|
||||
{
|
||||
return std::holds_alternative<Expression>(body);
|
||||
}
|
||||
|
||||
BlockStatement &getBlockBody()
|
||||
{
|
||||
return std::get<BlockStatement>(body);
|
||||
}
|
||||
|
||||
Expression &getExprBody()
|
||||
{
|
||||
return std::get<Expression>(body);
|
||||
}
|
||||
|
||||
~FunctionLiteralExprAst() = default;
|
||||
};
|
||||
|
||||
using FunctionLiteralExpr = std::shared_ptr<FunctionLiteralExprAst>;
|
||||
} // namespace Fig::Ast
|
||||
42
src/Ast/Expressions/InitExpr.hpp
Normal file
42
src/Ast/Expressions/InitExpr.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class InitExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
FString structName;
|
||||
|
||||
std::vector<std::pair<FString, Expression>> args;
|
||||
|
||||
enum class InitMode
|
||||
{
|
||||
Positional = 1,
|
||||
Named,
|
||||
Shorthand
|
||||
} initMode;
|
||||
|
||||
/*
|
||||
3 ways of calling constructor
|
||||
.1 Person {"Fig", 1, "IDK"};
|
||||
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
|
||||
.3 Person {name, age, sex};
|
||||
*/
|
||||
|
||||
InitExprAst()
|
||||
{
|
||||
type = AstType::InitExpr;
|
||||
}
|
||||
|
||||
InitExprAst(FString _structName, std::vector<std::pair<FString, Expression>> _args, InitMode _initMode) :
|
||||
structName(std::move(_structName)), args(std::move(_args)), initMode(_initMode)
|
||||
{
|
||||
type = AstType::InitExpr;
|
||||
}
|
||||
};
|
||||
|
||||
using InitExpr = std::shared_ptr<InitExprAst>;
|
||||
|
||||
}; // namespace Fig::Ast
|
||||
49
src/Ast/Expressions/PostfixExprs.hpp
Normal file
49
src/Ast/Expressions/PostfixExprs.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
// actually, function call is postfix, too
|
||||
// but it's too long, so use a single file (FunctionCall.hpp)
|
||||
|
||||
class MemberExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Expression base;
|
||||
FString member;
|
||||
|
||||
MemberExprAst()
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
|
||||
MemberExprAst(Expression _base, FString _member) :
|
||||
base(std::move(_base)), member(std::move(_member))
|
||||
{
|
||||
type = AstType::MemberExpr;
|
||||
}
|
||||
};
|
||||
|
||||
using MemberExpr = std::shared_ptr<MemberExprAst>;
|
||||
|
||||
class IndexExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Expression base;
|
||||
Expression index;
|
||||
|
||||
IndexExprAst()
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
}
|
||||
|
||||
IndexExprAst(Expression _base, Expression _index) :
|
||||
base(std::move(_base)), index(std::move(_index))
|
||||
{
|
||||
type = AstType::IndexExpr;
|
||||
}
|
||||
};
|
||||
|
||||
using IndexExpr = std::shared_ptr<IndexExprAst>;
|
||||
}; // namespace Fig::Ast
|
||||
29
src/Ast/Expressions/TernaryExpr.hpp
Normal file
29
src/Ast/Expressions/TernaryExpr.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
// condition ? val_true : val_false
|
||||
class TernaryExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Expression condition;
|
||||
Expression valueT;
|
||||
Expression valueF;
|
||||
|
||||
TernaryExprAst()
|
||||
{
|
||||
type = AstType::TernaryExpr;
|
||||
}
|
||||
TernaryExprAst(Expression _condition, Expression _valueT, Expression _valueF)
|
||||
{
|
||||
type = AstType::TernaryExpr;
|
||||
|
||||
condition = std::move(_condition);
|
||||
valueT = std::move(_valueT);
|
||||
valueF = std::move(_valueF);
|
||||
}
|
||||
};
|
||||
using TernaryExpr = std::shared_ptr<TernaryExprAst>;
|
||||
} // namespace Fig
|
||||
27
src/Ast/Expressions/UnaryExpr.hpp
Normal file
27
src/Ast/Expressions/UnaryExpr.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class UnaryExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
Operator op;
|
||||
Expression exp;
|
||||
|
||||
UnaryExprAst()
|
||||
{
|
||||
type = AstType::UnaryExpr;
|
||||
}
|
||||
UnaryExprAst(Operator _op, Expression _exp)
|
||||
{
|
||||
type = AstType::UnaryExpr;
|
||||
|
||||
op = _op;
|
||||
exp = std::move(_exp);
|
||||
}
|
||||
};
|
||||
|
||||
using UnaryExpr = std::shared_ptr<UnaryExprAst>;
|
||||
} // namespace Fig
|
||||
26
src/Ast/Expressions/ValueExpr.hpp
Normal file
26
src/Ast/Expressions/ValueExpr.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
#include <Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ValueExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
ObjectPtr val;
|
||||
|
||||
ValueExprAst()
|
||||
{
|
||||
type = AstType::ValueExpr;
|
||||
}
|
||||
ValueExprAst(ObjectPtr _val)
|
||||
{
|
||||
type = AstType::ValueExpr;
|
||||
val = std::move(_val);
|
||||
}
|
||||
};
|
||||
|
||||
using ValueExpr = std::shared_ptr<ValueExprAst>;
|
||||
};
|
||||
24
src/Ast/Expressions/VarExpr.hpp
Normal file
24
src/Ast/Expressions/VarExpr.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class VarExprAst final : public ExpressionAst
|
||||
{
|
||||
public:
|
||||
const FString name;
|
||||
VarExprAst() :
|
||||
name(u8"")
|
||||
{
|
||||
type = AstType::VarExpr;
|
||||
}
|
||||
VarExprAst(FString _name) :
|
||||
name(std::move(_name))
|
||||
{
|
||||
type = AstType::VarExpr;
|
||||
}
|
||||
};
|
||||
|
||||
using VarExpr = std::shared_ptr<VarExprAst>;
|
||||
}; // namespace Fig
|
||||
46
src/Ast/Statements/ControlSt.hpp
Normal file
46
src/Ast/Statements/ControlSt.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ReturnSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Expression retValue;
|
||||
|
||||
ReturnSt()
|
||||
{
|
||||
type = AstType::ReturnSt;
|
||||
}
|
||||
ReturnSt(Expression _retValue) :
|
||||
retValue(_retValue)
|
||||
{
|
||||
type = AstType::ReturnSt;
|
||||
}
|
||||
};
|
||||
|
||||
using Return = std::shared_ptr<ReturnSt>;
|
||||
|
||||
class BreakSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
BreakSt()
|
||||
{
|
||||
type = AstType::BreakSt;
|
||||
}
|
||||
};
|
||||
|
||||
using Break = std::shared_ptr<BreakSt>;
|
||||
|
||||
class ContinueSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
ContinueSt()
|
||||
{
|
||||
type = AstType::ContinueSt;
|
||||
}
|
||||
};
|
||||
|
||||
using Continue = std::shared_ptr<ContinueSt>;
|
||||
};
|
||||
21
src/Ast/Statements/ExpressionStmt.hpp
Normal file
21
src/Ast/Statements/ExpressionStmt.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ExpressionStmtAst final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Expression exp;
|
||||
ExpressionStmtAst()
|
||||
{
|
||||
type = AstType::ExpressionStmt;
|
||||
}
|
||||
ExpressionStmtAst(Expression _exp) : exp(std::move(_exp))
|
||||
{
|
||||
type = AstType::ExpressionStmt;
|
||||
}
|
||||
};
|
||||
using ExpressionStmt = std::shared_ptr<ExpressionStmtAst>;
|
||||
}
|
||||
30
src/Ast/Statements/ForSt.hpp
Normal file
30
src/Ast/Statements/ForSt.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ForSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Statement initSt;
|
||||
Expression condition;
|
||||
Statement incrementSt;
|
||||
BlockStatement body;
|
||||
|
||||
ForSt()
|
||||
{
|
||||
type = AstType::ForSt;
|
||||
}
|
||||
ForSt(Statement _initSt, Expression _condition, Statement _incrementSt, BlockStatement _body) :
|
||||
initSt(std::move(_initSt)),
|
||||
condition(std::move(_condition)),
|
||||
incrementSt(std::move(_incrementSt)),
|
||||
body(std::move(_body))
|
||||
{
|
||||
type = AstType::ForSt;
|
||||
}
|
||||
};
|
||||
|
||||
using For = std::shared_ptr<ForSt>;
|
||||
};
|
||||
45
src/Ast/Statements/FunctionDefSt.hpp
Normal file
45
src/Ast/Statements/FunctionDefSt.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
|
||||
#include <Value/value.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
/*
|
||||
func greet(greeting, name:String, age:Int, split:String=":") -> Null
|
||||
{
|
||||
io.println("{}, {}{}{}", greeting, name, split, age);
|
||||
}
|
||||
|
||||
`greeting`, `name`, `age` -> positional parameters
|
||||
`split` -> default parameter
|
||||
*/
|
||||
|
||||
class FunctionDefSt final : public StatementAst // for definition
|
||||
{
|
||||
public:
|
||||
FString name;
|
||||
FunctionParameters paras;
|
||||
bool isPublic;
|
||||
FString retType;
|
||||
BlockStatement body;
|
||||
FunctionDefSt() :
|
||||
retType(ValueType::Null.name)
|
||||
{
|
||||
type = AstType::FunctionDefSt;
|
||||
}
|
||||
FunctionDefSt(FString _name, FunctionParameters _paras, bool _isPublic, FString _retType, BlockStatement _body)
|
||||
{
|
||||
type = AstType::FunctionDefSt;
|
||||
|
||||
name = std::move(_name);
|
||||
paras = std::move(_paras);
|
||||
isPublic = _isPublic;
|
||||
retType = std::move(_retType);
|
||||
body = std::move(_body);
|
||||
}
|
||||
};
|
||||
using FunctionDef = std::shared_ptr<FunctionDefSt>;
|
||||
}; // namespace Fig
|
||||
68
src/Ast/Statements/IfSt.hpp
Normal file
68
src/Ast/Statements/IfSt.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class ElseSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
BlockStatement body;
|
||||
ElseSt()
|
||||
{
|
||||
type = AstType::ElseSt;
|
||||
}
|
||||
ElseSt(BlockStatement _body) :
|
||||
body(_body)
|
||||
{
|
||||
type = AstType::ElseSt;
|
||||
}
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<Else Ast at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
using Else = std::shared_ptr<ElseSt>;
|
||||
class ElseIfSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Expression condition;
|
||||
BlockStatement body;
|
||||
ElseIfSt()
|
||||
{
|
||||
type = AstType::ElseIfSt;
|
||||
}
|
||||
ElseIfSt(Expression _condition,
|
||||
BlockStatement _body) :
|
||||
condition(_condition), body(_body)
|
||||
{
|
||||
type = AstType::ElseIfSt;
|
||||
}
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<ElseIf Ast at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
using ElseIf = std::shared_ptr<ElseIfSt>;
|
||||
class IfSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Expression condition;
|
||||
BlockStatement body;
|
||||
std::vector<ElseIf> elifs;
|
||||
Else els;
|
||||
IfSt()
|
||||
{
|
||||
type = AstType::IfSt;
|
||||
}
|
||||
IfSt(Expression _condition,
|
||||
BlockStatement _body,
|
||||
std::vector<ElseIf> _elifs,
|
||||
Else _els) :
|
||||
condition(_condition), body(_body), elifs(_elifs), els(_els)
|
||||
{
|
||||
type = AstType::IfSt;
|
||||
}
|
||||
};
|
||||
using If = std::shared_ptr<IfSt>;
|
||||
}; // namespace Fig
|
||||
0
src/Ast/Statements/ImplementSt.hpp
Normal file
0
src/Ast/Statements/ImplementSt.hpp
Normal file
44
src/Ast/Statements/StructDefSt.hpp
Normal file
44
src/Ast/Statements/StructDefSt.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
|
||||
struct StructDefField
|
||||
{
|
||||
AccessModifier am;
|
||||
FString fieldName;
|
||||
FString tiName;
|
||||
Expression defaultValueExpr;
|
||||
|
||||
StructDefField() {}
|
||||
StructDefField(AccessModifier _am, FString _fieldName, FString _tiName, Expression _defaultValueExpr) :
|
||||
am(std::move(_am)), fieldName(std::move(_fieldName)), tiName(std::move(_tiName)), defaultValueExpr(std::move(_defaultValueExpr))
|
||||
{
|
||||
}
|
||||
};
|
||||
class StructDefSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
bool isPublic;
|
||||
const FString name;
|
||||
const std::vector<StructDefField> fields; // field name (:type name = default value expression)
|
||||
// name / name: String / name: String = "Fig"
|
||||
const BlockStatement body;
|
||||
StructDefSt()
|
||||
{
|
||||
type = AstType::StructSt;
|
||||
}
|
||||
StructDefSt(bool _isPublic, FString _name, std::vector<StructDefField> _fields, BlockStatement _body) :
|
||||
isPublic(std::move(_isPublic)), name(std::move(_name)), fields(std::move(_fields)), body(std::move(_body))
|
||||
{
|
||||
type = AstType::StructSt;
|
||||
}
|
||||
};
|
||||
|
||||
using StructDef = std::shared_ptr<StructDefSt>;
|
||||
}; // namespace Fig
|
||||
34
src/Ast/Statements/VarDef.hpp
Normal file
34
src/Ast/Statements/VarDef.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class VarDefAst final : public StatementAst
|
||||
{
|
||||
public:
|
||||
bool isPublic;
|
||||
bool isConst;
|
||||
FString name;
|
||||
FString typeName;
|
||||
Expression expr;
|
||||
|
||||
VarDefAst() :
|
||||
typeName(ValueType::Any.name)
|
||||
{
|
||||
type = AstType::VarDefSt;
|
||||
}
|
||||
VarDefAst(bool _isPublic, bool _isConst, FString _name, FString _info, Expression _expr) :
|
||||
typeName(std::move(_info))
|
||||
{
|
||||
type = AstType::VarDefSt;
|
||||
isPublic = _isPublic;
|
||||
isConst = _isConst;
|
||||
name = std::move(_name);
|
||||
expr = std::move(_expr);
|
||||
}
|
||||
};
|
||||
|
||||
using VarDef = std::shared_ptr<VarDefAst>;
|
||||
} // namespace Fig
|
||||
26
src/Ast/Statements/WhileSt.hpp
Normal file
26
src/Ast/Statements/WhileSt.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
class WhileSt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
Expression condition;
|
||||
BlockStatement body;
|
||||
|
||||
WhileSt()
|
||||
{
|
||||
type = AstType::WhileSt;
|
||||
}
|
||||
|
||||
WhileSt(Expression _condition, BlockStatement _body)
|
||||
: condition(_condition), body(_body)
|
||||
{
|
||||
type = AstType::WhileSt;
|
||||
}
|
||||
};
|
||||
|
||||
using While = std::shared_ptr<WhileSt>;
|
||||
};
|
||||
26
src/Ast/ast.hpp
Normal file
26
src/Ast/ast.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Ast/AccessModifier.hpp>
|
||||
#include <Ast/functionParameters.hpp>
|
||||
|
||||
#include <Ast/Expressions/BinaryExpr.hpp>
|
||||
#include <Ast/Expressions/ContainerInitExprs.hpp>
|
||||
#include <Ast/Expressions/FunctionCall.hpp>
|
||||
#include <Ast/Expressions/FunctionLiteralExpr.hpp>
|
||||
#include <Ast/Expressions/InitExpr.hpp>
|
||||
#include <Ast/Expressions/PostfixExprs.hpp>
|
||||
#include <Ast/Expressions/TernaryExpr.hpp>
|
||||
#include <Ast/Expressions/UnaryExpr.hpp>
|
||||
#include <Ast/Expressions/ValueExpr.hpp>
|
||||
#include <Ast/Expressions/VarExpr.hpp>
|
||||
|
||||
#include <Ast/Statements/VarDef.hpp>
|
||||
#include <Ast/Statements/WhileSt.hpp>
|
||||
#include <Ast/Statements/StructDefSt.hpp>
|
||||
#include <Ast/Statements/IfSt.hpp>
|
||||
#include <Ast/Statements/ImplementSt.hpp>
|
||||
#include <Ast/Statements/FunctionDefSt.hpp>
|
||||
#include <Ast/Statements/ControlSt.hpp>
|
||||
#include <Ast/Statements/ExpressionStmt.hpp>
|
||||
#include <Ast/Statements/ForSt.hpp>
|
||||
344
src/Ast/astBase.hpp
Normal file
344
src/Ast/astBase.hpp
Normal file
@@ -0,0 +1,344 @@
|
||||
#pragma once
|
||||
|
||||
#include <Token/token.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
enum class AstType : uint8_t
|
||||
{
|
||||
/* Base Class */
|
||||
_AstBase,
|
||||
StatementBase,
|
||||
ExpressionBase,
|
||||
/* Expression */
|
||||
ValueExpr,
|
||||
VarExpr,
|
||||
LambdaExpr,
|
||||
UnaryExpr,
|
||||
BinaryExpr,
|
||||
TernaryExpr,
|
||||
|
||||
/* Postfix */
|
||||
MemberExpr, // a.b
|
||||
IndexExpr, // a[b]
|
||||
FunctionCall, // a()
|
||||
|
||||
/* Literals */
|
||||
ListExpr, // [1, "2", 3
|
||||
TupleExpr, // (1, 2, 3)
|
||||
MapExpr, // {a: 1}
|
||||
InitExpr, // struct{"123", 456}
|
||||
FunctionLiteralExpr,
|
||||
|
||||
/* Statement */
|
||||
BlockStatement,
|
||||
ExpressionStmt,
|
||||
|
||||
VarDefSt,
|
||||
FunctionDefSt,
|
||||
StructSt,
|
||||
ImplementSt,
|
||||
|
||||
IfSt,
|
||||
ElseSt,
|
||||
ElseIfSt,
|
||||
|
||||
VarAssignSt,
|
||||
WhileSt,
|
||||
ForSt,
|
||||
ReturnSt,
|
||||
BreakSt,
|
||||
ContinueSt,
|
||||
};
|
||||
|
||||
// static const std::unordered_map<AstType, FString> astTypeToString{
|
||||
// /* Base Class */
|
||||
// {AstType::_AstBase, FString(u8"Ast")},
|
||||
// {AstType::StatementBase, FString(u8"Statement")},
|
||||
// {AstType::ExpressionBase, FString(u8"Expression")},
|
||||
// /* Expression */
|
||||
// {AstType::ValueExpr, FString(u8"ValueExpr")},
|
||||
// {AstType::LambdaExpr, FString(u8"LambdaExpr")},
|
||||
// {AstType::UnaryExpr, FString(u8"UnaryExpr")},
|
||||
// {AstType::BinaryExpr, FString(u8"BinaryExpr")},
|
||||
// {AstType::TernaryExpr, FString(u8"TernaryExpr")},
|
||||
|
||||
// {AstType::InitExpr, FString(u8"InitExpr")},
|
||||
|
||||
// /* Statement */
|
||||
// {AstType::BlockStatement, FString(u8"BlockStatement")},
|
||||
|
||||
// {AstType::VarDefSt, FString(u8"VarSt")},
|
||||
// {AstType::FunctionDefSt, FString(u8"FunctionDefSt")},
|
||||
// {AstType::StructSt, FString(u8"StructSt")},
|
||||
// {AstType::ImplementSt, FString(u8"ImplementSt")},
|
||||
|
||||
// {AstType::IfSt, FString(u8"IfSt")},
|
||||
// {AstType::ElseSt, FString(u8"ElseSt")},
|
||||
// {AstType::ElseIfSt, FString(u8"ElseIfSt")},
|
||||
// {AstType::VarAssignSt, FString(u8"VarAssignSt")},
|
||||
// {AstType::WhileSt, FString(u8"WhileSt")},
|
||||
// {AstType::ReturnSt, FString(u8"ReturnSt")},
|
||||
// {AstType::BreakSt, FString(u8"BreakSt")},
|
||||
// {AstType::ContinueSt, FString(u8"ContinueSt")},
|
||||
// };
|
||||
|
||||
struct AstAddressInfo
|
||||
{
|
||||
size_t line, column;
|
||||
};
|
||||
|
||||
class _AstBase
|
||||
{
|
||||
protected:
|
||||
AstType type;
|
||||
AstAddressInfo aai;
|
||||
|
||||
public:
|
||||
_AstBase(const _AstBase &) = default;
|
||||
_AstBase(_AstBase &&) = default;
|
||||
|
||||
_AstBase &operator=(const _AstBase &) = default;
|
||||
_AstBase &operator=(_AstBase &&) = default;
|
||||
|
||||
_AstBase() {}
|
||||
|
||||
void setAAI(AstAddressInfo _aai)
|
||||
{
|
||||
aai = std::move(_aai);
|
||||
}
|
||||
|
||||
virtual FString typeName()
|
||||
{
|
||||
return FString::fromStringView(
|
||||
FStringView::fromBasicStringView(magic_enum::enum_name(type)));
|
||||
}
|
||||
virtual FString toString()
|
||||
{
|
||||
return FString(std::format("<Base Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
|
||||
AstAddressInfo getAAI()
|
||||
{
|
||||
return aai;
|
||||
}
|
||||
|
||||
AstType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
class StatementAst : public _AstBase
|
||||
{
|
||||
public:
|
||||
using _AstBase::_AstBase;
|
||||
using _AstBase::operator=;
|
||||
StatementAst()
|
||||
{
|
||||
type = AstType::StatementBase;
|
||||
}
|
||||
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<Stmt Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
|
||||
class EofStmt final : public StatementAst
|
||||
{
|
||||
public:
|
||||
EofStmt()
|
||||
{
|
||||
type = AstType::StatementBase;
|
||||
}
|
||||
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<EOF Stmt at {}:{}>", aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
|
||||
class ExpressionAst : public _AstBase
|
||||
{
|
||||
public:
|
||||
using _AstBase::_AstBase;
|
||||
using _AstBase::operator=;
|
||||
ExpressionAst()
|
||||
{
|
||||
type = AstType::ExpressionBase;
|
||||
}
|
||||
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<Expr Ast '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
};
|
||||
enum class Operator : uint8_t
|
||||
{
|
||||
LeftParen,
|
||||
RightParen,
|
||||
|
||||
// 算术
|
||||
Add, // +
|
||||
Subtract, // -
|
||||
Multiply, // *
|
||||
Divide, // /
|
||||
Modulo, // %
|
||||
Power, // **
|
||||
|
||||
// 逻辑
|
||||
And, // and / &&
|
||||
Or, // or / ||
|
||||
Not, // not / !
|
||||
|
||||
// 比较
|
||||
Equal, // ==
|
||||
NotEqual, // !=
|
||||
Less, // <
|
||||
LessEqual, // <=
|
||||
Greater, // >
|
||||
GreaterEqual, // >=
|
||||
|
||||
// 三目
|
||||
TernaryCond,
|
||||
|
||||
// 位运算
|
||||
BitAnd, // &
|
||||
BitOr, // |
|
||||
BitXor, // ^
|
||||
BitNot, // ~
|
||||
ShiftLeft, // <<
|
||||
ShiftRight, // >>
|
||||
|
||||
// 赋值表达式
|
||||
Assign, // =
|
||||
// Walrus, // :=
|
||||
};
|
||||
|
||||
static const std::unordered_set<Operator> unaryOps{
|
||||
Operator::Not,
|
||||
Operator::Subtract,
|
||||
Operator::BitNot,
|
||||
};
|
||||
static const std::unordered_set<Operator> binaryOps{
|
||||
Operator::Add,
|
||||
Operator::Subtract,
|
||||
Operator::Multiply,
|
||||
Operator::Divide,
|
||||
Operator::Modulo,
|
||||
Operator::Power,
|
||||
Operator::And,
|
||||
Operator::Or,
|
||||
|
||||
Operator::Equal,
|
||||
Operator::NotEqual,
|
||||
Operator::Less,
|
||||
Operator::LessEqual,
|
||||
Operator::Greater,
|
||||
Operator::GreaterEqual,
|
||||
|
||||
Operator::BitAnd,
|
||||
Operator::BitOr,
|
||||
Operator::BitXor,
|
||||
Operator::BitNot,
|
||||
Operator::ShiftLeft,
|
||||
Operator::ShiftRight,
|
||||
|
||||
// Operator::Walrus,
|
||||
// Operator::Dot
|
||||
};
|
||||
static const std::unordered_set<Operator> ternaryOps{Operator::TernaryCond};
|
||||
|
||||
static const std::unordered_map<TokenType, Operator> TokenToOp{
|
||||
// 算术
|
||||
{TokenType::Plus, Operator::Add},
|
||||
{TokenType::Minus, Operator::Subtract},
|
||||
{TokenType::Asterisk, Operator::Multiply},
|
||||
{TokenType::Slash, Operator::Divide},
|
||||
{TokenType::Percent, Operator::Modulo},
|
||||
{TokenType::Power, Operator::Power},
|
||||
|
||||
// 逻辑
|
||||
{TokenType::And, Operator::And},
|
||||
{TokenType::DoubleAmpersand, Operator::And},
|
||||
{TokenType::Or, Operator::Or},
|
||||
{TokenType::DoublePipe, Operator::Or},
|
||||
{TokenType::Not, Operator::Not},
|
||||
|
||||
// 比较
|
||||
{TokenType::Equal, Operator::Equal},
|
||||
{TokenType::NotEqual, Operator::NotEqual},
|
||||
{TokenType::Less, Operator::Less},
|
||||
{TokenType::LessEqual, Operator::LessEqual},
|
||||
{TokenType::Greater, Operator::Greater},
|
||||
{TokenType::GreaterEqual, Operator::GreaterEqual},
|
||||
|
||||
// 三目
|
||||
{TokenType::Question, Operator::TernaryCond},
|
||||
|
||||
// 位运算
|
||||
{TokenType::Ampersand, Operator::BitAnd},
|
||||
{TokenType::Pipe, Operator::BitOr},
|
||||
{TokenType::Caret, Operator::BitXor},
|
||||
{TokenType::Tilde, Operator::BitNot},
|
||||
{TokenType::ShiftLeft, Operator::ShiftLeft},
|
||||
{TokenType::ShiftRight, Operator::ShiftRight},
|
||||
|
||||
// 赋值表达式
|
||||
{TokenType::Assign, Operator::Assign},
|
||||
// {TokenType::Walrus, Operator::Walrus},
|
||||
}; // :=
|
||||
|
||||
inline bool isOpUnary(Operator op)
|
||||
{
|
||||
return unaryOps.contains(op);
|
||||
}
|
||||
inline bool isOpBinary(Operator op)
|
||||
{
|
||||
return binaryOps.contains(op);
|
||||
}
|
||||
inline bool isOpTernary(Operator op)
|
||||
{
|
||||
return ternaryOps.contains(op);
|
||||
}
|
||||
|
||||
using AstBase = std::shared_ptr<_AstBase>;
|
||||
using Statement = std::shared_ptr<StatementAst>;
|
||||
using Expression = std::shared_ptr<ExpressionAst>;
|
||||
using Eof = std::shared_ptr<EofStmt>;
|
||||
|
||||
class BlockStatementAst : public StatementAst
|
||||
{
|
||||
public:
|
||||
std::vector<Statement> stmts;
|
||||
BlockStatementAst()
|
||||
{
|
||||
type = AstType::BlockStatement;
|
||||
}
|
||||
BlockStatementAst(std::vector<Statement> _stmts) :
|
||||
stmts(std::move(_stmts))
|
||||
{
|
||||
type = AstType::BlockStatement;
|
||||
}
|
||||
virtual FString typeName() override
|
||||
{
|
||||
return FString(u8"BlockStatement");
|
||||
}
|
||||
virtual FString toString() override
|
||||
{
|
||||
return FString(std::format("<StmtAst '{}' at {}:{}>", typeName().toBasicString(), aai.line, aai.column));
|
||||
}
|
||||
virtual ~BlockStatementAst() = default;
|
||||
};
|
||||
|
||||
using BlockStatement = std::shared_ptr<BlockStatementAst>;
|
||||
// static BlockStatement builtinEmptyBlockSt(new BlockStatementAst());
|
||||
}; // namespace Fig::Ast
|
||||
39
src/Ast/functionParameters.hpp
Normal file
39
src/Ast/functionParameters.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/astBase.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
namespace Fig::Ast
|
||||
{
|
||||
struct FunctionParameters // for define
|
||||
{
|
||||
/*
|
||||
Positional Parameters:
|
||||
fun test(pp1, pp2: Int)
|
||||
Default Parameters:
|
||||
fun test2(dp1 = 10, dp2:String = "default parameter 2")
|
||||
*/
|
||||
|
||||
using PosParasType = std::vector<std::pair<FString, FString>>;
|
||||
using DefParasType = std::vector<std::pair<FString, std::pair<FString, Expression>>>;
|
||||
|
||||
PosParasType posParas;
|
||||
DefParasType defParas; // default parameters
|
||||
|
||||
FunctionParameters()
|
||||
{
|
||||
|
||||
}
|
||||
FunctionParameters(PosParasType _posParas, DefParasType _defParas)
|
||||
{
|
||||
posParas = std::move(_posParas);
|
||||
defParas = std::move(_defParas);
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return posParas.size() + defParas.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
267
src/Context/context.hpp
Normal file
267
src/Context/context.hpp
Normal file
@@ -0,0 +1,267 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class Context : public std::enable_shared_from_this<Context>
|
||||
{
|
||||
private:
|
||||
FString scopeName;
|
||||
std::unordered_map<FString, std::shared_ptr<VariableSlot>> variables;
|
||||
|
||||
std::unordered_map<std::size_t, Function> functions;
|
||||
std::unordered_map<std::size_t, FString> functionNames;
|
||||
std::unordered_map<std::size_t, FString> structTypeNames;
|
||||
|
||||
public:
|
||||
ContextPtr parent;
|
||||
|
||||
Context(const Context &) = default;
|
||||
Context(const FString &name, ContextPtr p = nullptr) :
|
||||
scopeName(name), parent(p) {}
|
||||
Context(const FString &name, std::unordered_map<FString, TypeInfo> types, std::unordered_map<FString, std::shared_ptr<VariableSlot>> vars, std::unordered_map<FString, AccessModifier> _ams) :
|
||||
scopeName(std::move(name)), variables(std::move(vars)) {}
|
||||
|
||||
void setParent(ContextPtr _parent)
|
||||
{
|
||||
parent = _parent;
|
||||
}
|
||||
|
||||
void setScopeName(FString _name)
|
||||
{
|
||||
scopeName = std::move(_name);
|
||||
}
|
||||
|
||||
FString getScopeName() const
|
||||
{
|
||||
return scopeName;
|
||||
}
|
||||
|
||||
void merge(const Context &c)
|
||||
{
|
||||
variables.insert(c.variables.begin(), c.variables.end());
|
||||
functions.insert(c.functions.begin(), c.functions.end());
|
||||
functionNames.insert(c.functionNames.begin(), c.functionNames.end());
|
||||
structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end());
|
||||
}
|
||||
|
||||
std::unordered_map<size_t, Function> getFunctions() const
|
||||
{
|
||||
return functions;
|
||||
}
|
||||
|
||||
std::shared_ptr<VariableSlot> get(const FString &name)
|
||||
{
|
||||
auto it = variables.find(name);
|
||||
if (it != variables.end())
|
||||
return it->second;
|
||||
if (parent)
|
||||
return parent->get(name);
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
AccessModifier getAccessModifier(const FString &name)
|
||||
{
|
||||
if (variables.contains(name))
|
||||
{
|
||||
return variables[name]->am;
|
||||
}
|
||||
else if (parent != nullptr)
|
||||
{
|
||||
return parent->getAccessModifier(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
}
|
||||
bool isVariableMutable(const FString &name)
|
||||
{
|
||||
AccessModifier am = getAccessModifier(name); // may throw
|
||||
return !isAccessConst(am);
|
||||
}
|
||||
bool isVariablePublic(const FString &name)
|
||||
{
|
||||
AccessModifier am = getAccessModifier(name); // may throw
|
||||
return isAccessPublic(am);
|
||||
}
|
||||
void set(const FString &name, ObjectPtr value)
|
||||
{
|
||||
if (variables.contains(name))
|
||||
{
|
||||
if (!isVariableMutable(name))
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString())));
|
||||
}
|
||||
variables[name]->value = value;
|
||||
}
|
||||
else if (parent != nullptr)
|
||||
{
|
||||
parent->set(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||
}
|
||||
}
|
||||
void _update(const FString &name, ObjectPtr value)
|
||||
{
|
||||
if (variables.contains(name))
|
||||
{
|
||||
variables[name]->value = value;
|
||||
}
|
||||
else if (parent != nullptr)
|
||||
{
|
||||
parent->set(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(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())));
|
||||
}
|
||||
variables[name] = std::make_shared<VariableSlot>(
|
||||
name,
|
||||
value,
|
||||
ti,
|
||||
am);
|
||||
if (ti == ValueType::Function and value->getTypeInfo() == ValueType::Function)
|
||||
{
|
||||
auto &fn = value->as<Function>();
|
||||
functions[fn.id] = fn;
|
||||
functionNames[fn.id] = name;
|
||||
}
|
||||
if (ti == ValueType::StructType)
|
||||
{
|
||||
auto &st = value->as<StructType>();
|
||||
structTypeNames[st.id] = name;
|
||||
}
|
||||
}
|
||||
std::optional<Function> getFunction(std::size_t id)
|
||||
{
|
||||
auto it = functions.find(id);
|
||||
if (it != functions.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else if (parent)
|
||||
{
|
||||
return parent->getFunction(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
std::optional<FString> getFunctionName(std::size_t id)
|
||||
{
|
||||
auto it = functionNames.find(id);
|
||||
if (it != functionNames.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else if (parent)
|
||||
{
|
||||
return parent->getFunctionName(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
std::optional<FString> getStructName(std::size_t id)
|
||||
{
|
||||
auto it = structTypeNames.find(id);
|
||||
if (it != structTypeNames.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
else if (parent)
|
||||
{
|
||||
return parent->getFunctionName(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
bool contains(const FString &name)
|
||||
{
|
||||
if (variables.contains(name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (parent != nullptr)
|
||||
{
|
||||
return parent->contains(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool containsInThisScope(const FString &name) const
|
||||
{
|
||||
return variables.contains(name);
|
||||
}
|
||||
|
||||
TypeInfo getTypeInfo(const FString &name)
|
||||
{
|
||||
return get(name)->declaredType;
|
||||
}
|
||||
bool isInFunctionContext()
|
||||
{
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<Function ") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool isInLoopContext()
|
||||
{
|
||||
ContextPtr ctx = shared_from_this();
|
||||
while (ctx)
|
||||
{
|
||||
if (ctx->getScopeName().find(u8"<While ") == 0 or ctx->getScopeName().find(u8"<For ") == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ctx = ctx->parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void printStackTrace(std::ostream &os = std::cerr, int indent = 0) const
|
||||
{
|
||||
const Context *ctx = this;
|
||||
std::vector<const Context *> chain;
|
||||
|
||||
while (ctx)
|
||||
{
|
||||
chain.push_back(ctx);
|
||||
ctx = ctx->parent.get();
|
||||
}
|
||||
|
||||
os << "[STACK TRACE]\n";
|
||||
for (int i = static_cast<int>(chain.size()) - 1; i >= 0; --i)
|
||||
{
|
||||
os << " #" << (chain.size() - 1 - i)
|
||||
<< " " << chain[i]->scopeName.toBasicString()
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
}; // namespace Fig
|
||||
9
src/Context/context_forward.hpp
Normal file
9
src/Context/context_forward.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Context;
|
||||
using ContextPtr = std::shared_ptr<Context>;
|
||||
};
|
||||
59
src/Core/core.hpp
Normal file
59
src/Core/core.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
#define __FCORE_VERSION "0.3.2"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define __FCORE_PLATFORM "Windows"
|
||||
#elif defined(__APPLE__)
|
||||
#define __FCORE_PLATFORM "Apple"
|
||||
#elif defined(__linux__)
|
||||
#define __FCORE_PLATFORM "Linux"
|
||||
#elif defined(__unix__)
|
||||
#define __FCORE_PLATFORM "Unix"
|
||||
#else
|
||||
#define __FCORE_PLATFORM "Unknown"
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(_WIN32)
|
||||
#if defined(__clang__)
|
||||
#define __FCORE_COMPILER "llvm-mingw"
|
||||
#else
|
||||
#define __FCORE_COMPILER "MinGW"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define __FCORE_COMPILER "GCC"
|
||||
#endif
|
||||
#elif defined(__clang__)
|
||||
#define __FCORE_COMPILER "Clang"
|
||||
#elif defined(_MSC_VER)
|
||||
#define __FCORE_COMPILER "MSVC"
|
||||
#else
|
||||
#define __FCORE_COMPILER "Unknown"
|
||||
#endif
|
||||
|
||||
#if SIZE_MAX == 18446744073709551615ull
|
||||
#define __FCORE_ARCH "64"
|
||||
#else
|
||||
#define __FCORE_ARCH "86"
|
||||
#endif
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
namespace Core
|
||||
{
|
||||
inline constexpr std::string_view VERSION = __FCORE_VERSION;
|
||||
inline constexpr std::string_view LICENSE = "MIT";
|
||||
inline constexpr std::string_view AUTHOR = "PuqiAR";
|
||||
inline constexpr std::string_view PLATFORM = __FCORE_PLATFORM;
|
||||
inline constexpr std::string_view COMPILER = __FCORE_COMPILER;
|
||||
inline constexpr std::string_view COMPILE_TIME = __FCORE_COMPILE_TIME;
|
||||
inline constexpr std::string_view ARCH = __FCORE_ARCH;
|
||||
inline constexpr FString MAIN_FUNCTION = u8"main";
|
||||
}; // namespace Core
|
||||
}; // namespace Fig
|
||||
100
src/Core/fig_string.hpp
Normal file
100
src/Core/fig_string.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// using String = std::u8string;
|
||||
// using StringView = std::u8string_view;
|
||||
|
||||
class FStringView : public std::u8string_view
|
||||
{
|
||||
public:
|
||||
using std::u8string_view::u8string_view;
|
||||
|
||||
static FStringView fromBasicStringView(std::string_view sv)
|
||||
{
|
||||
return FStringView(reinterpret_cast<const char8_t*>(sv.data()), sv.size());
|
||||
}
|
||||
|
||||
explicit FStringView(std::string_view sv)
|
||||
{
|
||||
*this = fromBasicStringView(sv);
|
||||
}
|
||||
|
||||
explicit FStringView()
|
||||
{
|
||||
*this = fromBasicStringView(std::string_view(""));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class FString : public std::u8string
|
||||
{
|
||||
public:
|
||||
using std::u8string::u8string;
|
||||
explicit FString(const std::u8string &str)
|
||||
{
|
||||
*this = fromU8String(str);
|
||||
}
|
||||
explicit FString(std::string str)
|
||||
{
|
||||
*this = fromBasicString(str);
|
||||
}
|
||||
explicit FString(FStringView sv)
|
||||
{
|
||||
*this = fromStringView(sv);
|
||||
}
|
||||
std::string toBasicString() const
|
||||
{
|
||||
return std::string(this->begin(), this->end());
|
||||
}
|
||||
FStringView toStringView() const
|
||||
{
|
||||
return FStringView(this->data(), this->size());
|
||||
}
|
||||
|
||||
static FString fromBasicString(const std::string &str)
|
||||
{
|
||||
return FString(str.begin(), str.end());
|
||||
}
|
||||
|
||||
static FString fromStringView(FStringView sv)
|
||||
{
|
||||
return FString(reinterpret_cast<const char*> (sv.data()));
|
||||
}
|
||||
|
||||
static FString fromU8String(const std::u8string &str)
|
||||
{
|
||||
return FString(str.begin(), str.end());
|
||||
}
|
||||
|
||||
size_t length()
|
||||
{
|
||||
// get UTF8-String real length
|
||||
size_t len = 0;
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
if ((*it & 0xC0) != 0x80)
|
||||
{
|
||||
++len;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace Fig
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<Fig::FString>
|
||||
{
|
||||
std::size_t operator()(const Fig::FString &s) const
|
||||
{
|
||||
return std::hash<std::u8string>{}(static_cast<const std::u8string &>(s));
|
||||
}
|
||||
};
|
||||
}
|
||||
260
src/Core/utf8_iterator.hpp
Normal file
260
src/Core/utf8_iterator.hpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include <corecrt.h>
|
||||
#include <string>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <cwctype>
|
||||
// fuckyou C++
|
||||
// i don't know how to deal with unicode string in cpp
|
||||
// fuck
|
||||
// generate by Qwen3-Coder:
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class UTF8Char
|
||||
{
|
||||
private:
|
||||
std::u8string char_data_;
|
||||
|
||||
public:
|
||||
UTF8Char(const std::u8string &data) :
|
||||
char_data_(data) {}
|
||||
|
||||
// 获取UTF-8字符的字节长度
|
||||
static size_t getUTF8CharLength(char8_t first_byte)
|
||||
{
|
||||
if ((first_byte & 0x80) == 0x00) return 1;
|
||||
if ((first_byte & 0xE0) == 0xC0) return 2;
|
||||
if ((first_byte & 0xF0) == 0xE0) return 3;
|
||||
if ((first_byte & 0xF8) == 0xF0) return 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 转换为Unicode码点
|
||||
char32_t toCodePoint() const
|
||||
{
|
||||
if (char_data_.empty()) return 0;
|
||||
|
||||
size_t len = getUTF8CharLength(char_data_[0]);
|
||||
if (len > char_data_.length()) return 0;
|
||||
|
||||
char32_t code_point = 0;
|
||||
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
code_point = char_data_[0];
|
||||
break;
|
||||
case 2:
|
||||
code_point = ((char_data_[0] & 0x1F) << 6) | (char_data_[1] & 0x3F);
|
||||
break;
|
||||
case 3:
|
||||
code_point = ((char_data_[0] & 0x0F) << 12) | ((char_data_[1] & 0x3F) << 6) | (char_data_[2] & 0x3F);
|
||||
break;
|
||||
case 4:
|
||||
code_point = ((char_data_[0] & 0x07) << 18) | ((char_data_[1] & 0x3F) << 12) | ((char_data_[2] & 0x3F) << 6) | (char_data_[3] & 0x3F);
|
||||
break;
|
||||
}
|
||||
return code_point;
|
||||
}
|
||||
|
||||
inline bool operator==(char32_t ch)
|
||||
{
|
||||
return this->toCodePoint() == ch;
|
||||
}
|
||||
// 字符分类函数
|
||||
bool isAlpha() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswalpha(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isDigit() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswdigit(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isAlnum() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswalnum(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isSpace() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswspace(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isUpper() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswupper(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isLower() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswlower(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
bool isPunct() const
|
||||
{
|
||||
char32_t cp = toCodePoint();
|
||||
return std::iswpunct(static_cast<wint_t>(cp));
|
||||
}
|
||||
|
||||
// 获取底层数据
|
||||
const std::u8string &getString() const { return char_data_; }
|
||||
|
||||
// 获取字符长度(字节数)
|
||||
size_t length() const { return char_data_.length(); }
|
||||
|
||||
// 是否为空
|
||||
bool empty() const { return char_data_.empty(); }
|
||||
};
|
||||
|
||||
class UTF8Iterator
|
||||
{
|
||||
private:
|
||||
const std::u8string *str_;
|
||||
size_t pos_;
|
||||
|
||||
// 获取UTF-8字符的字节长度
|
||||
static size_t getUTF8CharLength(char8_t first_byte)
|
||||
{
|
||||
if ((first_byte & 0x80) == 0x00) return 1;
|
||||
if ((first_byte & 0xE0) == 0xC0) return 2;
|
||||
if ((first_byte & 0xF0) == 0xE0) return 3;
|
||||
if ((first_byte & 0xF8) == 0xF0) return 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 获取下一个字符的起始位置
|
||||
size_t getNextCharPos(size_t current_pos) const
|
||||
{
|
||||
if (current_pos >= str_->length()) return current_pos;
|
||||
size_t char_len = getUTF8CharLength((*str_)[current_pos]);
|
||||
return current_pos + char_len;
|
||||
}
|
||||
|
||||
// 获取前一个字符的起始位置
|
||||
size_t getPrevCharPos(size_t current_pos) const
|
||||
{
|
||||
if (current_pos == 0) return 0;
|
||||
|
||||
size_t pos = current_pos - 1;
|
||||
while (pos > 0 && (str_->at(pos) & 0xC0) == 0x80)
|
||||
{
|
||||
--pos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
public:
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
using value_type = UTF8Char;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const UTF8Char *;
|
||||
using reference = const UTF8Char &;
|
||||
|
||||
// 构造函数
|
||||
UTF8Iterator(const std::u8string &str, size_t pos = 0) :
|
||||
str_(&str), pos_(pos)
|
||||
{
|
||||
if (pos_ > str_->length()) pos_ = str_->length();
|
||||
}
|
||||
|
||||
// 前置递增
|
||||
UTF8Iterator &operator++()
|
||||
{
|
||||
pos_ = getNextCharPos(pos_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 后置递增
|
||||
UTF8Iterator operator++(int)
|
||||
{
|
||||
UTF8Iterator temp = *this;
|
||||
pos_ = getNextCharPos(pos_);
|
||||
return temp;
|
||||
}
|
||||
|
||||
// 前置递减
|
||||
UTF8Iterator &operator--()
|
||||
{
|
||||
pos_ = getPrevCharPos(pos_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 后置递减
|
||||
UTF8Iterator operator--(int)
|
||||
{
|
||||
UTF8Iterator temp = *this;
|
||||
pos_ = getPrevCharPos(pos_);
|
||||
return temp;
|
||||
}
|
||||
|
||||
// 解引用操作符 - 返回当前字符
|
||||
UTF8Char operator*() const
|
||||
{
|
||||
if (pos_ >= str_->length())
|
||||
{
|
||||
return UTF8Char(std::u8string());
|
||||
}
|
||||
size_t char_len = getUTF8CharLength((*str_)[pos_]);
|
||||
size_t end_pos = pos_ + char_len;
|
||||
if (end_pos > str_->length())
|
||||
{
|
||||
end_pos = str_->length();
|
||||
}
|
||||
return UTF8Char(str_->substr(pos_, end_pos - pos_));
|
||||
}
|
||||
UTF8Char peek() const
|
||||
{
|
||||
if (pos_ >= str_->length())
|
||||
{
|
||||
return UTF8Char(std::u8string());
|
||||
}
|
||||
|
||||
size_t next_pos = getNextCharPos(pos_);
|
||||
if (next_pos >= str_->length())
|
||||
{
|
||||
return UTF8Char(std::u8string());
|
||||
}
|
||||
|
||||
size_t char_len = getUTF8CharLength((*str_)[next_pos]);
|
||||
size_t end_pos = next_pos + char_len;
|
||||
if (end_pos > str_->length())
|
||||
{
|
||||
end_pos = str_->length();
|
||||
}
|
||||
|
||||
return UTF8Char(str_->substr(next_pos, end_pos - next_pos));
|
||||
}
|
||||
|
||||
// 窥探前一个字符
|
||||
UTF8Char peekPrev() const
|
||||
{
|
||||
if (pos_ == 0)
|
||||
{
|
||||
return UTF8Char(std::u8string());
|
||||
}
|
||||
|
||||
size_t prev_pos = getPrevCharPos(pos_);
|
||||
size_t char_len = getUTF8CharLength((*str_)[prev_pos]);
|
||||
size_t end_pos = prev_pos + char_len;
|
||||
if (end_pos > str_->length())
|
||||
{
|
||||
end_pos = str_->length();
|
||||
}
|
||||
|
||||
return UTF8Char(str_->substr(prev_pos, end_pos - prev_pos));
|
||||
}
|
||||
// 获取当前位置
|
||||
size_t position() const { return pos_; }
|
||||
size_t column() const { return pos_ + 1; }
|
||||
// 检查是否到达末尾
|
||||
bool isEnd() const { return pos_ >= str_->length(); }
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <warning.hpp>
|
||||
#include <Core/warning.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
55
src/Core/warning.hpp
Normal file
55
src/Core/warning.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <Utils/magic_enum/magic_enum.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Warning
|
||||
{
|
||||
private:
|
||||
size_t id; // the id (standard) of warning
|
||||
FString msg;
|
||||
size_t line, column;
|
||||
public:
|
||||
static const std::unordered_map<size_t, FString> standardWarnings;
|
||||
Warning(size_t _id, FString _msg)
|
||||
{
|
||||
id = _id;
|
||||
msg = std::move(_msg);
|
||||
}
|
||||
Warning(size_t _id, FString _msg, size_t _line, size_t _column)
|
||||
{
|
||||
id = _id;
|
||||
msg = std::move(_msg);
|
||||
line = _line;
|
||||
column = _column;
|
||||
}
|
||||
|
||||
auto getIDName()
|
||||
{
|
||||
return standardWarnings.at(id);
|
||||
}
|
||||
|
||||
auto getID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
auto getMsg()
|
||||
{
|
||||
return msg;
|
||||
}
|
||||
auto getLine()
|
||||
{
|
||||
return line;
|
||||
}
|
||||
auto getColumn()
|
||||
{
|
||||
return column;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
128
src/Error/error.hpp
Normal file
128
src/Error/error.hpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class AddressableError : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit AddressableError() {}
|
||||
explicit AddressableError(FStringView _msg,
|
||||
size_t _line,
|
||||
size_t _column,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
src_loc(loc), line(_line), column(_column)
|
||||
{
|
||||
message = _msg;
|
||||
}
|
||||
virtual FString toString() const
|
||||
{
|
||||
std::string msg = std::format("[AddressableError] {} at {}:{}, in [{}] {}", std::string(this->message.begin(), this->message.end()), this->line, this->column, this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
}
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
static std::string msg = toString().toBasicString();
|
||||
return msg.c_str();
|
||||
}
|
||||
std::source_location src_loc;
|
||||
|
||||
size_t getLine() const { return line; }
|
||||
size_t getColumn() const { return column; }
|
||||
FStringView getMessage() const { return message; }
|
||||
|
||||
virtual FString getErrorType() const
|
||||
{
|
||||
return FString(u8"AddressableError");
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t line, column;
|
||||
FStringView message;
|
||||
};
|
||||
|
||||
class UnaddressableError : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit UnaddressableError() {}
|
||||
explicit UnaddressableError(FStringView _msg,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
src_loc(loc)
|
||||
{
|
||||
message = _msg;
|
||||
}
|
||||
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());
|
||||
return FString(msg);
|
||||
}
|
||||
const char *what() const noexcept override
|
||||
{
|
||||
static std::string msg = toString().toBasicString();
|
||||
return msg.c_str();
|
||||
}
|
||||
std::source_location src_loc;
|
||||
FStringView getMessage() const { return message; }
|
||||
|
||||
virtual FString getErrorType() const
|
||||
{
|
||||
return FString(u8"UnaddressableError");
|
||||
}
|
||||
|
||||
protected:
|
||||
FStringView message;
|
||||
};
|
||||
|
||||
class SyntaxError : public AddressableError
|
||||
{
|
||||
public:
|
||||
using AddressableError::AddressableError;
|
||||
|
||||
explicit SyntaxError(FStringView _msg,
|
||||
size_t _line,
|
||||
size_t _column,
|
||||
std::source_location loc = std::source_location::current()) :
|
||||
AddressableError(_msg, _line, _column, loc)
|
||||
{
|
||||
}
|
||||
|
||||
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());
|
||||
return FString(msg);
|
||||
}
|
||||
|
||||
virtual FString getErrorType() const override
|
||||
{
|
||||
return FString(u8"SyntaxError");
|
||||
}
|
||||
};
|
||||
|
||||
class RuntimeError final : public UnaddressableError
|
||||
{
|
||||
public:
|
||||
using UnaddressableError::UnaddressableError;
|
||||
explicit RuntimeError(FStringView _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());
|
||||
return FString(msg);
|
||||
}
|
||||
|
||||
virtual FString getErrorType() const override
|
||||
{
|
||||
return FString(u8"RuntimeError");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Fig
|
||||
140
src/Error/errorLog.hpp
Normal file
140
src/Error/errorLog.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <Error/error.hpp>
|
||||
#include <Core/core.hpp>
|
||||
|
||||
#include <print>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
namespace ErrorLog
|
||||
{
|
||||
namespace TerminalColors
|
||||
{
|
||||
constexpr const char *Reset = "\033[0m";
|
||||
constexpr const char *Bold = "\033[1m";
|
||||
constexpr const char *Dim = "\033[2m";
|
||||
constexpr const char *Italic = "\033[3m";
|
||||
constexpr const char *Underline = "\033[4m";
|
||||
constexpr const char *Blink = "\033[5m";
|
||||
constexpr const char *Reverse = "\033[7m"; // 前背景反色
|
||||
constexpr const char *Hidden = "\033[8m"; // 隐藏文本
|
||||
constexpr const char *Strike = "\033[9m"; // 删除线
|
||||
|
||||
constexpr const char *Black = "\033[30m";
|
||||
constexpr const char *Red = "\033[31m";
|
||||
constexpr const char *Green = "\033[32m";
|
||||
constexpr const char *Yellow = "\033[33m";
|
||||
constexpr const char *Blue = "\033[34m";
|
||||
constexpr const char *Magenta = "\033[35m";
|
||||
constexpr const char *Cyan = "\033[36m";
|
||||
constexpr const char *White = "\033[37m";
|
||||
|
||||
constexpr const char *LightBlack = "\033[90m";
|
||||
constexpr const char *LightRed = "\033[91m";
|
||||
constexpr const char *LightGreen = "\033[92m";
|
||||
constexpr const char *LightYellow = "\033[93m";
|
||||
constexpr const char *LightBlue = "\033[94m";
|
||||
constexpr const char *LightMagenta = "\033[95m";
|
||||
constexpr const char *LightCyan = "\033[96m";
|
||||
constexpr const char *LightWhite = "\033[97m";
|
||||
|
||||
constexpr const char *DarkRed = "\033[38;2;128;0;0m";
|
||||
constexpr const char *DarkGreen = "\033[38;2;0;100;0m";
|
||||
constexpr const char *DarkYellow = "\033[38;2;128;128;0m";
|
||||
constexpr const char *DarkBlue = "\033[38;2;0;0;128m";
|
||||
constexpr const char *DarkMagenta = "\033[38;2;100;0;100m";
|
||||
constexpr const char *DarkCyan = "\033[38;2;0;128;128m";
|
||||
constexpr const char *DarkGray = "\033[38;2;64;64;64m";
|
||||
constexpr const char *Gray = "\033[38;2;128;128;128m";
|
||||
constexpr const char *Silver = "\033[38;2;192;192;192m";
|
||||
|
||||
constexpr const char *Navy = "\033[38;2;0;0;128m";
|
||||
constexpr const char *RoyalBlue = "\033[38;2;65;105;225m";
|
||||
constexpr const char *ForestGreen = "\033[38;2;34;139;34m";
|
||||
constexpr const char *Olive = "\033[38;2;128;128;0m";
|
||||
constexpr const char *Teal = "\033[38;2;0;128;128m";
|
||||
constexpr const char *Maroon = "\033[38;2;128;0;0m";
|
||||
constexpr const char *Purple = "\033[38;2;128;0;128m";
|
||||
constexpr const char *Orange = "\033[38;2;255;165;0m";
|
||||
constexpr const char *Gold = "\033[38;2;255;215;0m";
|
||||
constexpr const char *Pink = "\033[38;2;255;192;203m";
|
||||
constexpr const char *Crimson = "\033[38;2;220;20;60m";
|
||||
|
||||
constexpr const char *OnBlack = "\033[40m";
|
||||
constexpr const char *OnRed = "\033[41m";
|
||||
constexpr const char *OnGreen = "\033[42m";
|
||||
constexpr const char *OnYellow = "\033[43m";
|
||||
constexpr const char *OnBlue = "\033[44m";
|
||||
constexpr const char *OnMagenta = "\033[45m";
|
||||
constexpr const char *OnCyan = "\033[46m";
|
||||
constexpr const char *OnWhite = "\033[47m";
|
||||
|
||||
constexpr const char *OnLightBlack = "\033[100m";
|
||||
constexpr const char *OnLightRed = "\033[101m";
|
||||
constexpr const char *OnLightGreen = "\033[102m";
|
||||
constexpr const char *OnLightYellow = "\033[103m";
|
||||
constexpr const char *OnLightBlue = "\033[104m";
|
||||
constexpr const char *OnLightMagenta = "\033[105m";
|
||||
constexpr const char *OnLightCyan = "\033[106m";
|
||||
constexpr const char *OnLightWhite = "\033[107m";
|
||||
|
||||
constexpr const char *OnDarkBlue = "\033[48;2;0;0;128m";
|
||||
constexpr const char *OnGreenYellow = "\033[48;2;173;255;47m";
|
||||
constexpr const char *OnOrange = "\033[48;2;255;165;0m";
|
||||
constexpr const char *OnGray = "\033[48;2;128;128;128m";
|
||||
}; // namespace TerminalColors
|
||||
|
||||
inline void coloredPrint(const char *colorCode, FString msg)
|
||||
{
|
||||
std::print("{}{}{}", colorCode, msg.toBasicString(), TerminalColors::Reset);
|
||||
}
|
||||
|
||||
inline void coloredPrint(const char *colorCode, std::string msg)
|
||||
{
|
||||
std::print("{}{}{}", colorCode, msg, TerminalColors::Reset);
|
||||
}
|
||||
|
||||
|
||||
inline void logAddressableError(const AddressableError &err, FString fileName, std::vector<FString> sourceLines)
|
||||
{
|
||||
std::print("\n");
|
||||
namespace TC = TerminalColors;
|
||||
coloredPrint(TC::LightWhite, "An error occurred! ");
|
||||
coloredPrint(TC::White, std::format("Fig {} ({})[{} {} bit on `{}`]\n",Core::VERSION, Core::COMPILE_TIME, Core::COMPILER, Core::ARCH, Core::PLATFORM));
|
||||
coloredPrint(TC::LightRed, "✖ ");
|
||||
coloredPrint(TC::LightRed, std::format("{}: {}\n", err.getErrorType().toBasicString(), FString(err.getMessage()).toBasicString()));
|
||||
coloredPrint(TC::White, std::format(" at {}:{} in file '{}'\n", err.getLine(), err.getColumn(), fileName.toBasicString()));
|
||||
FString lineContent = ((int64_t(err.getLine()) - int64_t(1)) >= 0 ? sourceLines[err.getLine() - 1] : u8"<No Source>");
|
||||
coloredPrint(TC::LightBlue, std::format(" {}\n", lineContent.toBasicString()));
|
||||
FString pointerLine;
|
||||
for (size_t i = 1; i < err.getColumn(); ++i)
|
||||
{
|
||||
if (lineContent[i - 1] == U'\t')
|
||||
{
|
||||
pointerLine += U'\t';
|
||||
}
|
||||
else
|
||||
{
|
||||
pointerLine += U' ';
|
||||
}
|
||||
}
|
||||
pointerLine += U'^';
|
||||
coloredPrint(TC::LightGreen, std::format(" {}\n", pointerLine.toBasicString()));
|
||||
coloredPrint(TC::DarkGray, std::format("🔧 in function '{}' ({}:{})\n", err.src_loc.function_name(), err.src_loc.file_name(), err.src_loc.line()));
|
||||
}
|
||||
|
||||
inline void logUnaddressableError(const UnaddressableError &err)
|
||||
{
|
||||
std::print("\n");
|
||||
namespace TC = TerminalColors;
|
||||
coloredPrint(TC::LightWhite, "An error occurred! ");
|
||||
coloredPrint(TC::White, std::format("Fig {} ({})[{} {} bit on `{}`]\n", Core::VERSION, Core::COMPILE_TIME, Core::COMPILER, Core::ARCH, Core::PLATFORM));
|
||||
coloredPrint(TC::DarkRed, "✖");
|
||||
coloredPrint(TC::Red, std::format("{}: {}\n", err.getErrorType().toBasicString(), FString(err.getMessage()).toBasicString()));
|
||||
coloredPrint(TC::DarkGray, std::format("🔧 in function '{}' ({}:{})", err.src_loc.function_name(), err.src_loc.file_name(), err.src_loc.line()));
|
||||
}
|
||||
}; // namespace ErrorLog
|
||||
}; // namespace Fig
|
||||
1029
src/Evaluator/evaluator.cpp
Normal file
1029
src/Evaluator/evaluator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
116
src/Evaluator/evaluator.hpp
Normal file
116
src/Evaluator/evaluator.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <Ast/ast.hpp>
|
||||
|
||||
#include <Context/context.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Module/builtins.hpp>
|
||||
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct StatementResult
|
||||
{
|
||||
ObjectPtr result;
|
||||
enum class Flow
|
||||
{
|
||||
Normal,
|
||||
Return,
|
||||
Break,
|
||||
Continue
|
||||
} flow;
|
||||
|
||||
StatementResult(ObjectPtr val, Flow f = Flow::Normal) :
|
||||
result(val), flow(f)
|
||||
{
|
||||
}
|
||||
|
||||
static StatementResult normal(ObjectPtr val = Object::getNullInstance())
|
||||
{
|
||||
return StatementResult(val, Flow::Normal);
|
||||
}
|
||||
static StatementResult returnFlow(ObjectPtr val)
|
||||
{
|
||||
return StatementResult(val, Flow::Return);
|
||||
}
|
||||
static StatementResult breakFlow()
|
||||
{
|
||||
return StatementResult(Object::getNullInstance(), Flow::Break);
|
||||
}
|
||||
static StatementResult continueFlow()
|
||||
{
|
||||
return StatementResult(Object::getNullInstance(), Flow::Continue);
|
||||
}
|
||||
|
||||
bool isNormal() const { return flow == Flow::Normal; }
|
||||
bool shouldReturn() const { return flow == Flow::Return; }
|
||||
bool shouldBreak() const { return flow == Flow::Break; }
|
||||
bool shouldContinue() const { return flow == Flow::Continue; }
|
||||
};
|
||||
|
||||
class Evaluator
|
||||
{
|
||||
private:
|
||||
ContextPtr global;
|
||||
public:
|
||||
|
||||
void SetGlobalContext(ContextPtr ctx)
|
||||
{
|
||||
assert(ctx != nullptr);
|
||||
global = ctx;
|
||||
}
|
||||
|
||||
void CreateGlobalContext()
|
||||
{
|
||||
global = std::make_shared<Context>(
|
||||
FString(u8"<Global>"));
|
||||
}
|
||||
|
||||
void RegisterBuiltins()
|
||||
{
|
||||
assert(global != nullptr);
|
||||
|
||||
for (auto &[name, fn] : Builtins::builtinFunctions)
|
||||
{
|
||||
int argc = Builtins::getBuiltinFunctionParamCount(name);
|
||||
Function f(fn, argc);
|
||||
global->def(
|
||||
name,
|
||||
ValueType::Function,
|
||||
AccessModifier::Const,
|
||||
std::make_shared<Object>(f)
|
||||
);
|
||||
}
|
||||
|
||||
for (auto &[name, val] : Builtins::builtinValues)
|
||||
{
|
||||
global->def(
|
||||
name,
|
||||
val->getTypeInfo(),
|
||||
AccessModifier::Const,
|
||||
val
|
||||
);
|
||||
}
|
||||
}
|
||||
/* Left-value eval*/
|
||||
LvObject evalVarExpr(Ast::VarExpr, ContextPtr);
|
||||
LvObject evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b
|
||||
LvObject evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b]
|
||||
|
||||
LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b]
|
||||
|
||||
/* Right-value eval*/
|
||||
|
||||
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
|
||||
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
|
||||
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
|
||||
|
||||
RvObject evalFunctionCall(const Function&, const Ast::FunctionArguments&, const FString& ,ContextPtr); // function call
|
||||
RvObject eval(Ast::Expression, ContextPtr);
|
||||
|
||||
StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block
|
||||
StatementResult evalStatement(Ast::Statement, ContextPtr); // statement
|
||||
|
||||
StatementResult Run(std::vector<Ast::AstBase>); // Entry
|
||||
|
||||
void printStackTrace();
|
||||
};
|
||||
}; // namespace Fig
|
||||
42
src/Evaluator/evaluator_error.hpp
Normal file
42
src/Evaluator/evaluator_error.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <Error/error.hpp>
|
||||
#include <Ast/astBase.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class EvaluatorError final : public AddressableError
|
||||
{
|
||||
public:
|
||||
FString typeName;
|
||||
using AddressableError::AddressableError;
|
||||
EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
message = FStringView::fromBasicStringView(msg.toBasicString());
|
||||
line = ast->getAAI().line;
|
||||
column = ast->getAAI().column;
|
||||
|
||||
src_loc = std::move(loc);
|
||||
|
||||
typeName = std::move(_typeName);
|
||||
|
||||
}
|
||||
EvaluatorError(FString _typeName, std::string_view msg, Ast::AstBase ast, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
message = FStringView::fromBasicStringView(msg);
|
||||
line = ast->getAAI().line;
|
||||
column = ast->getAAI().column;
|
||||
|
||||
src_loc = std::move(loc);
|
||||
|
||||
typeName = std::move(_typeName);
|
||||
}
|
||||
|
||||
virtual FString getErrorType() const override
|
||||
{
|
||||
return typeName;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <fig_string.hpp>
|
||||
#include <error.hpp>
|
||||
#include <token.hpp>
|
||||
#include <lexer.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Token/token.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
|
||||
#include <fig_string.hpp>
|
||||
#include <utils.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Utils/utils.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -66,7 +66,7 @@ namespace Fig
|
||||
{FString(u8"func"), TokenType::Function},
|
||||
{FString(u8"var"), TokenType::Variable},
|
||||
{FString(u8"const"), TokenType::Const},
|
||||
{FString(u8"final"), TokenType::Final},
|
||||
// {FString(u8"final"), TokenType::Final},
|
||||
{FString(u8"while"), TokenType::While},
|
||||
{FString(u8"for"), TokenType::For},
|
||||
{FString(u8"if"), TokenType::If},
|
||||
94
src/Lexer/lexer.hpp
Normal file
94
src/Lexer/lexer.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <corecrt.h>
|
||||
#include <cuchar>
|
||||
#include <cwctype>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <Token/token.hpp>
|
||||
#include <Error/error.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Core/utf8_iterator.hpp>
|
||||
#include <Core/warning.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class Lexer final
|
||||
{
|
||||
private:
|
||||
size_t line;
|
||||
const FString source;
|
||||
SyntaxError error;
|
||||
UTF8Iterator it;
|
||||
|
||||
std::vector<Warning> warnings;
|
||||
|
||||
size_t last_line, last_column, column = 1;
|
||||
|
||||
bool hasNext()
|
||||
{
|
||||
return !this->it.isEnd();
|
||||
}
|
||||
|
||||
void skipLine();
|
||||
inline void next()
|
||||
{
|
||||
if (*it == U'\n')
|
||||
{
|
||||
++this->line;
|
||||
this->column = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
++this->column;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
void pushWarning(size_t id, FString msg)
|
||||
{
|
||||
warnings.push_back(Warning(id, std::move(msg), getCurrentLine(), getCurrentColumn()));
|
||||
}
|
||||
void pushWarning(size_t id, FString msg, size_t line, size_t column)
|
||||
{
|
||||
warnings.push_back(Warning(id, std::move(msg), line, column));
|
||||
}
|
||||
|
||||
public:
|
||||
static const std::unordered_map<FString, TokenType> symbol_map;
|
||||
static const std::unordered_map<FString, TokenType> keyword_map;
|
||||
|
||||
inline Lexer(const FString &_source) :
|
||||
source(_source), it(source)
|
||||
{
|
||||
line = 1;
|
||||
}
|
||||
inline size_t getCurrentLine()
|
||||
{
|
||||
return line;
|
||||
}
|
||||
inline size_t getCurrentColumn()
|
||||
{
|
||||
return column;
|
||||
}
|
||||
SyntaxError getError() const
|
||||
{
|
||||
return error;
|
||||
}
|
||||
std::vector<Warning> getWarnings() const
|
||||
{
|
||||
return warnings;
|
||||
}
|
||||
Token nextToken();
|
||||
|
||||
Token scanNumber();
|
||||
Token scanString();
|
||||
Token scanRawString();
|
||||
Token scanMultilineString();
|
||||
Token scanIdentifier();
|
||||
Token scanSymbol();
|
||||
Token scanComments();
|
||||
};
|
||||
} // namespace Fig
|
||||
152
src/Module/builtins.hpp
Normal file
152
src/Module/builtins.hpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <print>
|
||||
#include <iostream>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
namespace Builtins
|
||||
{
|
||||
const std::unordered_map<FString, ObjectPtr> builtinValues = {
|
||||
{u8"null", Object::getNullInstance()},
|
||||
{u8"true", Object::getTrueInstance()},
|
||||
{u8"false", Object::getFalseInstance()},
|
||||
};
|
||||
|
||||
using BuiltinFunction = std::function<ObjectPtr(const std::vector<ObjectPtr> &)>;
|
||||
|
||||
const std::unordered_map<FString, int> builtinFunctionArgCounts = {
|
||||
{u8"__fstdout_print", -1}, // variadic
|
||||
{u8"__fstdout_println", -1}, // variadic
|
||||
{u8"__fstdin_read", 0},
|
||||
{u8"__fstdin_readln", 0},
|
||||
{u8"__fvalue_type", 1},
|
||||
{u8"__fvalue_int_parse", 1},
|
||||
{u8"__fvalue_int_from", 1},
|
||||
{u8"__fvalue_double_parse", 1},
|
||||
{u8"__fvalue_double_from", 1},
|
||||
{u8"__fvalue_string_from", 1},
|
||||
};
|
||||
|
||||
const std::unordered_map<FString, BuiltinFunction> builtinFunctions{
|
||||
{u8"__fstdout_print", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args)
|
||||
{
|
||||
std::print("{}", arg->toString().toBasicString());
|
||||
}
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{u8"__fstdout_println", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
for (auto arg : args)
|
||||
{
|
||||
std::print("{}", arg->toString().toBasicString());
|
||||
}
|
||||
std::print("\n");
|
||||
return std::make_shared<Object>(ValueType::IntClass(args.size()));
|
||||
}},
|
||||
{u8"__fstdin_read", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
return std::make_shared<Object>(FString::fromBasicString(input));
|
||||
}},
|
||||
{u8"__fstdin_readln", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
return std::make_shared<Object>(FString::fromBasicString(line));
|
||||
}},
|
||||
{u8"__fvalue_type", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
return std::make_shared<Object>(args[0]->getTypeInfo().toString());
|
||||
}},
|
||||
{u8"__fvalue_int_parse", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
FString str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
{
|
||||
ValueType::IntClass val = std::stoi(str.toBasicString());
|
||||
return std::make_shared<Object>(val);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Invalid int string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_int_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::DoubleClass>())
|
||||
{
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(val->as<ValueType::DoubleClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(static_cast<ValueType::IntClass>(val->as<ValueType::BoolClass>() ? 1 : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to int", val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_double_parse", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
FString str = args[0]->as<ValueType::StringClass>();
|
||||
try
|
||||
{
|
||||
ValueType::DoubleClass val = std::stod(str.toBasicString());
|
||||
return std::make_shared<Object>(ValueType::DoubleClass(val));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Invalid double string for parsing", str.toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_double_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
if (val->is<ValueType::IntClass>())
|
||||
{
|
||||
return std::make_shared<Object>(static_cast<ValueType::DoubleClass>(val->as<ValueType::IntClass>()));
|
||||
}
|
||||
else if (val->is<ValueType::BoolClass>())
|
||||
{
|
||||
return std::make_shared<Object>(ValueType::DoubleClass(val->as<ValueType::BoolClass>() ? 1.0 : 0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Type '{}' cannot be converted to double", val->getTypeInfo().toString().toBasicString())));
|
||||
}
|
||||
}},
|
||||
{u8"__fvalue_string_from", [](const std::vector<ObjectPtr> &args) -> ObjectPtr {
|
||||
ObjectPtr val = args[0];
|
||||
return std::make_shared<Object>(val->toString());
|
||||
}},
|
||||
|
||||
};
|
||||
|
||||
inline bool isBuiltinFunction(const FString &name)
|
||||
{
|
||||
return builtinFunctions.find(name) != builtinFunctions.end();
|
||||
}
|
||||
|
||||
inline BuiltinFunction getBuiltinFunction(const FString &name)
|
||||
{
|
||||
auto it = builtinFunctions.find(name);
|
||||
if (it == builtinFunctions.end())
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
inline int getBuiltinFunctionParamCount(const FString &name)
|
||||
{
|
||||
auto it = builtinFunctionArgCounts.find(name);
|
||||
if (it == builtinFunctionArgCounts.end())
|
||||
{
|
||||
throw RuntimeError(FStringView(std::format("Builtin function '{}' not found", name.toBasicString())));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
}; // namespace Builtins
|
||||
}; // namespace Fig
|
||||
47
src/Module/module.hpp
Normal file
47
src/Module/module.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Context/context.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
// class Module
|
||||
// {
|
||||
// public:
|
||||
// const FString name;
|
||||
// const FString spec;
|
||||
// const FString path;
|
||||
|
||||
// std::shared_ptr<Context> context; // module-level context
|
||||
|
||||
// /*
|
||||
|
||||
// import module -> automatically create a module context and call function `init` if exists
|
||||
// all global functions, variables, structs, etc will be stored in module context
|
||||
// then module context will be linked to the current context
|
||||
|
||||
// */
|
||||
// Module(const FString &moduleName, const FString &moduleSpec, const FString &modulePath) :
|
||||
// name(moduleName), spec(moduleSpec), path(modulePath)
|
||||
// {
|
||||
// context = std::make_shared<Context>(FString(std::format("<Module {}>", name.toBasicString())), nullptr);
|
||||
// }
|
||||
|
||||
// bool hasSymbol(const FString &symbolName)
|
||||
// {
|
||||
// return context->contains(symbolName);
|
||||
// }
|
||||
// Object getSymbol(const FString &symbolName)
|
||||
// {
|
||||
// auto valOpt = context->get(symbolName);
|
||||
// if (!valOpt.has_value())
|
||||
// {
|
||||
// throw RuntimeError(FStringView(std::format("Symbol '{}' not found in module '{}'", symbolName.toBasicString(), name.toBasicString())));
|
||||
// }
|
||||
// return valOpt.value();
|
||||
// }
|
||||
// };
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <parser.hpp>
|
||||
#include <Parser/parser.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
@@ -39,8 +39,8 @@ namespace Fig
|
||||
// 海象运算符
|
||||
// {Ast::Operator::Walrus, {2, 1}}, // 右结合
|
||||
|
||||
// 点运算符
|
||||
{Ast::Operator::Dot, {40, 41}},
|
||||
// // 点运算符
|
||||
// {Ast::Operator::Dot, {40, 41}},
|
||||
};
|
||||
|
||||
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
||||
@@ -244,13 +244,6 @@ namespace Fig
|
||||
next();
|
||||
am = (isPublic ? AccessModifier::Public : AccessModifier::Normal);
|
||||
}
|
||||
else if (isThis(TokenType::Final))
|
||||
{
|
||||
next();
|
||||
expect(TokenType::Identifier, u8"field name");
|
||||
fieldName = currentToken().getValue();
|
||||
am = (isPublic ? AccessModifier::PublicFinal : AccessModifier::Final);
|
||||
}
|
||||
else if (isThis(TokenType::Const))
|
||||
{
|
||||
next();
|
||||
@@ -297,7 +290,7 @@ namespace Fig
|
||||
}
|
||||
else if (isThis(TokenType::Public))
|
||||
{
|
||||
if (isNext(TokenType::Const) or isNext(TokenType::Final))
|
||||
if (isNext(TokenType::Const))
|
||||
{
|
||||
next();
|
||||
fields.push_back(__parseStructField(true));
|
||||
@@ -334,7 +327,7 @@ namespace Fig
|
||||
next(); // consume `struct`
|
||||
stmts.push_back(__parseStructDef(false));
|
||||
}
|
||||
else if (isThis(TokenType::Const) or isThis(TokenType::Final))
|
||||
else if (isThis(TokenType::Const))
|
||||
{
|
||||
fields.push_back(__parseStructField(false));
|
||||
}
|
||||
@@ -865,12 +858,40 @@ namespace Fig
|
||||
tok = currentToken();
|
||||
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
|
||||
|
||||
/* Postfix */
|
||||
|
||||
if (tok.getType() == TokenType::LeftParen)
|
||||
{
|
||||
lhs = __parseCall(lhs);
|
||||
continue;
|
||||
}
|
||||
|
||||
// member access: a.b
|
||||
if (tok.getType() == TokenType::Dot)
|
||||
{
|
||||
next(); // consume '.'
|
||||
Token idTok = currentToken();
|
||||
if (!idTok.isIdentifier())
|
||||
throwAddressableError<SyntaxError>(FStringView(u8"Expected identifier after '.'"));
|
||||
|
||||
FString member = idTok.getValue();
|
||||
next(); // consume identifier
|
||||
|
||||
lhs = makeAst<Ast::MemberExprAst>(lhs, member);
|
||||
continue;
|
||||
}
|
||||
// index: x[expr]
|
||||
if (tok.getType() == TokenType::LeftBracket)
|
||||
{
|
||||
next(); // consume '['
|
||||
auto indexExpr = parseExpression(0, TokenType::RightBracket);
|
||||
expect(TokenType::RightBracket);
|
||||
next(); // consume ']'
|
||||
|
||||
lhs = makeAst<Ast::IndexExprAst>(lhs, indexExpr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ternary
|
||||
if (tok.getType() == TokenType::Question)
|
||||
{
|
||||
333
src/Parser/parser.hpp
Normal file
333
src/Parser/parser.hpp
Normal file
@@ -0,0 +1,333 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/ast.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Error/error.hpp>
|
||||
|
||||
#include <print>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class Parser
|
||||
{
|
||||
private:
|
||||
Lexer lexer;
|
||||
std::vector<Ast::AstBase> output;
|
||||
std::vector<Token> previousTokens;
|
||||
|
||||
size_t tokenPruduced = 0;
|
||||
size_t currentTokenIndex = 0;
|
||||
|
||||
std::unique_ptr<AddressableError> error;
|
||||
|
||||
Ast::AstAddressInfo currentAAI;
|
||||
|
||||
std::stack<Ast::Expression> exprStack;
|
||||
|
||||
bool needSemicolon = true;
|
||||
|
||||
class SemicolonDisabler
|
||||
{
|
||||
Parser *p;
|
||||
bool original;
|
||||
|
||||
public:
|
||||
SemicolonDisabler(Parser *parser) :
|
||||
p(parser), original(p->needSemicolon)
|
||||
{
|
||||
p->needSemicolon = false;
|
||||
}
|
||||
~SemicolonDisabler()
|
||||
{
|
||||
p->needSemicolon = original;
|
||||
}
|
||||
// disable copy and assign
|
||||
SemicolonDisabler(const SemicolonDisabler &) = delete;
|
||||
SemicolonDisabler &operator=(const SemicolonDisabler &) = delete;
|
||||
};
|
||||
|
||||
void pushNode(const Ast::AstBase &_node)
|
||||
{
|
||||
Ast::AstBase node = _node;
|
||||
node->setAAI(currentAAI);
|
||||
output.push_back(std::move(node));
|
||||
}
|
||||
void pushNode(const Ast::AstBase &_node, Ast::AstAddressInfo _aai)
|
||||
{
|
||||
Ast::AstBase node = _node;
|
||||
node->setAAI(_aai);
|
||||
output.push_back(node);
|
||||
}
|
||||
|
||||
bool isTokenSymbol(Token tok)
|
||||
{
|
||||
return Lexer::symbol_map.contains(tok.getValue());
|
||||
}
|
||||
bool isTokenOp(Token tok)
|
||||
{
|
||||
return Ast::TokenToOp.contains(tok.getType());
|
||||
}
|
||||
bool isEOF()
|
||||
{
|
||||
if (tokenPruduced == 0) return false;
|
||||
return currentToken() == EOFTok;
|
||||
}
|
||||
|
||||
public:
|
||||
using Precedence = uint32_t;
|
||||
static const std::unordered_map<Ast::Operator, std::pair<Precedence, Precedence>> opPrecedence;
|
||||
Parser(const Lexer &_lexer) :
|
||||
lexer(_lexer)
|
||||
{
|
||||
}
|
||||
|
||||
AddressableError* getError() const
|
||||
{
|
||||
return error.get();
|
||||
}
|
||||
|
||||
template <class _ErrT, typename = AddressableError>
|
||||
void throwAddressableError(FStringView 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");
|
||||
_ErrT spError(msg, line, column, loc);
|
||||
error = std::make_unique<_ErrT>(spError);
|
||||
throw spError;
|
||||
}
|
||||
template <class _ErrT, typename = AddressableError>
|
||||
void throwAddressableError(FStringView msg, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
static_assert(std::is_base_of_v<AddressableError, _ErrT>,
|
||||
"_ErrT must derive from AddressableError");
|
||||
// line, column provide by `currentAAI`
|
||||
_ErrT spError(msg, currentAAI.line, currentAAI.column, loc);
|
||||
error = std::make_unique<_ErrT>(spError);
|
||||
throw spError;
|
||||
}
|
||||
|
||||
template <class _ErrT, typename = UnaddressableError>
|
||||
void throwUnaddressableError(FStringView msg, std::source_location loc = std::source_location::current())
|
||||
{
|
||||
static_assert(std::is_base_of_v<AddressableError, _ErrT>,
|
||||
"_ErrT must derive from AddressableError");
|
||||
_ErrT spError(msg, loc);
|
||||
error = std::make_unique<_ErrT>(spError);
|
||||
throw spError;
|
||||
}
|
||||
|
||||
void setCurrentAAI(Ast::AstAddressInfo _aai)
|
||||
{
|
||||
currentAAI = std::move(_aai);
|
||||
}
|
||||
|
||||
Ast::AstAddressInfo getCurrentAAI() const
|
||||
{
|
||||
return currentAAI;
|
||||
}
|
||||
|
||||
inline const Token &nextToken()
|
||||
{
|
||||
// 没有Rollback时, 存在 currentTokenIndex = tokenPruduced - 1
|
||||
next();
|
||||
return currentToken();
|
||||
}
|
||||
inline void rollback()
|
||||
{
|
||||
if (int64_t(currentTokenIndex - 1) < int64_t(0))
|
||||
// 同下 next注释
|
||||
{
|
||||
throw std::runtime_error("Internal Error in Parser::rollbackToken, trying to rollback but it's already on the begin");
|
||||
}
|
||||
currentTokenIndex--;
|
||||
}
|
||||
inline void next()
|
||||
{
|
||||
if (int64_t(currentTokenIndex) < (int64_t(tokenPruduced) - 1))
|
||||
{
|
||||
/*
|
||||
必须两个都显示转换为int64_t.否则,负数时会超出范围,变成int64_t max, 并且 CTI也需要显示转换,否则转换完的pruduced又会被转回去,变为 int64_t max
|
||||
*/
|
||||
currentTokenIndex++;
|
||||
setCurrentAAI(Ast::AstAddressInfo{.line = currentToken().line, .column = currentToken().column});
|
||||
return;
|
||||
}
|
||||
if (isEOF()) return;
|
||||
const Token &tok = lexer.nextToken();
|
||||
tokenPruduced++;
|
||||
if (tok == IllegalTok) throw lexer.getError();
|
||||
currentTokenIndex = tokenPruduced - 1;
|
||||
setCurrentAAI(Ast::AstAddressInfo{.line = tok.line, .column = tok.column});
|
||||
previousTokens.push_back(tok);
|
||||
}
|
||||
inline const Token ¤tToken()
|
||||
{
|
||||
if (tokenPruduced == 0) return nextToken();
|
||||
return previousTokens.at(currentTokenIndex);
|
||||
}
|
||||
inline Token rollbackToken()
|
||||
{
|
||||
rollback();
|
||||
return previousTokens.at(currentTokenIndex);
|
||||
}
|
||||
|
||||
inline Token peekToken()
|
||||
{
|
||||
Token tok = nextToken();
|
||||
rollback();
|
||||
return tok;
|
||||
}
|
||||
|
||||
std::pair<Precedence, Precedence> getBindingPower(Ast::Operator op)
|
||||
{
|
||||
return opPrecedence.at(op);
|
||||
}
|
||||
Precedence getLeftBindingPower(Ast::Operator op)
|
||||
{
|
||||
return getBindingPower(op).first;
|
||||
}
|
||||
Precedence getRightBindingPower(Ast::Operator op)
|
||||
{
|
||||
return getBindingPower(op).second;
|
||||
}
|
||||
|
||||
// template <class _Tp, class... Args>
|
||||
// std::shared_ptr<_Tp> makeAst(Args &&...args)
|
||||
// {
|
||||
// _Tp node(std::forward<Args>(args)...);
|
||||
// node.setAAI(currentAAI);
|
||||
// return std::shared_ptr<_Tp>(new _Tp(node));
|
||||
// }
|
||||
template <class _Tp, class... Args>
|
||||
std::shared_ptr<_Tp> makeAst(Args &&...args)
|
||||
{
|
||||
std::shared_ptr<_Tp> ptr = std::make_shared<_Tp>(std::forward<Args>(args)...);
|
||||
ptr->setAAI(currentAAI);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void expectPeek(TokenType type)
|
||||
{
|
||||
if (peekToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||
magic_enum::enum_name(type),
|
||||
magic_enum::enum_name(peekToken().getType()))));
|
||||
}
|
||||
}
|
||||
|
||||
void expect(TokenType type)
|
||||
{
|
||||
if (currentToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||
magic_enum::enum_name(type),
|
||||
magic_enum::enum_name(currentToken().getType()))));
|
||||
}
|
||||
}
|
||||
|
||||
void expectPeek(TokenType type, FString expected)
|
||||
{
|
||||
if (peekToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||
expected.toBasicString(),
|
||||
magic_enum::enum_name(peekToken().getType()))));
|
||||
}
|
||||
}
|
||||
|
||||
void expect(TokenType type, FString expected)
|
||||
{
|
||||
if (currentToken().getType() != type)
|
||||
{
|
||||
throwAddressableError<SyntaxError>(FStringView(std::format("Expected `{}`, but got `{}`",
|
||||
expected.toBasicString(),
|
||||
magic_enum::enum_name(currentToken().getType()))));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] SemicolonDisabler disableSemicolon()
|
||||
{
|
||||
return SemicolonDisabler(this);
|
||||
}
|
||||
|
||||
void expectSemicolon()
|
||||
{
|
||||
// if need semicolon and stream has `;`, consume it. if not need semicolon, do nothing
|
||||
|
||||
if (!needSemicolon)
|
||||
{
|
||||
// disabled semicolon check
|
||||
if (currentToken().getType() == TokenType::Semicolon)
|
||||
{
|
||||
next(); // consume `;`
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// normal semicolon check
|
||||
expectConsume(TokenType::Semicolon);
|
||||
}
|
||||
|
||||
void expectConsume(TokenType type, FString expected)
|
||||
{
|
||||
expect(type, expected);
|
||||
next();
|
||||
}
|
||||
|
||||
void expectConsume(TokenType type)
|
||||
{
|
||||
expect(type);
|
||||
next();
|
||||
}
|
||||
|
||||
bool isNext(TokenType type)
|
||||
{
|
||||
return peekToken().getType() == type;
|
||||
}
|
||||
bool isThis(TokenType type)
|
||||
{
|
||||
return currentToken().getType() == type;
|
||||
}
|
||||
|
||||
static constexpr FString varDefTypeFollowed = u8"(Followed)";
|
||||
|
||||
Ast::VarDef __parseVarDef(bool); // entry: current is keyword `var` or `const` (isConst: Bool)
|
||||
ObjectPtr __parseValue();
|
||||
Ast::ValueExpr __parseValueExpr();
|
||||
Ast::FunctionParameters __parseFunctionParameters(); // entry: current is Token::LeftParen
|
||||
Ast::BlockStatement __parseBlockStatement(); // entry: current is Token::LeftBrace
|
||||
Ast::If __parseIf(); // entry: current is Token::If
|
||||
Ast::While __parseWhile(); // entry: current is Token::While
|
||||
Ast::Statement __parseIncrementStatement(); // only allowed in __parseFor function
|
||||
Ast::For __parseFor(); // entry: current is Token::For
|
||||
Ast::Return __parseReturn(); // entry: current is Token::Return
|
||||
Ast::Break __parseBreak(); // entry: current is Token::Break
|
||||
Ast::Continue __parseContinue(); // entry: current is Token::Continue
|
||||
|
||||
Ast::VarExpr __parseVarExpr(FString);
|
||||
Ast::FunctionDef __parseFunctionDef(bool); // entry: current is Token::Identifier (isPublic: Bool)
|
||||
Ast::StructDef __parseStructDef(bool); // entry: current is Token::Identifier (struct name) arg(isPublic: bool)
|
||||
|
||||
Ast::BinaryExpr __parseInfix(Ast::Expression, Ast::Operator, Precedence);
|
||||
Ast::UnaryExpr __parsePrefix(Ast::Operator, Precedence);
|
||||
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 `(`
|
||||
|
||||
Ast::FunctionLiteralExpr __parseFunctionLiteralExpr(); // entry: current is Token::LParen after Token::Function
|
||||
|
||||
Ast::Statement __parseStatement(); // entry: (idk)
|
||||
Ast::Expression parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
|
||||
std::vector<Ast::AstBase> parseAll();
|
||||
};
|
||||
}; // namespace Fig
|
||||
173
src/Token/token.hpp
Normal file
173
src/Token/token.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <Utils/magic_enum/magic_enum.hpp>
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
enum class TokenType : int8_t
|
||||
{
|
||||
Illegal = -1,
|
||||
EndOfFile = 0,
|
||||
|
||||
Comments,
|
||||
|
||||
Identifier,
|
||||
|
||||
/* Keywords */
|
||||
And, // and
|
||||
Or, // or
|
||||
Not, // not
|
||||
Import, // import
|
||||
Function, // func
|
||||
Variable, // var
|
||||
Const, // const
|
||||
// Final, // final
|
||||
While, // while
|
||||
For, // for
|
||||
If, // if
|
||||
Else, // else
|
||||
Struct, // struct
|
||||
Interface, // interface
|
||||
Implement, // implement
|
||||
Public, // public
|
||||
Return, // return
|
||||
Break, // break
|
||||
Continue, // continue
|
||||
|
||||
// TypeNull, // Null
|
||||
// TypeInt, // Int
|
||||
// TypeString, // String
|
||||
// TypeBool, // Bool
|
||||
// TypeDouble, // Double
|
||||
|
||||
/* Literal Types (not keyword)*/
|
||||
LiteralNumber, // number (int,float...)
|
||||
LiteralString, // FString
|
||||
LiteralBool, // bool (true/false)
|
||||
LiteralNull, // null (Null的唯一实例)
|
||||
|
||||
/* Punct */
|
||||
Plus, // +
|
||||
Minus, // -
|
||||
Asterisk, // *
|
||||
Slash, // /
|
||||
Percent, // %
|
||||
Caret, // ^
|
||||
Ampersand, // &
|
||||
Pipe, // |
|
||||
Tilde, // ~
|
||||
ShiftLeft, // <<
|
||||
ShiftRight, // >>
|
||||
// Exclamation, // !
|
||||
Question, // ?
|
||||
Assign, // =
|
||||
Less, // <
|
||||
Greater, // >
|
||||
Dot, // .
|
||||
Comma, // ,
|
||||
Colon, // :
|
||||
Semicolon, // ;
|
||||
SingleQuote, // '
|
||||
DoubleQuote, // "
|
||||
// Backtick, // `
|
||||
// At, // @
|
||||
// Hash, // #
|
||||
// Dollar, // $
|
||||
// Backslash, // '\'
|
||||
// Underscore, // _
|
||||
LeftParen, // (
|
||||
RightParen, // )
|
||||
LeftBracket, // [
|
||||
RightBracket, // ]
|
||||
LeftBrace, // {
|
||||
RightBrace, // }
|
||||
// LeftArrow, // <-
|
||||
RightArrow, // ->
|
||||
DoubleArrow, // =>
|
||||
Equal, // ==
|
||||
NotEqual, // !=
|
||||
LessEqual, // <=
|
||||
GreaterEqual, // >=
|
||||
PlusEqual, // +=
|
||||
MinusEqual, // -=
|
||||
AsteriskEqual, // *=
|
||||
SlashEqual, // /=
|
||||
PercentEqual, // %=
|
||||
CaretEqual, // ^=
|
||||
DoublePlus, // ++
|
||||
DoubleMinus, // --
|
||||
DoubleAmpersand, // &&
|
||||
DoublePipe, // ||
|
||||
Walrus, // :=
|
||||
Power, // **
|
||||
};
|
||||
|
||||
class Token final
|
||||
{
|
||||
friend bool operator==(const Token &l, const Token &r);
|
||||
|
||||
private:
|
||||
FString value;
|
||||
TokenType type;
|
||||
|
||||
public:
|
||||
size_t line, column;
|
||||
|
||||
inline Token() {};
|
||||
inline Token(const FString &_value, TokenType _type) :
|
||||
value(_value), type(_type) {}
|
||||
inline Token(const FString &_value, TokenType _type, size_t _line, size_t _column) :
|
||||
value(_value), type(_type)
|
||||
{
|
||||
line = _line;
|
||||
column = _column;
|
||||
}
|
||||
const Token& setPos(size_t _line, size_t _column)
|
||||
{
|
||||
line = _line;
|
||||
column = _column;
|
||||
return *this;
|
||||
}
|
||||
size_t getLength()
|
||||
{
|
||||
return value.length();
|
||||
}
|
||||
const FString& getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
inline FString toString() const
|
||||
{
|
||||
return FString(std::format(
|
||||
"Token('{}',{})",
|
||||
this->value.toBasicString(),
|
||||
magic_enum::enum_name(type)));
|
||||
}
|
||||
|
||||
bool isIdentifier()
|
||||
{
|
||||
return type == TokenType::Identifier;
|
||||
}
|
||||
bool isLiteral()
|
||||
{
|
||||
return type == TokenType::LiteralNull || type == TokenType::LiteralBool || type == TokenType::LiteralNumber || type == TokenType::LiteralString;
|
||||
}
|
||||
|
||||
TokenType getType() const
|
||||
{
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const Token &l, const Token &r)
|
||||
{
|
||||
return l.type == r.type and l.value == r.value;
|
||||
}
|
||||
|
||||
static Token IllegalTok(u8"ILLEGAL", TokenType::Illegal);
|
||||
static Token EOFTok(u8"EOF", TokenType::EndOfFile);
|
||||
} // namespace Fig
|
||||
187
src/Utils/AstPrinter.hpp
Normal file
187
src/Utils/AstPrinter.hpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <Ast/ast.hpp>
|
||||
#include <Utils/magic_enum/magic_enum.hpp>
|
||||
|
||||
using namespace Fig;
|
||||
using namespace Fig::Ast;
|
||||
class AstPrinter
|
||||
{
|
||||
public:
|
||||
void print(const AstBase &node, int indent = 0)
|
||||
{
|
||||
if (!node) return;
|
||||
switch (node->getType())
|
||||
{
|
||||
case AstType::BinaryExpr:
|
||||
printBinaryExpr(std::static_pointer_cast<BinaryExprAst>(node), indent);
|
||||
break;
|
||||
case AstType::UnaryExpr:
|
||||
printUnaryExpr(std::static_pointer_cast<UnaryExprAst>(node), indent);
|
||||
break;
|
||||
case AstType::ValueExpr:
|
||||
printValueExpr(std::static_pointer_cast<ValueExprAst>(node), indent);
|
||||
break;
|
||||
case AstType::VarDefSt:
|
||||
printVarDef(std::static_pointer_cast<VarDefAst>(node), indent);
|
||||
break;
|
||||
case AstType::VarExpr:
|
||||
printVarExpr(std::static_pointer_cast<VarExprAst>(node), indent);
|
||||
break;
|
||||
case AstType::BlockStatement:
|
||||
printBlockStatement(std::static_pointer_cast<BlockStatementAst>(node), indent);
|
||||
break;
|
||||
case AstType::FunctionCall:
|
||||
printFunctionCall(std::static_pointer_cast<FunctionCallExpr>(node), indent);
|
||||
break;
|
||||
case AstType::FunctionDefSt:
|
||||
printFunctionSt(std::static_pointer_cast<FunctionDefSt>(node), indent);
|
||||
break;
|
||||
case AstType::IfSt:
|
||||
printIfSt(std::static_pointer_cast<IfSt>(node), indent);
|
||||
break;
|
||||
case AstType::TernaryExpr:
|
||||
printTernaryExpr(std::static_pointer_cast<TernaryExprAst>(node), indent);
|
||||
break;
|
||||
default:
|
||||
printIndent(indent);
|
||||
std::cout << "Unknown AST Node\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void printIndent(int indent)
|
||||
{
|
||||
std::cout << std::string(indent, ' ');
|
||||
}
|
||||
|
||||
void printFString(const Fig::FString &fstr, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "FString: \"";
|
||||
std::cout.write(reinterpret_cast<const char *>(fstr.data()), fstr.size());
|
||||
std::cout << "\"\n";
|
||||
}
|
||||
|
||||
template <typename EnumT>
|
||||
void printEnum(const EnumT &value, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "Enum: " << magic_enum::enum_name(value) << "\n";
|
||||
}
|
||||
|
||||
void printBinaryExpr(const std::shared_ptr<BinaryExprAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "BinaryExpr\n";
|
||||
printEnum(node->op, indent + 2);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Left:\n";
|
||||
print(node->lexp, indent + 4);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Right:\n";
|
||||
print(node->rexp, indent + 4);
|
||||
}
|
||||
|
||||
void printUnaryExpr(const std::shared_ptr<UnaryExprAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "UnaryExpr\n";
|
||||
printEnum(node->op, indent + 2);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Expr:\n";
|
||||
print(node->exp, indent + 4);
|
||||
}
|
||||
|
||||
void printValueExpr(const std::shared_ptr<ValueExprAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "ValueExpr\n";
|
||||
printFString(node->val->toString(), indent + 2);
|
||||
}
|
||||
|
||||
void printVarDef(const std::shared_ptr<VarDefAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "VarDef\n";
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Name: ";
|
||||
printFString(node->name, 0);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Type: ";
|
||||
printFString(node->typeName, 0);
|
||||
if (node->expr)
|
||||
{
|
||||
printIndent(indent + 2);
|
||||
std::cout << "InitExpr:\n";
|
||||
print(node->expr, indent + 4);
|
||||
}
|
||||
}
|
||||
|
||||
void printVarExpr(const std::shared_ptr<VarExprAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "VarExpr\n";
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Name: ";
|
||||
printFString(node->name, 0);
|
||||
}
|
||||
|
||||
void printBlockStatement(const std::shared_ptr<BlockStatementAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "BlockStatement\n";
|
||||
for (const auto &stmt : node->stmts)
|
||||
{
|
||||
print(stmt, indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
void printFunctionCall(const std::shared_ptr<FunctionCallExpr> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "FunctionCall\n";
|
||||
printIndent(indent + 2);
|
||||
printIndent(indent + 2);
|
||||
}
|
||||
|
||||
void printFunctionSt(const std::shared_ptr<FunctionDefSt> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "FunctionSt\n";
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Name: ";
|
||||
printFString(node->name, 0);
|
||||
printIndent(indent + 2);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Body:\n";
|
||||
print(node->body, indent + 4);
|
||||
}
|
||||
|
||||
void printIfSt(const std::shared_ptr<IfSt> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "IfSt\n";
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Condition:\n";
|
||||
print(node->condition, indent + 4);
|
||||
printIndent(indent + 2);
|
||||
}
|
||||
|
||||
void printTernaryExpr(const std::shared_ptr<TernaryExprAst> &node, int indent)
|
||||
{
|
||||
printIndent(indent);
|
||||
std::cout << "TernaryExpr\n";
|
||||
printIndent(indent + 2);
|
||||
std::cout << "Condition:\n";
|
||||
print(node->condition, indent + 4);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "TrueExpr:\n";
|
||||
print(node->valueT, indent + 4);
|
||||
printIndent(indent + 2);
|
||||
std::cout << "FalseExpr:\n";
|
||||
print(node->valueF, indent + 4);
|
||||
}
|
||||
};
|
||||
2589
src/Utils/argparse/argparse.hpp
Normal file
2589
src/Utils/argparse/argparse.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1508
src/Utils/magic_enum/magic_enum.hpp
Normal file
1508
src/Utils/magic_enum/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load Diff
44
src/Utils/magic_enum/magic_enum_all.hpp
Normal file
44
src/Utils/magic_enum/magic_enum_all.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_containers.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
#include "magic_enum_format.hpp"
|
||||
#include "magic_enum_fuse.hpp"
|
||||
#include "magic_enum_iostream.hpp"
|
||||
#include "magic_enum_switch.hpp"
|
||||
#include "magic_enum_utility.hpp"
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_ALL_HPP
|
||||
1174
src/Utils/magic_enum/magic_enum_containers.hpp
Normal file
1174
src/Utils/magic_enum/magic_enum_containers.hpp
Normal file
File diff suppressed because it is too large
Load Diff
222
src/Utils/magic_enum/magic_enum_flags.hpp
Normal file
222
src/Utils/magic_enum/magic_enum_flags.hpp
Normal file
@@ -0,0 +1,222 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(push)
|
||||
#endif
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E, enum_subtype S, typename U = std::underlying_type_t<E>>
|
||||
constexpr U values_ors() noexcept {
|
||||
static_assert(S == enum_subtype::flags, "magic_enum::detail::values_ors requires valid subtype.");
|
||||
|
||||
auto ors = U{0};
|
||||
for (std::size_t i = 0; i < count_v<E, S>; ++i) {
|
||||
ors |= static_cast<U>(values_v<E, S>[i]);
|
||||
}
|
||||
|
||||
return ors;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
// Returns name from enum-flags value.
|
||||
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||
template <typename E>
|
||||
[[nodiscard]] auto enum_flags_name(E value, char_type sep = static_cast<char_type>('|')) -> detail::enable_if_t<E, string> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
string name;
|
||||
auto check_value = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (static_cast<U>(value) & v) != 0) {
|
||||
if (const auto n = detail::names_v<D, S>[i]; !n.empty()) {
|
||||
check_value |= v;
|
||||
if (!name.empty()) {
|
||||
name.append(1, sep);
|
||||
}
|
||||
name.append(n.data(), n.size());
|
||||
} else {
|
||||
return {}; // Value out of range.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == static_cast<U>(value)) {
|
||||
return name;
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
// Obtains enum-flags value from integer value.
|
||||
// Returns optional with enum-flags value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
if constexpr (detail::count_v<D, S> == 0) {
|
||||
static_cast<void>(value);
|
||||
return {}; // Empty enum.
|
||||
} else {
|
||||
if constexpr (detail::is_sparse_v<D, S>) {
|
||||
auto check_value = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||
if (const auto v = static_cast<U>(enum_value<D, S>(i)); (value & v) != 0) {
|
||||
check_value |= v;
|
||||
}
|
||||
}
|
||||
|
||||
if (check_value != 0 && check_value == value) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
} else {
|
||||
constexpr auto min = detail::min_v<D, S>;
|
||||
constexpr auto max = detail::values_ors<D, S>();
|
||||
|
||||
if (value >= min && value <= max) {
|
||||
return static_cast<D>(value);
|
||||
}
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
}
|
||||
|
||||
// Obtains enum-flags value from name.
|
||||
// Returns optional with enum-flags value.
|
||||
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||
[[nodiscard]] constexpr auto enum_flags_cast(string_view value, [[maybe_unused]] BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
if constexpr (detail::count_v<D, S> == 0) {
|
||||
static_cast<void>(value);
|
||||
return {}; // Empty enum.
|
||||
} else {
|
||||
auto result = U{0};
|
||||
while (!value.empty()) {
|
||||
const auto d = detail::find(value, '|');
|
||||
const auto s = (d == string_view::npos) ? value : value.substr(0, d);
|
||||
auto f = U{0};
|
||||
for (std::size_t i = 0; i < detail::count_v<D, S>; ++i) {
|
||||
if (detail::cmp_equal(s, detail::names_v<D, S>[i], p)) {
|
||||
f = static_cast<U>(enum_value<D, S>(i));
|
||||
result |= f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (f == U{0}) {
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
|
||||
}
|
||||
|
||||
if (result != U{0}) {
|
||||
return static_cast<D>(result);
|
||||
}
|
||||
return {}; // Invalid value or out of range.
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether enum-flags contains value with such value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D>(static_cast<U>(value)));
|
||||
}
|
||||
|
||||
// Checks whether enum-flags contains value with such integer value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D>(value));
|
||||
}
|
||||
|
||||
// Checks whether enum-flags contains enumerator with such name.
|
||||
template <typename E, typename BinaryPredicate = std::equal_to<>>
|
||||
[[nodiscard]] constexpr auto enum_flags_contains(string_view value, BinaryPredicate p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
return static_cast<bool>(enum_flags_cast<D>(value, std::move(p)));
|
||||
}
|
||||
|
||||
// Checks whether `flags set` contains `flag`.
|
||||
// Note: If `flag` equals 0, it returns false, as 0 is not a flag.
|
||||
template <typename E>
|
||||
constexpr auto enum_flags_test(E flags, E flag) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using U = underlying_type_t<E>;
|
||||
|
||||
return static_cast<U>(flag) && ((static_cast<U>(flags) & static_cast<U>(flag)) == static_cast<U>(flag));
|
||||
}
|
||||
|
||||
// Checks whether `lhs flags set` and `rhs flags set` have common flags.
|
||||
// Note: If `lhs flags set` or `rhs flags set` equals 0, it returns false, as 0 is not a flag, and therfore cannot have any matching flag.
|
||||
template <typename E>
|
||||
constexpr auto enum_flags_test_any(E lhs, E rhs) noexcept -> detail::enable_if_t<E, bool> {
|
||||
using U = underlying_type_t<E>;
|
||||
|
||||
return (static_cast<U>(lhs) & static_cast<U>(rhs)) != 0;
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FLAGS_HPP
|
||||
114
src/Utils/magic_enum/magic_enum_format.hpp
Normal file
114
src/Utils/magic_enum/magic_enum_format.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
|
||||
#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT)
|
||||
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT 1
|
||||
# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||
#endif
|
||||
|
||||
namespace magic_enum::customize {
|
||||
// customize enum to enable/disable automatic std::format
|
||||
template <typename E>
|
||||
constexpr bool enum_format_enabled() noexcept {
|
||||
return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT;
|
||||
}
|
||||
} // magic_enum::customize
|
||||
|
||||
#if defined(__cpp_lib_format)
|
||||
|
||||
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||
#include <format>
|
||||
#endif
|
||||
|
||||
template <typename E>
|
||||
struct std::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> {
|
||||
template <class FormatContext>
|
||||
auto format(E e, FormatContext& ctx) const {
|
||||
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||
}
|
||||
} else {
|
||||
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(FMT_VERSION)
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
template <typename E>
|
||||
struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : fmt::formatter<std::string_view> {
|
||||
template <class FormatContext>
|
||||
auto format(E e, FormatContext& ctx) const {
|
||||
static_assert(std::is_same_v<char, string_view::value_type>, "formatter requires string_view::value_type type same as char.");
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
if constexpr (magic_enum::detail::supported<D>::value) {
|
||||
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
|
||||
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
|
||||
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||
}
|
||||
} else {
|
||||
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
|
||||
return formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return formatter<std::string_view, char>::format(std::to_string(magic_enum::enum_integer<D>(e)), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE)
|
||||
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT
|
||||
# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE
|
||||
#endif
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP
|
||||
89
src/Utils/magic_enum/magic_enum_fuse.hpp
Normal file
89
src/Utils/magic_enum/magic_enum_fuse.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E>
|
||||
constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
|
||||
if (hash) {
|
||||
if (const auto index = enum_index(value)) {
|
||||
return (*hash << log2((enum_count<E>() << 1) - 1)) | *index;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
|
||||
return fuse_one_enum(0, value);
|
||||
}
|
||||
|
||||
template <typename E, typename... Es>
|
||||
constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
|
||||
return fuse_one_enum(fuse_enum(tail...), head);
|
||||
}
|
||||
|
||||
template <typename... Es>
|
||||
constexpr auto typesafe_fuse_enum(Es... values) noexcept {
|
||||
enum class enum_fuse_t : std::uintmax_t;
|
||||
const auto fuse = fuse_enum(values...);
|
||||
if (fuse) {
|
||||
return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
|
||||
}
|
||||
return optional<enum_fuse_t>{};
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
|
||||
template <typename... Es>
|
||||
[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
|
||||
static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
|
||||
static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
|
||||
static_assert((detail::log2(enum_count<std::decay_t<Es>>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
|
||||
#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
|
||||
const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
|
||||
#else
|
||||
const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
|
||||
#endif
|
||||
return MAGIC_ENUM_ASSERT(fuse), fuse;
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP
|
||||
117
src/Utils/magic_enum/magic_enum_iostream.hpp
Normal file
117
src/Utils/magic_enum/magic_enum_iostream.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
#include "magic_enum_flags.hpp"
|
||||
|
||||
#ifndef MAGIC_ENUM_USE_STD_MODULE
|
||||
#include <iosfwd>
|
||||
#endif
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace ostream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
|
||||
if constexpr (detail::supported<D>::value) {
|
||||
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||
if (const auto name = enum_flags_name<D>(value); !name.empty()) {
|
||||
for (const auto c : name) {
|
||||
os.put(c);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
} else {
|
||||
if (const auto name = enum_name<D>(value); !name.empty()) {
|
||||
for (const auto c : name) {
|
||||
os.put(c);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (os << static_cast<U>(value));
|
||||
}
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
|
||||
return value ? (os << *value) : os;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::ostream_operators
|
||||
|
||||
namespace istream_operators {
|
||||
|
||||
template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
|
||||
std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
std::basic_string<Char, Traits> s;
|
||||
is >> s;
|
||||
if constexpr (detail::supported<D>::value) {
|
||||
if constexpr (detail::subtype_v<D> == detail::enum_subtype::flags) {
|
||||
if (const auto v = enum_flags_cast<D>(s)) {
|
||||
value = *v;
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
} else {
|
||||
if (const auto v = enum_cast<D>(s)) {
|
||||
value = *v;
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
is.setstate(std::basic_ios<Char>::failbit);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
} // namespace magic_enum::istream_operators
|
||||
|
||||
namespace iostream_operators {
|
||||
|
||||
using magic_enum::ostream_operators::operator<<;
|
||||
using magic_enum::istream_operators::operator>>;
|
||||
|
||||
} // namespace magic_enum::iostream_operators
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_IOSTREAM_HPP
|
||||
195
src/Utils/magic_enum/magic_enum_switch.hpp
Normal file
195
src/Utils/magic_enum/magic_enum_switch.hpp
Normal file
@@ -0,0 +1,195 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct default_result_type {};
|
||||
|
||||
template <typename T>
|
||||
struct identity {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
struct nonesuch {};
|
||||
|
||||
template <typename F, typename V, bool = std::is_invocable_v<F, V>>
|
||||
struct invoke_result : identity<nonesuch> {};
|
||||
|
||||
template <typename F, typename V>
|
||||
struct invoke_result<F, V, true> : std::invoke_result<F, V> {};
|
||||
|
||||
template <typename F, typename V>
|
||||
using invoke_result_t = typename invoke_result<F, V>::type;
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||
constexpr auto common_invocable(std::index_sequence<I...>) noexcept {
|
||||
static_assert(std::is_enum_v<E>, "magic_enum::detail::invocable_index requires enum type.");
|
||||
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return identity<nonesuch>{};
|
||||
} else {
|
||||
return std::common_type<invoke_result_t<F, enum_constant<values_v<E, S>[I]>>...>{};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename Result, typename F>
|
||||
constexpr auto result_type() noexcept {
|
||||
static_assert(std::is_enum_v<E>, "magic_enum::detail::result_type requires enum type.");
|
||||
|
||||
constexpr auto seq = std::make_index_sequence<count_v<E, S>>{};
|
||||
using R = typename decltype(common_invocable<E, S, F>(seq))::type;
|
||||
if constexpr (std::is_same_v<Result, default_result_type>) {
|
||||
if constexpr (std::is_same_v<R, nonesuch>) {
|
||||
return identity<void>{};
|
||||
} else {
|
||||
return identity<R>{};
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_convertible_v<R, Result>) {
|
||||
return identity<Result>{};
|
||||
} else if constexpr (std::is_convertible_v<Result, R>) {
|
||||
return identity<R>{};
|
||||
} else {
|
||||
return identity<nonesuch>{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename Result, typename F, typename D = std::decay_t<E>, typename R = typename decltype(result_type<D, S, Result, F>())::type>
|
||||
using result_t = std::enable_if_t<std::is_enum_v<D> && !std::is_same_v<R, nonesuch>, R>;
|
||||
|
||||
#if !defined(MAGIC_ENUM_ENABLE_HASH) && !defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
|
||||
template <typename T = void>
|
||||
inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
|
||||
|
||||
template <>
|
||||
inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
|
||||
|
||||
template <std::size_t I, std::size_t End, typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||
constexpr decltype(auto) constexpr_switch_impl(F&& f, E value, Def&& def) {
|
||||
if constexpr(I < End) {
|
||||
constexpr auto v = enum_constant<enum_value<E, I, S>()>{};
|
||||
if (value == v) {
|
||||
if constexpr (std::is_invocable_r_v<R, F, decltype(v)>) {
|
||||
return static_cast<R>(std::forward<F>(f)(v));
|
||||
} else {
|
||||
return def();
|
||||
}
|
||||
} else {
|
||||
return constexpr_switch_impl<I + 1, End, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||
}
|
||||
} else {
|
||||
return def();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename R, typename E, enum_subtype S, typename F, typename Def>
|
||||
constexpr decltype(auto) constexpr_switch(F&& f, E value, Def&& def) {
|
||||
static_assert(is_enum_v<E>, "magic_enum::detail::constexpr_switch requires enum type.");
|
||||
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return def();
|
||||
} else {
|
||||
return constexpr_switch_impl<0, count_v<E, S>, R, E, S>(std::forward<F>(f), value, std::forward<Def>(def));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
template <typename Result = detail::default_result_type, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
detail::default_result_type_lambda<R>);
|
||||
#else
|
||||
return detail::constexpr_switch<R, D, S>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
detail::default_result_type_lambda<R>);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Result = detail::default_result_type, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value) {
|
||||
return enum_switch<Result, E, S>(std::forward<F>(f), value);
|
||||
}
|
||||
|
||||
template <typename Result, typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_switch requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
|
||||
#if defined(MAGIC_ENUM_ENABLE_HASH) || defined(MAGIC_ENUM_ENABLE_HASH_SWITCH)
|
||||
return detail::constexpr_switch<&detail::values_v<D, S>, detail::case_call_t::value>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
[&result]() -> R { return std::forward<Result>(result); });
|
||||
#else
|
||||
return detail::constexpr_switch<R, D, S>(
|
||||
std::forward<F>(f),
|
||||
value,
|
||||
[&result]() -> R { return std::forward<Result>(result); });
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Result, detail::enum_subtype S, typename E, typename F, typename R = detail::result_t<E, S, Result, F>>
|
||||
constexpr decltype(auto) enum_switch(F&& f, E value, Result&& result) {
|
||||
return enum_switch<Result, E, S>(std::forward<F>(f), value, std::forward<Result>(result));
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
template <>
|
||||
struct std::common_type<magic_enum::detail::nonesuch, magic_enum::detail::nonesuch> : magic_enum::detail::identity<magic_enum::detail::nonesuch> {};
|
||||
|
||||
template <typename T>
|
||||
struct std::common_type<T, magic_enum::detail::nonesuch> : magic_enum::detail::identity<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct std::common_type<magic_enum::detail::nonesuch, T> : magic_enum::detail::identity<T> {};
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_SWITCH_HPP
|
||||
138
src/Utils/magic_enum/magic_enum_utility.hpp
Normal file
138
src/Utils/magic_enum/magic_enum_utility.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// __ __ _ ______ _____
|
||||
// | \/ | (_) | ____| / ____|_ _
|
||||
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
|
||||
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
|
||||
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|
||||
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
|
||||
// __/ | https://github.com/Neargye/magic_enum
|
||||
// |___/ version 0.9.7
|
||||
//
|
||||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
#define NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace magic_enum {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E, enum_subtype S, typename F, std::size_t... I>
|
||||
constexpr auto for_each(F&& f, std::index_sequence<I...>) {
|
||||
constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> || ...);
|
||||
constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<F, enum_constant<values_v<E, S>[0]>>, std::invoke_result_t<F, enum_constant<values_v<E, S>[I]>>> && ...);
|
||||
|
||||
if constexpr (has_void_return) {
|
||||
(f(enum_constant<values_v<E, S>[I]>{}), ...);
|
||||
} else if constexpr (all_same_return) {
|
||||
return std::array{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||
} else {
|
||||
return std::tuple{f(enum_constant<values_v<E, S>[I]>{})...};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, enum_subtype S, typename F,std::size_t... I>
|
||||
constexpr bool all_invocable(std::index_sequence<I...>) {
|
||||
if constexpr (count_v<E, S> == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return (std::is_invocable_v<F, enum_constant<values_v<E, S>[I]>> && ...);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace magic_enum::detail
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>, typename F, detail::enable_if_t<E, int> = 0>
|
||||
constexpr auto enum_for_each(F&& f) {
|
||||
using D = std::decay_t<E>;
|
||||
static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type.");
|
||||
static_assert(detail::is_reflected_v<D, S>, "magic_enum requires enum implementation and valid max and min.");
|
||||
constexpr auto sep = std::make_index_sequence<detail::count_v<D, S>>{};
|
||||
|
||||
if constexpr (detail::all_invocable<D, S, F>(sep)) {
|
||||
return detail::for_each<D, S>(std::forward<F>(f), sep);
|
||||
} else {
|
||||
static_assert(detail::always_false_v<D>, "magic_enum::enum_for_each requires invocable of all enum value.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_next_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) + n);
|
||||
if (index >= 0 && index < count) {
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_next_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) + n) % count) + count) % count);
|
||||
if (index >= 0 && index < count) {
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
}
|
||||
return MAGIC_ENUM_ASSERT(false), value;
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_prev_value(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const std::ptrdiff_t index = (static_cast<std::ptrdiff_t>(*i) - n);
|
||||
if (index >= 0 && index < count) {
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template <typename E, detail::enum_subtype S = detail::subtype_v<E>>
|
||||
[[nodiscard]] constexpr auto enum_prev_value_circular(E value, std::ptrdiff_t n = 1) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
|
||||
using D = std::decay_t<E>;
|
||||
constexpr std::ptrdiff_t count = detail::count_v<D, S>;
|
||||
|
||||
if (const auto i = enum_index<D, S>(value)) {
|
||||
const std::ptrdiff_t index = ((((static_cast<std::ptrdiff_t>(*i) - n) % count) + count) % count);
|
||||
if (index >= 0 && index < count) {
|
||||
return enum_value<D, S>(static_cast<std::size_t>(index));
|
||||
}
|
||||
}
|
||||
return MAGIC_ENUM_ASSERT(false), value;
|
||||
}
|
||||
|
||||
} // namespace magic_enum
|
||||
|
||||
#endif // NEARGYE_MAGIC_ENUM_UTILITY_HPP
|
||||
109
src/Utils/utils.hpp
Normal file
109
src/Utils/utils.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <cwctype>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Fig::Utils
|
||||
{
|
||||
inline std::u32string utf8ToUtf32(const FString &s)
|
||||
{
|
||||
std::u32string result;
|
||||
size_t i = 0;
|
||||
while (i < s.size())
|
||||
{
|
||||
char32_t codepoint = 0;
|
||||
unsigned char c = static_cast<unsigned char>(s[i]);
|
||||
|
||||
if (c < 0x80)
|
||||
{
|
||||
codepoint = c;
|
||||
i += 1;
|
||||
}
|
||||
else if ((c >> 5) == 0x6)
|
||||
{
|
||||
codepoint = ((c & 0x1F) << 6) | (static_cast<unsigned char>(s[i + 1]) & 0x3F);
|
||||
i += 2;
|
||||
}
|
||||
else if ((c >> 4) == 0xE)
|
||||
{
|
||||
codepoint = ((c & 0x0F) << 12) | ((static_cast<unsigned char>(s[i + 1]) & 0x3F) << 6) | (static_cast<unsigned char>(s[i + 2]) & 0x3F);
|
||||
i += 3;
|
||||
}
|
||||
else if ((c >> 3) == 0x1E)
|
||||
{
|
||||
codepoint = ((c & 0x07) << 18) | ((static_cast<unsigned char>(s[i + 1]) & 0x3F) << 12) | ((static_cast<unsigned char>(s[i + 2]) & 0x3F) << 6) | (static_cast<unsigned char>(s[i + 3]) & 0x3F);
|
||||
i += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 1; // 跳过非法字节
|
||||
continue;
|
||||
}
|
||||
result.push_back(codepoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline FString utf32ToUtf8(const std::u32string &s)
|
||||
{
|
||||
FString result;
|
||||
for (char32_t cp : s)
|
||||
{
|
||||
if (cp < 0x80)
|
||||
{
|
||||
result.push_back(static_cast<char8_t>(cp));
|
||||
}
|
||||
else if (cp < 0x800)
|
||||
{
|
||||
result.push_back(static_cast<char8_t>((cp >> 6) | 0xC0));
|
||||
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||
}
|
||||
else if (cp < 0x10000)
|
||||
{
|
||||
result.push_back(static_cast<char8_t>((cp >> 12) | 0xE0));
|
||||
result.push_back(static_cast<char8_t>(((cp >> 6) & 0x3F) | 0x80));
|
||||
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(static_cast<char8_t>((cp >> 18) | 0xF0));
|
||||
result.push_back(static_cast<char8_t>(((cp >> 12) & 0x3F) | 0x80));
|
||||
result.push_back(static_cast<char8_t>(((cp >> 6) & 0x3F) | 0x80));
|
||||
result.push_back(static_cast<char8_t>((cp & 0x3F) | 0x80));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline FString toLower(const FString &s)
|
||||
{
|
||||
std::u32string u32 = utf8ToUtf32(s);
|
||||
std::locale loc("");
|
||||
for (auto &ch : u32)
|
||||
{
|
||||
ch = std::towlower(ch);
|
||||
}
|
||||
return utf32ToUtf8(u32);
|
||||
}
|
||||
|
||||
inline FString toUpper(const FString &s)
|
||||
{
|
||||
std::u32string u32 = utf8ToUtf32(s);
|
||||
std::locale loc("");
|
||||
for (auto &ch : u32)
|
||||
{
|
||||
ch = std::towupper(ch);
|
||||
}
|
||||
return utf32ToUtf8(u32);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool vectorContains(const T &t, const std::vector<T> v)
|
||||
{
|
||||
return std::find(v.begin(), v.end(), t) != v.end();
|
||||
}
|
||||
} // namespace Fig::Utils
|
||||
67
src/Value/Type.hpp
Normal file
67
src/Value/Type.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
|
||||
#include <variant>
|
||||
#include <map>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
class TypeInfo final
|
||||
{
|
||||
private:
|
||||
size_t id;
|
||||
|
||||
public:
|
||||
FString name;
|
||||
|
||||
FString toString() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
static std::map<FString, size_t> typeMap;
|
||||
|
||||
static size_t getID(FString _name)
|
||||
{
|
||||
return typeMap.at(_name);
|
||||
}
|
||||
size_t getInstanceID(FString _name) const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
TypeInfo();
|
||||
TypeInfo(FString _name, bool reg = false);
|
||||
TypeInfo(const TypeInfo &other) = default;
|
||||
|
||||
bool operator==(const TypeInfo &other) const
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
};
|
||||
|
||||
// class Value;
|
||||
namespace ValueType
|
||||
{
|
||||
extern const TypeInfo Any;
|
||||
extern const TypeInfo Null;
|
||||
extern const TypeInfo Int;
|
||||
extern const TypeInfo String;
|
||||
extern const TypeInfo Bool;
|
||||
extern const TypeInfo Double;
|
||||
extern const TypeInfo Function;
|
||||
extern const TypeInfo StructType;
|
||||
extern const TypeInfo StructInstance;
|
||||
extern const TypeInfo List;
|
||||
extern const TypeInfo Map;
|
||||
extern const TypeInfo Tuple;
|
||||
|
||||
using IntClass = int64_t;
|
||||
using DoubleClass = double;
|
||||
using BoolClass = bool;
|
||||
using NullClass = std::monostate;
|
||||
using StringClass = FString;
|
||||
}; // namespace ValueType
|
||||
}; // namespace Fig
|
||||
6
src/Value/containers.hpp
Normal file
6
src/Value/containers.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
|
||||
};
|
||||
67
src/Value/function.hpp
Normal file
67
src/Value/function.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ast/functionParameters.hpp>
|
||||
#include <Context/context_forward.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class Object;
|
||||
class Function
|
||||
{
|
||||
public:
|
||||
std::size_t id;
|
||||
Ast::FunctionParameters paras;
|
||||
TypeInfo retType;
|
||||
Ast::BlockStatement body;
|
||||
|
||||
bool isBuiltin = false;
|
||||
std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> builtin;
|
||||
int builtinParamCount = -1;
|
||||
|
||||
std::shared_ptr<Context> closureContext;
|
||||
|
||||
// ===== Constructors =====
|
||||
Function() :
|
||||
id(nextId()) {}
|
||||
|
||||
Function(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body, ContextPtr _closureContext) :
|
||||
id(nextId()), // 分配唯一 ID
|
||||
paras(std::move(_paras)),
|
||||
retType(std::move(_retType)),
|
||||
body(std::move(_body)),
|
||||
closureContext(std::move(_closureContext))
|
||||
{
|
||||
}
|
||||
|
||||
Function(std::function<std::shared_ptr<Object>(const std::vector<std::shared_ptr<Object>> &)> fn, int argc) :
|
||||
id(nextId()), isBuiltin(true), builtin(fn), builtinParamCount(argc) {}
|
||||
|
||||
// ===== Copy / Move =====
|
||||
Function(const Function &other) = default;
|
||||
Function(Function &&) noexcept = default;
|
||||
Function &operator=(const Function &) = default;
|
||||
Function &operator=(Function &&) noexcept = default;
|
||||
|
||||
// ===== Comparison =====
|
||||
bool operator==(const Function &other) const noexcept
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
bool operator!=(const Function &other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t nextId()
|
||||
{
|
||||
static std::atomic<std::size_t> counter{1};
|
||||
return counter++;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
31
src/Value/structInstance.hpp
Normal file
31
src/Value/structInstance.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct StructInstance
|
||||
{
|
||||
size_t parentId;
|
||||
ContextPtr localContext;
|
||||
|
||||
// ===== Constructors =====
|
||||
StructInstance(size_t _parentId, ContextPtr _localContext) :
|
||||
parentId(_parentId), localContext(std::move(_localContext)) {}
|
||||
|
||||
StructInstance(const StructInstance &other) = default;
|
||||
StructInstance(StructInstance &&) noexcept = default;
|
||||
StructInstance &operator=(const StructInstance &) = default;
|
||||
StructInstance &operator=(StructInstance &&) noexcept = default;
|
||||
|
||||
// ===== Comparison =====
|
||||
bool operator==(const StructInstance &other) const noexcept
|
||||
{
|
||||
return parentId == other.parentId && localContext == other.localContext;
|
||||
}
|
||||
bool operator!=(const StructInstance &other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
67
src/Value/structType.hpp
Normal file
67
src/Value/structType.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/fig_string.hpp>
|
||||
#include <Ast/Statements/StructDefSt.hpp>
|
||||
|
||||
#include <Value/Type.hpp>
|
||||
|
||||
#include <Context/context_forward.hpp>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
struct Field
|
||||
{
|
||||
AccessModifier am;
|
||||
FString name;
|
||||
TypeInfo type;
|
||||
Ast::Expression defaultValue;
|
||||
|
||||
Field(AccessModifier _am, FString _name, TypeInfo _type, Ast::Expression _defaultValue) :
|
||||
am(_am), name(std::move(_name)), type(std::move(_type)), defaultValue(std::move(_defaultValue)) {}
|
||||
|
||||
bool isPublic() const
|
||||
{
|
||||
return am == AccessModifier::Public || am == AccessModifier::PublicConst;
|
||||
}
|
||||
bool isConst() const
|
||||
{
|
||||
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct StructType
|
||||
{
|
||||
std::size_t id;
|
||||
ContextPtr defContext; // 定义时的上下文
|
||||
std::vector<Field> fields;
|
||||
|
||||
// ===== Constructors =====
|
||||
StructType(ContextPtr _defContext, std::vector<Field> _fields) :
|
||||
id(nextId()), defContext(std::move(_defContext)), fields(std::move(_fields)) {}
|
||||
|
||||
StructType(const StructType &other) = default;
|
||||
StructType(StructType &&) noexcept = default;
|
||||
StructType &operator=(const StructType &) = default;
|
||||
StructType &operator=(StructType &&) noexcept = default;
|
||||
|
||||
// ===== Comparison =====
|
||||
bool operator==(const StructType &other) const noexcept
|
||||
{
|
||||
return id == other.id;
|
||||
}
|
||||
bool operator!=(const StructType &other) const noexcept
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t nextId()
|
||||
{
|
||||
static std::atomic<std::size_t> counter{1};
|
||||
return counter++;
|
||||
}
|
||||
};
|
||||
} // namespace Fig
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <value.hpp>
|
||||
#include <Value/value.hpp>
|
||||
#include <Context/context.hpp>
|
||||
|
||||
// #include <iostream>
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace Fig
|
||||
id = typeMap.at(name); // may throw
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
||||
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
|
||||
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
|
||||
453
src/Value/value.hpp
Normal file
453
src/Value/value.hpp
Normal file
@@ -0,0 +1,453 @@
|
||||
#pragma once
|
||||
#include <Value/function.hpp>
|
||||
#include <Value/structType.hpp>
|
||||
#include <Value/structInstance.hpp>
|
||||
#include <Value/Type.hpp>
|
||||
#include <Value/valueError.hpp>
|
||||
|
||||
#include <variant>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <format>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
inline bool isDoubleInteger(ValueType::DoubleClass d)
|
||||
{
|
||||
return std::floor(d) == d;
|
||||
}
|
||||
|
||||
inline bool isNumberExceededIntLimit(ValueType::DoubleClass d)
|
||||
{
|
||||
static constexpr auto intMaxAsDouble =
|
||||
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::max());
|
||||
|
||||
static constexpr auto intMinAsDouble =
|
||||
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
|
||||
return d > intMaxAsDouble || d < intMinAsDouble;
|
||||
}
|
||||
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
using VariantType = std::variant<
|
||||
ValueType::NullClass,
|
||||
ValueType::IntClass,
|
||||
ValueType::DoubleClass,
|
||||
ValueType::StringClass,
|
||||
ValueType::BoolClass,
|
||||
Function,
|
||||
StructType,
|
||||
StructInstance>;
|
||||
|
||||
VariantType data;
|
||||
|
||||
Object() :
|
||||
data(ValueType::NullClass{}) {}
|
||||
Object(const ValueType::NullClass &n) :
|
||||
data(n) {}
|
||||
Object(const ValueType::IntClass &i) :
|
||||
data(i) {}
|
||||
explicit Object(const ValueType::DoubleClass &d) :
|
||||
data(d)
|
||||
{
|
||||
}
|
||||
Object(const ValueType::StringClass &s) :
|
||||
data(s) {}
|
||||
Object(const ValueType::BoolClass &b) :
|
||||
data(b) {}
|
||||
Object(const Function &f) :
|
||||
data(f) {}
|
||||
Object(const StructType &s) :
|
||||
data(s) {}
|
||||
Object(const StructInstance &s) :
|
||||
data(s) {}
|
||||
|
||||
Object(const Object &) = default;
|
||||
Object(Object &&) noexcept = default;
|
||||
Object &operator=(const Object &) = default;
|
||||
Object &operator=(Object &&) noexcept = default;
|
||||
|
||||
static Object defaultValue(TypeInfo ti)
|
||||
{
|
||||
if (ti == ValueType::Int)
|
||||
return Object(ValueType::IntClass(0));
|
||||
else if (ti == ValueType::Double)
|
||||
return Object(ValueType::DoubleClass(0.0));
|
||||
else if (ti == ValueType::String)
|
||||
return Object(ValueType::StringClass(u8""));
|
||||
else if (ti == ValueType::Bool)
|
||||
return Object(ValueType::BoolClass(false));
|
||||
else
|
||||
return *getNullInstance();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is() const
|
||||
{
|
||||
return std::holds_alternative<T>(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T &as()
|
||||
{
|
||||
return std::get<T>(data);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T &as() const
|
||||
{
|
||||
return std::get<T>(data);
|
||||
}
|
||||
|
||||
static std::shared_ptr<Object> getNullInstance()
|
||||
{
|
||||
static std::shared_ptr<Object> n = std::make_shared<Object>(ValueType::NullClass{});
|
||||
return n;
|
||||
}
|
||||
static std::shared_ptr<Object> getTrueInstance()
|
||||
{
|
||||
static std::shared_ptr<Object> t = std::make_shared<Object>(true);
|
||||
return t;
|
||||
}
|
||||
static std::shared_ptr<Object> getFalseInstance()
|
||||
{
|
||||
static std::shared_ptr<Object> f = std::make_shared<Object>(false);
|
||||
return f;
|
||||
}
|
||||
|
||||
TypeInfo getTypeInfo() const
|
||||
{
|
||||
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
|
||||
return ValueType::Any;
|
||||
},
|
||||
data);
|
||||
}
|
||||
|
||||
bool isNull() const { return is<ValueType::NullClass>(); }
|
||||
bool isNumeric() const { return is<ValueType::IntClass>() || is<ValueType::DoubleClass>(); }
|
||||
|
||||
ValueType::DoubleClass getNumericValue() const
|
||||
{
|
||||
if (is<ValueType::IntClass>())
|
||||
return static_cast<ValueType::DoubleClass>(as<ValueType::IntClass>());
|
||||
else if (is<ValueType::DoubleClass>())
|
||||
return as<ValueType::DoubleClass>();
|
||||
else
|
||||
throw RuntimeError(u8"getNumericValue: Not a numeric value");
|
||||
}
|
||||
|
||||
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::BoolClass>()) return as<ValueType::BoolClass>() ? FString(u8"true") : FString(u8"false");
|
||||
if (is<Function>())
|
||||
return FString(std::format("<Function {} at {:p}>",
|
||||
as<Function>().id,
|
||||
static_cast<const void *>(&as<Function>())));
|
||||
if (is<StructType>())
|
||||
return FString(std::format("<StructType {} at {:p}>",
|
||||
as<StructType>().id,
|
||||
static_cast<const void *>(&as<StructType>())));
|
||||
if (is<StructInstance>())
|
||||
return FString(std::format("<StructInstance '{}' at {:p}>",
|
||||
as<StructInstance>().parentId,
|
||||
static_cast<const void *>(&as<StructInstance>())));
|
||||
return FString(u8"<error>");
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string makeTypeErrorMessage(const char *prefix, const char *op,
|
||||
const Object &lhs, const Object &rhs)
|
||||
{
|
||||
auto lhs_type = lhs.getTypeInfo().name.toBasicString();
|
||||
auto rhs_type = rhs.getTypeInfo().name.toBasicString();
|
||||
return std::format("{}: {} '{}' {}", prefix, lhs_type, op, rhs_type);
|
||||
}
|
||||
|
||||
public:
|
||||
// math
|
||||
friend Object operator+(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
if (lhs.isNull() || rhs.isNull())
|
||||
throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() + rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
friend Object operator-(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
if (lhs.isNull() || rhs.isNull())
|
||||
throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() - rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FStringView(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)));
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
bool bothInt = lhs.is<ValueType::IntClass>() && rhs.is<ValueType::IntClass>();
|
||||
auto result = lhs.getNumericValue() * rhs.getNumericValue();
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FStringView(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)));
|
||||
if (lhs.isNumeric() && rhs.isNumeric())
|
||||
{
|
||||
auto rnv = rhs.getNumericValue();
|
||||
if (rnv == 0)
|
||||
throw ValueError(FStringView(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)));
|
||||
}
|
||||
|
||||
friend Object operator%(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
if (lhs.isNull() || rhs.isNull())
|
||||
throw ValueError(FStringView(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)));
|
||||
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)));
|
||||
}
|
||||
|
||||
// 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)));
|
||||
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)));
|
||||
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())));
|
||||
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"));
|
||||
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())));
|
||||
}
|
||||
|
||||
friend Object operator~(const Object &v)
|
||||
{
|
||||
if (!v.is<ValueType::IntClass>())
|
||||
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'", v.getTypeInfo().name.toBasicString())));
|
||||
return Object(~v.as<ValueType::IntClass>());
|
||||
}
|
||||
|
||||
// comparison
|
||||
friend bool operator==(const Object &lhs, const Object &rhs) { return lhs.data == rhs.data; }
|
||||
friend bool operator!=(const Object &lhs, const Object &rhs) { return !(lhs == rhs); }
|
||||
friend bool operator<(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
friend bool operator<=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs < rhs; }
|
||||
friend bool operator>(const Object &lhs, const Object &rhs)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
friend bool operator>=(const Object &lhs, const Object &rhs) { return lhs == rhs || lhs > rhs; }
|
||||
|
||||
// bitwise
|
||||
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)));
|
||||
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)));
|
||||
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)));
|
||||
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())));
|
||||
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)));
|
||||
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)));
|
||||
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)));
|
||||
if (base.isNumeric() && exp.isNumeric())
|
||||
{
|
||||
bool bothInt = base.is<ValueType::IntClass>() && exp.is<ValueType::IntClass>();
|
||||
auto result = std::pow(base.getNumericValue(), exp.getNumericValue());
|
||||
if (bothInt && !isNumberExceededIntLimit(result))
|
||||
return Object(static_cast<ValueType::IntClass>(result));
|
||||
return Object(result);
|
||||
}
|
||||
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "**", base, exp)));
|
||||
}
|
||||
};
|
||||
|
||||
using ObjectPtr = std::shared_ptr<Object>;
|
||||
using RvObject = ObjectPtr;
|
||||
|
||||
struct VariableSlot
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace Fig
|
||||
17
src/Value/valueError.hpp
Normal file
17
src/Value/valueError.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <Error/error.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
class ValueError : public UnaddressableError
|
||||
{
|
||||
public:
|
||||
using UnaddressableError::UnaddressableError;
|
||||
virtual FString toString() const override
|
||||
{
|
||||
std::string msg = std::format("[ValueError] {} in [{}] {}", std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
|
||||
return FString(msg);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,854 +0,0 @@
|
||||
#include <evaluator.hpp>
|
||||
#include <builtins.hpp>
|
||||
#include <utils.hpp>
|
||||
|
||||
namespace Fig
|
||||
{
|
||||
ObjectPtr Evaluator::__evalOp(Ast::Operator op, const ObjectPtr &lhs, const ObjectPtr &rhs)
|
||||
{
|
||||
using Fig::Ast::Operator;
|
||||
switch (op)
|
||||
{
|
||||
case Operator::Add: return std::make_shared<Object>(*lhs + *rhs);
|
||||
case Operator::Subtract: return std::make_shared<Object>(*lhs - *rhs);
|
||||
case Operator::Multiply: return std::make_shared<Object>((*lhs) * (*rhs));
|
||||
case Operator::Divide: return std::make_shared<Object>(*lhs / *rhs);
|
||||
case Operator::Modulo: return std::make_shared<Object>(*lhs % *rhs);
|
||||
case Operator::Power: return std::make_shared<Object>(power(*lhs, *rhs));
|
||||
|
||||
case Operator::And: return std::make_shared<Object>(*lhs && *rhs);
|
||||
case Operator::Or: return std::make_shared<Object>(*lhs || *rhs);
|
||||
case Operator::Not: return std::make_shared<Object>(!*lhs);
|
||||
|
||||
case Operator::Equal: return std::make_shared<Object>(*lhs == *rhs);
|
||||
case Operator::NotEqual: return std::make_shared<Object>(*lhs != *rhs);
|
||||
case Operator::Less: return std::make_shared<Object>(*lhs < *rhs);
|
||||
case Operator::LessEqual: return std::make_shared<Object>(*lhs <= *rhs);
|
||||
case Operator::Greater: return std::make_shared<Object>(*lhs > *rhs);
|
||||
case Operator::GreaterEqual: return std::make_shared<Object>(*lhs >= *rhs);
|
||||
|
||||
case Operator::BitAnd: return std::make_shared<Object>(bit_and(*lhs, *rhs));
|
||||
case Operator::BitOr: return std::make_shared<Object>(bit_or(*lhs, *rhs));
|
||||
case Operator::BitXor: return std::make_shared<Object>(bit_xor(*lhs, *rhs));
|
||||
case Operator::BitNot: return std::make_shared<Object>(bit_not(*lhs));
|
||||
case Operator::ShiftLeft: return std::make_shared<Object>(shift_left(*lhs, *rhs));
|
||||
case Operator::ShiftRight: return std::make_shared<Object>(shift_right(*lhs, *rhs));
|
||||
|
||||
case Operator::Assign: {
|
||||
*lhs = *rhs;
|
||||
return Object::getNullInstance();
|
||||
}
|
||||
|
||||
// case Operator::Walrus: {
|
||||
// static constexpr char WalrusErrorName[] = "WalrusError";
|
||||
// throw EvaluatorError<WalrusErrorName>(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now
|
||||
// }
|
||||
default:
|
||||
throw RuntimeError(FStringView(u8"Unsupported operator"));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectPtr Evaluator::evalBinary(const Ast::BinaryExpr &binExp)
|
||||
{
|
||||
if (binExp->op == Ast::Operator::Dot)
|
||||
{
|
||||
const ObjectPtr &lhs = eval(binExp->lexp);
|
||||
if (!lhs->is<StructInstance>())
|
||||
{
|
||||
static constexpr char AccessOpObjectNotStructError[] = "AccessOpObjectNotStructError";
|
||||
throw EvaluatorError<AccessOpObjectNotStructError>(FStringView(
|
||||
std::format("Object not a struct")),
|
||||
binExp->lexp->getAAI());
|
||||
}
|
||||
const StructInstance &st = lhs->as<StructInstance>();
|
||||
Ast::VarExpr varExp;
|
||||
Ast::FunctionCall fnCall;
|
||||
if ((varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(binExp->rexp)))
|
||||
{
|
||||
FString member = varExp->name;
|
||||
auto structTypeNameOpt = currentContext->getStructName(st.parentId);
|
||||
if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name"));
|
||||
FString structTypeName = *structTypeNameOpt;
|
||||
if (!st.localContext->containsInThisScope(member))
|
||||
{
|
||||
static constexpr char NoAttributeError[] = "NoAttributeError";
|
||||
throw EvaluatorError<NoAttributeError>(FStringView(
|
||||
std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), member.toBasicString())),
|
||||
binExp->rexp->getAAI());
|
||||
}
|
||||
if (!st.localContext->isVariablePublic(member))
|
||||
{
|
||||
static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError";
|
||||
throw EvaluatorError<AttributeIsPrivateError>(FStringView(
|
||||
std::format("Attribute '{}' of class `{}` is private",
|
||||
structTypeName.toBasicString(),
|
||||
member.toBasicString())),
|
||||
binExp->rexp->getAAI());
|
||||
}
|
||||
return *st.localContext->get(member); // safe
|
||||
}
|
||||
else if ((fnCall = std::dynamic_pointer_cast<Ast::FunctionCallExpr>(binExp->rexp)))
|
||||
{
|
||||
auto structTypeNameOpt = currentContext->getStructName(st.parentId);
|
||||
if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name"));
|
||||
FString structTypeName = *structTypeNameOpt;
|
||||
|
||||
FString fnName = u8"<anonymous>";
|
||||
if (auto var = std::dynamic_pointer_cast<Ast::VarExprAst>(fnCall->callee))
|
||||
fnName = var->name; // function in struct has its name, so we can get the name
|
||||
if (!st.localContext->containsInThisScope(fnName))
|
||||
{
|
||||
static constexpr char NoAttributeError[] = "NoAttributeError";
|
||||
throw EvaluatorError<NoAttributeError>(FStringView(
|
||||
std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), fnName.toBasicString())),
|
||||
binExp->rexp->getAAI());
|
||||
}
|
||||
if (!st.localContext->isVariablePublic(fnName))
|
||||
{
|
||||
static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError";
|
||||
throw EvaluatorError<AttributeIsPrivateError>(FStringView(
|
||||
std::format("Attribute '{}' of class `{}` is private",
|
||||
structTypeName.toBasicString(),
|
||||
fnName.toBasicString())),
|
||||
binExp->rexp->getAAI());
|
||||
}
|
||||
auto calleeValOpt = st.localContext->get(fnName);
|
||||
ObjectPtr calleeVal = *calleeValOpt;
|
||||
|
||||
if (!calleeVal->is<Function>())
|
||||
{
|
||||
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
|
||||
throw EvaluatorError<NotAFunctionErrorName>(
|
||||
FStringView(std::format(
|
||||
"'{}' is not a function or callable",
|
||||
calleeVal->toString().toBasicString())),
|
||||
currentAddressInfo);
|
||||
}
|
||||
|
||||
Function fn = calleeVal->as<Function>();
|
||||
|
||||
return evalFunctionCall(fn, fnCall->arg, fnName);
|
||||
}
|
||||
else
|
||||
{
|
||||
static constexpr char AccessOpNotAFieldNameError[] = "AccessOpNotAFieldNameError";
|
||||
throw EvaluatorError<AccessOpNotAFieldNameError>(FStringView(
|
||||
std::format("{} is not a field", binExp->rexp->toString().toBasicString())),
|
||||
binExp->rexp->getAAI());
|
||||
}
|
||||
}
|
||||
return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp));
|
||||
}
|
||||
ObjectPtr Evaluator::evalUnary(const Ast::UnaryExpr &unExp)
|
||||
{
|
||||
using Fig::Ast::Operator;
|
||||
switch (unExp->op)
|
||||
{
|
||||
case Operator::Not:
|
||||
return std::make_shared<Object>(!*eval(unExp->exp));
|
||||
case Operator::Subtract:
|
||||
return std::make_shared<Object>(-*eval(unExp->exp));
|
||||
case Operator::BitNot:
|
||||
return std::make_shared<Object>(bit_not(*eval(unExp->exp)));
|
||||
default:
|
||||
throw RuntimeError(FStringView(std::format("Unsupported unary operator: {}", magic_enum::enum_name(unExp->op))));
|
||||
}
|
||||
}
|
||||
|
||||
ObjectPtr Evaluator::evalFunctionCall(const Function &fn, const Ast::FunctionArguments &fnArgs, FString fnName)
|
||||
{
|
||||
const Function &fnStruct = fn;
|
||||
Ast::FunctionCallArgs evaluatedArgs;
|
||||
if (fnStruct.isBuiltin)
|
||||
{
|
||||
for (const auto &argExpr : fnArgs.argv)
|
||||
{
|
||||
evaluatedArgs.argv.push_back(eval(argExpr));
|
||||
}
|
||||
if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength())
|
||||
{
|
||||
static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError";
|
||||
throw EvaluatorError<BuiltinArgumentMismatchErrorName>(FStringView(std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), fnStruct.builtinParamCount, evaluatedArgs.getLength())), currentAddressInfo);
|
||||
}
|
||||
return fnStruct.builtin(evaluatedArgs.argv);
|
||||
}
|
||||
|
||||
// check argument, all types of parameters
|
||||
Ast::FunctionParameters fnParas = fnStruct.paras;
|
||||
if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
|
||||
{
|
||||
static constexpr char ArgumentMismatchErrorName[] = "ArgumentMismatchError";
|
||||
throw EvaluatorError<ArgumentMismatchErrorName>(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo);
|
||||
}
|
||||
|
||||
// positional parameters type check
|
||||
size_t i;
|
||||
for (i = 0; i < fnParas.posParas.size(); i++)
|
||||
{
|
||||
TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the name, use it, else throw
|
||||
ObjectPtr argVal = eval(fnArgs.argv[i]);
|
||||
TypeInfo actualType = argVal->getTypeInfo();
|
||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
||||
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.posParas[i].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
evaluatedArgs.argv.push_back(argVal);
|
||||
}
|
||||
// default parameters type check
|
||||
for (; i < fnArgs.getLength(); i++)
|
||||
{
|
||||
size_t defParamIndex = i - fnParas.posParas.size();
|
||||
TypeInfo expectedType = fnParas.defParas[defParamIndex].second.first;
|
||||
|
||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
||||
if (expectedType != defaultVal->getTypeInfo() and expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char DefaultParameterTypeErrorName[] = "DefaultParameterTypeError";
|
||||
throw EvaluatorError<DefaultParameterTypeErrorName>(FStringView(std::format("In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), defaultVal->getTypeInfo().toString().toBasicString(), expectedType.toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
|
||||
ObjectPtr argVal = eval(fnArgs.argv[i]);
|
||||
TypeInfo actualType = argVal->getTypeInfo();
|
||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
||||
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
evaluatedArgs.argv.push_back(argVal);
|
||||
}
|
||||
// default parameters filling
|
||||
for (; i < fnParas.size(); i++)
|
||||
{
|
||||
size_t defParamIndex = i - fnParas.posParas.size();
|
||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
||||
evaluatedArgs.argv.push_back(defaultVal);
|
||||
}
|
||||
// create new context for function call
|
||||
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())),
|
||||
fnStruct.closureContext);
|
||||
auto previousContext = currentContext;
|
||||
currentContext = newContext;
|
||||
// define parameters in new context
|
||||
for (size_t j = 0; j < fnParas.size(); j++)
|
||||
{
|
||||
FString paramName;
|
||||
TypeInfo paramType;
|
||||
if (j < fnParas.posParas.size())
|
||||
{
|
||||
paramName = fnParas.posParas[j].first;
|
||||
paramType = fnParas.posParas[j].second;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t defParamIndex = j - fnParas.posParas.size();
|
||||
paramName = fnParas.defParas[defParamIndex].first;
|
||||
paramType = fnParas.defParas[defParamIndex].second.first;
|
||||
}
|
||||
AccessModifier argAm = AccessModifier::Const;
|
||||
currentContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
|
||||
}
|
||||
// execute function body
|
||||
ObjectPtr retVal = Object::getNullInstance();
|
||||
for (const auto &stmt : fnStruct.body->stmts)
|
||||
{
|
||||
StatementResult sr = evalStatement(stmt);
|
||||
if (sr.shouldReturn())
|
||||
{
|
||||
retVal = sr.result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentContext = previousContext;
|
||||
if (fnStruct.retType != retVal->getTypeInfo() and fnStruct.retType != ValueType::Any)
|
||||
{
|
||||
static constexpr char ReturnTypeMismatchErrorName[] = "ReturnTypeMismatchError";
|
||||
throw EvaluatorError<ReturnTypeMismatchErrorName>(FStringView(std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), fnStruct.retType.toString().toBasicString(), retVal->getTypeInfo().toString().toBasicString())), currentAddressInfo);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
ObjectPtr Evaluator::eval(Ast::Expression exp)
|
||||
{
|
||||
using Fig::Ast::AstType;
|
||||
switch (exp->getType())
|
||||
{
|
||||
case AstType::ValueExpr: {
|
||||
auto valExp = std::dynamic_pointer_cast<Ast::ValueExprAst>(exp);
|
||||
return valExp->val;
|
||||
}
|
||||
case AstType::VarExpr: {
|
||||
auto varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(exp);
|
||||
auto val = currentContext->get(varExp->name);
|
||||
if (val.has_value())
|
||||
{
|
||||
return val.value();
|
||||
}
|
||||
static constexpr char UndefinedVariableErrorName[] = "UndefinedVariableError";
|
||||
throw EvaluatorError<UndefinedVariableErrorName>(FStringView(std::format("Variable '{}' is not defined in the current scope", varExp->name.toBasicString())), varExp->getAAI());
|
||||
}
|
||||
case AstType::BinaryExpr: {
|
||||
auto binExp = std::dynamic_pointer_cast<Ast::BinaryExprAst>(exp);
|
||||
return evalBinary(binExp);
|
||||
}
|
||||
case AstType::UnaryExpr: {
|
||||
auto unExp = std::dynamic_pointer_cast<Ast::UnaryExprAst>(exp);
|
||||
return evalUnary(unExp);
|
||||
}
|
||||
case AstType::FunctionCall: {
|
||||
auto fnCall = std::dynamic_pointer_cast<Ast::FunctionCallExpr>(exp);
|
||||
|
||||
ObjectPtr calleeVal = eval(fnCall->callee);
|
||||
|
||||
if (!calleeVal->is<Function>())
|
||||
{
|
||||
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
|
||||
throw EvaluatorError<NotAFunctionErrorName>(
|
||||
FStringView(std::format(
|
||||
"'{}' is not a function or callable",
|
||||
calleeVal->toString().toBasicString())),
|
||||
currentAddressInfo);
|
||||
}
|
||||
|
||||
Function fn = calleeVal->as<Function>();
|
||||
|
||||
FString fnName = u8"<anonymous>";
|
||||
if (auto var = std::dynamic_pointer_cast<Ast::VarExprAst>(fnCall->callee))
|
||||
fnName = var->name; // try to get function name
|
||||
|
||||
return evalFunctionCall(fn, fnCall->arg, fnName);
|
||||
}
|
||||
|
||||
case AstType::FunctionLiteralExpr: {
|
||||
auto fn = std::dynamic_pointer_cast<Ast::FunctionLiteralExprAst>(exp);
|
||||
|
||||
if (fn->isExprMode())
|
||||
{
|
||||
Ast::BlockStatement body = std::make_shared<Ast::BlockStatementAst>();
|
||||
body->setAAI(fn->getExprBody()->getAAI());
|
||||
Ast::Statement retSt = std::make_shared<Ast::ReturnSt>(fn->getExprBody());
|
||||
retSt->setAAI(fn->getExprBody()->getAAI());
|
||||
body->stmts.push_back(retSt);
|
||||
return std::make_shared<Object>(Function(
|
||||
fn->paras,
|
||||
ValueType::Any,
|
||||
body,
|
||||
currentContext));
|
||||
}
|
||||
else
|
||||
{
|
||||
Ast::BlockStatement body = fn->getBlockBody();
|
||||
return std::make_shared<Object>(Function(
|
||||
fn->paras,
|
||||
ValueType::Any,
|
||||
body,
|
||||
currentContext));
|
||||
}
|
||||
}
|
||||
case AstType::InitExpr: {
|
||||
auto initExpr = std::dynamic_pointer_cast<Ast::InitExprAst>(exp);
|
||||
if (!currentContext->contains(initExpr->structName))
|
||||
{
|
||||
static constexpr char StructNotFoundErrorName[] = "StructNotFoundError";
|
||||
throw EvaluatorError<StructNotFoundErrorName>(FStringView(std::format("Structure type '{}' not found", initExpr->structName.toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
ObjectPtr structTypeVal = currentContext->get(initExpr->structName).value();
|
||||
if (!structTypeVal->is<StructType>())
|
||||
{
|
||||
static constexpr char NotAStructTypeErrorName[] = "NotAStructTypeError";
|
||||
throw EvaluatorError<NotAStructTypeErrorName>(FStringView(std::format("'{}' is not a structure type", initExpr->structName.toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
const StructType &structT = structTypeVal->as<StructType>();
|
||||
ContextPtr defContext = structT.defContext; // definition context
|
||||
// check init args
|
||||
|
||||
size_t minArgs = 0;
|
||||
size_t maxArgs = structT.fields.size();
|
||||
|
||||
for (auto &f : structT.fields)
|
||||
{
|
||||
if (f.defaultValue == nullptr) minArgs++;
|
||||
}
|
||||
|
||||
size_t got = initExpr->args.size();
|
||||
if (got > maxArgs || got < minArgs)
|
||||
{
|
||||
static constexpr char StructInitArgumentMismatchErrorName[] = "StructInitArgumentMismatchError";
|
||||
throw EvaluatorError<StructInitArgumentMismatchErrorName>(FStringView(std::format("Structure '{}' expects {} to {} fields, but {} were provided", initExpr->structName.toBasicString(), minArgs, maxArgs, initExpr->args.size())), initExpr->getAAI());
|
||||
}
|
||||
|
||||
std::vector<std::pair<FString, ObjectPtr>> evaluatedArgs;
|
||||
for (const auto &[argName, argExpr] : initExpr->args)
|
||||
{
|
||||
evaluatedArgs.push_back({argName, eval(argExpr)});
|
||||
}
|
||||
ContextPtr instanceCtx = std::make_shared<Context>(
|
||||
FString(std::format("<StructInstance {}>", initExpr->structName.toBasicString())),
|
||||
currentContext);
|
||||
/*
|
||||
3 ways of calling constructor
|
||||
.1 Person {"Fig", 1, "IDK"};
|
||||
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
|
||||
.3 Person {name, age, sex};
|
||||
*/
|
||||
{
|
||||
using enum Ast::InitExprAst::InitMode;
|
||||
if (initExpr->initMode == Positional)
|
||||
{
|
||||
for (size_t i = 0; i < maxArgs; ++i)
|
||||
{
|
||||
const Field &field = structT.fields[i];
|
||||
const FString &fieldName = field.name;
|
||||
const TypeInfo &expectedType = field.type;
|
||||
if (i >= evaluatedArgs.size())
|
||||
{
|
||||
// we've checked argument count before, so here must be a default value
|
||||
ContextPtr previousContext = currentContext;
|
||||
currentContext = defContext; // evaluate default value in definition context
|
||||
|
||||
ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here
|
||||
|
||||
currentContext = previousContext;
|
||||
|
||||
// type check
|
||||
if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
|
||||
instanceCtx->def(fieldName, expectedType, field.am, defaultVal);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ObjectPtr &argVal = evaluatedArgs[i].second;
|
||||
if (expectedType != argVal->getTypeInfo() && expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
instanceCtx->def(fieldName, expectedType, field.am, argVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// named / shorthand init
|
||||
for (size_t i = 0; i < maxArgs; ++i)
|
||||
{
|
||||
const Field &field = structT.fields[i];
|
||||
const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
|
||||
if (instanceCtx->containsInThisScope(fieldName))
|
||||
{
|
||||
static constexpr char StructFieldRedeclarationErrorName[] = "StructFieldRedeclarationError";
|
||||
throw EvaluatorError<StructFieldRedeclarationErrorName>(FStringView(std::format("Field '{}' already initialized in structure '{}'", fieldName.toBasicString(), initExpr->structName.toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
if (i + 1 > got)
|
||||
{
|
||||
// use default value
|
||||
ContextPtr previousContext = currentContext;
|
||||
currentContext = defContext; // evaluate default value in definition context
|
||||
ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here
|
||||
currentContext = previousContext;
|
||||
|
||||
// type check
|
||||
const TypeInfo &expectedType = field.type;
|
||||
if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
|
||||
instanceCtx->def(fieldName, field.type, field.am, defaultVal);
|
||||
continue;
|
||||
}
|
||||
const ObjectPtr &argVal = evaluatedArgs[i].second;
|
||||
if (field.type != argVal->getTypeInfo() && field.type != ValueType::Any)
|
||||
{
|
||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), field.type.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
||||
}
|
||||
instanceCtx->def(fieldName, field.type, field.am, argVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
instanceCtx->merge(*structT.defContext);
|
||||
for (auto &[id, fn] : instanceCtx->getFunctions())
|
||||
{
|
||||
instanceCtx->_update(*instanceCtx->getFunctionName(id), Object(
|
||||
Function(fn.paras, fn.retType, fn.body, instanceCtx) // change its closureContext to struct instance's context
|
||||
));
|
||||
}
|
||||
return std::make_shared<Object>(StructInstance(structT.id, instanceCtx));
|
||||
}
|
||||
default:
|
||||
throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast<int>(exp->getType()))));
|
||||
return Object::getNullInstance();
|
||||
}
|
||||
}
|
||||
StatementResult Evaluator::evalBlockStatement(const Ast::BlockStatement &blockSt, ContextPtr context)
|
||||
{
|
||||
auto previousContext = currentContext;
|
||||
if (context)
|
||||
{
|
||||
currentContext = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentContext = std::make_shared<Context>(FString(std::format("<Block {}:{}>", blockSt->getAAI().line, blockSt->getAAI().column)), currentContext);
|
||||
}
|
||||
StatementResult lstResult = StatementResult::normal();
|
||||
for (const auto &s : blockSt->stmts)
|
||||
{
|
||||
StatementResult sr = evalStatement(s);
|
||||
if (!sr.isNormal())
|
||||
{
|
||||
lstResult = sr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentContext = previousContext;
|
||||
return lstResult;
|
||||
}
|
||||
StatementResult Evaluator::evalStatement(const Ast::Statement &stmt)
|
||||
{
|
||||
using Fig::Ast::AstType;
|
||||
currentAddressInfo = stmt->getAAI();
|
||||
switch (stmt->getType())
|
||||
{
|
||||
case AstType::VarDefSt: {
|
||||
auto varDef = std::dynamic_pointer_cast<Ast::VarDefAst>(stmt);
|
||||
if (currentContext->contains(varDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Variable '{}' already defined in this scope", varDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
ObjectPtr val;
|
||||
TypeInfo varTypeInfo;
|
||||
if (varDef->typeName == Parser::varDefTypeFollowed)
|
||||
{
|
||||
// has expr
|
||||
val = eval(varDef->expr);
|
||||
varTypeInfo = val->getTypeInfo();
|
||||
}
|
||||
else if (varDef->expr)
|
||||
{
|
||||
val = eval(varDef->expr);
|
||||
if (varDef->typeName != ValueType::Any.name)
|
||||
{
|
||||
TypeInfo expectedType(varDef->typeName);
|
||||
TypeInfo actualType = val->getTypeInfo();
|
||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
||||
{
|
||||
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("Variable '{}' expects type '{}', but got type '{}'", varDef->name.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), varDef->getAAI());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!varDef->typeName.empty())
|
||||
{
|
||||
varTypeInfo = TypeInfo(varDef->typeName); // may throw
|
||||
val = std::make_shared<Object>(Object::defaultValue(varTypeInfo));
|
||||
}
|
||||
AccessModifier am = (varDef->isPublic ? (varDef->isConst ? AccessModifier::PublicConst : AccessModifier::Public) : (varDef->isConst ? AccessModifier::Const : AccessModifier::Normal));
|
||||
currentContext->def(varDef->name, varTypeInfo, am, val);
|
||||
return StatementResult::normal();
|
||||
}
|
||||
case AstType::ExpressionStmt: {
|
||||
auto exprSt = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(stmt);
|
||||
eval(exprSt->exp);
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::BlockStatement: {
|
||||
auto blockSt = std::dynamic_pointer_cast<Ast::BlockStatementAst>(stmt);
|
||||
return evalBlockStatement(blockSt); // auto create new context in block statement
|
||||
};
|
||||
case AstType::FunctionDefSt: {
|
||||
auto fnDef = std::dynamic_pointer_cast<Ast::FunctionDefSt>(stmt);
|
||||
if (currentContext->containsInThisScope(fnDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Function '{}' already defined in this scope", fnDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
AccessModifier am = (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
currentContext->def(
|
||||
fnDef->name,
|
||||
ValueType::Function,
|
||||
am,
|
||||
std::make_shared<Object>(Function(
|
||||
fnDef->paras,
|
||||
TypeInfo(fnDef->retType),
|
||||
fnDef->body,
|
||||
currentContext)));
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::StructSt: {
|
||||
auto stDef = std::dynamic_pointer_cast<Ast::StructDefSt>(stmt);
|
||||
if (currentContext->containsInThisScope(stDef->name))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
std::vector<Field> fields;
|
||||
std::vector<FString> _fieldNames;
|
||||
for (Ast::StructDefField field : stDef->fields)
|
||||
{
|
||||
if (Utils::vectorContains(field.fieldName, _fieldNames))
|
||||
{
|
||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Field '{}' already defined in structure '{}'", field.fieldName.toBasicString(), stDef->name.toBasicString())), currentAddressInfo);
|
||||
}
|
||||
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr));
|
||||
}
|
||||
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
|
||||
stDef->name.toBasicString(),
|
||||
stDef->getAAI().line,
|
||||
stDef->getAAI().column)),
|
||||
currentContext);
|
||||
ContextPtr previousContext = currentContext;
|
||||
currentContext = defContext;
|
||||
|
||||
const Ast::BlockStatement &body = stDef->body;
|
||||
for (auto &st : body->stmts)
|
||||
{
|
||||
if (st->getType() != Ast::AstType::FunctionDefSt)
|
||||
{
|
||||
static constexpr char UnexpectedStatementInStructError[] = "UnexpectedStatementInStructError";
|
||||
throw EvaluatorError<UnexpectedStatementInStructError>(FStringView(
|
||||
std::format("Unexpected statement `{}` in struct declaration",
|
||||
st->toString().toBasicString())),
|
||||
st->getAAI());
|
||||
}
|
||||
evalStatement(st); // function def st
|
||||
}
|
||||
currentContext = previousContext;
|
||||
|
||||
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
||||
TypeInfo _(stDef->name, true); // register type name
|
||||
currentContext->def(
|
||||
stDef->name,
|
||||
ValueType::StructType,
|
||||
am,
|
||||
std::make_shared<Object>(StructType(
|
||||
defContext,
|
||||
fields)));
|
||||
return StatementResult::normal();
|
||||
}
|
||||
// case AstType::VarAssignSt: {
|
||||
// auto varAssign = std::dynamic_pointer_cast<Ast::VarAssignSt>(stmt);
|
||||
// if (!currentContext->contains(varAssign->varName))
|
||||
// {
|
||||
// static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError";
|
||||
// throw EvaluatorError<VariableNotFoundErrorName>(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||
// }
|
||||
// if (!currentContext->isVariableMutable(varAssign->varName))
|
||||
// {
|
||||
// static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError";
|
||||
// throw EvaluatorError<ConstAssignmentErrorName>(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo);
|
||||
// }
|
||||
// Object val = eval(varAssign->valueExpr);
|
||||
// if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any)
|
||||
// {
|
||||
// TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName);
|
||||
// TypeInfo actualType = val.getTypeInfo();
|
||||
// if (expectedType != actualType)
|
||||
// {
|
||||
// static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
||||
// throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
||||
// }
|
||||
// }
|
||||
// currentContext->set(varAssign->varName, val);
|
||||
// return StatementResult::normal();
|
||||
// };
|
||||
case AstType::IfSt: {
|
||||
auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt);
|
||||
ObjectPtr condVal = eval(ifSt->condition);
|
||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"If condition must be boolean"), currentAddressInfo);
|
||||
}
|
||||
if (condVal->as<ValueType::BoolClass>())
|
||||
{
|
||||
return evalBlockStatement(ifSt->body);
|
||||
}
|
||||
// else
|
||||
for (const auto &elif : ifSt->elifs)
|
||||
{
|
||||
ObjectPtr elifCondVal = eval(elif->condition);
|
||||
if (elifCondVal->getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"Else-if condition must be boolean"), currentAddressInfo);
|
||||
}
|
||||
if (elifCondVal->as<ValueType::BoolClass>())
|
||||
{
|
||||
return evalBlockStatement(elif->body);
|
||||
}
|
||||
}
|
||||
if (ifSt->els)
|
||||
{
|
||||
return evalBlockStatement(ifSt->els->body);
|
||||
}
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::WhileSt: {
|
||||
auto whileSt = std::dynamic_pointer_cast<Ast::WhileSt>(stmt);
|
||||
while (true)
|
||||
{
|
||||
ObjectPtr condVal = eval(whileSt->condition);
|
||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"While condition must be boolean"), whileSt->condition->getAAI());
|
||||
}
|
||||
if (!condVal->as<ValueType::BoolClass>())
|
||||
{
|
||||
break;
|
||||
}
|
||||
ContextPtr loopContext = std::make_shared<Context>(
|
||||
FString(std::format("<While {}:{}>",
|
||||
whileSt->getAAI().line, whileSt->getAAI().column)),
|
||||
currentContext); // every loop has its own context
|
||||
StatementResult sr = evalBlockStatement(whileSt->body, loopContext);
|
||||
if (sr.shouldReturn())
|
||||
{
|
||||
return sr;
|
||||
}
|
||||
if (sr.shouldBreak())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sr.shouldContinue())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return StatementResult::normal();
|
||||
};
|
||||
case AstType::ForSt: {
|
||||
auto forSt = std::dynamic_pointer_cast<Ast::ForSt>(stmt);
|
||||
ContextPtr loopContext = std::make_shared<Context>(
|
||||
FString(std::format("<For {}:{}>",
|
||||
forSt->getAAI().line, forSt->getAAI().column)),
|
||||
currentContext); // for loop has its own context
|
||||
ContextPtr previousContext = currentContext;
|
||||
currentContext = loopContext;
|
||||
|
||||
evalStatement(forSt->initSt); // ignore init statement result
|
||||
size_t iteration = 0;
|
||||
|
||||
while (true) // use while loop to simulate for loop, cause we need to check condition type every iteration
|
||||
{
|
||||
ObjectPtr condVal = eval(forSt->condition);
|
||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
||||
{
|
||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"For condition must be boolean"), forSt->condition->getAAI());
|
||||
}
|
||||
if (!condVal->as<ValueType::BoolClass>())
|
||||
{
|
||||
break;
|
||||
}
|
||||
iteration++;
|
||||
ContextPtr iterationContext = std::make_shared<Context>(
|
||||
FString(std::format("<For {}:{}, Iteration {}>",
|
||||
forSt->getAAI().line, forSt->getAAI().column, iteration)),
|
||||
loopContext); // every loop has its own context
|
||||
StatementResult sr = evalBlockStatement(forSt->body, iterationContext);
|
||||
if (sr.shouldReturn())
|
||||
{
|
||||
currentContext = previousContext; // restore context before return
|
||||
return sr;
|
||||
}
|
||||
if (sr.shouldBreak())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sr.shouldContinue())
|
||||
{
|
||||
// continue to next iteration
|
||||
continue;
|
||||
}
|
||||
currentContext = loopContext; // let increment statement be in loop context
|
||||
evalStatement(forSt->incrementSt); // ignore increment statement result
|
||||
}
|
||||
currentContext = previousContext; // restore context
|
||||
return StatementResult::normal();
|
||||
}
|
||||
case AstType::ReturnSt: {
|
||||
if (!currentContext->parent)
|
||||
{
|
||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||
}
|
||||
if (!currentContext->isInFunctionContext())
|
||||
{
|
||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
||||
}
|
||||
auto returnSt = std::dynamic_pointer_cast<Ast::ReturnSt>(stmt);
|
||||
return StatementResult::returnFlow(eval(returnSt->retValue));
|
||||
};
|
||||
case AstType::BreakSt: {
|
||||
if (!currentContext->parent)
|
||||
{
|
||||
static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError";
|
||||
throw EvaluatorError<BreakOutsideLoopErrorName>(FStringView(u8"'break' statement outside loop"), currentAddressInfo);
|
||||
}
|
||||
if (!currentContext->isInLoopContext())
|
||||
{
|
||||
static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError";
|
||||
throw EvaluatorError<BreakOutsideLoopErrorName>(FStringView(u8"'break' statement outside loop"), currentAddressInfo);
|
||||
}
|
||||
return StatementResult::breakFlow();
|
||||
};
|
||||
case AstType::ContinueSt: {
|
||||
if (!currentContext->parent)
|
||||
{
|
||||
static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError";
|
||||
throw EvaluatorError<ContinueOutsideLoopErrorName>(FStringView(u8"'continue' statement outside loop"), currentAddressInfo);
|
||||
}
|
||||
if (!currentContext->isInLoopContext())
|
||||
{
|
||||
static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError";
|
||||
throw EvaluatorError<ContinueOutsideLoopErrorName>(FStringView(u8"'continue' statement outside loop"), currentAddressInfo);
|
||||
}
|
||||
return StatementResult::continueFlow();
|
||||
};
|
||||
default:
|
||||
throw RuntimeError(FStringView(std::string("Unknown statement type:") + magic_enum::enum_name(stmt->getType()).data()));
|
||||
}
|
||||
return StatementResult::normal();
|
||||
}
|
||||
|
||||
void Evaluator::run()
|
||||
{
|
||||
for (auto ast : asts)
|
||||
{
|
||||
currentAddressInfo = ast->getAAI();
|
||||
if (std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast))
|
||||
{
|
||||
auto exprAst = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast);
|
||||
Ast::Expression exp = exprAst->exp;
|
||||
eval(exp);
|
||||
}
|
||||
else if (dynamic_cast<Ast::StatementAst *>(ast.get()))
|
||||
{
|
||||
auto stmtAst = std::dynamic_pointer_cast<Ast::StatementAst>(ast);
|
||||
evalStatement(stmtAst);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw RuntimeError(FStringView(u8"Unknown AST type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Evaluator::printStackTrace() const
|
||||
{
|
||||
if (currentContext)
|
||||
currentContext->printStackTrace();
|
||||
else
|
||||
std::cerr << "[STACK TRACE] (No context has been loaded)\n";
|
||||
}
|
||||
} // namespace Fig
|
||||
24
src/main.cpp
24
src/main.cpp
@@ -27,16 +27,16 @@ Copyright (C) 2020-2025 PuqiAR
|
||||
This software is licensed under the MIT License. See LICENSE.txt for details.
|
||||
*/
|
||||
|
||||
#include <argparse/argparse.hpp>
|
||||
#include <Utils/argparse/argparse.hpp>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
|
||||
#include <core.hpp>
|
||||
#include <lexer.hpp>
|
||||
#include <parser.hpp>
|
||||
#include <evaluator.hpp>
|
||||
#include <AstPrinter.hpp>
|
||||
#include <errorLog.hpp>
|
||||
#include <Core/core.hpp>
|
||||
#include <Lexer/lexer.hpp>
|
||||
#include <Parser/parser.hpp>
|
||||
#include <Evaluator/evaluator.hpp>
|
||||
#include <Utils/AstPrinter.hpp>
|
||||
#include <Error/errorLog.hpp>
|
||||
|
||||
static size_t addressableErrorCount = 0;
|
||||
static size_t unaddressableErrorCount = 0;
|
||||
@@ -117,13 +117,13 @@ int main(int argc, char **argv)
|
||||
// }
|
||||
|
||||
Fig::Parser parser(lexer);
|
||||
std::vector<Fig::Ast::AstBase> ast;
|
||||
std::vector<Fig::Ast::AstBase> asts;
|
||||
|
||||
std::vector<FString> sourceLines = splitSource(Fig::FString(source));
|
||||
|
||||
try
|
||||
{
|
||||
ast = parser.parseAll();
|
||||
asts = parser.parseAll();
|
||||
}
|
||||
catch (const Fig::AddressableError &e)
|
||||
{
|
||||
@@ -150,10 +150,12 @@ int main(int argc, char **argv)
|
||||
// printer.print(node);
|
||||
// }
|
||||
|
||||
Fig::Evaluator evaluator(ast);
|
||||
Fig::Evaluator evaluator;
|
||||
evaluator.CreateGlobalContext();
|
||||
evaluator.RegisterBuiltins();
|
||||
try
|
||||
{
|
||||
evaluator.run();
|
||||
evaluator.Run(asts);
|
||||
}
|
||||
catch (const Fig::AddressableError &e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user