Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env ruby
- # Node representing a flat value
- class Value
- def initialize(value)
- @value = value
- end
- def to_ast
- @value
- end
- end
- # Node representing a list of other nodes.
- class Tree
- attr_reader :parent
- def initialize(parent)
- @parent = parent
- @children = []
- end
- def <<(child)
- @children << child
- end
- def to_ast
- @children.map(&:to_ast)
- end
- end
- # Node having zero or one child and itself as its parent. It is the "empty"
- # node the parser always starts with.
- class Root
- def initialize
- @child = nil
- end
- def parent
- self
- end
- def <<(child)
- @child = child
- end
- def to_ast
- @child.to_ast
- end
- end
- # Parse a lisp expression, one token at a time.
- class Parser
- def initialize
- @root = Root.new
- @current_node = @root
- end
- def <<(token)
- case token
- when /^\d+$/
- insert Value.new(token.to_i)
- when '('
- nest_more
- when ')'
- nest_less
- else
- insert Value.new(token.to_s)
- end
- end
- def nest_more
- tree = Tree.new @current_node
- @current_node << tree
- @current_node = tree
- end
- def nest_less
- @current_node = @current_node.parent
- end
- def insert(value)
- @current_node << value
- end
- def to_ast
- @root.to_ast
- end
- end
- # Split input strings into tokens, separating with whitespace and splitting on
- # parentheses.
- class Tokenizer
- def tokens_for(input)
- input
- .split(/(?<=\()|(?=\))|\s+/) # ugly!
- .map(&:strip) # handle cases like '( foo )' => ['(', 'foo', ')']
- end
- end
- if $PROGRAM_NAME == __FILE__
- if ARGV[0] == '-h'
- puts "usage: echo '(some lispy :code (here))' | #{$PROGRAM_NAME}"
- exit
- end
- parser = Parser.new
- tokenizer = Tokenizer.new
- # TODO: maybe turn this into a REPL (well, once there's an evaluate step)
- ARGF.each do |line|
- tokenizer.tokens_for(line).each { |token| parser << token }
- end
- p parser.to_ast
- end
Add Comment
Please, Sign In to add comment