This commit is contained in:
2025-12-19 20:38:40 +08:00
commit 73c828d99b
83 changed files with 13068 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
#pragma once
namespace Fig
{
enum class AccessModifier
{
Normal,
Const,
Final,
Public,
PublicConst,
PublicFinal,
};
};

View 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

View 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

46
include/Ast/ControlSt.hpp Normal file
View 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>;
};

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

View File

@@ -0,0 +1,54 @@
// include/Ast/FunctionCall.hpp
#pragma once
#include <Ast/astBase.hpp>
#include <value.hpp>
namespace Fig::Ast
{
struct FunctionArguments
{
std::vector<Expression> argv;
size_t getLength() const { return argv.size(); }
};
struct FunctionCallArgs final
{
std::vector<Value> argv;
size_t getLength() const { return argv.size(); }
};
class FunctionCallExpr final : public ExpressionAst
{
public:
FString name;
FunctionArguments arg;
FunctionCallExpr()
{
type = AstType::FunctionCall;
}
FunctionCallExpr(FString _name, FunctionArguments _arg) :
name(std::move(_name)), arg(std::move(_arg))
{
type = AstType::FunctionCall;
}
virtual FString toString() override
{
FString s = name;
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

View File

@@ -0,0 +1,46 @@
#pragma once
#include <Ast/astBase.hpp>
#include <Ast/functionParameters.hpp>
#include <fig_string.hpp>
#include <value.hpp>
namespace Fig::Ast
{
/*
fun greet(greeting, name:String, age:Int, split:String=":") public -> Null
{
io.println("{}, {}{}{}", greeting, name, split, age);
}
`greeting`, `name`, `age` -> positional parameters
`split` -> default parameter
*/
class FunctionDefSt final : public StatementAst // for define
{
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
include/Ast/IfSt.hpp Normal file
View 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

View File

28
include/Ast/InitExpr.hpp Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <Ast/astBase.hpp>
namespace Fig::Ast
{
class InitExprAst final : public ExpressionAst
{
public:
FString structName;
std::vector<std::pair<FString, Expression>> args;
InitExprAst()
{
type = AstType::InitExpr;
}
InitExprAst(FString _structName, std::vector<std::pair<FString, Expression>> _args) :
structName(std::move(_structName)), args(std::move(_args))
{
type = AstType::InitExpr;
}
};
using InitExpr = std::shared_ptr<InitExprAst>;
}; // namespace Fig::Ast

View File

@@ -0,0 +1,43 @@
#pragma once
#include <Ast/astBase.hpp>
#include <Ast/functionParameters.hpp>
#include <Value/Type.hpp>
#include <fig_string.hpp>
namespace Fig::Ast
{
class LambdaExprAst : public ExpressionAst
{
public:
/*
Lambda:
fun (greeting) -> Null {}
*/
FunctionParameters paras;
FString retType;
BlockStatement body;
LambdaExprAst() :
retType(ValueType::Null.name)
{
type = AstType::LambdaExpr;
}
LambdaExprAst(FunctionParameters _paras, FString _retType, BlockStatement _body) :
retType(ValueType::Null.name)
{
paras = std::move(_paras);
retType = std::move(_retType);
body = std::move(_body);
}
virtual FString typeName() override
{
return FString(std::format("LambdaExprAst<{}>", retType.toBasicString()));
}
virtual ~LambdaExprAst() = default;
};
using LambdaExpr = std::shared_ptr<LambdaExprAst>;
}; // namespace Fig

View File

@@ -0,0 +1,45 @@
#pragma once
#include <Ast/astBase.hpp>
#include <fig_string.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

View 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
include/Ast/UnaryExpr.hpp Normal file
View 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
include/Ast/ValueExpr.hpp Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <Ast/astBase.hpp>
#include <value.hpp>
namespace Fig::Ast
{
class ValueExprAst final : public ExpressionAst
{
public:
Value val;
ValueExprAst()
{
type = AstType::ValueExpr;
}
ValueExprAst(Value _val)
{
type = AstType::ValueExpr;
val = std::move(_val);
}
};
using ValueExpr = std::shared_ptr<ValueExprAst>;
};

View File

@@ -0,0 +1,26 @@
#pragma once
#include <Ast/astBase.hpp>
namespace Fig::Ast
{
class VarAssignSt final : public StatementAst
{
public:
const FString varName;
const Expression valueExpr;
VarAssignSt()
{
type = AstType::VarAssignSt;
}
VarAssignSt(FString _varName, Expression _valueExpr) :
varName(std::move(_varName)), valueExpr(std::move(_valueExpr))
{
type = AstType::VarAssignSt;
}
};
using VarAssign = std::shared_ptr<VarAssignSt>;
}; // namespace Fig

34
include/Ast/VarDef.hpp Normal file
View 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

24
include/Ast/VarExpr.hpp Normal file
View 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

26
include/Ast/WhileSt.hpp Normal file
View 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>;
};

338
include/Ast/astBase.hpp Normal file
View File

@@ -0,0 +1,338 @@
#pragma once
#include <token.hpp>
#include <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,
FunctionCall,
LambdaExpr,
UnaryExpr,
BinaryExpr,
TernaryExpr,
ListExpr, // []
TupleExpr, // ()
MapExpr, // {}
InitExpr, // struct{}
/* Statement */
BlockStatement,
ExpressionStmt,
VarDefSt,
FunctionDefSt,
StructSt,
ImplementSt,
IfSt,
ElseSt,
ElseIfSt,
VarAssignSt,
WhileSt,
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 astTypeToString.at(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, // >>
// 赋值表达式
Walrus, // :=
// 点运算符 .
Dot,
};
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::Walrus, Operator::Walrus},
// 点运算符
{TokenType::Dot, Operator::Dot},
}; // :=
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:
const 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

View File

@@ -0,0 +1,39 @@
#pragma once
#include <Ast/astBase.hpp>
#include <Value/Type.hpp>
#include <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();
}
};
}

200
include/AstPrinter.hpp Normal file
View File

@@ -0,0 +1,200 @@
#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <ast.hpp>
#include <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::LambdaExpr:
printLambdaExpr(std::static_pointer_cast<LambdaExprAst>(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);
std::cout << "FuncName: ";
printFString(node->name, 0);
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 printLambdaExpr(const std::shared_ptr<LambdaExprAst> &node, int indent)
{
printIndent(indent);
std::cout << "LambdaExpr\n"
<< node->toString().toBasicString();
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);
}
};

190
include/Value/BaseValue.hpp Normal file
View File

@@ -0,0 +1,190 @@
#pragma once
#include <Value/Type.hpp>
#include <fig_string.hpp>
#include <memory>
#include <format>
namespace Fig
{
template <class T>
class __ValueWrapper
{
public:
const TypeInfo ti;
std::unique_ptr<T> data;
__ValueWrapper(const __ValueWrapper &other) :
ti(other.ti)
{
if (other.data)
data = std::make_unique<T>(*other.data);
}
__ValueWrapper(__ValueWrapper &&other) noexcept
:
ti(other.ti), data(std::move(other.data)) {}
__ValueWrapper &operator=(const __ValueWrapper &other)
{
if (this != &other)
{
if (other.data)
data = std::make_unique<T>(*other.data);
else
data.reset();
}
return *this;
}
__ValueWrapper &operator=(__ValueWrapper &&other) noexcept
{
if (this != &other)
{
data = std::move(other.data);
}
return *this;
}
const T &getValue() const
{
if (!data) throw std::runtime_error("Accessing null Value data");
return *data;
}
virtual size_t getSize() const
{
return sizeof(T);
}
virtual bool isNull() const
{
return !data;
}
virtual FString toString() const
{
if (!data)
return FString(std::format("<{} object (null)>", ti.name.toBasicString()));
return FString(std::format(
"<{} object @{:p}>",
ti.name.toBasicString(),
static_cast<const void *>(data.get())));
}
__ValueWrapper(const TypeInfo &_ti) :
ti(_ti) {}
__ValueWrapper(const T &x, const TypeInfo &_ti) :
ti(_ti)
{
data = std::make_unique<T>(x);
}
};
class Int final : public __ValueWrapper<ValueType::IntClass>
{
public:
Int(const ValueType::IntClass &x) :
__ValueWrapper(x, ValueType::Int) {}
Int(const Int &) = default;
Int(Int &&) noexcept = default;
Int &operator=(const Int &) = default;
Int &operator=(Int &&) noexcept = default;
bool operator==(const Int &other) const noexcept
{
return getValue() == other.getValue();
}
bool operator!=(const Int &other) const noexcept
{
return !(*this == other);
}
};
class Double final : public __ValueWrapper<ValueType::DoubleClass>
{
public:
Double(const ValueType::DoubleClass &x) :
__ValueWrapper(x, ValueType::Double) {}
Double(const Double &) = default;
Double(Double &&) noexcept = default;
Double &operator=(const Double &) = default;
Double &operator=(Double &&) noexcept = default;
bool operator==(const Double &other) const noexcept
{
return getValue() == other.getValue();
}
bool operator!=(const Double &other) const noexcept
{
return !(*this == other);
}
};
class Null final : public __ValueWrapper<ValueType::NullClass>
{
public:
Null() :
__ValueWrapper(ValueType::NullClass{}, ValueType::Null) {}
Null(const Null &) = default;
Null(Null &&) noexcept = default;
Null &operator=(const Null &) = default;
Null &operator=(Null &&) noexcept = default;
bool isNull() const override { return true; }
bool operator==(const Null &) const noexcept { return true; }
bool operator!=(const Null &) const noexcept { return false; }
};
class String final : public __ValueWrapper<ValueType::StringClass>
{
public:
String(const ValueType::StringClass &x) :
__ValueWrapper(x, ValueType::String) {}
String(const String &) = default;
String(String &&) noexcept = default;
String &operator=(const String &) = default;
String &operator=(String &&) noexcept = default;
bool operator==(const String &other) const noexcept
{
return getValue() == other.getValue();
}
bool operator!=(const String &other) const noexcept
{
return !(*this == other);
}
};
class Bool final : public __ValueWrapper<ValueType::BoolClass>
{
public:
Bool(const ValueType::BoolClass &x) :
__ValueWrapper(x, ValueType::Bool) {}
Bool(const Bool &) = default;
Bool(Bool &&) noexcept = default;
Bool &operator=(const Bool &) = default;
Bool &operator=(Bool &&) noexcept = default;
bool operator==(const Bool &other) const noexcept
{
return getValue() == other.getValue();
}
bool operator!=(const Bool &other) const noexcept
{
return !(*this == other);
}
};
} // namespace Fig

79
include/Value/Type.hpp Normal file
View File

@@ -0,0 +1,79 @@
#pragma once
#include <fig_string.hpp>
#include <variant>
#include <map>
#include <vector>
#include <list>
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;
/* complex types */
struct FunctionStruct;
using FunctionClass = FunctionStruct;
struct StructT;
using StructTypeClass = StructT;
struct StructInstanceT;
using StructInstanceClass = StructInstanceT;
}; // namespace ValueType
}; // namespace Fig

View File

@@ -0,0 +1,74 @@
#pragma once
#include <Value/BaseValue.hpp>
#include <Value/Type.hpp>
#include <Ast/functionParameters.hpp>
#include <atomic>
namespace Fig
{
/* complex objects */
struct FunctionStruct
{
std::size_t id;
Ast::FunctionParameters paras;
TypeInfo retType;
Ast::BlockStatement body;
FunctionStruct(Ast::FunctionParameters _paras, TypeInfo _retType, Ast::BlockStatement _body) :
id(nextId()), // 分配唯一 ID
paras(std::move(_paras)),
retType(std::move(_retType)),
body(std::move(_body))
{
}
FunctionStruct(const FunctionStruct &other) :
id(other.id), paras(other.paras), retType(other.retType), body(other.body) {}
FunctionStruct &operator=(const FunctionStruct &other) = default;
FunctionStruct(FunctionStruct &&) noexcept = default;
FunctionStruct &operator=(FunctionStruct &&) noexcept = default;
bool operator==(const FunctionStruct &other) const noexcept
{
return id == other.id;
}
bool operator!=(const FunctionStruct &other) const noexcept
{
return !(*this == other);
}
private:
static std::size_t nextId()
{
static std::atomic<std::size_t> counter{1};
return counter++;
}
};
class Function final : public __ValueWrapper<FunctionStruct>
{
public:
Function(const FunctionStruct &x) :
__ValueWrapper(ValueType::Function)
{
data = std::make_unique<FunctionStruct>(x);
}
Function(Ast::FunctionParameters paras, TypeInfo ret, Ast::BlockStatement body) :
__ValueWrapper(ValueType::Function)
{
data = std::make_unique<FunctionStruct>(
std::move(paras), std::move(ret), std::move(body));
}
bool operator==(const Function &other) const noexcept
{
if (!data || !other.data) return false;
return *data == *other.data; // call -> FunctionStruct::operator== (based on ID comparing)
}
Function(const Function &) = default;
Function(Function &&) noexcept = default;
Function &operator=(const Function &) = default;
Function &operator=(Function &&) noexcept = default;
};
} // namespace Fig

View File

@@ -0,0 +1,50 @@
#pragma once
#include <Value/BaseValue.hpp>
#include <context_forward.hpp>
namespace Fig
{
struct StructInstanceT final
{
FString structName; // 类的名字 (StructType), 非变量名。用于获取所属类
ContextPtr localContext;
StructInstanceT(FString _structName, ContextPtr _localContext) :
structName(std::move(_structName)), localContext(std::move(_localContext)) {}
StructInstanceT(const StructInstanceT &other) :
structName(other.structName), localContext(other.localContext) {}
StructInstanceT &operator=(const StructInstanceT &) = default;
StructInstanceT(StructInstanceT &&) = default;
StructInstanceT &operator=(StructInstanceT &&) = default;
bool operator==(const StructInstanceT &) const = default;
};
class StructInstance final : public __ValueWrapper<StructInstanceT>
{
public:
StructInstance(const StructInstanceT &x) :
__ValueWrapper(ValueType::StructInstance)
{
data = std::make_unique<StructInstanceT>(x);
}
StructInstance(FString _structName, ContextPtr _localContext) :
__ValueWrapper(ValueType::StructInstance)
{
data = std::make_unique<StructInstanceT>(std::move(_structName), std::move(_localContext));
}
bool operator==(const StructInstance &other) const noexcept
{
return data == other.data;
}
StructInstance(const StructInstance &) = default;
StructInstance(StructInstance &&) = default;
StructInstance &operator=(const StructInstance &) = default;
StructInstance &operator=(StructInstance &&) = default;
};
}; // namespace Fig

View File

@@ -0,0 +1,95 @@
#pragma once
#include <fig_string.hpp>
#include <Ast/StructDefSt.hpp>
#include <Value/Type.hpp>
#include <Value/BaseValue.hpp>
#include <context_forward.hpp>
#include <atomic>
namespace Fig
{
struct Field
{
bool isPublic() const
{
return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal;
}
bool isConst() const
{
return am == AccessModifier::Const or am == AccessModifier::PublicConst;
}
bool isFinal() const
{
return am == AccessModifier::Final or am == AccessModifier::PublicFinal;
}
AccessModifier am;
FString name;
TypeInfo type;
Ast::Expression defaultValue;
Field(AccessModifier _am, FString _name, TypeInfo _type, Ast::Expression _defaultValue) :
am(std::move(_am)), name(std::move(_name)), type(std::move(_type)), defaultValue(std::move(_defaultValue)) {}
};
struct StructT final// = StructType 结构体定义
{
std::size_t id;
ContextPtr defContext; // 定义时的上下文
std::vector<Field> fields;
StructT(ContextPtr _defContext, std::vector<Field> fieldsMap) :
defContext(std::move(_defContext)),
fields(std::move(fieldsMap))
{
id = nextId();
}
StructT(const StructT &other) :
id(other.id), fields(other.fields) {}
StructT &operator=(const StructT &other) = default;
StructT(StructT &&) noexcept = default;
StructT &operator=(StructT &&) noexcept = default;
bool operator==(const StructT &other) const noexcept
{
return id == other.id;
}
bool operator!=(const StructT &other) const noexcept
{
return !(*this == other);
}
private:
static std::size_t nextId()
{
static std::atomic<std::size_t> counter{1};
return counter++;
}
};
class StructType final : public __ValueWrapper<StructT>
{
public:
StructType(const StructT &x) :
__ValueWrapper(ValueType::StructType)
{
data = std::make_unique<StructT>(x);
}
StructType(ContextPtr _defContext, std::vector<Field> fieldsMap) :
__ValueWrapper(ValueType::StructType)
{
data = std::make_unique<StructT>(std::move(_defContext), std::move(fieldsMap));
}
bool operator==(const StructType &other) const noexcept
{
return data == other.data;
}
StructType(const StructType &) = default;
StructType(StructType &&) noexcept = default;
StructType &operator=(const StructType &) = default;
StructType &operator=(StructType &&) noexcept = default;
};
}; // namespace Fig

View File

@@ -0,0 +1,17 @@
#pragma once
#include <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);
}
};
};

File diff suppressed because it is too large Load Diff

23
include/ast.hpp Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <Ast/astBase.hpp>
#include <Ast/AccessModifier.hpp>
#include <Ast/BinaryExpr.hpp>
#include <Ast/ContainerInitExprs.hpp>
#include <Ast/ControlSt.hpp>
#include <Ast/ExpressionStmt.hpp>
#include <Ast/FunctionCall.hpp>
#include <Ast/functionParameters.hpp>
#include <Ast/FunctionDefSt.hpp>
#include <Ast/IfSt.hpp>
#include <Ast/ImplementSt.hpp>
#include <Ast/InitExpr.hpp>
#include <Ast/LambdaExpr.hpp>
#include <Ast/StructDefSt.hpp>
#include <Ast/TernaryExpr.hpp>
#include <Ast/UnaryExpr.hpp>
#include <Ast/ValueExpr.hpp>
#include <Ast/VarAssignSt.hpp>
#include <Ast/VarDef.hpp>
#include <Ast/VarExpr.hpp>
#include <Ast/WhileSt.hpp>

152
include/builtins.hpp Normal file
View File

@@ -0,0 +1,152 @@
#pragma once
#include <fig_string.hpp>
#include <value.hpp>
#include <unordered_map>
#include <functional>
#include <vector>
#include <print>
#include <iostream>
namespace Fig
{
namespace Builtins
{
const std::unordered_map<FString, Value> builtinValues = {
{u8"null", Value::getNullInstance()},
{u8"true", Value(true)},
{u8"false", Value(false)},
};
using BuiltinFunction = std::function<Value(const std::vector<Value> &)>;
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<Value> &args) -> Value {
for (auto arg : args)
{
std::print("{}", arg.toString().toBasicString());
}
return Value(Int(args.size()));
}},
{u8"__fstdout_println", [](const std::vector<Value> &args) -> Value {
for (auto arg : args)
{
std::print("{}", arg.toString().toBasicString());
}
std::print("\n");
return Value(Int(args.size()));
}},
{u8"__fstdin_read", [](const std::vector<Value> &args) -> Value {
std::string input;
std::cin >> input;
return Value(FString::fromBasicString(input));
}},
{u8"__fstdin_readln", [](const std::vector<Value> &args) -> Value {
std::string line;
std::getline(std::cin, line);
return Value(FString::fromBasicString(line));
}},
{u8"__fvalue_type", [](const std::vector<Value> &args) -> Value {
return Value(args[0].getTypeInfo().toString());
}},
{u8"__fvalue_int_parse", [](const std::vector<Value> &args) -> Value {
FString str = args[0].as<String>().getValue();
try
{
ValueType::IntClass val = std::stoi(str.toBasicString());
return Value(Int(val));
}
catch (...)
{
throw RuntimeError(FStringView(std::format("Invalid int string for parsing", str.toBasicString())));
}
}},
{u8"__fvalue_int_from", [](const std::vector<Value> &args) -> Value {
Value val = args[0];
if (val.is<Double>())
{
return Value(Int(static_cast<ValueType::IntClass>(val.as<Double>().getValue())));
}
else if (val.is<Bool>())
{
return Value(Int(val.as<Bool>().getValue() ? 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<Value> &args) -> Value {
FString str = args[0].as<String>().getValue();
try
{
ValueType::DoubleClass val = std::stod(str.toBasicString());
return Value(Double(val));
}
catch (...)
{
throw RuntimeError(FStringView(std::format("Invalid double string for parsing", str.toBasicString())));
}
}},
{u8"__fvalue_double_from", [](const std::vector<Value> &args) -> Value {
Value val = args[0];
if (val.is<Int>())
{
return Value(Double(static_cast<ValueType::DoubleClass>(val.as<Int>().getValue())));
}
else if (val.is<Bool>())
{
return Value(Double(val.as<Bool>().getValue() ? 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<Value> &args) -> Value {
Value val = args[0];
return Value(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

168
include/context.hpp Normal file
View File

@@ -0,0 +1,168 @@
#pragma once
#include <unordered_map>
#include <iostream>
#include <algorithm>
#include <context_forward.hpp>
#include <fig_string.hpp>
#include <value.hpp>
namespace Fig
{
struct Context
{
private:
FString scopeName;
std::unordered_map<FString, TypeInfo> varTypes;
std::unordered_map<FString, Value> variables;
std::unordered_map<FString, AccessModifier> ams;
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, Value> vars, std::unordered_map<FString, AccessModifier> _ams) :
scopeName(std::move(name)), varTypes(std::move(types)), variables(std::move(vars)), ams(std::move(_ams)) {}
void setParent(ContextPtr _parent)
{
parent = _parent;
}
void setScopeName(FString _name)
{
scopeName = std::move(_name);
}
FString getScopeName() const
{
return scopeName;
}
std::optional<Value> get(const FString &name)
{
auto it = variables.find(name);
if (it != variables.end())
return it->second;
if (parent)
return parent->get(name);
return std::nullopt;
}
AccessModifier getAccessModifier(const FString &name)
{
if (variables.contains(name))
{
return ams[name];
}
else if (parent != nullptr)
{
return parent->getAccessModifier(name);
}
else
{
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
}
}
ContextPtr createCopyWithPublicVariables()
{
std::unordered_map<FString, TypeInfo> _varTypes;
std::unordered_map<FString, Value> _variables;
std::unordered_map<FString, AccessModifier> _ams;
for (const auto &p : this->variables)
{
if (isVariablePublic(p.first))
{
_variables[p.first] = p.second;
_varTypes[p.first] = varTypes[p.first];
_ams[p.first] = ams[p.first];
}
}
return std::make_shared<Context>(this->scopeName, _varTypes, _variables, _ams);
}
bool isVariableMutable(const FString &name)
{
AccessModifier am = getAccessModifier(name); // may throw
return am == AccessModifier::Normal or am == AccessModifier::Public;
}
bool isVariablePublic(const FString &name)
{
AccessModifier am = getAccessModifier(name); // may throw
return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal;
}
void set(const FString &name, const Value &value)
{
if (variables.contains(name))
{
if (!isVariableMutable(name))
{
throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString())));
}
variables[name] = 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 Value &value = Any())
{
if (variables.contains(name))
{
throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
}
variables[name] = value;
varTypes[name] = ti;
ams[name] = am;
}
bool contains(const FString &name)
{
if (variables.contains(name))
{
return true;
}
else if (parent != nullptr)
{
return parent->contains(name);
}
return false;
}
TypeInfo getTypeInfo(const FString &name)
{
if (varTypes.contains(name))
{
return varTypes[name];
}
else if (parent != nullptr)
{
return parent->getTypeInfo(name);
}
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
}
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

View File

@@ -0,0 +1,9 @@
#pragma once
#include <memory>
namespace Fig
{
struct Context;
using ContextPtr = std::shared_ptr<Context>;
};

59
include/core.hpp Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <fig_string.hpp>
#include <cstdint>
#include <string_view>
#define __FCORE_VERSION "0.3.1"
#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

128
include/error.hpp Normal file
View File

@@ -0,0 +1,128 @@
#pragma once
#include <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
include/errorLog.hpp Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
#include <error.hpp>
#include <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

103
include/evaluator.hpp Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
#include <unordered_map>
#include <optional>
#include <error.hpp>
#include <fig_string.hpp>
#include <ast.hpp>
#include <value.hpp>
#include <context.hpp>
#include <parser.hpp>
namespace Fig
{
template <const char *errName>
class EvaluatorError final : public AddressableError
{
public:
virtual FString toString() const override
{
std::string msg = std::format("[Eve: {}] {} in [{}] {}", errName, std::string(this->message.begin(), this->message.end()), this->src_loc.file_name(), this->src_loc.function_name());
return FString(msg);
}
using AddressableError::AddressableError;
explicit EvaluatorError(FStringView _msg,
Ast::AstAddressInfo aai,
std::source_location loc = std::source_location::current()) :
AddressableError(_msg, aai.line, aai.column, loc)
{
}
};
struct StatementResult
{
Value result;
enum class Flow
{
Normal,
Return,
Break,
Continue
} flow;
StatementResult(Value val, Flow f = Flow::Normal) :
result(val), flow(f)
{
}
static StatementResult normal(Value val = Value::getNullInstance())
{
return StatementResult(val, Flow::Normal);
}
static StatementResult returnFlow(Value val)
{
return StatementResult(val, Flow::Return);
}
static StatementResult breakFlow()
{
return StatementResult(Value::getNullInstance(), Flow::Break);
}
static StatementResult continueFlow()
{
return StatementResult(Value::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:
std::vector<Ast::AstBase> asts;
std::shared_ptr<Context> globalContext;
std::shared_ptr<Context> currentContext;
Ast::AstAddressInfo currentAddressInfo;
public:
Evaluator(const std::vector<Ast::AstBase> &a) :
asts(a)
{
globalContext = std::make_shared<Context>(FString(u8"global"));
currentContext = globalContext;
}
std::shared_ptr<Context> getCurrentContext() { return currentContext; }
std::shared_ptr<Context> getGlobalContext() { return globalContext; }
Value __evalOp(Ast::Operator, const Value &, const Value & = Value::getNullInstance());
Value evalBinary(const Ast::BinaryExpr &);
Value evalUnary(const Ast::UnaryExpr &);
StatementResult evalStatement(const Ast::Statement &);
Value eval(Ast::Expression);
void run();
void printStackTrace() const;
};
} // namespace Fig

3
include/fassert.hpp Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
#define fassert(exp,msg) (if(!exp) throw msg;)

100
include/fig_string.hpp Normal file
View 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));
}
};
}

95
include/lexer.hpp Normal file
View File

@@ -0,0 +1,95 @@
#pragma once
#include <corecrt.h>
#include <cuchar>
#include <cwctype>
#include <unordered_map>
#include <vector>
#include <token.hpp>
#include <error.hpp>
#include <fig_string.hpp>
#include <utf8_iterator.hpp>
#include <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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

47
include/module.hpp Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include <memory>
#include <fig_string.hpp>
#include <value.hpp>
#include <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);
}
Value 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();
}
};
};

265
include/parser.hpp Normal file
View File

@@ -0,0 +1,265 @@
#pragma once
#include <ast.hpp>
#include <lexer.hpp>
#include <fig_string.hpp>
#include <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;
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 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 Token currentToken()
{
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(args...);
node.setAAI(currentAAI);
return std::shared_ptr<_Tp>(new _Tp(node));
}
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()))));
}
}
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)
Value __parseValue();
Ast::ValueExpr __parseValueExpr();
Ast::FunctionCall __parseFunctionCall(FString);
Ast::FunctionParameters __parseFunctionParameters(); // entry: current is Token::LeftParen
Ast::Statement __parseStatement(); // entry: (idk)
Ast::BlockStatement __parseBlockStatement(); // entry: current is Token::LeftBrace
Ast::VarAssign __parseVarAssign(FString); // entry: current is Token::Assign, para1 is var name
Ast::If __parseIf(); // entry: current is Token::If
Ast::While __parseWhile(); // entry: current is Token::While
Ast::Return __parseReturn(); // entry: current is Token::Return
Ast::VarExpr __parseVarExpr(FString);
Ast::LambdaExpr __parseLambdaExpr();
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::ListExpr __parseListExpr(); // entry: current is `[`
Ast::Expression __parseTupleOrParenExpr(); // 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 parseExpression(Precedence, TokenType = TokenType::Semicolon, TokenType = TokenType::Semicolon);
std::vector<Ast::AstBase> parseAll();
};
}; // namespace Fig

167
include/token.hpp Normal file
View File

@@ -0,0 +1,167 @@
#pragma once
#include <cstdint>
#include <format>
#include <magic_enum/magic_enum.hpp>
#include <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, // fun
Variable, // var
Const, // const
Final, // final
While, // while
For, // for
If, // if
Else, // else
Struct, // struct
Interface, // interface
Implement, // implement
Public, // public
Return, // return
// 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;
}
Token setPos(size_t _line, size_t _column)
{
line = _line;
column = _column;
return *this;
}
FString getValue()
{
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()
{
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

260
include/utf8_iterator.hpp Normal file
View 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

109
include/utils.hpp Normal file
View File

@@ -0,0 +1,109 @@
#pragma once
#pragma once
#include <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

373
include/value.hpp Normal file
View File

@@ -0,0 +1,373 @@
#pragma once
#include <Value/BaseValue.hpp>
#include <Value/valueError.hpp>
#include <Value/function.hpp>
#include <Value/structType.hpp>
#include <Value/structInstance.hpp>
#include <Value/Type.hpp>
#include <variant>
#include <cmath>
#include <string>
#include <format>
namespace Fig
{
class Value
{
public:
using VariantType = std::variant<Null, Int, Double, String, Bool, Function, StructType, StructInstance>;
VariantType data;
Value() :
data(Null{}) {}
Value(const Null &n) :
data(std::in_place_type<Null>, n) {}
Value(const Int &i) :
data(std::in_place_type<Int>, i) {}
Value(const Double &d) :
data(std::in_place_type<Double>, d)
{
ValueType::IntClass casted = static_cast<ValueType::IntClass>(d.getValue());
if (casted == d.getValue())
{
data.emplace<Int>(casted);
}
}
Value(const String &s) :
data(std::in_place_type<String>, s) {}
Value(const Bool &b) :
data(std::in_place_type<Bool>, b) {}
Value(const Function &f) :
data(std::in_place_type<Function>, f) {}
Value(const StructType &s) :
data(std::in_place_type<StructType>, s) {}
Value(const StructInstance &s) :
data(std::in_place_type<StructInstance>, s) {}
template <typename T,
typename = std::enable_if_t<
std::is_same_v<T, ValueType::IntClass>
|| std::is_same_v<T, ValueType::DoubleClass>
|| std::is_same_v<T, ValueType::StringClass>
|| std::is_same_v<T, ValueType::BoolClass>
|| std::is_same_v<T, ValueType::FunctionClass>
|| std::is_same_v<T, ValueType::StructTypeClass>>>
Value(const T &val)
{
// 不可以用 data = 的形式
// __ValueWrapper 构造、拷贝有限制
if constexpr (std::is_same_v<T, ValueType::IntClass>)
data.emplace<Int>(val);
else if constexpr (std::is_same_v<T, ValueType::DoubleClass>)
{
ValueType::IntClass casted = static_cast<ValueType::IntClass>(val);
if (casted == val)
{
data.emplace<Int>(casted);
}
else
{
data.emplace<Double>(val);
}
}
else if constexpr (std::is_same_v<T, ValueType::StringClass>)
data.emplace<String>(val);
else if constexpr (std::is_same_v<T, ValueType::BoolClass>)
data.emplace<Bool>(val);
else if constexpr (std::is_same_v<T, ValueType::FunctionClass>)
data.emplace<Function>(val);
else if constexpr (std::is_same_v<T, ValueType::StructTypeClass>)
data.emplace<StructType>(val);
else if constexpr (std::is_same_v<T, ValueType::StructInstanceClass>)
data.emplace<StructInstance>(val);
}
Value(const Value &) = default;
Value(Value &&) noexcept = default;
Value &operator=(const Value &) = default;
Value &operator=(Value &&) noexcept = default;
static Value defaultValue(TypeInfo ti)
{
if (ti == ValueType::Int)
return Value(Int(0));
else if (ti == ValueType::Double)
return Value(Double(0.0));
else if (ti == ValueType::String)
return Value(String(u8""));
else if (ti == ValueType::Bool)
return Value(Bool(false));
else if (ti == ValueType::Function)
return getNullInstance();
else if (ti == ValueType::StructType)
return getNullInstance();
else if (ti == ValueType::StructInstance)
return getNullInstance();
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 Value getNullInstance()
{
static Value v(Null{});
return v;
}
TypeInfo getTypeInfo() const
{
return std::visit([](auto &&val) { return val.ti; }, data);
}
bool isNull() const { return is<Null>(); }
bool isNumeric() const { return is<Int>() || is<Double>(); }
ValueType::DoubleClass getNumericValue() const
{
if (is<Int>())
return static_cast<ValueType::DoubleClass>(as<Int>().getValue());
else if (is<Double>())
return as<Double>().getValue();
else
throw RuntimeError(u8"getNumericValue: Not a numeric value");
}
FString toString() const
{
if (is<Null>()) return FString(u8"null");
if (is<Int>()) return FString(std::to_string(as<Int>().getValue()));
if (is<Double>()) return FString(std::to_string(as<Double>().getValue()));
if (is<String>()) return as<String>().getValue();
if (is<Bool>()) return as<Bool>().getValue() ? FString(u8"true") : FString(u8"false");
if (is<Function>())
{
return FString(std::format("<Function {} at {:p}>",
as<Function>().getValue().id,
static_cast<const void *>(as<Function>().data.get())));
}
if (is<StructType>())
{
return FString(std::format("<StructType {} at {:p}>",
as<StructType>().getValue().id,
static_cast<const void *>(as<StructType>().data.get())));
}
if (is<StructInstance>())
{
return FString(std::format("<Struct Instance('{}') at {:p}",
as<StructInstance>().getValue().structName.toBasicString(),
static_cast<const void *>(as<StructInstance>().data.get())));
}
return FString(u8"<error>");
}
private:
static std::string makeTypeErrorMessage(const char *prefix, const char *op,
const Value &lhs, const Value &rhs)
{
auto lhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, lhs.data);
auto rhs_type = std::visit([](auto &&v) { return v.ti.name.toBasicString(); }, rhs.data);
return std::format("{}: {} '{}' {}", prefix, lhs_type, op, rhs_type);
}
public:
// math
friend Value operator+(const Value &lhs, const Value &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot add", "+", lhs, rhs)));
if (lhs.isNumeric() and rhs.isNumeric())
return lhs.getNumericValue() + rhs.getNumericValue();
if (lhs.is<String>() && rhs.is<String>())
return Value(ValueType::StringClass(lhs.as<String>().getValue() + rhs.as<String>().getValue()));
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "+", lhs, rhs)));
}
friend Value operator-(const Value &lhs, const Value &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot subtract", "-", lhs, rhs)));
if (lhs.isNumeric() and rhs.isNumeric())
return lhs.getNumericValue() - rhs.getNumericValue();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "-", lhs, rhs)));
}
friend Value operator*(const Value &lhs, const Value &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot multiply", "*", lhs, rhs)));
if (lhs.isNumeric() and rhs.isNumeric())
return lhs.getNumericValue() * rhs.getNumericValue();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "*", lhs, rhs)));
}
friend Value operator/(const Value &lhs, const Value &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot divide", "/", lhs, rhs)));
if (lhs.isNumeric() and rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Division by zero", "/", lhs, rhs)));
return lhs.getNumericValue() / rnv;
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "/", lhs, rhs)));
}
friend Value operator%(const Value &lhs, const Value &rhs)
{
if (lhs.isNull() || rhs.isNull())
throw ValueError(FStringView(makeTypeErrorMessage("Cannot modulo", "%", lhs, rhs)));
if (lhs.isNumeric() and rhs.isNumeric())
{
auto rnv = rhs.getNumericValue();
if (rnv == 0) throw ValueError(FStringView(makeTypeErrorMessage("Modulo by zero", "%", lhs, rhs)));
return fmod(lhs.getNumericValue(), rhs.getNumericValue());
}
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported operation", "%", lhs, rhs)));
}
// logic
friend Value operator&&(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Bool>() || !rhs.is<Bool>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical AND requires bool", "&&", lhs, rhs)));
return Value(lhs.as<Bool>().getValue() && rhs.as<Bool>().getValue());
}
friend Value operator||(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Bool>() || !rhs.is<Bool>())
throw ValueError(FStringView(makeTypeErrorMessage("Logical OR requires bool", "||", lhs, rhs)));
return Value(lhs.as<Bool>().getValue() || rhs.as<Bool>().getValue());
}
friend Value operator!(const Value &v)
{
if (!v.is<Bool>())
throw ValueError(FStringView(std::format("Logical NOT requires bool: '{}'",
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
return Value(!v.as<Bool>().getValue());
}
friend Value operator-(const Value &v)
{
if (v.isNull())
throw ValueError(FStringView(std::format("Unary minus cannot be applied to null")));
if (v.is<Int>())
return Value(-v.as<Int>().getValue());
if (v.is<Double>())
return Value(-v.as<Double>().getValue());
throw ValueError(FStringView(std::format("Unary minus requires int or double: '{}'",
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
}
friend Value operator~(const Value &v)
{
if (!v.is<Int>())
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'",
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
return Value(~v.as<Int>().getValue());
}
// compare → now returns bool
friend bool operator==(const Value &lhs, const Value &rhs)
{
return lhs.data == rhs.data;
}
friend bool operator!=(const Value &lhs, const Value &rhs)
{
return !(lhs.data == rhs.data);
}
friend bool operator<(const Value &lhs, const Value &rhs)
{
if (lhs.isNumeric() and rhs.isNumeric())
return lhs.getNumericValue() < rhs.getNumericValue();
if (lhs.is<String>() && rhs.is<String>()) return lhs.as<String>().getValue() < rhs.as<String>().getValue();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", "<", lhs, rhs)));
}
friend bool operator<=(const Value &lhs, const Value &rhs)
{
return lhs == rhs or lhs < rhs;
}
friend bool operator>(const Value &lhs, const Value &rhs)
{
if (lhs.isNumeric() and rhs.isNumeric())
return lhs.getNumericValue() > rhs.getNumericValue();
if (lhs.is<String>() && rhs.is<String>()) return lhs.as<String>().getValue() > rhs.as<String>().getValue();
throw ValueError(FStringView(makeTypeErrorMessage("Unsupported comparison", ">", lhs, rhs)));
}
friend bool operator>=(const Value &lhs, const Value &rhs)
{
return lhs == rhs or lhs > rhs;
}
// bitwise
friend Value bit_and(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Int>() || !rhs.is<Int>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise AND requires int", "&", lhs, rhs)));
return Value(lhs.as<Int>().getValue() & rhs.as<Int>().getValue());
}
friend Value bit_or(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Int>() || !rhs.is<Int>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise OR requires int", "|", lhs, rhs)));
return Value(lhs.as<Int>().getValue() | rhs.as<Int>().getValue());
}
friend Value bit_xor(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Int>() || !rhs.is<Int>())
throw ValueError(FStringView(makeTypeErrorMessage("Bitwise XOR requires int", "^", lhs, rhs)));
return Value(lhs.as<Int>().getValue() ^ rhs.as<Int>().getValue());
}
friend Value bit_not(const Value &v)
{
if (!v.is<Int>())
throw ValueError(FStringView(std::format("Bitwise NOT requires int: '{}'",
std::visit([](auto &&val) { return val.ti.name.toBasicString(); }, v.data))));
return Value(~v.as<Int>().getValue());
}
friend Value shift_left(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Int>() || !rhs.is<Int>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift left requires int", "<<", lhs, rhs)));
return Value(lhs.as<Int>().getValue() << rhs.as<Int>().getValue());
}
friend Value shift_right(const Value &lhs, const Value &rhs)
{
if (!lhs.is<Int>() || !rhs.is<Int>())
throw ValueError(FStringView(makeTypeErrorMessage("Shift right requires int", ">>", lhs, rhs)));
return Value(lhs.as<Int>().getValue() >> rhs.as<Int>().getValue());
}
};
using Any = Value;
} // namespace Fig

55
include/warning.hpp Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include <fig_string.hpp>
#include <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;
}
};
};