gisele 0.0.1 → 0.1.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 +39 -1
- data/gisele.noespec +1 -1
- data/lib/gisele/command.rb +13 -4
- data/lib/gisele/errors.rb +21 -0
- data/lib/gisele/language/ast/helpers.rb +37 -0
- data/lib/gisele/language/ast/node.rb +54 -0
- data/lib/gisele/language/ast/unit.rb +10 -0
- data/lib/gisele/language/ast.rb +14 -0
- data/lib/gisele/language/sugar_removal.rb +53 -0
- data/lib/gisele/language/syntax/bool_and.rb +14 -0
- data/lib/gisele/language/syntax/bool_lit.rb +14 -0
- data/lib/gisele/language/syntax/bool_not.rb +14 -0
- data/lib/gisele/language/syntax/bool_or.rb +14 -0
- data/lib/gisele/language/syntax/bool_paren.rb +14 -0
- data/lib/gisele/language/syntax/else_clause.rb +14 -0
- data/lib/gisele/language/syntax/elsif_clause.rb +16 -0
- data/lib/gisele/language/syntax/event_set.rb +15 -0
- data/lib/gisele/language/syntax/fluent_def.rb +18 -0
- data/lib/gisele/language/syntax/grammar.citrus +202 -0
- data/lib/gisele/language/syntax/if_st.rb +18 -0
- data/lib/gisele/language/syntax/implicit_seq_st.rb +16 -0
- data/lib/gisele/language/syntax/node.rb +35 -0
- data/lib/gisele/language/syntax/par_st.rb +14 -0
- data/lib/gisele/language/syntax/seq_st.rb +14 -0
- data/lib/gisele/language/syntax/st_list.rb +14 -0
- data/lib/gisele/language/syntax/task_call_st.rb +14 -0
- data/lib/gisele/language/syntax/task_def.rb +17 -0
- data/lib/gisele/language/syntax/task_refinement.rb +15 -0
- data/lib/gisele/language/syntax/task_signature.rb +15 -0
- data/lib/gisele/language/syntax/trackvar_def.rb +19 -0
- data/lib/gisele/language/syntax/unit.rb +14 -0
- data/lib/gisele/language/syntax/var_ref.rb +14 -0
- data/lib/gisele/language/syntax/while_st.rb +16 -0
- data/lib/gisele/language/syntax.rb +29 -0
- data/lib/gisele/language/transformer.rb +38 -0
- data/lib/gisele/language.rb +15 -1
- data/lib/gisele/version.rb +2 -2
- data/lib/gisele.rb +7 -1
- data/spec/command/main/gisele_ast_ruby.stdout +38 -28
- data/spec/command/main/gisele_help.stdout +5 -0
- data/spec/command/main/gisele_no_sugar.cmd +1 -0
- data/spec/command/main/gisele_no_sugar.stdout +61 -0
- data/spec/fixtures/tasks/simple.ast +18 -14
- data/spec/fixtures/tasks/simple.gis +2 -0
- data/spec/spec_helper.rb +3 -3
- data/spec/test_examples.rb +3 -3
- data/spec/unit/language/ast/test_node.rb +61 -0
- data/spec/unit/language/sugar_removal/test_if_to_guarded_commands.rb +90 -0
- data/spec/unit/language/{test_grammar.rb → syntax/test_grammar.rb} +18 -30
- data/spec/unit/language/syntax/test_to_ast.rb +245 -0
- data/spec/unit/language/test_ast.rb +21 -247
- data/spec/unit/language/test_syntax.rb +48 -0
- data/spec/unit/language/test_transformer.rb +68 -0
- data/spec/unit/test_language.rb +35 -0
- metadata +69 -25
- data/lib/gisele/language/grammar.citrus +0 -246
- data/lib/gisele/language/parser.rb +0 -30
- data/spec/unit/language/test_parser.rb +0 -27
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Gisele::Language
|
3
|
+
describe SugarRemoval do
|
4
|
+
describe "if_st to case_st" do
|
5
|
+
include AST::Helpers
|
6
|
+
|
7
|
+
def ast(source)
|
8
|
+
Syntax.ast(source.strip, :root => :if_st)
|
9
|
+
end
|
10
|
+
|
11
|
+
def rewrite(ast)
|
12
|
+
SugarRemoval.new.call(ast)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'rewrites single if correctly' do
|
16
|
+
source = ast("if goodCond Task1 end")
|
17
|
+
expected = \
|
18
|
+
[:case_st,
|
19
|
+
[:when_clause, [:var_ref, "goodCond"], [:task_call_st, "Task1"] ]]
|
20
|
+
rewrite(source).should eq(expected)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'negates the else clause' do
|
24
|
+
source = ast("if goodCond Task1 else Task2 end")
|
25
|
+
expected = \
|
26
|
+
[:case_st,
|
27
|
+
[:when_clause, [:var_ref, "goodCond"], [:task_call_st, "Task1"] ],
|
28
|
+
[:when_clause, [:bool_not, [:var_ref, "goodCond"]], [:task_call_st, "Task2"] ]
|
29
|
+
]
|
30
|
+
rewrite(source).should eq(expected)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'handles elsif clauses correctly' do
|
34
|
+
source = ast(<<-IF)
|
35
|
+
if c1 Task1
|
36
|
+
elsif c2 Task2
|
37
|
+
elsif c3 Task3
|
38
|
+
else Task4
|
39
|
+
end
|
40
|
+
IF
|
41
|
+
expected = \
|
42
|
+
[:case_st,
|
43
|
+
[:when_clause,
|
44
|
+
[:var_ref, "c1"],
|
45
|
+
[:task_call_st, "Task1"] ],
|
46
|
+
[:when_clause,
|
47
|
+
[:bool_and,
|
48
|
+
[:var_ref, "c2"],
|
49
|
+
[:bool_not, [:var_ref, "c1"]] ],
|
50
|
+
[:task_call_st, "Task2"] ],
|
51
|
+
[:when_clause,
|
52
|
+
[:bool_and,
|
53
|
+
[:var_ref, "c3"],
|
54
|
+
[:bool_and,
|
55
|
+
[:bool_not, [:var_ref, "c2"]],
|
56
|
+
[:bool_not, [:var_ref, "c1"]] ]],
|
57
|
+
[:task_call_st, "Task3"] ],
|
58
|
+
[:when_clause,
|
59
|
+
[:bool_and,
|
60
|
+
[:bool_not, [:var_ref, "c3"]],
|
61
|
+
[:bool_and,
|
62
|
+
[:bool_not, [:var_ref, "c2"]],
|
63
|
+
[:bool_not, [:var_ref, "c1"]]]],
|
64
|
+
[:task_call_st, "Task4"] ],
|
65
|
+
]
|
66
|
+
rewrite(source).should eq(expected)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'avoids double negations' do
|
70
|
+
source = ast("if not(goodCond) Task1 else Task2 end")
|
71
|
+
expected = \
|
72
|
+
[:case_st,
|
73
|
+
[:when_clause,
|
74
|
+
[:bool_not, [:var_ref, "goodCond"]],
|
75
|
+
[:task_call_st, "Task1"] ],
|
76
|
+
[:when_clause,
|
77
|
+
[:var_ref, "goodCond"],
|
78
|
+
[:task_call_st, "Task2"] ] ]
|
79
|
+
rewrite(source).should eq(expected)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'recurse on other nodes' do
|
83
|
+
if_st = ast("if goodCond Task1 end")
|
84
|
+
rw_st = rewrite(if_st)
|
85
|
+
rewrite([:unit, if_st]).should eq([:unit, rw_st])
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
module Gisele::Language
|
2
|
+
module Gisele::Language::Syntax
|
3
3
|
describe Grammar do
|
4
4
|
|
5
|
-
let(:grammar){ Gisele::Language::Grammar }
|
5
|
+
let(:grammar){ Gisele::Language::Syntax::Grammar }
|
6
6
|
|
7
7
|
def parse(text, rule, consume = true)
|
8
8
|
grammar.parse(text, :root => rule, :consume => consume)
|
@@ -130,22 +130,6 @@ module Gisele::Language
|
|
130
130
|
|
131
131
|
end # event
|
132
132
|
|
133
|
-
describe 'the event_commalist rule' do
|
134
|
-
|
135
|
-
it 'parses a singleton list' do
|
136
|
-
parse('Task:start', :event_commalist).should eq('Task:start')
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'parses multiple events' do
|
140
|
-
parse('Task:start, an_event', :event_commalist).should eq('Task:start, an_event')
|
141
|
-
end
|
142
|
-
|
143
|
-
it 'recognizes invalid events' do
|
144
|
-
lambda{ parse('Task:start, NotAnEvent', :event_commalist) }.should raise_error(Citrus::ParseError)
|
145
|
-
end
|
146
|
-
|
147
|
-
end # event_commalist
|
148
|
-
|
149
133
|
describe 'the event_set rule' do
|
150
134
|
|
151
135
|
it 'parses empty sets' do
|
@@ -158,6 +142,10 @@ module Gisele::Language
|
|
158
142
|
parse('{ Task:start }', :event_set).should eq('{ Task:start }')
|
159
143
|
end
|
160
144
|
|
145
|
+
it 'parses event sets' do
|
146
|
+
parse('{Task:start, Task:end}', :event_set).should eq('{Task:start, Task:end}')
|
147
|
+
end
|
148
|
+
|
161
149
|
it 'recognizes invalid events in the set' do
|
162
150
|
lambda{ parse('{Task:start, NotAnEvent}', :event_set) }.should raise_error(Citrus::ParseError)
|
163
151
|
end
|
@@ -238,51 +226,51 @@ module Gisele::Language
|
|
238
226
|
|
239
227
|
### Process statements
|
240
228
|
|
241
|
-
describe 'the
|
229
|
+
describe 'the par_st rule' do
|
242
230
|
|
243
231
|
it 'parses a single parallel statement' do
|
244
232
|
expr = 'par Task1 Task2 end'
|
245
|
-
parse(expr, :
|
233
|
+
parse(expr, :par_st).should eq(expr)
|
246
234
|
end
|
247
235
|
|
248
|
-
end #
|
236
|
+
end # par_st
|
249
237
|
|
250
|
-
describe 'the
|
238
|
+
describe 'the seq_st rule' do
|
251
239
|
|
252
240
|
it 'parses a single sequence statement' do
|
253
241
|
expr = 'seq Task1 Task2 end'
|
254
|
-
parse(expr, :
|
242
|
+
parse(expr, :seq_st).should eq(expr)
|
255
243
|
end
|
256
244
|
|
257
|
-
end #
|
245
|
+
end # seq_st
|
258
246
|
|
259
247
|
describe 'the while_statement rule' do
|
260
248
|
|
261
249
|
it 'parses a single while statement' do
|
262
250
|
expr = 'while badCond Task end'
|
263
|
-
parse(expr, :
|
251
|
+
parse(expr, :while_st).should eq(expr)
|
264
252
|
end
|
265
253
|
|
266
254
|
end # while_statement
|
267
255
|
|
268
|
-
describe 'the
|
256
|
+
describe 'the if_st rule' do
|
269
257
|
|
270
258
|
it 'parses a single if statement' do
|
271
259
|
expr = 'if goodCond Task end'
|
272
|
-
parse(expr, :
|
260
|
+
parse(expr, :if_st).should eq(expr)
|
273
261
|
end
|
274
262
|
|
275
263
|
it 'supports an optional else' do
|
276
264
|
expr = 'if goodCond GoodTask else BadTask end'
|
277
|
-
parse(expr, :
|
265
|
+
parse(expr, :if_st).should eq(expr)
|
278
266
|
end
|
279
267
|
|
280
268
|
it 'supports an optional elsif clauses' do
|
281
269
|
expr = 'if goodCond GoodTask elsif otherCond OtherTask elsif yetAnother BadTask end'
|
282
|
-
parse(expr, :
|
270
|
+
parse(expr, :if_st).should eq(expr)
|
283
271
|
end
|
284
272
|
|
285
|
-
end #
|
273
|
+
end # if_st
|
286
274
|
|
287
275
|
describe 'the process_statement rule' do
|
288
276
|
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Gisele::Language::Syntax
|
3
|
+
describe Grammar, "to_ast" do
|
4
|
+
|
5
|
+
def ast(text, rule, consume = true)
|
6
|
+
@parsed = Grammar.parse(text, :root => rule, :consume => consume).to_ast
|
7
|
+
end
|
8
|
+
|
9
|
+
after{ @parsed.should be_a(Gisele::Language::AST::Node) }
|
10
|
+
|
11
|
+
describe "the bool_expr rule" do
|
12
|
+
|
13
|
+
it 'returns expected ast on simple expressions' do
|
14
|
+
expected = [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]]
|
15
|
+
ast("diagKnown and platLow", :bool_expr).should eq(expected)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'respects priorities' do
|
19
|
+
expected = [:bool_or, [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]], [:var_ref, "platHigh"]]
|
20
|
+
ast("diagKnown and platLow or platHigh", :bool_expr).should eq(expected)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'supports double negations' do
|
24
|
+
expected = [:bool_not, [:bool_not, [:var_ref, "diagKnown"]]]
|
25
|
+
ast("not not(diagKnown)", :bool_expr).should eq(expected)
|
26
|
+
end
|
27
|
+
|
28
|
+
end # bool_expr
|
29
|
+
|
30
|
+
describe 'the event_set rule' do
|
31
|
+
|
32
|
+
it 'parses empty lists as expected' do
|
33
|
+
expr = '{ }'
|
34
|
+
expected = [:event_set]
|
35
|
+
ast(expr, :event_set).should eq(expected)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'parses singleton lists as expected' do
|
39
|
+
expr = '{Diagnosis:start}'
|
40
|
+
expected = [:event_set, "Diagnosis:start"]
|
41
|
+
ast(expr, :event_set).should eq(expected)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'parses non empty lists as expected' do
|
45
|
+
expr = '{Diagnosis:start, an_event, another_one}'
|
46
|
+
expected = [:event_set, "Diagnosis:start", "an_event", "another_one"]
|
47
|
+
ast(expr, :event_set).should eq(expected)
|
48
|
+
end
|
49
|
+
|
50
|
+
end # event_set
|
51
|
+
|
52
|
+
describe "the fluent_def rule" do
|
53
|
+
|
54
|
+
it 'parses fluent definitions as expected' do
|
55
|
+
defn = "fluent diagKnown {Diagnosis:start, diagnosis}, {Treatment:end} initially false"
|
56
|
+
expected = [:fluent,
|
57
|
+
"diagKnown",
|
58
|
+
[:event_set, "Diagnosis:start", "diagnosis"],
|
59
|
+
[:event_set, "Treatment:end"],
|
60
|
+
false]
|
61
|
+
ast(defn, :fluent_def).should eq(expected)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'does not require the initial value' do
|
65
|
+
defn = "fluent diagKnown {Diagnosis:start, diagnosis}, {Treatment:end}"
|
66
|
+
expected = [:fluent,
|
67
|
+
"diagKnown",
|
68
|
+
[:event_set, "Diagnosis:start", "diagnosis"],
|
69
|
+
[:event_set, "Treatment:end"],
|
70
|
+
nil]
|
71
|
+
ast(defn, :fluent_def).should eq(expected)
|
72
|
+
end
|
73
|
+
|
74
|
+
end # fluent_def rule
|
75
|
+
|
76
|
+
describe "the trackvar_def rule" do
|
77
|
+
|
78
|
+
it 'parses tracking variable definitions as expected' do
|
79
|
+
defn = "trackvar mplus {Diagnosis:start}"
|
80
|
+
expected = [:trackvar,
|
81
|
+
"mplus",
|
82
|
+
[:event_set, "Diagnosis:start"],
|
83
|
+
[:event_set],
|
84
|
+
nil]
|
85
|
+
ast(defn, :trackvar_def).should eq(expected)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'supports obsolete events and initial value' do
|
89
|
+
defn = "trackvar mplus {Diagnosis:start}, {Treatment:end} initially true"
|
90
|
+
expected = [:trackvar,
|
91
|
+
"mplus",
|
92
|
+
[:event_set, "Diagnosis:start"],
|
93
|
+
[:event_set, "Treatment:end"],
|
94
|
+
true]
|
95
|
+
ast(defn, :trackvar_def).should eq(expected)
|
96
|
+
end
|
97
|
+
|
98
|
+
end # trackvar_def rule
|
99
|
+
|
100
|
+
describe "the task_call_st rule" do
|
101
|
+
|
102
|
+
it 'parses as expected' do
|
103
|
+
ast("Diagnosis", :task_call_st).should eq([:task_call_st, "Diagnosis"])
|
104
|
+
end
|
105
|
+
|
106
|
+
end # task_call_statement
|
107
|
+
|
108
|
+
describe "the par_st rule" do
|
109
|
+
|
110
|
+
it 'parses as expected' do
|
111
|
+
expr = "par Task1 Task2 end"
|
112
|
+
expected = [:par_st, [:task_call_st, "Task1"], [:task_call_st, "Task2"]]
|
113
|
+
ast(expr, :par_st).should eq(expected)
|
114
|
+
end
|
115
|
+
|
116
|
+
end # par_st
|
117
|
+
|
118
|
+
describe "the seq_st rule" do
|
119
|
+
|
120
|
+
it 'parses as expected' do
|
121
|
+
expr = "seq Task1 Task2 end"
|
122
|
+
expected = [:seq_st, [:task_call_st, "Task1"], [:task_call_st, "Task2"]]
|
123
|
+
ast(expr, :seq_st).should eq(expected)
|
124
|
+
end
|
125
|
+
|
126
|
+
end # seq_st
|
127
|
+
|
128
|
+
describe "the while_statement rule" do
|
129
|
+
|
130
|
+
it 'parses as expected' do
|
131
|
+
expr = "while goodCond Task1 end"
|
132
|
+
expected = \
|
133
|
+
[:while_st,
|
134
|
+
[:var_ref, "goodCond"],
|
135
|
+
[:task_call_st, "Task1"]]
|
136
|
+
ast(expr, :while_st).should eq(expected)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'recognizes implicit sequences' do
|
140
|
+
expr = "while goodCond Task1 Task2 end"
|
141
|
+
expected = \
|
142
|
+
[:while_st,
|
143
|
+
[:var_ref, "goodCond"],
|
144
|
+
[:seq_st, [:task_call_st, "Task1"], [:task_call_st, "Task2"]]]
|
145
|
+
ast(expr, :while_st).should eq(expected)
|
146
|
+
end
|
147
|
+
|
148
|
+
end # while_statement
|
149
|
+
|
150
|
+
describe "the else_clause rule" do
|
151
|
+
|
152
|
+
it 'parses as expected' do
|
153
|
+
expr = "else Task1 "
|
154
|
+
expected = \
|
155
|
+
[:else_clause, [:task_call_st, "Task1"]]
|
156
|
+
ast(expr, :else_clause).should eq(expected)
|
157
|
+
end
|
158
|
+
|
159
|
+
end # else_clause
|
160
|
+
|
161
|
+
describe "the elsif_clause rule" do
|
162
|
+
|
163
|
+
it 'parses as expected' do
|
164
|
+
expr = "elsif goodCond Task1 "
|
165
|
+
expected = \
|
166
|
+
[:elsif_clause, [:var_ref, "goodCond"], [:task_call_st, "Task1"]]
|
167
|
+
ast(expr, :elsif_clause).should eq(expected)
|
168
|
+
end
|
169
|
+
|
170
|
+
end # elsif_clause
|
171
|
+
|
172
|
+
describe "the if_statement rule" do
|
173
|
+
|
174
|
+
it 'parses as expected' do
|
175
|
+
expr = "if goodCond Task1 end"
|
176
|
+
expected = \
|
177
|
+
[:if_st, [:var_ref, "goodCond"], [:task_call_st, "Task1"]]
|
178
|
+
ast(expr, :if_st).should eq(expected)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'supports a else clause' do
|
182
|
+
expr = "if goodCond Task1 else Task2 end"
|
183
|
+
expected = \
|
184
|
+
[:if_st,
|
185
|
+
[:var_ref, "goodCond"], [:task_call_st, "Task1"],
|
186
|
+
[:else_clause, [:task_call_st, "Task2"]] ]
|
187
|
+
ast(expr, :if_st).should eq(expected)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'supports elsif clauses' do
|
191
|
+
expr = "if goodCond Task1 elsif otherCond Task2 elsif stillAnother Task3 else Task4 end"
|
192
|
+
expected = \
|
193
|
+
[:if_st,
|
194
|
+
[:var_ref, "goodCond"], [:task_call_st, "Task1"],
|
195
|
+
[:elsif_clause,
|
196
|
+
[:var_ref, "otherCond"], [:task_call_st, "Task2"]],
|
197
|
+
[:elsif_clause,
|
198
|
+
[:var_ref, "stillAnother"], [:task_call_st, "Task3"]],
|
199
|
+
[:else_clause,
|
200
|
+
[:task_call_st, "Task4"]] ]
|
201
|
+
ast(expr, :if_st).should eq(expected)
|
202
|
+
end
|
203
|
+
|
204
|
+
end # if_statement
|
205
|
+
|
206
|
+
describe 'the task_refinement rule' do
|
207
|
+
|
208
|
+
it 'parses as expected' do
|
209
|
+
expr = "refinement Task1 end"
|
210
|
+
expected = [:task_refinement, [:task_call_st, "Task1"]]
|
211
|
+
ast(expr, :task_refinement).should eq(expected)
|
212
|
+
end
|
213
|
+
|
214
|
+
end # task_refinement
|
215
|
+
|
216
|
+
describe 'the task_signature rule' do
|
217
|
+
|
218
|
+
it 'parses as expected' do
|
219
|
+
expr = "fluent diagKnown {}, {}\ntrackvar mplus {}"
|
220
|
+
expected = \
|
221
|
+
[ :task_signature,
|
222
|
+
[:fluent, "diagKnown", [:event_set], [:event_set], nil],
|
223
|
+
[:trackvar, "mplus", [:event_set], [:event_set], nil]]
|
224
|
+
ast(expr, :task_signature).should eq(expected)
|
225
|
+
end
|
226
|
+
|
227
|
+
end # task_signature
|
228
|
+
|
229
|
+
describe 'the task_def rule' do
|
230
|
+
|
231
|
+
it 'parses as expected' do
|
232
|
+
expr = "task Task1 fluent diagKnown {}, {} refinement Task2 end end"
|
233
|
+
expected = \
|
234
|
+
[:task_def, "Task1",
|
235
|
+
[:task_signature,
|
236
|
+
[:fluent, "diagKnown", [:event_set], [:event_set], nil]],
|
237
|
+
[:task_refinement,
|
238
|
+
[:task_call_st, "Task2"]]]
|
239
|
+
ast(expr, :task_def).should eq(expected)
|
240
|
+
end
|
241
|
+
|
242
|
+
end # task_def
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|