Last active
August 29, 2015 14:23
-
-
Save yorickpeterse/998466d018ebf3e32ec3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$:.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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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