sexpr 0.2.0 → 0.3.0
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/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
|