ebnf 1.1.2 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +218 -196
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/bin/ebnf +40 -21
- data/etc/abnf-core.ebnf +52 -0
- data/etc/abnf.abnf +121 -0
- data/etc/abnf.ebnf +124 -0
- data/etc/abnf.sxp +45 -0
- data/etc/doap.ttl +23 -15
- data/etc/ebnf.ebnf +21 -33
- data/etc/ebnf.html +171 -160
- data/etc/{ebnf.rb → ebnf.ll1.rb} +30 -107
- data/etc/ebnf.ll1.sxp +182 -183
- data/etc/ebnf.peg.rb +90 -0
- data/etc/ebnf.peg.sxp +84 -0
- data/etc/ebnf.sxp +40 -41
- data/etc/iso-ebnf.ebnf +140 -0
- data/etc/iso-ebnf.isoebnf +138 -0
- data/etc/iso-ebnf.sxp +65 -0
- data/etc/sparql.ebnf +4 -4
- data/etc/sparql.html +1603 -1751
- data/etc/sparql.ll1.sxp +7372 -7372
- data/etc/sparql.peg.rb +532 -0
- data/etc/sparql.peg.sxp +597 -0
- data/etc/sparql.sxp +363 -362
- data/etc/turtle.ebnf +3 -3
- data/etc/turtle.html +465 -517
- data/etc/{turtle.rb → turtle.ll1.rb} +3 -4
- data/etc/turtle.ll1.sxp +425 -425
- data/etc/turtle.peg.rb +182 -0
- data/etc/turtle.peg.sxp +199 -0
- data/etc/turtle.sxp +103 -101
- data/lib/ebnf.rb +7 -2
- data/lib/ebnf/abnf.rb +301 -0
- data/lib/ebnf/abnf/core.rb +23 -0
- data/lib/ebnf/abnf/meta.rb +111 -0
- data/lib/ebnf/base.rb +128 -87
- data/lib/ebnf/bnf.rb +1 -26
- data/lib/ebnf/ebnf/meta.rb +90 -0
- data/lib/ebnf/isoebnf.rb +229 -0
- data/lib/ebnf/isoebnf/meta.rb +75 -0
- data/lib/ebnf/ll1.rb +140 -8
- data/lib/ebnf/ll1/lexer.rb +37 -32
- data/lib/ebnf/ll1/parser.rb +113 -73
- data/lib/ebnf/ll1/scanner.rb +83 -51
- data/lib/ebnf/native.rb +320 -0
- data/lib/ebnf/parser.rb +285 -302
- data/lib/ebnf/peg.rb +39 -0
- data/lib/ebnf/peg/parser.rb +561 -0
- data/lib/ebnf/peg/rule.rb +241 -0
- data/lib/ebnf/rule.rb +453 -163
- data/lib/ebnf/terminals.rb +21 -0
- data/lib/ebnf/writer.rb +561 -88
- metadata +114 -28
- data/etc/sparql.rb +0 -45773
data/lib/ebnf/ll1/lexer.rb
CHANGED
@@ -29,7 +29,7 @@ module EBNF::LL1
|
|
29
29
|
# warn error.inspect
|
30
30
|
# end
|
31
31
|
#
|
32
|
-
# @see
|
32
|
+
# @see https://en.wikipedia.org/wiki/Lexical_analysis
|
33
33
|
class Lexer
|
34
34
|
include Enumerable
|
35
35
|
|
@@ -43,10 +43,10 @@ module EBNF::LL1
|
|
43
43
|
"\\'" => '\'', # \u0027 (apostrophe-quote, single quote mark)
|
44
44
|
'\\\\' => '\\' # \u005C (backslash)
|
45
45
|
}.freeze
|
46
|
-
ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})
|
47
|
-
ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})
|
48
|
-
ECHAR = /\\./
|
49
|
-
UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}
|
46
|
+
ESCAPE_CHAR4 = /\\u(?:[0-9A-Fa-f]{4,4})/u.freeze # \uXXXX
|
47
|
+
ESCAPE_CHAR8 = /\\U(?:[0-9A-Fa-f]{8,8})/u.freeze # \UXXXXXXXX
|
48
|
+
ECHAR = /\\./u.freeze # More liberal unescaping
|
49
|
+
UCHAR = /#{ESCAPE_CHAR4}|#{ESCAPE_CHAR8}/n.freeze
|
50
50
|
|
51
51
|
##
|
52
52
|
# @return [Regexp] defines whitespace, including comments, otherwise whitespace must be explicit in terminals
|
@@ -59,7 +59,7 @@ module EBNF::LL1
|
|
59
59
|
#
|
60
60
|
# @param [String] string
|
61
61
|
# @return [String]
|
62
|
-
# @see
|
62
|
+
# @see https://www.w3.org/TR/rdf-sparql-query/#codepointEscape
|
63
63
|
def self.unescape_codepoints(string)
|
64
64
|
string = string.dup
|
65
65
|
string.force_encoding(Encoding::ASCII_8BIT) if string.respond_to?(:force_encoding)
|
@@ -81,7 +81,7 @@ module EBNF::LL1
|
|
81
81
|
#
|
82
82
|
# @param [String] input
|
83
83
|
# @return [String]
|
84
|
-
# @see
|
84
|
+
# @see https://www.w3.org/TR/rdf-sparql-query/#grammarEscapes
|
85
85
|
def self.unescape_string(input)
|
86
86
|
input.gsub(ECHAR) { |escaped| ESCAPE_CHARS[escaped] || escaped[1..-1]}
|
87
87
|
end
|
@@ -98,8 +98,8 @@ module EBNF::LL1
|
|
98
98
|
# @yieldparam [Lexer] lexer
|
99
99
|
# @return [Lexer]
|
100
100
|
# @raise [Lexer::Error] on invalid input
|
101
|
-
def self.tokenize(input, terminals, options
|
102
|
-
lexer = self.new(input, terminals, options)
|
101
|
+
def self.tokenize(input, terminals, **options, &block)
|
102
|
+
lexer = self.new(input, terminals, **options)
|
103
103
|
block_given? ? block.call(lexer) : lexer
|
104
104
|
end
|
105
105
|
|
@@ -115,17 +115,23 @@ module EBNF::LL1
|
|
115
115
|
# Whitespace between tokens, including comments
|
116
116
|
# @option options[Integer] :high_water passed to scanner
|
117
117
|
# @option options[Integer] :low_water passed to scanner
|
118
|
-
def initialize(input = nil, terminals = nil, options
|
118
|
+
def initialize(input = nil, terminals = nil, **options)
|
119
119
|
@options = options.dup
|
120
120
|
@whitespace = @options[:whitespace]
|
121
121
|
@terminals = terminals.map do |term|
|
122
|
-
term.is_a?(Array)
|
122
|
+
if term.is_a?(Array) && term.length ==3
|
123
|
+
# Last element is options
|
124
|
+
Terminal.new(term[0], term[1], **term[2])
|
125
|
+
elsif term.is_a?(Array)
|
126
|
+
Terminal.new(*term)
|
127
|
+
else
|
128
|
+
term
|
129
|
+
end
|
123
130
|
end
|
124
131
|
|
125
132
|
raise Error, "Terminal patterns not defined" unless @terminals && @terminals.length > 0
|
126
133
|
|
127
|
-
@
|
128
|
-
@scanner = Scanner.new(input, options)
|
134
|
+
@scanner = Scanner.new(input, **options)
|
129
135
|
end
|
130
136
|
|
131
137
|
##
|
@@ -140,12 +146,6 @@ module EBNF::LL1
|
|
140
146
|
# @return [String]
|
141
147
|
attr_accessor :input
|
142
148
|
|
143
|
-
##
|
144
|
-
# The current line number (zero-based).
|
145
|
-
#
|
146
|
-
# @return [Integer]
|
147
|
-
attr_reader :lineno
|
148
|
-
|
149
149
|
##
|
150
150
|
# Returns `true` if the input string is lexically valid.
|
151
151
|
#
|
@@ -187,7 +187,7 @@ module EBNF::LL1
|
|
187
187
|
|
188
188
|
@first ||= begin
|
189
189
|
{} while !scanner.eos? && skip_whitespace
|
190
|
-
return
|
190
|
+
return nil if scanner.eos?
|
191
191
|
|
192
192
|
token = match_token(*types)
|
193
193
|
|
@@ -226,7 +226,7 @@ module EBNF::LL1
|
|
226
226
|
# @return [Token]
|
227
227
|
def recover(*types)
|
228
228
|
until scanner.eos? || tok = match_token(*types)
|
229
|
-
if scanner.skip_until(@whitespace || /\s
|
229
|
+
if scanner.skip_until(@whitespace || /\s+/m).nil? # Skip past current "token"
|
230
230
|
# No whitespace at the end, must be and end of string
|
231
231
|
scanner.terminate
|
232
232
|
else
|
@@ -236,6 +236,14 @@ module EBNF::LL1
|
|
236
236
|
scanner.unscan if tok
|
237
237
|
first
|
238
238
|
end
|
239
|
+
|
240
|
+
##
|
241
|
+
# The current line number (one-based).
|
242
|
+
#
|
243
|
+
# @return [Integer]
|
244
|
+
def lineno
|
245
|
+
scanner.lineno
|
246
|
+
end
|
239
247
|
protected
|
240
248
|
|
241
249
|
# @return [StringScanner]
|
@@ -246,9 +254,7 @@ module EBNF::LL1
|
|
246
254
|
def skip_whitespace
|
247
255
|
# skip all white space, but keep track of the current line number
|
248
256
|
while @whitespace && !scanner.eos?
|
249
|
-
|
250
|
-
@lineno += matched.count("\n")
|
251
|
-
else
|
257
|
+
unless scanner.scan(@whitespace)
|
252
258
|
return
|
253
259
|
end
|
254
260
|
end
|
@@ -274,7 +280,6 @@ module EBNF::LL1
|
|
274
280
|
if matched = scanner.scan(term.regexp)
|
275
281
|
#STDERR.puts " matched #{term.type.inspect}: #{matched.inspect}"
|
276
282
|
tok = token(term.type, term.canonicalize(matched))
|
277
|
-
@lineno += matched.count("\n")
|
278
283
|
return tok
|
279
284
|
end
|
280
285
|
end
|
@@ -300,7 +305,7 @@ module EBNF::LL1
|
|
300
305
|
# Cause strings and codepoints to be unescaped.
|
301
306
|
# @option options [Regexp] :partial_regexp
|
302
307
|
# A regular expression matching the beginning of this terminal; useful for terminals that match things longer than the scanner low water mark.
|
303
|
-
def initialize(type, regexp, options
|
308
|
+
def initialize(type, regexp, **options)
|
304
309
|
@type, @regexp, @options = type, regexp, options
|
305
310
|
@partial_regexp = options[:partial_regexp]
|
306
311
|
@map = options.fetch(:map, {})
|
@@ -353,8 +358,8 @@ module EBNF::LL1
|
|
353
358
|
# Scanner instance with access to matched groups
|
354
359
|
# @param [Hash{Symbol => Object}] options
|
355
360
|
# @return [Token]
|
356
|
-
def token(type, value, options
|
357
|
-
Token.new(type, value,
|
361
|
+
def token(type, value, **options)
|
362
|
+
Token.new(type, value, lineno: lineno, **options)
|
358
363
|
end
|
359
364
|
|
360
365
|
##
|
@@ -365,7 +370,7 @@ module EBNF::LL1
|
|
365
370
|
# token.type #=> :LANGTAG
|
366
371
|
# token.value #=> "en"
|
367
372
|
#
|
368
|
-
# @see
|
373
|
+
# @see https://en.wikipedia.org/wiki/Lexical_analysis#Token
|
369
374
|
class Token
|
370
375
|
##
|
371
376
|
# The token's symbol type.
|
@@ -398,7 +403,7 @@ module EBNF::LL1
|
|
398
403
|
# @param [String] value
|
399
404
|
# @param [Hash{Symbol => Object}] options
|
400
405
|
# @option options [Integer] :lineno (nil)
|
401
|
-
def initialize(type, value, options
|
406
|
+
def initialize(type, value, **options)
|
402
407
|
@type = type.to_s.to_sym if type
|
403
408
|
@value = value.to_s
|
404
409
|
@options = options.dup
|
@@ -486,7 +491,7 @@ module EBNF::LL1
|
|
486
491
|
# "invalid token '%' on line 10",
|
487
492
|
# input: query, token: '%', lineno: 9)
|
488
493
|
#
|
489
|
-
# @see
|
494
|
+
# @see https://ruby-doc.org/core/classes/StandardError.html
|
490
495
|
class Error < StandardError
|
491
496
|
##
|
492
497
|
# The input string associated with the error.
|
@@ -514,7 +519,7 @@ module EBNF::LL1
|
|
514
519
|
# @option options [String] :input (nil)
|
515
520
|
# @option options [String] :token (nil)
|
516
521
|
# @option options [Integer] :lineno (nil)
|
517
|
-
def initialize(message, options
|
522
|
+
def initialize(message, **options)
|
518
523
|
@input = options[:input]
|
519
524
|
@token = options[:token]
|
520
525
|
@lineno = options[:lineno]
|
data/lib/ebnf/ll1/parser.rb
CHANGED
@@ -3,12 +3,52 @@ require 'ebnf/ll1/lexer'
|
|
3
3
|
module EBNF::LL1
|
4
4
|
##
|
5
5
|
# A Generic LL1 parser using a lexer and branch tables defined using the SWAP tool chain (modified).
|
6
|
+
#
|
7
|
+
# # Creating terminal definitions and parser rules to parse generated grammars
|
8
|
+
#
|
9
|
+
# The parser is initialized to callbacks invoked on entry and exit
|
10
|
+
# to each `terminal` and `production`. A trivial parser loop can be described as follows:
|
11
|
+
#
|
12
|
+
# require 'ebnf/ll1/parser'
|
13
|
+
# require 'meta'
|
14
|
+
#
|
15
|
+
# class Parser
|
16
|
+
# include Meta
|
17
|
+
# include EBNF::LL1::Parser
|
18
|
+
#
|
19
|
+
# terminal(:SYMBOL, /([a-z]|[A-Z]|[0-9]|_)+/) do |prod, token, input|
|
20
|
+
# # Add data based on scanned token to input
|
21
|
+
# input[:symbol] = token.value
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# start_production(:rule) do |input, current, callback|
|
25
|
+
# # Process on start of production
|
26
|
+
# # Set state for entry into recursed rules through current
|
27
|
+
#
|
28
|
+
# # Callback to parser loop with callback
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# production(:rule) do |input, current, callback|
|
32
|
+
# # Process on end of production
|
33
|
+
# # return results in input, retrieve results from recursed rules in current
|
34
|
+
#
|
35
|
+
# # Callback to parser loop with callback
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# def initialize(input)
|
39
|
+
# parse(input, start_symbol,
|
40
|
+
# branch: BRANCH,
|
41
|
+
# first: FIRST,
|
42
|
+
# follow: FOLLOW,
|
43
|
+
# cleanup: CLEANUP
|
44
|
+
# ) do |context, *data|
|
45
|
+
# # Process calls from callback from productions
|
46
|
+
#
|
47
|
+
# rescue ArgumentError, RDF::LL1::Parser::Error => e
|
48
|
+
# progress("Parsing completed with errors:\n\t#{e.message}")
|
49
|
+
# raise RDF::ReaderError, e.message if validate?
|
50
|
+
# end
|
6
51
|
module Parser
|
7
|
-
##
|
8
|
-
# @private
|
9
|
-
# level above which debug messages are supressed
|
10
|
-
DEBUG_LEVEL = 10
|
11
|
-
|
12
52
|
##
|
13
53
|
# @return [Integer] line number of current token
|
14
54
|
attr_reader :lineno
|
@@ -51,10 +91,10 @@ module EBNF::LL1
|
|
51
91
|
# @yieldparam [Proc] block
|
52
92
|
# Block passed to initialization for yielding to calling parser.
|
53
93
|
# Should conform to the yield specs for #initialize
|
54
|
-
def terminal(term, regexp, options
|
94
|
+
def terminal(term, regexp, **options, &block)
|
55
95
|
@patterns ||= []
|
56
96
|
# Passed in order to define evaulation sequence
|
57
|
-
@patterns << EBNF::LL1::Lexer::Terminal.new(term, regexp, options)
|
97
|
+
@patterns << EBNF::LL1::Lexer::Terminal.new(term, regexp, **options)
|
58
98
|
@terminal_handlers ||= {}
|
59
99
|
@terminal_handlers[term] = block if block_given?
|
60
100
|
end
|
@@ -122,7 +162,14 @@ module EBNF::LL1
|
|
122
162
|
|
123
163
|
def method_missing(method, *args, &block)
|
124
164
|
if @delegate ||= nil
|
125
|
-
|
165
|
+
# special handling when last arg is **options
|
166
|
+
params = @delegate.method(method).parameters
|
167
|
+
if params.any? {|t, _| t == :keyrest} && args.last.is_a?(Hash)
|
168
|
+
opts = args.pop
|
169
|
+
@delegate.send(method, *args, **opts, &block)
|
170
|
+
else
|
171
|
+
@delegate.send(method, *args, &block)
|
172
|
+
end
|
126
173
|
else
|
127
174
|
super
|
128
175
|
end
|
@@ -179,7 +226,7 @@ module EBNF::LL1
|
|
179
226
|
# def each_statement(&block)
|
180
227
|
# @callback = block
|
181
228
|
#
|
182
|
-
# parse(START.to_sym) do |context, *data|
|
229
|
+
# parse(input, START.to_sym) do |context, *data|
|
183
230
|
# case context
|
184
231
|
# when :statement
|
185
232
|
# yield *data
|
@@ -198,16 +245,13 @@ module EBNF::LL1
|
|
198
245
|
# Lists valid terminals that can precede each production (for error recovery).
|
199
246
|
# @option options [Hash{Symbol,String => Array<Symbol,String>}] :follow ({})
|
200
247
|
# Lists valid terminals that can follow each production (for error recovery).
|
201
|
-
# @option options [Boolean] :validate (false)
|
202
|
-
# whether to validate the parsed statements and values. If not validating, the parser will attempt to recover from errors.
|
203
|
-
# @option options [Boolean] :progress
|
204
|
-
# Show progress of parser productions
|
205
|
-
# @option options [Boolean] :debug
|
206
|
-
# Detailed debug output
|
207
|
-
# @option options [Boolean] :reset_on_start
|
208
|
-
# Reset the parser state if the start token set with `prod` is found in a production. This reduces the production stack depth growth, which is appropriate for some grammars.
|
209
248
|
# @option options[Integer] :high_water passed to lexer
|
249
|
+
# @option options [Logger] :logger for errors/progress/debug.
|
210
250
|
# @option options[Integer] :low_water passed to lexer
|
251
|
+
# @option options [Boolean] :reset_on_start
|
252
|
+
# Reset the parser state if the start token set with `prod` is found in a production. This reduces the production stack depth growth, which is appropriate for some grammars.
|
253
|
+
# @option options [Boolean] :validate (false)
|
254
|
+
# whether to validate the parsed statements and values. If not validating, the parser will attempt to recover from errors.
|
211
255
|
# @yield [context, *data]
|
212
256
|
# Yields for to return data to parser
|
213
257
|
# @yieldparam [:statement, :trace] context
|
@@ -218,18 +262,14 @@ module EBNF::LL1
|
|
218
262
|
# @raise [Exception] Raises exceptions for parsing errors
|
219
263
|
# or errors raised during processing callbacks. Internal
|
220
264
|
# errors are raised using {Error}.
|
221
|
-
# @see
|
222
|
-
def parse(input = nil, start = nil, options
|
265
|
+
# @see https://cs.adelaide.edu.au/~charles/lt/Lectures/07-ErrorRecovery.pdf
|
266
|
+
def parse(input = nil, start = nil, **options, &block)
|
223
267
|
@options = options.dup
|
224
|
-
@options[:debug] ||= case
|
225
|
-
when @options[:progress] then 2
|
226
|
-
when @options[:validate] then 1
|
227
|
-
end
|
228
268
|
@branch = options[:branch]
|
229
269
|
@first = options[:first] ||= {}
|
230
270
|
@follow = options[:follow] ||= {}
|
231
271
|
@cleanup = options[:cleanup] ||= {}
|
232
|
-
@lexer = input.is_a?(Lexer) ? input : Lexer.new(input, self.class.patterns,
|
272
|
+
@lexer = input.is_a?(Lexer) ? input : Lexer.new(input, self.class.patterns, **@options)
|
233
273
|
@productions = []
|
234
274
|
@parse_callback = block
|
235
275
|
@recovering = false
|
@@ -349,9 +389,9 @@ module EBNF::LL1
|
|
349
389
|
end
|
350
390
|
|
351
391
|
# Get the list of follows for this sequence, this production and the stacked productions.
|
352
|
-
debug("recovery", "stack follows:"
|
392
|
+
debug("recovery", "stack follows:")
|
353
393
|
todo_stack.reverse.each do |todo|
|
354
|
-
debug("recovery"
|
394
|
+
debug("recovery") {" #{todo[:prod]}: #{@follow[todo[:prod]].inspect}"}
|
355
395
|
end
|
356
396
|
|
357
397
|
# Find all follows to the top of the stack
|
@@ -459,15 +499,16 @@ module EBNF::LL1
|
|
459
499
|
protected
|
460
500
|
|
461
501
|
##
|
462
|
-
# Error information, used as level `
|
502
|
+
# Error information, used as level `3` logger messages.
|
503
|
+
# Messages may be logged and are saved for reporting at end of parsing.
|
463
504
|
#
|
464
505
|
# @param [String] node Relevant location associated with message
|
465
506
|
# @param [String] message Error string
|
466
|
-
# @param [Hash] options
|
507
|
+
# @param [Hash{Symbol => Object}] options
|
467
508
|
# @option options [URI, #to_s] :production
|
468
509
|
# @option options [Token] :token
|
469
|
-
# @see
|
470
|
-
def error(node, message, options
|
510
|
+
# @see #debug
|
511
|
+
def error(node, message, **options)
|
471
512
|
lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
|
472
513
|
m = "ERROR "
|
473
514
|
m += "[line: #{lineno}] " if lineno
|
@@ -476,83 +517,82 @@ module EBNF::LL1
|
|
476
517
|
m += ", production = #{options[:production].inspect}" if options[:production]
|
477
518
|
@error_log << m unless @recovering
|
478
519
|
@recovering = true
|
479
|
-
debug(node, m, options.
|
520
|
+
debug(node, m, level: options.fetch(:level, 3), **options)
|
480
521
|
if options[:raise] || @options[:validate]
|
481
522
|
raise Error.new(m, lineno: lineno, token: options[:token], production: options[:production])
|
482
523
|
end
|
483
524
|
end
|
484
525
|
|
485
526
|
##
|
486
|
-
# Warning information, used as level `
|
527
|
+
# Warning information, used as level `2` logger messages.
|
528
|
+
# Messages may be logged and are saved for reporting at end of parsing.
|
487
529
|
#
|
488
530
|
# @param [String] node Relevant location associated with message
|
489
531
|
# @param [String] message Error string
|
490
532
|
# @param [Hash] options
|
491
533
|
# @option options [URI, #to_s] :production
|
492
534
|
# @option options [Token] :token
|
493
|
-
# @see
|
494
|
-
def warn(node, message, options
|
535
|
+
# @see #debug
|
536
|
+
def warn(node, message, **options)
|
537
|
+
lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
|
495
538
|
m = "WARNING "
|
496
|
-
m += "[line: #{
|
539
|
+
m += "[line: #{lineno}] " if lineno
|
497
540
|
m += message
|
498
541
|
m += " (found #{options[:token].inspect})" if options[:token]
|
499
542
|
m += ", production = #{options[:production].inspect}" if options[:production]
|
500
543
|
@error_log << m unless @recovering
|
501
|
-
debug(node, m,
|
544
|
+
debug(node, m, level: 2, lineno: lineno, **options)
|
502
545
|
end
|
503
546
|
|
504
547
|
##
|
505
|
-
# Progress
|
548
|
+
# Progress logged when parsing. Passed as level `1` logger messages.
|
549
|
+
#
|
550
|
+
# The call is ignored, unless `@options[:logger]` is set.
|
506
551
|
#
|
507
|
-
# @overload progress(node, message, options)
|
552
|
+
# @overload progress(node, message, **options, &block)
|
508
553
|
# @param [String] node Relevant location associated with message
|
509
554
|
# @param [String] message ("")
|
510
555
|
# @param [Hash] options
|
511
556
|
# @option options [Integer] :depth
|
512
557
|
# Recursion depth for indenting output
|
513
|
-
# @see
|
558
|
+
# @see #debug
|
514
559
|
def progress(node, *args, &block)
|
515
|
-
return unless @options[:
|
560
|
+
return unless @options[:logger]
|
561
|
+
lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
|
516
562
|
args << {} unless args.last.is_a?(Hash)
|
517
|
-
args.last[:level] ||=
|
563
|
+
args.last[:level] ||= 1
|
564
|
+
args.last[:lineno] ||= lineno
|
518
565
|
debug(node, *args, &block)
|
519
566
|
end
|
520
567
|
|
521
568
|
##
|
522
|
-
#
|
569
|
+
# Debug logging.
|
523
570
|
#
|
524
|
-
# The call is ignored, unless `@options[:
|
525
|
-
# case it yields tracing information as indicated. Additionally,
|
526
|
-
# if `@options[:debug]` is an Integer, the call is aborted if the
|
527
|
-
# `:level` option is less than than `:level`.
|
571
|
+
# The call is ignored, unless `@options[:logger]` is set.
|
528
572
|
#
|
529
|
-
# @overload debug(node, message, options)
|
573
|
+
# @overload debug(node, message, **options)
|
530
574
|
# @param [Array<String>] args Relevant location associated with message
|
531
575
|
# @param [Hash] options
|
532
576
|
# @option options [Integer] :depth
|
533
577
|
# Recursion depth for indenting output
|
534
|
-
# @
|
535
|
-
|
536
|
-
|
537
|
-
# progress information, and anything higher is for various levels
|
538
|
-
# of debug information.
|
539
|
-
#
|
540
|
-
# @yield trace, level, lineno, depth, args
|
541
|
-
# @yieldparam [:trace] trace
|
542
|
-
# @yieldparam [Integer] level
|
543
|
-
# @yieldparam [Integer] lineno
|
544
|
-
# @yieldparam [Integer] depth Recursive depth of productions
|
545
|
-
# @yieldparam [Array<String>] args
|
546
|
-
# @yieldreturn [String] added to message
|
547
|
-
def debug(*args)
|
548
|
-
return unless @options[:debug] && @parse_callback
|
578
|
+
# @yieldreturn [String] additional string appended to `message`.
|
579
|
+
def debug(*args, &block)
|
580
|
+
return unless @options[:logger]
|
549
581
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
550
|
-
|
551
|
-
|
552
|
-
|
582
|
+
lineno = @lineno || (options[:token].lineno if options[:token].respond_to?(:lineno))
|
583
|
+
level = options.fetch(:level, 0)
|
553
584
|
depth = options[:depth] || self.depth
|
554
|
-
|
555
|
-
|
585
|
+
|
586
|
+
if self.respond_to?(:log_debug)
|
587
|
+
level = [:debug, :info, :warn, :error, :fatal][level]
|
588
|
+
log_debug(*args, **options.merge(level: level, lineno: lineno, depth: depth), &block)
|
589
|
+
elsif @options[:logger].respond_to?(:add)
|
590
|
+
args << yield if block_given?
|
591
|
+
@options[:logger].add(level, "[#{lineno}]" + (" " * depth) + args.join(" "))
|
592
|
+
elsif @options[:logger].respond_to?(:<<)
|
593
|
+
args << yield if block_given?
|
594
|
+
@options[:logger] << "[#{lineno}]" + (" " * depth) + args.join(" ")
|
595
|
+
end
|
556
596
|
end
|
557
597
|
|
558
598
|
private
|
@@ -563,7 +603,7 @@ module EBNF::LL1
|
|
563
603
|
if handler
|
564
604
|
# Create a new production data element, potentially allowing handler
|
565
605
|
# to customize before pushing on the @prod_data stack
|
566
|
-
|
606
|
+
debug("#{prod}(:start):#{@prod_data.length}") {@prod_data.last}
|
567
607
|
data = {}
|
568
608
|
begin
|
569
609
|
self.class.eval_with_binding(self) {
|
@@ -577,12 +617,12 @@ module EBNF::LL1
|
|
577
617
|
elsif [:merge, :star].include?(@cleanup[prod])
|
578
618
|
# Save current data to merge later
|
579
619
|
@prod_data << {}
|
580
|
-
|
620
|
+
debug("#{prod}(:start}:#{@prod_data.length}:cleanup:#{@cleanup[prod]}") { get_token.inspect + (@recovering ? ' recovering' : '')}
|
581
621
|
else
|
582
622
|
# Make sure we push as many was we pop, even if there is no
|
583
623
|
# explicit start handler
|
584
624
|
@prod_data << {} if self.class.production_handlers[prod]
|
585
|
-
|
625
|
+
debug("#{prod}(:start:#{@prod_data.length})") { get_token.inspect + (@recovering ? ' recovering' : '')}
|
586
626
|
end
|
587
627
|
#puts "prod_data(s): " + @prod_data.inspect
|
588
628
|
end
|
@@ -616,7 +656,7 @@ module EBNF::LL1
|
|
616
656
|
else Array(input[k]) + Array(v)
|
617
657
|
end
|
618
658
|
end
|
619
|
-
|
659
|
+
debug("#{prod}(:finish):#{@prod_data.length} cleanup:#{@cleanup[prod]}") {@prod_data.last}
|
620
660
|
else
|
621
661
|
progress("#{prod}(:finish):#{@prod_data.length}") { "recovering" if @recovering }
|
622
662
|
end
|
@@ -723,7 +763,7 @@ module EBNF::LL1
|
|
723
763
|
# "invalid token '%' on line 10",
|
724
764
|
# token: '%', lineno: 9, production: :turtleDoc)
|
725
765
|
#
|
726
|
-
# @see
|
766
|
+
# @see https://ruby-doc.org/core/classes/StandardError.html
|
727
767
|
class Error < StandardError
|
728
768
|
##
|
729
769
|
# The current production.
|
@@ -751,7 +791,7 @@ module EBNF::LL1
|
|
751
791
|
# @option options [Symbol] :production (nil)
|
752
792
|
# @option options [String] :token (nil)
|
753
793
|
# @option options [Integer] :lineno (nil)
|
754
|
-
def initialize(message, options
|
794
|
+
def initialize(message, **options)
|
755
795
|
@production = options[:production]
|
756
796
|
@token = options[:token]
|
757
797
|
@lineno = options[:lineno] || (@token.lineno if @token.respond_to?(:lineno))
|