radd_djur 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +101 -0
- data/lib/radd_djur/dsl.rb +53 -0
- data/lib/radd_djur/grammar.rb +191 -0
- data/lib/radd_djur.rb +20 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 81c593af1a6b8fa7adbc48d74bd12a0d452cc3b8
|
4
|
+
data.tar.gz: 9c2d118ba3047f06b2745f69e73ea1f3b777a2d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e665757f618377aeff4b7556fb0ed3c6d77f1e290701f82280c9768e69e481e7f0d0031b71d2cc2dd5b93f546b31e6a84322c0fdbe86be0ed893cab2ac907a7c
|
7
|
+
data.tar.gz: 1ef6ebb0611545820d957cd29c2c309504942238fab8fdfce57afd459848f6edc9d4dbab5e50f14b7d183c6bcf601398ae85ee85a8ba13389556d0f09f32a0eb
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
radd\_djur - Packrat parser combinator library for Ruby
|
2
|
+
=======================================================
|
3
|
+
|
4
|
+
This project aims to provide immutable data structures for Ruby.
|
5
|
+
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
```bash
|
10
|
+
$ gem install radd_djur
|
11
|
+
```
|
12
|
+
|
13
|
+
Documentation
|
14
|
+
-------------
|
15
|
+
|
16
|
+
* [API Reference](http://rubydoc.info/github/shugo/radd_djur/frames)
|
17
|
+
|
18
|
+
Example
|
19
|
+
-------
|
20
|
+
|
21
|
+
require "radd_djur"
|
22
|
+
|
23
|
+
# Activate refinements for radd_djur DSL.
|
24
|
+
# In the DSL, a Ruby symbol represents a nonterminal symbol, and a Ruby
|
25
|
+
# string represents a terminal symbol.
|
26
|
+
using RaddDjur::DSL
|
27
|
+
|
28
|
+
# Define a new grammar, whose start symbol is additive.
|
29
|
+
g = RaddDjur::Grammar.new(:additive) {
|
30
|
+
|
31
|
+
# Grammar#define defines a new parsing rule for a nonterminal symbol.
|
32
|
+
# The first argument is the name of the nonterminal symbol.
|
33
|
+
# In the following comments, parsing rules are expressed in PEG.
|
34
|
+
|
35
|
+
# additive <- multitive '+' additive / multitive
|
36
|
+
define :additive do
|
37
|
+
:multitive.bind { |x|
|
38
|
+
"+".bind {
|
39
|
+
:additive.bind { |y|
|
40
|
+
ret x + y
|
41
|
+
}
|
42
|
+
}
|
43
|
+
} / :multitive
|
44
|
+
end
|
45
|
+
|
46
|
+
# multitive <- primary '*' multitive / primary
|
47
|
+
define :multitive do
|
48
|
+
:primary.bind { |x|
|
49
|
+
"*".bind {
|
50
|
+
:multitive.bind { |y|
|
51
|
+
ret x * y
|
52
|
+
}
|
53
|
+
}
|
54
|
+
} / :primary
|
55
|
+
end
|
56
|
+
|
57
|
+
# primary <- '(' additive ')' / digits
|
58
|
+
define :primary do
|
59
|
+
"(".bind {
|
60
|
+
:additive.bind { |x|
|
61
|
+
")".bind {
|
62
|
+
ret x
|
63
|
+
}
|
64
|
+
}
|
65
|
+
} / :digits
|
66
|
+
end
|
67
|
+
|
68
|
+
# digits <- [0-9]+
|
69
|
+
define :digits do
|
70
|
+
(?0..?9).one_or_more.bind { |xs|
|
71
|
+
ret xs.foldl1(&:+).to_i
|
72
|
+
}
|
73
|
+
end
|
74
|
+
}
|
75
|
+
p g.parse("2*(3+4)")
|
76
|
+
|
77
|
+
License
|
78
|
+
-------
|
79
|
+
|
80
|
+
(The MIT License)
|
81
|
+
|
82
|
+
Copyright (c) 2013 Shugo Maeda
|
83
|
+
|
84
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
85
|
+
a copy of this software and associated documentation files (the
|
86
|
+
'Software'), to deal in the Software without restriction, including
|
87
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
88
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
89
|
+
permit persons to whom the Software is furnished to do so, subject to
|
90
|
+
the following conditions:
|
91
|
+
|
92
|
+
The above copyright notice and this permission notice shall be
|
93
|
+
included in all copies or substantial portions of the Software.
|
94
|
+
|
95
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
96
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
97
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
98
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
99
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
100
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
101
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RaddDjur
|
2
|
+
module DSL
|
3
|
+
refine Object do
|
4
|
+
def bind(&block)
|
5
|
+
to_parser.bind(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def /(p2)
|
9
|
+
to_parser / p2
|
10
|
+
end
|
11
|
+
|
12
|
+
def optional
|
13
|
+
to_parser.optional
|
14
|
+
end
|
15
|
+
|
16
|
+
def zero_or_more
|
17
|
+
to_parser.zero_or_more
|
18
|
+
end
|
19
|
+
|
20
|
+
def one_or_more
|
21
|
+
to_parser.one_or_more
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_parser
|
25
|
+
raise TypeError, "#{self.class} can't be converted to a Parser"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
refine Symbol do
|
30
|
+
def to_parser
|
31
|
+
Grammar::Parser.new(&self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
refine String do
|
36
|
+
def to_parser
|
37
|
+
Grammar::Parsers.string(self)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
refine Range do
|
42
|
+
def to_parser
|
43
|
+
Grammar::Parsers.any_char.bind { |c|
|
44
|
+
if self.cover?(c)
|
45
|
+
Grammar::Parsers.ret(c)
|
46
|
+
else
|
47
|
+
Grammar::Parsers.fail
|
48
|
+
end
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require "immutable"
|
2
|
+
require "radd_djur/dsl"
|
3
|
+
|
4
|
+
using RaddDjur::DSL
|
5
|
+
|
6
|
+
module RaddDjur
|
7
|
+
class Grammar
|
8
|
+
class Result
|
9
|
+
end
|
10
|
+
|
11
|
+
class Parsed < Result
|
12
|
+
attr_reader :value, :remainder
|
13
|
+
|
14
|
+
def initialize(value, remainder)
|
15
|
+
@value = value
|
16
|
+
@remainder = remainder
|
17
|
+
end
|
18
|
+
|
19
|
+
def succeeded?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
NO_PARSE = Result.new
|
25
|
+
|
26
|
+
def NO_PARSE.succeeded?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
class ParseError < StandardError; end
|
31
|
+
|
32
|
+
class Derivs
|
33
|
+
include Immutable
|
34
|
+
|
35
|
+
def initialize(grammar, str)
|
36
|
+
@grammar = grammar
|
37
|
+
@str = str
|
38
|
+
@memo = {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def char
|
42
|
+
@memo[:char] ||=
|
43
|
+
Promise.delay {
|
44
|
+
if @str.empty?
|
45
|
+
NO_PARSE
|
46
|
+
else
|
47
|
+
Parsed.new(@str[0], Derivs.new(@grammar, @str[1..-1]))
|
48
|
+
end
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(mid, *args)
|
53
|
+
@memo[mid] ||= @grammar.parser(mid).parse(self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module Parsers
|
58
|
+
include Immutable
|
59
|
+
|
60
|
+
module_function
|
61
|
+
|
62
|
+
def ret(value)
|
63
|
+
Parser.new { |d|
|
64
|
+
Promise.eager(Parsed.new(value, d))
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def fail
|
69
|
+
Parser.new { |d|
|
70
|
+
Promise.eager(NO_PARSE)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def any_char
|
75
|
+
Parser.new(&:char)
|
76
|
+
end
|
77
|
+
|
78
|
+
def char(ch)
|
79
|
+
any_char.bind { |c|
|
80
|
+
if c == ch
|
81
|
+
ret c
|
82
|
+
else
|
83
|
+
fail
|
84
|
+
end
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def string(str)
|
89
|
+
if str.empty?
|
90
|
+
ret ""
|
91
|
+
else
|
92
|
+
char(str[0]).bind { |c|
|
93
|
+
string(str[1..-1]).bind { |s|
|
94
|
+
ret c + s
|
95
|
+
}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Parser
|
102
|
+
include Immutable
|
103
|
+
include Parsers
|
104
|
+
|
105
|
+
def initialize(&block)
|
106
|
+
@block = block
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_parser
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def parse(d)
|
114
|
+
@block.call(d)
|
115
|
+
end
|
116
|
+
|
117
|
+
def bind(&f2)
|
118
|
+
p1 = self
|
119
|
+
Parser.new { |d|
|
120
|
+
Promise.lazy {
|
121
|
+
result = p1.parse(d).force
|
122
|
+
if result.succeeded?
|
123
|
+
p2 = f2.call(result.value)
|
124
|
+
p2.to_parser.parse(result.remainder)
|
125
|
+
else
|
126
|
+
Promise.eager(NO_PARSE)
|
127
|
+
end
|
128
|
+
}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def /(p2)
|
133
|
+
p1 = self
|
134
|
+
Parser.new { |d|
|
135
|
+
Promise.lazy {
|
136
|
+
result = p1.parse(d).force
|
137
|
+
if result.succeeded?
|
138
|
+
Promise.eager(result)
|
139
|
+
else
|
140
|
+
p2.to_parser.parse(d)
|
141
|
+
end
|
142
|
+
}
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
def optional
|
147
|
+
self / ret(nil)
|
148
|
+
end
|
149
|
+
|
150
|
+
def zero_or_more
|
151
|
+
one_or_more / ret(List.empty)
|
152
|
+
end
|
153
|
+
|
154
|
+
def one_or_more
|
155
|
+
bind { |x|
|
156
|
+
zero_or_more.bind { |xs|
|
157
|
+
ret xs.cons(x)
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
include Parsers
|
164
|
+
|
165
|
+
def initialize(start_symbol, &block)
|
166
|
+
@parsers = {}
|
167
|
+
@start_symbol = start_symbol
|
168
|
+
instance_exec(&block) if block
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse(str)
|
172
|
+
result = Derivs.new(self, str).send(@start_symbol).force
|
173
|
+
if !result.succeeded?
|
174
|
+
raise ParseError, "parse error"
|
175
|
+
end
|
176
|
+
result.value
|
177
|
+
end
|
178
|
+
|
179
|
+
def define(sym, parser = yield)
|
180
|
+
@parsers[sym] = parser.to_parser
|
181
|
+
end
|
182
|
+
|
183
|
+
def parser(sym)
|
184
|
+
@parsers[sym]
|
185
|
+
end
|
186
|
+
|
187
|
+
def method_missing(mid, *args)
|
188
|
+
Parser.new(&mid)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
data/lib/radd_djur.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
def with_tailcall_optimization
|
2
|
+
if defined?(RubyVM)
|
3
|
+
old_option = RubyVM::InstructionSequence.compile_option
|
4
|
+
RubyVM::InstructionSequence.compile_option = {
|
5
|
+
trace_instruction: false,
|
6
|
+
tailcall_optimization: true
|
7
|
+
}
|
8
|
+
end
|
9
|
+
begin
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
if defined?(RubyVM)
|
13
|
+
RubyVM::InstructionSequence.compile_option = old_option
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
with_tailcall_optimization do
|
19
|
+
require "radd_djur/grammar"
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: radd_djur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shugo Maeda
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: immutable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.2
|
27
|
+
description:
|
28
|
+
email: shugo@ruby-lang.org
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/radd_djur.rb
|
35
|
+
- lib/radd_djur/dsl.rb
|
36
|
+
- lib/radd_djur/grammar.rb
|
37
|
+
homepage: http://github.com/shugo/radd_djur
|
38
|
+
licenses: []
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.5.1
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Packrat parser combinator library for Ruby
|
60
|
+
test_files: []
|
61
|
+
has_rdoc:
|