#ifndef YARP_AST_H
#define YARP_AST_H

#include "yarp/defines.h"
#include "yarp/util/yp_constant_pool.h"
#include "yarp/util/yp_string.h"

#include <assert.h>
#include <stddef.h>
#include <stdint.h>

// This enum represents every type of token in the Ruby source.
typedef enum yp_token_type {
<%- tokens.each do |token| -%>
    <%= token.declaration %>
<%- end -%>
    YP_TOKEN_MAXIMUM, // the maximum token value
} yp_token_type_t;

// This struct represents a token in the Ruby source. We use it to track both
// type and location information.
typedef struct {
    yp_token_type_t type;
    const uint8_t *start;
    const uint8_t *end;
} yp_token_t;

// This represents a range of bytes in the source string to which a node or
// token corresponds.
typedef struct {
    const uint8_t *start;
    const uint8_t *end;
} yp_location_t;

struct yp_node;

typedef struct yp_node_list {
    struct yp_node **nodes;
    size_t size;
    size_t capacity;
} yp_node_list_t;

enum yp_node_type {
<%- nodes.each_with_index do |node, index| -%>
    <%= node.type %> = <%= index + 1 %>,
<%- end -%>
    YP_SCOPE_NODE
};

// Deprecated aliases
#define YP_NODE_SCOPE_NODE YP_SCOPE_NODE
<%- nodes.each do |node| -%>
#define <%= node.type.sub(/^YP_/, 'YP_NODE_') %> <%= node.type %>
<%- end -%>

typedef uint16_t yp_node_type_t;
typedef uint16_t yp_node_flags_t;

// We store the flags enum in every node in the tree. Some flags are common to
// all nodes (the ones listed below). Others are specific to certain node types.
static const yp_node_flags_t YP_NODE_FLAG_NEWLINE = 0x1;

// For easy access, we define some macros to check node type
#define YP_NODE_TYPE(node) ((enum yp_node_type)node->type)
#define YP_NODE_TYPE_P(node, type) (YP_NODE_TYPE(node) == (type))

// This is the overall tagged union representing a node in the syntax tree.
typedef struct yp_node {
    // This represents the type of the node. It somewhat maps to the nodes that
    // existed in the original grammar and ripper, but it's not a 1:1 mapping.
    yp_node_type_t type;

    // This represents any flags on the node
    yp_node_flags_t flags;

    // This is the location of the node in the source. It's a range of bytes
    // containing a start and an end.
    yp_location_t location;
} yp_node_t;
<%- nodes.each do |node| -%>

// <%= node.name %>
//
// Type: <%= node.type %>
<%- if (node_flags = node.fields.find { |field| field.is_a? YARP::FlagsField }) -%>
// Flags:
<%- found = flags.find { |flag| flag.name == node_flags.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%>
<%- found.values.each do |value| -%>
//    YP_<%= found.human.upcase %>_<%= value.name %>
<%- end -%>
<%- end -%>
typedef struct yp_<%= node.human %> {
    yp_node_t base;
<%- node.fields.grep_v(YARP::FlagsField).each do |field| -%>
    <%= case field
    when YARP::NodeField, YARP::OptionalNodeField then "struct #{field.c_type} *#{field.name}"
    when YARP::NodeListField then "struct yp_node_list #{field.name}"
    when YARP::ConstantField, YARP::OptionalConstantField then "yp_constant_id_t #{field.name}"
    when YARP::ConstantListField then "yp_constant_id_list_t #{field.name}"
    when YARP::StringField then "yp_string_t #{field.name}"
    when YARP::LocationField, YARP::OptionalLocationField then "yp_location_t #{field.name}"
    when YARP::UInt32Field then "uint32_t #{field.name}"
    else raise field.class.name
    end
    %>;
<%- end -%>
} yp_<%= node.human %>_t;
<%- end -%>
<%- flags.each do |flag| -%>

// <%= flag.name %>
typedef enum {
    <%- flag.values.each.with_index(YARP::COMMON_FLAGS) do |value, index| -%>
    YP_<%= flag.human.upcase %>_<%= value.name %> = 1 << <%= index %>,
    <%- end -%>
} yp_<%= flag.human %>_t;
<%- end -%>

#endif // YARP_AST_H
