created own directory for parser
This commit is contained in:
14
lib/parser/README.markdown
Normal file
14
lib/parser/README.markdown
Normal file
@ -0,0 +1,14 @@
|
||||
Parser
|
||||
================
|
||||
|
||||
This includes the parser and generated ast.
|
||||
|
||||
Parslet is really great in that it:
|
||||
- does not generate code but instean gives a clean dsl to define a grammar
|
||||
- uses ruby modules so one can split the grammars up
|
||||
- has a seperate tranform stage to generate an ast layer
|
||||
|
||||
Especially the last point is great. Since it is seperate it does not clutter up the actual grammar.
|
||||
And it can generate a layer that has no links to the actual parser anymore, thus saving/automating
|
||||
a complete tranformation process.
|
||||
|
70
lib/parser/copied_parser.rb
Normal file
70
lib/parser/copied_parser.rb
Normal file
@ -0,0 +1,70 @@
|
||||
#start from an example of parslet (assumed to be checked out at the same level as crystal for now)
|
||||
|
||||
$:.unshift File.dirname(__FILE__) + "/../../../parslet/lib"
|
||||
|
||||
require 'pp'
|
||||
require 'rspec'
|
||||
require 'parslet'
|
||||
require 'parslet/rig/rspec'
|
||||
require 'parslet/convenience'
|
||||
|
||||
class InfixExpressionParser < Parslet::Parser
|
||||
root :variable_assignment_list
|
||||
|
||||
rule(:space) { match[' '] }
|
||||
|
||||
def cts atom
|
||||
atom >> space.repeat
|
||||
end
|
||||
def infix *args
|
||||
Infix.new(*args)
|
||||
end
|
||||
|
||||
# This is the heart of the infix expression parser: real simple definitions
|
||||
# for all the pieces we need.
|
||||
rule(:mul_op) { cts match['*/'] }
|
||||
rule(:add_op) { cts match['+-'] }
|
||||
rule(:digit) { match['0-9'] }
|
||||
rule(:integer) { cts digit.repeat(1).as(:int) }
|
||||
|
||||
rule(:expression) { infix_expression(integer,
|
||||
[mul_op, 2, :left],
|
||||
[add_op, 1, :right]) }
|
||||
|
||||
# And now adding variable assignments to that, just to a) demonstrate this
|
||||
# embedded in a bigger parser, and b) make the example interesting.
|
||||
rule(:variable_assignment_list) {
|
||||
variable_assignment.repeat(1) }
|
||||
rule(:variable_assignment) {
|
||||
identifier.as(:ident) >> equal_sign >> expression.as(:exp) >> eol }
|
||||
rule(:identifier) {
|
||||
cts (match['a-z'] >> match['a-zA-Z0-9'].repeat) }
|
||||
rule(:equal_sign) {
|
||||
cts str('=') }
|
||||
rule(:eol) {
|
||||
cts(str("\n")) | any.absent? }
|
||||
end
|
||||
|
||||
class InfixInterpreter < Parslet::Transform
|
||||
rule(int: simple(:int)) { Integer(int) }
|
||||
rule(ident: simple(:ident), exp: simple(:result)) { |d|
|
||||
d[:doc][d[:ident].to_s.strip.to_sym] = d[:result] }
|
||||
|
||||
rule(l: simple(:l), o: /^\*/, r: simple(:r)) { l * r }
|
||||
rule(l: simple(:l), o: /^\+/, r: simple(:r)) { l + r }
|
||||
end
|
||||
|
||||
input = <<ASSIGNMENTS
|
||||
a = 1
|
||||
b = 2
|
||||
c = 3 * 25
|
||||
d = 100 + 3*4
|
||||
ASSIGNMENTS
|
||||
|
||||
puts input
|
||||
|
||||
int_tree = InfixExpressionParser.new.parse_with_debug(input)
|
||||
bindings = {}
|
||||
result = InfixInterpreter.new.apply(int_tree, doc: bindings)
|
||||
|
||||
pp bindings
|
50
lib/parser/parser.rb
Normal file
50
lib/parser/parser.rb
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
module Vm
|
||||
class Parser < Parslet::Parser
|
||||
rule(:name) { match('[a-z]').repeat(1).as(:name) >> space? }
|
||||
rule(:number) { match('[0-9]').repeat(1).as(:number) >> space? }
|
||||
rule(:space) { match('\s').repeat(1) }
|
||||
rule(:space?) { space.maybe }
|
||||
|
||||
rule(:args) {
|
||||
lparen >>
|
||||
((expression.as(:arg) >> (comma >> expression.as(:arg)).repeat(0)).maybe).as(:args) >>
|
||||
rparen
|
||||
}
|
||||
|
||||
rule(:funcall) { name.as(:funcall) >> args }
|
||||
|
||||
rule(:expression) { cond | funcall | number | name }
|
||||
|
||||
rule(:lparen) { str('(') >> space? }
|
||||
rule(:rparen) { str(')') >> space? }
|
||||
rule(:comma) { str(',') >> space? }
|
||||
|
||||
rule(:cond) {
|
||||
if_kw >> lparen >> expression.as(:cond) >> rparen >>
|
||||
body.as(:if_true) >>
|
||||
else_kw >>
|
||||
body.as(:if_false)
|
||||
}
|
||||
|
||||
rule(:body) { lbrace >> expression.as(:body) >> rbrace }
|
||||
rule(:lbrace) { str('{') >> space? }
|
||||
rule(:rbrace) { str('}') >> space? }
|
||||
rule(:comma) { str(',') >> space? }
|
||||
rule(:if_kw) { str('if') >> space? }
|
||||
rule(:else_kw) { str('else') >> space? }
|
||||
|
||||
rule(:func) {
|
||||
func_kw >> name.as(:func) >> params >> body
|
||||
}
|
||||
|
||||
rule(:func_kw) { str('function') >> space? }
|
||||
|
||||
rule(:params) {
|
||||
lparen >>
|
||||
((name.as(:param) >> (comma >> name.as(:param)).repeat(0)).maybe).as(:params) >>
|
||||
rparen
|
||||
}
|
||||
rule(:root){ func.repeat(0) >> expression | expression | args }
|
||||
end
|
||||
end
|
33
lib/parser/transform.rb
Normal file
33
lib/parser/transform.rb
Normal file
@ -0,0 +1,33 @@
|
||||
require 'parslet'
|
||||
require 'vm/nodes'
|
||||
|
||||
module Vm
|
||||
class Transform < Parslet::Transform
|
||||
rule(:number => simple(:value)) { NumberExpression.new(value.to_i) }
|
||||
rule(:name => simple(:name)) { NameExpression.new(name.to_s) }
|
||||
|
||||
rule(:arg => simple(:arg)) { arg }
|
||||
rule(:args => sequence(:args)) { args }
|
||||
|
||||
rule(:funcall => simple(:funcall),
|
||||
:args => simple(:args)) { FuncallExpression.new(funcall.name, [args]) }
|
||||
|
||||
rule(:funcall => simple(:funcall),
|
||||
:args => sequence(:args)) { FuncallExpression.new(funcall.name, args) }
|
||||
|
||||
rule(:cond => simple(:cond),
|
||||
:if_true => {:body => simple(:if_true)},
|
||||
:if_false => {:body => simple(:if_false)}) { ConditionalExpression.new(cond, if_true, if_false) }
|
||||
|
||||
rule(:param => simple(:param)) { param }
|
||||
rule(:params => sequence(:params)) { params }
|
||||
|
||||
rule(:func => simple(:func),
|
||||
:params => simple(:name),
|
||||
:body => simple(:body)) { FunctionExpression.new(func.name, [name], body) }
|
||||
|
||||
rule(:func => simple(:func),
|
||||
:params => sequence(:params),
|
||||
:body => simple(:body)) { FunctionExpression.new(func.name, params, body) }
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user