tdparser 1.5.1 → 1.6.0

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: 25d5372580072f4703947c061df7b6d2cb5f85c3c073ef130747f211671a04e4
4
- data.tar.gz: 94d335eb747dfe643cc5e488c15b22cc0bd3936bf2a76ed2bbec9008e30e2c6f
3
+ metadata.gz: 9df17ece1521def452aa92e124da446abd512000073b36a989e53fb0f5032574
4
+ data.tar.gz: 1f1766675086fcba1870127d05419eaa5aa6de6f436347fad23f7b5d37afc8c6
5
5
  SHA512:
6
- metadata.gz: 48e4f42bb49c592021fd4fb4455a5120a8087f639ab9f21cf2986d90a2d0f87b1f108bd6315197934769f738064828e372b10ea1dba2495266486c58a5f56dff
7
- data.tar.gz: 34cf78b68bf980d9814f1b02ba6bfbe3b9a8a9167bb6b84d8d0a598b060f496d9805a5eca42314fb3c66fcd70b0ceb0cbfd409273f445ffcf3750e99a8c88e71
6
+ metadata.gz: 3821c0d37e95f49d3db3fe8230ba9e662cb9bb648497698dac95c34c0507396d78c02720943ab1cd4028f457c930e90f37a93024b68a2bf06664498ef7290357
7
+ data.tar.gz: c8ac3e9fca74e15a1a888f376ff9058fdb797f6944876a3f77f224dd4de4cf20f7ed88891b115d7138c624cb91a07bed0144f1cc8d0ee1a96b97a1275ed02295
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## 1.6.0 - 2024-11-17
4
+
5
+ * Add more readable method aliases.
6
+ * Make token buffer's state accessible attribute.
7
+ * Split files such as parsers. You might need to require individual files.
8
+ * Change token buffer class not to inherit from array class.
9
+ * Move XML token generator class to upper namespace.
10
+
3
11
  ## 1.5.1 - 2024-11-10
4
12
 
5
13
  This is a maintenance release, no user facing changes.
data/README CHANGED
@@ -23,6 +23,11 @@ the feature of (2), we need not consider how to prevent conflicts
23
23
  among production rules. In addition, TDParser can be viewed as an
24
24
  internal DSL for writing LL(k) grammars because of (3).
25
25
 
26
+ Repository is located at:
27
+
28
+ * Disroot[https://git.disroot.org/gemmaro/tdparser]
29
+ * GitHub[https://github.com/gemmaro/tdparser] (for GitHub Pages)
30
+
26
31
  == License
27
32
 
28
33
  Copyright(C) 2003, 2004, 2005, 2006 Takaaki Tateishi <ttate@ttsky.net>
data/Rakefile CHANGED
@@ -16,3 +16,7 @@ Rake::TestTask.new do |t|
16
16
  t.libs << 'samples' << 'test'
17
17
  t.test_files = FileList['test/*_test.rb']
18
18
  end
19
+
20
+ task :gensig do
21
+ sh 'typeprof', '-r', 'forwardable', '-r', 'strscan', '-r', 'enumerable', '-o', 'sig/tdparser.gen.rbs', 'sig/tdparser.rbs', *Dir['lib/**/*.rb']
22
+ end
@@ -0,0 +1,29 @@
1
+ module TDParser
2
+ class ActionParser < CompositeParser # :nodoc:
3
+ attr_reader :action
4
+
5
+ def initialize(parser, act)
6
+ @action = act
7
+ super(parser)
8
+ end
9
+
10
+ def call(tokens, buff)
11
+ if (x = @parsers[0].call(tokens, buff)).nil?
12
+ nil
13
+ else
14
+ x = TokenBuffer[*x]
15
+ x.map = buff.map
16
+ Sequence[@action[x]]
17
+ end
18
+ end
19
+
20
+ def ==(other)
21
+ super(other) &&
22
+ (@action == other.action)
23
+ end
24
+
25
+ def to_s
26
+ "(#{@parsers[0]} <action>)"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ module TDParser
2
+ class AnyParser < Parser # :nodoc:
3
+ def call(tokens, _buff)
4
+ t = tokens.shift
5
+ if t.nil?
6
+ nil
7
+ else
8
+ Sequence[t]
9
+ end
10
+ end
11
+
12
+ def to_s
13
+ '<any>'
14
+ end
15
+
16
+ def ==(_other)
17
+ true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ module TDParser
2
+ class BackrefParser < ReferenceParser # :nodoc:
3
+ attr_reader :label, :equality
4
+
5
+ def initialize(label, eqsym)
6
+ @label = label
7
+ @equality = eqsym
8
+ end
9
+
10
+ def call(tokens, buff)
11
+ ys = buff.map[@label]
12
+ if ys.nil? || ys.empty?
13
+ nil
14
+ else
15
+ back_ref(ys.dup, @equality).call(tokens, buff)
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ "<backref:#{@label}>"
21
+ end
22
+
23
+ def ==(other)
24
+ super(other) &&
25
+ (@label == other.label) &&
26
+ (@equality == other.equality)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ module TDParser
2
+ module BufferUtils # :nodoc:
3
+ def prepare(buff)
4
+ b = TokenBuffer.new
5
+ b.map = buff.map
6
+ b
7
+ end
8
+
9
+ def recover(buff, ts)
10
+ buff.each { |b| ts.unshift(b) }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,80 @@
1
+ module TDParser
2
+ class ChoiceParser < CompositeParser # :nodoc:
3
+ def call(tokens, buff)
4
+ b = prepare(buff)
5
+ if (x = @parsers[0].call(tokens, b)).nil?
6
+ recover(b, tokens)
7
+ @parsers[1].call(tokens, buff)
8
+ else
9
+ buff.insert(0, *b)
10
+ x
11
+ end
12
+ end
13
+
14
+ def to_s
15
+ "(#{@parsers[0]} | #{@parsers[1]})"
16
+ end
17
+
18
+ def shared_sequence(r1, r2)
19
+ if r1.is_a?(ConcatParser) && r2.is_a?(ConcatParser)
20
+ r11 = r1.parsers[0]
21
+ r12 = r1.parsers[1]
22
+ r21 = r2.parsers[0]
23
+ r22 = r2.parsers[1]
24
+ if r11.same?(r21)
25
+ share, r12, r22, = shared_sequence(r12, r22)
26
+ return [r11 - share, r12, r22] if share
27
+
28
+ return [r11, r12, r22]
29
+
30
+ end
31
+ end
32
+ [nil, r1, r2]
33
+ end
34
+
35
+ def optimize(default = false)
36
+ r1 = @parsers[0]
37
+ r2 = @parsers[1]
38
+ if r1.is_a?(ActionParser)
39
+ act1 = r1.action
40
+ r1 = r1.parsers[0]
41
+ end
42
+ if r2.is_a?(ActionParser)
43
+ act2 = r2.action
44
+ r2 = r2.parsers[0]
45
+ end
46
+ share, r12, r22, = shared_sequence(r1, r2)
47
+ if share
48
+ r = share - (r12 + r22)
49
+ if act1
50
+ r = if act2
51
+ r >> proc do |x|
52
+ y0, y1, *_ = x.pop
53
+ if y0
54
+ act1.call(x.push(*y0))
55
+ else
56
+ act2.call(x.push(*y1))
57
+ end
58
+ end
59
+ else
60
+ r >> proc do |x|
61
+ y0, = x.pop
62
+ act1.call(x.push(*y0)) if y0
63
+ end
64
+ end
65
+ elsif act2
66
+ r = r >> proc do |x|
67
+ _, y1, = x.pop
68
+ act2.call(x.push(*y1)) if y1
69
+ end
70
+ end
71
+ return r
72
+ end
73
+ if default
74
+ dup
75
+ else
76
+ super(default)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,29 @@
1
+ module TDParser
2
+ class CompositeParser < Parser # :nodoc:
3
+ attr_accessor :parsers
4
+
5
+ def initialize(*parsers)
6
+ @parsers = parsers
7
+ end
8
+
9
+ def optimize(default = false)
10
+ parser = dup
11
+ parser.parsers = @parsers.collect { |x| x.optimize(default) }
12
+ parser
13
+ end
14
+
15
+ def ==(other)
16
+ (self.class == other.class) &&
17
+ (@parsers == other.parsers)
18
+ end
19
+
20
+ def same?(r)
21
+ super(r) &&
22
+ @parsers.zip(r.parsers).all? { |x, y| x.same?(y) }
23
+ end
24
+
25
+ def to_s
26
+ "<composite: #{@parsers.collect(&:to_s)}>"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module TDParser
2
+ class ConcatParser < CompositeParser # :nodoc:
3
+ def call(tokens, buff)
4
+ if (x = @parsers[0].call(tokens, buff)).nil?
5
+ nil
6
+ elsif (y = @parsers[1].call(tokens, buff)).nil?
7
+ nil
8
+ else
9
+ x + y
10
+ end
11
+ end
12
+
13
+ def -(other)
14
+ @parsers[0] - (@parsers[1] - other)
15
+ end
16
+
17
+ def to_s
18
+ "(#{@parsers[0]} #{@parsers[1]})"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module TDParser
2
+ class ConditionParser < Parser # :nodoc:
3
+ attr_reader :condition
4
+
5
+ def initialize(&condition)
6
+ @condition = condition
7
+ end
8
+
9
+ def call(_tokens, buff)
10
+ return unless (res = @condition.call(buff.map))
11
+
12
+ Sequence[res]
13
+ end
14
+
15
+ def to_s
16
+ "<condition:#{@condition}>"
17
+ end
18
+
19
+ def ==(other)
20
+ super(other) &&
21
+ (@condition == other.condition)
22
+ end
23
+
24
+ def same?(_r)
25
+ false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ module TDParser
2
+ class EmptyParser < Parser # :nodoc:
3
+ def call(_tokens, _buff)
4
+ Sequence[nil]
5
+ end
6
+
7
+ def to_s
8
+ '<empty>'
9
+ end
10
+
11
+ def ==(_other)
12
+ true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module TDParser
2
+ class FailParser < Parser # :nodoc:
3
+ def call(_tokens, _buff)
4
+ nil
5
+ end
6
+
7
+ def to_s
8
+ '<fail>'
9
+ end
10
+
11
+ def ==
12
+ (self.class == r.class)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module TDParser
2
+ class Grammar # :nodoc:
3
+ include TDParser
4
+
5
+ alias define instance_eval
6
+
7
+ def method_missing(sym, *args)
8
+ if sym[-1, 1] == '='
9
+ parser, = args
10
+ name = sym[0..-2]
11
+ parser.is_a?(Parser) or parser = token(parser)
12
+ self.class.instance_eval do
13
+ instance_methods.include?(name.intern) or
14
+ define_method(name) { parser }
15
+ end
16
+ elsif args.empty?
17
+ rule(sym)
18
+ else
19
+ raise(NoMethodError, "undefined method `#{sym}' for #{inspect}")
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,73 @@
1
+ module TDParser
2
+ class IterationParser < CompositeParser # :nodoc:
3
+ attr_reader :min, :range
4
+
5
+ def initialize(parser, n, range)
6
+ @min = n
7
+ @range = range
8
+ super(parser)
9
+ end
10
+
11
+ def call(ts, buff)
12
+ r = @parsers[0]
13
+ n = @min
14
+ x = true
15
+ xs = []
16
+ while n.positive?
17
+ n -= 1
18
+ b = prepare(buff)
19
+ if (x = r.call(ts, b)).nil?
20
+ recover(b, ts)
21
+ break
22
+ else
23
+ buff.insert(0, *b)
24
+ xs.push(x)
25
+ end
26
+ end
27
+ if x.nil?
28
+ nil
29
+ else
30
+ if range
31
+ range.each do
32
+ loop do
33
+ y = x
34
+ b = prepare(buff)
35
+ if (x = r.call(ts, b)).nil?
36
+ recover(b, ts)
37
+ x = y
38
+ break
39
+ else
40
+ buff.insert(0, *b)
41
+ xs.push(x)
42
+ end
43
+ end
44
+ end
45
+ else
46
+ loop do
47
+ y = x
48
+ b = prepare(buff)
49
+ if (x = r.call(ts, b)).nil?
50
+ recover(b, ts)
51
+ x = y
52
+ break
53
+ else
54
+ buff.insert(0, *b)
55
+ xs.push(x)
56
+ end
57
+ end
58
+ end
59
+ Sequence[xs]
60
+ end
61
+ end
62
+
63
+ def to_s
64
+ "(#{@parsers[0]})*#{@range ? @range.to_s : @min.to_s}"
65
+ end
66
+
67
+ def ==(other)
68
+ super(other) &&
69
+ (@min == other.min) &&
70
+ (@range == other.range)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ module TDParser
2
+ class LabelParser < CompositeParser # :nodoc:
3
+ attr_reader :label
4
+
5
+ def initialize(parser, label)
6
+ @label = label
7
+ super(parser)
8
+ end
9
+
10
+ def call(tokens, buff)
11
+ x = @parsers[0].call(tokens, buff)
12
+ buff.map[@label] = x
13
+ x
14
+ end
15
+
16
+ def ==(other)
17
+ super(other) &&
18
+ (@label == other.label)
19
+ end
20
+
21
+ def to_s
22
+ "(#{@parsers[0]}/#{@label})"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module TDParser
2
+ class NegativeParser < CompositeParser # :nodoc:
3
+ def call(tokens, buff)
4
+ b = prepare(buff)
5
+ r = @parsers[0].call(tokens, b)
6
+ rev = b.reverse
7
+ recover(b, tokens)
8
+ return unless r.nil?
9
+
10
+ Sequence[Sequence[*rev]]
11
+ end
12
+
13
+ def to_s
14
+ "~#{@parsers[0]}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module TDParser
2
+ class NonTerminalParser < Parser # :nodoc:
3
+ attr_reader :context, :symbol, :options
4
+
5
+ def initialize(context, sym, *options)
6
+ @context = context
7
+ @symbol = sym
8
+ @options = options
9
+ end
10
+
11
+ def call(tokens, buff)
12
+ res = nil
13
+ case @symbol
14
+ when Symbol, String
15
+ res = @context.__send__(@symbol, *@options).call(tokens, buff)
16
+ when Parser
17
+ res = @symbol.call(tokens, buff)
18
+ end
19
+ res
20
+ end
21
+
22
+ def ==(other)
23
+ (self.class == other.class) &&
24
+ (@context == other.context) &&
25
+ (@symbol == other.symbol) &&
26
+ (@options == other.options)
27
+ end
28
+
29
+ def to_s
30
+ @symbol.to_s
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module TDParser
2
+ class NoneParser < Parser # :nodoc:
3
+ def call(tokens, _buff)
4
+ t = tokens.shift
5
+ return unless t.nil?
6
+
7
+ Sequence[nil]
8
+ end
9
+
10
+ def to_s
11
+ '<none>'
12
+ end
13
+
14
+ def ==(_other)
15
+ true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module TDParser
2
+ class ParallelParser < CompositeParser # :nodoc:
3
+ def call(tokens, buff)
4
+ b = prepare(buff)
5
+ if (x = @parsers[0].call(tokens, b)).nil?
6
+ recover(b, tokens)
7
+ Sequence[Sequence[nil, @parsers[1].call(tokens, buff)]]
8
+ else
9
+ buff.insert(0, *b)
10
+ Sequence[Sequence[x, nil]]
11
+ end
12
+ end
13
+
14
+ def to_s
15
+ "(#{@parsers[0]} + #{@parsers[1]})"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,104 @@
1
+ module TDParser
2
+ class Parser
3
+ include BufferUtils
4
+ include TDParser
5
+
6
+ def to_proc
7
+ proc { |*x| call(*x) }
8
+ end
9
+
10
+ def to_s
11
+ '??'
12
+ end
13
+
14
+ def call(*args); end
15
+
16
+ def optimize(_default = false)
17
+ dup
18
+ end
19
+
20
+ def ==(_other)
21
+ false
22
+ end
23
+
24
+ def same?(r)
25
+ self == r
26
+ end
27
+
28
+ def -(other)
29
+ ConcatParser.new(self, other)
30
+ end
31
+
32
+ def +(other)
33
+ ParallelParser.new(self, other)
34
+ end
35
+
36
+ def |(other)
37
+ ChoiceParser.new(self, other).optimize(true)
38
+ end
39
+
40
+ def *(other)
41
+ if other.is_a?(Range)
42
+ n = other.min
43
+ else
44
+ n = other
45
+ other = nil
46
+ end
47
+ IterationParser.new(self, n, other)
48
+ end
49
+
50
+ def >>(other)
51
+ ActionParser.new(self, other)
52
+ end
53
+
54
+ def /(other)
55
+ LabelParser.new(self, other)
56
+ end
57
+
58
+ def %(other)
59
+ StackParser.new(self, other)
60
+ end
61
+
62
+ def >(other)
63
+ Parser.new do |tokens, buff|
64
+ buff[other] = buff.dup
65
+ self[tokens, buff]
66
+ end
67
+ end
68
+
69
+ def ~@
70
+ NegativeParser.new(self)
71
+ end
72
+
73
+ def parse(tokens = nil, buff = nil, &blk)
74
+ buff ||= TokenBuffer.new
75
+ @tokens = if blk.nil?
76
+ if tokens.respond_to?(:shift) && tokens.respond_to?(:unshift)
77
+ tokens
78
+ elsif tokens.respond_to?(:each)
79
+ TokenGenerator.new(tokens)
80
+ else
81
+ tokens
82
+ end
83
+ else
84
+ TokenGenerator.new(&blk)
85
+ end
86
+ r = call(@tokens, buff)
87
+ if r.nil?
88
+ nil
89
+ else
90
+ r[0]
91
+ end
92
+ end
93
+
94
+ def peek
95
+ t = @tokens.shift
96
+ @tokens.unshift(t) unless t.nil?
97
+ t
98
+ end
99
+
100
+ def do(&block)
101
+ self >> block
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,23 @@
1
+ module TDParser
2
+ class ReferenceParser < Parser # :nodoc:
3
+ private
4
+
5
+ def back_ref(xs, eqsym)
6
+ x = xs.shift
7
+ xs.inject(token(x, eqsym)) do |acc, x|
8
+ case x
9
+ when Sequence
10
+ acc - back_ref(x, eqsym)
11
+ else
12
+ acc - token(x, eqsym)
13
+ end
14
+ end
15
+ end
16
+
17
+ alias __backref__ back_ref
18
+
19
+ def same?(_r)
20
+ false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module TDParser
2
+ class Sequence < Array # :nodoc:
3
+ def +(other)
4
+ dup.concat(other)
5
+ end
6
+ end
7
+ end