summaryrefslogtreecommitdiffstats
path: root/lib/blink/parser
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2005-04-13 15:24:36 +0000
committerLuke Kanies <luke@madstop.com>2005-04-13 15:24:36 +0000
commit6ee8b4e7a9731f969347e317456e8d9712fe2641 (patch)
tree0e2aa758bb523b4a2f1c752a625bd2d95462edf4 /lib/blink/parser
parent5416017c05e44fc635ad14ffdf1ac1163a4cc6e5 (diff)
downloadpuppet-6ee8b4e7a9731f969347e317456e8d9712fe2641.tar.gz
puppet-6ee8b4e7a9731f969347e317456e8d9712fe2641.tar.xz
puppet-6ee8b4e7a9731f969347e317456e8d9712fe2641.zip
reorganizing
git-svn-id: https://reductivelabs.com/svn/puppet/library/trunk@96 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/blink/parser')
-rw-r--r--lib/blink/parser/grammar.ra451
-rw-r--r--lib/blink/parser/interpreter.rb222
-rw-r--r--lib/blink/parser/lexer.rb182
-rw-r--r--lib/blink/parser/makefile5
-rw-r--r--lib/blink/parser/parser.rb683
5 files changed, 1543 insertions, 0 deletions
diff --git a/lib/blink/parser/grammar.ra b/lib/blink/parser/grammar.ra
new file mode 100644
index 000000000..ab429a662
--- /dev/null
+++ b/lib/blink/parser/grammar.ra
@@ -0,0 +1,451 @@
+#/usr/bin/ruby
+
+# $Id$
+# vim: syntax=ruby
+
+# the parser
+
+class Blink::Parser::Parser
+token WORD LBRACK QTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE FALSE EQUALS
+token QMARK LPAREN RPAREN
+rule
+program: statements {
+ if val[0].is_a?(Array)
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0]])
+ end
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Blink[:parseonly]
+ begin
+ puts result.tree(0)
+ rescue NoMethodError => detail
+ puts detail
+ exit(78)
+ end
+ else
+ require 'blink/parser/interpreter'
+ result = Blink::Parser::Interpreter.new(result)
+ end
+}
+
+statements: statement
+ | statements statement {
+ if val[0].is_a?(Array)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0],val[1]])
+ end
+}
+
+statement: object
+ | assignment
+ | selector
+ | functioncall
+
+object: WORD LBRACK rvalue RBRACK LBRACE params endcomma RBRACE {
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::ObjectDef.new(
+ :pin => "[]",
+ :line => @lexer.line,
+ :object => leaf,
+ :name => val[2],
+ :params => val[5]
+ )
+}
+
+assignment: WORD EQUALS rvalue {
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :name => leaf,
+ :value => val[2]
+ )
+}
+
+params: param { result = val[0] }
+ | params COMMA param {
+ if val[0].is_a?(Array)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = [val[0],val[2]]
+ end
+}
+
+param: QTEXT FARROW rvalue {
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+rvalues: rvalue
+ | rvalues rvalue {
+ if val[0].is_a?(Array)
+ result = val[0].push(val[1])
+ else
+ result = AST::Array.new(val[0],val[1])
+ end
+}
+
+rvalue: QTEXT {
+ result = AST::String.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+}
+ | selector
+ | object
+ | functioncall
+ | WORD { # these are variable names
+ result = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+}
+
+selector: WORD QMARK svalues {
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+# I'm probably going to regret reusing 'param' here...
+svalues: param
+ | LBRACE sintvalues RBRACE { result = val[1] }
+
+sintvalues: param
+ | sintvalues param {
+ if val[0].is_a?(Array)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0],val[1]])
+ end
+}
+
+functioncall: WORD LPAREN rvalues RPAREN {
+ result = AST::FunctionCall.new(
+ :pin => '()',
+ :name => AST::Word.new(:value => val[0], :line => @lexer.line),
+ :values => val[2]
+ )
+}
+ | WORD LPAREN RPAREN {
+ result = FunctionDef.new(
+ :pin => '()',
+ :name => val[0]
+ )
+}
+
+endcomma: # nothing
+ | COMMA { result = nil }
+
+end
+---- header ----
+require 'blink/parser/lexer'
+#require 'blink/parser/interpreter'
+
+module Blink
+ class ParseError < Racc::ParseError; end
+end
+
+---- inner ----
+def file=(file)
+ @lexer.file = file
+end
+
+def initialize
+ @lexer = Blink::Parser::Lexer.new()
+ if Blink[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #puts "Parse stack:"
+ #puts stack
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ error = "line %s: parse error after '%s'" % [@lexer.line,@lexer.last]
+
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ raise Blink::ParseError.new(error)
+end
+
+# how should I do error handling here?
+def parse
+ yyparse(@lexer,:scan)
+ #begin
+ # yyparse(@lexer,:scan)
+ #rescue Racc::ParseError => detail
+ # raise Racc::ParseError.new("line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last])
+ #end
+end
+
+def string=(string)
+ @lexer.string = string
+end
+
+# the parent class for all of our syntactical objects
+class AST
+ attr_accessor :line
+ @@pink = ""
+ @@green = ""
+ @@yellow = ""
+ @@reset = ""
+
+ @@indent = " " * 4
+ @@indline = @@pink + ("-" * 4) + @@reset
+ @@midline = @@yellow + ("-" * 4) + @@reset
+
+ def AST.indention
+ return @@indent * @@indention
+ end
+
+ def AST.midline
+ return @@midline
+ end
+
+ def typewrap(string)
+ #return self.class.to_s.sub(/.+::/,'') + "(" + @@green + string +@@reset+ ")"
+ return @@green + string +@@reset+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
+ end
+
+ def initialize(*rest)
+ begin
+ args = Hash[*rest]
+ rescue ArgumentError
+ raise ArgumentError.new("Arguments must be passed as name => value pairs")
+ end
+ args.each { |param,value|
+ method = param.to_s + "="
+ unless self.respond_to?(method)
+ raise "Invalid parameter %s to object class %s" %
+ [method,self.class.to_s]
+ end
+
+ begin
+ #Blink.debug("sending %s to %s" % [method, self.class])
+ self.send(method,value)
+ rescue => detail
+ # XXX this should be more normal error correction
+ raise "Could not set parameter %s on class %s: %s" %
+ [method,self.class.to_s,detail]
+ end
+ }
+ end
+
+ class ASTArray < Array
+ def tree(indent = 0)
+ #puts((AST.indent * indent) + self.pin)
+ self.collect { |child|
+ child.tree(indent)
+ }.join("\n" + (AST.midline * (indent+1)) + "\n")
+ end
+ end
+
+ # this differentiation is used by the interpreter
+ # XXX i now need a standard mechanism for descending into children
+
+ # these objects have children
+ class Branch < AST
+ include Enumerable
+ attr_accessor :pin
+
+ def each
+ @children.each { |child|
+ yield child
+ }
+ end
+
+ def tree(indent = 0)
+ return ((@@indline * indent) + self.typewrap(self.pin)) + "\n" +
+ self.collect { |child|
+ child.tree(indent + 1)
+ }.join("\n")
+ end
+ end
+
+ # and these ones don't
+ class Leaf < AST
+ attr_accessor :value, :type
+
+ def tree(indent = 0)
+ return ((@@indent * indent) + self.typewrap(self.value))
+ end
+
+ def to_s
+ return @value
+ end
+ end
+
+ class String < AST::Leaf
+ attr_accessor :value
+ end
+
+ class Word < AST::Leaf
+ attr_accessor :value
+ end
+
+ class ObjectDef < AST::Branch
+ attr_accessor :name, :object
+ attr_reader :params
+
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ def [](index)
+ return @params[index]
+ end
+
+ def each
+ #Blink.debug("each called on %s" % self)
+ [@object,@name,@params].flatten.each { |param|
+ #Blink.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def initialize(*args)
+ super(*args)
+ end
+
+ def params=(params)
+ if params.is_a?(Array)
+ @params = params
+ else
+ @params = [params]
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @object.tree(indent + 1),
+ @name.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @params.collect { |param|
+ begin
+ param.tree(indent + 1)
+ rescue NoMethodError => detail
+ puts "failed to tree"
+ puts @params
+ p param
+ raise
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@name,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+ end
+
+ class ObjectParam < AST::Branch
+ attr_accessor :value, :param
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@param,@value]
+ end
+ end
+
+ class Selector < AST::Branch
+ attr_accessor :param, :value
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+ end
+
+ class VarDef < AST::Branch
+ attr_accessor :name, :value
+
+ def each
+ [@name,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@value]
+ end
+ end
+
+ class FunctionCall < AST::Branch
+ attr_accessor :name, :values
+
+ def each
+ [@name,@values].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @values.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@values]
+ end
+ end
+end
diff --git a/lib/blink/parser/interpreter.rb b/lib/blink/parser/interpreter.rb
new file mode 100644
index 000000000..0e117fb61
--- /dev/null
+++ b/lib/blink/parser/interpreter.rb
@@ -0,0 +1,222 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the interpreter
+#
+# this builds our virtual pinball machine, into which we'll place our host-specific
+# information and out of which we'll receive our host-specific configuration
+
+require 'strscan'
+require 'blink'
+require 'blink/parser/parser'
+
+
+module Blink
+ class IntepreterError < RuntimeError; end
+ module Parser
+ #---------------------------------------------------------------
+ class Interpreter
+ # just shorten the constant path a bit, using what amounts to an alias
+ AST = Blink::Parser::Parser::AST
+
+ # make it a class method, since it's not an instance method...
+ def Interpreter.descend(root,depthfirst = true,&block)
+ #Blink.debug("root is %s of type %s" % [root,root.class])
+ root.each_with_index { |thing,index|
+ # this is a problem...
+ # we want to descend into all syntactical objects, but
+ # we don't want to descend into Blink::Objects because
+ # that would mean operating directly on attributes, which
+ # we don't want
+ if depthfirst
+ if thing.is_a?(AST::Branch)
+ Blink.debug("descending thing %s of type %s" %
+ [thing,thing.class])
+ Interpreter.descend(thing,&block)
+ end
+ block.call(thing,index,root)
+ else
+ block.call(thing,index,root)
+ if thing.is_a?(AST::Branch)
+ Blink.debug("descending thing %s of type %s" %
+ [thing,thing.class])
+ Interpreter.descend(thing,&block)
+ end
+ end
+ }
+ end
+
+ #------------------------------------------------------------
+ def askfunc(name,*args)
+ if func = Blink::Function[name]
+ # XXX when we're remote, we'll need to do this differently...
+ func.call(*args)
+ else
+ raise "Undefined function %s" % name
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # when we have an 'eval' function, we should do that instead
+ # for now, we only support variables in strings
+ def strinterp(string)
+ regex = Regexp.new('\$\{(\w+)\}}')
+ while match = regex.match(string) do
+ string.sub!(regex,self.varvalue(match[0]))
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # basically just return the variable value from the symbol
+ # table
+ def varvalue(variable)
+ unless @symtable.include?(variable)
+ raise "Undefined variable %s" % variable
+ end
+
+ return @symtable[variable]
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # create our interpreter
+ def initialize(tree)
+ @tree = tree
+
+ @symtable = Hash.new(nil)
+ @factable = Hash.new(nil)
+ @objectable = Hash.new { |hash,key|
+ #hash[key] = IObject.new(key)
+ hash[key] = {:name => key}
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # execute all of the passes (probably just one, in the end)
+ def run
+ regex = %r{^pass}
+ self.methods.sort.each { |method,value|
+ if method =~ regex
+ Blink.debug("calling %s" % method)
+ self.send(method)
+ end
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # i don't know how to deal with evaluation here --
+ # all Leafs need to be turned into real values, but only in
+ # those trees which i know are under 'true' branches
+ def pass1_umeverything
+ Interpreter.descend(@tree) { |object,index,parent|
+ case object
+ # handle the leaves first
+ when AST::String then
+ # interpolate all variables in the string in-place
+ self.strinterp(object.value)
+ when AST::Word then
+ if parent.is_a?(AST::VarDef) # if we're in an assignment
+ # um, we pretty much don't do anything
+ else
+ # this is where i interpolate the variable, right?
+ # replace the variable AST with a string AST, I guess
+ # unless, of course, the variable points to another
+ # object...
+ # crap, what if it does?
+ end
+ when AST::VarDef then
+ unless object.name.is_a?(AST::Word)
+ raise InterpreterError.new("invalid variable name")
+ end
+
+ # this is quite probably more than a simple value...
+ case object.value
+ when AST::String then
+ @symtable[object.name.value] = object.value.value
+ when AST::Word then
+ # just copy whatever's already in the symtable
+ @symtable[object.name.value] =
+ @symtable[object.value.value]
+ else
+ # um, i have no idea what to do in other cases...
+ end
+ when AST::FunctionCall then
+ when AST::ObjectDef then
+ object.params.each { |param|
+ }
+ end
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # this pass creates the actual objects
+ # eventually it will probably be one of the last passes, but
+ # it's the easiest to create, so...
+
+ # XXX this won't really work for the long term --
+ # this will cause each operation on an object to be treated
+ # as an independent copy of the object, which will fail
+ # purposefully
+ def disabled_pass1_mkobjects
+ Interpreter.descend(@tree) { |object,index,parent|
+ case object
+ when Blink::Parser::Parser::AST::ObjectDef then # yuk
+ args = {}
+ object.each { |param|
+ # heh, this is weird
+ # the parameter object stores its value in @value
+ # and that's an object, so you have to call .value
+ # again
+ args[param.param] = param.value.value
+ }
+
+ args[:name] = object.name.value
+ klass = "Blink::Objects::" + object.type.capitalize
+ newobj = eval(klass).new(args)
+ parent[index] = newobj
+ when Blink::Parser::Parser::AST::ObjectParam then
+ # nothing
+ end
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def disabled_pass2_exeobjects
+ Blink.debug("tree is %s" % @tree)
+ Blink.debug("tree type is %s" % @tree.class)
+ Interpreter.descend(@tree) { |object,index,parent|
+ #Blink.debug("object is %s" % object)
+ puts("object is %s" % object)
+ case
+ when object.is_a?(Blink::Objects) then
+ object.evaluate
+ end
+ }
+ end
+
+ class IObject < Hash
+ attr_accessor :name
+
+ @ohash = {}
+ @oarray = []
+
+ def initialize(name)
+ if @ohash.include?(name)
+ raise "%s already exists" % name
+ else
+ @ohash[name] = self
+ @oarray.push(self)
+ end
+ end
+ end
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/blink/parser/lexer.rb b/lib/blink/parser/lexer.rb
new file mode 100644
index 000000000..66e75a315
--- /dev/null
+++ b/lib/blink/parser/lexer.rb
@@ -0,0 +1,182 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the scanner/lexer
+
+require 'strscan'
+require 'blink'
+
+
+module Blink
+ class LexError < RuntimeError; end
+ module Parser
+ #---------------------------------------------------------------
+ class Lexer
+ attr_reader :line, :last, :file
+
+ @@tokens = {
+ %r{#.+} => :COMMENT,
+ %r{\[} => :LBRACK,
+ %r{\]} => :RBRACK,
+ %r{\{} => :LBRACE,
+ %r{\}} => :RBRACE,
+ %r{\(} => :LPAREN,
+ %r{\)} => :RPAREN,
+ %r{"} => :DQUOTE,
+ %r{\n} => :RETURN,
+ %r{'} => :SQUOTE,
+ %r{=} => :EQUALS,
+ %r{,} => :COMMA,
+ %r{\?} => :QMARK,
+ %r{\\} => :BACKSLASH,
+ %r{=>} => :FARROW,
+ %r{\w+} => :WORD,
+ %r{:\w+} => :SYMBOL
+ }
+
+ # scan the whole file
+ # basically just used for testing
+ def fullscan
+ array = []
+
+ self.scan { |token,str|
+ #Blink.debug("got token '%s' => '%s'" % [token,str])
+ if token.nil?
+ return array
+ else
+ array.push([token,str])
+ end
+ }
+ return array
+ end
+
+ # this is probably pretty damned inefficient...
+ # it'd be nice not to have to load the whole file first...
+ def file=(file)
+ @file = file
+ File.open(file) { |of|
+ str = ""
+ of.each { |line| str += line }
+ @scanner = StringScanner.new(str)
+ }
+ end
+
+ def initialize
+ @line = 1
+ @last = ""
+ @scanner = nil
+ @file = nil
+ @skip = %r{\s+}
+ end
+
+ def rest
+ @scanner.rest
+ end
+
+ # this is the heart of the lexer
+ def scan
+ Blink.debug("entering scan")
+ if @scanner.nil?
+ raise TypeError.new("Invalid or empty string")
+ end
+
+ @scanner.skip(@skip)
+ until @scanner.eos? do
+ yielded = false
+ sendbreak = false # gah, this is a nasty hack
+ stoken = nil
+ sregex = nil
+ value = ""
+
+ # first find out which type of token we've got
+ @@tokens.each { |regex,token|
+ # we're just checking, which doesn't advance the scan
+ # pointer
+ tmp = @scanner.check(regex)
+ if tmp.nil?
+ #blink.debug("did not match %s to '%s'" %
+ # [regex,@scanner.rest])
+ next
+ end
+
+ # find the longest match
+ if tmp.length > value.length
+ value = tmp
+ stoken = token
+ sregex = regex
+ else
+ # we've already got a longer match
+ next
+ end
+ }
+
+ # error out if we didn't match anything at all
+ if stoken.nil?
+ raise "Could not match '%s'" % @scanner.rest
+ end
+
+ value = @scanner.scan(sregex)
+
+ if value == ""
+ raise "Didn't match regex on token %s" % stoken
+ end
+
+ # token-specific operations
+ # if this gets much more complicated, it should
+ # be moved up to where the tokens themselves are defined
+ # which will get me about 75% of the way to a lexer generator
+ case stoken
+ when :COMMENT then
+ # just throw comments away
+ when :RETURN then
+ Blink.debug("one more line")
+ @line += 1
+ @scanner.skip(@skip)
+ when :DQUOTE then
+ #Blink.debug("searching '%s' after '%s'" % [self.rest,value])
+ value = self.slurpstring(value)
+ yield [:QTEXT,value]
+ @last = value
+ #stoken = :QTEXT
+ Blink.debug("got string '%s' => '%s'" % [:QTEXT,value])
+ when :SYMBOL then
+ value.sub!(/^:/,'')
+ yield [:QTEXT,value]
+ @last = value
+ Blink.debug("got token '%s' => '%s'" % [:QTEXT,value])
+ else
+ yield [stoken,value]
+ @last = value
+ Blink.debug("got token '%s' => '%s'" % [stoken,value])
+ end
+ @scanner.skip(@skip)
+ end
+ @scanner = nil
+ yield [false,false]
+ end
+
+ # we've encountered an opening quote...
+ # slurp in the rest of the string and return it
+ def slurpstring(quote)
+ #Blink.debug("searching '%s'" % self.rest)
+ str = @scanner.scan_until(/[^\\]#{quote}/)
+ #str = @scanner.scan_until(/"/)
+ if str.nil?
+ raise Blink::LexError.new("Unclosed quote after '%s' in '%s'" %
+ [self.last,self.rest])
+ else
+ str.sub!(/#{quote}$/,"")
+ str.gsub!(/\\#{quote}/,quote)
+ end
+
+ return str
+ end
+
+ def string=(string)
+ @scanner = StringScanner.new(string)
+ end
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/blink/parser/makefile b/lib/blink/parser/makefile
new file mode 100644
index 000000000..eea119d56
--- /dev/null
+++ b/lib/blink/parser/makefile
@@ -0,0 +1,5 @@
+#parser.rb: grammar.ry
+# ryacc --output parser grammar
+
+parser.rb: grammar.ra
+ racc -o$@ grammar.ra
diff --git a/lib/blink/parser/parser.rb b/lib/blink/parser/parser.rb
new file mode 100644
index 000000000..2c341f410
--- /dev/null
+++ b/lib/blink/parser/parser.rb
@@ -0,0 +1,683 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by racc 1.4.4
+# from racc grammer file "grammar.ra".
+#
+
+require 'racc/parser'
+
+
+require 'blink/parser/lexer'
+#require 'blink/parser/interpreter'
+
+module Blink
+ class ParseError < Racc::ParseError; end
+end
+
+
+module Blink
+
+ module Parser
+
+ class Parser < Racc::Parser
+
+module_eval <<'..end grammar.ra modeval..id5273b1fd0f', 'grammar.ra', 171
+def file=(file)
+ @lexer.file = file
+end
+
+def initialize
+ @lexer = Blink::Parser::Lexer.new()
+ if Blink[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #puts "Parse stack:"
+ #puts stack
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ error = "line %s: parse error after '%s'" % [@lexer.line,@lexer.last]
+
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ raise Blink::ParseError.new(error)
+end
+
+# how should I do error handling here?
+def parse
+ yyparse(@lexer,:scan)
+ #begin
+ # yyparse(@lexer,:scan)
+ #rescue Racc::ParseError => detail
+ # raise Racc::ParseError.new("line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last])
+ #end
+end
+
+def string=(string)
+ @lexer.string = string
+end
+
+# the parent class for all of our syntactical objects
+class AST
+ attr_accessor :line
+ @@pink = ""
+ @@green = ""
+ @@yellow = ""
+ @@reset = ""
+
+ @@indent = " " * 4
+ @@indline = @@pink + ("-" * 4) + @@reset
+ @@midline = @@yellow + ("-" * 4) + @@reset
+
+ def AST.indention
+ return @@indent * @@indention
+ end
+
+ def AST.midline
+ return @@midline
+ end
+
+ def typewrap(string)
+ #return self.class.to_s.sub(/.+::/,'') + "(" + @@green + string +@@reset+ ")"
+ return @@green + string +@@reset+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
+ end
+
+ def initialize(*rest)
+ begin
+ args = Hash[*rest]
+ rescue ArgumentError
+ raise ArgumentError.new("Arguments must be passed as name => value pairs")
+ end
+ args.each { |param,value|
+ method = param.to_s + "="
+ unless self.respond_to?(method)
+ raise "Invalid parameter %s to object class %s" %
+ [method,self.class.to_s]
+ end
+
+ begin
+ #Blink.debug("sending %s to %s" % [method, self.class])
+ self.send(method,value)
+ rescue => detail
+ # XXX this should be more normal error correction
+ raise "Could not set parameter %s on class %s: %s" %
+ [method,self.class.to_s,detail]
+ end
+ }
+ end
+
+ class ASTArray < Array
+ def tree(indent = 0)
+ #puts((AST.indent * indent) + self.pin)
+ self.collect { |child|
+ child.tree(indent)
+ }.join("\n" + (AST.midline * (indent+1)) + "\n")
+ end
+ end
+
+ # this differentiation is used by the interpreter
+ # XXX i now need a standard mechanism for descending into children
+
+ # these objects have children
+ class Branch < AST
+ include Enumerable
+ attr_accessor :pin
+
+ def each
+ @children.each { |child|
+ yield child
+ }
+ end
+
+ def tree(indent = 0)
+ return ((@@indline * indent) + self.typewrap(self.pin)) + "\n" +
+ self.collect { |child|
+ child.tree(indent + 1)
+ }.join("\n")
+ end
+ end
+
+ # and these ones don't
+ class Leaf < AST
+ attr_accessor :value, :type
+
+ def tree(indent = 0)
+ return ((@@indent * indent) + self.typewrap(self.value))
+ end
+
+ def to_s
+ return @value
+ end
+ end
+
+ class String < AST::Leaf
+ attr_accessor :value
+ end
+
+ class Word < AST::Leaf
+ attr_accessor :value
+ end
+
+ class ObjectDef < AST::Branch
+ attr_accessor :name, :object
+ attr_reader :params
+
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ def [](index)
+ return @params[index]
+ end
+
+ def each
+ #Blink.debug("each called on %s" % self)
+ [@object,@name,@params].flatten.each { |param|
+ #Blink.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def initialize(*args)
+ super(*args)
+ end
+
+ def params=(params)
+ if params.is_a?(Array)
+ @params = params
+ else
+ @params = [params]
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @object.tree(indent + 1),
+ @name.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @params.collect { |param|
+ begin
+ param.tree(indent + 1)
+ rescue NoMethodError => detail
+ puts "failed to tree"
+ puts @params
+ p param
+ raise
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@name,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+ end
+
+ class ObjectParam < AST::Branch
+ attr_accessor :value, :param
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@param,@value]
+ end
+ end
+
+ class Selector < AST::Branch
+ attr_accessor :param, :value
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+ end
+
+ class VarDef < AST::Branch
+ attr_accessor :name, :value
+
+ def each
+ [@name,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@value]
+ end
+ end
+
+ class FunctionCall < AST::Branch
+ attr_accessor :name, :values
+
+ def each
+ [@name,@values].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @values.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@values]
+ end
+ end
+end
+..end grammar.ra modeval..id5273b1fd0f
+
+##### racc 1.4.4 generates ###
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 18, :_reduce_1,
+ 1, 19, :_reduce_none,
+ 2, 19, :_reduce_3,
+ 1, 20, :_reduce_none,
+ 1, 20, :_reduce_none,
+ 1, 20, :_reduce_none,
+ 1, 20, :_reduce_none,
+ 8, 21, :_reduce_8,
+ 3, 22, :_reduce_9,
+ 1, 26, :_reduce_10,
+ 3, 26, :_reduce_11,
+ 3, 28, :_reduce_12,
+ 1, 29, :_reduce_none,
+ 2, 29, :_reduce_14,
+ 1, 25, :_reduce_15,
+ 1, 25, :_reduce_none,
+ 1, 25, :_reduce_none,
+ 1, 25, :_reduce_none,
+ 3, 23, :_reduce_19,
+ 1, 30, :_reduce_none,
+ 3, 30, :_reduce_21,
+ 1, 31, :_reduce_none,
+ 2, 31, :_reduce_23,
+ 4, 24, :_reduce_24,
+ 3, 24, :_reduce_25,
+ 0, 27, :_reduce_none,
+ 1, 27, :_reduce_27 ]
+
+racc_reduce_n = 28
+
+racc_shift_n = 46
+
+racc_action_table = [
+ 17, 17, 19, 19, 21, 30, 11, 38, 11, 17,
+ 21, 19, 22, 29, 35, 27, 9, 10, 12, 10,
+ 12, 17, 17, 19, 19, 21, 33, 3, 13, 3,
+ 39, 21, 43, 44, 21 ]
+
+racc_action_check = [
+ 28, 12, 28, 12, 32, 21, 3, 32, 17, 11,
+ 10, 11, 10, 13, 28, 12, 3, 3, 3, 17,
+ 17, 30, 9, 30, 9, 22, 25, 6, 5, 0,
+ 33, 39, 40, 42, 43 ]
+
+racc_action_pointer = [
+ 27, nil, nil, 3, nil, 28, 25, nil, nil, 20,
+ 6, 7, -1, 13, nil, nil, nil, 5, nil, nil,
+ nil, -4, 21, nil, nil, 21, nil, nil, -2, nil,
+ 19, nil, 0, 24, nil, nil, nil, nil, nil, 27,
+ 22, nil, 26, 30, nil, nil ]
+
+racc_action_default = [
+ -28, -5, -6, -28, -7, -28, -1, -2, -4, -28,
+ -28, -28, -28, -28, -3, -16, -18, -28, -9, -15,
+ -17, -28, -28, -20, -19, -28, -13, -25, -28, 46,
+ -28, -22, -28, -28, -14, -24, -12, -23, -21, -28,
+ -26, -10, -28, -27, -8, -11 ]
+
+racc_goto_table = [
+ 23, 8, 18, 7, 25, 26, 5, 8, 2, 14,
+ 4, 40, 31, 42, 2, 6, 4, 28, 24, 32,
+ nil, 34, 37, 36, nil, nil, nil, nil, nil, 41,
+ nil, nil, nil, 45 ]
+
+racc_goto_check = [
+ 11, 4, 8, 3, 8, 8, 1, 4, 6, 3,
+ 7, 9, 11, 10, 6, 2, 7, 12, 13, 14,
+ nil, 8, 11, 8, nil, nil, nil, nil, nil, 11,
+ nil, nil, nil, 11 ]
+
+racc_goto_pointer = [
+ nil, 6, 15, 3, 1, nil, 8, 10, -7, -28,
+ -27, -10, 5, 8, -3 ]
+
+racc_goto_default = [
+ nil, nil, nil, nil, 20, 1, 15, 16, nil, nil,
+ nil, nil, nil, nil, nil ]
+
+racc_token_table = {
+ false => 0,
+ Object.new => 1,
+ :WORD => 2,
+ :LBRACK => 3,
+ :QTEXT => 4,
+ :RBRACK => 5,
+ :LBRACE => 6,
+ :RBRACE => 7,
+ :SYMBOL => 8,
+ :FARROW => 9,
+ :COMMA => 10,
+ :TRUE => 11,
+ :FALSE => 12,
+ :EQUALS => 13,
+ :QMARK => 14,
+ :LPAREN => 15,
+ :RPAREN => 16 }
+
+racc_use_result_var = true
+
+racc_nt_base = 17
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+'$end',
+'error',
+'WORD',
+'LBRACK',
+'QTEXT',
+'RBRACK',
+'LBRACE',
+'RBRACE',
+'SYMBOL',
+'FARROW',
+'COMMA',
+'TRUE',
+'FALSE',
+'EQUALS',
+'QMARK',
+'LPAREN',
+'RPAREN',
+'$start',
+'program',
+'statements',
+'statement',
+'object',
+'assignment',
+'selector',
+'functioncall',
+'rvalue',
+'params',
+'endcomma',
+'param',
+'rvalues',
+'svalues',
+'sintvalues']
+
+Racc_debug_parser = false
+
+##### racc system variables end #####
+
+ # reduce 0 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 31
+ def _reduce_1( val, _values, result )
+ if val[0].is_a?(Array)
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0]])
+ end
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Blink[:parseonly]
+ begin
+ puts result.tree(0)
+ rescue NoMethodError => detail
+ puts detail
+ exit(78)
+ end
+ else
+ require 'blink/parser/interpreter'
+ result = Blink::Parser::Interpreter.new(result)
+ end
+ result
+ end
+.,.,
+
+ # reduce 2 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 41
+ def _reduce_3( val, _values, result )
+ if val[0].is_a?(Array)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0],val[1]])
+ end
+ result
+ end
+.,.,
+
+ # reduce 4 omitted
+
+ # reduce 5 omitted
+
+ # reduce 6 omitted
+
+ # reduce 7 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 60
+ def _reduce_8( val, _values, result )
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::ObjectDef.new(
+ :pin => "[]",
+ :line => @lexer.line,
+ :object => leaf,
+ :name => val[2],
+ :params => val[5]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 73
+ def _reduce_9( val, _values, result )
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :name => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 74
+ def _reduce_10( val, _values, result )
+ result = val[0]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 83
+ def _reduce_11( val, _values, result )
+ if val[0].is_a?(Array)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = [val[0],val[2]]
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 96
+ def _reduce_12( val, _values, result )
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 13 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 105
+ def _reduce_14( val, _values, result )
+ if val[0].is_a?(Array)
+ result = val[0].push(val[1])
+ else
+ result = AST::Array.new(val[0],val[1])
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 112
+ def _reduce_15( val, _values, result )
+ result = AST::String.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+ # reduce 16 omitted
+
+ # reduce 17 omitted
+
+ # reduce 18 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 128
+ def _reduce_19( val, _values, result )
+ leaf = AST::Word.new(
+ :line => @lexer.line,
+ :value => val[0]
+ )
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 20 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 131
+ def _reduce_21( val, _values, result )
+ result = val[1]
+ result
+ end
+.,.,
+
+ # reduce 22 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 142
+ def _reduce_23( val, _values, result )
+ if val[0].is_a?(Array)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new([val[0],val[1]])
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 150
+ def _reduce_24( val, _values, result )
+ result = AST::FunctionCall.new(
+ :pin => '()',
+ :name => AST::Word.new(:value => val[0], :line => @lexer.line),
+ :values => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 156
+ def _reduce_25( val, _values, result )
+ result = FunctionDef.new(
+ :pin => '()',
+ :name => val[0]
+ )
+ result
+ end
+.,.,
+
+ # reduce 26 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 158
+ def _reduce_27( val, _values, result )
+ result = nil
+ result
+ end
+.,.,
+
+ def _reduce_none( val, _values, result )
+ result
+ end
+
+ end # class Parser
+
+ end # module Parser
+
+end # module Blink