Skip to content
Browse files

Going to the lab.

  • Loading branch information...
1 parent 10de345 commit 65a042e8d9000ea4836996e8bf351cf1bc1e2bcb @chriswailes committed Mar 7, 2011
Showing with 87 additions and 11 deletions.
  1. +1 −0 AUTHORS
  2. 0 LICENSE
  3. 0 README
  4. +8 −0 TODO
  5. +6 −2 ast.rb
  6. +71 −6 parser.rb
  7. +1 −3 test.rb
View
1 AUTHORS
@@ -0,0 +1 @@
+Chris Wailes <chris.wailes@gmail.com>
View
0 LICENSE
No changes.
View
0 README
No changes.
View
8 TODO
@@ -0,0 +1,8 @@
+Operator priority and associativity.
+Identify Shift/Reduce and Reduce/Reduce conflicts in explain.
+Input checking. Upercase/lowercase for :TERM and :NONTERM. Proc arity.
+Parsing errors that can be caught by the user's code.
+Use LALR(1) lookahead sets to optimize parsing.
+Optimize the parsing algorithm (remove unecessary copy operations and branching).
+Saving and loading the parse table.
+Implement some unit testing.
View
8 ast.rb
@@ -58,8 +58,12 @@ def has_key?(key)
end
def initialize
- @notes = Hash.new()
- @parent = nil
+ if self.class == RLTK::Node
+ raise Exception, 'Attempting to instantiate the RLTK::Node class.'
+ else
+ @notes = Hash.new()
+ @parent = nil
+ end
end
def inspect
View
77 parser.rb
@@ -21,6 +21,7 @@ class InternalParserError < Exception; end
class Parser
def Parser.inherited(klass)
klass.class_exec do
+ @precs = {:'!left' => 0, :'!right' => 0, :'!non' => 0}
@proxy = RuleProxy.new(self)
@rules = Hash.new {|h, k| h[k] = Array.new}
@start_symbol = nil
@@ -216,7 +217,41 @@ def self.get_star(token)
return new_token
end
- def self.rule(symbol, expression = nil, &action)
+ def self.left(*symbols)
+ symbols.each do |s|
+ if s.is_a?(Symbol) and s.to_s == s.to_s.upcase
+ @precs[s] = [:left, @precs[:'!left'] += 1]
+ else
+ raise InternalParserError, 'Incorrect token specification given to the left directive.'
+ end
+ end
+ end
+
+ def self.nonassoc(*symbols)
+ symbols.each do |s|
+ if s.is_a?(Symbol) and s.to_s == s.to_s.upcase
+ @precs[s] = [:non, @precs[:'!non'] += 1]
+ else
+ raise InternalParserError, 'Incorrect token specification given to the nonassoc directive.'
+ end
+ end
+ end
+
+ def self.precs
+ @precs
+ end
+
+ def self.right(*symbols)
+ symbols.each do |s|
+ if s.is_a?(Symbol) and s.to_s == s.to_s.upcase
+ @precs[s] = [:right, @precs[:'!right'] += 1]
+ else
+ raise InternalParserError, 'Incorrect token specification given to the right directive.'
+ end
+ end
+ end
+
+ def self.rule(symbol, expression = nil, precedence = nil, &action)
# Convert the 'symbol' to a Symbol if it isn't already.
symbol = symbol.to_sym if not symbol.is_a?(Symbol)
@@ -229,7 +264,7 @@ def self.rule(symbol, expression = nil, &action)
# Collect rules by symbol and by rule id.
if expression
- @rules[symbol] << (rule = @proxy.clause(expression, &action))
+ @rules[symbol] << (rule = @proxy.clause(expression, precedence, &action))
@rules[rule.id] = rule
else
@@ -262,7 +297,13 @@ def parse(tokens, verbose = false)
processing = [ParseStack.new]
moving_on = []
+ # Iterate over the tokens. We don't procede to the
+ # next token until every stack is done with the
+ # current one.
tokens.each do |token|
+
+ # If we don't have any active stacks the string
+ # isn't in the language.
if processing.length == 0
raise ParsingError, 'String not in language.'
end
@@ -271,12 +312,20 @@ def parse(tokens, verbose = false)
v.puts("Current token: #{token.type}#{if token.value then "(#{token.value})" end}")
end
+ # Iterate over the stacks until each one is done.
until processing.empty?
stack = processing.shift
new_stacks = []
- self.class.table[stack.state].on?(token.type).each do |action|
+ # Get the available actions for this stack.
+ actions = self.class.table[stack.state].on?(token.type)
+
+ # Filter the actions based on precedence and
+ # associativity.
+
+
+ actions.each do |action|
new_stacks << (nstack = stack.copy)
if verbose
@@ -373,12 +422,17 @@ class Rule
attr_reader :tokens
attr_reader :action
- def initialize(id, symbol, tokens, &action)
+ def initialize(id, symbol, tokens, precedence = nil, &action)
@id = id
@symbol = symbol
@tokens = tokens
+ @prec = precedence
@action = action || Proc.new {}
+ if not @prec.is_a?(Symbol)
+ raise InternalParserError, 'Non-Symbol object used to specify precedence.'
+ end
+
@dot_index = @tokens.index {|t| t.type == :DOT}
end
@@ -423,7 +477,7 @@ def initialize(parser)
@symbol = nil
end
- def clause(expression, &action)
+ def clause(expression, precedence = nil, &action)
tokens = @lexer.lex(expression)
new_tokens = [Token.new(:DOT)]
@@ -456,8 +510,19 @@ def clause(expression, &action)
end
end
+ # If no precedence is specified use the precedence of the
+ # last terminal in the production.
+ if not precedence and new_tokens.length > 1
+ new_tokens.reverse_each do |t|
+ if t.type == :TERM
+ precedence = t.value
+ break
+ end
+ end
+ end
+
# Add the item to the current list.
- @rules << (rule = Rule.new(self.next_id, @symbol, new_tokens, &action))
+ @rules << (rule = Rule.new(self.next_id, @symbol, new_tokens, precedence, &action))
# Return the item from this clause.
return rule
View
4 test.rb
@@ -1,7 +1,5 @@
#!/usr/bin/ruby
-require 'pp'
-
require 'lexer'
require 'parser'
@@ -28,4 +26,4 @@ class ABParser < RLTK::Parser
lexer = ABLexer.new
parser = ABParser.new
-pp parser.parse(lexer.lex(ARGV[0]), if ARGV[1] then (ARGV[1] == 'true') ? true : ARGV[1] end)
+puts parser.parse(lexer.lex(ARGV[0]), if ARGV[1] then (ARGV[1] == 'true') ? true : ARGV[1] end)

0 comments on commit 65a042e

Please sign in to comment.
Something went wrong with that request. Please try again.