Skip to content

Instantly share code, notes, and snippets.

@yorickpeterse
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yorickpeterse/998466d018ebf3e32ec3 to your computer and use it in GitHub Desktop.
Save yorickpeterse/998466d018ebf3e32ec3 to your computer and use it in GitHub Desktop.
$:.unshift(File.expand_path('../lib', __FILE__))
require 'ast'
require 'oga'
class Node
attr_reader :type
def initialize(type, children = [])
@type = type.to_sym
@children = children
end
def to_a
@children
end
def assign(other)
Node.new(:assign, [self, other])
end
def eq(other)
if other.is_a?(String)
other = Node.new(:string, [other])
end
Node.new(:eq, [self, other])
end
def and(other)
Node.new(:and, [self, other])
end
def or(other)
Node.new(:or, [self, other])
end
def is_a(klass)
Node.new(:send, [self, 'is_a?', Node.new(:lit, [klass.to_s])])
end
def method_missing(name, *args)
node = Node.new(:send, [self, name.to_s, *args])
node
end
def add_block(*args)
Node.new(:block, [self, args, yield])
end
def if_true
Node.new(:if, [self, yield])
end
def followed_by(other)
Node.new(:begin, [self, other])
end
def inspect
"(#{type} #{@children.map(&:inspect).join(' ')})"
end
end
class Generator
def process(ast, indent = 0)
send("on_#{ast.type}", ast, indent)
end
def on_begin(ast, indent = 0)
ast.to_a.map { |child| process(child, indent) }.join("\n\n")
end
def on_assign(ast, indent = 0)
var, val = *ast
var_str = process(var)
val_str = process(val)
indent_line("#{var_str} = #{val_str}", indent)
end
def on_eq(ast, indent = 0)
left, right = *ast
left_str = process(left, indent)
right_str = process(right, indent)
"#{left_str} == #{right_str}"
end
def on_and(ast, indent = 0)
left, right = *ast
left_str = process(left, indent)
right_str = process(right, indent)
"#{left_str} && #{right_str}"
end
def on_or(ast, indent = 0)
left, right = *ast
left_str = process(left, indent)
right_str = process(right, indent)
"(#{left_str} || #{right_str})"
end
def on_if(ast, indent = 0)
cond, body = *ast
cond_str = process(cond)
body_str = process(body, indent + 1)
spacing = spacing(indent)
<<-EOF.chomp
#{spacing}if #{cond_str}
#{body_str}
#{spacing}end
EOF
end
def on_send(ast, indent = 0)
receiver, name, *args = *ast
call = name
unless args.empty?
arg_strs = args.map { |arg| process(arg) }
call = "#{call}(#{arg_strs.join(', ')})"
end
if receiver
call = "#{process(receiver, indent)}.#{call}"
end
call
end
def on_block(ast, indent = 0)
receiver, args, body = *ast
receiver_str = process(receiver)
body_str = body ? process(body, indent + 1) : ''
if args.empty?
open = 'do'
else
arg_strs = args.map { |arg| process(arg) }
open = "do |#{arg_strs.join(', ')}|"
end
spacing = spacing(indent)
<<-EOF.chomp
#{spacing}#{receiver_str} #{open}
#{body_str}
#{spacing}end
EOF
end
def on_string(ast, indent = 0)
ast.to_a[0].inspect
end
def on_lit(ast, indent = 0)
indent_line(ast.to_a[0], indent)
end
private
def indent_line(line, indent)
spacing(indent) + line
end
def spacing(indent)
' ' * indent
end
end
def lit(name)
Node.new(:lit, [name.to_s])
end
child = lit('child')
matched = lit('matched')
document = lit('document')
block = lit('proc').add_block(document) do
assign = matched.assign(lit(Oga::XML::NodeSet).new)
n1 = lit('n1')
n2 = lit('n2')
n3 = lit('n3')
conditions = document.children.each.add_block(n1) do
n1.is_a(Oga::XML::Element).or(n1.is_a(Oga::XML::Attribute)).and(n1.name.eq('people')).if_true do
n1.children.each.add_block(n2) do
n2.is_a(Oga::XML::Element).or(n2.is_a(Oga::XML::Attribute)).and(n2.name.eq('person')).if_true do
n2.children.each.add_block(n3) do
n3.is_a(Oga::XML::Element).or(n3.is_a(Oga::XML::Attribute)).and(n3.name.eq('name')).if_true do
n3.text.eq('Alice').if_true { matched.push(n2) }
end
end
end
end
end
end
assign.followed_by(conditions).followed_by(matched)
end
generated = lit('generated').assign(block)
generator = Generator.new
puts generator.process(generated)
generated = proc do |document|
matched = Oga::XML::NodeSet.new
document.children.each do |n1|
if (n1.is_a?(Oga::XML::Element) || n1.is_a?(Oga::XML::Attribute)) && n1.name == "people"
n1.children.each do |n2|
if (n2.is_a?(Oga::XML::Element) || n2.is_a?(Oga::XML::Attribute)) && n2.name == "person"
n2.children.each do |n3|
if (n3.is_a?(Oga::XML::Element) || n3.is_a?(Oga::XML::Attribute)) && n3.name == "name"
if n3.text == "Alice"
matched.push(n2)
end
end
end
end
end
end
end
matched
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment