sexpr 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGELOG.md +24 -1
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +2 -0
  4. data/LICENCE.md +1 -1
  5. data/Manifest.txt +1 -0
  6. data/README.md +46 -31
  7. data/examples/bool_expr/bool_expr.citrus +72 -0
  8. data/examples/bool_expr/bool_expr.rb +86 -0
  9. data/examples/bool_expr/bool_expr.sexp.yml +23 -0
  10. data/lib/sexpr.rb +27 -20
  11. data/lib/sexpr/errors.rb +15 -0
  12. data/lib/sexpr/grammar.rb +21 -77
  13. data/lib/sexpr/grammar/matching.rb +56 -0
  14. data/lib/sexpr/grammar/options.rb +53 -0
  15. data/lib/sexpr/grammar/parsing.rb +20 -0
  16. data/lib/sexpr/grammar/tagging.rb +49 -0
  17. data/lib/sexpr/loader.rb +0 -1
  18. data/lib/sexpr/matcher.rb +15 -0
  19. data/lib/sexpr/matcher/alternative.rb +30 -0
  20. data/lib/sexpr/matcher/many.rb +54 -0
  21. data/lib/sexpr/matcher/reference.rb +32 -0
  22. data/lib/sexpr/matcher/rule.rb +31 -0
  23. data/lib/sexpr/matcher/sequence.rb +30 -0
  24. data/lib/sexpr/matcher/terminal.rb +37 -0
  25. data/lib/sexpr/node.rb +16 -0
  26. data/lib/sexpr/parser.rb +48 -0
  27. data/lib/sexpr/parser/citrus.rb +67 -0
  28. data/lib/sexpr/version.rb +2 -2
  29. data/sexpr.gemspec +1 -0
  30. data/sexpr.noespec +2 -1
  31. data/spec/grammar/matching/test_compile_rule.rb +23 -0
  32. data/spec/grammar/matching/test_compile_rule_defn.rb +103 -0
  33. data/spec/grammar/options/test_install_parser.rb +36 -0
  34. data/spec/grammar/options/test_install_path.rb +19 -0
  35. data/spec/grammar/options/test_install_root.rb +27 -0
  36. data/spec/grammar/tagging/test_looks_a_sexpr.rb +20 -0
  37. data/spec/grammar/tagging/test_mod2rulename.rb +19 -0
  38. data/spec/grammar/tagging/test_rule2modname.rb +19 -0
  39. data/spec/grammar/tagging/test_tag_sexpr.rb +28 -0
  40. data/spec/grammar/test_new.rb +15 -0
  41. data/spec/grammar/test_parse.rb +27 -18
  42. data/spec/grammar/test_sexpr.rb +53 -0
  43. data/spec/{alternative → matcher/alternative}/test_eat.rb +1 -1
  44. data/spec/{alternative → matcher/alternative}/test_match_q.rb +1 -1
  45. data/spec/{many → matcher/many}/test_eat.rb +1 -1
  46. data/spec/{many → matcher/many}/test_initialize.rb +1 -1
  47. data/spec/{many → matcher/many}/test_match_q.rb +1 -1
  48. data/spec/{reference → matcher/reference}/test_eat.rb +1 -1
  49. data/spec/{reference → matcher/reference}/test_match_q.rb +1 -1
  50. data/spec/{rule → matcher/rule}/test_eat.rb +1 -1
  51. data/spec/{rule → matcher/rule}/test_match_q.rb +1 -1
  52. data/spec/{sequence → matcher/sequence}/test_eat.rb +1 -1
  53. data/spec/{sequence → matcher/sequence}/test_match_q.rb +1 -1
  54. data/spec/{terminal → matcher/terminal}/test_eat.rb +1 -1
  55. data/spec/{terminal → matcher/terminal}/test_match_q.rb +1 -1
  56. data/spec/{terminal → matcher/terminal}/test_terminal_match.rb +1 -1
  57. data/spec/node/test_sexpr_body.rb +18 -0
  58. data/spec/node/test_sexpr_type.rb +14 -0
  59. data/spec/parser/citrus/test_new.rb +28 -0
  60. data/spec/parser/citrus/test_parse.rb +40 -0
  61. data/spec/parser/citrus/test_recognize.rb +18 -0
  62. data/spec/parser/citrus/test_registration.rb +20 -0
  63. data/spec/parser/citrus/test_to_sexpr.rb +16 -0
  64. data/spec/parser/test_factor.rb +17 -0
  65. data/spec/parser/test_input_text.rb +27 -0
  66. data/spec/spec_helper.rb +18 -1
  67. data/spec/test_load.rb +40 -13
  68. data/spec/test_readme_examples.rb +40 -30
  69. data/spec/test_sexpr.rb +1 -1
  70. metadata +118 -68
  71. data/lib/sexpr/alternative.rb +0 -28
  72. data/lib/sexpr/element.rb +0 -9
  73. data/lib/sexpr/many.rb +0 -52
  74. data/lib/sexpr/reference.rb +0 -30
  75. data/lib/sexpr/rule.rb +0 -29
  76. data/lib/sexpr/sequence.rb +0 -28
  77. data/lib/sexpr/terminal.rb +0 -35
  78. data/spec/bool_expr.yml +0 -19
  79. data/spec/grammar/test_compile_rule.rb +0 -25
  80. data/spec/grammar/test_compile_rule_defn.rb +0 -98
  81. data/spec/grammar/test_fetch.rb +0 -18
  82. data/spec/grammar/test_root.rb +0 -20
  83. data/spec/test_bool_expr.rb +0 -27
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ module Sexpr
3
+ describe Grammar, "sexpr" do
4
+
5
+ def sexpr(expr, opts = {})
6
+ @sexpr = Sexpr.load(:parser => parser).sexpr(expr, opts)
7
+ end
8
+
9
+ after{
10
+ @sexpr.should be_a(Sexpr) if @sexpr
11
+ }
12
+
13
+ context 'when no parser is set' do
14
+ let(:parser){ nil }
15
+
16
+ it 'silently returns a sexpr array' do
17
+ sexpr([:sexpr, "world"]).should eq([:sexpr, "world"])
18
+ end
19
+
20
+ it 'raises an error when parser is needed' do
21
+ lambda{
22
+ sexpr("Hello world")
23
+ }.should raise_error(NoParserError)
24
+ end
25
+
26
+ end
27
+
28
+ context 'when a parser is set' do
29
+ let(:parser){
30
+ Object.new.extend Module.new{
31
+ include Parser
32
+ def sexpr(s, options = {})
33
+ [options[:root] || :parsed, s]
34
+ end
35
+ }
36
+ }
37
+
38
+ it 'silently returns a sexpr array' do
39
+ sexpr([:sexpr, "world"]).should eq([:sexpr, "world"])
40
+ end
41
+
42
+ it 'delegates the call to the parser' do
43
+ sexpr("Hello world").should eq([:parsed, "Hello world"])
44
+ end
45
+
46
+ it 'passes options' do
47
+ sexpr("world", :root => :hello).should eq([:hello, "world"])
48
+ end
49
+
50
+ end # when a parser is set
51
+
52
+ end
53
+ end
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Alternative, "eat" do
4
4
 
5
5
  let(:alt1){ Terminal.new(nil) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Alternative, "match?" do
4
4
 
5
5
  let(:alt1){ Terminal.new(nil) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Many, "eat" do
4
4
 
5
5
  let(:term){ Terminal.new(/^[a-z]+$/) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Many, "initialize" do
4
4
 
5
5
  it 'understands a single min' do
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Many do
4
4
 
5
5
  let(:term){ Terminal.new(/^[a-z]+$/) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Reference, "eat" do
4
4
 
5
5
  let(:grammar){ {:hello => Terminal.new(/^[a-z]+$/)} }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Reference, "match?" do
4
4
 
5
5
  let(:grammar){ {:hello => Terminal.new(/^[a-z]+$/)} }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Rule, "eat" do
4
4
 
5
5
  let(:defn){ Sequence.new [Terminal.new(/^[a-z]+$/)] }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Rule, 'match?' do
4
4
 
5
5
  let(:defn){ Sequence.new [Terminal.new(/^[a-z]+$/)] }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Sequence, "eat" do
4
4
 
5
5
  let(:alt1){ Terminal.new(nil) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Sequence, 'match?' do
4
4
 
5
5
  let(:alt1){ Terminal.new(nil) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Terminal, "eat" do
4
4
 
5
5
  let(:rule){ Terminal.new(/^[a-z]+$/) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Terminal, "match?" do
4
4
 
5
5
  let(:rule){ Terminal.new(arg) }
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- module Sexpr
2
+ module Sexpr::Matcher
3
3
  describe Terminal, "terminal_match?" do
4
4
 
5
5
  before do
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ module Sexpr
3
+ describe Node, "sexpr_body" do
4
+
5
+ it 'returns the head' do
6
+ sexpr([:lit, true]).sexpr_body.should eq([true])
7
+ end
8
+
9
+ it 'returns an empty array if no body' do
10
+ sexpr([:lit]).sexpr_body.should eq([])
11
+ end
12
+
13
+ it 'is aliased as sexp_body' do
14
+ sexpr([:lit, true]).sexp_body.should eq([true])
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ module Sexpr
3
+ describe Node, "sexpr_type" do
4
+
5
+ it 'returns the head' do
6
+ sexpr([:lit, true]).sexpr_type.should eq(:lit)
7
+ end
8
+
9
+ it 'is aliased as sexp_type' do
10
+ sexpr([:lit, true]).sexp_type.should eq(:lit)
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ module Sexpr::Parser
3
+ describe Citrus, "new" do
4
+
5
+ it 'factors a Parser instance when a Citrus::Grammar' do
6
+ p = Citrus.new(bool_expr_parser)
7
+ p.should be_a(Citrus)
8
+ p.parser.should eq(bool_expr_parser)
9
+ end
10
+
11
+ it 'factors a Parser instance when a Path to a .citrus file' do
12
+ p = Citrus.new(fixtures_path/"bool_expr.citrus")
13
+ p.should be_a(Citrus)
14
+ p.parser.should eq(bool_expr_parser)
15
+ end
16
+
17
+ it 'factors a Parser instance when a Path to a .citrus file' do
18
+ p = Citrus.new (fixtures_path/"bool_expr.citrus").to_s
19
+ p.should be_a(Citrus)
20
+ p.parser.should eq(bool_expr_parser)
21
+ end
22
+
23
+ it 'passes the options' do
24
+ Citrus.new(bool_expr_parser, {:hello => "World"}).options[:hello].should eq("World")
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ module Sexpr::Parser
3
+ describe Citrus, "parse" do
4
+
5
+ let(:parser){ Citrus.new(bool_expr_parser) }
6
+
7
+ it 'delegates the call to the Citrus parser' do
8
+ parser.parse("true").should be_a(::Citrus::Match)
9
+ end
10
+
11
+ it 'raises a Citrus::ParserError when parsing fails' do
12
+ lambda{
13
+ parser.parse("bl and or")
14
+ }.should raise_error(::Citrus::ParseError)
15
+ end
16
+
17
+ it 'recognizes the :root option' do
18
+ parser.parse("true", :root => :bool_lit).should be_a(::Citrus::Match)
19
+ parser.parse("x", :root => :var_ref).should be_a(::Citrus::Match)
20
+ lambda{
21
+ parser.parse("x", :root => :bool_lit)
22
+ }.should raise_error(::Citrus::ParseError)
23
+ end
24
+
25
+ it 'recognizes the :consume option' do
26
+ lambda{
27
+ parser.parse("true or")
28
+ }.should raise_error(::Citrus::ParseError)
29
+ parser.parse("true or", :consume => false).should eq("true")
30
+ end
31
+
32
+ it 'can parse from a Path' do
33
+ Path.tmpfile do |tmp|
34
+ tmp.write "x and y"
35
+ parser.parse(tmp).should be_a(::Citrus::Match)
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ module Sexpr::Parser
3
+ describe Citrus, "recognize?" do
4
+
5
+ it 'returns true on Citrus parsers' do
6
+ Citrus.should be_recognize(bool_expr_parser)
7
+ end
8
+
9
+ it 'returns true on a Citrus Path' do
10
+ Citrus.should be_recognize(fixtures_path/"bool_expr.citrus")
11
+ end
12
+
13
+ it 'returns false when not recognized' do
14
+ (Citrus.recognizes?(self)).should be_false
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ module Sexpr::Parser
3
+ describe Citrus do
4
+
5
+ it 'should be registered' do
6
+ Sexpr::Parser.find_parser_class(bool_expr_parser).should eq(Citrus)
7
+ end
8
+
9
+ it 'should be served for a Citrus grammar' do
10
+ Sexpr::Parser.factor(bool_expr_parser).should be_a(Citrus)
11
+ end
12
+
13
+ it 'should accept factor options, defaulting to defaults' do
14
+ cit = Sexpr::Parser.factor(bool_expr_parser, {:hello => "World"})
15
+ cit.options[:hello].should eq("World")
16
+ cit.options.should have_key(:from_match_to_sexpr)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ module Sexpr::Parser
3
+ describe Citrus, "sexpr" do
4
+
5
+ it 'calls value by default' do
6
+ parser = Citrus.new(bool_expr_parser)
7
+ parser.sexpr("true").should eq([:bool_lit, true])
8
+ end
9
+
10
+ it 'delegates to from_match_to_sexpr if specified' do
11
+ parser = Citrus.new(bool_expr_parser, :from_match_to_sexpr => lambda{|x| 12})
12
+ parser.sexpr("true").should eq(12)
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ module Sexpr
3
+ describe Parser, "factor" do
4
+
5
+ it 'silently returns when given a Parser' do
6
+ p = Object.new.extend(Parser)
7
+ Parser.factor(p).should eq(p)
8
+ end
9
+
10
+ it 'raises a UnrecognizedParserError when no class can be found' do
11
+ lambda{
12
+ Parser.factor(self)
13
+ }.should raise_error(UnrecognizedParserError)
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module Sexpr
3
+ describe Parser, "input_text" do
4
+ include Parser
5
+
6
+ it 'recognizes a string' do
7
+ input_text("Hello world").should eq("Hello world")
8
+ end
9
+
10
+ it 'accepts a path' do
11
+ input_text(Path.here).should eq(File.read(__FILE__))
12
+ end
13
+
14
+ it 'accepts an IO' do
15
+ File.open(__FILE__, 'r') do |io|
16
+ input_text(io).should eq(File.read(__FILE__))
17
+ end
18
+ end
19
+
20
+ it 'raises a InvalidParseSourceError when invalid source' do
21
+ lambda{
22
+ input_text(self)
23
+ }.should raise_error(InvalidParseSourceError)
24
+ end
25
+
26
+ end
27
+ end
@@ -1,3 +1,20 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
1
  require 'epath'
2
+ root = Path.backfind('.[lib]')
3
+
4
+ require 'citrus'
5
+
6
+ $LOAD_PATH.unshift (root/"lib").to_s
3
7
  require 'sexpr'
8
+ require (root/"examples/bool_expr/bool_expr").to_s
9
+
10
+ def fixtures_path
11
+ Path.dir/"../examples/bool_expr"
12
+ end
13
+
14
+ def bool_expr_parser
15
+ BoolExpr.parser.parser
16
+ end
17
+
18
+ def sexpr(*args)
19
+ Sexpr.sexpr(*args)
20
+ end
@@ -2,31 +2,58 @@ require 'spec_helper'
2
2
  module Sexpr
3
3
  describe Sexpr, "load" do
4
4
 
5
- let(:grammar){ Sexpr.load(arg) }
5
+ subject{ Sexpr.load(arg) }
6
6
 
7
- context "on grammar.yml" do
8
- let(:arg){ Path.dir/"grammar.yml" }
7
+ after do
8
+ subject.should be_a(Grammar)
9
+ subject[:bool_expr].should be_a(Matcher::Alternative)
10
+ end
11
+
12
+ context "on a YAML path" do
13
+ let(:arg){ fixtures_path/"bool_expr.sexp.yml" }
14
+
15
+ it 'sets the path on the grammar' do
16
+ subject.path.should eq(arg)
17
+ end
18
+
19
+ it 'finds the parser with the relative path' do
20
+ subject.parser.should be_a(Parser::Citrus)
21
+ end
9
22
 
10
- it 'returns a Grammar' do
11
- grammar.should be_a(Grammar)
23
+ end # grammar.yml
24
+
25
+ context "on a YAML path as a String" do
26
+ let(:arg){ (fixtures_path/"bool_expr.sexp.yml").to_s }
27
+
28
+ it 'sets the path on the grammar' do
29
+ subject.path.should eq(arg)
30
+ end
31
+
32
+ it 'finds the parser with the relative path' do
33
+ subject.parser.should be_a(Parser::Citrus)
12
34
  end
13
35
 
14
36
  end # grammar.yml
15
37
 
16
- context 'with a Hash' do
17
- let(:arg){ {:hello => /[a-z]+/} }
38
+ context 'with an explicit Hash' do
39
+ let(:arg){ {:rules => {:bool_expr => [true, false]}} }
18
40
 
19
- it 'returns a Grammar' do
20
- grammar.should be_a(Grammar)
41
+ it 'does not set a path' do
42
+ subject.path.should be_nil
21
43
  end
22
44
 
23
45
  end
24
46
 
25
47
  context 'with a String' do
26
- let(:arg){ "hello: true" }
27
-
28
- it 'returns a Grammar' do
29
- grammar.should be_a(Grammar)
48
+ let(:arg){
49
+ <<-YAML
50
+ rules:
51
+ bool_expr: [true, false]
52
+ YAML
53
+ }
54
+
55
+ it 'does not set a path' do
56
+ subject.path.should be_nil
30
57
  end
31
58
 
32
59
  end