ghostwheel 0.0.1
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/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
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
require "rake/gempackagetask"
|
3
|
+
|
4
|
+
DIR = File.dirname(__FILE__)
|
5
|
+
LIB = File.join(DIR, "lib", "ghost_wheel.rb")
|
6
|
+
VERSION = File.read(LIB)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
|
7
|
+
|
8
|
+
task :default => [:test]
|
9
|
+
|
10
|
+
Rake::TestTask.new do |test|
|
11
|
+
test.libs << "test"
|
12
|
+
test.test_files = %w[test/ts_all.rb]
|
13
|
+
test.verbose = true
|
14
|
+
end
|
15
|
+
|
16
|
+
GEM_SPEC = Gem::Specification.new do |spec|
|
17
|
+
spec.name = "ghostwheel"
|
18
|
+
spec.version = VERSION
|
19
|
+
spec.platform = Gem::Platform::RUBY
|
20
|
+
spec.summary = "Ghost Wheel is a pure Ruby parser generator."
|
21
|
+
spec.files = Dir.glob("{lib,test}/**/*.rb").
|
22
|
+
delete_if { |item| item.include?(".svn") } +
|
23
|
+
["Rakefile", "setup.rb"]
|
24
|
+
|
25
|
+
spec.test_suite_file = "test/ts_all.rb"
|
26
|
+
# spec.has_rdoc = true
|
27
|
+
# spec.extra_rdoc_files = %w{README INSTALL TODO CHANGELOG LICENSE}
|
28
|
+
# spec.rdoc_options << '--title' << 'Ghost Wheel Documentation' <<
|
29
|
+
# '--main' << 'README'
|
30
|
+
|
31
|
+
spec.require_path = 'lib'
|
32
|
+
|
33
|
+
spec.author = "James Edward Gray II"
|
34
|
+
spec.email = "james@grayproductions.net"
|
35
|
+
spec.rubyforge_project = "ghostwheel"
|
36
|
+
spec.homepage = "http://ghostwheel.rubyforge.org"
|
37
|
+
spec.description = <<END_DESC
|
38
|
+
Ghost Wheel is a packrat parser generator handling scanning and parsing of
|
39
|
+
context-free grammars. Parsers can be built using an EBNF-like syntax or a with
|
40
|
+
a Ruby DSL.
|
41
|
+
END_DESC
|
42
|
+
end
|
43
|
+
Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
|
44
|
+
pkg.need_zip = true
|
45
|
+
pkg.need_tar = true
|
46
|
+
end
|
data/lib/ghost_wheel.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "ghost_wheel/errors"
|
4
|
+
require "ghost_wheel/parse_results"
|
5
|
+
|
6
|
+
require "ghost_wheel/scanner"
|
7
|
+
require "ghost_wheel/parser"
|
8
|
+
|
9
|
+
require "ghost_wheel/expression"
|
10
|
+
require "ghost_wheel/expression/literal"
|
11
|
+
require "ghost_wheel/expression/sequence"
|
12
|
+
require "ghost_wheel/expression/alternation"
|
13
|
+
require "ghost_wheel/expression/empty"
|
14
|
+
require "ghost_wheel/expression/optional"
|
15
|
+
require "ghost_wheel/expression/repetition"
|
16
|
+
require "ghost_wheel/expression/look_ahead"
|
17
|
+
require "ghost_wheel/expression/end_of_file"
|
18
|
+
require "ghost_wheel/expression/query"
|
19
|
+
require "ghost_wheel/expression/transform"
|
20
|
+
require "ghost_wheel/expression/rule"
|
21
|
+
|
22
|
+
require "ghost_wheel/build_parser"
|
23
|
+
require "ghost_wheel/parser_builder/ruby"
|
24
|
+
require "ghost_wheel/parser_builder/ghost_wheel"
|
25
|
+
|
26
|
+
module GhostWheel
|
27
|
+
VERSION = "0.0.1"
|
28
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class StackUnderflowError < RuntimeError; end
|
5
|
+
class EmptyRuleError < RuntimeError; end
|
6
|
+
class ParseError < RuntimeError; end
|
7
|
+
class EmptyParseError < ParseError; end
|
8
|
+
class FailedParseError < ParseError; end
|
9
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
def parse(scanner, cache = Hash.new)
|
6
|
+
cache_key = "#{object_id}:#{scanner.pos}"
|
7
|
+
cache_hit = cache[cache_key]
|
8
|
+
if cache_hit.nil?
|
9
|
+
result = uncached_parse(scanner, cache)
|
10
|
+
cache[cache_key] = [result, scanner.pos]
|
11
|
+
result
|
12
|
+
else
|
13
|
+
scanner.pos = cache_hit.last
|
14
|
+
cache_hit.first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
self.class == other.class
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Alternation < Expression
|
6
|
+
def initialize(*expressions)
|
7
|
+
@expressions = expressions
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :expressions
|
11
|
+
|
12
|
+
def uncached_parse(scanner, cache)
|
13
|
+
@expressions.each do |expression|
|
14
|
+
result = expression.parse(scanner, cache)
|
15
|
+
return result unless result.is_a? FailedParseResult
|
16
|
+
end
|
17
|
+
FailedParseResult.instance
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
super and @expressions == other.expressions
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module GhostWheel
|
6
|
+
class Expression
|
7
|
+
class EndOfFile < Expression
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
def uncached_parse(scanner, cache)
|
11
|
+
if scanner.eos?
|
12
|
+
EmptyParseResult.instance
|
13
|
+
else
|
14
|
+
FailedParseResult.instance
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Literal < Expression
|
6
|
+
def initialize(literal, skip = false)
|
7
|
+
@literal = case literal
|
8
|
+
when Regexp then literal
|
9
|
+
when Fixnum then /#{Regexp.escape(literal.chr)}/
|
10
|
+
else /#{Regexp.escape(literal.to_s)}/
|
11
|
+
end
|
12
|
+
@skip = skip
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :literal, :skip
|
16
|
+
alias_method :skip?, :skip
|
17
|
+
|
18
|
+
def uncached_parse(scanner, cache)
|
19
|
+
if scanner.scan(@literal)
|
20
|
+
@skip ? EmptyParseResult.instance : ParseResult.new(scanner.matched)
|
21
|
+
else
|
22
|
+
FailedParseResult.instance
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
super and @literal.to_s == other.literal.to_s and @skip == other.skip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class LookAhead < Expression
|
6
|
+
def initialize(expression, negate = false)
|
7
|
+
@expression = expression
|
8
|
+
@negate = negate
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :expression, :negate
|
12
|
+
alias_method :negate?, :negate
|
13
|
+
|
14
|
+
def uncached_parse(scanner, cache)
|
15
|
+
matched = nil
|
16
|
+
scanner.transaction do
|
17
|
+
matched = @expression.parse(scanner, cache)
|
18
|
+
scanner.abort
|
19
|
+
end
|
20
|
+
|
21
|
+
if matched.is_a? FailedParseResult
|
22
|
+
@negate ? EmptyParseResult.instance : matched
|
23
|
+
else
|
24
|
+
@negate ? FailedParseResult.instance : EmptyParseResult.instance
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
super and @expression == other.expression and @negate == other.negate
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Optional < Expression
|
6
|
+
def initialize(expression)
|
7
|
+
@expression = expression
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :expression
|
11
|
+
|
12
|
+
def uncached_parse(scanner, cache)
|
13
|
+
result = @expression.parse(scanner, cache)
|
14
|
+
if result.is_a? FailedParseResult
|
15
|
+
EmptyParseResult.instance
|
16
|
+
else
|
17
|
+
result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
super and @expression == other.expression
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Query < Expression
|
6
|
+
def initialize(expression, &query)
|
7
|
+
@expression = expression
|
8
|
+
@query = query
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :expression, :query
|
12
|
+
|
13
|
+
def uncached_parse(scanner, cache)
|
14
|
+
result = @expression.parse(scanner, cache)
|
15
|
+
if result.is_a? FailedParseResult
|
16
|
+
result
|
17
|
+
else
|
18
|
+
value = result.is_a?(ParseResult) ? result.value : result
|
19
|
+
if @query[value]
|
20
|
+
result
|
21
|
+
else
|
22
|
+
FailedParseResult.instance
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
super and @expression == other.expression and @query == other.query
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Repetition < Expression
|
6
|
+
def initialize(expression, minimum_count = 0, maximum_count = 1.0 / 0.0)
|
7
|
+
@expression = expression
|
8
|
+
@minimum_count = minimum_count
|
9
|
+
@maximum_count = maximum_count
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :expression, :minimum_count, :maximum_count
|
13
|
+
|
14
|
+
def uncached_parse(scanner, cache)
|
15
|
+
scanner.transaction do
|
16
|
+
parsed = ParseResult.new(Array.new)
|
17
|
+
while parsed.value.size < @maximum_count
|
18
|
+
case result = @expression.parse(scanner, cache)
|
19
|
+
when FailedParseResult then break
|
20
|
+
when ParseResult then parsed.value << result.value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
if parsed.value.size >= @minimum_count
|
25
|
+
if parsed.value.empty?
|
26
|
+
EmptyParseResult.instance
|
27
|
+
else
|
28
|
+
parsed
|
29
|
+
end
|
30
|
+
else
|
31
|
+
scanner.abort(FailedParseResult.instance)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def ==(other)
|
37
|
+
super and
|
38
|
+
@expression == other.expression and
|
39
|
+
@minimum_count == other.minimum_count and
|
40
|
+
@maximum_count == other.maximum_count
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Rule < Expression
|
6
|
+
def initialize(expression = nil)
|
7
|
+
@expression = expression
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :expression
|
11
|
+
|
12
|
+
def uncached_parse(scanner, cache)
|
13
|
+
if @expression.nil?
|
14
|
+
raise EmptyRuleError, "You failed to set an expression for this rule."
|
15
|
+
end
|
16
|
+
@expression.parse(scanner, cache)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
super and @expression == other.expression
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Sequence < Expression
|
6
|
+
def initialize(*expressions)
|
7
|
+
@expressions = expressions
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :expressions
|
11
|
+
|
12
|
+
def uncached_parse(scanner, cache)
|
13
|
+
scanner.transaction do
|
14
|
+
parsed = ParseResult.new(Array.new)
|
15
|
+
@expressions.each do |expression|
|
16
|
+
case result = expression.parse(scanner, cache)
|
17
|
+
when FailedParseResult then scanner.abort(result)
|
18
|
+
when ParseResult then parsed.value << result.value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
parsed
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
super and @expressions == other.expressions
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
|
3
|
+
module GhostWheel
|
4
|
+
class Expression
|
5
|
+
class Transform < Expression
|
6
|
+
def initialize(expression, &transformer)
|
7
|
+
@expression = expression
|
8
|
+
@transformer = transformer
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :expression, :transformer
|
12
|
+
|
13
|
+
def uncached_parse(scanner, cache)
|
14
|
+
result = @expression.parse(scanner, cache)
|
15
|
+
if result.is_a? FailedParseResult
|
16
|
+
result
|
17
|
+
else
|
18
|
+
value = result.is_a?(ParseResult) ? result.value : result
|
19
|
+
ParseResult.new(@transformer[value])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
super and
|
25
|
+
@expression == other.expression and
|
26
|
+
@transformer == other.transformer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|