Last active
August 29, 2015 14:04
-
-
Save tbrooke/6455af22cff82f904967 to your computer and use it in GitHub Desktop.
Parser - Example Parser
This file contains hidden or 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
## Simple Parser | |
## | |
## This parser implments an example of a parser similiar to Hoplon or Pollen | |
## in that it translates an s expression to html | |
## ie (h1 "The Great Gatsby") produces <h1>The Great Gatsby</h1> | |
# | |
# | |
# Input | |
# | |
# | |
# Initial S Expression | |
# | |
# (html | |
# (head | |
# (title "Hello World")) | |
# (body | |
# (h1 "Hello World") | |
# (p "This is your parser"))) | |
# | |
# | |
# '(html(head(title "Hello World"))(body (h1 "Hello World")(p "This is your parser")))' | |
# | |
## Converted to nested array of symbols with class Reader | |
## | |
## parse_string == | |
## [:html, [:head, [:title, "Hello World"]], [:body, [:h1, "Hello World"], [:p, "This is your parser"]]] | |
## | |
## | |
### Finally to HTML | |
### | |
### <html> | |
### <head> | |
### <title>Hello World</title> | |
### </head> | |
### <body> | |
### <h1> Hello World </h1> | |
### <p> This is your parser</p> | |
### </body> | |
### </html> | |
require 'strscan' | |
class Reader < StringScanner | |
# Check for Missing parens | |
def initialize(string) | |
unless(string.count('(') == string.count(')')) | |
raise Exception, "Missing closing parentheses" | |
end | |
super(string) | |
end | |
# Pull the tokens from the S_Exp into an Array | |
def parse | |
exp = [] | |
while true | |
case fetch_token | |
when '(' | |
exp << parse | |
when ')' | |
break | |
when :"'" | |
case fetch_token | |
when '(' then exp << [:quote].concat([parse]) | |
else exp << [:quote, @token] | |
end | |
when String, Fixnum, Symbol | |
exp << @token | |
when nil | |
break | |
end | |
end | |
exp | |
end | |
# Use scan with regex to grab each token | |
def fetch_token | |
skip(/\s+/) | |
return nil if(eos?) | |
@token = | |
# Match parentheses | |
if scan(/[\(\)]/) | |
matched | |
# Match a string | |
elsif scan(/"([^"\\]|\\.)*"/) | |
eval(matched) | |
# Match an integer | |
elsif scan(/[\-\+]?[0-9]+/) | |
matched.to_i | |
# Match a single quote (for single quoting) | |
elsif scan(/'/) | |
matched.to_sym | |
# Match a symbol | |
elsif scan(/[^\(\)\s]+/) | |
matched.to_sym | |
# If we've gotten here then we have an invalid token | |
else | |
near = scan %r{.{0,20}} | |
raise "Invalid character at position #{pos} near '#{near}'." | |
end | |
end | |
end | |
class Parser | |
# Parse a string containing an S-Expression into a | |
# nested set of Ruby arrays | |
def parse_string(string) | |
tree = Reader.new(string).parse | |
tree = tree[0] # pull off the outer Array | |
return tree | |
end | |
def to_html(data) | |
if (data.is_a?(Array)) | |
tag = data[0] | |
children = data[1..-1] | |
return "<#{tag}>" + children.map {|x| to_html(x)}.join(' ') + "</#{tag}>" | |
else | |
return data | |
end | |
end | |
# Convert a set of nested arrays back into an S-Expression | |
def to_sexp(data) | |
if( data.is_a?(Array)) | |
mapped = data.map do |item| | |
if( item.is_a?(Array)) | |
to_sexp(item) | |
else | |
item.to_s | |
end | |
end | |
"(" + mapped.join(" ") + ")" | |
else | |
data.to_s | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment