lrama 0.5.12 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +1 -1
  3. data/Gemfile +1 -1
  4. data/NEWS.md +20 -0
  5. data/README.md +14 -3
  6. data/Steepfile +3 -0
  7. data/lib/lrama/grammar/code/printer_code.rb +1 -1
  8. data/lib/lrama/grammar/code/rule_action.rb +1 -1
  9. data/lib/lrama/grammar/code.rb +19 -7
  10. data/lib/lrama/grammar/parameterizing_rule.rb +6 -0
  11. data/lib/lrama/grammar/parameterizing_rule_builder.rb +34 -0
  12. data/lib/lrama/grammar/parameterizing_rule_resolver.rb +30 -0
  13. data/lib/lrama/grammar/parameterizing_rule_rhs_builder.rb +53 -0
  14. data/lib/lrama/grammar/rule_builder.rb +26 -22
  15. data/lib/lrama/grammar.rb +15 -41
  16. data/lib/lrama/lexer/grammar_file.rb +21 -0
  17. data/lib/lrama/lexer/location.rb +77 -2
  18. data/lib/lrama/lexer/token/instantiate_rule.rb +18 -0
  19. data/lib/lrama/lexer/token/user_code.rb +10 -10
  20. data/lib/lrama/lexer/token.rb +1 -1
  21. data/lib/lrama/lexer.rb +21 -11
  22. data/lib/lrama/parser.rb +619 -454
  23. data/lib/lrama/states_reporter.rb +1 -1
  24. data/lib/lrama/version.rb +1 -1
  25. data/parser.y +95 -30
  26. data/sig/lrama/grammar/code/printer_code.rbs +1 -1
  27. data/sig/lrama/grammar/code.rbs +5 -5
  28. data/sig/lrama/grammar/parameterizing_rule.rbs +10 -0
  29. data/sig/lrama/grammar/parameterizing_rule_builder.rbs +19 -0
  30. data/sig/lrama/grammar/parameterizing_rule_resolver.rbs +16 -0
  31. data/sig/lrama/grammar/parameterizing_rule_rhs_builder.rbs +18 -0
  32. data/sig/lrama/grammar/parameterizing_rules/builder/base.rbs +2 -2
  33. data/sig/lrama/grammar/parameterizing_rules/builder.rbs +1 -1
  34. data/sig/lrama/grammar/rule_builder.rbs +2 -4
  35. data/sig/lrama/lexer/grammar_file.rbs +15 -0
  36. data/sig/lrama/lexer/location.rbs +13 -1
  37. data/sig/lrama/lexer/token/{parameterizing.rbs → instantiate_rule.rbs} +2 -7
  38. metadata +15 -5
  39. data/lib/lrama/lexer/token/parameterizing.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '008f1a5df39b115123be84cb5e1edd379d88458bc95256b0e4fe4659481ceb51'
4
- data.tar.gz: 02af30da8cf4cb2cad841f43b603433edafab09b0cf0e8993201c2e9aea12029
3
+ metadata.gz: 7b1a3e3181fc7060235e167ea97a92809b31aa5b74ef5978ab70415252bb87e3
4
+ data.tar.gz: 51a6ee0620321936fcd9d81c44ece3f9e444b977dc8fa940ddf920a5bb3b1dad
5
5
  SHA512:
6
- metadata.gz: 12cf8b98035052e89f9060dd73ec2b32ad9c5bec2fe4076e6ee0416832039962cf973c7ca895b482f2f434500648638c01a717d67357aa055eccce851995adaf
7
- data.tar.gz: a4cffc85bb5945a3d4d037c0eadee0df22e30236085e01f06bffe1646d0acafd09fdb606860752cdea9dbfdf45baa1fc8d244504b7a0ca29c37ab60f9a727367
6
+ metadata.gz: f2b8805ad79cc0c3f125eba2715d274387191e6a355910d11c88f918eae4906b9398444162210c7ce955b1ba3f67cabf3b0679569a65d63bdc52328f2d7dc2f7
7
+ data.tar.gz: 93de8cc0c7f9154f37825d59010a4850cc81b8a66f460d70f95aed44c0eb1224e583d1afb95155be2d9ae063d286a4fa719c133c993c6ec9bfba1c490dfc997b
@@ -13,7 +13,7 @@ jobs:
13
13
  strategy:
14
14
  fail-fast: false
15
15
  matrix:
16
- ruby: ['head', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5']
16
+ ruby: ['head', '3.3', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5']
17
17
  steps:
18
18
  - uses: actions/checkout@v4
19
19
  - uses: ruby/setup-ruby@v1
data/Gemfile CHANGED
@@ -12,6 +12,6 @@ gem "stackprof", platforms: [:ruby] # stackprof doesn't support Windows
12
12
  # Recent steep requires Ruby >= 3.0.0.
13
13
  # Then skip install on some CI jobs.
14
14
  if !ENV['GITHUB_ACTION'] || ENV['INSTALL_STEEP'] == 'true'
15
- gem "rbs", "3.3.0", require: false
15
+ gem "rbs", "3.4.0", require: false
16
16
  gem "steep", "1.6.0", require: false
17
17
  end
data/NEWS.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # NEWS for Lrama
2
2
 
3
+ ## Lrama 0.6.0 (2023-12-25)
4
+
5
+ ### User defined parameterizing rules
6
+
7
+ Allow to define parameterizing rule by `%rule` directive.
8
+
9
+ ```
10
+ %rule pair(X, Y): X Y { $$ = $1 + $2; }
11
+ ;
12
+
13
+ %%
14
+
15
+ program: stmt
16
+ ;
17
+
18
+ stmt: pair(ODD, EVEN) <num>
19
+ | pair(EVEN, ODD) <num>
20
+ ;
21
+ ```
22
+
3
23
  ## Lrama 0.5.11 (2023-12-02)
4
24
 
5
25
  ### Type specification of parameterizing rules
data/README.md CHANGED
@@ -49,9 +49,13 @@ Enter the formula:
49
49
 
50
50
  ## Versions and Branches
51
51
 
52
- ### v0_5 (`master` branch)
52
+ ### v0_6 (`master` branch)
53
53
 
54
- This branch is for Ruby 3.3. `lrama_0_5` branch is created from this branch, once Ruby 3.3 is released.
54
+ This branch is for Ruby 3.4. `lrama_0_6` branch is created from this branch, once Ruby 3.4 is released.
55
+
56
+ ### v0_5 (`lrama_0_5` branch)
57
+
58
+ This branch is for Ruby 3.3.
55
59
 
56
60
  ### v0_4 (`lrama_0_4` branch)
57
61
 
@@ -91,6 +95,13 @@ $ bundle exec rbs collection install
91
95
  $ bundle exec steep check
92
96
  ```
93
97
 
98
+ Running both of them:
99
+
100
+ ```shell
101
+ $ bundle install
102
+ $ bundle exec rake
103
+ ```
104
+
94
105
  ### Profiling Lrama
95
106
 
96
107
  #### 1. Create parse.tmp.y in ruby/ruby
@@ -136,7 +147,7 @@ $ stackprof --d3-flamegraph tmp/stackprof-cpu-myapp.dump > tmp/flamegraph.html
136
147
 
137
148
  ## Release flow
138
149
 
139
- 1. Update `Lrama::VERSION`
150
+ 1. Update `Lrama::VERSION` and NEWS.md
140
151
  2. Release as a gem by `rake release`
141
152
  3. Update Lrama in ruby/ruby by `cp -r LEGAL.md NEWS.md MIT exe lib template ruby/tool/lrama`
142
153
  4. Create new release on [GitHub](https://github.com/ruby/lrama/releases)
data/Steepfile CHANGED
@@ -8,6 +8,9 @@ target :lib do
8
8
  check "lib/lrama/grammar/code.rb"
9
9
  check "lib/lrama/grammar/counter.rb"
10
10
  check "lib/lrama/grammar/error_token.rb"
11
+ check "lib/lrama/grammar/parameterizing_rule_builder.rb"
12
+ check "lib/lrama/grammar/parameterizing_rule_resolver.rb"
13
+ check "lib/lrama/grammar/parameterizing_rule_rhs_builder.rb"
11
14
  check "lib/lrama/grammar/parameterizing_rules"
12
15
  check "lib/lrama/grammar/percent_code.rb"
13
16
  check "lib/lrama/grammar/precedence.rb"
@@ -2,7 +2,7 @@ module Lrama
2
2
  class Grammar
3
3
  class Code
4
4
  class PrinterCode < Code
5
- def initialize(type: nil, token_code: nil, tag: nil)
5
+ def initialize(type:, token_code:, tag:)
6
6
  super(type: type, token_code: token_code)
7
7
  @tag = tag
8
8
  end
@@ -2,7 +2,7 @@ module Lrama
2
2
  class Grammar
3
3
  class Code
4
4
  class RuleAction < Code
5
- def initialize(type: nil, token_code: nil, rule: nil)
5
+ def initialize(type:, token_code:, rule:)
6
6
  super(type: type, token_code: token_code)
7
7
  @rule = rule
8
8
  end
@@ -1,12 +1,29 @@
1
1
  require "forwardable"
2
+ require "lrama/grammar/code/initial_action_code"
3
+ require "lrama/grammar/code/no_reference_code"
4
+ require "lrama/grammar/code/printer_code"
5
+ require "lrama/grammar/code/rule_action"
2
6
 
3
7
  module Lrama
4
8
  class Grammar
5
- class Code < Struct.new(:type, :token_code, keyword_init: true)
9
+ class Code
6
10
  extend Forwardable
7
11
 
8
12
  def_delegators "token_code", :s_value, :line, :column, :references
9
13
 
14
+ attr_reader :type, :token_code
15
+
16
+ def initialize(type:, token_code:)
17
+ @type = type
18
+ @token_code = token_code
19
+ end
20
+
21
+ def ==(other)
22
+ self.class == other.class &&
23
+ self.type == other.type &&
24
+ self.token_code == other.token_code
25
+ end
26
+
10
27
  # $$, $n, @$, @n are translated to C code
11
28
  def translated_code
12
29
  t_code = s_value.dup
@@ -17,7 +34,7 @@ module Lrama
17
34
 
18
35
  str = reference_to_c(ref)
19
36
 
20
- t_code[first_column..last_column] = str
37
+ t_code[first_column...last_column] = str
21
38
  end
22
39
 
23
40
  return t_code
@@ -31,8 +48,3 @@ module Lrama
31
48
  end
32
49
  end
33
50
  end
34
-
35
- require "lrama/grammar/code/initial_action_code"
36
- require "lrama/grammar/code/no_reference_code"
37
- require "lrama/grammar/code/printer_code"
38
- require "lrama/grammar/code/rule_action"
@@ -0,0 +1,6 @@
1
+ module Lrama
2
+ class Grammar
3
+ class ParameterizingRule < Struct.new(:rules, :token, keyword_init: true)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,34 @@
1
+ module Lrama
2
+ class Grammar
3
+ class ParameterizingRuleBuilder
4
+ attr_reader :name, :parameters, :rhs
5
+
6
+ def initialize(name, parameters, rhs)
7
+ @name = name
8
+ @parameters = parameters
9
+ @rhs = rhs
10
+ @required_parameters_count = parameters.count
11
+ end
12
+
13
+ def build_rules(token, actual_args, rule_counter, lhs_tag, line, rule_builders)
14
+ validate_argument_number!(token)
15
+ lhs = lhs(actual_args)
16
+ @rhs.map do |rhs|
17
+ rhs.build_rules(token, actual_args, parameters, rule_counter, lhs, lhs_tag, line, rule_builders)
18
+ end.flatten
19
+ end
20
+
21
+ private
22
+
23
+ def validate_argument_number!(token)
24
+ unless @required_parameters_count == token.args.count
25
+ raise "Invalid number of arguments. expect: #{@required_parameters_count} actual: #{token.args.count}"
26
+ end
27
+ end
28
+
29
+ def lhs(actual_args)
30
+ Lrama::Lexer::Token::Ident.new(s_value: "#{name}_#{actual_args.map(&:s_value).join('_')}")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ module Lrama
2
+ class Grammar
3
+ class ParameterizingRuleResolver
4
+ def initialize
5
+ @parameterizing_rule_builders = []
6
+ end
7
+
8
+ def add_parameterizing_rule_builder(builder)
9
+ @parameterizing_rule_builders << builder
10
+ end
11
+
12
+ def defined?(name)
13
+ !rule_builders(name).empty?
14
+ end
15
+
16
+ def build_rules(token, rule_counter, lhs_tag, line)
17
+ builder = rule_builders(token.s_value).last
18
+ raise "Unknown parameterizing rule #{token.s_value} at line #{token.line}" unless builder
19
+
20
+ builder.build_rules(token, token.args, rule_counter, lhs_tag, line, @parameterizing_rule_builders)
21
+ end
22
+
23
+ private
24
+
25
+ def rule_builders(name)
26
+ @parameterizing_rule_builders.select { |builder| builder.name == name }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ module Lrama
2
+ class Grammar
3
+ class ParameterizingRuleRhsBuilder
4
+ attr_accessor :symbols, :user_code, :precedence_sym
5
+
6
+ def initialize
7
+ @symbols = []
8
+ @user_code = nil
9
+ @precedence_sym = nil
10
+ end
11
+
12
+ def build_rules(token, actual_args, parameters, rule_counter, lhs, lhs_tag, line, rule_builders)
13
+ nested_rules = build_nested_rules(token, actual_args, parameters, rule_counter, lhs_tag, line, rule_builders)
14
+ rule = Rule.new(id: rule_counter.increment, _lhs: lhs, _rhs: rhs(token, actual_args, parameters, nested_rules.last), lhs_tag: lhs_tag, token_code: user_code, precedence_sym: precedence_sym, lineno: line)
15
+ ParameterizingRule.new(rules: nested_rules.map(&:rules) + [rule], token: lhs)
16
+ end
17
+
18
+ private
19
+
20
+ def build_nested_rules(token, actual_args, parameters, rule_counter, lhs_tag, line, rule_builders)
21
+ symbols.each_with_index.map do |sym, i|
22
+ next unless sym.is_a?(Lexer::Token::InstantiateRule)
23
+
24
+ builder = rule_builders.select { |builder| builder.name == sym.s_value }.last
25
+ raise "Unknown parameterizing rule #{token.s_value} at line #{token.line}" unless builder
26
+
27
+ builder.build_rules(sym, nested_actual_args(actual_args, parameters, i), rule_counter, lhs_tag, line, rule_builders)
28
+ end.flatten.compact
29
+ end
30
+
31
+ def nested_actual_args(actual_args, parameters, idx)
32
+ symbols[idx].args.map do |arg|
33
+ i = parameters.index { |parameter| parameter.s_value == arg.s_value }
34
+ i.nil? ? arg : actual_args[i]
35
+ end
36
+ end
37
+
38
+ def rhs(token, actual_args, parameters, nested_rule)
39
+ symbols.map do |sym|
40
+ if sym.is_a?(Lexer::Token::InstantiateRule)
41
+ sym.args.map do |arg|
42
+ idx = parameters.index { |parameter| parameter.s_value == arg.s_value }
43
+ idx.nil? ? sym : nested_rule&.token
44
+ end
45
+ else
46
+ idx = parameters.index { |parameter| parameter.s_value == sym.s_value }
47
+ idx.nil? ? sym : actual_args[idx]
48
+ end
49
+ end.flatten
50
+ end
51
+ end
52
+ end
53
+ end
@@ -51,22 +51,14 @@ module Lrama
51
51
  freeze_rhs
52
52
  end
53
53
 
54
- def setup_rules
54
+ def setup_rules(parameterizing_resolver)
55
55
  preprocess_references unless @skip_preprocess_references
56
- process_rhs
56
+ process_rhs(parameterizing_resolver)
57
57
  build_rules
58
58
  end
59
59
 
60
- def parameterizing_rules
61
- @parameterizing_rules
62
- end
63
-
64
- def midrule_action_rules
65
- @midrule_action_rules
66
- end
67
-
68
60
  def rules
69
- @rules
61
+ @parameterizing_rules + @midrule_action_rules + @rules
70
62
  end
71
63
 
72
64
  private
@@ -95,9 +87,9 @@ module Lrama
95
87
  end
96
88
  end
97
89
 
98
- # rhs is a mixture of variety type of tokens like `Ident`, `Parameterizing`, `UserCode` and so on.
90
+ # rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on.
99
91
  # `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`.
100
- def process_rhs
92
+ def process_rhs(parameterizing_resolver)
101
93
  return if @replaced_rhs
102
94
 
103
95
  @replaced_rhs = []
@@ -109,12 +101,17 @@ module Lrama
109
101
  @replaced_rhs << token
110
102
  when Lrama::Lexer::Token::Ident
111
103
  @replaced_rhs << token
112
- when Lrama::Lexer::Token::Parameterizing
113
- parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
114
- parameterizing.build.each do |r|
115
- @parameterizing_rules << r
104
+ when Lrama::Lexer::Token::InstantiateRule
105
+ if parameterizing_resolver.defined?(token.rule_name)
106
+ parameterizing = parameterizing_resolver.build_rules(token, @rule_counter, @lhs_tag, line)
107
+ @parameterizing_rules = @parameterizing_rules + parameterizing.map(&:rules).flatten
108
+ @replaced_rhs = @replaced_rhs + parameterizing.map(&:token).flatten.uniq
109
+ else
110
+ # TODO: Delete when the standard library will defined as a grammar file.
111
+ parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
112
+ @parameterizing_rules = @parameterizing_rules + parameterizing.build
113
+ @replaced_rhs << parameterizing.build_token
116
114
  end
117
- @replaced_rhs << parameterizing.build_token
118
115
  when Lrama::Lexer::Token::UserCode
119
116
  prefix = token.referred ? "@" : "$@"
120
117
  new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
@@ -124,7 +121,7 @@ module Lrama
124
121
  rule_builder.lhs = new_token
125
122
  rule_builder.user_code = token
126
123
  rule_builder.complete_input
127
- rule_builder.setup_rules
124
+ rule_builder.setup_rules(parameterizing_resolver)
128
125
 
129
126
  @rule_builders_for_derived_rules << rule_builder
130
127
  else
@@ -146,8 +143,15 @@ module Lrama
146
143
  else
147
144
  candidates = rhs.each_with_index.select {|token, i| token.referred_by?(ref_name) }
148
145
 
149
- raise "Referring symbol `#{ref_name}` is duplicated. #{token}" if candidates.size >= 2
150
- raise "Referring symbol `#{ref_name}` is not found. #{token}" unless referring_symbol = candidates.first
146
+ if candidates.size >= 2
147
+ location = token.location.partial_location(ref.first_column, ref.last_column)
148
+ raise location.generate_error_message("Referring symbol `#{ref_name}` is duplicated.")
149
+ end
150
+
151
+ unless (referring_symbol = candidates.first)
152
+ location = token.location.partial_location(ref.first_column, ref.last_column)
153
+ raise location.generate_error_message("Referring symbol `#{ref_name}` is not found.")
154
+ end
151
155
 
152
156
  ref.index = referring_symbol[1] + 1
153
157
  end
@@ -167,7 +171,7 @@ module Lrama
167
171
  end
168
172
 
169
173
  def flush_user_code
170
- if c = @user_code
174
+ if (c = @user_code)
171
175
  @rhs << c
172
176
  @user_code = nil
173
177
  end
data/lib/lrama/grammar.rb CHANGED
@@ -8,6 +8,10 @@ require "lrama/grammar/printer"
8
8
  require "lrama/grammar/reference"
9
9
  require "lrama/grammar/rule"
10
10
  require "lrama/grammar/rule_builder"
11
+ require "lrama/grammar/parameterizing_rule_builder"
12
+ require "lrama/grammar/parameterizing_rule_resolver"
13
+ require "lrama/grammar/parameterizing_rule_rhs_builder"
14
+ require "lrama/grammar/parameterizing_rule"
11
15
  require "lrama/grammar/symbol"
12
16
  require "lrama/grammar/type"
13
17
  require "lrama/grammar/union"
@@ -36,6 +40,7 @@ module Lrama
36
40
  @rule_builders = []
37
41
  @rules = []
38
42
  @sym_to_rules = {}
43
+ @parameterizing_resolver = ParameterizingRuleResolver.new
39
44
  @empty_symbol = nil
40
45
  @eof_symbol = nil
41
46
  @error_symbol = nil
@@ -69,7 +74,7 @@ module Lrama
69
74
  return sym
70
75
  end
71
76
 
72
- if sym = @symbols.find {|s| s.id == id }
77
+ if (sym = @symbols.find {|s| s.id == id })
73
78
  return sym
74
79
  end
75
80
 
@@ -129,6 +134,10 @@ module Lrama
129
134
  @rule_builders << builder
130
135
  end
131
136
 
137
+ def add_parameterizing_rule_builder(builder)
138
+ @parameterizing_resolver.add_parameterizing_rule_builder(builder)
139
+ end
140
+
132
141
  def prologue_first_lineno=(prologue_first_lineno)
133
142
  @aux.prologue_first_lineno = prologue_first_lineno
134
143
  end
@@ -310,7 +319,7 @@ module Lrama
310
319
 
311
320
  def setup_rules
312
321
  @rule_builders.each do |builder|
313
- builder.setup_rules
322
+ builder.setup_rules(@parameterizing_resolver)
314
323
  end
315
324
  end
316
325
 
@@ -350,56 +359,21 @@ module Lrama
350
359
  @accept_symbol = term
351
360
  end
352
361
 
353
- # 1. Add $accept rule to the top of rules
354
- # 2. Extract action in the middle of RHS into new Empty rule
355
- # 3. Append id and extract action then create Rule
356
- #
357
- # Bison 3.8.2 uses different orders for symbol number and rule number
358
- # when a rule has actions in the middle of a rule.
359
- #
360
- # For example,
361
- #
362
- # `program: $@1 top_compstmt`
363
- #
364
- # Rules are ordered like below,
365
- #
366
- # 1 $@1: ε
367
- # 2 program: $@1 top_compstmt
368
- #
369
- # Symbols are ordered like below,
370
- #
371
- # 164 program
372
- # 165 $@1
373
- #
374
362
  def normalize_rules
375
- # 1. Add $accept rule to the top of rules
376
- accept = @accept_symbol
377
- eof = @eof_symbol
363
+ # Add $accept rule to the top of rules
378
364
  lineno = @rule_builders.first ? @rule_builders.first.line : 0
379
- @rules << Rule.new(id: @rule_counter.increment, _lhs: accept.id, _rhs: [@rule_builders.first.lhs, eof.id], token_code: nil, lineno: lineno)
365
+ @rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [@rule_builders.first.lhs, @eof_symbol.id], token_code: nil, lineno: lineno)
380
366
 
381
367
  setup_rules
382
368
 
383
369
  @rule_builders.each do |builder|
384
- # Extract actions in the middle of RHS into new rules.
385
- builder.midrule_action_rules.each do |rule|
386
- @rules << rule
387
- end
388
-
389
370
  builder.rules.each do |rule|
390
- add_nterm(id: rule._lhs)
391
- @rules << rule
392
- end
393
-
394
- builder.parameterizing_rules.each do |rule|
395
371
  add_nterm(id: rule._lhs, tag: rule.lhs_tag)
396
372
  @rules << rule
397
373
  end
398
-
399
- builder.midrule_action_rules.each do |rule|
400
- add_nterm(id: rule._lhs)
401
- end
402
374
  end
375
+
376
+ @rules.sort_by!(&:id)
403
377
  end
404
378
 
405
379
  # Collect symbols from rules
@@ -0,0 +1,21 @@
1
+ module Lrama
2
+ class Lexer
3
+ class GrammarFile
4
+ attr_reader :path, :text
5
+
6
+ def initialize(path, text)
7
+ @path = path
8
+ @text = text
9
+ end
10
+
11
+ def ==(other)
12
+ self.class == other.class &&
13
+ self.path == other.path
14
+ end
15
+
16
+ def lines
17
+ @lines ||= text.split("\n")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,9 +1,10 @@
1
1
  module Lrama
2
2
  class Lexer
3
3
  class Location
4
- attr_reader :first_line, :first_column, :last_line, :last_column
4
+ attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column
5
5
 
6
- def initialize(first_line:, first_column:, last_line:, last_column:)
6
+ def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
7
+ @grammar_file = grammar_file
7
8
  @first_line = first_line
8
9
  @first_column = first_column
9
10
  @last_line = last_line
@@ -12,11 +13,85 @@ module Lrama
12
13
 
13
14
  def ==(other)
14
15
  self.class == other.class &&
16
+ self.grammar_file == other.grammar_file &&
15
17
  self.first_line == other.first_line &&
16
18
  self.first_column == other.first_column &&
17
19
  self.last_line == other.last_line &&
18
20
  self.last_column == other.last_column
19
21
  end
22
+
23
+ def partial_location(left, right)
24
+ offset = -first_column
25
+ new_first_line = -1
26
+ new_first_column = -1
27
+ new_last_line = -1
28
+ new_last_column = -1
29
+
30
+ _text.each.with_index do |line, index|
31
+ new_offset = offset + line.length + 1
32
+
33
+ if offset <= left && left <= new_offset
34
+ new_first_line = first_line + index
35
+ new_first_column = left - offset
36
+ end
37
+
38
+ if offset <= right && right <= new_offset
39
+ new_last_line = first_line + index
40
+ new_last_column = right - offset
41
+ end
42
+
43
+ offset = new_offset
44
+ end
45
+
46
+ Location.new(
47
+ grammar_file: grammar_file,
48
+ first_line: new_first_line, first_column: new_first_column,
49
+ last_line: new_last_line, last_column: new_last_column
50
+ )
51
+ end
52
+
53
+ def to_s
54
+ "#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
55
+ end
56
+
57
+ def generate_error_message(error_message)
58
+ <<~ERROR.chomp
59
+ #{path}:#{first_line}:#{first_column}: #{error_message}
60
+ #{line_with_carets}
61
+ ERROR
62
+ end
63
+
64
+ def line_with_carets
65
+ <<~TEXT
66
+ #{text}
67
+ #{carets}
68
+ TEXT
69
+ end
70
+
71
+ private
72
+
73
+ def path
74
+ grammar_file.path
75
+ end
76
+
77
+ def blanks
78
+ (text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
79
+ end
80
+
81
+ def carets
82
+ blanks + '^' * (last_column - first_column)
83
+ end
84
+
85
+ def text
86
+ @text ||= _text.join("\n")
87
+ end
88
+
89
+ def _text
90
+ @_text ||=begin
91
+ range = (first_line - 1)...last_line
92
+ grammar_file.lines[range] or raise "#{range} is invalid"
93
+ end
94
+ end
20
95
  end
21
96
  end
22
97
  end
@@ -0,0 +1,18 @@
1
+ module Lrama
2
+ class Lexer
3
+ class Token
4
+ class InstantiateRule < Token
5
+ attr_accessor :args
6
+
7
+ def initialize(s_value:, alias_name: nil, location: nil, args: [])
8
+ super s_value: s_value, alias_name: alias_name, location: location
9
+ @args = args
10
+ end
11
+
12
+ def rule_name
13
+ s_value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end