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.
Files changed (50) hide show
  1. data/Rakefile +46 -0
  2. data/lib/ghost_wheel.rb +28 -0
  3. data/lib/ghost_wheel/build_parser.rb +11 -0
  4. data/lib/ghost_wheel/errors.rb +9 -0
  5. data/lib/ghost_wheel/expression.rb +22 -0
  6. data/lib/ghost_wheel/expression/alternation.rb +25 -0
  7. data/lib/ghost_wheel/expression/empty.rb +15 -0
  8. data/lib/ghost_wheel/expression/end_of_file.rb +19 -0
  9. data/lib/ghost_wheel/expression/literal.rb +31 -0
  10. data/lib/ghost_wheel/expression/look_ahead.rb +33 -0
  11. data/lib/ghost_wheel/expression/optional.rb +26 -0
  12. data/lib/ghost_wheel/expression/query.rb +32 -0
  13. data/lib/ghost_wheel/expression/repetition.rb +44 -0
  14. data/lib/ghost_wheel/expression/rule.rb +24 -0
  15. data/lib/ghost_wheel/expression/sequence.rb +30 -0
  16. data/lib/ghost_wheel/expression/transform.rb +30 -0
  17. data/lib/ghost_wheel/parse_results.rb +9 -0
  18. data/lib/ghost_wheel/parser.rb +71 -0
  19. data/lib/ghost_wheel/parser_builder/ghost_wheel.rb +100 -0
  20. data/lib/ghost_wheel/parser_builder/ruby.rb +175 -0
  21. data/lib/ghost_wheel/scanner.rb +42 -0
  22. data/setup.rb +1360 -0
  23. data/test/dsl/tc_build_parser.rb +29 -0
  24. data/test/dsl/tc_ghost_wheel_dsl.rb +143 -0
  25. data/test/dsl/tc_ruby_dsl.rb +227 -0
  26. data/test/example/tc_json_core.rb +95 -0
  27. data/test/example/tc_json_ghost_wheel.rb +48 -0
  28. data/test/example/tc_json_ruby.rb +81 -0
  29. data/test/helpers/ghost_wheel_namespace.rb +24 -0
  30. data/test/helpers/json_tests.rb +63 -0
  31. data/test/helpers/parse_helpers.rb +83 -0
  32. data/test/parser/tc_alternation_expression.rb +27 -0
  33. data/test/parser/tc_empty_expression.rb +15 -0
  34. data/test/parser/tc_end_of_file_expression.rb +27 -0
  35. data/test/parser/tc_literal_expression.rb +55 -0
  36. data/test/parser/tc_look_ahead_expression.rb +41 -0
  37. data/test/parser/tc_memoization.rb +31 -0
  38. data/test/parser/tc_optional_expression.rb +31 -0
  39. data/test/parser/tc_parser.rb +78 -0
  40. data/test/parser/tc_query_expression.rb +40 -0
  41. data/test/parser/tc_repetition_expression.rb +76 -0
  42. data/test/parser/tc_rule_expression.rb +32 -0
  43. data/test/parser/tc_scanning.rb +192 -0
  44. data/test/parser/tc_sequence_expression.rb +42 -0
  45. data/test/parser/tc_transform_expression.rb +77 -0
  46. data/test/ts_all.rb +7 -0
  47. data/test/ts_dsl.rb +8 -0
  48. data/test/ts_example.rb +7 -0
  49. data/test/ts_parser.rb +19 -0
  50. metadata +94 -0
@@ -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
@@ -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,11 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ module GhostWheel
4
+ def self.build_parser(grammar = nil, &parser_init)
5
+ if grammar
6
+ ParserBuilder::GhostWheel.new(grammar).to_parser
7
+ else
8
+ ParserBuilder::Ruby.new(&parser_init).to_parser
9
+ end
10
+ end
11
+ 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,15 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ require "singleton"
4
+
5
+ module GhostWheel
6
+ class Expression
7
+ class Empty < Expression
8
+ include Singleton
9
+
10
+ def uncached_parse(not_used, cache)
11
+ EmptyParseResult.instance
12
+ end
13
+ end
14
+ end
15
+ 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