sexpr 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +24 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/LICENCE.md +1 -1
- data/Manifest.txt +1 -0
- data/README.md +46 -31
- data/examples/bool_expr/bool_expr.citrus +72 -0
- data/examples/bool_expr/bool_expr.rb +86 -0
- data/examples/bool_expr/bool_expr.sexp.yml +23 -0
- data/lib/sexpr.rb +27 -20
- data/lib/sexpr/errors.rb +15 -0
- data/lib/sexpr/grammar.rb +21 -77
- data/lib/sexpr/grammar/matching.rb +56 -0
- data/lib/sexpr/grammar/options.rb +53 -0
- data/lib/sexpr/grammar/parsing.rb +20 -0
- data/lib/sexpr/grammar/tagging.rb +49 -0
- data/lib/sexpr/loader.rb +0 -1
- data/lib/sexpr/matcher.rb +15 -0
- data/lib/sexpr/matcher/alternative.rb +30 -0
- data/lib/sexpr/matcher/many.rb +54 -0
- data/lib/sexpr/matcher/reference.rb +32 -0
- data/lib/sexpr/matcher/rule.rb +31 -0
- data/lib/sexpr/matcher/sequence.rb +30 -0
- data/lib/sexpr/matcher/terminal.rb +37 -0
- data/lib/sexpr/node.rb +16 -0
- data/lib/sexpr/parser.rb +48 -0
- data/lib/sexpr/parser/citrus.rb +67 -0
- data/lib/sexpr/version.rb +2 -2
- data/sexpr.gemspec +1 -0
- data/sexpr.noespec +2 -1
- data/spec/grammar/matching/test_compile_rule.rb +23 -0
- data/spec/grammar/matching/test_compile_rule_defn.rb +103 -0
- data/spec/grammar/options/test_install_parser.rb +36 -0
- data/spec/grammar/options/test_install_path.rb +19 -0
- data/spec/grammar/options/test_install_root.rb +27 -0
- data/spec/grammar/tagging/test_looks_a_sexpr.rb +20 -0
- data/spec/grammar/tagging/test_mod2rulename.rb +19 -0
- data/spec/grammar/tagging/test_rule2modname.rb +19 -0
- data/spec/grammar/tagging/test_tag_sexpr.rb +28 -0
- data/spec/grammar/test_new.rb +15 -0
- data/spec/grammar/test_parse.rb +27 -18
- data/spec/grammar/test_sexpr.rb +53 -0
- data/spec/{alternative → matcher/alternative}/test_eat.rb +1 -1
- data/spec/{alternative → matcher/alternative}/test_match_q.rb +1 -1
- data/spec/{many → matcher/many}/test_eat.rb +1 -1
- data/spec/{many → matcher/many}/test_initialize.rb +1 -1
- data/spec/{many → matcher/many}/test_match_q.rb +1 -1
- data/spec/{reference → matcher/reference}/test_eat.rb +1 -1
- data/spec/{reference → matcher/reference}/test_match_q.rb +1 -1
- data/spec/{rule → matcher/rule}/test_eat.rb +1 -1
- data/spec/{rule → matcher/rule}/test_match_q.rb +1 -1
- data/spec/{sequence → matcher/sequence}/test_eat.rb +1 -1
- data/spec/{sequence → matcher/sequence}/test_match_q.rb +1 -1
- data/spec/{terminal → matcher/terminal}/test_eat.rb +1 -1
- data/spec/{terminal → matcher/terminal}/test_match_q.rb +1 -1
- data/spec/{terminal → matcher/terminal}/test_terminal_match.rb +1 -1
- data/spec/node/test_sexpr_body.rb +18 -0
- data/spec/node/test_sexpr_type.rb +14 -0
- data/spec/parser/citrus/test_new.rb +28 -0
- data/spec/parser/citrus/test_parse.rb +40 -0
- data/spec/parser/citrus/test_recognize.rb +18 -0
- data/spec/parser/citrus/test_registration.rb +20 -0
- data/spec/parser/citrus/test_to_sexpr.rb +16 -0
- data/spec/parser/test_factor.rb +17 -0
- data/spec/parser/test_input_text.rb +27 -0
- data/spec/spec_helper.rb +18 -1
- data/spec/test_load.rb +40 -13
- data/spec/test_readme_examples.rb +40 -30
- data/spec/test_sexpr.rb +1 -1
- metadata +118 -68
- data/lib/sexpr/alternative.rb +0 -28
- data/lib/sexpr/element.rb +0 -9
- data/lib/sexpr/many.rb +0 -52
- data/lib/sexpr/reference.rb +0 -30
- data/lib/sexpr/rule.rb +0 -29
- data/lib/sexpr/sequence.rb +0 -28
- data/lib/sexpr/terminal.rb +0 -35
- data/spec/bool_expr.yml +0 -19
- data/spec/grammar/test_compile_rule.rb +0 -25
- data/spec/grammar/test_compile_rule_defn.rb +0 -98
- data/spec/grammar/test_fetch.rb +0 -18
- data/spec/grammar/test_root.rb +0 -20
- 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
|
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
data/spec/test_load.rb
CHANGED
@@ -2,31 +2,58 @@ require 'spec_helper'
|
|
2
2
|
module Sexpr
|
3
3
|
describe Sexpr, "load" do
|
4
4
|
|
5
|
-
|
5
|
+
subject{ Sexpr.load(arg) }
|
6
6
|
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
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
|
17
|
-
let(:arg){ {:
|
38
|
+
context 'with an explicit Hash' do
|
39
|
+
let(:arg){ {:rules => {:bool_expr => [true, false]}} }
|
18
40
|
|
19
|
-
it '
|
20
|
-
|
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){
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|