rly 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,3 +2,4 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - rbx-19mode
5
+ - jruby-19mode
data/README.md CHANGED
@@ -16,3 +16,9 @@ Install via rubygems
16
16
  You need to create lexer and parser classes for each grammar you want to process.
17
17
  It is commonly done by subclassing {Rly::Lex} and {Rly::Parse} classes (check the
18
18
  appropriate docs).
19
+
20
+ You can also read the tutorials on the wiki:
21
+
22
+ * [Calculator Tutorial Part 1: Basic lexer](https://github.com/farcaller/rly/wiki/Calculator-Tutorial-Part-1:-Basic-lexer)
23
+ * [Calculator Tutorial Part 2: Basic parser](https://github.com/farcaller/rly/wiki/Calculator-Tutorial-Part-2:-Basic-parser)
24
+ * [Calculator Tutorial Part 3: Advanced parser](https://github.com/farcaller/rly/wiki/Calculator-Tutorial-Part-3:-Advanced-parser)
@@ -1,4 +1,4 @@
1
- Created by PLY version 3.4 (http://www.dabeaz.com/ply)
1
+ Created by rly version <%= ver %>
2
2
 
3
3
  Grammar
4
4
 
@@ -89,6 +89,7 @@ module Rly
89
89
  # t = lex.next # => nil
90
90
  def input(input)
91
91
  @input << input
92
+ nil
92
93
  end
93
94
 
94
95
  # Processes the next token in input
@@ -249,6 +250,7 @@ module Rly
249
250
  else
250
251
  raise ArgumentError
251
252
  end
253
+ nil
252
254
  end
253
255
 
254
256
  # Specifies a list of one-char literals
@@ -272,6 +274,7 @@ module Rly
272
274
  # end
273
275
  def literals(lit)
274
276
  @literals = lit
277
+ nil
275
278
  end
276
279
 
277
280
  # Specifies a list of one-char symbols to be ignored in input
@@ -297,6 +300,7 @@ module Rly
297
300
  # end
298
301
  def ignore(ign)
299
302
  @ignores = ign
303
+ nil
300
304
  end
301
305
 
302
306
  # Specifies a block that should be called on error
@@ -325,6 +329,7 @@ module Rly
325
329
  # end
326
330
  def on_error(&block)
327
331
  @error_block = block
332
+ nil
328
333
  end
329
334
  end
330
335
  end
@@ -25,7 +25,7 @@ module Rly
25
25
  @start = nil
26
26
  end
27
27
 
28
- def add_production(name, symbols, &block)
28
+ def add_production(name, symbols, enforced_prec=nil, &block)
29
29
  raise ArgumentError unless name.downcase == name
30
30
  raise ArgumentError if name == :error
31
31
 
@@ -36,7 +36,13 @@ module Rly
36
36
  end
37
37
  end
38
38
 
39
- precedence = prec_for_rightmost_terminal(symbols)
39
+ if enforced_prec
40
+ precedence = @precedence[enforced_prec]
41
+ raise RuntimeError.new("Nothing known about the precedence of '#{enforced_prec}'") unless precedence
42
+ @used_precedence[precedence] = true
43
+ else
44
+ precedence = prec_for_rightmost_terminal(symbols)
45
+ end
40
46
 
41
47
  mapname = "#{name.to_s} -> #{symbols.to_s}"
42
48
  raise ArgumentError if @prodmap[mapname]
@@ -1,4 +1,6 @@
1
1
  require "rly/parse/lr_table"
2
+ require "rly/version"
3
+ require "erb"
2
4
 
3
5
  module Rly
4
6
 
@@ -17,7 +19,7 @@ module Rly
17
19
  def to_s
18
20
  fn = File.join(File.dirname(__FILE__), '..', '..', '..', 'assets', 'ply_dump.erb')
19
21
  e = ERB.new(open(fn).read)
20
- e.result(TinyContext.new(g: @grammar, backlog: @backlog).get_binding)
22
+ e.result(TinyContext.new(g: @grammar, backlog: @backlog, ver: Rly::VERSION).get_binding)
21
23
  end
22
24
 
23
25
  def self.stub
@@ -8,6 +8,7 @@ module Rly
8
8
  return @lexer_class if @lexer_class
9
9
 
10
10
  @lexer_class = Class.new(Lex) do
11
+ token :PREC, /\%prec/
11
12
  token :ID, /[a-zA-Z_][a-zA-Z_0-9]*/
12
13
  token :LITERAL, /"."|'.'/ do |t|
13
14
  t.value = t.value[1]
@@ -25,10 +26,20 @@ module Rly
25
26
 
26
27
  @grammar = Grammar.new(self.class.lexer_class.terminals)
27
28
 
29
+ @grammar.add_production(:grammar_def, [:grammar]) do |d, g|
30
+ d.value = g.value
31
+ end
32
+ @grammar.add_production(:grammar, [:ID, ':', :rules, :PREC, :ID]) do |g, pname, _, r, _, prec|
33
+ productions = []
34
+ r.value.each do |p|
35
+ productions << [pname.value.to_sym, p, prec.value.to_sym]
36
+ end
37
+ g.value = productions
38
+ end
28
39
  @grammar.add_production(:grammar, [:ID, ':', :rules]) do |g, pname, _, r|
29
40
  productions = []
30
41
  r.value.each do |p|
31
- productions << [pname.value.to_sym, p]
42
+ productions << [pname.value.to_sym, p, nil]
32
43
  end
33
44
  g.value = productions
34
45
  end
@@ -2,5 +2,12 @@ module Rly
2
2
  class YaccSymbol
3
3
  attr_accessor :type, :value, :lineno, :endlineno, :lexpos, :endlexpos
4
4
 
5
+ def to_s
6
+ @value.to_s
7
+ end
8
+
9
+ def inspect
10
+ "#<YaccSymbol #{@type} '#{@value}'>"
11
+ end
5
12
  end
6
13
  end
@@ -1,4 +1,4 @@
1
1
  module Rly
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  PLY_BASE_VERSION = "3.4"
4
4
  end
@@ -2,6 +2,7 @@ require "rly/lex"
2
2
  require "rly/parse/grammar"
3
3
  require "rly/parse/yacc_production"
4
4
  require "rly/parse/yacc_symbol"
5
+ require "rly/parse/ply_dump"
5
6
 
6
7
  module Rly
7
8
  class YaccError < RuntimeError; end
@@ -16,7 +17,11 @@ module Rly
16
17
  @grammar = grammar
17
18
  end
18
19
 
19
- def parse(input=nil)
20
+ def inspect
21
+ "#<#{self.class} ...>"
22
+ end
23
+
24
+ def parse(input=nil, trace=false)
20
25
  lookahead = nil
21
26
  lookaheadstack = []
22
27
  actions = @lr_table.lr_action
@@ -51,7 +56,7 @@ module Rly
51
56
  # is already set, we just use that. Otherwise, we'll pull
52
57
  # the next token off of the lookaheadstack or from the lexer
53
58
 
54
- # DBG # puts "State: #{state}"
59
+ puts "State : #{state}" if trace
55
60
 
56
61
  unless lookahead
57
62
  if lookaheadstack.empty?
@@ -65,6 +70,8 @@ module Rly
65
70
  end
66
71
  end
67
72
 
73
+ puts "Stack : #{(@symstack[1..-1].map{|s|s.type}.join(' ') + ' ' + lookahead.inspect).lstrip}" if trace
74
+
68
75
  # Check the action table
69
76
  ltype = lookahead.type
70
77
  t = actions[state][ltype]
@@ -75,7 +82,7 @@ module Rly
75
82
  @statestack.push(t)
76
83
  state = t
77
84
 
78
- # DBG # puts "Action : Shift and goto state #{t}"
85
+ puts "Action : Shift and goto state #{t}" if trace
79
86
 
80
87
  @symstack.push(lookahead)
81
88
  lookahead = nil
@@ -96,11 +103,13 @@ module Rly
96
103
  sym.type = pname
97
104
  sym.value = nil
98
105
 
99
- # DBG # if plen
100
- # DBG # puts "Action : Reduce rule [#{p}] with [#{@symstack[-plen..@symstack.length].map{|s|s.value}.join(', ')}] and goto state #{-t}"
101
- # DBG # else
102
- # DBG # puts "Action : Reduce rule [#{p}] with [] and goto state #{-t}"
103
- # DBG # end
106
+ if trace
107
+ if plen
108
+ puts "Action : Reduce rule [#{p}] with [#{@symstack[-plen..@symstack.length].map{|s|s.value}.join(', ')}] and goto state #{-t}"
109
+ else
110
+ puts "Action : Reduce rule [#{p}] with [] and goto state #{-t}"
111
+ end
112
+ end
104
113
 
105
114
  if plen
106
115
  targ = @symstack.pop(plen)
@@ -118,7 +127,7 @@ module Rly
118
127
  @statestack.pop(plen)
119
128
  instance_exec(*targ, &p.block)
120
129
 
121
- # DBG # puts "Result : #{targ[0].value}"
130
+ puts "Result : #{targ[0].value}" if trace
122
131
 
123
132
  @symstack.push(sym)
124
133
  state = goto[@statestack[-1]][pname]
@@ -151,7 +160,7 @@ module Rly
151
160
  @statestack.pop(plen)
152
161
  pslice[0] = instance_exec(*pslice, &p.block)
153
162
 
154
- # DBG # puts "Result : #{targ[0].value}"
163
+ puts "Result : #{targ[0].value}" if trace
155
164
 
156
165
  @symstack.push(sym)
157
166
  state = goto[@statestack[-1]][pname]
@@ -176,7 +185,7 @@ module Rly
176
185
  n = @symstack[-1]
177
186
  result = n.value
178
187
 
179
- # DBG # puts "Done : Returning #{result}"
188
+ puts "Done : Returning #{result}" if trace
180
189
 
181
190
  return result
182
191
  end
@@ -194,7 +203,7 @@ module Rly
194
203
  # first syntax error. This function is only called if
195
204
  # errorcount == 0.
196
205
  if errorcount == 0 || @errorok == true
197
- errorcount = error_count
206
+ errorcount = self.class.error_count
198
207
  @errorok = false
199
208
  errtoken = lookahead
200
209
  errtoken = nil if errtoken.type == :"$end"
@@ -289,20 +298,26 @@ module Rly
289
298
 
290
299
  @grammar = Grammar.new(@lex.class.terminals)
291
300
 
292
- self.class.prec_rules.each do |assoc, terms|
293
- terms.each_with_index do |term, i|
301
+ self.class.prec_rules.each do |assoc, terms, i|
302
+ terms.each do |term|
294
303
  @grammar.set_precedence(term, assoc, i)
295
304
  end
296
305
  end
297
306
 
298
- self.class.parsed_rules.each do |prod, block|
299
- @grammar.add_production(*prod, &block)
307
+ self.class.parsed_rules.each do |pname, p, prec, block|
308
+ @grammar.add_production(pname, p, prec, &block)
300
309
  end
301
310
 
302
311
  @grammar.set_start
303
312
 
304
313
  @grammar.build_lritems
305
314
 
315
+ if self.class.store_grammar_def
316
+ d = PlyDump.new(@grammar)
317
+ gdef = d.to_s
318
+ open(self.class.store_grammar_def, 'w') { |f| f.write(gdef) }
319
+ end
320
+
306
321
  @lr_table = LRTable.new(@grammar)
307
322
 
308
323
  @lr_table.parse_table
@@ -311,14 +326,20 @@ module Rly
311
326
  end
312
327
 
313
328
  class << self
314
- attr_accessor :rules, :grammar, :lexer_class, :prec_rules
329
+ attr_accessor :rules, :grammar, :lexer_class, :prec_rules, :error_handler, :store_grammar_def
330
+
331
+ def store_grammar(fn)
332
+ @store_grammar_def = fn
333
+ end
315
334
 
316
335
  def rule(desc, &block)
317
336
  self.rules << [desc, block]
337
+ nil
318
338
  end
319
339
 
320
340
  def lexer(&block)
321
341
  @lexer_class = Class.new(Lex, &block)
342
+ nil
322
343
  end
323
344
 
324
345
  def rules
@@ -327,7 +348,9 @@ module Rly
327
348
 
328
349
  def precedence(*prec)
329
350
  assoc = prec.shift
330
- self.prec_rules << [assoc, prec.reverse]
351
+ count = self.prec_rules.length + 1
352
+ self.prec_rules << [assoc, prec, count]
353
+ nil
331
354
  end
332
355
 
333
356
  def prec_rules
@@ -339,13 +362,13 @@ module Rly
339
362
  end
340
363
 
341
364
  def parsed_rules
342
- @parsed_rules if @parsed_rules
365
+ return @parsed_rules if @parsed_rules
343
366
 
344
367
  @parsed_rules = []
345
368
  rp = RuleParser.new
346
- self.rules.each do |d, b|
347
- rp.parse(d).each do |prod|
348
- @parsed_rules << [prod, b]
369
+ self.rules.each do |desc, block|
370
+ rp.parse(desc).each do |(pname, p, prec)|
371
+ @parsed_rules << [pname, p, prec, block]
349
372
  end
350
373
  end
351
374
  @parsed_rules
@@ -3,7 +3,7 @@ require "rly"
3
3
  module CalcSpecExample
4
4
  class CalcLex < Rly::Lex
5
5
  literals '=+-*/()'
6
- ignore " \t"
6
+ ignore " \t\n"
7
7
 
8
8
  token :NAME, /[a-zA-Z_][a-zA-Z0-9_]*/
9
9
 
@@ -12,11 +12,10 @@ module CalcSpecExample
12
12
  t
13
13
  end
14
14
 
15
- token(/\n+/) { |t| t.lexer.lineno += t.value.count("\n") }
16
-
17
15
  on_error do |t|
18
16
  puts "Illegal character #{t.value}"
19
17
  t.lexer.pos += 1
18
+ nil
20
19
  end
21
20
  end
22
21
 
@@ -44,9 +43,9 @@ module CalcSpecExample
44
43
  ex.value = e1.value.send(op.value, e2.value)
45
44
  end
46
45
 
47
- # rule 'expression : "-" expression %prec UMINUS' do |ex, _, e|
48
- # ex.value = - e.value
49
- # end
46
+ rule 'expression : "-" expression %prec UMINUS' do |ex, _, e|
47
+ ex.value = - e.value
48
+ end
50
49
 
51
50
  rule 'expression : "(" expression ")"' do |ex, _, e, _|
52
51
  ex.value = e.value
@@ -92,4 +91,8 @@ describe 'Calculator' do
92
91
  @calc.parse('magic = 42')
93
92
  @calc.parse('2 * magic').should == 84
94
93
  end
94
+
95
+ it "follows special case precedence rules" do
96
+ @calc.parse('2 + - 2 + 1').should == 1
97
+ end
95
98
  end
@@ -12,9 +12,9 @@ describe Rly::RuleParser do
12
12
  productions = p.parse(s)
13
13
 
14
14
  productions.length.should == 4
15
- productions[0].should == [:expression, [:expression, '+', :expression]]
16
- productions[1].should == [:expression, [:expression, '-', :expression]]
17
- productions[2].should == [:expression, [:expression, '*', :expression]]
18
- productions[3].should == [:expression, [:expression, '/', :expression]]
15
+ productions[0].should == [:expression, [:expression, '+', :expression], nil]
16
+ productions[1].should == [:expression, [:expression, '-', :expression], nil]
17
+ productions[2].should == [:expression, [:expression, '*', :expression], nil]
18
+ productions[3].should == [:expression, [:expression, '/', :expression], nil]
19
19
  end
20
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: