openclonk/src/script/C4AulAST.h

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