lrama 0.5.5 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d79363afacc07dac12ab5c1a861123e099626591104079c962e02f3df74a24f3
4
- data.tar.gz: d8ddf78087e27510ab9da808301505a10ec0151ef55d1df3a59cc80ffde81b53
3
+ metadata.gz: c786e8955350ca51e6226aacd493073f7c31714ef7d5911dfb16259aa9415513
4
+ data.tar.gz: d167b1a2df9dcbc8f31912e8842388aa7d6a788a8d3eeff0278c6d3ed5ecbac6
5
5
  SHA512:
6
- metadata.gz: acc15bb56862ea03c6b195253ba881786acf498719f930b8a8435a60f86b44d4383778cc215e7497971833c4be596a9c41a6c5134f77703a9f5cd0a6b5714ad2
7
- data.tar.gz: d45b2ec3a22ce2e29beb6a5ec0f4d7b142cebca3b6ca76786fa19b841c257d3841d5a7a43d7d564459b8f9c5b05faa7229bd9b2e2178592ec3988b64f77b535c
6
+ metadata.gz: c89fe932ef32b5f441b87df33c2431c8631a069c293306d0a39c858e462731e11d753117ab4e7c48b4e4e41015405328a8d8f5590fbce3544e67babdaeddd8a6
7
+ data.tar.gz: 619d1affd9f09c5c1b7748f712005b60073638be709e8bae986e39801173cf9de32555cf05837dbb7450360989989f5277e76a496eec702ace75c3b4af944471
data/.codespellignore ADDED
File without changes
@@ -0,0 +1,16 @@
1
+ name: CodeSpell
2
+ on:
3
+ - pull_request
4
+ jobs:
5
+ codespell:
6
+ name: CodeSpell
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - name: CodeSpell
11
+ uses: codespell-project/actions-codespell@master
12
+ with:
13
+ check_filenames: true
14
+ check_hidden: true
15
+ ignore_words_file: .codespellignore
16
+ exclude_file: lib/lrama/parser.rb
@@ -15,7 +15,7 @@ jobs:
15
15
  matrix:
16
16
  ruby: ['head', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5']
17
17
  steps:
18
- - uses: actions/checkout@v3
18
+ - uses: actions/checkout@v4
19
19
  - uses: ruby/setup-ruby@v1
20
20
  with:
21
21
  ruby-version: ${{ matrix.ruby }}
@@ -29,7 +29,7 @@ jobs:
29
29
  matrix:
30
30
  ruby: ['head']
31
31
  steps:
32
- - uses: actions/checkout@v3
32
+ - uses: actions/checkout@v4
33
33
  - uses: ruby/setup-ruby@v1
34
34
  with:
35
35
  ruby-version: ${{ matrix.ruby }}
@@ -38,13 +38,27 @@ jobs:
38
38
  - run: bundle exec rspec
39
39
  check-misc:
40
40
  runs-on: ubuntu-20.04
41
+ strategy:
42
+ matrix:
43
+ ruby: ['head']
41
44
  steps:
42
- - uses: actions/checkout@v3
45
+ - uses: actions/checkout@v4
46
+ - uses: ruby/setup-ruby@v1
47
+ with:
48
+ ruby-version: ${{ matrix.ruby }}
49
+ bundler-cache: true
50
+ - run: bundle install
51
+
43
52
  # Copy from https://github.com/ruby/ruby/blob/089227e94823542acfdafa68541d330eee42ffea/.github/workflows/check_misc.yml#L27
44
53
  - name: Check for trailing spaces
45
54
  run: |
46
- git grep -I -n '[ ]$' -- '*.rb' '*.[chy]' '*.rs' && exit 1 || :
55
+ git grep -I -n '[ ]$' -- '*.rb' '*.[chy]' '*.rs' ':!spec/' && exit 1 || :
47
56
  git grep -n '^[ ][ ]*$' -- '*.md' && exit 1 || :
57
+
58
+ - name: Check for parser.rb is up to date
59
+ run: |
60
+ bundle exec rake build:racc_parser
61
+ git diff --color --no-ext-diff --ignore-submodules --exit-code lib/lrama/parser.rb
48
62
  steep-check:
49
63
  runs-on: ubuntu-20.04
50
64
  strategy:
@@ -52,7 +66,7 @@ jobs:
52
66
  matrix:
53
67
  ruby: ['head']
54
68
  steps:
55
- - uses: actions/checkout@v3
69
+ - uses: actions/checkout@v4
56
70
  - uses: ruby/setup-ruby@v1
57
71
  with:
58
72
  ruby-version: ${{ matrix.ruby }}
@@ -65,13 +79,15 @@ jobs:
65
79
  strategy:
66
80
  fail-fast: false
67
81
  matrix:
68
- baseruby: ['3.0']
82
+ # '3.0' is the oldest living ruby version
83
+ # '2.5' is for BASERUBY
84
+ baseruby: ['head', '3.0', '2.5']
69
85
  ruby_branch: ['master']
70
86
  defaults:
71
87
  run:
72
88
  working-directory: ../ruby/build
73
89
  steps:
74
- - uses: actions/checkout@v3
90
+ - uses: actions/checkout@v4
75
91
  - uses: ruby/setup-ruby@v1
76
92
  with:
77
93
  ruby-version: ${{ matrix.baseruby }}
data/.gitignore CHANGED
@@ -4,3 +4,4 @@
4
4
  /Gemfile.lock
5
5
  /pkg/
6
6
  coverage/
7
+ /parser.output
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gem "rspec"
6
6
  gem "pry"
7
7
  # stackprof doesn't support Windows
8
8
  gem "stackprof", platforms: [:ruby]
9
+ gem "racc"
9
10
  gem "rake"
10
11
  gem "rbs", require: false
11
12
  gem "steep", require: false
data/README.md CHANGED
@@ -61,12 +61,78 @@ This branch generates "parse.c" compatible with Bison 3.8.2 for ruby 3.0, 3.1, 3
61
61
 
62
62
  Lrama is executed with BASERUBY when building ruby from source code. Therefore Lrama needs to support BASERUBY, currently 2.5, or later version.
63
63
 
64
- This also requires Lrama to be able to run with only default gems and bundled gems.
64
+ This also requires Lrama to be able to run with only default gems because BASERUBY runs with `--disable=gems` option.
65
65
 
66
- ## Build Ruby
66
+ ## Development
67
+
68
+ ### How to generate new_parser.rb
69
+
70
+ ```shell
71
+ $ rake build:racc_parser
72
+ ```
73
+
74
+ `new_parser.rb` is generated from `parser.y` by Racc.
75
+ Run the rake command when you update `parser.y` then commit changes of both files.
76
+
77
+ ### Test
78
+
79
+ Running tests:
80
+
81
+ ```shell
82
+ $ bundle install
83
+ $ bundle exec rspec
84
+ ```
85
+
86
+ Running type check:
87
+
88
+ ```shell
89
+ $ bundle install
90
+ $ bundle exec rbs collection install
91
+ $ bundle exec steep check
92
+ ```
93
+
94
+ ### Profiling Lrama
95
+
96
+ #### 1. Create parse.tmp.y in ruby/ruby
97
+
98
+ ```shell
99
+ $ ruby tool/id2token.rb parse.y > parse.tmp.y
100
+ $ cp parse.tmp.y dir/lrama/tmp
101
+ ```
102
+
103
+ #### 2. Enable Profiler
104
+
105
+ ```diff
106
+ diff --git a/exe/lrama b/exe/lrama
107
+ index ba5fb06..2497178 100755
108
+ --- a/exe/lrama
109
+ +++ b/exe/lrama
110
+ @@ -3,4 +3,6 @@
111
+ $LOAD_PATH << File.join(__dir__, "../lib")
112
+ require "lrama"
113
+
114
+ -Lrama::Command.new.run(ARGV.dup)
115
+ +Lrama::Report::Profile.report_profile do
116
+ + Lrama::Command.new.run(ARGV.dup)
117
+ +end
118
+ ```
119
+
120
+ #### 3. Run Lrama
121
+
122
+ ```shell
123
+ $ exe/lrama -o parse.tmp.c --header=parse.tmp.h tmp/parse.tmp.y
124
+ ```
125
+
126
+ #### 4. Generate Flamegraph
127
+
128
+ ```shell
129
+ $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
130
+ ```
131
+
132
+ ### Build Ruby
67
133
 
68
134
  1. Install Lrama
69
- 2. Run `make YACC=lrama`
135
+ 2. Run `make main`
70
136
 
71
137
  ## Release flow
72
138
 
data/Rakefile CHANGED
@@ -1 +1,13 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ namespace "build" do
4
+ desc "build parser from parser.y by using Racc"
5
+ task :racc_parser do
6
+ `bundle exec racc parser.y --embedded -o lib/lrama/parser.rb`
7
+ end
8
+
9
+ desc "build parser for debugging"
10
+ task :racc_verbose_parser do
11
+ `bundle exec racc parser.y --embedded -o lib/lrama/parser.rb -t --log-file=parser.output`
12
+ end
13
+ end
data/Steepfile CHANGED
@@ -1,10 +1,13 @@
1
1
  # D = Steep::Diagnostic
2
2
  #
3
3
  target :lib do
4
+ repo_path '.gem_rbs_collection/'
4
5
  signature "sig"
5
6
 
6
7
  check "lib/lrama/bitmap.rb"
8
+ check "lib/lrama/digraph.rb"
7
9
  check "lib/lrama/report/duration.rb"
8
10
  check "lib/lrama/report/profile.rb"
11
+ check "lib/lrama/token/type.rb"
9
12
  check "lib/lrama/warning.rb"
10
13
  end
data/exe/lrama CHANGED
@@ -3,4 +3,4 @@
3
3
  $LOAD_PATH << File.join(__dir__, "../lib")
4
4
  require "lrama"
5
5
 
6
- Lrama::Command.new(ARGV.dup).run
6
+ Lrama::Command.new.run(ARGV.dup)
data/lib/lrama/command.rb CHANGED
@@ -1,53 +1,35 @@
1
- require 'optparse'
2
-
3
1
  module Lrama
4
2
  class Command
5
- def initialize(argv)
6
- @argv = argv
7
-
8
- @skeleton = "bison/yacc.c"
9
- @header = false
10
- @header_file = nil
11
- @report = []
12
- @report_file = nil
13
- @outfile = "y.tab.c"
14
- @trace = []
15
- @error_recovery = false
16
- @grammar_file = nil
17
- @report_file = nil
18
- @trace_opts = nil
19
- @report_opts = nil
20
- end
3
+ def run(argv)
4
+ options = OptionParser.new.parse(argv)
21
5
 
22
- def run
23
- parse_option
24
-
25
- Report::Duration.enable if @trace_opts[:time]
6
+ Report::Duration.enable if options.trace_opts[:time]
26
7
 
27
8
  warning = Lrama::Warning.new
28
- grammar = Lrama::Parser.new(@y.read).parse
29
- @y.close if @y != STDIN
30
- states = Lrama::States.new(grammar, warning, trace_state: (@trace_opts[:automaton] || @trace_opts[:closure]))
9
+ text = options.y.read
10
+ options.y.close if options.y != STDIN
11
+ grammar = Lrama::Parser.new(text).parse
12
+ states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
31
13
  states.compute
32
14
  context = Lrama::Context.new(states)
33
15
 
34
- if @report_file
16
+ if options.report_file
35
17
  reporter = Lrama::StatesReporter.new(states)
36
- File.open(@report_file, "w+") do |f|
37
- reporter.report(f, **@report_opts)
18
+ File.open(options.report_file, "w+") do |f|
19
+ reporter.report(f, **options.report_opts)
38
20
  end
39
21
  end
40
22
 
41
- File.open(@outfile, "w+") do |f|
23
+ File.open(options.outfile, "w+") do |f|
42
24
  Lrama::Output.new(
43
25
  out: f,
44
- output_file_path: @outfile,
45
- template_name: @skeleton,
46
- grammar_file_path: @grammar_file,
47
- header_file_path: @header_file,
26
+ output_file_path: options.outfile,
27
+ template_name: options.skeleton,
28
+ grammar_file_path: options.grammar_file,
29
+ header_file_path: options.header_file,
48
30
  context: context,
49
31
  grammar: grammar,
50
- error_recovery: @error_recovery,
32
+ error_recovery: options.error_recovery,
51
33
  ).render
52
34
  end
53
35
 
@@ -55,108 +37,5 @@ module Lrama
55
37
  exit 1
56
38
  end
57
39
  end
58
-
59
- private
60
-
61
- def validate_report(report)
62
- bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
63
- others = %w[verbose]
64
- list = bison_list + others
65
- not_supported = %w[cex none]
66
- h = { grammar: true }
67
-
68
- report.each do |r|
69
- if list.include?(r) && !not_supported.include?(r)
70
- h[r.to_sym] = true
71
- else
72
- raise "Invalid report option \"#{r}\"."
73
- end
74
- end
75
-
76
- if h[:all]
77
- (bison_list - not_supported).each do |r|
78
- h[r.to_sym] = true
79
- end
80
-
81
- h.delete(:all)
82
- end
83
-
84
- return h
85
- end
86
-
87
- def validate_trace(trace)
88
- list = %w[
89
- none locations scan parse automaton bitsets
90
- closure grammar resource sets muscles tools
91
- m4-early m4 skeleton time ielr cex all
92
- ]
93
- h = {}
94
-
95
- trace.each do |t|
96
- if list.include?(t)
97
- h[t.to_sym] = true
98
- else
99
- raise "Invalid trace option \"#{t}\"."
100
- end
101
- end
102
-
103
- return h
104
- end
105
-
106
- def parse_option
107
- opt = OptionParser.new
108
-
109
- # opt.on('-h') {|v| p v }
110
- opt.on('-V', '--version') {|v| puts "lrama #{Lrama::VERSION}"; exit 0 }
111
-
112
- # Tuning the Parser
113
- opt.on('-S', '--skeleton=FILE') {|v| @skeleton = v }
114
- opt.on('-t') { } # Do nothing
115
-
116
- # Output Files:
117
- opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
118
- opt.on('-d') { @header = true }
119
- opt.on('-r', '--report=THINGS', Array) {|v| @report = v }
120
- opt.on('--report-file=FILE') {|v| @report_file = v }
121
- opt.on('-v') { } # Do nothing
122
- opt.on('-o', '--output=FILE') {|v| @outfile = v }
123
-
124
- # Hidden
125
- opt.on('--trace=THINGS', Array) {|v| @trace = v }
126
-
127
- # Error Recovery
128
- opt.on('-e') {|v| @error_recovery = true }
129
-
130
- opt.parse!(@argv)
131
-
132
- @trace_opts = validate_trace(@trace)
133
- @report_opts = validate_report(@report)
134
-
135
- @grammar_file = @argv.shift
136
-
137
- if !@grammar_file
138
- abort "File should be specified\n"
139
- end
140
-
141
- if @grammar_file == '-'
142
- @grammar_file = @argv.shift or abort "File name for STDIN should be specified\n"
143
- @y = STDIN
144
- else
145
- @y = File.open(@grammar_file, 'r')
146
- end
147
-
148
- if !@report.empty? && @report_file.nil? && @grammar_file
149
- @report_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".output"
150
- end
151
-
152
- if !@header_file && @header
153
- case
154
- when @outfile
155
- @header_file = File.dirname(@outfile) + "/" + File.basename(@outfile, ".*") + ".h"
156
- when @grammar_file
157
- @header_file = File.dirname(@grammar_file) + "/" + File.basename(@grammar_file, ".*") + ".h"
158
- end
159
- end
160
- end
161
40
  end
162
41
  end
data/lib/lrama/context.rb CHANGED
@@ -170,7 +170,7 @@ module Lrama
170
170
  return a
171
171
  end
172
172
 
173
- # Mapping from rule number to lenght of RHS.
173
+ # Mapping from rule number to length of RHS.
174
174
  # Dummy rule is appended as the first element whose value is 0
175
175
  # because 0 means error in yydefact.
176
176
  def yyr2
@@ -214,7 +214,7 @@ module Lrama
214
214
  (rule_id + 1) * -1
215
215
  end
216
216
 
217
- # Symbol number is assinged to term first then nterm.
217
+ # Symbol number is assigned to term first then nterm.
218
218
  # This method calculates sequence_number for nterm.
219
219
  def nterm_number_to_sequence_number(nterm_number)
220
220
  nterm_number - @states.terms.count
@@ -259,7 +259,7 @@ module Lrama
259
259
  actions[conflict.symbol.number] = ErrorActionNumber
260
260
  end
261
261
 
262
- # If default_reduction_rule, replase default_reduction_rule in
262
+ # If default_reduction_rule, replace default_reduction_rule in
263
263
  # actions with zero.
264
264
  if state.default_reduction_rule
265
265
  actions.map! do |e|
@@ -272,7 +272,7 @@ module Lrama
272
272
  end
273
273
 
274
274
  # If no default_reduction_rule, default behavior is an
275
- # error then replase ErrorActionNumber with zero.
275
+ # error then replace ErrorActionNumber with zero.
276
276
  if !state.default_reduction_rule
277
277
  actions.map! do |e|
278
278
  if e == ErrorActionNumber
data/lib/lrama/digraph.rb CHANGED
@@ -40,8 +40,7 @@ module Lrama
40
40
  end
41
41
 
42
42
  if @h[x] == d
43
- while true do
44
- z = @stack.pop
43
+ while (z = @stack.pop) do
45
44
  @h[z] = Float::INFINITY
46
45
  break if z == x
47
46
  @result[z] = @result[x] # F (Top of S) = F x
@@ -2,8 +2,8 @@ module Lrama
2
2
  class Grammar
3
3
  class Union < Struct.new(:code, :lineno, keyword_init: true)
4
4
  def braces_less_code
5
- # Remove braces
6
- code.s_value[1..-2]
5
+ # Braces is already removed by lexer
6
+ code.s_value
7
7
  end
8
8
  end
9
9
  end
data/lib/lrama/grammar.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "strscan"
2
+
1
3
  require "lrama/grammar/auxiliary"
2
4
  require "lrama/grammar/code"
3
5
  require "lrama/grammar/error_token"
@@ -306,6 +308,188 @@ module Lrama
306
308
  @nterms ||= @symbols.select(&:nterm?)
307
309
  end
308
310
 
311
+ def extract_references
312
+ unless initial_action.nil?
313
+ scanner = StringScanner.new(initial_action.s_value)
314
+ references = []
315
+
316
+ while !scanner.eos? do
317
+ start = scanner.pos
318
+ case
319
+ # $ references
320
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
321
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
322
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
323
+ references << [:dollar, "$", tag, start, scanner.pos - 1]
324
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
325
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
326
+ references << [:dollar, Integer(scanner[2]), tag, start, scanner.pos - 1]
327
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
328
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
329
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
330
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
331
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
332
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
333
+
334
+ # @ references
335
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
336
+ when scanner.scan(/@\$/) # @$
337
+ references << [:at, "$", nil, start, scanner.pos - 1]
338
+ when scanner.scan(/@(\d+)/) # @1
339
+ references << [:at, Integer(scanner[1]), nil, start, scanner.pos - 1]
340
+ when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
341
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
342
+ when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
343
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
344
+ else
345
+ scanner.getch
346
+ end
347
+ end
348
+
349
+ initial_action.token_code.references = references
350
+ build_references(initial_action.token_code)
351
+ end
352
+
353
+ @printers.each do |printer|
354
+ scanner = StringScanner.new(printer.code.s_value)
355
+ references = []
356
+
357
+ while !scanner.eos? do
358
+ start = scanner.pos
359
+ case
360
+ # $ references
361
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
362
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
363
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
364
+ references << [:dollar, "$", tag, start, scanner.pos - 1]
365
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
366
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
367
+ references << [:dollar, Integer(scanner[2]), tag, start, scanner.pos - 1]
368
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
369
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
370
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
371
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
372
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
373
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
374
+
375
+ # @ references
376
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
377
+ when scanner.scan(/@\$/) # @$
378
+ references << [:at, "$", nil, start, scanner.pos - 1]
379
+ when scanner.scan(/@(\d+)/) # @1
380
+ references << [:at, Integer(scanner[1]), nil, start, scanner.pos - 1]
381
+ when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
382
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
383
+ when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
384
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
385
+ else
386
+ scanner.getch
387
+ end
388
+ end
389
+
390
+ printer.code.token_code.references = references
391
+ build_references(printer.code.token_code)
392
+ end
393
+
394
+ @error_tokens.each do |error_token|
395
+ scanner = StringScanner.new(error_token.code.s_value)
396
+ references = []
397
+
398
+ while !scanner.eos? do
399
+ start = scanner.pos
400
+ case
401
+ # $ references
402
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
403
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
404
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
405
+ references << [:dollar, "$", tag, start, scanner.pos - 1]
406
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
407
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
408
+ references << [:dollar, Integer(scanner[2]), tag, start, scanner.pos - 1]
409
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
410
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
411
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
412
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
413
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
414
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
415
+
416
+ # @ references
417
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
418
+ when scanner.scan(/@\$/) # @$
419
+ references << [:at, "$", nil, start, scanner.pos - 1]
420
+ when scanner.scan(/@(\d+)/) # @1
421
+ references << [:at, Integer(scanner[1]), nil, start, scanner.pos - 1]
422
+ when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
423
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
424
+ when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
425
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
426
+ else
427
+ scanner.getch
428
+ end
429
+ end
430
+
431
+ error_token.code.token_code.references = references
432
+ build_references(error_token.code.token_code)
433
+ end
434
+
435
+ @_rules.each do |lhs, rhs, _|
436
+ rhs.each_with_index do |token, index|
437
+ next if token.class == Lrama::Grammar::Symbol || token.type != Lrama::Lexer::Token::User_code
438
+
439
+ scanner = StringScanner.new(token.s_value)
440
+ references = []
441
+
442
+ while !scanner.eos? do
443
+ start = scanner.pos
444
+ case
445
+ # $ references
446
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
447
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\$/) # $$, $<long>$
448
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
449
+ references << [:dollar, "$", tag, start, scanner.pos - 1]
450
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?(\d+)/) # $1, $2, $<long>1
451
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
452
+ references << [:dollar, Integer(scanner[2]), tag, start, scanner.pos - 1]
453
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?([a-zA-Z_][a-zA-Z0-9_]*)/) # $foo, $expr, $<long>program (named reference without brackets)
454
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
455
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
456
+ when scanner.scan(/\$(<[a-zA-Z0-9_]+>)?\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # $expr.right, $expr-right, $<long>program (named reference with brackets)
457
+ tag = scanner[1] ? Lrama::Lexer::Token.new(type: Lrama::Lexer::Token::Tag, s_value: scanner[1]) : nil
458
+ references << [:dollar, scanner[2], tag, start, scanner.pos - 1]
459
+
460
+ # @ references
461
+ # It need to wrap an identifier with brackets to use ".-" for identifiers
462
+ when scanner.scan(/@\$/) # @$
463
+ references << [:at, "$", nil, start, scanner.pos - 1]
464
+ when scanner.scan(/@(\d+)/) # @1
465
+ references << [:at, Integer(scanner[1]), nil, start, scanner.pos - 1]
466
+ when scanner.scan(/@([a-zA-Z][a-zA-Z0-9_]*)/) # @foo, @expr (named reference without brackets)
467
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
468
+ when scanner.scan(/@\[([a-zA-Z_.][-a-zA-Z0-9_.]*)\]/) # @expr.right, @expr-right (named reference with brackets)
469
+ references << [:at, scanner[1], nil, start, scanner.pos - 1]
470
+
471
+ when scanner.scan(/\/\*/)
472
+ scanner.scan_until(/\*\//)
473
+ else
474
+ scanner.getch
475
+ end
476
+ end
477
+
478
+ token.references = references
479
+ token.numberize_references(lhs, rhs)
480
+ build_references(token)
481
+ end
482
+ end
483
+ end
484
+
485
+ def create_token(type, s_value, line, column)
486
+ t = Token.new(type: type, s_value: s_value)
487
+ t.line = line
488
+ t.column = column
489
+
490
+ return t
491
+ end
492
+
309
493
  private
310
494
 
311
495
  def find_nterm_by_id!(id)
@@ -470,7 +654,9 @@ module Lrama
470
654
 
471
655
  # Fill #number and #token_id
472
656
  def fill_symbol_number
473
- # TODO: why start from 256
657
+ # Character literal in grammar file has
658
+ # token id corresponding to ASCII code by default,
659
+ # so start token_id from 256.
474
660
  token_id = 256
475
661
 
476
662
  # YYEMPTY = -2