/*                                                              -*- c++ -*-
 *
 * Copyright (c) 2014 - 2019 Christian Schoenebeck and Andreas Persson
 * Copyright (c) 2020 - 2025 Christian Schoenebeck
 *
 * http://www.linuxsampler.org
 *
 * This file is part of LinuxSampler and released under the same terms.
 * See README file for details.
 */

// This header defines VM core implementation internal data types only used
// inside the parser and core VM implementation of this source directory. Not
// intended to be used in other source code parts (other source code
// directories) of the sampler.

#ifndef LS_INSTRPARSERTREE_H
#define LS_INSTRPARSERTREE_H

#include <vector>
#include <iostream>
#include <map>
#include <set>
#include <string.h> // for memset()
#include <assert.h>
#include "../common/global.h"
#include "../common/Ref.h"
#include "../common/ArrayList.h"
#include "../common/optional.h"
#include "common.h"

namespace LinuxSampler {
    
class ParserContext;
class ExecContext;

enum StmtType_t {
    STMT_LEAF,
    STMT_LIST,
    STMT_BRANCH,
    STMT_LOOP,
    STMT_SYNC,
    STMT_NOOP,
};

enum Qualifier_t {
    QUALIFIER_NONE = 0,
    QUALIFIER_CONST = 1,
    QUALIFIER_POLYPHONIC = (1<<1),
    QUALIFIER_PATCH = (1<<2),
    QUALIFIER_LOCAL = (1<<3),
};

struct PatchVarBlock {
    CodeBlock nameBlock;
    optional<CodeBlock> exprBlock;
};

enum class StackCtx_t : char {
    CURRENT,
    PREVIOUS
};

/**
 * Convenience function used for retrieving the (assumed) data type of a given
 * script variable name.
 *
 * @param name - some script variable name (e.g. "$foo")
 * @return variable's assumed data type (e.g. INT_EXPR for example above)
 */
inline ExprType_t exprTypeOfVarName(const String& name) {
    if (name.empty()) return (ExprType_t) -1;
    const char prefix = name[0];
    switch (prefix) {
        case '$': return INT_EXPR;
        case '%': return INT_ARR_EXPR;
        case '~': return REAL_EXPR;
        case '?': return REAL_ARR_EXPR;
        case '@': return STRING_EXPR;
        case '!': return STRING_ARR_EXPR;
    }
    return (ExprType_t) -1;
}

inline ExprType_t scalarTypeOfArray(ExprType_t arrayType) {
    if (arrayType == INT_ARR_EXPR) return INT_EXPR;
    if (arrayType == REAL_ARR_EXPR) return REAL_EXPR;
    if (arrayType == STRING_ARR_EXPR) return STRING_EXPR;
    assert(false);
    return EMPTY_EXPR; // just to shut up the compiler
}

inline String qualifierStr(Qualifier_t qualifier) {
    switch (qualifier) {
        case QUALIFIER_NONE:          return "none";
        case QUALIFIER_CONST:         return "const";
        case QUALIFIER_POLYPHONIC:    return "polyphonic";
        case QUALIFIER_PATCH:         return "patch";
        case QUALIFIER_LOCAL:         return "local";
    }
    return "unknown";
}

/**
 * Used by parser for parser error messages to provide a text with all data
 * types accepted by the given built-in function @a fn for the respective
 * function argument @a iArg.
 */
String acceptedArgTypesStr(VMFunction* fn, vmint iArg);

class Node {
public:
    Node();
    virtual ~Node();
    virtual void dump(int level = 0) = 0;
    virtual bool isPolyphonic() const = 0;
    void printIndents(int n);
};
typedef Ref<Node> NodeRef;

class DataStackUser : virtual public Node {
public:
    virtual void setDataStackCtx(StackCtx_t ctx) = 0;
};
typedef Ref<DataStackUser,Node> DataStackUserRef;

//TODO: right now only deriving class is UserFunctionCall, so this base class might be dropped if no other deriving class is added in future
class LocalDataObject : virtual public DataStackUser {
public:
    virtual void destructLocalDataObject() = 0;
    virtual void forkLocalDataObject() = 0;
};
typedef Ref<LocalDataObject,Node> LocalDataObjectRef;

class Statement;
class Expression;

class InnerExpr : virtual public Node {
    std::vector< Ref<Statement,Node> > preEvalStmts;
public:
    Statement* preEvalStatement(uint i) const;
protected:
    void addPreEvalStatement(Ref<Statement,Node> stmt);
    void addPreEvalStatement(Statement* stmt) = delete;
    void addInnerExpr(Ref<Expression,Node> exp);
    void addInnerExpr(Expression* exp) = delete;
    void addInnerExpr(Ref<Expression,Node> exp1, Ref<Expression,Node> exp2);
    void addInnerExpr(Expression* exp1, Expression* exp2) = delete;
};

class Expression : public InnerExpr, virtual public VMExpr {
public:
    virtual ExprType_t exprType() const = 0;
    virtual bool isConstExpr() const = 0;
    virtual String evalCastToStr() = 0;
};
typedef Ref<Expression,Node> ExpressionRef;

class Unit : virtual public VMUnit, virtual public Node {
    StdUnit_t unit;
public:
    Unit(StdUnit_t type) : unit(type) {}
    StdUnit_t unitType() const OVERRIDE { return unit; }
    static vmint convIntToUnitFactor(vmint iValue, VMUnit* srcUnit, VMUnit* dstUnit);
    static vmint convIntToUnitFactor(vmint iValue, vmfloat srcFactor, vmfloat dstFactor);
    static vmfloat convRealToUnitFactor(vmfloat fValue, VMUnit* srcUnit, VMUnit* dstUnit);
    static vmfloat convRealToUnitFactor(vmfloat fValue, vmfloat srcFactor, vmfloat dstFactor);
};
typedef Ref<Unit,Node> UnitRef;

class NumberExpr : virtual public Unit, virtual public VMNumberExpr, virtual public Expression {
public:
};
typedef Ref<NumberExpr,Node> NumberExprRef;

class IntExpr : virtual public NumberExpr, virtual public VMIntExpr {
public:
    ExprType_t exprType() const OVERRIDE { return INT_EXPR; }
    vmint evalIntToUnitFactor(vmfloat unitFactor);
    String evalCastToStr() OVERRIDE;
};
typedef Ref<IntExpr,Node> IntExprRef;

class RealExpr : virtual public NumberExpr, virtual public VMRealExpr  {
public:
    ExprType_t exprType() const OVERRIDE { return REAL_EXPR; }
    vmfloat evalRealToUnitFactor(vmfloat unitFactor);
    String evalCastToStr() OVERRIDE;
};
typedef Ref<RealExpr,Node> RealExprRef;

class ArrayExpr : virtual public VMArrayExpr, virtual public Expression {
public:
};
typedef Ref<ArrayExpr,Node> ArrayExprRef;

class NumberArrayExpr : virtual public ArrayExpr, virtual public VMNumberArrayExpr {
public:
};
typedef Ref<NumberArrayExpr,Node> NumberArrayExprRef;

class IntArrayExpr : virtual public NumberArrayExpr, virtual public VMIntArrayExpr {
public:
    ExprType_t exprType() const OVERRIDE { return INT_ARR_EXPR; }
    String evalCastToStr() OVERRIDE;
};
typedef Ref<IntArrayExpr,Node> IntArrayExprRef;

class RealArrayExpr : virtual public NumberArrayExpr, virtual public VMRealArrayExpr {
public:
    ExprType_t exprType() const OVERRIDE { return REAL_ARR_EXPR; }
    String evalCastToStr() OVERRIDE;
};
typedef Ref<RealArrayExpr,Node> RealArrayExprRef;

class StringExpr : virtual public VMStringExpr, virtual public Expression {
public:
    ExprType_t exprType() const OVERRIDE { return STRING_EXPR; }
    String evalCastToStr() OVERRIDE { return evalStr(); }
};
typedef Ref<StringExpr,Node> StringExprRef;

struct IntLitDef {
    vmint value; //NOTE: sequence matters! Since this is usually initialized with VMIntExpr::evalInt() it should be before member unitFactor, since the latter is usually initialized with VMIntExpr::unitFactor() which does not evaluate the expression.
    vmfloat unitFactor = VM_NO_FACTOR;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
};

class IntLiteral FINAL : public IntExpr {
    bool finalVal;
    vmfloat unitPrefixFactor;
    vmint value;
public:
    IntLiteral(const IntLitDef& def);
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return unitPrefixFactor; }
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return true; }
    bool isPolyphonic() const OVERRIDE { return false; }
    bool isFinal() const OVERRIDE { return finalVal; }
};
typedef Ref<IntLiteral,Node> IntLiteralRef;

struct RealLitDef {
    vmfloat value; //NOTE: sequence matters! Since this is usually initialized with VMRealExpr::evalReal() it should be before member unitFactor, since the latter is usually initialized with VMRealExpr::unitFactor() which does not evaluate the expression.
    vmfloat unitFactor = VM_NO_FACTOR;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
};

class RealLiteral FINAL : public RealExpr {
    bool finalVal;
    vmfloat unitPrefixFactor;
    vmfloat value;
public:
    RealLiteral(const RealLitDef& def);
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return unitPrefixFactor; }
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return true; }
    bool isPolyphonic() const OVERRIDE { return false; }
    bool isFinal() const OVERRIDE { return finalVal; }
};
typedef Ref<RealLiteral,Node> RealLiteralRef;

class StringLiteral FINAL : public StringExpr {
    String value;
public:
    StringLiteral(const String& value) : value(value) { }
    bool isConstExpr() const OVERRIDE { return true; }
    void dump(int level = 0) OVERRIDE;
    String evalStr() OVERRIDE { return value; }
    bool isPolyphonic() const OVERRIDE { return false; }
};
typedef Ref<StringLiteral,Node> StringLiteralRef;

class Args FINAL : public InnerExpr, virtual public VMFnArgs {
public:
    std::vector<ExpressionRef> args;

    void add(ExpressionRef arg) { args.push_back(arg); addInnerExpr(arg); }
    void dump(int level = 0) OVERRIDE;
    vmint argsCount() const OVERRIDE { return (vmint) args.size(); }
    VMExpr* arg(vmint i) OVERRIDE { return (i >= 0 && i < argsCount()) ? &*args.at(i) : NULL; }
    ExpressionRef argRef(vmint i) const { return (i >= 0 && i < argsCount()) ? args.at(i) : ExpressionRef(); }
    bool isPolyphonic() const OVERRIDE;
};
typedef Ref<Args,Node> ArgsRef;

struct VariableDecl {
    ParserContext* ctx;
    bool isPolyphonic;
    bool isConst;
    bool isLocal;
    bool acquireLocalMemPos = true;
    vmint elements = 1;
    vmint memPos;
    vmint unitFactorMemPos;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
    ExpressionRef initExpr;
};

struct LocalVarLoc {
    int valueOffset = -1;
    int unitFactorOffset = -1;
};

class Variable : virtual public VMVariable, virtual public Expression, virtual public DataStackUser {
public:
    bool isConstExpr() const OVERRIDE { return bConst; }
    bool isAssignable() const OVERRIDE { return !bConst; }
    virtual void assign(Expression* expr) = 0;
    void assignExpr(VMExpr* expr) OVERRIDE { Expression* e = dynamic_cast<Expression*>(expr); if (e) assign(e); }
    bool isLocalVar() const { return bLocal; }
    virtual void initLocalVar() {}
    ExpressionRef initializationExpr() const { return initExpr; }
    virtual void destructLocalVar() {}
    virtual void destructLocalVar(StackCtx_t ctx, LocalVarLoc loc) {}
    virtual void forkLocalVar() {}
    virtual void forkLocalVar(StackCtx_t ctx, LocalVarLoc loc) {}
    virtual LocalVarLoc acquireLocalVarLoc() = 0;
    virtual void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) = 0;
    void setDataStackCtx(StackCtx_t ctx) OVERRIDE { dataStackCtx = ctx; }
protected:
    Variable(const VariableDecl& decl);

    ParserContext* context;
    vmint memPos;
    bool bConst;
    bool bLocal;
    StackCtx_t dataStackCtx;
    ExpressionRef initExpr;
};
typedef Ref<Variable,Node> VariableRef;

class NumberVariable : public Variable, virtual public NumberExpr {
    bool polyphonic;
    bool finalVal;
protected:
    vmint unitFactorMemPos;
public:
    bool isPolyphonic() const OVERRIDE { return polyphonic; }
    bool isFinal() const OVERRIDE { return finalVal; }
    vmfloat unitFactor() const OVERRIDE;
    vmfloat unitFactor(StackCtx_t ctx, LocalVarLoc loc) const;
    void initLocalVar() OVERRIDE;
protected:
    NumberVariable(const VariableDecl& decl);
};
typedef Ref<NumberVariable,Node> NumberVariableRef;

class IntVariable : public NumberVariable, virtual public IntExpr {
public:
    IntVariable(const VariableDecl& decl);
    void assign(Expression* expr) OVERRIDE;
    vmint evalInt() OVERRIDE;
    vmint evalInt(StackCtx_t ctx, LocalVarLoc loc) const;
    void dump(int level = 0) OVERRIDE;
    LocalVarLoc acquireLocalVarLoc() OVERRIDE;
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
};
typedef Ref<IntVariable,Node> IntVariableRef;

class RealVariable : public NumberVariable, virtual public RealExpr {
public:
    RealVariable(const VariableDecl& decl);
    void assign(Expression* expr) OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat evalReal(StackCtx_t ctx, LocalVarLoc loc) const;
    void dump(int level = 0) OVERRIDE;
    LocalVarLoc acquireLocalVarLoc() OVERRIDE;
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
};
typedef Ref<RealVariable,Node> RealVariableRef;

struct IntVarDef /* : VariableDecl*/ { //NOTE: derived, aggregate initializer-lists requires C++17
    // additions for IntVarDef
    vmint value = 0; //NOTE: sequence matters! Since this is usually initialized with VMIntExpr::evalInt() it should be before member unitFactor, since the latter is usually initialized with VMIntExpr::unitFactor() which does not evaluate the expression.
    vmfloat unitFactor = VM_NO_FACTOR;
    // copied from VariableDecl
    ParserContext* ctx;
    bool isPolyphonic;
    bool isConst;
    bool isLocal;
    bool acquireLocalMemPos = true;
    vmint elements = 1;
    vmint memPos;
    vmint unitFactorMemPos;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
};

class ConstIntVariable FINAL : public IntVariable {
    vmfloat unitPrefixFactor;
    vmint value;
public:
    ConstIntVariable(const IntVarDef& def);
    void assign(Expression* expr) OVERRIDE;
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return unitPrefixFactor; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<ConstIntVariable,Node> ConstIntVariableRef;

struct RealVarDef /* : VariableDecl*/ { //NOTE: derived, aggregate initializer-lists requires C++17
    // additions for RealVarDef
    vmfloat value = vmfloat(0); //NOTE: sequence matters! Since this is usually initialized with VMRealExpr::evalReal() it should be before member unitFactor, since the latter is usually initialized with VMRealExpr::unitFactor() which does not evaluate the expression.
    vmfloat unitFactor = VM_NO_FACTOR;
    // copied from VariableDecl
    ParserContext* ctx;
    bool isPolyphonic;
    bool isConst;
    bool isLocal;
    bool acquireLocalMemPos = true;
    vmint elements = 1;
    vmint memPos;
    vmint unitFactorMemPos;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
};

class ConstRealVariable FINAL : public RealVariable {
    vmfloat unitPrefixFactor;
    vmfloat value;
public:
    ConstRealVariable(const RealVarDef& def);
    void assign(Expression* expr) OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return unitPrefixFactor; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<ConstRealVariable,Node> ConstRealVariableRef;

class BuiltInIntVariable FINAL : public IntVariable {
    String name;
    VMIntPtr* ptr;
public:
    BuiltInIntVariable(const String& name, VMIntPtr* ptr);
    bool isAssignable() const OVERRIDE { return ptr->isAssignable(); }
    void assign(Expression* expr) OVERRIDE;
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<BuiltInIntVariable,Node> BuiltInIntVariableRef;

struct ArrayVarDef /* : VariableDecl*/ { //NOTE: derived, aggregate initializer-lists requires C++17
    // copied from VariableDecl
    ParserContext* ctx;
    bool isPolyphonic;
    bool isConst;
    bool isLocal;
    bool acquireLocalMemPos = true;
    vmint elements = 1;
    vmint memPos;
    vmint unitFactorMemPos;
    StdUnit_t unitType = VM_NO_UNIT;
    bool isFinal;
    // additions for ArrayVarDef
    ArgsRef values;
};

class ArrayVariable : public Variable, virtual public ArrayExpr {
protected:
    vmint nElements;
    ArgsRef initValues;
public:
    virtual void assign(Args* args) = 0;
    ArgsRef initializationArgs() const { return initValues; }
    bool isPolyphonic() const OVERRIDE { return false; }
protected:
    ArrayVariable(const ArrayVarDef& decl);
};
typedef Ref<ArrayVariable,Node> ArrayVariableRef;

class NumberArrayVariable : public ArrayVariable, virtual public NumberArrayExpr {
protected:
    ArrayList<vmfloat> unitFactors;
    vmint unitFactorMemPos;
public:
    vmfloat unitFactorOfElement(vmuint i) const OVERRIDE;
    vmfloat unitFactorOfElement(vmuint i, StackCtx_t ctx, LocalVarLoc loc) const;
    void assignElementUnitFactor(vmuint i, vmfloat factor) OVERRIDE;
    void initLocalVar() OVERRIDE;
protected:
    NumberArrayVariable(const ArrayVarDef& decl);
};
typedef Ref<NumberArrayVariable,Node> NumberArrayVariableRef;

class IntArrayVariable : public NumberArrayVariable, virtual public IntArrayExpr {
    ArrayList<vmint> values;
public:
    IntArrayVariable(const ArrayVarDef& decl);
    ExprType_t exprType() const OVERRIDE { return INT_ARR_EXPR; }
    vmint arraySize() const OVERRIDE;
    vmint evalIntElement(vmuint i) OVERRIDE;
    vmint evalIntElement(vmuint i, StackCtx_t ctx, LocalVarLoc loc) const;
    void assign(Expression* expr) OVERRIDE { assert(false); } //TODO: implement whole-array assignment
    void assign(Args* args) OVERRIDE;
    void assignIntElement(vmuint i, vmint value) OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    LocalVarLoc acquireLocalVarLoc() OVERRIDE;
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
};
typedef Ref<IntArrayVariable,Node> IntArrayVariableRef;

class RealArrayVariable FINAL : public NumberArrayVariable, virtual public RealArrayExpr {
    ArrayList<vmfloat> values;
public:
    RealArrayVariable(const ArrayVarDef& decl);
    ExprType_t exprType() const OVERRIDE { return REAL_ARR_EXPR; }
    vmint arraySize() const OVERRIDE;
    vmfloat evalRealElement(vmuint i) OVERRIDE;
    vmfloat evalRealElement(vmuint i, StackCtx_t ctx, LocalVarLoc loc) const;
    void assign(Expression* expr) OVERRIDE { assert(false); } //TODO: implement whole-array assignment
    void assign(Args* args) OVERRIDE;
    void assignRealElement(vmuint i, vmfloat value) OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    LocalVarLoc acquireLocalVarLoc() OVERRIDE;
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
};
typedef Ref<RealArrayVariable,Node> RealArrayVariableRef;

class BuiltInIntArrayVariable FINAL : public IntArrayVariable {
    String name;
    VMInt8Array* array;
public:
    BuiltInIntArrayVariable(const String& name, VMInt8Array* array);
    vmint arraySize() const OVERRIDE { return array->size; }
    vmint evalIntElement(vmuint i) OVERRIDE;
    void assignIntElement(vmuint i, vmint value) OVERRIDE;
    vmfloat unitFactorOfElement(vmuint i) const OVERRIDE { return VM_NO_FACTOR; }
    void assignElementUnitFactor(vmuint i, vmfloat factor) OVERRIDE {}
    bool isAssignable() const OVERRIDE { return !array->readonly; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<BuiltInIntArrayVariable,Node> BuiltInIntArrayVariableRef;

class IntArrayElement FINAL : public IntVariable {
    IntArrayExprRef array;
    IntExprRef index;
    vmint currentIndex;
public:
    IntArrayElement(IntArrayExprRef array, IntExprRef arrayIndex);
    void assign(Expression* expr) OVERRIDE;
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<IntArrayElement,Node> IntArrayElementRef;

class RealArrayElement FINAL : public RealVariable {
    RealArrayExprRef array;
    IntExprRef index;
    vmint currentIndex;
public:
    RealArrayElement(RealArrayExprRef array, IntExprRef arrayIndex);
    void assign(Expression* expr) OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<RealArrayElement,Node> RealArrayElementRef;

class StringVariable : public Variable, virtual public StringExpr {
public:
    StringVariable(ParserContext* ctx);
    void assign(Expression* expr) OVERRIDE;
    String evalStr() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return false; }
    LocalVarLoc acquireLocalVarLoc() OVERRIDE { assert(false); }
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE { assert(false); }
protected:
    StringVariable(ParserContext* ctx, bool bConst, bool bLocal);
    StringVariable(ParserContext* ctx, bool bConst, bool bLocal, ExpressionRef initExpr);
};
typedef Ref<StringVariable,Node> StringVariableRef;

class ConstStringVariable FINAL : public StringVariable {
    String value;
public:
    ConstStringVariable(ParserContext* ctx, String value = "");
    void assign(Expression* expr) OVERRIDE;
    String evalStr() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<ConstStringVariable,Node> ConstStringVariableRef;

class LocalStringVariable : public StringVariable {
public:
    LocalStringVariable(ParserContext* ctx, StringExprRef initExpr = StringExprRef());
    void assign(Expression* expr) OVERRIDE;
    String evalStr() OVERRIDE;
    String evalStr(StackCtx_t ctx, LocalVarLoc loc) const;
    void dump(int level = 0) OVERRIDE;
    void initLocalVar() OVERRIDE;
    void destructLocalVar() OVERRIDE;
    void destructLocalVar(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
    void forkLocalVar() OVERRIDE;
    void forkLocalVar(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
    LocalVarLoc acquireLocalVarLoc() OVERRIDE;
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE;
};
typedef Ref<LocalStringVariable,Node> LocalStringVariableRef;

class Params FINAL : public Node {
public:
    void add(const char* paramName, VariableRef paramVar);
    void dump(int level = 0) OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return false; };
    vmint paramsCount() const { return (vmint) params.size(); }
    vmint minRequiredArgs() const;
    vmint maxAllowedArgs() const { return paramsCount(); }
    String paramName(vmint i) const { return (i >= 0 && i < paramsCount()) ? params.at(i).name : ""; }
    Variable* param(vmint i) { return (i >= 0 && i < paramsCount()) ? &*params.at(i).var : NULL; }
    const Variable* param(vmint i) const { return (i >= 0 && i < paramsCount()) ? &*params.at(i).var : NULL; }
    VariableRef paramRef(vmint i) { return (i >= 0 && i < paramsCount()) ? params.at(i).var : VariableRef(); }
    Variable* lastParam() { return paramsCount() ? param(paramsCount() - 1) : NULL; }
private:
    struct Param {
        String name;
        VariableRef var;
    };
    std::vector<Param> params;
};
typedef Ref<Params,Node> ParamsRef;

class BinaryOp : virtual public Expression {
protected:
    ExpressionRef lhs;
    ExpressionRef rhs;
public:
    BinaryOp(ExpressionRef lhs, ExpressionRef rhs) : lhs(lhs), rhs(rhs) { Expression::addInnerExpr(lhs, rhs); }
    bool isConstExpr() const OVERRIDE { return lhs->isConstExpr() && rhs->isConstExpr(); }
    bool isPolyphonic() const OVERRIDE { return lhs->isPolyphonic() || rhs->isPolyphonic(); }
};
typedef Ref<BinaryOp,Node> BinaryOpRef;

class NumberBinaryOp : public BinaryOp, virtual public NumberExpr {
public:
    NumberBinaryOp(NumberExprRef lhs, NumberExprRef rhs) : BinaryOp(lhs, rhs) { }
    bool isFinal() const OVERRIDE;
};
typedef Ref<NumberBinaryOp,Node> NumberBinaryOpRef;

class IntBinaryOp : public NumberBinaryOp, virtual public IntExpr {
public:
    IntBinaryOp(IntExprRef lhs, IntExprRef rhs) : NumberBinaryOp(lhs, rhs) { }
};
typedef Ref<IntBinaryOp,Node> IntBinaryOpRef;

class VaritypeScalarBinaryOp : public NumberBinaryOp, virtual public IntExpr, virtual public RealExpr {
public:
    VaritypeScalarBinaryOp(NumberExprRef lhs, NumberExprRef rhs) : NumberBinaryOp(lhs, rhs) { }
    ExprType_t exprType() const OVERRIDE;
    String evalCastToStr() OVERRIDE;
};
typedef Ref<VaritypeScalarBinaryOp,Node> VaritypeScalarBinaryOpRef;

class Add FINAL : public VaritypeScalarBinaryOp {
public:
    Add(NumberExprRef lhs, NumberExprRef rhs);
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<Add,Node> AddRef;

class Sub FINAL : public VaritypeScalarBinaryOp {
public:
    Sub(NumberExprRef lhs, NumberExprRef rhs);
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<Sub,Node> SubRef;

class Mul FINAL : public VaritypeScalarBinaryOp {
public:
    Mul(NumberExprRef lhs, NumberExprRef rhs);
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<Mul,Node> MulRef;

class Div FINAL : public VaritypeScalarBinaryOp {
public:
    Div(NumberExprRef lhs, NumberExprRef rhs);
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
};
typedef Ref<Div,Node> DivRef;

class Mod FINAL : public IntBinaryOp {
public:
    Mod(IntExprRef lhs, IntExprRef rhs) : IntBinaryOp(lhs, rhs), Unit(VM_NO_UNIT) { }
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<Mod,Node> ModRef;

class Statement : virtual public Node {
public:
    virtual StmtType_t statementType() const = 0;
    virtual Statement* dependencyStatement(uint i) const = 0;
};
typedef Ref<Statement,Node> StatementRef;

// Just used by parser to avoid "not a statement" parser warning, will be
// filtered out by parser. So it will not be part of the VM tree after parsing.
class NoOperation FINAL : public Statement {
public:
    NoOperation() : Statement() {}
    StmtType_t statementType() const OVERRIDE { return STMT_NOOP; }
    void dump(int level = 0) OVERRIDE {}
    bool isPolyphonic() const OVERRIDE { return false; }
    Statement* dependencyStatement(uint i) const OVERRIDE { return NULL; }
};
typedef Ref<NoOperation,Node> NoOperationRef;

bool isNoOperation(StatementRef statement);

class LeafStatement : public Statement {
public:
    virtual StmtFlags_t exec() = 0;
    StmtType_t statementType() const OVERRIDE { return STMT_LEAF; }
};
typedef Ref<LeafStatement,Node> LeafStatementRef;

class Statements : public Statement {
    std::vector<StatementRef> stmts;
    std::vector<LocalDataObject*> localObjs;
public:
    Statements() {}
    Statements(Ref<Statements,Node> arg);
    void addFirst(StatementRef arg);
    void addAtPos(StatementRef arg, size_t pos);
    void add(StatementRef arg);
    void add(Ref<Statements,Node> arg);
    void dump(int level = 0) OVERRIDE;
    StmtType_t statementType() const OVERRIDE { return STMT_LIST; }
    virtual uint startStatement() const { return 0; }
    virtual Statement* statement(uint i);
    Statement* dependencyStatement(uint i) const OVERRIDE { return NULL; }
    bool isPolyphonic() const OVERRIDE;
    virtual uint localDataSize() const { return 0; }
    virtual bool needsNewDataStackFrame() const { return localDataSize(); }
    uint dataStackFrameSize() const;
    virtual void constructLocalData() {}
    virtual void destructLocalData();
    virtual void forkLocalData();
private:
    void collectLocalDataObjects();
    void collectLocalDataObjectsOf(Statement* stmt);
};
typedef Ref<Statements,Node> StatementsRef;

class BranchStatement : public Statement {
public:
    StmtType_t statementType() const OVERRIDE { return STMT_BRANCH; }
    virtual vmint evalBranch() = 0;
    virtual Statements* branch(vmuint i) const = 0;
};

class DynamicVariableCall FINAL : public Variable, virtual public IntExpr, virtual public StringExpr, virtual public IntArrayExpr {
    VMDynVar* dynVar;
    String varName;
public:
    DynamicVariableCall(const String& name, ParserContext* ctx, VMDynVar* v);
    ExprType_t exprType() const OVERRIDE { return dynVar->exprType(); }
    bool isConstExpr() const OVERRIDE { return dynVar->isConstExpr(); }
    bool isAssignable() const OVERRIDE { return dynVar->isAssignable(); }
    bool isPolyphonic() const OVERRIDE { return false; }
    void assign(Expression* expr) OVERRIDE { dynVar->assignExpr(expr); }
    VMIntArrayExpr* asIntArray() const OVERRIDE { return dynVar->asIntArray(); }
    vmint evalInt() OVERRIDE;
    String evalStr() OVERRIDE;
    String evalCastToStr() OVERRIDE;
    vmint arraySize() const OVERRIDE { return dynVar->asIntArray()->arraySize(); }
    vmint evalIntElement(vmuint i) OVERRIDE { return dynVar->asIntArray()->evalIntElement(i); }
    void assignIntElement(vmuint i, vmint value) OVERRIDE { return dynVar->asIntArray()->assignIntElement(i, value); }
    vmfloat unitFactorOfElement(vmuint i) const OVERRIDE { return VM_NO_FACTOR; }
    void assignElementUnitFactor(vmuint i, vmfloat factor) OVERRIDE {}
    void dump(int level = 0) OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    bool isFinal() const OVERRIDE { return false; }
    LocalVarLoc acquireLocalVarLoc() OVERRIDE { assert(false); }
    void moveToVarLoc(StackCtx_t ctx, LocalVarLoc loc) OVERRIDE { assert(false); }
};
typedef Ref<DynamicVariableCall,Node> DynamicVariableCallRef;

class FunctionCall : virtual public LeafStatement, virtual public IntExpr, virtual public RealExpr, virtual public StringExpr, virtual public ArrayExpr {
    String functionName;
    ArgsRef args;
    VMFunction* fn;
    VMFnResult* result;
public:
    FunctionCall(const char* function, ArgsRef args, VMFunction* fn);
    virtual ~FunctionCall();
    void dump(int level = 0) OVERRIDE;
    StmtFlags_t exec() OVERRIDE;
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    vmint arraySize() const OVERRIDE;
    VMIntArrayExpr* asIntArray() const OVERRIDE;
    VMRealArrayExpr* asRealArray() const OVERRIDE;
    String evalStr() OVERRIDE;
    bool isConstExpr() const OVERRIDE { return false; }
    ExprType_t exprType() const OVERRIDE;
    String evalCastToStr() OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return args->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE;
    bool isFinal() const OVERRIDE;
    Statement* dependencyStatement(uint i) const OVERRIDE { return args->preEvalStatement(i); }
protected:
    VMFnResult* execVMFn();
};
typedef Ref<FunctionCall,Node> FunctionCallRef;

class NoFunctionCall FINAL : public FunctionCall {
public:
    NoFunctionCall() : FunctionCall("nothing", new Args, NULL), Unit(VM_NO_UNIT) {}
    StmtType_t statementType() const OVERRIDE { return STMT_NOOP; }
};
typedef Ref<NoFunctionCall,Node> NoFunctionCallRef;

class Subroutine : public Statements {
    uint localDataSz;
protected:
    std::vector<VariableRef> localVars;
public:
    Subroutine(ParserContext* ctx, StatementsRef statements);
    void dump(int level = 0) OVERRIDE;
    void assignLocalVariables(ParserContext* ctx);
    uint localDataSize() const OVERRIDE { return localDataSz; }
    void constructLocalData() OVERRIDE;
    void destructLocalData() OVERRIDE;
private:
    typedef Statements Super;
};
typedef Ref<Subroutine,Node> SubroutineRef;

class UserFunction : public Subroutine {
    ParamsRef parms;
    VariableRef resVar;
    uint minRequiredArgs;
    uint startStmt;
public:
    UserFunction(ParserContext* ctx, StatementsRef statements);
    uint startStatement() const OVERRIDE { return startStmt; }
    void setArgumentsPassed(uint n);
    void assignParams(ParamsRef params);
    void assignResultVar(VariableRef var);
    void assignLocalVariables(ParserContext* ctx);
    Params* params() { return &*parms; }
    Variable* resultVar() { return resVar ? &*resVar : NULL; }
    void constructLocalData() OVERRIDE;
    void destructLocalData() OVERRIDE;
    Statement* dependencyStatement(uint i) const OVERRIDE { return NULL; }
protected:
    bool isFunctionParam(const Variable* localVar) const;
private:
    typedef Subroutine Super;
};
typedef Ref<UserFunction,Node> UserFunctionRef;

class UserFunctionCall FINAL :
    virtual public Statements, virtual public IntExpr, virtual public RealExpr,
    virtual public IntArrayExpr, virtual public RealArrayExpr,
    virtual public StringExpr, virtual public LocalDataObject
{
    String fnName;
    UserFunctionRef fn;
    ArgsRef args;
    LocalVarLoc resultVarLoc;
    StackCtx_t dataStackCtx;
public:
    UserFunctionCall(const char* function, UserFunctionRef fn, ArgsRef args);
    //FIXME: needs a Ref<> object to register itself to Expression subclass, so this can't be done directly in the constructor unfortunately
    void initUserFunctionCall(Ref<UserFunctionCall,Node> self);
    void initUserFunctionCall(UserFunctionCall* self) = delete;
    String functionName() const { return fnName; }
    void dump(int level = 0) OVERRIDE;
    uint localDataSize() const OVERRIDE { return fn->localDataSize(); }
    uint startStatement() const OVERRIDE { return fn->startStatement(); }
    Statement* statement(uint i) OVERRIDE { return fn->statement(i); }
    Statement* dependencyStatement(uint i) const OVERRIDE { return args->preEvalStatement(i); }
    vmint evalInt() OVERRIDE;
    vmfloat evalReal() OVERRIDE;
    String evalStr() OVERRIDE;

    vmint arraySize() const OVERRIDE;
    vmint evalIntElement(vmuint i) OVERRIDE;
    void assignIntElement(vmuint i, vmint value) OVERRIDE;
    vmfloat evalRealElement(vmuint i) OVERRIDE;
    void assignRealElement(vmuint i, vmfloat value) OVERRIDE;
    vmfloat unitFactorOfElement(vmuint i) const OVERRIDE;
    void assignElementUnitFactor(vmuint i, vmfloat factor) OVERRIDE;

    bool isConstExpr() const OVERRIDE { return false; }
    ExprType_t exprType() const OVERRIDE;
    String evalCastToStr() OVERRIDE;
    bool isPolyphonic() const OVERRIDE;
    vmfloat unitFactor() const OVERRIDE;
    bool isFinal() const OVERRIDE;
    void constructLocalData() OVERRIDE;
    void destructLocalData() OVERRIDE;
    void forkLocalData() OVERRIDE;
    void destructLocalDataObject() OVERRIDE;
    void forkLocalDataObject() OVERRIDE;
    void setDataStackCtx(StackCtx_t ctx) OVERRIDE { dataStackCtx = ctx; }
};
typedef Ref<UserFunctionCall,Node> UserFunctionCallRef;

class EventHandler : public Subroutine, virtual public VMEventHandler {
    bool usingPolyphonics;
public:
    void dump(int level = 0) OVERRIDE;
    EventHandler(ParserContext* ctx, StatementsRef statements);
    bool isPolyphonic() const OVERRIDE { return usingPolyphonics; }
};
typedef Ref<EventHandler,Node> EventHandlerRef;

class OnNote FINAL : public EventHandler {
public:
    OnNote(ParserContext* ctx, StatementsRef statements) : EventHandler(ctx, statements) {}
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_NOTE; }
    String eventHandlerName() const OVERRIDE { return "note"; }
};
typedef Ref<OnNote,Node> OnNoteRef;

class OnInit FINAL : public EventHandler {
public:
    OnInit(ParserContext* ctx, StatementsRef statements);
    OnInit(ParserContext* ctx, StatementsRef globalStatements, StatementsRef statements);
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_INIT; }
    String eventHandlerName() const OVERRIDE { return "init"; }
};
typedef Ref<OnInit,Node> OnInitRef;

class OnRelease FINAL : public EventHandler {
public:
    OnRelease(ParserContext* ctx, StatementsRef statements) : EventHandler(ctx, statements) {}
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_RELEASE; }
    String eventHandlerName() const OVERRIDE { return "release"; }
};
typedef Ref<OnRelease,Node> OnReleaseRef;

class OnController FINAL : public EventHandler {
public:
    OnController(ParserContext* ctx, StatementsRef statements) : EventHandler(ctx, statements) {}
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_CONTROLLER; }
    String eventHandlerName() const OVERRIDE { return "controller"; }
};
typedef Ref<OnController,Node> OnControllerRef;

class OnRpn FINAL : public EventHandler {
public:
    OnRpn(ParserContext* ctx, StatementsRef statements) : EventHandler(ctx, statements) {}
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_RPN; }
    String eventHandlerName() const OVERRIDE { return "rpn"; }
};
typedef Ref<OnRpn,Node> OnRpnRef;

class OnNrpn FINAL : public EventHandler {
public:
    OnNrpn(ParserContext* ctx, StatementsRef statements) : EventHandler(ctx, statements) {}
    VMEventHandlerType_t eventHandlerType() const OVERRIDE { return VM_EVENT_HANDLER_NRPN; }
    String eventHandlerName() const OVERRIDE { return "nrpn"; }
};
typedef Ref<OnNrpn,Node> OnNrpnRef;

class EventHandlers FINAL : virtual public Node {
    std::vector<EventHandlerRef> args;
public:
    EventHandlers();
    ~EventHandlers();
    void add(EventHandlerRef arg);
    void dump(int level = 0) OVERRIDE;
    EventHandler* eventHandlerByName(const String& name) const;
    EventHandler* eventHandler(uint index) const;
    inline uint size() const { return (int) args.size(); }
    bool isPolyphonic() const OVERRIDE;
};
typedef Ref<EventHandlers,Node> EventHandlersRef;

class Assignment FINAL : public LeafStatement {
protected:
    VariableRef variable;
    ExpressionRef value;
public:
    Assignment(VariableRef variable, ExpressionRef value);
    void dump(int level = 0) OVERRIDE;
    StmtFlags_t exec() OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return (variable && variable->isPolyphonic()) || (value && value->isPolyphonic()); }
    Statement* dependencyStatement(uint i) const OVERRIDE { return value->preEvalStatement(i); }
};
typedef Ref<Assignment,Node> AssignmentRef;

class ArrayArgsAssignment FINAL : public LeafStatement {
    ArrayVariableRef variable;
    ArgsRef values;
public:
    ArrayArgsAssignment(ArrayVariableRef variable, ArgsRef values);
    void dump(int level = 0) OVERRIDE;
    StmtFlags_t exec() OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return (variable && variable->isPolyphonic()) || (values && values->isPolyphonic()); }
    Statement* dependencyStatement(uint i) const OVERRIDE { return values->preEvalStatement(i); }
};
typedef Ref<ArrayArgsAssignment,Node> ArrayArgsAssignmentRef;

class If FINAL : public BranchStatement {
    IntExprRef condition;
    StatementsRef ifStatements;
    StatementsRef elseStatements;
public:
    If(IntExprRef condition, StatementsRef ifStatements, StatementsRef elseStatements) :
        condition(condition), ifStatements(ifStatements), elseStatements(elseStatements) { }
    If(IntExprRef condition, StatementsRef statements) :
        condition(condition), ifStatements(statements) { }
    void dump(int level = 0) OVERRIDE;
    vmint evalBranch() OVERRIDE;
    Statements* branch(vmuint i) const OVERRIDE;
    bool isPolyphonic() const OVERRIDE;
    Statement* dependencyStatement(uint i) const OVERRIDE { return condition->preEvalStatement(i); }
};
typedef Ref<If,Node> IfRef;

struct CaseBranch FINAL {
    IntExprRef from;
    IntExprRef to;
    StatementsRef statements;
};
typedef std::vector<CaseBranch> CaseBranches;

class SelectCase FINAL : public BranchStatement, public InnerExpr {
    IntExprRef select;
    CaseBranches branches;
public:
    SelectCase(IntExprRef select, const CaseBranches& branches);
    void dump(int level = 0) OVERRIDE;
    vmint evalBranch() OVERRIDE;
    Statements* branch(vmuint i) const OVERRIDE;
    bool isPolyphonic() const OVERRIDE;
    Statement* dependencyStatement(uint i) const OVERRIDE { return preEvalStatement(i); }
};
typedef Ref<SelectCase,Node> SelectCaseRef;

class While FINAL : public Statement, public InnerExpr {
    IntExprRef m_condition;
    StatementsRef m_statements;
public:
    While(IntExprRef condition, StatementsRef statements) :
        m_condition(condition), m_statements(statements) { addInnerExpr(condition); }
    StmtType_t statementType() const OVERRIDE { return STMT_LOOP; }
    void dump(int level = 0) OVERRIDE;
    bool evalLoopStartCondition();
    Statements* statements() const;
    bool isPolyphonic() const OVERRIDE { return m_condition->isPolyphonic() || m_statements->isPolyphonic(); }
    Statement* dependencyStatement(uint i) const OVERRIDE { return preEvalStatement(i); }
};

class SyncBlock FINAL : public Statement {
    StatementsRef m_statements;
public:
    SyncBlock(StatementsRef statements) : m_statements(statements) {}
    StmtType_t statementType() const OVERRIDE { return STMT_SYNC; }
    void dump(int level = 0) OVERRIDE;
    Statements* statements() const;
    bool isPolyphonic() const OVERRIDE { return m_statements->isPolyphonic(); }
    Statement* dependencyStatement(uint i) const OVERRIDE { return NULL; }
};
typedef Ref<SyncBlock,Node> SyncBlockRef;

class Neg FINAL : virtual public IntExpr, virtual public RealExpr {
    NumberExprRef expr;
public:
    Neg(NumberExprRef expr);
    ExprType_t exprType() const OVERRIDE { return expr->exprType(); }
    vmint evalInt() OVERRIDE { return (expr) ? -(expr->asInt()->evalInt()) : 0; }
    vmfloat evalReal() OVERRIDE { return (expr) ? -(expr->asReal()->evalReal()) : vmfloat(0); }
    String evalCastToStr() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return expr->isConstExpr(); }
    bool isPolyphonic() const OVERRIDE { return expr->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE { return expr->unitFactor(); }
    bool isFinal() const OVERRIDE { return expr->isFinal(); }
};
typedef Ref<Neg,Node> NegRef;

class ConcatString FINAL : public StringExpr {
    ExpressionRef lhs;
    ExpressionRef rhs;
public:
    ConcatString(ExpressionRef lhs, ExpressionRef rhs);
    String evalStr() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return lhs->isPolyphonic() || rhs->isPolyphonic(); }
};
typedef Ref<ConcatString,Node> ConcatStringRef;

class Relation FINAL : public IntExpr {
public:
    enum Type {
        LESS_THAN,
        GREATER_THAN,
        LESS_OR_EQUAL,
        GREATER_OR_EQUAL,
        EQUAL,
        NOT_EQUAL
    };
    Relation(ExpressionRef lhs, Type type, ExpressionRef rhs);
    vmint evalInt() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE;
    bool isPolyphonic() const OVERRIDE { return lhs->isPolyphonic() || rhs->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    bool isFinal() const OVERRIDE { return false; }
private:
    ExpressionRef lhs;
    ExpressionRef rhs;
    Type type;
};
typedef Ref<Relation,Node> RelationRef;

class Or FINAL : public IntBinaryOp {
public:
    Or(IntExprRef lhs, IntExprRef rhs) : IntBinaryOp(lhs,rhs), Unit(VM_NO_UNIT) {}
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<Or,Node> OrRef;

class BitwiseOr FINAL : public IntBinaryOp {
public:
    BitwiseOr(IntExprRef lhs, IntExprRef rhs) : IntBinaryOp(lhs,rhs), Unit(VM_NO_UNIT) {}
    vmint evalInt() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
};
typedef Ref<BitwiseOr,Node> BitwiseOrRef;

class And FINAL : public IntBinaryOp {
public:
    And(IntExprRef lhs, IntExprRef rhs) : IntBinaryOp(lhs,rhs), Unit(VM_NO_UNIT) {}
    vmint evalInt() OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    void dump(int level = 0) OVERRIDE;
};
typedef Ref<And,Node> AndRef;

class BitwiseAnd FINAL : public IntBinaryOp {
public:
    BitwiseAnd(IntExprRef lhs, IntExprRef rhs) : IntBinaryOp(lhs,rhs), Unit(VM_NO_UNIT) {}
    vmint evalInt() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
};
typedef Ref<BitwiseAnd,Node> BitwiseAndRef;

class Not FINAL : virtual public IntExpr {
    IntExprRef expr;
public:
    Not(IntExprRef expr);
    vmint evalInt() OVERRIDE { return !expr->evalInt(); }
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return expr->isConstExpr(); }
    bool isPolyphonic() const OVERRIDE { return expr->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    bool isFinal() const OVERRIDE { return expr->isFinal(); }
};
typedef Ref<Not,Node> NotRef;

class BitwiseNot FINAL : virtual public IntExpr {
    IntExprRef expr;
public:
    BitwiseNot(IntExprRef expr);
    vmint evalInt() OVERRIDE { return ~expr->evalInt(); }
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return expr->isConstExpr(); }
    bool isPolyphonic() const OVERRIDE { return expr->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE { return VM_NO_FACTOR; }
    bool isFinal() const OVERRIDE { return expr->isFinal(); }
};
typedef Ref<BitwiseNot,Node> BitwiseNotRef;

class Final FINAL : virtual public IntExpr, virtual public RealExpr {
    NumberExprRef expr;
public:
    Final(NumberExprRef expr);
    ExprType_t exprType() const OVERRIDE { return expr->exprType(); }
    vmint evalInt() OVERRIDE { return expr->asInt()->evalInt(); }
    vmfloat evalReal() OVERRIDE { return expr->asReal()->evalReal(); }
    String evalCastToStr() OVERRIDE;
    void dump(int level = 0) OVERRIDE;
    bool isConstExpr() const OVERRIDE { return expr->isConstExpr(); }
    bool isPolyphonic() const OVERRIDE { return expr->isPolyphonic(); }
    vmfloat unitFactor() const OVERRIDE { return expr->unitFactor(); }
    bool isFinal() const OVERRIDE { return true; }
};
typedef Ref<Final,Node> FinalRef;

class ParserContext FINAL : public VMParserContext {
public:
    struct Error {
        String txt;
        int line;
    };
    typedef Error Warning;

    void* scanner;
    std::istream* is;
    int nbytes;
    std::vector<ParserIssue> vErrors;
    std::vector<ParserIssue> vWarnings;
    std::vector<ParserIssue> vIssues;
    std::vector<CodeBlock>   vPreprocessorComments;
    std::vector<void*>       vAutoFreeAfterParse;
    std::map<String,PatchVarBlock> patchVars;

    std::set<String> builtinPreprocessorConditions;
    std::set<String> userPreprocessorConditions;

    std::map<String,VariableRef> globalVarTable;
    std::map<String,VariableRef> localVarTable;
    std::map<String,UserFunctionRef> userFnTable;
    vmint globalIntVarCount;
    vmint globalRealVarCount;
    vmint globalStrVarCount;
    vmint globalUnitFactorCount;
    vmint polyphonicIntVarCount;
    vmint polyphonicRealVarCount;
    vmint polyphonicUnitFactorCount;
    uint localDataSize;

    bool globalScope;

    StatementsRef globalStatements;
    EventHandlersRef handlers;

    OnInitRef onInit;
    OnNoteRef onNote;
    OnReleaseRef onRelease;
    OnControllerRef onController;
    OnRpnRef onRpn;
    OnNrpnRef onNrpn;

    ArrayList<vmint>* globalIntMemory;
    ArrayList<vmfloat>* globalRealMemory;
    ArrayList<String>* globalStrMemory;
    ArrayList<vmfloat>* globalUnitFactorMemory;
    vmint requiredMaxStackSize;
    vmint requiredMaxDataStackSize;

    VMFunctionProvider* functionProvider;

    ExecContext* execContext;

    ParserContext(VMFunctionProvider* parent) :
        scanner(NULL), is(NULL), nbytes(0),
        globalIntVarCount(0), globalRealVarCount(0), globalStrVarCount(0),
        globalUnitFactorCount(0),
        polyphonicIntVarCount(0), polyphonicRealVarCount(0),
        polyphonicUnitFactorCount(0), localDataSize(0), globalScope(false),
        globalIntMemory(NULL), globalRealMemory(NULL), globalStrMemory(NULL),
        globalUnitFactorMemory(NULL),
        requiredMaxStackSize(-1), requiredMaxDataStackSize(-1),
        functionProvider(parent), execContext(NULL)
    {
    }
    virtual ~ParserContext();
    VariableRef localVar(const String& name);
    VariableRef globalVar(const String& name);
    IntVariableRef globalIntVar(const String& name);
    RealVariableRef globalRealVar(const String& name);
    StringVariableRef globalStrVar(const String& name);
    VariableRef variableByName(const String& name);
    UserFunctionRef userFunctionByName(const String& name);
    static uint localDataSizePadded(uint size);
    uint localDataSizePadded() const;
    void dropLocalVariables();
    void addErr(int firstLine, int lastLine, int firstColumn, int lastColumn,
                int firstByte, int lengthBytes, const char* txt);
    void addWrn(int firstLine, int lastLine, int firstColumn, int lastColumn,
                int firstByte, int lengthBytes, const char* txt);
    void addPreprocessorComment(int firstLine, int lastLine, int firstColumn,
                                int lastColumn, int firstByte, int lengthBytes);
    void createScanner(std::istream* is);
    void destroyScanner();
    bool setPreprocessorCondition(const char* name);
    bool resetPreprocessorCondition(const char* name);
    bool isPreprocessorConditionSet(const char* name);
    void autoFreeAfterParse(void* data);
    std::vector<ParserIssue> issues() const OVERRIDE;
    std::vector<ParserIssue> errors() const OVERRIDE;
    std::vector<ParserIssue> warnings() const OVERRIDE;
    std::vector<CodeBlock> preprocessorComments() const OVERRIDE;
    VMEventHandler* eventHandler(uint index) OVERRIDE;
    VMEventHandler* eventHandlerByName(const String& name) OVERRIDE;
    void registerBuiltInConstIntVariables(const std::map<String,vmint>& vars);
    void registerBuiltInConstRealVariables(const std::map<String,vmfloat>& vars);
    void registerBuiltInIntVariables(const std::map<String,VMIntPtr*>& vars);
    void registerBuiltInIntArrayVariables(const std::map<String,VMInt8Array*>& vars);
    void registerBuiltInDynVariables(const std::map<String,VMDynVar*>& vars);

    template<typename T>
    int acquireLocalMemPos(vmint n = 1) {
        int pos = localDataSize;
        if (pos % alignof(T))
            pos += alignof(T) - pos % alignof(T);
        localDataSize = pos + n * sizeof(T);
        return pos;
    }
};

#if VM_TREE_EXTRA_SANITY_CHECKS
#if !defined(__clang__) && __cplusplus < 201703L
# warning Not checking VM data stack alignment as compiler does not provide __STDCPP_DEFAULT_NEW_ALIGNMENT__ !
#else
static_assert(
    __STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(vmint) &&
    __STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(vmfloat) &&
    __STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(void*),
    "Compiler doesn't guarentee required alignment for VM's data stack"
);
#endif
#endif

#define VM_ALIGN alignas(vmint) alignas(vmfloat) alignas(void*)

class ExecContext FINAL : public VMExecContext {
public:
    struct OpStackFrame {
        Statement* statement;
        int subindex;
        int state;
    };
    struct DataStackFrame {
        int previousFrame;
        uint dataSize;
        VM_ALIGN char data[];
    };

    ArrayList<vmint> polyphonicIntMemory;
    ArrayList<vmfloat> polyphonicRealMemory;
    ArrayList<vmfloat> polyphonicUnitFactorMemory;
    VMExecStatus_t status;
    StmtFlags_t flags;
    ArrayList<OpStackFrame> opStack;
    ArrayList<char> dataStack;
    int opStackFrame;
    int dataStackPtr;
    vmint suspendMicroseconds;
    size_t instructionsCount;
    struct ExitRes {
        Expression* value;
        IntLiteral intLiteral;
        RealLiteral realLiteral;
        StringLiteral stringLiteral;

        ExitRes() :
            intLiteral({ .value = 0 }), realLiteral({ .value = 0.0 }),
            stringLiteral("") { }
    } exitRes;
    int localDataObjCount;
    VMDeploymentCase deployment;

    ExecContext();
    virtual ~ExecContext();

    inline void pushOpStack(Statement* stmt) {
        opStackFrame++;
        //printf("pushOpStack() -> %d\n", opStackFrame);
        if (opStackFrame >= opStack.size()) return;
        opStack[opStackFrame].statement = stmt;
        opStack[opStackFrame].subindex  = 0;
        opStack[opStackFrame].state  = 0;
    }

    inline void popOpStack() {
        opStack[opStackFrame].statement = NULL;
        opStack[opStackFrame].subindex  = -1;
        opStack[opStackFrame].state     = -1;
        opStackFrame--;
        //printf("popOpStack() -> %d\n", opStackFrame);
    }

    inline DataStackFrame* dataStackFrame(int framePtr) const {
        assert(framePtr >= 0 && framePtr + offsetof(DataStackFrame, data) <= dataStack.size());
        return (DataStackFrame*) &dataStack[framePtr];
    }

    inline DataStackFrame* dataStackFrame(StackCtx_t context) const {
        DataStackFrame* frame = dataStackFrame(
            (context == StackCtx_t::PREVIOUS) ?
                dataStackFrame(dataStackPtr)->previousFrame : dataStackPtr
        );
        assert(((char*)frame) + sizeof(DataStackFrame) <= &dataStack[0] + dataStack.size());
        assert(((char*)frame) + sizeof(DataStackFrame) + frame->dataSize <= &dataStack[0] + dataStack.size());
        return frame;
    }

    inline DataStackFrame* dataStackFrame() const {
        return dataStackFrame(dataStackPtr);
    }

    template<typename T>
    T& dataStackVar(StackCtx_t ctx, vmint memPos, vmint n = 1) const {
        DataStackFrame* frame = dataStackFrame(ctx);
        assert(memPos >= 0 && memPos + n * sizeof(T) <= frame->dataSize);
        char* ptr = &frame->data[memPos];
        assert(ptr + n * sizeof(T) <= &dataStack[0] + dataStack.size());
        return *(T*)ptr;
    }

    inline void pushDataStack(Statements* stmts) {
        //printf("push datastack (from %d)\n", dataStackPtr);
        const int oldFramePtr = dataStackPtr;
        if (dataStackPtr == -1) {
            dataStackPtr = 0;
        } else {
            DataStackFrame* oldFrame = dataStackFrame();
            dataStackPtr += offsetof(DataStackFrame, data) + oldFrame->dataSize;
        }
        DataStackFrame* newFrame = dataStackFrame();
        const uint dataSize = stmts->localDataSize();
        //printf("stackptr=%d datasize=%d totalsize=%lu\n", dataStackPtr, dataSize, sizeof(DataStackFrame) + dataSize);
        assert(&newFrame->data[0] + dataSize <= &dataStack[0] + dataStack.size());
        newFrame->previousFrame = oldFramePtr;
        newFrame->dataSize = dataSize;
        memset(&newFrame->data[0], 0, dataSize);
    }

    inline void popDataStack() {
        //printf("pop datastack\n");
        DataStackFrame* frame = dataStackFrame();
        //printf("stackptr %d -> %d\n", dataStackPtr, frame->previousFrame);
        dataStackPtr = frame->previousFrame;
        //dumpDataStackFrame();
    }

    void dumpDataStackFrame() const {
        DataStackFrame* frame = (dataStackPtr >= 0) ? dataStackFrame() : NULL;
        printf("----- Data Stack Frame -------------\n");
        printf(" [ dataStackPtr=%d dataSize=%d ]\n", dataStackPtr, frame ? frame->dataSize : 0);
        if (frame && frame->dataSize) {
            const int bytesPerLine = 8;
            for (int i = 0; i < frame->dataSize; ++i) {
                printf(" %02x", frame->data[i] & 0xff);
                if ((i % bytesPerLine == bytesPerLine - 1) || i == frame->dataSize - 1)
                    printf(" <- [%d:%d]\n", i - i % bytesPerLine, i);
            }
        }
        printf("------------------------------------\n");
    }

    inline void reset() {
        if (localDataObjCount > 0)
            _destructLocalDataObjects();
        opStack[0].statement = NULL;
        opStack[0].subindex  = -1;
        opStack[0].state     = -1;
        opStackFrame = -1;
        dataStackPtr = -1;
        flags = STMT_SUCCESS;
        if (deployment <= VM_LIVE_TESTING)
            assert(!localDataObjCount);
        localDataObjCount = 0;
    }

    inline void clearExitRes() {
        exitRes.value = NULL;
    }

    vmint suspensionTimeMicroseconds() const OVERRIDE {
        return suspendMicroseconds;
    }

    void resetPolyphonicData() OVERRIDE {
        if (!polyphonicIntMemory.empty())
            memset(&polyphonicIntMemory[0], 0, polyphonicIntMemory.size() * sizeof(vmint));
        if (!polyphonicRealMemory.empty())
            memset(&polyphonicRealMemory[0], 0, polyphonicRealMemory.size() * sizeof(vmfloat));
        if (!polyphonicUnitFactorMemory.empty()) {
            const vmint sz = polyphonicUnitFactorMemory.size();
            for (vmint i = 0; i < sz; ++i)
                polyphonicUnitFactorMemory[i] = VM_NO_FACTOR;
        }
    }

    void copyPolyphonicDataFrom(VMExecContext* ectx) OVERRIDE;

    size_t instructionsPerformed() const OVERRIDE {
        return instructionsCount;
    }

    void signalAbort() OVERRIDE {
        flags = StmtFlags_t(flags | STMT_ABORT_SIGNALLED);
    }

    void forkTo(VMExecContext* ectx) const OVERRIDE;

    VMExpr* exitResult() OVERRIDE {
        return exitRes.value;
    }

private:
    void _destructLocalDataObjects();
    void _forkLocalDataObjects();
};

} // namespace LinuxSampler

#endif // LS_INSTRPARSERTREE_H
