include 'peg/sexp'

node = object.new
node.prototype [
    init: { name |
            my.node_name = name
            export my, name
          }
    to_s: { "<#{my.node_name}: #{my.matched}>" }
]

each_ast = { elements, block |
  elements.each { e |
    when { e.has_method?(:ast)} { block e.ast }
         { e.has_method?(:elements) } { each_ast e.elements, ->block }
  }
}

add_ast = { node, list |
  each_ast node.elements, { n |
    true? n
      { list << n }
  }

  list
}

ast = { name, block |
  n = node.new name
  n.ast = ->block
}

ast :grammar {
  add_ast my, s[:grammar]
}

ast :rule_def {
  name = my.elements.deq.text
  add_ast my, s[:rule_def, name]
}

ast :rule_list {
  list = s[:any]
  add_ast my, list

  true? list.nodes.length == 1
  { list.nodes.first }
  { list }
}

ast :rule_seq {
  list = s[:seq]
  add_ast my, list

  true? list.nodes.length == 1
  { list.nodes.first }
  { list }
}

ast :str_lit {
  s[:str, my.content.text]
}

ast :rule_ref {
  s[:rule_ref my.name.text]
}

ast :regex_rule {
  s[:regex, my.content.text]
}

ast :rule_label {
  add_ast my, s[:label, my.label_name.text]
}

ast :maybe_rule {
  add_ast my, s[:maybe]
}

ast :many_rule {
  add_ast my, s[:many]
}

ast :kleene_rule {
  add_ast my, s[:kleene]
}

ast :not_rule {
  add_ast my, s[:no]
}

ast :and_rule {
  add_ast my, s[:and]
}

ast :anything_rule {
  s[:anything]
}

ast :set_action {
  s[:action, my.text]
}

ast :set_squish {
  s[:squish, my.elements.first.matches[1]]
}