lrama 0.5.5 → 0.5.7

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.
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