whittle 0.0.5 → 0.0.6
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.
- data/lib/whittle/non_terminal.rb +12 -0
- data/lib/whittle/parser.rb +2 -2
- data/lib/whittle/rule.rb +5 -6
- data/lib/whittle/rule_set.rb +10 -5
- data/lib/whittle/terminal.rb +12 -0
- data/lib/whittle/version.rb +1 -1
- data/lib/whittle.rb +2 -0
- data/spec/unit/parser/non_terminal_ambiguity_spec.rb +19 -0
- metadata +7 -3
@@ -0,0 +1,12 @@
|
|
1
|
+
# Whittle: A little LALR(1) parser in pure ruby, without a generator.
|
2
|
+
#
|
3
|
+
# Copyright (c) Chris Corbyn, 2011
|
4
|
+
|
5
|
+
module Whittle
|
6
|
+
# Represents an nonterminal Rule, which refers to other Rules.
|
7
|
+
class NonTerminal < Rule
|
8
|
+
def terminal?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/whittle/parser.rb
CHANGED
@@ -76,7 +76,7 @@ module Whittle
|
|
76
76
|
raise ArgumentError,
|
77
77
|
"Parser#rule does not accept both a Hash and a block" if name.kind_of?(Hash)
|
78
78
|
|
79
|
-
rules[name] = RuleSet.new(name)
|
79
|
+
rules[name] = RuleSet.new(name, false)
|
80
80
|
rules[name].tap { |r| yield r }
|
81
81
|
else
|
82
82
|
key, value = if name.kind_of?(Hash)
|
@@ -88,7 +88,7 @@ module Whittle
|
|
88
88
|
[name, name]
|
89
89
|
end
|
90
90
|
|
91
|
-
rules[key] = RuleSet.new(key)
|
91
|
+
rules[key] = RuleSet.new(key, true)
|
92
92
|
rules[key][value].as(:value)
|
93
93
|
end
|
94
94
|
end
|
data/lib/whittle/rule.rb
CHANGED
@@ -28,7 +28,6 @@ module Whittle
|
|
28
28
|
@components = components
|
29
29
|
@action = DUMP_ACTION
|
30
30
|
@name = name
|
31
|
-
@terminal = components.length == 1 && !components.first.kind_of?(Symbol)
|
32
31
|
@assoc = :right
|
33
32
|
@prec = 0
|
34
33
|
|
@@ -40,7 +39,7 @@ module Whittle
|
|
40
39
|
|
41
40
|
pattern = @components.first
|
42
41
|
|
43
|
-
if
|
42
|
+
if terminal?
|
44
43
|
@pattern = if pattern.kind_of?(Regexp)
|
45
44
|
Regexp.new("\\G#{pattern}")
|
46
45
|
else
|
@@ -57,7 +56,7 @@ module Whittle
|
|
57
56
|
# @return [Boolean]
|
58
57
|
# true if this rule represents a terminal symbol
|
59
58
|
def terminal?
|
60
|
-
|
59
|
+
raise "Must be implemented by subclass"
|
61
60
|
end
|
62
61
|
|
63
62
|
# Walks all possible branches from the given rule, building a parse table.
|
@@ -83,7 +82,7 @@ module Whittle
|
|
83
82
|
end || [self, new_offset].hash
|
84
83
|
|
85
84
|
if sym.nil?
|
86
|
-
|
85
|
+
assert_reducible!(state, sym)
|
87
86
|
|
88
87
|
state[sym] = {
|
89
88
|
:action => :reduce,
|
@@ -231,7 +230,7 @@ module Whittle
|
|
231
230
|
#
|
232
231
|
# Returns nil if nothing is matched.
|
233
232
|
def scan(source, offset, line)
|
234
|
-
return nil unless
|
233
|
+
return nil unless terminal?
|
235
234
|
|
236
235
|
if match = source.match(@pattern, offset)
|
237
236
|
{
|
@@ -255,7 +254,7 @@ module Whittle
|
|
255
254
|
end
|
256
255
|
end
|
257
256
|
|
258
|
-
def
|
257
|
+
def assert_reducible!(instructions, sym)
|
259
258
|
if instructions.key?(sym) && !instructions[sym][:rule].equal?(self)
|
260
259
|
message = <<-END.gsub(/(^|$)\s+/m, " ")
|
261
260
|
Unresolvable conflict found between rules
|
data/lib/whittle/rule_set.rb
CHANGED
@@ -15,9 +15,13 @@ module Whittle
|
|
15
15
|
#
|
16
16
|
# @param [Symbol, String] name
|
17
17
|
# the name of the rule in the grammar
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
#
|
19
|
+
# @param [Boolean] terminal
|
20
|
+
# the class the use to add new rules
|
21
|
+
def initialize(name, terminal = false)
|
22
|
+
@name = name
|
23
|
+
@rules = []
|
24
|
+
@terminal = terminal
|
21
25
|
end
|
22
26
|
|
23
27
|
# Enumerate all Rules in the set.
|
@@ -30,7 +34,8 @@ module Whittle
|
|
30
34
|
# @param [Object...] components...
|
31
35
|
# a variable list of components (Symbols, Strings, or Regexps)
|
32
36
|
def [](*components)
|
33
|
-
|
37
|
+
klass = terminal? ? Terminal : NonTerminal
|
38
|
+
klass.new(@name, *components).tap do |rule|
|
34
39
|
@rules << rule
|
35
40
|
end
|
36
41
|
end
|
@@ -88,7 +93,7 @@ module Whittle
|
|
88
93
|
# @return [Boolean]
|
89
94
|
# true if this rule is a terminal symbol
|
90
95
|
def terminal?
|
91
|
-
@
|
96
|
+
@terminal
|
92
97
|
end
|
93
98
|
|
94
99
|
# Predicate test for whether or not this RuleSet references a nonterminal Symbol.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Whittle: A little LALR(1) parser in pure ruby, without a generator.
|
2
|
+
#
|
3
|
+
# Copyright (c) Chris Corbyn, 2011
|
4
|
+
|
5
|
+
module Whittle
|
6
|
+
# Represents an terminal Rule, matching a pattern in the input String
|
7
|
+
class Terminal < Rule
|
8
|
+
def terminal?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/whittle/version.rb
CHANGED
data/lib/whittle.rb
CHANGED
@@ -8,5 +8,7 @@ require "whittle/errors/unconsumed_input_error"
|
|
8
8
|
require "whittle/errors/parse_error"
|
9
9
|
require "whittle/errors/grammar_error"
|
10
10
|
require "whittle/rule"
|
11
|
+
require "whittle/terminal"
|
12
|
+
require "whittle/non_terminal"
|
11
13
|
require "whittle/rule_set"
|
12
14
|
require "whittle/parser"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "a parser with a nonterminal rule looking like a terminal" do
|
4
|
+
let(:parser) do
|
5
|
+
Class.new(Whittle::Parser) do
|
6
|
+
rule("+")
|
7
|
+
|
8
|
+
rule(:prog) do |r|
|
9
|
+
r["+"]
|
10
|
+
end
|
11
|
+
|
12
|
+
start(:prog)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "recognises the correct terminal" do
|
17
|
+
parser.new.parse("+").should == "+"
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whittle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2011-12-02 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70361567853620 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '2.6'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70361567853620
|
25
25
|
description: ! "Write powerful parsers by defining a series of very simple rules\n
|
26
26
|
\ and operations to perform as those rules are matched. Whittle\n
|
27
27
|
\ parsers are written in pure ruby and as such are extremely
|
@@ -46,9 +46,11 @@ files:
|
|
46
46
|
- lib/whittle/errors/grammar_error.rb
|
47
47
|
- lib/whittle/errors/parse_error.rb
|
48
48
|
- lib/whittle/errors/unconsumed_input_error.rb
|
49
|
+
- lib/whittle/non_terminal.rb
|
49
50
|
- lib/whittle/parser.rb
|
50
51
|
- lib/whittle/rule.rb
|
51
52
|
- lib/whittle/rule_set.rb
|
53
|
+
- lib/whittle/terminal.rb
|
52
54
|
- lib/whittle/version.rb
|
53
55
|
- spec/spec_helper.rb
|
54
56
|
- spec/unit/parser/empty_rule_spec.rb
|
@@ -56,6 +58,7 @@ files:
|
|
56
58
|
- spec/unit/parser/error_reporting_spec.rb
|
57
59
|
- spec/unit/parser/grouped_expr_spec.rb
|
58
60
|
- spec/unit/parser/multiple_precedence_spec.rb
|
61
|
+
- spec/unit/parser/non_terminal_ambiguity_spec.rb
|
59
62
|
- spec/unit/parser/noop_spec.rb
|
60
63
|
- spec/unit/parser/pass_through_parser_spec.rb
|
61
64
|
- spec/unit/parser/precedence_spec.rb
|
@@ -98,6 +101,7 @@ test_files:
|
|
98
101
|
- spec/unit/parser/error_reporting_spec.rb
|
99
102
|
- spec/unit/parser/grouped_expr_spec.rb
|
100
103
|
- spec/unit/parser/multiple_precedence_spec.rb
|
104
|
+
- spec/unit/parser/non_terminal_ambiguity_spec.rb
|
101
105
|
- spec/unit/parser/noop_spec.rb
|
102
106
|
- spec/unit/parser/pass_through_parser_spec.rb
|
103
107
|
- spec/unit/parser/precedence_spec.rb
|