sexpr 0.5.1 → 0.6.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.
Files changed (89) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/Gemfile +3 -3
  3. data/Gemfile.lock +14 -14
  4. data/Rakefile +0 -4
  5. data/examples/bool_expr/bool_expr.rb +1 -95
  6. data/lib/sexpr/grammar/matching.rb +3 -3
  7. data/lib/sexpr/grammar/parsing.rb +1 -1
  8. data/lib/sexpr/grammar/tagging.rb +16 -13
  9. data/lib/sexpr/loader.rb +1 -0
  10. data/lib/sexpr/matcher.rb +3 -2
  11. data/lib/sexpr/matcher/many.rb +16 -1
  12. data/lib/sexpr/matcher/non_terminal.rb +31 -0
  13. data/lib/sexpr/matcher/rule.rb +3 -6
  14. data/lib/sexpr/node.rb +1 -1
  15. data/lib/sexpr/parser.rb +1 -1
  16. data/lib/sexpr/processor.rb +11 -1
  17. data/lib/sexpr/processor/helper.rb +1 -1
  18. data/lib/sexpr/processor/sexpr_coercions.rb +6 -14
  19. data/lib/sexpr/rewriter.rb +0 -8
  20. data/lib/sexpr/version.rb +3 -3
  21. data/sexpr.gemspec +3 -3
  22. data/sexpr.noespec +13 -6
  23. data/spec/integration/bool_expr/test_not_push.rb +38 -0
  24. data/spec/integration/bool_expr/test_parsing.rb +15 -0
  25. data/spec/integration/bool_expr/test_tagging.rb +25 -0
  26. data/spec/integration/bool_expr/test_validation.rb +15 -0
  27. data/spec/{test_readme_examples.rb → integration/test_readme_examples.rb} +0 -0
  28. data/spec/spec_helper.rb +1 -1
  29. data/spec/unit/grammar/matching/test_compile_rule.rb +55 -0
  30. data/spec/{grammar → unit/grammar}/matching/test_compile_rule_defn.rb +0 -0
  31. data/spec/unit/grammar/matching/test_compile_rules.rb +45 -0
  32. data/spec/{grammar → unit/grammar}/options/test_install_parser.rb +0 -0
  33. data/spec/{grammar → unit/grammar}/options/test_install_path.rb +0 -0
  34. data/spec/{grammar → unit/grammar}/options/test_install_root.rb +0 -0
  35. data/spec/{grammar → unit/grammar}/tagging/test_looks_a_sexpr.rb +0 -0
  36. data/spec/{grammar → unit/grammar}/tagging/test_mod2rulename.rb +0 -0
  37. data/spec/{grammar → unit/grammar}/tagging/test_rule2modname.rb +0 -0
  38. data/spec/{grammar → unit/grammar}/tagging/test_tag_sexpr.rb +0 -0
  39. data/spec/unit/grammar/tagging/test_tagging_module_for.rb +72 -0
  40. data/spec/{grammar → unit/grammar}/test_new.rb +0 -0
  41. data/spec/{grammar → unit/grammar}/test_parse.rb +0 -0
  42. data/spec/{grammar → unit/grammar}/test_sexpr.rb +0 -0
  43. data/spec/{matcher → unit/matcher}/alternative/test_eat.rb +1 -1
  44. data/spec/{matcher → unit/matcher}/alternative/test_match_q.rb +0 -0
  45. data/spec/{matcher → unit/matcher}/many/test_eat.rb +0 -0
  46. data/spec/{matcher → unit/matcher}/many/test_initialize.rb +0 -0
  47. data/spec/{matcher → unit/matcher}/many/test_match_q.rb +0 -0
  48. data/spec/unit/matcher/non_terminal/test_eat.rb +31 -0
  49. data/spec/unit/matcher/non_terminal/test_match_q.rb +32 -0
  50. data/spec/{matcher → unit/matcher}/reference/test_eat.rb +0 -0
  51. data/spec/{matcher → unit/matcher}/reference/test_match_q.rb +0 -0
  52. data/spec/unit/matcher/rule/test_eat.rb +17 -0
  53. data/spec/unit/matcher/rule/test_match_q.rb +17 -0
  54. data/spec/{matcher → unit/matcher}/sequence/test_eat.rb +0 -0
  55. data/spec/{matcher → unit/matcher}/sequence/test_match_q.rb +0 -0
  56. data/spec/{matcher → unit/matcher}/terminal/test_eat.rb +1 -1
  57. data/spec/{matcher → unit/matcher}/terminal/test_match_q.rb +0 -0
  58. data/spec/{matcher → unit/matcher}/terminal/test_terminal_match.rb +0 -0
  59. data/spec/{node → unit/node}/test_sexpr_body.rb +0 -0
  60. data/spec/{node → unit/node}/test_sexpr_copy.rb +0 -0
  61. data/spec/{node → unit/node}/test_sexpr_type.rb +0 -0
  62. data/spec/{node → unit/node}/test_tracking_markers.rb +0 -0
  63. data/spec/{parser → unit/parser}/citrus/test_new.rb +0 -0
  64. data/spec/{parser → unit/parser}/citrus/test_parse.rb +0 -0
  65. data/spec/{parser → unit/parser}/citrus/test_recognize.rb +0 -0
  66. data/spec/{parser → unit/parser}/citrus/test_registration.rb +0 -0
  67. data/spec/{parser → unit/parser}/citrus/test_to_sexpr.rb +0 -0
  68. data/spec/{parser → unit/parser}/test_factor.rb +0 -0
  69. data/spec/{parser → unit/parser}/test_input_text.rb +0 -0
  70. data/spec/{processor → unit/processor}/helper/test_call.rb +0 -0
  71. data/spec/{processor → unit/processor}/test_apply.rb +0 -0
  72. data/spec/{processor → unit/processor}/test_build_helper_chain.rb +0 -0
  73. data/spec/{processor → unit/processor}/test_call.rb +0 -0
  74. data/spec/unit/processor/test_grammar.rb +56 -0
  75. data/spec/{processor → unit/processor}/test_helper.rb +0 -0
  76. data/spec/{processor → unit/processor}/test_sexpr_coercions.rb +6 -2
  77. data/spec/{processor → unit/processor}/test_use.rb +0 -0
  78. data/spec/{rewriter → unit/rewriter}/test_copy_and_apply.rb +0 -0
  79. data/spec/{test_load.rb → unit/test_load.rb} +3 -2
  80. data/spec/{test_rewriter.rb → unit/test_rewriter.rb} +0 -0
  81. data/spec/{test_sexpr.rb → unit/test_sexpr.rb} +1 -1
  82. data/tasks/gem.rake +9 -4
  83. data/tasks/test.rake +26 -0
  84. metadata +163 -117
  85. data/spec/grammar/matching/test_compile_rule.rb +0 -23
  86. data/spec/matcher/rule/test_eat.rb +0 -21
  87. data/spec/matcher/rule/test_match_q.rb +0 -24
  88. data/tasks/spec_test.rake +0 -71
  89. data/tasks/unit_test.rake +0 -76
@@ -1,3 +1,19 @@
1
+ # 0.6.0 / 2013-09-26
2
+
3
+ * Major enhancements (possibly breaking changes)
4
+
5
+ * All grammar rules are now proper Matcher::Rule instances. A NonTerminal
6
+ matcher is added and is used for non-terminal rule productions and matching
7
+ (all but Terminal and Alternative).
8
+ * `Rewriter.sexpr_grammar` and `Rewriter#sexpr_grammar` have been removed.
9
+ `Processor.grammar` and `Processor#grammar` are provided instead.
10
+
11
+ * Minor enhancements
12
+
13
+ * Added `Grammar#tagging_module_for(rulename)` that returns the user-defined
14
+ module used to tag a given grammar rule production.
15
+ * `Grammar#looks_a_sexpr?(arg)` became public.
16
+
1
17
  # 0.5.1 / 2012-03-13
2
18
 
3
19
  * Minor enhancements
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  group :development do
4
- gem "epath", "~> 0.0.1"
4
+ gem "path", "~> 1.3"
5
5
  gem "citrus", "~> 2.4"
6
- gem "rake", "~> 0.9.2"
7
- gem "rspec", "~> 2.8"
6
+ gem "rake", "~> 10.0"
7
+ gem "rspec", "~> 2.10"
8
8
  gem "wlang", "~> 0.10.2"
9
9
  end
@@ -2,17 +2,17 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  citrus (2.4.1)
5
- diff-lcs (1.1.3)
6
- epath (0.0.1)
7
- rake (0.9.2.2)
8
- rspec (2.8.0)
9
- rspec-core (~> 2.8.0)
10
- rspec-expectations (~> 2.8.0)
11
- rspec-mocks (~> 2.8.0)
12
- rspec-core (2.8.0)
13
- rspec-expectations (2.8.0)
14
- diff-lcs (~> 1.1.2)
15
- rspec-mocks (2.8.0)
5
+ diff-lcs (1.2.4)
6
+ path (1.3.3)
7
+ rake (10.1.0)
8
+ rspec (2.14.1)
9
+ rspec-core (~> 2.14.0)
10
+ rspec-expectations (~> 2.14.0)
11
+ rspec-mocks (~> 2.14.0)
12
+ rspec-core (2.14.5)
13
+ rspec-expectations (2.14.2)
14
+ diff-lcs (>= 1.1.3, < 2.0)
15
+ rspec-mocks (2.14.3)
16
16
  wlang (0.10.2)
17
17
 
18
18
  PLATFORMS
@@ -21,7 +21,7 @@ PLATFORMS
21
21
 
22
22
  DEPENDENCIES
23
23
  citrus (~> 2.4)
24
- epath (~> 0.0.1)
25
- rake (~> 0.9.2)
26
- rspec (~> 2.8)
24
+ path (~> 1.3)
25
+ rake (~> 10.0)
26
+ rspec (~> 2.10)
27
27
  wlang (~> 0.10.2)
data/Rakefile CHANGED
@@ -1,7 +1,3 @@
1
- # Dynamically load the gem spec
2
- $gemspec_file = File.expand_path('../sexpr.gemspec', __FILE__)
3
- $gemspec = Kernel.eval(File.read($gemspec_file))
4
-
5
1
  # We run tests by default
6
2
  task :default => :test
7
3
 
@@ -56,98 +56,4 @@ module BoolExpr
56
56
 
57
57
  end # class NotPushProcessor
58
58
 
59
- end # module BoolExpr
60
-
61
- describe BoolExpr do
62
- subject{ BoolExpr }
63
-
64
- describe "the parsing feature" do
65
-
66
- it 'parses boolean expressions without error' do
67
- subject.parse("x and y").should be_a(Citrus::Match)
68
- subject.parse("not(y)").should be_a(Citrus::Match)
69
- subject.parse("not(true)").should be_a(Citrus::Match)
70
- end
71
-
72
- it 'provides a shortcut to get s-expressions directly' do
73
- subject.sexpr("x and y").should eq([:bool_and, [:var_ref, "x"], [:var_ref, "y"]])
74
- end
75
-
76
- end # parsing
77
-
78
- describe "the tagging feature" do
79
-
80
- it 'tags parsing results with the Sexpr module' do
81
- sexpr = subject.sexpr("x and y")
82
- sexpr.should be_a(Sexpr)
83
- sexpr.sexpr_type.should eq(:bool_and)
84
- sexpr.sexpr_body.should eq([[:var_ref, "x"], [:var_ref, "y"]])
85
- end
86
-
87
- it 'tags parsing results with user modules' do
88
- subject.sexpr("x and y").should be_a(BoolExpr::And)
89
- end
90
-
91
- it 'allows tagging manually' do
92
- subject.sexpr([:bool_lit, true]).should be_a(BoolExpr::Lit)
93
- end
94
-
95
- it 'applies tagging recursively' do
96
- sexpr = subject.sexpr([:bool_not, [:bool_lit, true]])
97
- sexpr.last.should be_a(BoolExpr::Lit)
98
- end
99
-
100
- end # taggging
101
-
102
- describe 'the validation feature' do
103
-
104
- it 'validates s-expressions' do
105
- subject.match?([:bool_lit, true]).should be_true
106
- subject.match?([:bool_lit, "x"]).should be_false
107
- end
108
-
109
- it 'validates s-expressions against specific rules' do
110
- subject[:bool_lit].match?([:bool_lit, true]).should be_true
111
- subject[:bool_and].match?([:bool_lit, true]).should be_false
112
- end
113
-
114
- end # validating
115
-
116
- describe BoolExpr::NotPushProcessor do
117
-
118
- def _(expr)
119
- BoolExpr.sexpr(expr)
120
- end
121
-
122
- def rw(expr)
123
- BoolExpr::NotPushProcessor.new.call(expr)
124
- end
125
-
126
- it 'does nothing on variable references' do
127
- rw("not x").should eq([:bool_not, [:var_ref, "x"]])
128
- end
129
-
130
- it 'rewrites literals through negating them' do
131
- rw("not true").should eq(_ "false")
132
- rw("not false").should eq(_ "true")
133
- end
134
-
135
- it 'rewrites not through removing them' do
136
- rw("not not true").should eq(_ "true")
137
- end
138
-
139
- it 'rewrites or through and of negated terms' do
140
- rw("not(x or y)").should eq(_ "not(x) and not(y)")
141
- end
142
-
143
- it 'rewrites and through or of negated terms' do
144
- rw("not(x and y)").should eq(_ "not(x) or not(y)")
145
- end
146
-
147
- it 'rewrites recursively' do
148
- rw("not(x and not(y))").should eq(_ "not(x) or y")
149
- end
150
-
151
- end # rewriting
152
-
153
- end if defined?(RSpec)
59
+ end # module BoolExpr
@@ -15,7 +15,7 @@ module Sexpr
15
15
  end
16
16
  alias :=== :match?
17
17
 
18
- private
18
+ private
19
19
 
20
20
  def compile_rules(rules)
21
21
  Hash[rules.map{|k,v|
@@ -26,10 +26,10 @@ module Sexpr
26
26
  def compile_rule(name, defn)
27
27
  case rule = compile_rule_defn(defn)
28
28
  when Matcher::Terminal, Matcher::Alternative
29
- rule
30
29
  else
31
- Matcher::Rule.new(name, rule)
30
+ rule = Matcher::NonTerminal.new(name, rule)
32
31
  end
32
+ Matcher::Rule.new(name, rule)
33
33
  end
34
34
 
35
35
  def compile_rule_defn(arg, grammar = self)
@@ -11,7 +11,7 @@ module Sexpr
11
11
  parser!.parse(input, options)
12
12
  end
13
13
 
14
- private
14
+ private
15
15
 
16
16
  def parser!
17
17
  unless p = parser
@@ -19,6 +19,15 @@ module Sexpr
19
19
  nil
20
20
  end
21
21
 
22
+ def tagging_module_for(rulename)
23
+ if ref = tagging_reference
24
+ modname = rule2modname(rulename)
25
+ ref.const_get(modname, false) rescue default_tagging_module
26
+ else
27
+ default_tagging_module
28
+ end
29
+ end
30
+
22
31
  def sexpr(input, markers = nil)
23
32
  case input
24
33
  when Array
@@ -29,7 +38,11 @@ module Sexpr
29
38
  end
30
39
  end
31
40
 
32
- private
41
+ def looks_a_sexpr?(arg)
42
+ arg.is_a?(Array) and arg.first.is_a?(Symbol)
43
+ end
44
+
45
+ private
33
46
 
34
47
  def tag_sexpr(sexpr, markers = nil, force = false)
35
48
  return sexpr unless looks_a_sexpr?(sexpr)
@@ -53,21 +66,11 @@ module Sexpr
53
66
  end
54
67
 
55
68
  def tag_sexpr_with_user_module(sexpr)
56
- if ref = tagging_reference
57
- rulename = sexpr.first
58
- modname = rule2modname(rulename)
59
- tag = ref.const_get(modname, false) rescue default_tagging_module
60
- sexpr.extend(tag) if tag
61
- elsif tag = default_tagging_module
62
- sexpr.extend(tag)
63
- end
69
+ tag = tagging_module_for(sexpr.first)
70
+ sexpr.extend(tag) if tag
64
71
  sexpr
65
72
  end
66
73
 
67
- def looks_a_sexpr?(arg)
68
- arg.is_a?(Array) and arg.first.is_a?(Symbol)
69
- end
70
-
71
74
  def parser!
72
75
  raise NoParserError, "No parser set.", caller
73
76
  end
@@ -0,0 +1 @@
1
+
@@ -10,6 +10,7 @@ end # module Sexpr
10
10
  require_relative "matcher/alternative"
11
11
  require_relative "matcher/many"
12
12
  require_relative "matcher/reference"
13
- require_relative "matcher/rule"
14
13
  require_relative "matcher/sequence"
15
- require_relative "matcher/terminal"
14
+ require_relative "matcher/terminal"
15
+ require_relative "matcher/non_terminal"
16
+ require_relative "matcher/rule"
@@ -28,11 +28,26 @@ module Sexpr
28
28
  i >= @min ? last : nil
29
29
  end
30
30
 
31
+ def parse(sexp, to = [])
32
+ i, last, got = 0, sexp, []
33
+ while sexp && (@max.nil? || i < @max)
34
+ if res = @term.parse(sexp, got)
35
+ last = res
36
+ i += 1
37
+ end
38
+ sexp = res
39
+ end
40
+ if i >= @min
41
+ got.each{|x| to << x }
42
+ last
43
+ end
44
+ end
45
+
31
46
  def inspect
32
47
  "(many #{term.inspect}, #{min}, #{max})"
33
48
  end
34
49
 
35
- private
50
+ private
36
51
 
37
52
  def minmax(min, max)
38
53
  case min
@@ -0,0 +1,31 @@
1
+ module Sexpr
2
+ module Matcher
3
+ class NonTerminal
4
+ include Matcher
5
+
6
+ attr_reader :name
7
+ attr_reader :defn
8
+
9
+ def initialize(name, defn)
10
+ @name = name
11
+ @defn = defn
12
+ end
13
+
14
+ def match?(sexp)
15
+ return nil unless sexp.is_a?(Array)
16
+ return false unless sexp.first && (sexp.first == name)
17
+ defn.match?(sexp[1..-1])
18
+ end
19
+
20
+ def eat(sexp)
21
+ return nil unless match?(sexp.first)
22
+ sexp[1..-1]
23
+ end
24
+
25
+ def inspect
26
+ "(non-terminal #{name}, #{defn.inspect})"
27
+ end
28
+
29
+ end # class NonTerminal
30
+ end # module Matcher
31
+ end # module Sexpr
@@ -12,14 +12,11 @@ module Sexpr
12
12
  end
13
13
 
14
14
  def match?(sexp)
15
- return nil unless sexp.is_a?(Array)
16
- return false unless sexp.first && (sexp.first == name)
17
- defn.match?(sexp[1..-1])
15
+ defn.match?(sexp)
18
16
  end
19
17
 
20
18
  def eat(sexp)
21
- return nil unless match?(sexp.first)
22
- sexp[1..-1]
19
+ defn.eat(sexp)
23
20
  end
24
21
 
25
22
  def inspect
@@ -28,4 +25,4 @@ module Sexpr
28
25
 
29
26
  end # class Rule
30
27
  end # module Matcher
31
- end # module Sexpr
28
+ end # module Sexpr
@@ -31,7 +31,7 @@ module Sexpr
31
31
  end
32
32
  alias :dup :sexpr_copy
33
33
 
34
- private
34
+ private
35
35
 
36
36
  def sexpr_copy_tagging(copy)
37
37
  (class << self; self; end).included_modules.each do |mod|
@@ -28,7 +28,7 @@ module Sexpr
28
28
 
29
29
  end # class methods
30
30
 
31
- private
31
+ private
32
32
 
33
33
  def input_text(input)
34
34
  case input
@@ -6,6 +6,12 @@ module Sexpr
6
6
 
7
7
  class << self
8
8
 
9
+ def grammar(grammar = nil)
10
+ @grammar = grammar if grammar
11
+ @grammar ||= (@grammar || superclass.grammar) rescue Sexpr
12
+ @grammar
13
+ end
14
+
9
15
  def preprocessors
10
16
  @preprocessors ||= superclass.preprocessors.dup rescue [ ]
11
17
  end
@@ -46,6 +52,10 @@ module Sexpr
46
52
  @options = options || {}
47
53
  end
48
54
 
55
+ def grammar
56
+ self.class.grammar
57
+ end
58
+
49
59
  def call(sexpr)
50
60
  apply(preprocess(sexpr))
51
61
  end
@@ -62,7 +72,7 @@ module Sexpr
62
72
  raise UnexpectedSexprError, "Unexpected sexpr: #{sexpr.inspect}"
63
73
  end
64
74
 
65
- private
75
+ private
66
76
 
67
77
  def preprocess(sexpr)
68
78
  preprocessors = self.class.preprocessors
@@ -16,7 +16,7 @@ module Sexpr
16
16
  yield(processor, sexpr)
17
17
  end
18
18
 
19
- private
19
+ private
20
20
 
21
21
  def next_call(processor, sexpr, toplevel)
22
22
  if nic = next_in_chain
@@ -5,40 +5,32 @@ module Sexpr
5
5
  module Methods
6
6
 
7
7
  def parse(*args)
8
- sexpr_grammar.parse(*args)
8
+ grammar.parse(*args)
9
9
  end
10
10
 
11
11
  def sexpr(*args)
12
- sexpr_grammar.sexpr(*args)
12
+ grammar.sexpr(*args)
13
13
  end
14
14
 
15
15
  end
16
16
 
17
17
  def call(processor, sexpr, &bl)
18
+ g = processor.class.grammar
19
+
18
20
  # input coercion
19
- sexpr = grammar(processor).sexpr(sexpr)
21
+ sexpr = g.sexpr(sexpr)
20
22
 
21
23
  # recursive call
22
24
  sexpr = next_call(processor, sexpr, bl)
23
25
 
24
26
  # output coercion
25
27
  if sexpr.is_a?(Array) and sexpr.first.is_a?(Symbol)
26
- grammar(processor).sexpr(sexpr)
28
+ g.sexpr(sexpr)
27
29
  else
28
30
  sexpr
29
31
  end
30
32
  end
31
33
 
32
- private
33
-
34
- def grammar(processor)
35
- if processor.respond_to?(:sexpr_grammar)
36
- processor.sexpr_grammar
37
- else
38
- Sexpr
39
- end
40
- end
41
-
42
34
  end # class SexprCoercions
43
35
  end # class Processor
44
36
  end # module Sexpr