forked from Mirrors/openclonk
551 lines
11 KiB
C++
551 lines
11 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
// C4Aul abstract syntax tree nodes
|
|
|
|
#ifndef INC_C4AulAST
|
|
#define INC_C4AulAST
|
|
|
|
#include "script/C4Value.h"
|
|
|
|
namespace aul { namespace ast {
|
|
class Noop;
|
|
class StringLit;
|
|
class IntLit;
|
|
class BoolLit;
|
|
class ArrayLit;
|
|
class ProplistLit;
|
|
class NilLit;
|
|
class ThisLit;
|
|
class VarExpr;
|
|
class UnOpExpr;
|
|
class BinOpExpr;
|
|
class AssignmentExpr;
|
|
class SubscriptExpr;
|
|
class SliceExpr;
|
|
class CallExpr;
|
|
class ParExpr;
|
|
class FunctionExpr;
|
|
class Block;
|
|
class Return;
|
|
class ForLoop;
|
|
class RangeLoop;
|
|
class DoLoop;
|
|
class WhileLoop;
|
|
class Break;
|
|
class Continue;
|
|
class If;
|
|
class VarDecl;
|
|
class FunctionDecl;
|
|
class IncludePragma;
|
|
class AppendtoPragma;
|
|
class Script;
|
|
}}
|
|
|
|
namespace aul {
|
|
class AstVisitor
|
|
{
|
|
public:
|
|
virtual ~AstVisitor() = default;
|
|
|
|
virtual void visit(const ::aul::ast::Noop *) {}
|
|
virtual void visit(const ::aul::ast::StringLit *) {}
|
|
virtual void visit(const ::aul::ast::IntLit *) {}
|
|
virtual void visit(const ::aul::ast::BoolLit *) {}
|
|
virtual void visit(const ::aul::ast::ArrayLit *) {}
|
|
virtual void visit(const ::aul::ast::ProplistLit *) {}
|
|
virtual void visit(const ::aul::ast::NilLit *) {}
|
|
virtual void visit(const ::aul::ast::ThisLit *) {}
|
|
virtual void visit(const ::aul::ast::VarExpr *n) {}
|
|
virtual void visit(const ::aul::ast::UnOpExpr *) {}
|
|
virtual void visit(const ::aul::ast::BinOpExpr *) {}
|
|
virtual void visit(const ::aul::ast::AssignmentExpr *) {}
|
|
virtual void visit(const ::aul::ast::SubscriptExpr *) {}
|
|
virtual void visit(const ::aul::ast::SliceExpr *) {}
|
|
virtual void visit(const ::aul::ast::CallExpr *) {}
|
|
virtual void visit(const ::aul::ast::ParExpr *) {}
|
|
virtual void visit(const ::aul::ast::Block *) {}
|
|
virtual void visit(const ::aul::ast::Return *) {}
|
|
virtual void visit(const ::aul::ast::ForLoop *) {}
|
|
virtual void visit(const ::aul::ast::RangeLoop *) {}
|
|
virtual void visit(const ::aul::ast::DoLoop *) {}
|
|
virtual void visit(const ::aul::ast::WhileLoop *) {}
|
|
virtual void visit(const ::aul::ast::Break *) {}
|
|
virtual void visit(const ::aul::ast::Continue *) {}
|
|
virtual void visit(const ::aul::ast::If *) {}
|
|
virtual void visit(const ::aul::ast::VarDecl *) {}
|
|
virtual void visit(const ::aul::ast::FunctionDecl *) {}
|
|
virtual void visit(const ::aul::ast::FunctionExpr *) {}
|
|
virtual void visit(const ::aul::ast::IncludePragma *) {}
|
|
virtual void visit(const ::aul::ast::AppendtoPragma *) {}
|
|
virtual void visit(const ::aul::ast::Script *) {}
|
|
|
|
// This template will catch any type missing from the list above
|
|
// to ensure that the nodes don't accidentally get visited via a
|
|
// base class instead
|
|
template<class T>
|
|
void visit(const T *) = delete;
|
|
};
|
|
}
|
|
|
|
namespace aul { namespace ast {
|
|
|
|
#define AST_NODE(cls) \
|
|
public: \
|
|
virtual void accept(::aul::AstVisitor *v) const override { v->visit(this); } \
|
|
template<class... T> static std::unique_ptr<cls> New(const char *loc, T &&...t) { auto n = std::make_unique<cls>(std::forward<T>(t)...); n->loc = loc; return n; } \
|
|
private:
|
|
|
|
class Node
|
|
{
|
|
public:
|
|
virtual ~Node() = default;
|
|
|
|
struct Location
|
|
{
|
|
std::string file;
|
|
size_t line = 0;
|
|
size_t column = 0;
|
|
};
|
|
|
|
const char *loc = nullptr;
|
|
|
|
virtual void accept(::aul::AstVisitor *) const = 0;
|
|
};
|
|
|
|
class Stmt : public Node
|
|
{
|
|
public:
|
|
// Does executing this statement generate a return value?
|
|
virtual bool has_value() const { return false; }
|
|
};
|
|
|
|
typedef std::unique_ptr<Stmt> StmtPtr;
|
|
|
|
class Noop : public Stmt
|
|
{
|
|
AST_NODE(Noop);
|
|
};
|
|
|
|
class Expr : public Stmt
|
|
{
|
|
public:
|
|
bool has_value() const override { return true; }
|
|
};
|
|
typedef std::unique_ptr<Expr> ExprPtr;
|
|
|
|
class Literal : public Expr
|
|
{};
|
|
|
|
class StringLit : public Literal
|
|
{
|
|
AST_NODE(StringLit);
|
|
public:
|
|
explicit StringLit(const std::string &value) : value(value) {}
|
|
std::string value;
|
|
};
|
|
|
|
class IntLit : public Literal
|
|
{
|
|
AST_NODE(IntLit);
|
|
public:
|
|
explicit IntLit(int32_t value) : value(value) {}
|
|
uint32_t value;
|
|
};
|
|
|
|
class BoolLit : public Literal
|
|
{
|
|
AST_NODE(BoolLit);
|
|
public:
|
|
explicit BoolLit(bool value) : value(value) {}
|
|
bool value;
|
|
};
|
|
|
|
class ArrayLit : public Literal
|
|
{
|
|
AST_NODE(ArrayLit);
|
|
public:
|
|
std::vector<ExprPtr> values;
|
|
};
|
|
|
|
class ProplistLit : public Literal
|
|
{
|
|
AST_NODE(ProplistLit);
|
|
public:
|
|
std::vector<std::pair<std::string, ExprPtr>> values;
|
|
};
|
|
|
|
class NilLit : public Literal
|
|
{
|
|
AST_NODE(NilLit);
|
|
};
|
|
|
|
class ThisLit : public Literal
|
|
{
|
|
AST_NODE(ThisLit);
|
|
};
|
|
|
|
class VarExpr : public Expr
|
|
{
|
|
AST_NODE(VarExpr);
|
|
public:
|
|
explicit VarExpr(const std::string &identifier) : identifier(identifier) {}
|
|
std::string identifier;
|
|
};
|
|
|
|
class UnOpExpr : public Expr
|
|
{
|
|
AST_NODE(UnOpExpr);
|
|
public:
|
|
UnOpExpr(int op, ExprPtr &&operand) : op(op), operand(std::move(operand)) {}
|
|
ExprPtr operand;
|
|
int op; // TODO: Make this a proper operator type
|
|
};
|
|
|
|
class BinOpExpr : public Expr
|
|
{
|
|
AST_NODE(BinOpExpr);
|
|
public:
|
|
BinOpExpr(int op, ExprPtr &&lhs, ExprPtr &&rhs) : op(op), lhs(std::move(lhs)), rhs(std::move(rhs)) {}
|
|
ExprPtr lhs, rhs;
|
|
int op; // TODO: Make this a proper operator type
|
|
};
|
|
|
|
class AssignmentExpr : public Expr
|
|
{
|
|
AST_NODE(AssignmentExpr);
|
|
public:
|
|
AssignmentExpr(ExprPtr &&lhs, ExprPtr &&rhs) : lhs(std::move(lhs)), rhs(std::move(rhs)) {}
|
|
ExprPtr lhs, rhs;
|
|
};
|
|
|
|
class SubscriptExpr : public Expr
|
|
{
|
|
AST_NODE(SubscriptExpr);
|
|
public:
|
|
SubscriptExpr(ExprPtr &&object, ExprPtr &&index) : object(std::move(object)), index(std::move(index)) {}
|
|
ExprPtr object, index;
|
|
};
|
|
|
|
class SliceExpr : public Expr
|
|
{
|
|
AST_NODE(SliceExpr);
|
|
public:
|
|
SliceExpr(ExprPtr &&object, ExprPtr &&start, ExprPtr &&end) : object(std::move(object)), start(std::move(start)), end(std::move(end)) {}
|
|
ExprPtr object, start, end;
|
|
};
|
|
|
|
class CallExpr : public Expr
|
|
{
|
|
AST_NODE(CallExpr);
|
|
public:
|
|
bool safe_call = false; // Will this call fail gracefully when the function doesn't exist?
|
|
bool append_unnamed_pars = false; // Will this call append all unnamed parameters of the current function?
|
|
ExprPtr context;
|
|
std::vector<ExprPtr> args;
|
|
std::string callee;
|
|
};
|
|
|
|
class ParExpr : public Expr
|
|
{
|
|
AST_NODE(ParExpr);
|
|
public:
|
|
explicit ParExpr(ExprPtr &&arg) : arg(std::move(arg)) {}
|
|
ExprPtr arg;
|
|
};
|
|
|
|
class Block : public Stmt
|
|
{
|
|
AST_NODE(Block);
|
|
public:
|
|
std::vector<StmtPtr> children;
|
|
};
|
|
|
|
class ControlFlow : public Stmt
|
|
{};
|
|
|
|
class Return : public ControlFlow
|
|
{
|
|
AST_NODE(Return);
|
|
public:
|
|
explicit Return(ExprPtr &&value) : value(std::move(value)) {}
|
|
ExprPtr value;
|
|
};
|
|
|
|
class Loop : public ControlFlow
|
|
{
|
|
public:
|
|
ExprPtr cond;
|
|
StmtPtr body;
|
|
};
|
|
typedef std::unique_ptr<Loop> LoopPtr;
|
|
|
|
class ForLoop : public Loop
|
|
{
|
|
AST_NODE(ForLoop);
|
|
public:
|
|
StmtPtr init;
|
|
ExprPtr incr;
|
|
};
|
|
|
|
class RangeLoop : public Loop
|
|
{
|
|
AST_NODE(RangeLoop);
|
|
public:
|
|
std::string var;
|
|
bool scoped_var = false;
|
|
};
|
|
|
|
class DoLoop : public Loop
|
|
{
|
|
AST_NODE(DoLoop);
|
|
};
|
|
|
|
class WhileLoop : public Loop
|
|
{
|
|
AST_NODE(WhileLoop);
|
|
};
|
|
|
|
class LoopControl : public ControlFlow
|
|
{};
|
|
|
|
class Break : public LoopControl
|
|
{
|
|
AST_NODE(Break);
|
|
};
|
|
|
|
class Continue : public LoopControl
|
|
{
|
|
AST_NODE(Continue);
|
|
};
|
|
|
|
class If : public ControlFlow
|
|
{
|
|
AST_NODE(If);
|
|
public:
|
|
ExprPtr cond;
|
|
StmtPtr iftrue, iffalse;
|
|
};
|
|
|
|
class Decl : public Stmt
|
|
{};
|
|
typedef std::unique_ptr<Decl> DeclPtr;
|
|
|
|
class VarDecl : public Decl
|
|
{
|
|
AST_NODE(VarDecl);
|
|
public:
|
|
enum class Scope
|
|
{
|
|
Func,
|
|
Object,
|
|
Global
|
|
};
|
|
|
|
Scope scope;
|
|
bool constant;
|
|
|
|
struct Var
|
|
{
|
|
std::string name;
|
|
ExprPtr init;
|
|
};
|
|
std::vector<Var> decls;
|
|
};
|
|
|
|
class Function
|
|
{
|
|
public:
|
|
struct Parameter
|
|
{
|
|
std::string name;
|
|
C4V_Type type;
|
|
explicit Parameter(const std::string &name, C4V_Type type = C4V_Any) : name(name), type(type) {}
|
|
};
|
|
std::vector<Parameter> params;
|
|
bool has_unnamed_params = false;
|
|
std::unique_ptr<Block> body;
|
|
|
|
virtual ~Function() = default;
|
|
virtual void accept(::aul::AstVisitor *v) const = 0;
|
|
};
|
|
|
|
class FunctionDecl : public Decl, public Function
|
|
{
|
|
AST_NODE(FunctionDecl);
|
|
public:
|
|
explicit FunctionDecl(const std::string &name) : name(name) {}
|
|
std::string name;
|
|
bool is_global = false;
|
|
};
|
|
|
|
class FunctionExpr : public Expr, public Function
|
|
{
|
|
// This node is used for constant proplists
|
|
AST_NODE(FunctionExpr);
|
|
public:
|
|
};
|
|
|
|
class Pragma : public Decl
|
|
{};
|
|
|
|
class IncludePragma : public Pragma
|
|
{
|
|
AST_NODE(IncludePragma);
|
|
public:
|
|
explicit IncludePragma(const std::string &what) : what(what) {}
|
|
std::string what;
|
|
};
|
|
|
|
class AppendtoPragma : public Pragma
|
|
{
|
|
AST_NODE(AppendtoPragma);
|
|
public:
|
|
AppendtoPragma() = default;
|
|
explicit AppendtoPragma(const std::string &what) : what(what) {}
|
|
std::string what;
|
|
};
|
|
|
|
class Script : public Node
|
|
{
|
|
AST_NODE(Script);
|
|
public:
|
|
std::vector<DeclPtr> declarations;
|
|
};
|
|
|
|
#undef AST_NODE
|
|
|
|
}}
|
|
|
|
namespace aul {
|
|
// A recursive visitor that visits the children of all nodes. Override the visit() functions you're interested in in child classes.
|
|
class DefaultRecursiveVisitor : public AstVisitor
|
|
{
|
|
public:
|
|
~DefaultRecursiveVisitor() override = default;
|
|
|
|
using AstVisitor::visit;
|
|
|
|
void visit(const ::aul::ast::ArrayLit *n) override
|
|
{
|
|
for (const auto &c : n->values)
|
|
c->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::ProplistLit *n) override
|
|
{
|
|
for (const auto &c : n->values)
|
|
c.second->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::UnOpExpr *n) override
|
|
{
|
|
n->operand->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::BinOpExpr *n) override
|
|
{
|
|
n->lhs->accept(this);
|
|
n->rhs->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::AssignmentExpr *n) override
|
|
{
|
|
n->lhs->accept(this);
|
|
n->rhs->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::SubscriptExpr *n) override
|
|
{
|
|
n->object->accept(this);
|
|
n->index->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::SliceExpr *n) override
|
|
{
|
|
n->object->accept(this);
|
|
n->start->accept(this);
|
|
n->end->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::CallExpr *n) override
|
|
{
|
|
if (n->context)
|
|
n->context->accept(this);
|
|
for (const auto &a : n->args)
|
|
a->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::ParExpr *n) override
|
|
{
|
|
n->arg->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::Block *n) override
|
|
{
|
|
for (const auto &s : n->children)
|
|
s->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::Return *n) override
|
|
{
|
|
n->value->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::ForLoop *n) override
|
|
{
|
|
if (n->init)
|
|
n->init->accept(this);
|
|
if (n->cond)
|
|
n->cond->accept(this);
|
|
if (n->incr)
|
|
n->incr->accept(this);
|
|
n->body->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::RangeLoop *n) override
|
|
{
|
|
n->cond->accept(this);
|
|
n->body->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::DoLoop *n) override
|
|
{
|
|
n->body->accept(this);
|
|
n->cond->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::WhileLoop *n) override
|
|
{
|
|
n->cond->accept(this);
|
|
n->body->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::If *n) override
|
|
{
|
|
n->cond->accept(this);
|
|
n->iftrue->accept(this);
|
|
if (n->iffalse)
|
|
n->iffalse->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::VarDecl *n) override
|
|
{
|
|
for (const auto &d : n->decls)
|
|
if (d.init)
|
|
d.init->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::FunctionDecl *n) override
|
|
{
|
|
n->body->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::FunctionExpr *n) override
|
|
{
|
|
n->body->accept(this);
|
|
}
|
|
void visit(const ::aul::ast::Script *n) override
|
|
{
|
|
for (const auto &d : n->declarations)
|
|
d->accept(this);
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif
|