ghostwheel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +46 -0
- data/lib/ghost_wheel.rb +28 -0
- data/lib/ghost_wheel/build_parser.rb +11 -0
- data/lib/ghost_wheel/errors.rb +9 -0
- data/lib/ghost_wheel/expression.rb +22 -0
- data/lib/ghost_wheel/expression/alternation.rb +25 -0
- data/lib/ghost_wheel/expression/empty.rb +15 -0
- data/lib/ghost_wheel/expression/end_of_file.rb +19 -0
- data/lib/ghost_wheel/expression/literal.rb +31 -0
- data/lib/ghost_wheel/expression/look_ahead.rb +33 -0
- data/lib/ghost_wheel/expression/optional.rb +26 -0
- data/lib/ghost_wheel/expression/query.rb +32 -0
- data/lib/ghost_wheel/expression/repetition.rb +44 -0
- data/lib/ghost_wheel/expression/rule.rb +24 -0
- data/lib/ghost_wheel/expression/sequence.rb +30 -0
- data/lib/ghost_wheel/expression/transform.rb +30 -0
- data/lib/ghost_wheel/parse_results.rb +9 -0
- data/lib/ghost_wheel/parser.rb +71 -0
- data/lib/ghost_wheel/parser_builder/ghost_wheel.rb +100 -0
- data/lib/ghost_wheel/parser_builder/ruby.rb +175 -0
- data/lib/ghost_wheel/scanner.rb +42 -0
- data/setup.rb +1360 -0
- data/test/dsl/tc_build_parser.rb +29 -0
- data/test/dsl/tc_ghost_wheel_dsl.rb +143 -0
- data/test/dsl/tc_ruby_dsl.rb +227 -0
- data/test/example/tc_json_core.rb +95 -0
- data/test/example/tc_json_ghost_wheel.rb +48 -0
- data/test/example/tc_json_ruby.rb +81 -0
- data/test/helpers/ghost_wheel_namespace.rb +24 -0
- data/test/helpers/json_tests.rb +63 -0
- data/test/helpers/parse_helpers.rb +83 -0
- data/test/parser/tc_alternation_expression.rb +27 -0
- data/test/parser/tc_empty_expression.rb +15 -0
- data/test/parser/tc_end_of_file_expression.rb +27 -0
- data/test/parser/tc_literal_expression.rb +55 -0
- data/test/parser/tc_look_ahead_expression.rb +41 -0
- data/test/parser/tc_memoization.rb +31 -0
- data/test/parser/tc_optional_expression.rb +31 -0
- data/test/parser/tc_parser.rb +78 -0
- data/test/parser/tc_query_expression.rb +40 -0
- data/test/parser/tc_repetition_expression.rb +76 -0
- data/test/parser/tc_rule_expression.rb +32 -0
- data/test/parser/tc_scanning.rb +192 -0
- data/test/parser/tc_sequence_expression.rb +42 -0
- data/test/parser/tc_transform_expression.rb +77 -0
- data/test/ts_all.rb +7 -0
- data/test/ts_dsl.rb +8 -0
- data/test/ts_example.rb +7 -0
- data/test/ts_parser.rb +19 -0
- metadata +94 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers json_tests])
|
7
|
+
|
8
|
+
class TestJSONGhostWheel < Test::Unit::TestCase
|
9
|
+
include ParseHelpers
|
10
|
+
include JSONTests
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@parser = GhostWheel.build_parser( %q{
|
14
|
+
keyword = 'true' { true } | 'false' { false } | 'null' { nil }
|
15
|
+
|
16
|
+
number = /-?(?:0|[1-9]\d*)(?:\.\d+(?:[eE][+-]?\d+)?)?/
|
17
|
+
{ ast.include?(".") ? Float(ast) : Integer(ast) }
|
18
|
+
|
19
|
+
string_content = /\\\\["\\\\\/]/ { ast[-1, 1] }
|
20
|
+
| /\\\\[bfnrt]/
|
21
|
+
{ Hash[*%W[b \n f \f n \n r \r t \t]][ast[-1, 1]] }
|
22
|
+
| /\\\\u[0-9a-fA-F]{4}/
|
23
|
+
{ [Integer("0x#{ast[2..-1]}")].pack("U") }
|
24
|
+
| /[^\\\\"]+/
|
25
|
+
string = '"' string_content* '"' { ast.flatten[1..-2].join }
|
26
|
+
|
27
|
+
array_content = value_with_array_sep+ value
|
28
|
+
{ ast[0].inject([]) { |a, v| a.push(*v) } + ast[-1..-1] }
|
29
|
+
| value? { ast.is_a?(EmptyParseResult) ? [] : [ast] }
|
30
|
+
array = /\[\s*/ array_content /\s*\]/ { ast[1] }
|
31
|
+
|
32
|
+
object_pair = string /\s*:\s*/ value { {ast[0] => ast[-1]} }
|
33
|
+
object_pair_and_sep = object_pair /\s*;\s*/ { ast[0] }
|
34
|
+
object_content = object_pair_and_sep+ object_pair { ast.flatten }
|
35
|
+
| object_pair?
|
36
|
+
{ ast.is_a?(EmptyParseResult) ? [] : [ast] }
|
37
|
+
object = /\\\{\s*/ object_content /\\\}\s*/
|
38
|
+
{ ast[1].inject({}) { |h, p| h.merge(p) } }
|
39
|
+
|
40
|
+
value_space = /\s*/
|
41
|
+
value_content = keyword | number | string | array | object
|
42
|
+
value = value_space value_content value_space { ast[1] }
|
43
|
+
value_with_array_sep = value /\s*,\s*/ { ast[0] }
|
44
|
+
|
45
|
+
json := value EOF { ast[0] }
|
46
|
+
} )[:json]
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers json_tests])
|
7
|
+
|
8
|
+
class TestJSONRuby < Test::Unit::TestCase
|
9
|
+
include ParseHelpers
|
10
|
+
include JSONTests
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@parser = GhostWheel.build_parser do
|
14
|
+
rule( :keyword,
|
15
|
+
alt("true", "false", "null") { |k|
|
16
|
+
{"true" => true, "false" => false, "null" => nil}[k]
|
17
|
+
} )
|
18
|
+
|
19
|
+
rule( :number,
|
20
|
+
/-?(?:0|[1-9]\d*)(?:\.\d+(?:[eE][+-]?\d+)?)?/ ) { |n|
|
21
|
+
n.include?(".") ? Float(n) : Integer(n)
|
22
|
+
}
|
23
|
+
|
24
|
+
rule( :string,
|
25
|
+
seq(
|
26
|
+
skip('"'),
|
27
|
+
zplus(
|
28
|
+
alt(
|
29
|
+
lit(%r{\\["\\/]}) { |e| e[-1, 1] },
|
30
|
+
lit(/\\[bfnrt]/) { |e|
|
31
|
+
Hash[*%W[b \n f \f n \n r \r t \t]][e[-1, 1]]
|
32
|
+
},
|
33
|
+
lit(/\\u[0-9a-fA-F]{4}/) { |e|
|
34
|
+
[Integer("0x#{e[2..-1]}")].pack("U")
|
35
|
+
},
|
36
|
+
/[^\\"]+/
|
37
|
+
)
|
38
|
+
),
|
39
|
+
skip('"')
|
40
|
+
) { |s| s.flatten.join } )
|
41
|
+
|
42
|
+
rule( :array,
|
43
|
+
seq(
|
44
|
+
skip(/\[\s*/),
|
45
|
+
alt(
|
46
|
+
seq(
|
47
|
+
oplus(seq(:value, skip(/\s*,\s*/))),
|
48
|
+
:value
|
49
|
+
) { |a| a[0].inject([]) { |vs, v| vs.push(*v) } + a[-1..-1] },
|
50
|
+
seq(opt(:value))
|
51
|
+
),
|
52
|
+
skip(/\s*\]/)
|
53
|
+
) { |a| a.first } )
|
54
|
+
|
55
|
+
rule( :object_pair,
|
56
|
+
seq(:string, /\s*:\s*/, :value) { |kv| {kv[0] => kv[-1]} } )
|
57
|
+
rule( :object,
|
58
|
+
seq(
|
59
|
+
skip(/\{\s*/),
|
60
|
+
alt(
|
61
|
+
seq(
|
62
|
+
oplus(seq(:object_pair, skip(/\s*;\s*/))),
|
63
|
+
:object_pair
|
64
|
+
) { |ps| ps[0][0] + ps[-1..-1] },
|
65
|
+
seq(opt(:object_pair))
|
66
|
+
),
|
67
|
+
skip(/\}\s*/)
|
68
|
+
) { |ps| ps[0].inject({}) { |h, p| h.merge(p) } } )
|
69
|
+
|
70
|
+
rule(:value_space, opt(skip(/\s+/)))
|
71
|
+
rule( :value,
|
72
|
+
seq(
|
73
|
+
:value_space,
|
74
|
+
alt(:object, :array, :string, :number, :keyword),
|
75
|
+
:value_space
|
76
|
+
) { |v| v[0] } )
|
77
|
+
|
78
|
+
parser(:json, seq(:value, eof) { |j| j[0] })
|
79
|
+
end[:json]
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "ghost_wheel"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Including this module in a test case embeds the GhostWheel namespace for those
|
7
|
+
# tests. Constants both in the root GhostWheel module and in the
|
8
|
+
# GhostWheel::Expression class may be used without qualification.
|
9
|
+
#
|
10
|
+
module GhostWheelNamespace
|
11
|
+
#
|
12
|
+
# Includes the GhostWheel module in +test_case+ and builds a
|
13
|
+
# self.const_missing() method to handle lookups in the GhostWheel::Expression
|
14
|
+
# class.
|
15
|
+
#
|
16
|
+
def self.included(test_case)
|
17
|
+
test_case.instance_eval do
|
18
|
+
include GhostWheel
|
19
|
+
def const_missing(const)
|
20
|
+
GhostWheel::Expression.const_get(const) rescue super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module JSONTests
|
4
|
+
def test_keyword_parsing
|
5
|
+
assert_parses(true, parse("true"))
|
6
|
+
assert_parses(false, parse("false"))
|
7
|
+
assert_parses(nil, parse("null"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_number_parsing
|
11
|
+
assert_parses(42, parse("42"))
|
12
|
+
assert_parses(-13, parse("-13"))
|
13
|
+
assert_parses(3.1415, parse("3.1415"))
|
14
|
+
assert_parses(-0.01, parse("-0.01"))
|
15
|
+
|
16
|
+
assert_parses(0.2e1, parse("0.2e1"))
|
17
|
+
assert_parses(0.2e+1, parse("0.2e+1"))
|
18
|
+
assert_parses(0.2e-1, parse("0.2e-1"))
|
19
|
+
assert_parses(0.2E1, parse("0.2e1"))
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_string_parsing
|
23
|
+
assert_parses(String.new, parse(%Q{""}))
|
24
|
+
assert_parses("James", parse(%Q{"James"}))
|
25
|
+
|
26
|
+
assert_parses(%Q{nested "quotes"}, parse('"nested \"quotes\""'))
|
27
|
+
assert_parses("\n", parse(%Q{"\\n"}))
|
28
|
+
assert_parses("a", parse(%Q{"\\u#{"%04X" % ?a}"}))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_array_parsing
|
32
|
+
assert_parses(Array.new, parse(%Q{[]}))
|
33
|
+
assert_parses(["James", 3.1415, true], parse(%Q{["James", 3.1415, true]}))
|
34
|
+
assert_parses([1, [2, [3]]], parse(%Q{[1, [2, [3]]]}))
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_object_parsing
|
38
|
+
assert_parses(Hash.new, parse(%Q{{}}))
|
39
|
+
assert_parses( {"James" => 3.1415, "Gray" => true},
|
40
|
+
parse(%Q{{"James": 3.1415; "Gray": true}}) )
|
41
|
+
assert_parses( {"Array" => [1, 2, 3], "Object" => {"nested" => "objects"}},
|
42
|
+
parse(<<-END_OBJECT) )
|
43
|
+
{"Array": [1, 2, 3]; "Object": {"nested": "objects"}}
|
44
|
+
END_OBJECT
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_error_parsing
|
48
|
+
assert_doesnt_parse(parse("unknown"))
|
49
|
+
|
50
|
+
assert_doesnt_parse(parse("$1,000"))
|
51
|
+
assert_doesnt_parse(parse("1_000"))
|
52
|
+
assert_doesnt_parse(parse("1K"))
|
53
|
+
|
54
|
+
assert_doesnt_parse(parse(%Q{"}))
|
55
|
+
assert_doesnt_parse(parse(%Q{"\\i"}))
|
56
|
+
|
57
|
+
assert_doesnt_parse(parse("["))
|
58
|
+
assert_doesnt_parse(parse("[1,,2]"))
|
59
|
+
|
60
|
+
assert_doesnt_parse(parse("{"))
|
61
|
+
assert_doesnt_parse(parse(%q{{"key": true false}}))
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "ghost_wheel"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), "ghost_wheel_namespace")
|
6
|
+
|
7
|
+
module ParseHelpers
|
8
|
+
include GhostWheelNamespace
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
##################
|
13
|
+
### Assertions ###
|
14
|
+
##################
|
15
|
+
|
16
|
+
def assert_parses(expected_ast, actual_ast, message = nil)
|
17
|
+
if message.nil?
|
18
|
+
parsed = actual_ast.is_a?(ParseResult) ? actual_ast.value.inspect :
|
19
|
+
actual_ast.class
|
20
|
+
message = "Parsed #{parsed} while expecting #{expected_ast.inspect}."
|
21
|
+
end
|
22
|
+
|
23
|
+
assert_block(message) do
|
24
|
+
actual_ast.is_a?(ParseResult) and actual_ast.value == expected_ast
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def assert_doesnt_parse(actual_ast, message = "Parse succeeded.")
|
29
|
+
assert_block(message) { actual_ast.is_a?(FailedParseResult) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_parses_empty(actual_ast, message = "Parse was not empty.")
|
33
|
+
assert_block(message) { actual_ast.is_a?(EmptyParseResult) }
|
34
|
+
end
|
35
|
+
|
36
|
+
########################
|
37
|
+
### Parser Shortcuts ###
|
38
|
+
########################
|
39
|
+
|
40
|
+
def parse(source)
|
41
|
+
@parser.parse(Scanner.new(source))
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_alternation(*expressions)
|
45
|
+
Alternation.new(*wrap_literals(expressions)).parse(@source)
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_eof
|
49
|
+
EndOfFile.instance.parse(@source)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_literal(literal, *args)
|
53
|
+
Literal.new(literal, *args).parse(@source)
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_look_ahead(literal, *args)
|
57
|
+
LookAhead.new(Literal.new(literal), *args).parse(@source)
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_optional(literal)
|
61
|
+
Optional.new(Literal.new(literal)).parse(@source)
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_repetition(literal, *args)
|
65
|
+
Repetition.new(Literal.new(literal), *args).parse(@source)
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_sequence(*expressions)
|
69
|
+
Sequence.new(*wrap_literals(expressions)).parse(@source)
|
70
|
+
end
|
71
|
+
|
72
|
+
########################
|
73
|
+
### Literal Wrappers ###
|
74
|
+
########################
|
75
|
+
|
76
|
+
def wrap_literal(literal)
|
77
|
+
literal.is_a?(Expression) ? literal : Literal.new(literal)
|
78
|
+
end
|
79
|
+
|
80
|
+
def wrap_literals(literals)
|
81
|
+
literals.map { |literal| wrap_literal(literal) }
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers ghost_wheel_namespace])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
7
|
+
|
8
|
+
class TestAlternationExpression < Test::Unit::TestCase
|
9
|
+
include GhostWheelNamespace
|
10
|
+
include ParseHelpers
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@source = Scanner.new("null")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_match
|
17
|
+
assert_parses("null", parse_alternation(*%w[true false null]))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_failed_match
|
21
|
+
assert_doesnt_parse(parse_alternation(*%w[true false nil]))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_empty_match
|
25
|
+
assert_parses_empty(parse_alternation(Empty.instance))
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers ghost_wheel_namespace])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
7
|
+
|
8
|
+
class TestEmptyExpression < Test::Unit::TestCase
|
9
|
+
include GhostWheelNamespace
|
10
|
+
include ParseHelpers
|
11
|
+
|
12
|
+
def test_empty_expression
|
13
|
+
assert_parses_empty(Empty.instance.parse(Scanner.new("not_used")))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers ghost_wheel_namespace])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
7
|
+
|
8
|
+
class TestEndOfFileExpression < Test::Unit::TestCase
|
9
|
+
include GhostWheelNamespace
|
10
|
+
include ParseHelpers
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@source = Scanner.new("data")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_match
|
17
|
+
test_failed_match
|
18
|
+
assert_parses_empty(parse_eof)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_failed_match
|
22
|
+
4.times do
|
23
|
+
assert_doesnt_parse(parse_eof)
|
24
|
+
@source.scan(/./)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers ghost_wheel_namespace])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
7
|
+
|
8
|
+
class TestLiteralExpression < Test::Unit::TestCase
|
9
|
+
include GhostWheelNamespace
|
10
|
+
include ParseHelpers
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@source = Scanner.new("some words to parse")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_string_match
|
17
|
+
assert_parses("some", parse_literal("some"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_failed_string_match
|
21
|
+
assert_doesnt_parse(parse_literal("doesn't match"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_character_match
|
25
|
+
assert_parses("s", parse_literal(?s))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_failed_character_match
|
29
|
+
assert_doesnt_parse(parse_literal(?S))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_regexp_match
|
33
|
+
assert_parses("some words ", parse_literal(/(?:\w+\s+){2}/))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_failed_regexp_match
|
37
|
+
assert_doesnt_parse(parse_literal(/(?=missing)/))
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_skip_literal_match
|
41
|
+
assert_parses_empty(parse_literal("some", true))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_failed_skip_literal_match
|
45
|
+
assert_doesnt_parse(parse_literal("doesn't match", true))
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_equality
|
49
|
+
assert_not_equal(Literal.new("keyword"), Literal.new("tag"))
|
50
|
+
assert_not_equal(Literal.new("keyword"), Literal.new("keyword", true))
|
51
|
+
|
52
|
+
assert_equal(Literal.new("keyword"), Literal.new("keyword"))
|
53
|
+
assert_equal(Literal.new("keyword"), Literal.new(/keyword/))
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "test/unit"
|
4
|
+
|
5
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers ghost_wheel_namespace])
|
6
|
+
require File.join(File.dirname(__FILE__), %w[.. helpers parse_helpers])
|
7
|
+
|
8
|
+
class TestLookAheadExpression < Test::Unit::TestCase
|
9
|
+
include GhostWheelNamespace
|
10
|
+
include ParseHelpers
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@source = Scanner.new("ahead")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_look_ahead_match
|
17
|
+
assert_parses_empty(parse_look_ahead("ahead"))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_failed_look_ahead_match
|
21
|
+
assert_doesnt_parse(parse_look_ahead("missing"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_negative_look_ahead_match
|
25
|
+
assert_parses_empty(parse_look_ahead("missing", true))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_failed_negative_look_ahead_match
|
29
|
+
assert_doesnt_parse(parse_look_ahead("ahead", true))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_equality
|
33
|
+
assert_not_equal( LookAhead.new(Literal.new("one")),
|
34
|
+
LookAhead.new(Literal.new("two")) )
|
35
|
+
assert_not_equal( LookAhead.new(Literal.new("one"), true),
|
36
|
+
LookAhead.new(Literal.new("one")) )
|
37
|
+
|
38
|
+
assert_equal( LookAhead.new(Literal.new("one")),
|
39
|
+
LookAhead.new(Literal.new("one")) )
|
40
|
+
end
|
41
|
+
end
|