sexp = object.new
# Keep track of sexp types by name
sexp.types = [:]
sexp.init = { name |
my.name = name
}
sexp.prototype.to_s = {
"s#{[my.name] + my.nodes}"
}
sexp.prototype.== = { rhs |
my = my
true? rhs.has_method?(:name) && rhs.has_method?(:nodes)
{ my.name == rhs.name && { my.nodes == rhs.nodes } }
}
sexp.prototype.<< = { val |
my.nodes << val
my
}
sexp.prototype.concat = { val |
my.nodes.concat val
my
}
sexp.prototype.last = {
my.nodes.last
}
sexp.prototype.map! = { block |
my.nodes.map! ->block
}
# Shared initializer for sexp types
initializer = { nodes |
my.nodes = nodes
true? my.nodes.length == 1
{ my.value = nodes.last }
}
# Create new sexp with methods to access the given methods.
make = { name, *meths |
new_thing = sexp.new(name)
meths.each_with_index { name, i |
new_thing.prototype.add_method name, {
nodes[i]
}
}
new_thing.init = ->initializer
sexp.types[name] = new_thing
}
make :grammar
make :rule_def
make :any
make :seq
make :str
make :rule_ref
make :anything
make :nothing
make :regex
make :label
make :maybe
make :kleene
make :many
make :no
make :and
make :action
make :squish
# Little shortcut to make sexps via s[...]
s = object.new
s.get = { name, *args |
sexp.types[name].new args
}
sexp? = { val |
val.has_method?(:name) && { sexp.types[val.name] }
}
export ->sexp?, :sexp?
export s, :s