[Feat] 详细区分左值(LvObject)与右值(RvObject -> ObjectPtr)
[Impl] 重构evaluator.cpp + hpp 全部 [Feat] 增加对于IndexExpr的解析 [Fix][Impl] 现在点运算符不由BinaryExpr负责,增加MemberExpr,单独实现解析 [Impl] 项目目录全部翻修, src/目录下单独文件夹放置每一个模块
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
enum class AccessModifier
|
|
||||||
{
|
|
||||||
Normal,
|
|
||||||
Const,
|
|
||||||
Final,
|
|
||||||
Public,
|
|
||||||
PublicConst,
|
|
||||||
PublicFinal,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#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/ForSt.hpp>
|
|
||||||
#include <Ast/FunctionCall.hpp>
|
|
||||||
#include <Ast/functionParameters.hpp>
|
|
||||||
#include <Ast/FunctionDefSt.hpp>
|
|
||||||
#include <Ast/FunctionLiteralExpr.hpp>
|
|
||||||
#include <Ast/IfSt.hpp>
|
|
||||||
#include <Ast/ImplementSt.hpp>
|
|
||||||
#include <Ast/InitExpr.hpp>
|
|
||||||
#include <Ast/StructDefSt.hpp>
|
|
||||||
#include <Ast/TernaryExpr.hpp>
|
|
||||||
#include <Ast/UnaryExpr.hpp>
|
|
||||||
#include <Ast/ValueExpr.hpp>
|
|
||||||
#include <Ast/VarDef.hpp>
|
|
||||||
#include <Ast/VarExpr.hpp>
|
|
||||||
#include <Ast/WhileSt.hpp>
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <builtins.hpp>
|
|
||||||
#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:
|
|
||||||
const char* errorName = errName;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual FString getErrorType() const override
|
|
||||||
{
|
|
||||||
return FString("[Eval]" + std::string(errorName));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct StatementResult
|
|
||||||
{
|
|
||||||
ObjectPtr result;
|
|
||||||
enum class Flow
|
|
||||||
{
|
|
||||||
Normal,
|
|
||||||
Return,
|
|
||||||
Break,
|
|
||||||
Continue
|
|
||||||
} flow;
|
|
||||||
|
|
||||||
StatementResult(ObjectPtr val, Flow f = Flow::Normal) :
|
|
||||||
result(val), flow(f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static StatementResult normal(ObjectPtr val = Object::getNullInstance())
|
|
||||||
{
|
|
||||||
return StatementResult(val, Flow::Normal);
|
|
||||||
}
|
|
||||||
static StatementResult returnFlow(ObjectPtr val)
|
|
||||||
{
|
|
||||||
return StatementResult(val, Flow::Return);
|
|
||||||
}
|
|
||||||
static StatementResult breakFlow()
|
|
||||||
{
|
|
||||||
return StatementResult(Object::getNullInstance(), Flow::Break);
|
|
||||||
}
|
|
||||||
static StatementResult continueFlow()
|
|
||||||
{
|
|
||||||
return StatementResult(Object::getNullInstance(), Flow::Continue);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNormal() const { return flow == Flow::Normal; }
|
|
||||||
bool shouldReturn() const { return flow == Flow::Return; }
|
|
||||||
bool shouldBreak() const { return flow == Flow::Break; }
|
|
||||||
bool shouldContinue() const { return flow == Flow::Continue; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Evaluator
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
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;
|
|
||||||
|
|
||||||
for (auto &[name, fn] : Builtins::builtinFunctions)
|
|
||||||
{
|
|
||||||
int argc = Builtins::getBuiltinFunctionParamCount(name);
|
|
||||||
Function f(fn, argc);
|
|
||||||
globalContext->def(
|
|
||||||
name,
|
|
||||||
ValueType::Function,
|
|
||||||
AccessModifier::PublicConst,
|
|
||||||
std::make_shared<Object>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &[name, val] : Builtins::builtinValues)
|
|
||||||
{
|
|
||||||
globalContext->def(
|
|
||||||
name,
|
|
||||||
val->getTypeInfo(),
|
|
||||||
AccessModifier::PublicConst,
|
|
||||||
val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Context> getCurrentContext() { return currentContext; }
|
|
||||||
std::shared_ptr<Context> getGlobalContext() { return globalContext; }
|
|
||||||
|
|
||||||
ObjectPtr __evalOp(Ast::Operator, const ObjectPtr &, const ObjectPtr & = Object::getNullInstance());
|
|
||||||
ObjectPtr evalBinary(const Ast::BinaryExpr &);
|
|
||||||
ObjectPtr evalUnary(const Ast::UnaryExpr &);
|
|
||||||
|
|
||||||
StatementResult evalBlockStatement(const Ast::BlockStatement &, ContextPtr = nullptr);
|
|
||||||
StatementResult evalStatement(const Ast::Statement &);
|
|
||||||
|
|
||||||
ObjectPtr evalFunctionCall(const Function &, const Ast::FunctionArguments &, FString fnName = u8"<anonymous>");
|
|
||||||
|
|
||||||
ObjectPtr eval(Ast::Expression);
|
|
||||||
void run();
|
|
||||||
void printStackTrace() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Fig
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
23
src/Ast/AccessModifier.hpp
Normal file
23
src/Ast/AccessModifier.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
enum class AccessModifier : uint8_t
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Const,
|
||||||
|
Public,
|
||||||
|
PublicConst,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isAccessPublic(AccessModifier am)
|
||||||
|
{
|
||||||
|
return am == AccessModifier::Public || am == AccessModifier::PublicConst;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isAccessConst(AccessModifier am)
|
||||||
|
{
|
||||||
|
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
|
||||||
namespace Fig::Ast
|
namespace Fig::Ast
|
||||||
{
|
{
|
||||||
@@ -21,7 +21,7 @@ namespace Fig::Ast
|
|||||||
class FunctionCallExpr final : public ExpressionAst
|
class FunctionCallExpr final : public ExpressionAst
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Expression callee; // 不是 name 了!!
|
Expression callee;
|
||||||
FunctionArguments arg;
|
FunctionArguments arg;
|
||||||
|
|
||||||
FunctionCallExpr()
|
FunctionCallExpr()
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
#include <Ast/functionParameters.hpp>
|
#include <Ast/functionParameters.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
namespace Fig::Ast
|
namespace Fig::Ast
|
||||||
49
src/Ast/Expressions/PostfixExprs.hpp
Normal file
49
src/Ast/Expressions/PostfixExprs.hpp
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig::Ast
|
||||||
|
{
|
||||||
|
// actually, function call is postfix, too
|
||||||
|
// but it's too long, so use a single file (FunctionCall.hpp)
|
||||||
|
|
||||||
|
class MemberExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression base;
|
||||||
|
FString member;
|
||||||
|
|
||||||
|
MemberExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::MemberExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberExprAst(Expression _base, FString _member) :
|
||||||
|
base(std::move(_base)), member(std::move(_member))
|
||||||
|
{
|
||||||
|
type = AstType::MemberExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using MemberExpr = std::shared_ptr<MemberExprAst>;
|
||||||
|
|
||||||
|
class IndexExprAst final : public ExpressionAst
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Expression base;
|
||||||
|
Expression index;
|
||||||
|
|
||||||
|
IndexExprAst()
|
||||||
|
{
|
||||||
|
type = AstType::IndexExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexExprAst(Expression _base, Expression _index) :
|
||||||
|
base(std::move(_base)), index(std::move(_index))
|
||||||
|
{
|
||||||
|
type = AstType::IndexExpr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using IndexExpr = std::shared_ptr<IndexExprAst>;
|
||||||
|
}; // namespace Fig::Ast
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
|
||||||
namespace Fig::Ast
|
namespace Fig::Ast
|
||||||
{
|
{
|
||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
#include <Ast/functionParameters.hpp>
|
#include <Ast/functionParameters.hpp>
|
||||||
#include <fig_string.hpp>
|
|
||||||
|
|
||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
|
||||||
namespace Fig::Ast
|
namespace Fig::Ast
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
#include <fig_string.hpp>
|
|
||||||
#include <Ast/AccessModifier.hpp>
|
#include <Ast/AccessModifier.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
26
src/Ast/ast.hpp
Normal file
26
src/Ast/ast.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
#include <Ast/AccessModifier.hpp>
|
||||||
|
#include <Ast/functionParameters.hpp>
|
||||||
|
|
||||||
|
#include <Ast/Expressions/BinaryExpr.hpp>
|
||||||
|
#include <Ast/Expressions/ContainerInitExprs.hpp>
|
||||||
|
#include <Ast/Expressions/FunctionCall.hpp>
|
||||||
|
#include <Ast/Expressions/FunctionLiteralExpr.hpp>
|
||||||
|
#include <Ast/Expressions/InitExpr.hpp>
|
||||||
|
#include <Ast/Expressions/PostfixExprs.hpp>
|
||||||
|
#include <Ast/Expressions/TernaryExpr.hpp>
|
||||||
|
#include <Ast/Expressions/UnaryExpr.hpp>
|
||||||
|
#include <Ast/Expressions/ValueExpr.hpp>
|
||||||
|
#include <Ast/Expressions/VarExpr.hpp>
|
||||||
|
|
||||||
|
#include <Ast/Statements/VarDef.hpp>
|
||||||
|
#include <Ast/Statements/WhileSt.hpp>
|
||||||
|
#include <Ast/Statements/StructDefSt.hpp>
|
||||||
|
#include <Ast/Statements/IfSt.hpp>
|
||||||
|
#include <Ast/Statements/ImplementSt.hpp>
|
||||||
|
#include <Ast/Statements/FunctionDefSt.hpp>
|
||||||
|
#include <Ast/Statements/ControlSt.hpp>
|
||||||
|
#include <Ast/Statements/ExpressionStmt.hpp>
|
||||||
|
#include <Ast/Statements/ForSt.hpp>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <token.hpp>
|
#include <Token/token.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -20,15 +20,21 @@ namespace Fig::Ast
|
|||||||
/* Expression */
|
/* Expression */
|
||||||
ValueExpr,
|
ValueExpr,
|
||||||
VarExpr,
|
VarExpr,
|
||||||
FunctionCall,
|
|
||||||
LambdaExpr,
|
LambdaExpr,
|
||||||
UnaryExpr,
|
UnaryExpr,
|
||||||
BinaryExpr,
|
BinaryExpr,
|
||||||
TernaryExpr,
|
TernaryExpr,
|
||||||
ListExpr, // []
|
|
||||||
TupleExpr, // ()
|
/* Postfix */
|
||||||
MapExpr, // {}
|
MemberExpr, // a.b
|
||||||
InitExpr, // struct{}
|
IndexExpr, // a[b]
|
||||||
|
FunctionCall, // a()
|
||||||
|
|
||||||
|
/* Literals */
|
||||||
|
ListExpr, // [1, "2", 3
|
||||||
|
TupleExpr, // (1, 2, 3)
|
||||||
|
MapExpr, // {a: 1}
|
||||||
|
InitExpr, // struct{"123", 456}
|
||||||
FunctionLiteralExpr,
|
FunctionLiteralExpr,
|
||||||
|
|
||||||
/* Statement */
|
/* Statement */
|
||||||
@@ -63,7 +69,7 @@ namespace Fig::Ast
|
|||||||
// {AstType::UnaryExpr, FString(u8"UnaryExpr")},
|
// {AstType::UnaryExpr, FString(u8"UnaryExpr")},
|
||||||
// {AstType::BinaryExpr, FString(u8"BinaryExpr")},
|
// {AstType::BinaryExpr, FString(u8"BinaryExpr")},
|
||||||
// {AstType::TernaryExpr, FString(u8"TernaryExpr")},
|
// {AstType::TernaryExpr, FString(u8"TernaryExpr")},
|
||||||
|
|
||||||
// {AstType::InitExpr, FString(u8"InitExpr")},
|
// {AstType::InitExpr, FString(u8"InitExpr")},
|
||||||
|
|
||||||
// /* Statement */
|
// /* Statement */
|
||||||
@@ -112,8 +118,7 @@ namespace Fig::Ast
|
|||||||
virtual FString typeName()
|
virtual FString typeName()
|
||||||
{
|
{
|
||||||
return FString::fromStringView(
|
return FString::fromStringView(
|
||||||
FStringView::fromBasicStringView(magic_enum::enum_name(type))
|
FStringView::fromBasicStringView(magic_enum::enum_name(type)));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
virtual FString toString()
|
virtual FString toString()
|
||||||
{
|
{
|
||||||
@@ -216,9 +221,6 @@ namespace Fig::Ast
|
|||||||
// 赋值表达式
|
// 赋值表达式
|
||||||
Assign, // =
|
Assign, // =
|
||||||
// Walrus, // :=
|
// Walrus, // :=
|
||||||
|
|
||||||
// 点运算符 .
|
|
||||||
Dot,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_set<Operator> unaryOps{
|
static const std::unordered_set<Operator> unaryOps{
|
||||||
@@ -251,7 +253,8 @@ namespace Fig::Ast
|
|||||||
Operator::ShiftRight,
|
Operator::ShiftRight,
|
||||||
|
|
||||||
// Operator::Walrus,
|
// Operator::Walrus,
|
||||||
Operator::Dot};
|
// Operator::Dot
|
||||||
|
};
|
||||||
static const std::unordered_set<Operator> ternaryOps{Operator::TernaryCond};
|
static const std::unordered_set<Operator> ternaryOps{Operator::TernaryCond};
|
||||||
|
|
||||||
static const std::unordered_map<TokenType, Operator> TokenToOp{
|
static const std::unordered_map<TokenType, Operator> TokenToOp{
|
||||||
@@ -291,9 +294,7 @@ namespace Fig::Ast
|
|||||||
|
|
||||||
// 赋值表达式
|
// 赋值表达式
|
||||||
{TokenType::Assign, Operator::Assign},
|
{TokenType::Assign, Operator::Assign},
|
||||||
// {TokenType::Walrus, Operator::Walrus},
|
// {TokenType::Walrus, Operator::Walrus},
|
||||||
// 点运算符
|
|
||||||
{TokenType::Dot, Operator::Dot},
|
|
||||||
}; // :=
|
}; // :=
|
||||||
|
|
||||||
inline bool isOpUnary(Operator op)
|
inline bool isOpUnary(Operator op)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include <Ast/astBase.hpp>
|
#include <Ast/astBase.hpp>
|
||||||
#include <Value/Type.hpp>
|
#include <Value/Type.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
namespace Fig::Ast
|
namespace Fig::Ast
|
||||||
{
|
{
|
||||||
@@ -4,32 +4,31 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <context_forward.hpp>
|
#include <Context/context_forward.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
|
|
||||||
class Context : public std::enable_shared_from_this<Context>
|
class Context : public std::enable_shared_from_this<Context>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
FString scopeName;
|
FString scopeName;
|
||||||
std::unordered_map<FString, TypeInfo> varTypes;
|
std::unordered_map<FString, std::shared_ptr<VariableSlot>> variables;
|
||||||
std::unordered_map<FString, ObjectPtr> variables;
|
|
||||||
std::unordered_map<FString, AccessModifier> ams;
|
|
||||||
|
|
||||||
std::unordered_map<std::size_t, Function> functions;
|
std::unordered_map<std::size_t, Function> functions;
|
||||||
std::unordered_map<std::size_t, FString> functionNames;
|
std::unordered_map<std::size_t, FString> functionNames;
|
||||||
|
|
||||||
std::unordered_map<std::size_t, FString> structTypeNames;
|
std::unordered_map<std::size_t, FString> structTypeNames;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContextPtr parent;
|
ContextPtr parent;
|
||||||
|
|
||||||
Context(const Context &) = default;
|
Context(const Context &) = default;
|
||||||
Context(const FString &name, ContextPtr p = nullptr) :
|
Context(const FString &name, ContextPtr p = nullptr) :
|
||||||
scopeName(name), parent(p) {}
|
scopeName(name), parent(p) {}
|
||||||
Context(const FString &name, std::unordered_map<FString, TypeInfo> types, std::unordered_map<FString, ObjectPtr> vars, std::unordered_map<FString, AccessModifier> _ams) :
|
Context(const FString &name, std::unordered_map<FString, TypeInfo> types, std::unordered_map<FString, std::shared_ptr<VariableSlot>> vars, std::unordered_map<FString, AccessModifier> _ams) :
|
||||||
scopeName(std::move(name)), varTypes(std::move(types)), variables(std::move(vars)), ams(std::move(_ams)) {}
|
scopeName(std::move(name)), variables(std::move(vars)) {}
|
||||||
|
|
||||||
void setParent(ContextPtr _parent)
|
void setParent(ContextPtr _parent)
|
||||||
{
|
{
|
||||||
@@ -46,11 +45,9 @@ namespace Fig
|
|||||||
return scopeName;
|
return scopeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void merge(const Context& c)
|
void merge(const Context &c)
|
||||||
{
|
{
|
||||||
varTypes.insert(c.varTypes.begin(), c.varTypes.end());
|
|
||||||
variables.insert(c.variables.begin(), c.variables.end());
|
variables.insert(c.variables.begin(), c.variables.end());
|
||||||
ams.insert(c.ams.begin(), c.ams.end());
|
|
||||||
functions.insert(c.functions.begin(), c.functions.end());
|
functions.insert(c.functions.begin(), c.functions.end());
|
||||||
functionNames.insert(c.functionNames.begin(), c.functionNames.end());
|
functionNames.insert(c.functionNames.begin(), c.functionNames.end());
|
||||||
structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end());
|
structTypeNames.insert(c.structTypeNames.begin(), c.structTypeNames.end());
|
||||||
@@ -61,20 +58,20 @@ namespace Fig
|
|||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ObjectPtr> get(const FString &name)
|
std::shared_ptr<VariableSlot> get(const FString &name)
|
||||||
{
|
{
|
||||||
auto it = variables.find(name);
|
auto it = variables.find(name);
|
||||||
if (it != variables.end())
|
if (it != variables.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
if (parent)
|
if (parent)
|
||||||
return parent->get(name);
|
return parent->get(name);
|
||||||
return std::nullopt;
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
}
|
}
|
||||||
AccessModifier getAccessModifier(const FString &name)
|
AccessModifier getAccessModifier(const FString &name)
|
||||||
{
|
{
|
||||||
if (variables.contains(name))
|
if (variables.contains(name))
|
||||||
{
|
{
|
||||||
return ams[name];
|
return variables[name]->am;
|
||||||
}
|
}
|
||||||
else if (parent != nullptr)
|
else if (parent != nullptr)
|
||||||
{
|
{
|
||||||
@@ -85,33 +82,17 @@ namespace Fig
|
|||||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContextPtr createCopyWithPublicVariables()
|
|
||||||
{
|
|
||||||
std::unordered_map<FString, TypeInfo> _varTypes;
|
|
||||||
std::unordered_map<FString, ObjectPtr> _variables;
|
|
||||||
std::unordered_map<FString, AccessModifier> _ams;
|
|
||||||
for (const auto &p : this->variables)
|
|
||||||
{
|
|
||||||
if (isVariablePublic(p.first))
|
|
||||||
{
|
|
||||||
_variables[p.first] = std::make_shared<Object>(*p.second); // copy
|
|
||||||
_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)
|
bool isVariableMutable(const FString &name)
|
||||||
{
|
{
|
||||||
AccessModifier am = getAccessModifier(name); // may throw
|
AccessModifier am = getAccessModifier(name); // may throw
|
||||||
return am == AccessModifier::Normal or am == AccessModifier::Public;
|
return !isAccessConst(am);
|
||||||
}
|
}
|
||||||
bool isVariablePublic(const FString &name)
|
bool isVariablePublic(const FString &name)
|
||||||
{
|
{
|
||||||
AccessModifier am = getAccessModifier(name); // may throw
|
AccessModifier am = getAccessModifier(name); // may throw
|
||||||
return am == AccessModifier::Public or am == AccessModifier::PublicConst or am == AccessModifier::PublicFinal;
|
return isAccessPublic(am);
|
||||||
}
|
}
|
||||||
void set(const FString &name, const Object &value)
|
void set(const FString &name, ObjectPtr value)
|
||||||
{
|
{
|
||||||
if (variables.contains(name))
|
if (variables.contains(name))
|
||||||
{
|
{
|
||||||
@@ -119,7 +100,7 @@ namespace Fig
|
|||||||
{
|
{
|
||||||
throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString())));
|
throw RuntimeError(FStringView(std::format("Variable '{}' is immutable", name.toBasicString())));
|
||||||
}
|
}
|
||||||
variables[name] = std::make_shared<Object>(value);
|
variables[name]->value = value;
|
||||||
}
|
}
|
||||||
else if (parent != nullptr)
|
else if (parent != nullptr)
|
||||||
{
|
{
|
||||||
@@ -130,11 +111,11 @@ namespace Fig
|
|||||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void _update(const FString &name, const Object &value)
|
void _update(const FString &name, ObjectPtr value)
|
||||||
{
|
{
|
||||||
if (variables.contains(name))
|
if (variables.contains(name))
|
||||||
{
|
{
|
||||||
variables[name] = std::make_shared<Object>(value);
|
variables[name]->value = value;
|
||||||
}
|
}
|
||||||
else if (parent != nullptr)
|
else if (parent != nullptr)
|
||||||
{
|
{
|
||||||
@@ -151,10 +132,11 @@ namespace Fig
|
|||||||
{
|
{
|
||||||
throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
throw RuntimeError(FStringView(std::format("Variable '{}' already defined in this scope", name.toBasicString())));
|
||||||
}
|
}
|
||||||
variables[name] = std::make_shared<Object>(*value);
|
variables[name] = std::make_shared<VariableSlot>(
|
||||||
varTypes[name] = ti;
|
name,
|
||||||
ams[name] = am;
|
value,
|
||||||
|
ti,
|
||||||
|
am);
|
||||||
if (ti == ValueType::Function and value->getTypeInfo() == ValueType::Function)
|
if (ti == ValueType::Function and value->getTypeInfo() == ValueType::Function)
|
||||||
{
|
{
|
||||||
auto &fn = value->as<Function>();
|
auto &fn = value->as<Function>();
|
||||||
@@ -234,15 +216,7 @@ namespace Fig
|
|||||||
|
|
||||||
TypeInfo getTypeInfo(const FString &name)
|
TypeInfo getTypeInfo(const FString &name)
|
||||||
{
|
{
|
||||||
if (varTypes.contains(name))
|
return get(name)->declaredType;
|
||||||
{
|
|
||||||
return varTypes[name];
|
|
||||||
}
|
|
||||||
else if (parent != nullptr)
|
|
||||||
{
|
|
||||||
return parent->getTypeInfo(name);
|
|
||||||
}
|
|
||||||
throw RuntimeError(FStringView(std::format("Variable '{}' not defined", name.toBasicString())));
|
|
||||||
}
|
}
|
||||||
bool isInFunctionContext()
|
bool isInFunctionContext()
|
||||||
{
|
{
|
||||||
@@ -262,8 +236,7 @@ namespace Fig
|
|||||||
ContextPtr ctx = shared_from_this();
|
ContextPtr ctx = shared_from_this();
|
||||||
while (ctx)
|
while (ctx)
|
||||||
{
|
{
|
||||||
if (ctx->getScopeName().find(u8"<While ") == 0 or
|
if (ctx->getScopeName().find(u8"<While ") == 0 or ctx->getScopeName().find(u8"<For ") == 0)
|
||||||
ctx->getScopeName().find(u8"<For ") == 0)
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <warning.hpp>
|
#include <Core/warning.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <Utils/magic_enum/magic_enum.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <error.hpp>
|
#include <Error/error.hpp>
|
||||||
#include <core.hpp>
|
#include <Core/core.hpp>
|
||||||
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
1029
src/Evaluator/evaluator.cpp
Normal file
1029
src/Evaluator/evaluator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
116
src/Evaluator/evaluator.hpp
Normal file
116
src/Evaluator/evaluator.hpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include <Ast/ast.hpp>
|
||||||
|
|
||||||
|
#include <Context/context.hpp>
|
||||||
|
#include <Error/error.hpp>
|
||||||
|
#include <Module/builtins.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
struct StatementResult
|
||||||
|
{
|
||||||
|
ObjectPtr result;
|
||||||
|
enum class Flow
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Return,
|
||||||
|
Break,
|
||||||
|
Continue
|
||||||
|
} flow;
|
||||||
|
|
||||||
|
StatementResult(ObjectPtr val, Flow f = Flow::Normal) :
|
||||||
|
result(val), flow(f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static StatementResult normal(ObjectPtr val = Object::getNullInstance())
|
||||||
|
{
|
||||||
|
return StatementResult(val, Flow::Normal);
|
||||||
|
}
|
||||||
|
static StatementResult returnFlow(ObjectPtr val)
|
||||||
|
{
|
||||||
|
return StatementResult(val, Flow::Return);
|
||||||
|
}
|
||||||
|
static StatementResult breakFlow()
|
||||||
|
{
|
||||||
|
return StatementResult(Object::getNullInstance(), Flow::Break);
|
||||||
|
}
|
||||||
|
static StatementResult continueFlow()
|
||||||
|
{
|
||||||
|
return StatementResult(Object::getNullInstance(), Flow::Continue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNormal() const { return flow == Flow::Normal; }
|
||||||
|
bool shouldReturn() const { return flow == Flow::Return; }
|
||||||
|
bool shouldBreak() const { return flow == Flow::Break; }
|
||||||
|
bool shouldContinue() const { return flow == Flow::Continue; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Evaluator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
ContextPtr global;
|
||||||
|
public:
|
||||||
|
|
||||||
|
void SetGlobalContext(ContextPtr ctx)
|
||||||
|
{
|
||||||
|
assert(ctx != nullptr);
|
||||||
|
global = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateGlobalContext()
|
||||||
|
{
|
||||||
|
global = std::make_shared<Context>(
|
||||||
|
FString(u8"<Global>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterBuiltins()
|
||||||
|
{
|
||||||
|
assert(global != nullptr);
|
||||||
|
|
||||||
|
for (auto &[name, fn] : Builtins::builtinFunctions)
|
||||||
|
{
|
||||||
|
int argc = Builtins::getBuiltinFunctionParamCount(name);
|
||||||
|
Function f(fn, argc);
|
||||||
|
global->def(
|
||||||
|
name,
|
||||||
|
ValueType::Function,
|
||||||
|
AccessModifier::Const,
|
||||||
|
std::make_shared<Object>(f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &[name, val] : Builtins::builtinValues)
|
||||||
|
{
|
||||||
|
global->def(
|
||||||
|
name,
|
||||||
|
val->getTypeInfo(),
|
||||||
|
AccessModifier::Const,
|
||||||
|
val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Left-value eval*/
|
||||||
|
LvObject evalVarExpr(Ast::VarExpr, ContextPtr);
|
||||||
|
LvObject evalMemberExpr(Ast::MemberExpr, ContextPtr); // a.b
|
||||||
|
LvObject evalIndexExpr(Ast::IndexExpr, ContextPtr); // a[b]
|
||||||
|
|
||||||
|
LvObject evalLv(Ast::Expression, ContextPtr); // for access: a.b / index a[b]
|
||||||
|
|
||||||
|
/* Right-value eval*/
|
||||||
|
|
||||||
|
RvObject evalBinary(Ast::BinaryExpr, ContextPtr); // normal binary expr: +, -, *....
|
||||||
|
RvObject evalUnary(Ast::UnaryExpr, ContextPtr); // unary expr
|
||||||
|
RvObject evalTernary(Ast::TernaryExpr, ContextPtr); // ternary expr
|
||||||
|
|
||||||
|
RvObject evalFunctionCall(const Function&, const Ast::FunctionArguments&, const FString& ,ContextPtr); // function call
|
||||||
|
RvObject eval(Ast::Expression, ContextPtr);
|
||||||
|
|
||||||
|
StatementResult evalBlockStatement(Ast::BlockStatement, ContextPtr); // block
|
||||||
|
StatementResult evalStatement(Ast::Statement, ContextPtr); // statement
|
||||||
|
|
||||||
|
StatementResult Run(std::vector<Ast::AstBase>); // Entry
|
||||||
|
|
||||||
|
void printStackTrace();
|
||||||
|
};
|
||||||
|
}; // namespace Fig
|
||||||
42
src/Evaluator/evaluator_error.hpp
Normal file
42
src/Evaluator/evaluator_error.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Error/error.hpp>
|
||||||
|
#include <Ast/astBase.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
class EvaluatorError final : public AddressableError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FString typeName;
|
||||||
|
using AddressableError::AddressableError;
|
||||||
|
EvaluatorError(FString _typeName, FString msg, Ast::AstBase ast, std::source_location loc = std::source_location::current())
|
||||||
|
{
|
||||||
|
message = FStringView::fromBasicStringView(msg.toBasicString());
|
||||||
|
line = ast->getAAI().line;
|
||||||
|
column = ast->getAAI().column;
|
||||||
|
|
||||||
|
src_loc = std::move(loc);
|
||||||
|
|
||||||
|
typeName = std::move(_typeName);
|
||||||
|
|
||||||
|
}
|
||||||
|
EvaluatorError(FString _typeName, std::string_view msg, Ast::AstBase ast, std::source_location loc = std::source_location::current())
|
||||||
|
{
|
||||||
|
message = FStringView::fromBasicStringView(msg);
|
||||||
|
line = ast->getAAI().line;
|
||||||
|
column = ast->getAAI().column;
|
||||||
|
|
||||||
|
src_loc = std::move(loc);
|
||||||
|
|
||||||
|
typeName = std::move(_typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual FString getErrorType() const override
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <error.hpp>
|
#include <Error/error.hpp>
|
||||||
#include <token.hpp>
|
#include <Token/token.hpp>
|
||||||
#include <lexer.hpp>
|
#include <Lexer/lexer.hpp>
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <utils.hpp>
|
#include <Utils/utils.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -66,7 +66,7 @@ namespace Fig
|
|||||||
{FString(u8"func"), TokenType::Function},
|
{FString(u8"func"), TokenType::Function},
|
||||||
{FString(u8"var"), TokenType::Variable},
|
{FString(u8"var"), TokenType::Variable},
|
||||||
{FString(u8"const"), TokenType::Const},
|
{FString(u8"const"), TokenType::Const},
|
||||||
{FString(u8"final"), TokenType::Final},
|
// {FString(u8"final"), TokenType::Final},
|
||||||
{FString(u8"while"), TokenType::While},
|
{FString(u8"while"), TokenType::While},
|
||||||
{FString(u8"for"), TokenType::For},
|
{FString(u8"for"), TokenType::For},
|
||||||
{FString(u8"if"), TokenType::If},
|
{FString(u8"if"), TokenType::If},
|
||||||
@@ -6,11 +6,11 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <token.hpp>
|
#include <Token/token.hpp>
|
||||||
#include <error.hpp>
|
#include <Error/error.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <utf8_iterator.hpp>
|
#include <Core/utf8_iterator.hpp>
|
||||||
#include <warning.hpp>
|
#include <Core/warning.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
47
src/Module/module.hpp
Normal file
47
src/Module/module.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <Core/fig_string.hpp>
|
||||||
|
#include <Value/value.hpp>
|
||||||
|
#include <Context/context.hpp>
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
// class Module
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// const FString name;
|
||||||
|
// const FString spec;
|
||||||
|
// const FString path;
|
||||||
|
|
||||||
|
// std::shared_ptr<Context> context; // module-level context
|
||||||
|
|
||||||
|
// /*
|
||||||
|
|
||||||
|
// import module -> automatically create a module context and call function `init` if exists
|
||||||
|
// all global functions, variables, structs, etc will be stored in module context
|
||||||
|
// then module context will be linked to the current context
|
||||||
|
|
||||||
|
// */
|
||||||
|
// Module(const FString &moduleName, const FString &moduleSpec, const FString &modulePath) :
|
||||||
|
// name(moduleName), spec(moduleSpec), path(modulePath)
|
||||||
|
// {
|
||||||
|
// context = std::make_shared<Context>(FString(std::format("<Module {}>", name.toBasicString())), nullptr);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// bool hasSymbol(const FString &symbolName)
|
||||||
|
// {
|
||||||
|
// return context->contains(symbolName);
|
||||||
|
// }
|
||||||
|
// Object getSymbol(const FString &symbolName)
|
||||||
|
// {
|
||||||
|
// auto valOpt = context->get(symbolName);
|
||||||
|
// if (!valOpt.has_value())
|
||||||
|
// {
|
||||||
|
// throw RuntimeError(FStringView(std::format("Symbol '{}' not found in module '{}'", symbolName.toBasicString(), name.toBasicString())));
|
||||||
|
// }
|
||||||
|
// return valOpt.value();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <parser.hpp>
|
#include <Parser/parser.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -39,8 +39,8 @@ namespace Fig
|
|||||||
// 海象运算符
|
// 海象运算符
|
||||||
// {Ast::Operator::Walrus, {2, 1}}, // 右结合
|
// {Ast::Operator::Walrus, {2, 1}}, // 右结合
|
||||||
|
|
||||||
// 点运算符
|
// // 点运算符
|
||||||
{Ast::Operator::Dot, {40, 41}},
|
// {Ast::Operator::Dot, {40, 41}},
|
||||||
};
|
};
|
||||||
|
|
||||||
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
Ast::VarDef Parser::__parseVarDef(bool isPublic)
|
||||||
@@ -244,13 +244,6 @@ namespace Fig
|
|||||||
next();
|
next();
|
||||||
am = (isPublic ? AccessModifier::Public : AccessModifier::Normal);
|
am = (isPublic ? AccessModifier::Public : AccessModifier::Normal);
|
||||||
}
|
}
|
||||||
else if (isThis(TokenType::Final))
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
expect(TokenType::Identifier, u8"field name");
|
|
||||||
fieldName = currentToken().getValue();
|
|
||||||
am = (isPublic ? AccessModifier::PublicFinal : AccessModifier::Final);
|
|
||||||
}
|
|
||||||
else if (isThis(TokenType::Const))
|
else if (isThis(TokenType::Const))
|
||||||
{
|
{
|
||||||
next();
|
next();
|
||||||
@@ -297,7 +290,7 @@ namespace Fig
|
|||||||
}
|
}
|
||||||
else if (isThis(TokenType::Public))
|
else if (isThis(TokenType::Public))
|
||||||
{
|
{
|
||||||
if (isNext(TokenType::Const) or isNext(TokenType::Final))
|
if (isNext(TokenType::Const))
|
||||||
{
|
{
|
||||||
next();
|
next();
|
||||||
fields.push_back(__parseStructField(true));
|
fields.push_back(__parseStructField(true));
|
||||||
@@ -334,7 +327,7 @@ namespace Fig
|
|||||||
next(); // consume `struct`
|
next(); // consume `struct`
|
||||||
stmts.push_back(__parseStructDef(false));
|
stmts.push_back(__parseStructDef(false));
|
||||||
}
|
}
|
||||||
else if (isThis(TokenType::Const) or isThis(TokenType::Final))
|
else if (isThis(TokenType::Const))
|
||||||
{
|
{
|
||||||
fields.push_back(__parseStructField(false));
|
fields.push_back(__parseStructField(false));
|
||||||
}
|
}
|
||||||
@@ -865,12 +858,40 @@ namespace Fig
|
|||||||
tok = currentToken();
|
tok = currentToken();
|
||||||
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
|
if (tok.getType() == TokenType::Semicolon || tok == EOFTok) break;
|
||||||
|
|
||||||
|
/* Postfix */
|
||||||
|
|
||||||
if (tok.getType() == TokenType::LeftParen)
|
if (tok.getType() == TokenType::LeftParen)
|
||||||
{
|
{
|
||||||
lhs = __parseCall(lhs);
|
lhs = __parseCall(lhs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// member access: a.b
|
||||||
|
if (tok.getType() == TokenType::Dot)
|
||||||
|
{
|
||||||
|
next(); // consume '.'
|
||||||
|
Token idTok = currentToken();
|
||||||
|
if (!idTok.isIdentifier())
|
||||||
|
throwAddressableError<SyntaxError>(FStringView(u8"Expected identifier after '.'"));
|
||||||
|
|
||||||
|
FString member = idTok.getValue();
|
||||||
|
next(); // consume identifier
|
||||||
|
|
||||||
|
lhs = makeAst<Ast::MemberExprAst>(lhs, member);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// index: x[expr]
|
||||||
|
if (tok.getType() == TokenType::LeftBracket)
|
||||||
|
{
|
||||||
|
next(); // consume '['
|
||||||
|
auto indexExpr = parseExpression(0, TokenType::RightBracket);
|
||||||
|
expect(TokenType::RightBracket);
|
||||||
|
next(); // consume ']'
|
||||||
|
|
||||||
|
lhs = makeAst<Ast::IndexExprAst>(lhs, indexExpr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// ternary
|
// ternary
|
||||||
if (tok.getType() == TokenType::Question)
|
if (tok.getType() == TokenType::Question)
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ast.hpp>
|
#include <Ast/ast.hpp>
|
||||||
#include <lexer.hpp>
|
#include <Lexer/lexer.hpp>
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <error.hpp>
|
#include <Error/error.hpp>
|
||||||
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <Utils/magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,7 @@ namespace Fig
|
|||||||
Function, // func
|
Function, // func
|
||||||
Variable, // var
|
Variable, // var
|
||||||
Const, // const
|
Const, // const
|
||||||
Final, // final
|
// Final, // final
|
||||||
While, // while
|
While, // while
|
||||||
For, // for
|
For, // for
|
||||||
If, // if
|
If, // if
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ast.hpp>
|
#include <Ast/ast.hpp>
|
||||||
#include <magic_enum/magic_enum.hpp>
|
#include <Utils/magic_enum/magic_enum.hpp>
|
||||||
|
|
||||||
using namespace Fig;
|
using namespace Fig;
|
||||||
using namespace Fig::Ast;
|
using namespace Fig::Ast;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <cwctype>
|
#include <cwctype>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <map>
|
#include <map>
|
||||||
6
src/Value/containers.hpp
Normal file
6
src/Value/containers.hpp
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Fig
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Ast/functionParameters.hpp>
|
#include <Ast/functionParameters.hpp>
|
||||||
#include <context_forward.hpp>
|
#include <Context/context_forward.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <context_forward.hpp>
|
#include <Context/context_forward.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fig_string.hpp>
|
#include <Core/fig_string.hpp>
|
||||||
#include <Ast/StructDefSt.hpp>
|
#include <Ast/Statements/StructDefSt.hpp>
|
||||||
|
|
||||||
#include <Value/Type.hpp>
|
#include <Value/Type.hpp>
|
||||||
|
|
||||||
#include <context_forward.hpp>
|
#include <Context/context_forward.hpp>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -23,16 +23,13 @@ namespace Fig
|
|||||||
|
|
||||||
bool isPublic() const
|
bool isPublic() const
|
||||||
{
|
{
|
||||||
return am == AccessModifier::Public || am == AccessModifier::PublicConst || am == AccessModifier::PublicFinal;
|
return am == AccessModifier::Public || am == AccessModifier::PublicConst;
|
||||||
}
|
}
|
||||||
bool isConst() const
|
bool isConst() const
|
||||||
{
|
{
|
||||||
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
return am == AccessModifier::Const || am == AccessModifier::PublicConst;
|
||||||
}
|
}
|
||||||
bool isFinal() const
|
|
||||||
{
|
|
||||||
return am == AccessModifier::Final || am == AccessModifier::PublicFinal;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StructType
|
struct StructType
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <value.hpp>
|
#include <Value/value.hpp>
|
||||||
|
#include <Context/context.hpp>
|
||||||
|
|
||||||
// #include <iostream>
|
// #include <iostream>
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ namespace Fig
|
|||||||
id = typeMap.at(name); // may throw
|
id = typeMap.at(name); // may throw
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
const TypeInfo ValueType::Any(FString(u8"Any"), true); // id: 1
|
||||||
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
|
const TypeInfo ValueType::Null(FString(u8"Null"), true); // id: 2
|
||||||
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
|
const TypeInfo ValueType::Int(FString(u8"Int"), true); // id: 3
|
||||||
@@ -19,8 +19,11 @@ namespace Fig
|
|||||||
|
|
||||||
inline bool isNumberExceededIntLimit(ValueType::DoubleClass d)
|
inline bool isNumberExceededIntLimit(ValueType::DoubleClass d)
|
||||||
{
|
{
|
||||||
static constexpr ValueType::DoubleClass intMaxAsDouble = static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::max());
|
static constexpr auto intMaxAsDouble =
|
||||||
static constexpr ValueType::DoubleClass intMinAsDouble = static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
|
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::max());
|
||||||
|
|
||||||
|
static constexpr auto intMinAsDouble =
|
||||||
|
static_cast<ValueType::DoubleClass>(std::numeric_limits<ValueType::IntClass>::min());
|
||||||
return d > intMaxAsDouble || d < intMinAsDouble;
|
return d > intMaxAsDouble || d < intMinAsDouble;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,13 +48,9 @@ namespace Fig
|
|||||||
data(n) {}
|
data(n) {}
|
||||||
Object(const ValueType::IntClass &i) :
|
Object(const ValueType::IntClass &i) :
|
||||||
data(i) {}
|
data(i) {}
|
||||||
Object(const ValueType::DoubleClass &d)
|
explicit Object(const ValueType::DoubleClass &d) :
|
||||||
|
data(d)
|
||||||
{
|
{
|
||||||
ValueType::IntClass casted = static_cast<ValueType::IntClass>(d);
|
|
||||||
if (casted == d)
|
|
||||||
data = casted;
|
|
||||||
else
|
|
||||||
data = d;
|
|
||||||
}
|
}
|
||||||
Object(const ValueType::StringClass &s) :
|
Object(const ValueType::StringClass &s) :
|
||||||
data(s) {}
|
data(s) {}
|
||||||
@@ -392,5 +391,63 @@ namespace Fig
|
|||||||
};
|
};
|
||||||
|
|
||||||
using ObjectPtr = std::shared_ptr<Object>;
|
using ObjectPtr = std::shared_ptr<Object>;
|
||||||
|
using RvObject = ObjectPtr;
|
||||||
|
|
||||||
|
struct VariableSlot
|
||||||
|
{
|
||||||
|
FString name;
|
||||||
|
ObjectPtr value;
|
||||||
|
TypeInfo declaredType;
|
||||||
|
AccessModifier am;
|
||||||
|
|
||||||
|
bool isRef = false;
|
||||||
|
std::shared_ptr<VariableSlot> refTarget;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LvObject
|
||||||
|
{
|
||||||
|
std::shared_ptr<VariableSlot> slot;
|
||||||
|
|
||||||
|
const ObjectPtr& get() const
|
||||||
|
{
|
||||||
|
auto s = resolve(slot);
|
||||||
|
return s->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const ObjectPtr& v)
|
||||||
|
{
|
||||||
|
auto s = resolve(slot);
|
||||||
|
if (s->declaredType != ValueType::Any && s->declaredType != v->getTypeInfo())
|
||||||
|
{
|
||||||
|
throw RuntimeError(
|
||||||
|
FStringView(
|
||||||
|
std::format("Variable `{}` expects type `{}`, but got '{}'",
|
||||||
|
s->name.toBasicString(),
|
||||||
|
s->declaredType.toString().toBasicString(),
|
||||||
|
v->getTypeInfo().toString().toBasicString())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isAccessConst(s->am))
|
||||||
|
{
|
||||||
|
throw RuntimeError(FStringView(
|
||||||
|
std::format("Variable `{}` is immutable", s->name.toBasicString())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
s->value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString name() const { return resolve(slot)->name; }
|
||||||
|
TypeInfo declaredType() const { return resolve(slot)->declaredType; }
|
||||||
|
AccessModifier access() const { return resolve(slot)->am; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<VariableSlot> resolve(std::shared_ptr<VariableSlot> s) const
|
||||||
|
{
|
||||||
|
while (s->isRef) s = s->refTarget;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fig
|
} // namespace Fig
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <error.hpp>
|
#include <Error/error.hpp>
|
||||||
|
|
||||||
namespace Fig
|
namespace Fig
|
||||||
{
|
{
|
||||||
@@ -1,854 +0,0 @@
|
|||||||
#include <evaluator.hpp>
|
|
||||||
#include <builtins.hpp>
|
|
||||||
#include <utils.hpp>
|
|
||||||
|
|
||||||
namespace Fig
|
|
||||||
{
|
|
||||||
ObjectPtr Evaluator::__evalOp(Ast::Operator op, const ObjectPtr &lhs, const ObjectPtr &rhs)
|
|
||||||
{
|
|
||||||
using Fig::Ast::Operator;
|
|
||||||
switch (op)
|
|
||||||
{
|
|
||||||
case Operator::Add: return std::make_shared<Object>(*lhs + *rhs);
|
|
||||||
case Operator::Subtract: return std::make_shared<Object>(*lhs - *rhs);
|
|
||||||
case Operator::Multiply: return std::make_shared<Object>((*lhs) * (*rhs));
|
|
||||||
case Operator::Divide: return std::make_shared<Object>(*lhs / *rhs);
|
|
||||||
case Operator::Modulo: return std::make_shared<Object>(*lhs % *rhs);
|
|
||||||
case Operator::Power: return std::make_shared<Object>(power(*lhs, *rhs));
|
|
||||||
|
|
||||||
case Operator::And: return std::make_shared<Object>(*lhs && *rhs);
|
|
||||||
case Operator::Or: return std::make_shared<Object>(*lhs || *rhs);
|
|
||||||
case Operator::Not: return std::make_shared<Object>(!*lhs);
|
|
||||||
|
|
||||||
case Operator::Equal: return std::make_shared<Object>(*lhs == *rhs);
|
|
||||||
case Operator::NotEqual: return std::make_shared<Object>(*lhs != *rhs);
|
|
||||||
case Operator::Less: return std::make_shared<Object>(*lhs < *rhs);
|
|
||||||
case Operator::LessEqual: return std::make_shared<Object>(*lhs <= *rhs);
|
|
||||||
case Operator::Greater: return std::make_shared<Object>(*lhs > *rhs);
|
|
||||||
case Operator::GreaterEqual: return std::make_shared<Object>(*lhs >= *rhs);
|
|
||||||
|
|
||||||
case Operator::BitAnd: return std::make_shared<Object>(bit_and(*lhs, *rhs));
|
|
||||||
case Operator::BitOr: return std::make_shared<Object>(bit_or(*lhs, *rhs));
|
|
||||||
case Operator::BitXor: return std::make_shared<Object>(bit_xor(*lhs, *rhs));
|
|
||||||
case Operator::BitNot: return std::make_shared<Object>(bit_not(*lhs));
|
|
||||||
case Operator::ShiftLeft: return std::make_shared<Object>(shift_left(*lhs, *rhs));
|
|
||||||
case Operator::ShiftRight: return std::make_shared<Object>(shift_right(*lhs, *rhs));
|
|
||||||
|
|
||||||
case Operator::Assign: {
|
|
||||||
*lhs = *rhs;
|
|
||||||
return Object::getNullInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
// case Operator::Walrus: {
|
|
||||||
// static constexpr char WalrusErrorName[] = "WalrusError";
|
|
||||||
// throw EvaluatorError<WalrusErrorName>(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now
|
|
||||||
// }
|
|
||||||
default:
|
|
||||||
throw RuntimeError(FStringView(u8"Unsupported operator"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectPtr Evaluator::evalBinary(const Ast::BinaryExpr &binExp)
|
|
||||||
{
|
|
||||||
if (binExp->op == Ast::Operator::Dot)
|
|
||||||
{
|
|
||||||
const ObjectPtr &lhs = eval(binExp->lexp);
|
|
||||||
if (!lhs->is<StructInstance>())
|
|
||||||
{
|
|
||||||
static constexpr char AccessOpObjectNotStructError[] = "AccessOpObjectNotStructError";
|
|
||||||
throw EvaluatorError<AccessOpObjectNotStructError>(FStringView(
|
|
||||||
std::format("Object not a struct")),
|
|
||||||
binExp->lexp->getAAI());
|
|
||||||
}
|
|
||||||
const StructInstance &st = lhs->as<StructInstance>();
|
|
||||||
Ast::VarExpr varExp;
|
|
||||||
Ast::FunctionCall fnCall;
|
|
||||||
if ((varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(binExp->rexp)))
|
|
||||||
{
|
|
||||||
FString member = varExp->name;
|
|
||||||
auto structTypeNameOpt = currentContext->getStructName(st.parentId);
|
|
||||||
if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name"));
|
|
||||||
FString structTypeName = *structTypeNameOpt;
|
|
||||||
if (!st.localContext->containsInThisScope(member))
|
|
||||||
{
|
|
||||||
static constexpr char NoAttributeError[] = "NoAttributeError";
|
|
||||||
throw EvaluatorError<NoAttributeError>(FStringView(
|
|
||||||
std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), member.toBasicString())),
|
|
||||||
binExp->rexp->getAAI());
|
|
||||||
}
|
|
||||||
if (!st.localContext->isVariablePublic(member))
|
|
||||||
{
|
|
||||||
static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError";
|
|
||||||
throw EvaluatorError<AttributeIsPrivateError>(FStringView(
|
|
||||||
std::format("Attribute '{}' of class `{}` is private",
|
|
||||||
structTypeName.toBasicString(),
|
|
||||||
member.toBasicString())),
|
|
||||||
binExp->rexp->getAAI());
|
|
||||||
}
|
|
||||||
return *st.localContext->get(member); // safe
|
|
||||||
}
|
|
||||||
else if ((fnCall = std::dynamic_pointer_cast<Ast::FunctionCallExpr>(binExp->rexp)))
|
|
||||||
{
|
|
||||||
auto structTypeNameOpt = currentContext->getStructName(st.parentId);
|
|
||||||
if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name"));
|
|
||||||
FString structTypeName = *structTypeNameOpt;
|
|
||||||
|
|
||||||
FString fnName = u8"<anonymous>";
|
|
||||||
if (auto var = std::dynamic_pointer_cast<Ast::VarExprAst>(fnCall->callee))
|
|
||||||
fnName = var->name; // function in struct has its name, so we can get the name
|
|
||||||
if (!st.localContext->containsInThisScope(fnName))
|
|
||||||
{
|
|
||||||
static constexpr char NoAttributeError[] = "NoAttributeError";
|
|
||||||
throw EvaluatorError<NoAttributeError>(FStringView(
|
|
||||||
std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), fnName.toBasicString())),
|
|
||||||
binExp->rexp->getAAI());
|
|
||||||
}
|
|
||||||
if (!st.localContext->isVariablePublic(fnName))
|
|
||||||
{
|
|
||||||
static constexpr char AttributeIsPrivateError[] = "AttributeIsPrivateError";
|
|
||||||
throw EvaluatorError<AttributeIsPrivateError>(FStringView(
|
|
||||||
std::format("Attribute '{}' of class `{}` is private",
|
|
||||||
structTypeName.toBasicString(),
|
|
||||||
fnName.toBasicString())),
|
|
||||||
binExp->rexp->getAAI());
|
|
||||||
}
|
|
||||||
auto calleeValOpt = st.localContext->get(fnName);
|
|
||||||
ObjectPtr calleeVal = *calleeValOpt;
|
|
||||||
|
|
||||||
if (!calleeVal->is<Function>())
|
|
||||||
{
|
|
||||||
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
|
|
||||||
throw EvaluatorError<NotAFunctionErrorName>(
|
|
||||||
FStringView(std::format(
|
|
||||||
"'{}' is not a function or callable",
|
|
||||||
calleeVal->toString().toBasicString())),
|
|
||||||
currentAddressInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
Function fn = calleeVal->as<Function>();
|
|
||||||
|
|
||||||
return evalFunctionCall(fn, fnCall->arg, fnName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static constexpr char AccessOpNotAFieldNameError[] = "AccessOpNotAFieldNameError";
|
|
||||||
throw EvaluatorError<AccessOpNotAFieldNameError>(FStringView(
|
|
||||||
std::format("{} is not a field", binExp->rexp->toString().toBasicString())),
|
|
||||||
binExp->rexp->getAAI());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp));
|
|
||||||
}
|
|
||||||
ObjectPtr Evaluator::evalUnary(const Ast::UnaryExpr &unExp)
|
|
||||||
{
|
|
||||||
using Fig::Ast::Operator;
|
|
||||||
switch (unExp->op)
|
|
||||||
{
|
|
||||||
case Operator::Not:
|
|
||||||
return std::make_shared<Object>(!*eval(unExp->exp));
|
|
||||||
case Operator::Subtract:
|
|
||||||
return std::make_shared<Object>(-*eval(unExp->exp));
|
|
||||||
case Operator::BitNot:
|
|
||||||
return std::make_shared<Object>(bit_not(*eval(unExp->exp)));
|
|
||||||
default:
|
|
||||||
throw RuntimeError(FStringView(std::format("Unsupported unary operator: {}", magic_enum::enum_name(unExp->op))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectPtr Evaluator::evalFunctionCall(const Function &fn, const Ast::FunctionArguments &fnArgs, FString fnName)
|
|
||||||
{
|
|
||||||
const Function &fnStruct = fn;
|
|
||||||
Ast::FunctionCallArgs evaluatedArgs;
|
|
||||||
if (fnStruct.isBuiltin)
|
|
||||||
{
|
|
||||||
for (const auto &argExpr : fnArgs.argv)
|
|
||||||
{
|
|
||||||
evaluatedArgs.argv.push_back(eval(argExpr));
|
|
||||||
}
|
|
||||||
if (fnStruct.builtinParamCount != -1 && fnStruct.builtinParamCount != evaluatedArgs.getLength())
|
|
||||||
{
|
|
||||||
static constexpr char BuiltinArgumentMismatchErrorName[] = "BuiltinArgumentMismatchError";
|
|
||||||
throw EvaluatorError<BuiltinArgumentMismatchErrorName>(FStringView(std::format("Builtin function '{}' expects {} arguments, but {} were provided", fnName.toBasicString(), fnStruct.builtinParamCount, evaluatedArgs.getLength())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
return fnStruct.builtin(evaluatedArgs.argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check argument, all types of parameters
|
|
||||||
Ast::FunctionParameters fnParas = fnStruct.paras;
|
|
||||||
if (fnArgs.getLength() < fnParas.posParas.size() || fnArgs.getLength() > fnParas.size())
|
|
||||||
{
|
|
||||||
static constexpr char ArgumentMismatchErrorName[] = "ArgumentMismatchError";
|
|
||||||
throw EvaluatorError<ArgumentMismatchErrorName>(FStringView(std::format("Function '{}' expects {} to {} arguments, but {} were provided", fnName.toBasicString(), fnParas.posParas.size(), fnParas.size(), fnArgs.getLength())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// positional parameters type check
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < fnParas.posParas.size(); i++)
|
|
||||||
{
|
|
||||||
TypeInfo expectedType(fnParas.posParas[i].second); // look up type info, if exists a type with the name, use it, else throw
|
|
||||||
ObjectPtr argVal = eval(fnArgs.argv[i]);
|
|
||||||
TypeInfo actualType = argVal->getTypeInfo();
|
|
||||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
|
||||||
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.posParas[i].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
evaluatedArgs.argv.push_back(argVal);
|
|
||||||
}
|
|
||||||
// default parameters type check
|
|
||||||
for (; i < fnArgs.getLength(); i++)
|
|
||||||
{
|
|
||||||
size_t defParamIndex = i - fnParas.posParas.size();
|
|
||||||
TypeInfo expectedType = fnParas.defParas[defParamIndex].second.first;
|
|
||||||
|
|
||||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
|
||||||
if (expectedType != defaultVal->getTypeInfo() and expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char DefaultParameterTypeErrorName[] = "DefaultParameterTypeError";
|
|
||||||
throw EvaluatorError<DefaultParameterTypeErrorName>(FStringView(std::format("In function '{}', default parameter '{}' has type '{}', which does not match the expected type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), defaultVal->getTypeInfo().toString().toBasicString(), expectedType.toString().toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectPtr argVal = eval(fnArgs.argv[i]);
|
|
||||||
TypeInfo actualType = argVal->getTypeInfo();
|
|
||||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char ArgumentTypeMismatchErrorName[] = "ArgumentTypeMismatchError";
|
|
||||||
throw EvaluatorError<ArgumentTypeMismatchErrorName>(FStringView(std::format("In function '{}', argument '{}' expects type '{}', but got type '{}'", fnName.toBasicString(), fnParas.defParas[defParamIndex].first.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
evaluatedArgs.argv.push_back(argVal);
|
|
||||||
}
|
|
||||||
// default parameters filling
|
|
||||||
for (; i < fnParas.size(); i++)
|
|
||||||
{
|
|
||||||
size_t defParamIndex = i - fnParas.posParas.size();
|
|
||||||
ObjectPtr defaultVal = eval(fnParas.defParas[defParamIndex].second.second);
|
|
||||||
evaluatedArgs.argv.push_back(defaultVal);
|
|
||||||
}
|
|
||||||
// create new context for function call
|
|
||||||
auto newContext = std::make_shared<Context>(FString(std::format("<Function {}()>", fnName.toBasicString())),
|
|
||||||
fnStruct.closureContext);
|
|
||||||
auto previousContext = currentContext;
|
|
||||||
currentContext = newContext;
|
|
||||||
// define parameters in new context
|
|
||||||
for (size_t j = 0; j < fnParas.size(); j++)
|
|
||||||
{
|
|
||||||
FString paramName;
|
|
||||||
TypeInfo paramType;
|
|
||||||
if (j < fnParas.posParas.size())
|
|
||||||
{
|
|
||||||
paramName = fnParas.posParas[j].first;
|
|
||||||
paramType = fnParas.posParas[j].second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t defParamIndex = j - fnParas.posParas.size();
|
|
||||||
paramName = fnParas.defParas[defParamIndex].first;
|
|
||||||
paramType = fnParas.defParas[defParamIndex].second.first;
|
|
||||||
}
|
|
||||||
AccessModifier argAm = AccessModifier::Const;
|
|
||||||
currentContext->def(paramName, paramType, argAm, evaluatedArgs.argv[j]);
|
|
||||||
}
|
|
||||||
// execute function body
|
|
||||||
ObjectPtr retVal = Object::getNullInstance();
|
|
||||||
for (const auto &stmt : fnStruct.body->stmts)
|
|
||||||
{
|
|
||||||
StatementResult sr = evalStatement(stmt);
|
|
||||||
if (sr.shouldReturn())
|
|
||||||
{
|
|
||||||
retVal = sr.result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentContext = previousContext;
|
|
||||||
if (fnStruct.retType != retVal->getTypeInfo() and fnStruct.retType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char ReturnTypeMismatchErrorName[] = "ReturnTypeMismatchError";
|
|
||||||
throw EvaluatorError<ReturnTypeMismatchErrorName>(FStringView(std::format("Function '{}' expects return type '{}', but got type '{}'", fnName.toBasicString(), fnStruct.retType.toString().toBasicString(), retVal->getTypeInfo().toString().toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectPtr Evaluator::eval(Ast::Expression exp)
|
|
||||||
{
|
|
||||||
using Fig::Ast::AstType;
|
|
||||||
switch (exp->getType())
|
|
||||||
{
|
|
||||||
case AstType::ValueExpr: {
|
|
||||||
auto valExp = std::dynamic_pointer_cast<Ast::ValueExprAst>(exp);
|
|
||||||
return valExp->val;
|
|
||||||
}
|
|
||||||
case AstType::VarExpr: {
|
|
||||||
auto varExp = std::dynamic_pointer_cast<Ast::VarExprAst>(exp);
|
|
||||||
auto val = currentContext->get(varExp->name);
|
|
||||||
if (val.has_value())
|
|
||||||
{
|
|
||||||
return val.value();
|
|
||||||
}
|
|
||||||
static constexpr char UndefinedVariableErrorName[] = "UndefinedVariableError";
|
|
||||||
throw EvaluatorError<UndefinedVariableErrorName>(FStringView(std::format("Variable '{}' is not defined in the current scope", varExp->name.toBasicString())), varExp->getAAI());
|
|
||||||
}
|
|
||||||
case AstType::BinaryExpr: {
|
|
||||||
auto binExp = std::dynamic_pointer_cast<Ast::BinaryExprAst>(exp);
|
|
||||||
return evalBinary(binExp);
|
|
||||||
}
|
|
||||||
case AstType::UnaryExpr: {
|
|
||||||
auto unExp = std::dynamic_pointer_cast<Ast::UnaryExprAst>(exp);
|
|
||||||
return evalUnary(unExp);
|
|
||||||
}
|
|
||||||
case AstType::FunctionCall: {
|
|
||||||
auto fnCall = std::dynamic_pointer_cast<Ast::FunctionCallExpr>(exp);
|
|
||||||
|
|
||||||
ObjectPtr calleeVal = eval(fnCall->callee);
|
|
||||||
|
|
||||||
if (!calleeVal->is<Function>())
|
|
||||||
{
|
|
||||||
static constexpr char NotAFunctionErrorName[] = "NotAFunctionError";
|
|
||||||
throw EvaluatorError<NotAFunctionErrorName>(
|
|
||||||
FStringView(std::format(
|
|
||||||
"'{}' is not a function or callable",
|
|
||||||
calleeVal->toString().toBasicString())),
|
|
||||||
currentAddressInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
Function fn = calleeVal->as<Function>();
|
|
||||||
|
|
||||||
FString fnName = u8"<anonymous>";
|
|
||||||
if (auto var = std::dynamic_pointer_cast<Ast::VarExprAst>(fnCall->callee))
|
|
||||||
fnName = var->name; // try to get function name
|
|
||||||
|
|
||||||
return evalFunctionCall(fn, fnCall->arg, fnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
case AstType::FunctionLiteralExpr: {
|
|
||||||
auto fn = std::dynamic_pointer_cast<Ast::FunctionLiteralExprAst>(exp);
|
|
||||||
|
|
||||||
if (fn->isExprMode())
|
|
||||||
{
|
|
||||||
Ast::BlockStatement body = std::make_shared<Ast::BlockStatementAst>();
|
|
||||||
body->setAAI(fn->getExprBody()->getAAI());
|
|
||||||
Ast::Statement retSt = std::make_shared<Ast::ReturnSt>(fn->getExprBody());
|
|
||||||
retSt->setAAI(fn->getExprBody()->getAAI());
|
|
||||||
body->stmts.push_back(retSt);
|
|
||||||
return std::make_shared<Object>(Function(
|
|
||||||
fn->paras,
|
|
||||||
ValueType::Any,
|
|
||||||
body,
|
|
||||||
currentContext));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Ast::BlockStatement body = fn->getBlockBody();
|
|
||||||
return std::make_shared<Object>(Function(
|
|
||||||
fn->paras,
|
|
||||||
ValueType::Any,
|
|
||||||
body,
|
|
||||||
currentContext));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case AstType::InitExpr: {
|
|
||||||
auto initExpr = std::dynamic_pointer_cast<Ast::InitExprAst>(exp);
|
|
||||||
if (!currentContext->contains(initExpr->structName))
|
|
||||||
{
|
|
||||||
static constexpr char StructNotFoundErrorName[] = "StructNotFoundError";
|
|
||||||
throw EvaluatorError<StructNotFoundErrorName>(FStringView(std::format("Structure type '{}' not found", initExpr->structName.toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
ObjectPtr structTypeVal = currentContext->get(initExpr->structName).value();
|
|
||||||
if (!structTypeVal->is<StructType>())
|
|
||||||
{
|
|
||||||
static constexpr char NotAStructTypeErrorName[] = "NotAStructTypeError";
|
|
||||||
throw EvaluatorError<NotAStructTypeErrorName>(FStringView(std::format("'{}' is not a structure type", initExpr->structName.toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
const StructType &structT = structTypeVal->as<StructType>();
|
|
||||||
ContextPtr defContext = structT.defContext; // definition context
|
|
||||||
// check init args
|
|
||||||
|
|
||||||
size_t minArgs = 0;
|
|
||||||
size_t maxArgs = structT.fields.size();
|
|
||||||
|
|
||||||
for (auto &f : structT.fields)
|
|
||||||
{
|
|
||||||
if (f.defaultValue == nullptr) minArgs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t got = initExpr->args.size();
|
|
||||||
if (got > maxArgs || got < minArgs)
|
|
||||||
{
|
|
||||||
static constexpr char StructInitArgumentMismatchErrorName[] = "StructInitArgumentMismatchError";
|
|
||||||
throw EvaluatorError<StructInitArgumentMismatchErrorName>(FStringView(std::format("Structure '{}' expects {} to {} fields, but {} were provided", initExpr->structName.toBasicString(), minArgs, maxArgs, initExpr->args.size())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<FString, ObjectPtr>> evaluatedArgs;
|
|
||||||
for (const auto &[argName, argExpr] : initExpr->args)
|
|
||||||
{
|
|
||||||
evaluatedArgs.push_back({argName, eval(argExpr)});
|
|
||||||
}
|
|
||||||
ContextPtr instanceCtx = std::make_shared<Context>(
|
|
||||||
FString(std::format("<StructInstance {}>", initExpr->structName.toBasicString())),
|
|
||||||
currentContext);
|
|
||||||
/*
|
|
||||||
3 ways of calling constructor
|
|
||||||
.1 Person {"Fig", 1, "IDK"};
|
|
||||||
.2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered
|
|
||||||
.3 Person {name, age, sex};
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
using enum Ast::InitExprAst::InitMode;
|
|
||||||
if (initExpr->initMode == Positional)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < maxArgs; ++i)
|
|
||||||
{
|
|
||||||
const Field &field = structT.fields[i];
|
|
||||||
const FString &fieldName = field.name;
|
|
||||||
const TypeInfo &expectedType = field.type;
|
|
||||||
if (i >= evaluatedArgs.size())
|
|
||||||
{
|
|
||||||
// we've checked argument count before, so here must be a default value
|
|
||||||
ContextPtr previousContext = currentContext;
|
|
||||||
currentContext = defContext; // evaluate default value in definition context
|
|
||||||
|
|
||||||
ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here
|
|
||||||
|
|
||||||
currentContext = previousContext;
|
|
||||||
|
|
||||||
// type check
|
|
||||||
if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
|
||||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceCtx->def(fieldName, expectedType, field.am, defaultVal);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ObjectPtr &argVal = evaluatedArgs[i].second;
|
|
||||||
if (expectedType != argVal->getTypeInfo() && expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
|
||||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
instanceCtx->def(fieldName, expectedType, field.am, argVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// named / shorthand init
|
|
||||||
for (size_t i = 0; i < maxArgs; ++i)
|
|
||||||
{
|
|
||||||
const Field &field = structT.fields[i];
|
|
||||||
const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name);
|
|
||||||
if (instanceCtx->containsInThisScope(fieldName))
|
|
||||||
{
|
|
||||||
static constexpr char StructFieldRedeclarationErrorName[] = "StructFieldRedeclarationError";
|
|
||||||
throw EvaluatorError<StructFieldRedeclarationErrorName>(FStringView(std::format("Field '{}' already initialized in structure '{}'", fieldName.toBasicString(), initExpr->structName.toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
if (i + 1 > got)
|
|
||||||
{
|
|
||||||
// use default value
|
|
||||||
ContextPtr previousContext = currentContext;
|
|
||||||
currentContext = defContext; // evaluate default value in definition context
|
|
||||||
ObjectPtr defaultVal = eval(field.defaultValue); // it can't be null here
|
|
||||||
currentContext = previousContext;
|
|
||||||
|
|
||||||
// type check
|
|
||||||
const TypeInfo &expectedType = field.type;
|
|
||||||
if (expectedType != defaultVal->getTypeInfo() && expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
|
||||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceCtx->def(fieldName, field.type, field.am, defaultVal);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const ObjectPtr &argVal = evaluatedArgs[i].second;
|
|
||||||
if (field.type != argVal->getTypeInfo() && field.type != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError";
|
|
||||||
throw EvaluatorError<StructFieldTypeMismatchErrorName>(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), field.type.toString().toBasicString(), argVal->getTypeInfo().toString().toBasicString())), initExpr->getAAI());
|
|
||||||
}
|
|
||||||
instanceCtx->def(fieldName, field.type, field.am, argVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instanceCtx->merge(*structT.defContext);
|
|
||||||
for (auto &[id, fn] : instanceCtx->getFunctions())
|
|
||||||
{
|
|
||||||
instanceCtx->_update(*instanceCtx->getFunctionName(id), Object(
|
|
||||||
Function(fn.paras, fn.retType, fn.body, instanceCtx) // change its closureContext to struct instance's context
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return std::make_shared<Object>(StructInstance(structT.id, instanceCtx));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast<int>(exp->getType()))));
|
|
||||||
return Object::getNullInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
StatementResult Evaluator::evalBlockStatement(const Ast::BlockStatement &blockSt, ContextPtr context)
|
|
||||||
{
|
|
||||||
auto previousContext = currentContext;
|
|
||||||
if (context)
|
|
||||||
{
|
|
||||||
currentContext = context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentContext = std::make_shared<Context>(FString(std::format("<Block {}:{}>", blockSt->getAAI().line, blockSt->getAAI().column)), currentContext);
|
|
||||||
}
|
|
||||||
StatementResult lstResult = StatementResult::normal();
|
|
||||||
for (const auto &s : blockSt->stmts)
|
|
||||||
{
|
|
||||||
StatementResult sr = evalStatement(s);
|
|
||||||
if (!sr.isNormal())
|
|
||||||
{
|
|
||||||
lstResult = sr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentContext = previousContext;
|
|
||||||
return lstResult;
|
|
||||||
}
|
|
||||||
StatementResult Evaluator::evalStatement(const Ast::Statement &stmt)
|
|
||||||
{
|
|
||||||
using Fig::Ast::AstType;
|
|
||||||
currentAddressInfo = stmt->getAAI();
|
|
||||||
switch (stmt->getType())
|
|
||||||
{
|
|
||||||
case AstType::VarDefSt: {
|
|
||||||
auto varDef = std::dynamic_pointer_cast<Ast::VarDefAst>(stmt);
|
|
||||||
if (currentContext->contains(varDef->name))
|
|
||||||
{
|
|
||||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
|
||||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Variable '{}' already defined in this scope", varDef->name.toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
ObjectPtr val;
|
|
||||||
TypeInfo varTypeInfo;
|
|
||||||
if (varDef->typeName == Parser::varDefTypeFollowed)
|
|
||||||
{
|
|
||||||
// has expr
|
|
||||||
val = eval(varDef->expr);
|
|
||||||
varTypeInfo = val->getTypeInfo();
|
|
||||||
}
|
|
||||||
else if (varDef->expr)
|
|
||||||
{
|
|
||||||
val = eval(varDef->expr);
|
|
||||||
if (varDef->typeName != ValueType::Any.name)
|
|
||||||
{
|
|
||||||
TypeInfo expectedType(varDef->typeName);
|
|
||||||
TypeInfo actualType = val->getTypeInfo();
|
|
||||||
if (expectedType != actualType and expectedType != ValueType::Any)
|
|
||||||
{
|
|
||||||
static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
|
||||||
throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("Variable '{}' expects type '{}', but got type '{}'", varDef->name.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), varDef->getAAI());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!varDef->typeName.empty())
|
|
||||||
{
|
|
||||||
varTypeInfo = TypeInfo(varDef->typeName); // may throw
|
|
||||||
val = std::make_shared<Object>(Object::defaultValue(varTypeInfo));
|
|
||||||
}
|
|
||||||
AccessModifier am = (varDef->isPublic ? (varDef->isConst ? AccessModifier::PublicConst : AccessModifier::Public) : (varDef->isConst ? AccessModifier::Const : AccessModifier::Normal));
|
|
||||||
currentContext->def(varDef->name, varTypeInfo, am, val);
|
|
||||||
return StatementResult::normal();
|
|
||||||
}
|
|
||||||
case AstType::ExpressionStmt: {
|
|
||||||
auto exprSt = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(stmt);
|
|
||||||
eval(exprSt->exp);
|
|
||||||
return StatementResult::normal();
|
|
||||||
};
|
|
||||||
case AstType::BlockStatement: {
|
|
||||||
auto blockSt = std::dynamic_pointer_cast<Ast::BlockStatementAst>(stmt);
|
|
||||||
return evalBlockStatement(blockSt); // auto create new context in block statement
|
|
||||||
};
|
|
||||||
case AstType::FunctionDefSt: {
|
|
||||||
auto fnDef = std::dynamic_pointer_cast<Ast::FunctionDefSt>(stmt);
|
|
||||||
if (currentContext->containsInThisScope(fnDef->name))
|
|
||||||
{
|
|
||||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
|
||||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Function '{}' already defined in this scope", fnDef->name.toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
AccessModifier am = (fnDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
|
||||||
currentContext->def(
|
|
||||||
fnDef->name,
|
|
||||||
ValueType::Function,
|
|
||||||
am,
|
|
||||||
std::make_shared<Object>(Function(
|
|
||||||
fnDef->paras,
|
|
||||||
TypeInfo(fnDef->retType),
|
|
||||||
fnDef->body,
|
|
||||||
currentContext)));
|
|
||||||
return StatementResult::normal();
|
|
||||||
};
|
|
||||||
case AstType::StructSt: {
|
|
||||||
auto stDef = std::dynamic_pointer_cast<Ast::StructDefSt>(stmt);
|
|
||||||
if (currentContext->containsInThisScope(stDef->name))
|
|
||||||
{
|
|
||||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
|
||||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
std::vector<Field> fields;
|
|
||||||
std::vector<FString> _fieldNames;
|
|
||||||
for (Ast::StructDefField field : stDef->fields)
|
|
||||||
{
|
|
||||||
if (Utils::vectorContains(field.fieldName, _fieldNames))
|
|
||||||
{
|
|
||||||
static constexpr char RedeclarationErrorName[] = "RedeclarationError";
|
|
||||||
throw EvaluatorError<RedeclarationErrorName>(FStringView(std::format("Field '{}' already defined in structure '{}'", field.fieldName.toBasicString(), stDef->name.toBasicString())), currentAddressInfo);
|
|
||||||
}
|
|
||||||
fields.push_back(Field(field.am, field.fieldName, TypeInfo(field.tiName), field.defaultValueExpr));
|
|
||||||
}
|
|
||||||
ContextPtr defContext = std::make_shared<Context>(FString(std::format("<Struct {} at {}:{}>",
|
|
||||||
stDef->name.toBasicString(),
|
|
||||||
stDef->getAAI().line,
|
|
||||||
stDef->getAAI().column)),
|
|
||||||
currentContext);
|
|
||||||
ContextPtr previousContext = currentContext;
|
|
||||||
currentContext = defContext;
|
|
||||||
|
|
||||||
const Ast::BlockStatement &body = stDef->body;
|
|
||||||
for (auto &st : body->stmts)
|
|
||||||
{
|
|
||||||
if (st->getType() != Ast::AstType::FunctionDefSt)
|
|
||||||
{
|
|
||||||
static constexpr char UnexpectedStatementInStructError[] = "UnexpectedStatementInStructError";
|
|
||||||
throw EvaluatorError<UnexpectedStatementInStructError>(FStringView(
|
|
||||||
std::format("Unexpected statement `{}` in struct declaration",
|
|
||||||
st->toString().toBasicString())),
|
|
||||||
st->getAAI());
|
|
||||||
}
|
|
||||||
evalStatement(st); // function def st
|
|
||||||
}
|
|
||||||
currentContext = previousContext;
|
|
||||||
|
|
||||||
AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const);
|
|
||||||
TypeInfo _(stDef->name, true); // register type name
|
|
||||||
currentContext->def(
|
|
||||||
stDef->name,
|
|
||||||
ValueType::StructType,
|
|
||||||
am,
|
|
||||||
std::make_shared<Object>(StructType(
|
|
||||||
defContext,
|
|
||||||
fields)));
|
|
||||||
return StatementResult::normal();
|
|
||||||
}
|
|
||||||
// case AstType::VarAssignSt: {
|
|
||||||
// auto varAssign = std::dynamic_pointer_cast<Ast::VarAssignSt>(stmt);
|
|
||||||
// if (!currentContext->contains(varAssign->varName))
|
|
||||||
// {
|
|
||||||
// static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError";
|
|
||||||
// throw EvaluatorError<VariableNotFoundErrorName>(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo);
|
|
||||||
// }
|
|
||||||
// if (!currentContext->isVariableMutable(varAssign->varName))
|
|
||||||
// {
|
|
||||||
// static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError";
|
|
||||||
// throw EvaluatorError<ConstAssignmentErrorName>(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo);
|
|
||||||
// }
|
|
||||||
// Object val = eval(varAssign->valueExpr);
|
|
||||||
// if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any)
|
|
||||||
// {
|
|
||||||
// TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName);
|
|
||||||
// TypeInfo actualType = val.getTypeInfo();
|
|
||||||
// if (expectedType != actualType)
|
|
||||||
// {
|
|
||||||
// static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError";
|
|
||||||
// throw EvaluatorError<VariableTypeMismatchErrorName>(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// currentContext->set(varAssign->varName, val);
|
|
||||||
// return StatementResult::normal();
|
|
||||||
// };
|
|
||||||
case AstType::IfSt: {
|
|
||||||
auto ifSt = std::dynamic_pointer_cast<Ast::IfSt>(stmt);
|
|
||||||
ObjectPtr condVal = eval(ifSt->condition);
|
|
||||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
|
||||||
{
|
|
||||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
|
||||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"If condition must be boolean"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
if (condVal->as<ValueType::BoolClass>())
|
|
||||||
{
|
|
||||||
return evalBlockStatement(ifSt->body);
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
for (const auto &elif : ifSt->elifs)
|
|
||||||
{
|
|
||||||
ObjectPtr elifCondVal = eval(elif->condition);
|
|
||||||
if (elifCondVal->getTypeInfo() != ValueType::Bool)
|
|
||||||
{
|
|
||||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
|
||||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"Else-if condition must be boolean"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
if (elifCondVal->as<ValueType::BoolClass>())
|
|
||||||
{
|
|
||||||
return evalBlockStatement(elif->body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ifSt->els)
|
|
||||||
{
|
|
||||||
return evalBlockStatement(ifSt->els->body);
|
|
||||||
}
|
|
||||||
return StatementResult::normal();
|
|
||||||
};
|
|
||||||
case AstType::WhileSt: {
|
|
||||||
auto whileSt = std::dynamic_pointer_cast<Ast::WhileSt>(stmt);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ObjectPtr condVal = eval(whileSt->condition);
|
|
||||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
|
||||||
{
|
|
||||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
|
||||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"While condition must be boolean"), whileSt->condition->getAAI());
|
|
||||||
}
|
|
||||||
if (!condVal->as<ValueType::BoolClass>())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ContextPtr loopContext = std::make_shared<Context>(
|
|
||||||
FString(std::format("<While {}:{}>",
|
|
||||||
whileSt->getAAI().line, whileSt->getAAI().column)),
|
|
||||||
currentContext); // every loop has its own context
|
|
||||||
StatementResult sr = evalBlockStatement(whileSt->body, loopContext);
|
|
||||||
if (sr.shouldReturn())
|
|
||||||
{
|
|
||||||
return sr;
|
|
||||||
}
|
|
||||||
if (sr.shouldBreak())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (sr.shouldContinue())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return StatementResult::normal();
|
|
||||||
};
|
|
||||||
case AstType::ForSt: {
|
|
||||||
auto forSt = std::dynamic_pointer_cast<Ast::ForSt>(stmt);
|
|
||||||
ContextPtr loopContext = std::make_shared<Context>(
|
|
||||||
FString(std::format("<For {}:{}>",
|
|
||||||
forSt->getAAI().line, forSt->getAAI().column)),
|
|
||||||
currentContext); // for loop has its own context
|
|
||||||
ContextPtr previousContext = currentContext;
|
|
||||||
currentContext = loopContext;
|
|
||||||
|
|
||||||
evalStatement(forSt->initSt); // ignore init statement result
|
|
||||||
size_t iteration = 0;
|
|
||||||
|
|
||||||
while (true) // use while loop to simulate for loop, cause we need to check condition type every iteration
|
|
||||||
{
|
|
||||||
ObjectPtr condVal = eval(forSt->condition);
|
|
||||||
if (condVal->getTypeInfo() != ValueType::Bool)
|
|
||||||
{
|
|
||||||
static constexpr char ConditionTypeErrorName[] = "ConditionTypeError";
|
|
||||||
throw EvaluatorError<ConditionTypeErrorName>(FStringView(u8"For condition must be boolean"), forSt->condition->getAAI());
|
|
||||||
}
|
|
||||||
if (!condVal->as<ValueType::BoolClass>())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
iteration++;
|
|
||||||
ContextPtr iterationContext = std::make_shared<Context>(
|
|
||||||
FString(std::format("<For {}:{}, Iteration {}>",
|
|
||||||
forSt->getAAI().line, forSt->getAAI().column, iteration)),
|
|
||||||
loopContext); // every loop has its own context
|
|
||||||
StatementResult sr = evalBlockStatement(forSt->body, iterationContext);
|
|
||||||
if (sr.shouldReturn())
|
|
||||||
{
|
|
||||||
currentContext = previousContext; // restore context before return
|
|
||||||
return sr;
|
|
||||||
}
|
|
||||||
if (sr.shouldBreak())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (sr.shouldContinue())
|
|
||||||
{
|
|
||||||
// continue to next iteration
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
currentContext = loopContext; // let increment statement be in loop context
|
|
||||||
evalStatement(forSt->incrementSt); // ignore increment statement result
|
|
||||||
}
|
|
||||||
currentContext = previousContext; // restore context
|
|
||||||
return StatementResult::normal();
|
|
||||||
}
|
|
||||||
case AstType::ReturnSt: {
|
|
||||||
if (!currentContext->parent)
|
|
||||||
{
|
|
||||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
|
||||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
if (!currentContext->isInFunctionContext())
|
|
||||||
{
|
|
||||||
static constexpr char ReturnOutsideFunctionErrorName[] = "ReturnOutsideFunctionError";
|
|
||||||
throw EvaluatorError<ReturnOutsideFunctionErrorName>(FStringView(u8"'return' statement outside function"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
auto returnSt = std::dynamic_pointer_cast<Ast::ReturnSt>(stmt);
|
|
||||||
return StatementResult::returnFlow(eval(returnSt->retValue));
|
|
||||||
};
|
|
||||||
case AstType::BreakSt: {
|
|
||||||
if (!currentContext->parent)
|
|
||||||
{
|
|
||||||
static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError";
|
|
||||||
throw EvaluatorError<BreakOutsideLoopErrorName>(FStringView(u8"'break' statement outside loop"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
if (!currentContext->isInLoopContext())
|
|
||||||
{
|
|
||||||
static constexpr char BreakOutsideLoopErrorName[] = "BreakOutsideLoopError";
|
|
||||||
throw EvaluatorError<BreakOutsideLoopErrorName>(FStringView(u8"'break' statement outside loop"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
return StatementResult::breakFlow();
|
|
||||||
};
|
|
||||||
case AstType::ContinueSt: {
|
|
||||||
if (!currentContext->parent)
|
|
||||||
{
|
|
||||||
static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError";
|
|
||||||
throw EvaluatorError<ContinueOutsideLoopErrorName>(FStringView(u8"'continue' statement outside loop"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
if (!currentContext->isInLoopContext())
|
|
||||||
{
|
|
||||||
static constexpr char ContinueOutsideLoopErrorName[] = "ContinueOutsideLoopError";
|
|
||||||
throw EvaluatorError<ContinueOutsideLoopErrorName>(FStringView(u8"'continue' statement outside loop"), currentAddressInfo);
|
|
||||||
}
|
|
||||||
return StatementResult::continueFlow();
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
throw RuntimeError(FStringView(std::string("Unknown statement type:") + magic_enum::enum_name(stmt->getType()).data()));
|
|
||||||
}
|
|
||||||
return StatementResult::normal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Evaluator::run()
|
|
||||||
{
|
|
||||||
for (auto ast : asts)
|
|
||||||
{
|
|
||||||
currentAddressInfo = ast->getAAI();
|
|
||||||
if (std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast))
|
|
||||||
{
|
|
||||||
auto exprAst = std::dynamic_pointer_cast<Ast::ExpressionStmtAst>(ast);
|
|
||||||
Ast::Expression exp = exprAst->exp;
|
|
||||||
eval(exp);
|
|
||||||
}
|
|
||||||
else if (dynamic_cast<Ast::StatementAst *>(ast.get()))
|
|
||||||
{
|
|
||||||
auto stmtAst = std::dynamic_pointer_cast<Ast::StatementAst>(ast);
|
|
||||||
evalStatement(stmtAst);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw RuntimeError(FStringView(u8"Unknown AST type"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Evaluator::printStackTrace() const
|
|
||||||
{
|
|
||||||
if (currentContext)
|
|
||||||
currentContext->printStackTrace();
|
|
||||||
else
|
|
||||||
std::cerr << "[STACK TRACE] (No context has been loaded)\n";
|
|
||||||
}
|
|
||||||
} // namespace Fig
|
|
||||||
24
src/main.cpp
24
src/main.cpp
@@ -27,16 +27,16 @@ Copyright (C) 2020-2025 PuqiAR
|
|||||||
This software is licensed under the MIT License. See LICENSE.txt for details.
|
This software is licensed under the MIT License. See LICENSE.txt for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <argparse/argparse.hpp>
|
#include <Utils/argparse/argparse.hpp>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <core.hpp>
|
#include <Core/core.hpp>
|
||||||
#include <lexer.hpp>
|
#include <Lexer/lexer.hpp>
|
||||||
#include <parser.hpp>
|
#include <Parser/parser.hpp>
|
||||||
#include <evaluator.hpp>
|
#include <Evaluator/evaluator.hpp>
|
||||||
#include <AstPrinter.hpp>
|
#include <Utils/AstPrinter.hpp>
|
||||||
#include <errorLog.hpp>
|
#include <Error/errorLog.hpp>
|
||||||
|
|
||||||
static size_t addressableErrorCount = 0;
|
static size_t addressableErrorCount = 0;
|
||||||
static size_t unaddressableErrorCount = 0;
|
static size_t unaddressableErrorCount = 0;
|
||||||
@@ -117,13 +117,13 @@ int main(int argc, char **argv)
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
Fig::Parser parser(lexer);
|
Fig::Parser parser(lexer);
|
||||||
std::vector<Fig::Ast::AstBase> ast;
|
std::vector<Fig::Ast::AstBase> asts;
|
||||||
|
|
||||||
std::vector<FString> sourceLines = splitSource(Fig::FString(source));
|
std::vector<FString> sourceLines = splitSource(Fig::FString(source));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ast = parser.parseAll();
|
asts = parser.parseAll();
|
||||||
}
|
}
|
||||||
catch (const Fig::AddressableError &e)
|
catch (const Fig::AddressableError &e)
|
||||||
{
|
{
|
||||||
@@ -150,10 +150,12 @@ int main(int argc, char **argv)
|
|||||||
// printer.print(node);
|
// printer.print(node);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Fig::Evaluator evaluator(ast);
|
Fig::Evaluator evaluator;
|
||||||
|
evaluator.CreateGlobalContext();
|
||||||
|
evaluator.RegisterBuiltins();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
evaluator.run();
|
evaluator.Run(asts);
|
||||||
}
|
}
|
||||||
catch (const Fig::AddressableError &e)
|
catch (const Fig::AddressableError &e)
|
||||||
{
|
{
|
||||||
|
|||||||
17
test.fig
17
test.fig
@@ -1,14 +1,15 @@
|
|||||||
struct test
|
struct Person
|
||||||
{
|
{
|
||||||
bar:String = "1233";
|
name: String;
|
||||||
func foo()
|
|
||||||
|
func getName() -> String
|
||||||
{
|
{
|
||||||
__fstdout_println(bar);
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var x := test{};
|
var person := Person{"PuqiAR"};
|
||||||
x.foo();
|
|
||||||
|
|
||||||
x.bar = "555";
|
const print := __fstdout_println;
|
||||||
x.foo();
|
|
||||||
|
print(person.getName());
|
||||||
11
xmake.lua
11
xmake.lua
@@ -13,10 +13,15 @@ target("Fig")
|
|||||||
add_cxxflags("-static")
|
add_cxxflags("-static")
|
||||||
add_cxxflags("-stdlib=libc++")
|
add_cxxflags("-stdlib=libc++")
|
||||||
|
|
||||||
add_files("src/*.cpp")
|
add_files("src/main.cpp")
|
||||||
|
add_files("src/Core/warning.cpp")
|
||||||
add_includedirs("include")
|
add_files("src/Evaluator/evaluator.cpp")
|
||||||
|
add_files("src/Lexer/lexer.cpp")
|
||||||
|
add_files("src/Parser/parser.cpp")
|
||||||
|
add_files("src/Value/value.cpp")
|
||||||
|
|
||||||
|
add_includedirs("src")
|
||||||
|
|
||||||
set_warnings("all")
|
set_warnings("all")
|
||||||
|
|
||||||
add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"")
|
add_defines("__FCORE_COMPILE_TIME=\"" .. os.date("%Y-%m-%d %H:%M:%S") .. "\"")
|
||||||
Reference in New Issue
Block a user