kpeg 0.8.5 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +10 -0
- data/.gemtest +0 -0
- data/Gemfile +11 -3
- data/History.txt +21 -0
- data/LICENSE +25 -0
- data/Manifest.txt +47 -0
- data/README.rdoc +222 -0
- data/Rakefile +23 -11
- data/bin/kpeg +4 -2
- data/examples/calculator/calculator.kpeg +17 -0
- data/examples/calculator/calculator.rb +7 -0
- data/examples/foreign_reference/literals.kpeg +5 -0
- data/examples/foreign_reference/matcher.kpeg +9 -0
- data/examples/foreign_reference/matcher.rb +5 -0
- data/examples/lua_string/driver.rb +21 -0
- data/examples/lua_string/lua_string.kpeg +14 -0
- data/examples/lua_string/lua_string.kpeg.rb +460 -0
- data/examples/phone_number/README.md +3 -0
- data/examples/phone_number/phone_number.kpeg +20 -0
- data/examples/phone_number/phone_number.rb +6 -0
- data/examples/upper/README.md +83 -0
- data/examples/upper/upper.kpeg +24 -0
- data/examples/upper/upper.rb +9 -0
- data/kpeg.gemspec +35 -17
- data/lib/hoe/kpeg.rb +94 -0
- data/lib/kpeg.rb +3 -0
- data/lib/kpeg/code_generator.rb +16 -3
- data/lib/kpeg/compiled_parser.rb +18 -28
- data/lib/kpeg/format_parser.kpeg +129 -0
- data/lib/kpeg/format_parser.rb +88 -49
- data/lib/kpeg/grammar.rb +10 -0
- data/lib/kpeg/string_escape.kpeg +20 -0
- data/test/inputs/comments.kpeg +5 -0
- data/test/test_file_parser_roundtrip.rb +3 -3
- data/test/test_gen_calc.rb +2 -2
- data/test/test_kpeg.rb +2 -2
- data/test/test_kpeg_code_generator.rb +65 -2
- data/test/test_kpeg_compiled_parser.rb +2 -2
- data/test/test_kpeg_format.rb +49 -4
- data/test/test_kpeg_grammar_renderer.rb +2 -2
- data/test/test_left_recursion.rb +2 -2
- data/{doc → vim}/syntax_kpeg/ftdetect/kpeg.vim +0 -0
- data/{doc → vim}/syntax_kpeg/syntax/kpeg.vim +0 -0
- metadata +89 -26
- data/README.md +0 -183
- data/lib/kpeg/version.rb +0 -3
@@ -1,12 +1,12 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'kpeg'
|
2
3
|
require 'kpeg/format_parser'
|
3
4
|
require 'kpeg/grammar_renderer'
|
4
5
|
require 'kpeg/code_generator'
|
5
6
|
require 'stringio'
|
6
|
-
require 'test/unit'
|
7
7
|
|
8
|
-
class TestKPegRoundtrip <
|
9
|
-
PATH = File.expand_path("../../lib/kpeg/
|
8
|
+
class TestKPegRoundtrip < MiniTest::Unit::TestCase
|
9
|
+
PATH = File.expand_path("../../lib/kpeg/format_parser.kpeg", __FILE__)
|
10
10
|
def test_roundtrip
|
11
11
|
data = File.read(PATH)
|
12
12
|
|
data/test/test_gen_calc.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'kpeg/format_parser'
|
4
4
|
require 'kpeg/code_generator'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
|
-
class TestKPegCodeGenerator <
|
7
|
+
class TestKPegCodeGenerator < MiniTest::Unit::TestCase
|
8
8
|
GRAMMAR = <<-'STR'
|
9
9
|
Stmt = - Expr:e EOL { @answers << e }
|
10
10
|
| ( !EOL . )* EOL { puts "error" }
|
data/test/test_kpeg.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
class TestKPeg <
|
5
|
+
class TestKPeg < MiniTest::Unit::TestCase
|
6
6
|
def assert_match(m, str)
|
7
7
|
assert_kind_of KPeg::MatchString, m
|
8
8
|
assert_equal str, m.string
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require '
|
2
|
+
require 'minitest/autorun'
|
3
3
|
require 'kpeg'
|
4
4
|
require 'kpeg/code_generator'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
|
-
class TestKPegCodeGenerator <
|
7
|
+
class TestKPegCodeGenerator < MiniTest::Unit::TestCase
|
8
8
|
def test_dot
|
9
9
|
gram = KPeg.grammar do |g|
|
10
10
|
g.root = g.dot
|
@@ -1370,6 +1370,69 @@ end
|
|
1370
1370
|
assert_equal 5, code.failing_rule_offset
|
1371
1371
|
end
|
1372
1372
|
|
1373
|
+
def test_directive_footer
|
1374
|
+
gram = KPeg.grammar do |g|
|
1375
|
+
g.root = g.dot
|
1376
|
+
g.directives['footer'] = g.action("\n# require 'some/subclass'\n")
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
str = <<-STR
|
1380
|
+
require 'kpeg/compiled_parser'
|
1381
|
+
|
1382
|
+
class Test < KPeg::CompiledParser
|
1383
|
+
|
1384
|
+
# root = .
|
1385
|
+
def _root
|
1386
|
+
_tmp = get_byte
|
1387
|
+
set_failed_rule :_root unless _tmp
|
1388
|
+
return _tmp
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
Rules = {}
|
1392
|
+
Rules[:_root] = rule_info("root", ".")
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
# require 'some/subclass'
|
1396
|
+
STR
|
1397
|
+
|
1398
|
+
cg = KPeg::CodeGenerator.new "Test", gram
|
1399
|
+
|
1400
|
+
assert_equal str, cg.output
|
1401
|
+
|
1402
|
+
assert cg.parse("hello")
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
def test_directive_header
|
1406
|
+
gram = KPeg.grammar do |g|
|
1407
|
+
g.root = g.dot
|
1408
|
+
g.directives['header'] = g.action("\n# coding: UTF-8\n")
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
str = <<-STR
|
1412
|
+
# coding: UTF-8
|
1413
|
+
require 'kpeg/compiled_parser'
|
1414
|
+
|
1415
|
+
class Test < KPeg::CompiledParser
|
1416
|
+
|
1417
|
+
# root = .
|
1418
|
+
def _root
|
1419
|
+
_tmp = get_byte
|
1420
|
+
set_failed_rule :_root unless _tmp
|
1421
|
+
return _tmp
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
Rules = {}
|
1425
|
+
Rules[:_root] = rule_info("root", ".")
|
1426
|
+
end
|
1427
|
+
STR
|
1428
|
+
|
1429
|
+
cg = KPeg::CodeGenerator.new "Test", gram
|
1430
|
+
|
1431
|
+
assert_equal str, cg.output
|
1432
|
+
|
1433
|
+
assert cg.parse("hello")
|
1434
|
+
end
|
1435
|
+
|
1373
1436
|
def test_setup_actions
|
1374
1437
|
gram = KPeg.grammar do |g|
|
1375
1438
|
g.root = g.dot
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'kpeg/compiled_parser'
|
4
4
|
require 'stringio'
|
5
5
|
|
6
|
-
class TestKPegCompiledParser <
|
6
|
+
class TestKPegCompiledParser < MiniTest::Unit::TestCase
|
7
7
|
|
8
8
|
gram = <<-GRAM
|
9
9
|
letter = [a-z]
|
data/test/test_kpeg_format.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'kpeg/format_parser'
|
4
4
|
require 'kpeg/grammar_renderer'
|
5
5
|
require 'stringio'
|
6
6
|
require 'rubygems'
|
7
7
|
|
8
|
-
class TestKPegFormat <
|
8
|
+
class TestKPegFormat < MiniTest::Unit::TestCase
|
9
9
|
G = KPeg::Grammar.new
|
10
10
|
|
11
|
-
gram = File.read File.expand_path("../../lib/kpeg/
|
11
|
+
gram = File.read File.expand_path("../../lib/kpeg/format_parser.kpeg", __FILE__)
|
12
12
|
KPeg.compile gram, "TestParser", self
|
13
13
|
|
14
14
|
def match(str, gram=nil, log=false)
|
@@ -386,6 +386,51 @@ Value = NUMBER:i { i }
|
|
386
386
|
assert_rule G.seq(G.ref('b'), G.ref("c")), m
|
387
387
|
end
|
388
388
|
|
389
|
+
def test_parser_directive
|
390
|
+
m = match <<-GRAMMAR
|
391
|
+
%% header {
|
392
|
+
# coding: UTF-8
|
393
|
+
}
|
394
|
+
|
395
|
+
a=b
|
396
|
+
GRAMMAR
|
397
|
+
|
398
|
+
assert_rule G.ref("b"), m
|
399
|
+
|
400
|
+
expected = {
|
401
|
+
"header" => KPeg::Action.new("\n# coding: UTF-8\n")
|
402
|
+
}
|
403
|
+
|
404
|
+
assert_equal expected, m.directives
|
405
|
+
end
|
406
|
+
|
407
|
+
def test_parser_directive_duplicate
|
408
|
+
m = nil
|
409
|
+
|
410
|
+
out, err = capture_io do
|
411
|
+
m = match <<-GRAMMAR
|
412
|
+
%% header {
|
413
|
+
# coding: UTF-8
|
414
|
+
}
|
415
|
+
|
416
|
+
a=b
|
417
|
+
|
418
|
+
%% header {
|
419
|
+
# coding: ISO-8859-1
|
420
|
+
}
|
421
|
+
GRAMMAR
|
422
|
+
end
|
423
|
+
|
424
|
+
assert_empty out
|
425
|
+
assert_equal "directive \"header\" appears more than once\n", err
|
426
|
+
|
427
|
+
expected = {
|
428
|
+
"header" => KPeg::Action.new("\n# coding: ISO-8859-1\n")
|
429
|
+
}
|
430
|
+
|
431
|
+
assert_equal expected, m.directives
|
432
|
+
end
|
433
|
+
|
389
434
|
def test_parser_setup
|
390
435
|
m = match "%% { def initialize; end }\na=b"
|
391
436
|
assert_rule G.ref("b"), m
|
@@ -469,7 +514,7 @@ fact = fact "*" num
|
|
469
514
|
end
|
470
515
|
|
471
516
|
def test_roundtrip
|
472
|
-
path = File.expand_path("../../lib/kpeg/
|
517
|
+
path = File.expand_path("../../lib/kpeg/format_parser.kpeg", __FILE__)
|
473
518
|
parser = KPeg::FormatParser.new File.read(path)
|
474
519
|
assert parser.parse, "Unable to parse"
|
475
520
|
|
@@ -1,9 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'kpeg/grammar_renderer'
|
4
4
|
require 'stringio'
|
5
5
|
|
6
|
-
class TestKPegGrammarRenderer <
|
6
|
+
class TestKPegGrammarRenderer < MiniTest::Unit::TestCase
|
7
7
|
def test_escape
|
8
8
|
str = "hello\nbob"
|
9
9
|
assert_equal 'hello\nbob', KPeg::GrammarRenderer.escape(str)
|
data/test/test_left_recursion.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
require '
|
1
|
+
require 'minitest/autorun'
|
2
2
|
require 'kpeg'
|
3
3
|
require 'kpeg/format_parser'
|
4
4
|
require 'kpeg/code_generator'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
|
-
class TestKPegLeftRecursion <
|
7
|
+
class TestKPegLeftRecursion < MiniTest::Unit::TestCase
|
8
8
|
GRAMMAR = <<-'STR'
|
9
9
|
|
10
10
|
name = name:n "[]" { [:array, n] }
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kpeg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Evan Phoenix
|
@@ -15,50 +15,109 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-03-07 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: minitest
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
hash:
|
28
|
+
hash: 37
|
29
29
|
segments:
|
30
|
-
-
|
31
|
-
|
30
|
+
- 2
|
31
|
+
- 11
|
32
|
+
- 3
|
33
|
+
version: 2.11.3
|
32
34
|
type: :development
|
33
35
|
version_requirements: *id001
|
34
|
-
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rdoc
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 19
|
45
|
+
segments:
|
46
|
+
- 3
|
47
|
+
- 10
|
48
|
+
version: "3.10"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: hoe
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 27
|
60
|
+
segments:
|
61
|
+
- 2
|
62
|
+
- 12
|
63
|
+
version: "2.12"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
description: |-
|
67
|
+
KPeg is a simple PEG library for Ruby. It provides an API as well as native
|
68
|
+
grammar to build the grammar.
|
69
|
+
|
70
|
+
KPeg strives to provide a simple, powerful API without being too exotic.
|
71
|
+
|
72
|
+
KPeg supports direct left recursion of rules via the
|
73
|
+
{OMeta memoization}[http://www.vpri.org/pdf/tr2008003_experimenting.pdf] trick.
|
35
74
|
email:
|
36
75
|
- evan@fallingsnow.net
|
37
76
|
executables:
|
38
77
|
- kpeg
|
39
78
|
extensions: []
|
40
79
|
|
41
|
-
extra_rdoc_files:
|
42
|
-
|
80
|
+
extra_rdoc_files:
|
81
|
+
- History.txt
|
82
|
+
- Manifest.txt
|
43
83
|
files:
|
84
|
+
- .autotest
|
85
|
+
- Gemfile
|
86
|
+
- History.txt
|
87
|
+
- LICENSE
|
88
|
+
- Manifest.txt
|
89
|
+
- README.rdoc
|
90
|
+
- Rakefile
|
91
|
+
- bin/kpeg
|
92
|
+
- examples/calculator/calculator.kpeg
|
93
|
+
- examples/calculator/calculator.rb
|
94
|
+
- examples/foreign_reference/literals.kpeg
|
95
|
+
- examples/foreign_reference/matcher.kpeg
|
96
|
+
- examples/foreign_reference/matcher.rb
|
97
|
+
- examples/lua_string/driver.rb
|
98
|
+
- examples/lua_string/lua_string.kpeg
|
99
|
+
- examples/lua_string/lua_string.kpeg.rb
|
100
|
+
- examples/phone_number/README.md
|
101
|
+
- examples/phone_number/phone_number.kpeg
|
102
|
+
- examples/phone_number/phone_number.rb
|
103
|
+
- examples/upper/README.md
|
104
|
+
- examples/upper/upper.kpeg
|
105
|
+
- examples/upper/upper.rb
|
106
|
+
- kpeg.gemspec
|
107
|
+
- lib/hoe/kpeg.rb
|
108
|
+
- lib/kpeg.rb
|
44
109
|
- lib/kpeg/code_generator.rb
|
45
110
|
- lib/kpeg/compiled_parser.rb
|
111
|
+
- lib/kpeg/format_parser.kpeg
|
46
112
|
- lib/kpeg/format_parser.rb
|
47
113
|
- lib/kpeg/grammar.rb
|
48
114
|
- lib/kpeg/grammar_renderer.rb
|
49
115
|
- lib/kpeg/match.rb
|
50
116
|
- lib/kpeg/parser.rb
|
51
117
|
- lib/kpeg/position.rb
|
118
|
+
- lib/kpeg/string_escape.kpeg
|
52
119
|
- lib/kpeg/string_escape.rb
|
53
|
-
-
|
54
|
-
- lib/kpeg.rb
|
55
|
-
- bin/kpeg
|
56
|
-
- doc/syntax_kpeg/ftdetect/kpeg.vim
|
57
|
-
- doc/syntax_kpeg/syntax/kpeg.vim
|
58
|
-
- README.md
|
59
|
-
- Rakefile
|
60
|
-
- kpeg.gemspec
|
61
|
-
- Gemfile
|
120
|
+
- test/inputs/comments.kpeg
|
62
121
|
- test/test_file_parser_roundtrip.rb
|
63
122
|
- test/test_gen_calc.rb
|
64
123
|
- test/test_kpeg.rb
|
@@ -67,12 +126,16 @@ files:
|
|
67
126
|
- test/test_kpeg_format.rb
|
68
127
|
- test/test_kpeg_grammar_renderer.rb
|
69
128
|
- test/test_left_recursion.rb
|
129
|
+
- vim/syntax_kpeg/ftdetect/kpeg.vim
|
130
|
+
- vim/syntax_kpeg/syntax/kpeg.vim
|
131
|
+
- .gemtest
|
70
132
|
homepage: https://github.com/evanphx/kpeg
|
71
133
|
licenses: []
|
72
134
|
|
73
135
|
post_install_message:
|
74
|
-
rdoc_options:
|
75
|
-
|
136
|
+
rdoc_options:
|
137
|
+
- --main
|
138
|
+
- README.rdoc
|
76
139
|
require_paths:
|
77
140
|
- lib
|
78
141
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -95,11 +158,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
158
|
version: "0"
|
96
159
|
requirements: []
|
97
160
|
|
98
|
-
rubyforge_project:
|
99
|
-
rubygems_version: 1.8.
|
161
|
+
rubyforge_project: kpeg
|
162
|
+
rubygems_version: 1.8.17
|
100
163
|
signing_key:
|
101
164
|
specification_version: 3
|
102
|
-
summary:
|
165
|
+
summary: KPeg is a simple PEG library for Ruby
|
103
166
|
test_files:
|
104
167
|
- test/test_file_parser_roundtrip.rb
|
105
168
|
- test/test_gen_calc.rb
|
data/README.md
DELETED
@@ -1,183 +0,0 @@
|
|
1
|
-
KPeg
|
2
|
-
====
|
3
|
-
|
4
|
-
KPeg is a simple PEG library for Ruby. It provides an API as well as native grammar to build the grammar.
|
5
|
-
|
6
|
-
KPeg strives to provide a simple, powerful API without being too exotic.
|
7
|
-
|
8
|
-
KPeg supports direct left recursion of rules via the [OMeta memoization](http://www.vpri.org/pdf/tr2008003_experimenting.pdf) trick.
|
9
|
-
|
10
|
-
## Writing your first grammar
|
11
|
-
|
12
|
-
### Setting up your grammar
|
13
|
-
|
14
|
-
All grammars start with with the class/module name that will be your parser
|
15
|
-
|
16
|
-
%% name = Example::Parser
|
17
|
-
|
18
|
-
After that a block of ruby code can be defined that will be added into the class body of your parser. Attributes that are defined in this block can be accessed within your parser as instance variables. Methods can also be defined in this block and used in action blocks as well.
|
19
|
-
|
20
|
-
%% {
|
21
|
-
attr_accessor :something_cool
|
22
|
-
|
23
|
-
def something_awesome
|
24
|
-
# do something awesome
|
25
|
-
end
|
26
|
-
}
|
27
|
-
|
28
|
-
### Defining literals
|
29
|
-
|
30
|
-
Literals are static declarations of characters or regular expressions designed for reuse in the grammar. These can be constants or variables. Literals can take strings, regular expressions or character ranges
|
31
|
-
|
32
|
-
ALPHA = /[A-Za-z]/
|
33
|
-
DIGIT = /[0-9]/
|
34
|
-
period = "."
|
35
|
-
string = "a string"
|
36
|
-
regex = /(regexs?)+/
|
37
|
-
char_range = [b-t]
|
38
|
-
|
39
|
-
Literals can also accept multiple definitions
|
40
|
-
|
41
|
-
vowel = "a" | "e" | "i" | "o" | "u"
|
42
|
-
alpha = /[A-Z]/ | /[a-z]/
|
43
|
-
|
44
|
-
### Defining Rules for Values
|
45
|
-
|
46
|
-
Before you can start parsing a string you will need to define rules that you will use to accept or reject that string. There are many different types of rules available in kpeg
|
47
|
-
|
48
|
-
The most basic of these rules is a string capture
|
49
|
-
|
50
|
-
alpha = < /[A-Za-z]/ > { text }
|
51
|
-
|
52
|
-
|
53
|
-
While this looks very much like the ALPHA literal defined above it differs in one important way, the text captured by the rule defined between the < and > symbols will be set as the text variable in block that follows. You can also explicitly define the variable that you would like but only with existing rules or literals.
|
54
|
-
|
55
|
-
letter = alpha:a { a }
|
56
|
-
|
57
|
-
Additionally blocks can return true or false values based upon an expression within the block. To return true if a test passes do the following:
|
58
|
-
|
59
|
-
match_greater_than_10 = < num:n > &{ n > 10 }
|
60
|
-
|
61
|
-
To test and return a false value if the test passes do the following:
|
62
|
-
|
63
|
-
do_not_match_greater_than_10 = < num:n > !{ n > 10 }
|
64
|
-
|
65
|
-
Rules can also act like functions and take parameters. An example of this is lifted from the [Email List Validator](https://github.com/larb/email_address_validator), where an ascii value is passed in and the character is evaluated against it returning a true if it matches
|
66
|
-
|
67
|
-
d(num) = <.> &{ text[0] == num }
|
68
|
-
|
69
|
-
Rules support some regular expression syntax for matching
|
70
|
-
|
71
|
-
+ maybe ?
|
72
|
-
+ many +
|
73
|
-
+ kleene *
|
74
|
-
+ groupings ()
|
75
|
-
|
76
|
-
Examples
|
77
|
-
|
78
|
-
letters = alpha+
|
79
|
-
words = alpha+ space* period?
|
80
|
-
sentence = (letters+ | space+)+
|
81
|
-
|
82
|
-
Kpeg also allows a rule to define the acceptable number of matches in the form of a range. In regular expressions this is often denoted with syntax like {0,3}. Kpeg uses this syntax to accomplish match ranges [min, max].
|
83
|
-
|
84
|
-
matches_3_to_5_times = letter[3,5]
|
85
|
-
matches_3_to_any_times = letter[3,*]
|
86
|
-
|
87
|
-
### Defining Actions
|
88
|
-
|
89
|
-
Illustrated above in some of the examples, kpeg allows you to perform actions based upon a match that are described in block provided or in the rule definition itself.
|
90
|
-
|
91
|
-
num = /[1-9][0-9]*/
|
92
|
-
sum = < num:n1 "+" num:n2 > { n1 + n2 }
|
93
|
-
|
94
|
-
As of version 0.8 an alternate syntax has been added for calling defined methods as actions.
|
95
|
-
|
96
|
-
%% {
|
97
|
-
def add(n1, n2){
|
98
|
-
n1 + n2
|
99
|
-
}
|
100
|
-
}
|
101
|
-
num = /[1-9][0-9]*/
|
102
|
-
sum = < num:n1 "+" num:n2 > ~add(n1, n2)
|
103
|
-
|
104
|
-
### Referencing an external grammar
|
105
|
-
|
106
|
-
Kpeg allows you to run a rule that is defined in an external grammar. This is useful if there is a defined set of rules that you would like to reuse in another parser. To do this, create your grammar and generate a parser using the kpeg command line tool.
|
107
|
-
|
108
|
-
kpeg literals.kpeg
|
109
|
-
|
110
|
-
Once you have the generated parser, include that file into your new grammar
|
111
|
-
|
112
|
-
%{
|
113
|
-
require "literals.kpeg.rb"
|
114
|
-
}
|
115
|
-
|
116
|
-
Then create a variable to hold to foreign interface and pass it the class name of your parser. In this case my parser class name is Literal
|
117
|
-
|
118
|
-
%foreign_grammer = Literal
|
119
|
-
|
120
|
-
You can then use rules defined in the foreign grammar in the local grammar file like so
|
121
|
-
|
122
|
-
sentence = (%foreign_grammer.alpha %foreign_grammer.space*)+ %foreign_grammer.period
|
123
|
-
|
124
|
-
### Comments
|
125
|
-
|
126
|
-
Kpeg allows comments to be added to the grammar file by using the # symbol
|
127
|
-
|
128
|
-
# This is a comment in my grammar
|
129
|
-
|
130
|
-
## Generating and running your parser
|
131
|
-
|
132
|
-
Before you can generate your parser you will need to define a root rule. This will be the first rule run against the string provided to the parser
|
133
|
-
|
134
|
-
root = sentence
|
135
|
-
|
136
|
-
To generate the parser run the kpeg command with the kpeg file(s) as an argument. This will generate a ruby file with the same name as your grammar file.
|
137
|
-
|
138
|
-
kpeg example.kpeg
|
139
|
-
|
140
|
-
Include your generated parser file into an application that you want to use the parser in and run it. Create a new instance of the parser and pass in the string you want to evaluate. When parse is called on the parser instance it will return a true if the sting is matched, or false if it doesn't.
|
141
|
-
|
142
|
-
require "example.kpeg.rb"
|
143
|
-
|
144
|
-
parser = Example::Parser.new(string_to_evaluate)
|
145
|
-
parser.parse
|
146
|
-
|
147
|
-
## Shortcuts and other techniques
|
148
|
-
|
149
|
-
Per vito, you can get the current line or current column in the following way
|
150
|
-
|
151
|
-
line = { current_line }
|
152
|
-
column = { current_column }
|
153
|
-
foo = line:line ... { # use line here }
|
154
|
-
|
155
|
-
## AST Generation
|
156
|
-
|
157
|
-
As of Kpeg 0.8 a parser can now generate an AST. To define an AST node use the following syntax
|
158
|
-
|
159
|
-
%% assign = ast Assignment(name, value)
|
160
|
-
|
161
|
-
Once you have a defined AST node, it can be used in your grammar like so
|
162
|
-
|
163
|
-
assignment = identifier:i space* = space* value:v ~assign(i,v)
|
164
|
-
|
165
|
-
This will create a new Assign node that you can add into your AST.
|
166
|
-
|
167
|
-
For a good example of usage check out [Talon](https://github.com/evanphx/talon)
|
168
|
-
|
169
|
-
## Examples
|
170
|
-
|
171
|
-
There are several examples available in the /examples directory. The upper parser has a readme with a step by step description of the grammar.
|
172
|
-
|
173
|
-
## Projects using kpeg
|
174
|
-
|
175
|
-
[Dang](https://github.com/veganstraightedge/dang)
|
176
|
-
|
177
|
-
[Email Address Validator](https://github.com/larb/email_address_validator)
|
178
|
-
|
179
|
-
[Callisto](https://github.com/dwaite/Callisto)
|
180
|
-
|
181
|
-
[Doodle](https://github.com/vito/doodle)
|
182
|
-
|
183
|
-
[Kanbanpad](https://kanbanpad.com) (uses kpeg for parsing of the 'enter something' bar)
|