lrama 0.6.11 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gh-pages.yml +46 -0
  3. data/.github/workflows/test.yaml +35 -7
  4. data/.gitignore +1 -0
  5. data/.rdoc_options +2 -0
  6. data/Gemfile +4 -2
  7. data/NEWS.md +60 -0
  8. data/README.md +44 -15
  9. data/Rakefile +13 -1
  10. data/Steepfile +2 -0
  11. data/doc/Index.md +58 -0
  12. data/lib/lrama/bitmap.rb +3 -0
  13. data/lib/lrama/command.rb +2 -1
  14. data/lib/lrama/digraph.rb +30 -0
  15. data/lib/lrama/grammar/binding.rb +47 -15
  16. data/lib/lrama/grammar/rule.rb +8 -0
  17. data/lib/lrama/grammar/rule_builder.rb +3 -15
  18. data/lib/lrama/grammar.rb +8 -3
  19. data/lib/lrama/lexer/grammar_file.rb +8 -1
  20. data/lib/lrama/lexer/location.rb +17 -1
  21. data/lib/lrama/lexer/token/char.rb +1 -0
  22. data/lib/lrama/lexer/token/ident.rb +1 -0
  23. data/lib/lrama/lexer/token/instantiate_rule.rb +6 -1
  24. data/lib/lrama/lexer/token/tag.rb +3 -1
  25. data/lib/lrama/lexer/token/user_code.rb +5 -1
  26. data/lib/lrama/lexer/token.rb +14 -2
  27. data/lib/lrama/lexer.rb +4 -5
  28. data/lib/lrama/logger.rb +4 -0
  29. data/lib/lrama/option_parser.rb +10 -8
  30. data/lib/lrama/options.rb +2 -1
  31. data/lib/lrama/parser.rb +10 -4
  32. data/lib/lrama/state.rb +288 -1
  33. data/lib/lrama/states/item.rb +8 -0
  34. data/lib/lrama/states.rb +69 -2
  35. data/lib/lrama/trace_reporter.rb +17 -2
  36. data/lib/lrama/version.rb +1 -1
  37. data/lrama.gemspec +1 -1
  38. data/parser.y +4 -3
  39. data/rbs_collection.lock.yaml +3 -3
  40. data/sig/generated/lrama/bitmap.rbs +11 -0
  41. data/sig/generated/lrama/digraph.rbs +39 -0
  42. data/sig/generated/lrama/grammar/binding.rbs +34 -0
  43. data/sig/generated/lrama/lexer/grammar_file.rbs +28 -0
  44. data/sig/generated/lrama/lexer/location.rbs +52 -0
  45. data/sig/{lrama → generated/lrama}/lexer/token/char.rbs +2 -0
  46. data/sig/{lrama → generated/lrama}/lexer/token/ident.rbs +2 -0
  47. data/sig/{lrama → generated/lrama}/lexer/token/instantiate_rule.rbs +8 -0
  48. data/sig/{lrama → generated/lrama}/lexer/token/tag.rbs +3 -0
  49. data/sig/{lrama → generated/lrama}/lexer/token/user_code.rbs +6 -1
  50. data/sig/{lrama → generated/lrama}/lexer/token.rbs +26 -3
  51. data/sig/generated/lrama/logger.rbs +14 -0
  52. data/sig/generated/lrama/trace_reporter.rbs +25 -0
  53. data/sig/lrama/grammar/rule_builder.rbs +0 -1
  54. data/sig/lrama/options.rbs +1 -0
  55. metadata +19 -14
  56. data/sig/lrama/bitmap.rbs +0 -7
  57. data/sig/lrama/digraph.rbs +0 -23
  58. data/sig/lrama/grammar/binding.rbs +0 -19
  59. data/sig/lrama/lexer/grammar_file.rbs +0 -17
  60. data/sig/lrama/lexer/location.rbs +0 -26
data/lib/lrama/grammar.rb CHANGED
@@ -28,14 +28,14 @@ module Lrama
28
28
  attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux, :parameterizing_rule_resolver
29
29
  attr_accessor :union, :expect, :printers, :error_tokens, :lex_param, :parse_param, :initial_action,
30
30
  :after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack,
31
- :symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations
31
+ :symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations, :define
32
32
 
33
33
  def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_term_by_s_value,
34
34
  :find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol,
35
35
  :find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type,
36
36
  :fill_printer, :fill_destructor, :fill_error_token, :sort_by_number!
37
37
 
38
- def initialize(rule_counter)
38
+ def initialize(rule_counter, define = {})
39
39
  @rule_counter = rule_counter
40
40
 
41
41
  # Code defined by "%code"
@@ -57,6 +57,7 @@ module Lrama
57
57
  @aux = Auxiliary.new
58
58
  @no_stdlib = false
59
59
  @locations = false
60
+ @define = define.map {|d| d.split('=') }.to_h
60
61
 
61
62
  append_special_symbols
62
63
  end
@@ -171,6 +172,10 @@ module Lrama
171
172
  @sym_to_rules[sym.number]
172
173
  end
173
174
 
175
+ def ielr_defined?
176
+ @define.key?('lr.type') && @define['lr.type'] == 'ielr'
177
+ end
178
+
174
179
  private
175
180
 
176
181
  def compute_nullable
@@ -294,7 +299,7 @@ module Lrama
294
299
  end
295
300
 
296
301
  def resolve_inline_rules
297
- while @rule_builders.any? {|r| r.has_inline_rules? } do
302
+ while @rule_builders.any?(&:has_inline_rules?) do
298
303
  @rule_builders = @rule_builders.flat_map do |builder|
299
304
  if builder.has_inline_rules?
300
305
  builder.resolve_inline_rules
@@ -1,30 +1,37 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Lexer
5
6
  class GrammarFile
6
7
  class Text < String
8
+ # @rbs () -> String
7
9
  def inspect
8
10
  length <= 50 ? super : "#{self[0..47]}...".inspect
9
11
  end
10
12
  end
11
13
 
12
- attr_reader :path, :text
14
+ attr_reader :path #: String
15
+ attr_reader :text #: String
13
16
 
17
+ # @rbs (String path, String text) -> void
14
18
  def initialize(path, text)
15
19
  @path = path
16
20
  @text = Text.new(text).freeze
17
21
  end
18
22
 
23
+ # @rbs () -> String
19
24
  def inspect
20
25
  "<#{self.class}: @path=#{path}, @text=#{text.inspect}>"
21
26
  end
22
27
 
28
+ # @rbs (GrammarFile other) -> bool
23
29
  def ==(other)
24
30
  self.class == other.class &&
25
31
  self.path == other.path
26
32
  end
27
33
 
34
+ # @rbs () -> Array[String]
28
35
  def lines
29
36
  @lines ||= text.split("\n")
30
37
  end
@@ -1,10 +1,16 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Lexer
5
6
  class Location
6
- attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column
7
+ attr_reader :grammar_file #: GrammarFile
8
+ attr_reader :first_line #: Integer
9
+ attr_reader :first_column #: Integer
10
+ attr_reader :last_line #: Integer
11
+ attr_reader :last_column #: Integer
7
12
 
13
+ # @rbs (grammar_file: GrammarFile, first_line: Integer, first_column: Integer, last_line: Integer, last_column: Integer) -> void
8
14
  def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
9
15
  @grammar_file = grammar_file
10
16
  @first_line = first_line
@@ -13,6 +19,7 @@ module Lrama
13
19
  @last_column = last_column
14
20
  end
15
21
 
22
+ # @rbs (Location other) -> bool
16
23
  def ==(other)
17
24
  self.class == other.class &&
18
25
  self.grammar_file == other.grammar_file &&
@@ -22,6 +29,7 @@ module Lrama
22
29
  self.last_column == other.last_column
23
30
  end
24
31
 
32
+ # @rbs (Integer left, Integer right) -> Location
25
33
  def partial_location(left, right)
26
34
  offset = -first_column
27
35
  new_first_line = -1
@@ -52,10 +60,12 @@ module Lrama
52
60
  )
53
61
  end
54
62
 
63
+ # @rbs () -> String
55
64
  def to_s
56
65
  "#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
57
66
  end
58
67
 
68
+ # @rbs (String error_message) -> String
59
69
  def generate_error_message(error_message)
60
70
  <<~ERROR.chomp
61
71
  #{path}:#{first_line}:#{first_column}: #{error_message}
@@ -63,6 +73,7 @@ module Lrama
63
73
  ERROR
64
74
  end
65
75
 
76
+ # @rbs () -> String
66
77
  def line_with_carets
67
78
  <<~TEXT
68
79
  #{text}
@@ -72,22 +83,27 @@ module Lrama
72
83
 
73
84
  private
74
85
 
86
+ # @rbs () -> String
75
87
  def path
76
88
  grammar_file.path
77
89
  end
78
90
 
91
+ # @rbs () -> String
79
92
  def blanks
80
93
  (text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
81
94
  end
82
95
 
96
+ # @rbs () -> String
83
97
  def carets
84
98
  blanks + '^' * (last_column - first_column)
85
99
  end
86
100
 
101
+ # @rbs () -> String
87
102
  def text
88
103
  @text ||= _text.join("\n")
89
104
  end
90
105
 
106
+ # @rbs () -> Array[String]
91
107
  def _text
92
108
  @_text ||=begin
93
109
  range = (first_line - 1)...last_line
@@ -1,3 +1,4 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
@@ -1,3 +1,4 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
@@ -1,21 +1,26 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Lexer
5
6
  class Token
6
7
  class InstantiateRule < Token
7
- attr_reader :args, :lhs_tag
8
+ attr_reader :args #: Array[Lexer::Token]
9
+ attr_reader :lhs_tag #: Lexer::Token::Tag?
8
10
 
11
+ # @rbs (s_value: String, ?alias_name: String, ?location: Location, ?args: Array[Lexer::Token], ?lhs_tag: Lexer::Token::Tag?) -> void
9
12
  def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil)
10
13
  super s_value: s_value, alias_name: alias_name, location: location
11
14
  @args = args
12
15
  @lhs_tag = lhs_tag
13
16
  end
14
17
 
18
+ # @rbs () -> String
15
19
  def rule_name
16
20
  s_value
17
21
  end
18
22
 
23
+ # @rbs () -> Integer
19
24
  def args_count
20
25
  args.count
21
26
  end
@@ -1,11 +1,13 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Lexer
5
6
  class Token
6
7
  class Tag < Token
7
- # Omit "<>"
8
+ # @rbs () -> String
8
9
  def member
10
+ # Omit "<>"
9
11
  s_value[1..-2] or raise "Unexpected Tag format (#{s_value})"
10
12
  end
11
13
  end
@@ -1,3 +1,4 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require "strscan"
@@ -6,14 +7,16 @@ module Lrama
6
7
  class Lexer
7
8
  class Token
8
9
  class UserCode < Token
9
- attr_accessor :tag
10
+ attr_accessor :tag #: Lexer::Token::Tag
10
11
 
12
+ # @rbs () -> Array[Lrama::Grammar::Reference]
11
13
  def references
12
14
  @references ||= _references
13
15
  end
14
16
 
15
17
  private
16
18
 
19
+ # @rbs () -> Array[Lrama::Grammar::Reference]
17
20
  def _references
18
21
  scanner = StringScanner.new(s_value)
19
22
  references = [] #: Array[Grammar::Reference]
@@ -32,6 +35,7 @@ module Lrama
32
35
  references
33
36
  end
34
37
 
38
+ # @rbs (StringScanner scanner) -> Lrama::Grammar::Reference?
35
39
  def scan_reference(scanner)
36
40
  start = scanner.pos
37
41
  case
@@ -1,3 +1,4 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require_relative 'token/char'
@@ -9,9 +10,12 @@ require_relative 'token/user_code'
9
10
  module Lrama
10
11
  class Lexer
11
12
  class Token
12
- attr_reader :s_value, :location
13
- attr_accessor :alias_name, :referred
13
+ attr_reader :s_value #: String
14
+ attr_reader :location #: Location
15
+ attr_accessor :alias_name #: String
16
+ attr_accessor :referred #: bool
14
17
 
18
+ # @rbs (s_value: String, ?alias_name: String, ?location: Location) -> void
15
19
  def initialize(s_value:, alias_name: nil, location: nil)
16
20
  s_value.freeze
17
21
  @s_value = s_value
@@ -19,36 +23,44 @@ module Lrama
19
23
  @location = location
20
24
  end
21
25
 
26
+ # @rbs () -> String
22
27
  def to_s
23
28
  "value: `#{s_value}`, location: #{location}"
24
29
  end
25
30
 
31
+ # @rbs (String string) -> bool
26
32
  def referred_by?(string)
27
33
  [self.s_value, self.alias_name].compact.include?(string)
28
34
  end
29
35
 
36
+ # @rbs (Token other) -> bool
30
37
  def ==(other)
31
38
  self.class == other.class && self.s_value == other.s_value
32
39
  end
33
40
 
41
+ # @rbs () -> Integer
34
42
  def first_line
35
43
  location.first_line
36
44
  end
37
45
  alias :line :first_line
38
46
 
47
+ # @rbs () -> Integer
39
48
  def first_column
40
49
  location.first_column
41
50
  end
42
51
  alias :column :first_column
43
52
 
53
+ # @rbs () -> Integer
44
54
  def last_line
45
55
  location.last_line
46
56
  end
47
57
 
58
+ # @rbs () -> Integer
48
59
  def last_column
49
60
  location.last_column
50
61
  end
51
62
 
63
+ # @rbs (Lrama::Grammar::Reference ref, String message) -> bot
52
64
  def invalid_ref(ref, message)
53
65
  location = self.location.partial_location(ref.first_column, ref.last_column)
54
66
  raise location.generate_error_message(message)
data/lib/lrama/lexer.rb CHANGED
@@ -169,12 +169,11 @@ module Lrama
169
169
  def lex_comment
170
170
  until @scanner.eos? do
171
171
  case
172
- when @scanner.scan(/\n/)
173
- newline
174
- when @scanner.scan(/\*\//)
172
+ when @scanner.scan_until(/[\s\S]*?\*\//)
173
+ @scanner.matched.count("\n").times { newline }
175
174
  return
176
- else
177
- @scanner.getch
175
+ when @scanner.scan_until(/\n/)
176
+ newline
178
177
  end
179
178
  end
180
179
  end
data/lib/lrama/logger.rb CHANGED
@@ -1,15 +1,19 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class Logger
6
+ # @rbs (IO out) -> void
5
7
  def initialize(out = STDERR)
6
8
  @out = out
7
9
  end
8
10
 
11
+ # @rbs (String message) -> void
9
12
  def warn(message)
10
13
  @out << message << "\n"
11
14
  end
12
15
 
16
+ # @rbs (String message) -> void
13
17
  def error(message)
14
18
  @out << message << "\n"
15
19
  end
@@ -59,8 +59,8 @@ module Lrama
59
59
  o.separator ''
60
60
  o.separator 'Tuning the Parser:'
61
61
  o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v }
62
- o.on('-t', 'reserved, do nothing') { }
63
- o.on('--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true }
62
+ o.on('-t', '--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true }
63
+ o.on('-D', '--define=NAME[=VALUE]', Array, "similar to '%define NAME VALUE'") {|v| @options.define = v }
64
64
  o.separator ''
65
65
  o.separator 'Output:'
66
66
  o.on('-H', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v }
@@ -86,6 +86,7 @@ module Lrama
86
86
  o.on_tail ' automaton display states'
87
87
  o.on_tail ' closure display states'
88
88
  o.on_tail ' rules display grammar rules'
89
+ o.on_tail ' only-explicit-rules display only explicit grammar rules'
89
90
  o.on_tail ' actions display grammar rules with actions'
90
91
  o.on_tail ' time display generation time'
91
92
  o.on_tail ' all include all the above traces'
@@ -136,26 +137,27 @@ module Lrama
136
137
 
137
138
  VALID_TRACES = %w[
138
139
  locations scan parse automaton bitsets closure
139
- grammar rules actions resource sets muscles
140
- tools m4-early m4 skeleton time ielr cex
140
+ grammar rules only-explicit-rules actions resource
141
+ sets muscles tools m4-early m4 skeleton time ielr cex
141
142
  ].freeze
142
143
  NOT_SUPPORTED_TRACES = %w[
143
144
  locations scan parse bitsets grammar resource
144
145
  sets muscles tools m4-early m4 skeleton ielr cex
145
146
  ].freeze
147
+ SUPPORTED_TRACES = VALID_TRACES - NOT_SUPPORTED_TRACES
146
148
 
147
149
  def validate_trace(trace)
148
150
  h = {}
149
151
  return h if trace.empty? || trace == ['none']
150
- supported = VALID_TRACES - NOT_SUPPORTED_TRACES
152
+ all_traces = SUPPORTED_TRACES - %w[only-explicit-rules]
151
153
  if trace == ['all']
152
- supported.each { |t| h[t.to_sym] = true }
154
+ all_traces.each { |t| h[t.gsub(/-/, '_').to_sym] = true }
153
155
  return h
154
156
  end
155
157
 
156
158
  trace.each do |t|
157
- if supported.include?(t)
158
- h[t.to_sym] = true
159
+ if SUPPORTED_TRACES.include?(t)
160
+ h[t.gsub(/-/, '_').to_sym] = true
159
161
  else
160
162
  raise "Invalid trace option \"#{t}\"."
161
163
  end
data/lib/lrama/options.rb CHANGED
@@ -7,10 +7,11 @@ module Lrama
7
7
  :report_file, :outfile,
8
8
  :error_recovery, :grammar_file,
9
9
  :trace_opts, :report_opts,
10
- :diagnostic, :y, :debug
10
+ :diagnostic, :y, :debug, :define
11
11
 
12
12
  def initialize
13
13
  @skeleton = "bison/yacc.c"
14
+ @define = {}
14
15
  @header = false
15
16
  @header_file = nil
16
17
  @report_file = nil
data/lib/lrama/parser.rb CHANGED
@@ -658,17 +658,18 @@ module_eval(<<'...end parser.y/module_eval...', 'parser.y', 428)
658
658
 
659
659
  include Lrama::Report::Duration
660
660
 
661
- def initialize(text, path, debug = false)
661
+ def initialize(text, path, debug = false, define = {})
662
662
  @grammar_file = Lrama::Lexer::GrammarFile.new(path, text)
663
663
  @yydebug = debug
664
664
  @rule_counter = Lrama::Grammar::Counter.new(0)
665
665
  @midrule_action_counter = Lrama::Grammar::Counter.new(1)
666
+ @define = define
666
667
  end
667
668
 
668
669
  def parse
669
670
  report_duration(:parse) do
670
671
  @lexer = Lrama::Lexer.new(@grammar_file)
671
- @grammar = Lrama::Grammar.new(@rule_counter)
672
+ @grammar = Lrama::Grammar.new(@rule_counter, @define)
672
673
  @precedence_number = 0
673
674
  reset_precs
674
675
  do_parse
@@ -914,7 +915,7 @@ racc_reduce_table = [
914
915
  2, 73, :_reduce_15,
915
916
  1, 60, :_reduce_none,
916
917
  2, 60, :_reduce_17,
917
- 3, 60, :_reduce_none,
918
+ 3, 60, :_reduce_18,
918
919
  2, 60, :_reduce_none,
919
920
  2, 60, :_reduce_20,
920
921
  2, 60, :_reduce_21,
@@ -1328,7 +1329,12 @@ module_eval(<<'.,.,', 'parser.y', 26)
1328
1329
  end
1329
1330
  .,.,
1330
1331
 
1331
- # reduce 18 omitted
1332
+ module_eval(<<'.,.,', 'parser.y', 27)
1333
+ def _reduce_18(val, _values, result)
1334
+ @grammar.define[val[1].s_value] = val[2]&.s_value
1335
+ result
1336
+ end
1337
+ .,.,
1332
1338
 
1333
1339
  # reduce 19 omitted
1334
1340