gisele 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.md +16 -1
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +2 -0
  4. data/gisele.gemspec +1 -0
  5. data/gisele.noespec +2 -1
  6. data/lib/gisele/command.rb +21 -6
  7. data/lib/gisele/language/ast/bool_and.rb +14 -0
  8. data/lib/gisele/language/ast/bool_expr.rb +14 -0
  9. data/lib/gisele/language/ast/bool_not.rb +14 -0
  10. data/lib/gisele/language/ast/bool_or.rb +14 -0
  11. data/lib/gisele/language/ast/elsif_clause.rb +14 -0
  12. data/lib/gisele/language/ast/helpers.rb +15 -9
  13. data/lib/gisele/language/ast/if_st.rb +14 -0
  14. data/lib/gisele/language/ast/node.rb +39 -3
  15. data/lib/gisele/language/ast/task_call_st.rb +14 -0
  16. data/lib/gisele/language/ast/var_ref.rb +14 -0
  17. data/lib/gisele/language/ast/when_clause.rb +14 -0
  18. data/lib/gisele/language/ast/while_st.rb +14 -0
  19. data/lib/gisele/language/ast.rb +2 -2
  20. data/lib/gisele/language/dot.yml +19 -0
  21. data/lib/gisele/language/sugar_removal.rb +30 -9
  22. data/lib/gisele/language/syntax/bool_expr.rb +14 -0
  23. data/lib/gisele/language/syntax/bool_lit.rb +1 -1
  24. data/lib/gisele/language/syntax/grammar.citrus +4 -3
  25. data/lib/gisele/language/syntax/node.rb +2 -1
  26. data/lib/gisele/language/to_graph.rb +119 -0
  27. data/lib/gisele/language.rb +4 -1
  28. data/lib/gisele/loader.rb +1 -0
  29. data/lib/gisele/version.rb +1 -1
  30. data/spec/command/main/gisele_ast_ruby.stdout +6 -3
  31. data/spec/command/main/gisele_graph.cmd +1 -0
  32. data/spec/command/main/gisele_graph.stdout +30 -0
  33. data/spec/command/main/gisele_help.stdout +10 -5
  34. data/spec/command/main/gisele_no_sugar.stdout +11 -5
  35. data/spec/command/main/gisele_version.stdout +1 -1
  36. data/spec/fixtures/tasks/complete.gis +18 -0
  37. data/spec/fixtures/tasks/simple.ast +57 -19
  38. data/spec/spec_helper.rb +8 -0
  39. data/spec/unit/language/ast/test_node.rb +22 -0
  40. data/spec/unit/language/sugar_removal/test_if_to_guarded_commands.rb +32 -12
  41. data/spec/unit/language/syntax/test_to_ast.rb +23 -11
  42. data/spec/unit/language/test_syntax.rb +5 -0
  43. data/spec/unit/language/test_to_graph.rb +10 -0
  44. metadata +50 -18
@@ -1,6 +1,8 @@
1
1
  module Gisele
2
2
  module Language
3
3
 
4
+ DOT_ATTRIBUTES = YAML.load_file(Path.dir/"language/dot.yml")
5
+
4
6
  def rule2mod(rule)
5
7
  rule.to_s.gsub(/(^|_)([a-z])/){|x| $2.capitalize}.to_sym
6
8
  end
@@ -17,4 +19,5 @@ end # module Gisele
17
19
  require_relative 'language/syntax'
18
20
  require_relative 'language/ast'
19
21
  require_relative 'language/transformer'
20
- require_relative 'language/sugar_removal'
22
+ require_relative 'language/sugar_removal'
23
+ require_relative 'language/to_graph'
data/lib/gisele/loader.rb CHANGED
@@ -2,3 +2,4 @@ require "citrus"
2
2
  require "epath"
3
3
  require "quickl"
4
4
  require "awesome_print"
5
+ require "yargi"
@@ -2,7 +2,7 @@ module Gisele
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 1
5
+ MINOR = 2
6
6
  TINY = 0
7
7
 
8
8
  def self.to_s
@@ -26,10 +26,13 @@
26
26
  [
27
27
  :if_st,
28
28
  [
29
- :bool_not,
29
+ :bool_expr,
30
30
  [
31
- :var_ref,
32
- "diagKnown"
31
+ :bool_not,
32
+ [
33
+ :var_ref,
34
+ "diagKnown"
35
+ ]
33
36
  ]
34
37
  ],
35
38
  [
@@ -0,0 +1 @@
1
+ gisele --graph tasks/complete.gis
@@ -0,0 +1,30 @@
1
+ digraph G {
2
+ graph[]
3
+ V0 [shape="circle" style="filled" fillcolor="black" fixed="true" width="0.1" label=""]
4
+ V1 [shape="circle" style="filled" fillcolor="black" fixed="true" width="0.1" label=""]
5
+ V2 [shape="box" label="FirstTask"]
6
+ V3 [shape="diamond" label=""]
7
+ V4 [shape="box" height="0.1" style="filled" fillcolor="black" label=""]
8
+ V5 [shape="box" height="0.1" style="filled" fillcolor="black" label=""]
9
+ V6 [shape="box" label="MakeJoy"]
10
+ V7 [shape="box" label="DrinkBeers"]
11
+ V8 [shape="box" label="DrinkBeer"]
12
+ V9 [shape="box" label="DoNothingSpecial"]
13
+ V10 [shape="diamond" label="not(endOfTheDay)"]
14
+ V11 [shape="box" label="WaitAndSee"]
15
+ V2 -> V3 []
16
+ V6 -> V5 []
17
+ V4 -> V6 []
18
+ V7 -> V5 []
19
+ V4 -> V7 []
20
+ V3 -> V4 [label="everyoneHappy"]
21
+ V5 -> V10 []
22
+ V8 -> V10 []
23
+ V3 -> V8 [label="(someoneHappy and not(everyoneHappy))"]
24
+ V9 -> V10 []
25
+ V3 -> V9 [label="(not(someoneHappy) and not(everyoneHappy))"]
26
+ V11 -> V10 []
27
+ V10 -> V1 [label="false"]
28
+ V10 -> V11 [label="true"]
29
+ V0 -> V2 []
30
+ }
@@ -3,11 +3,12 @@ Gisele - A Process Analyzer Toolset
3
3
 
4
4
  SYNOPSIS
5
5
  gisele [--version] [--help]
6
- gisele [--ast] PROCESS_FILE
6
+ gisele [--ast | --graph] PROCESS_FILE
7
7
 
8
8
  OPTIONS
9
9
  --ast=[MODE] Prints the process abstract syntax tree (debug,ruby)
10
10
  --no-sugar Apply syntactic sugar removal
11
+ --graph=[MODE] Converts and print a graph (dot)
11
12
  --help Show this help message
12
13
  --version Show version and exit
13
14
 
@@ -15,13 +16,17 @@ DESCRIPTION
15
16
  The Gisele process analyzer toolset provides tools and technique to model and analyze
16
17
  complex process models such as care processes.
17
18
 
18
- When --ast is used, the command parses a process file and prints its Abstract Syntax
19
+ When --no-sugar is specified, syntactic sugar is first removed before making any other
20
+ transformation. For now, this rewrites all `if` statements as explicit `case` guarded
21
+ commands.
22
+
23
+ When --ast is used, the command parses the process file and prints its Abstract Syntax
19
24
  Tree (AST) on standard output. By default, this option prints the AST for manual
20
25
  debugging, that is with colors and extra information. Use --ast=ruby to get a ruby
21
26
  array for automatic processing.
22
27
 
23
- When --no-sugar is specified, syntactic sugar is first removed before making any other
24
- transformation. For now, this rewrites all `if` statements as explicit `case` guarded
25
- commands.
28
+ When --graph is used, the command parses the process file. It then converts the AST into
29
+ a directed graph representing the process as a box-and-arrow workflow and outputs it on
30
+ standard output. For now, the only output format available is dot (from graphviz).
26
31
 
27
32
  SystemExit
@@ -28,10 +28,13 @@
28
28
  [
29
29
  :when_clause,
30
30
  [
31
- :bool_not,
31
+ :bool_expr,
32
32
  [
33
- :var_ref,
34
- "diagKnown"
33
+ :bool_not,
34
+ [
35
+ :var_ref,
36
+ "diagKnown"
37
+ ]
35
38
  ]
36
39
  ],
37
40
  [
@@ -42,8 +45,11 @@
42
45
  [
43
46
  :when_clause,
44
47
  [
45
- :var_ref,
46
- "diagKnown"
48
+ :bool_expr,
49
+ [
50
+ :var_ref,
51
+ "diagKnown"
52
+ ]
47
53
  ],
48
54
  [
49
55
  :task_call_st,
@@ -1,2 +1,2 @@
1
- gisele 0.0.1 (c) The University of Louvain
1
+ gisele 0.2.0 (c) The University of Louvain
2
2
  SystemExit
@@ -0,0 +1,18 @@
1
+ task Complete
2
+ refinement
3
+ FirstTask
4
+ if everyoneHappy
5
+ par
6
+ MakeJoy
7
+ DrinkBeers
8
+ end
9
+ elsif someoneHappy
10
+ DrinkBeer
11
+ else
12
+ DoNothingSpecial
13
+ end
14
+ while not(endOfTheDay)
15
+ WaitAndSee
16
+ end
17
+ end
18
+ end
@@ -1,19 +1,57 @@
1
- [:unit,
2
- [:task_def, "Simple",
3
- [:task_signature,
4
- [:fluent, "diagKnown",
5
- [:event_set, "Diagnosis:start"],
6
- [:event_set, "Treatment:end"],
7
- nil ]
8
- ],
9
- [:task_refinement,
10
- [:seq_st,
11
- [:if_st,
12
- [:bool_not, [:var_ref, "diagKnown"]],
13
- [:task_call_st, "Diagnosis"],
14
- [:else_clause,
15
- [:task_call_st, "NoDiag"] ]],
16
- [:task_call_st, "Treatment"]]
17
- ]
18
- ]
19
- ]
1
+ [
2
+ :unit,
3
+ [
4
+ :task_def,
5
+ "Simple",
6
+ [
7
+ :task_signature,
8
+ [
9
+ :fluent,
10
+ "diagKnown",
11
+ [
12
+ :event_set,
13
+ "Diagnosis:start"
14
+ ],
15
+ [
16
+ :event_set,
17
+ "Treatment:end"
18
+ ],
19
+ nil
20
+ ]
21
+ ],
22
+ [
23
+ :task_refinement,
24
+ [
25
+ :seq_st,
26
+ [
27
+ :if_st,
28
+ [
29
+ :bool_expr,
30
+ [
31
+ :bool_not,
32
+ [
33
+ :var_ref,
34
+ "diagKnown"
35
+ ]
36
+ ]
37
+ ],
38
+ [
39
+ :task_call_st,
40
+ "Diagnosis"
41
+ ],
42
+ [
43
+ :else_clause,
44
+ [
45
+ :task_call_st,
46
+ "NoDiag"
47
+ ]
48
+ ]
49
+ ],
50
+ [
51
+ :task_call_st,
52
+ "Treatment"
53
+ ]
54
+ ]
55
+ ]
56
+ ]
57
+ ]
data/spec/spec_helper.rb CHANGED
@@ -21,6 +21,14 @@ module SpecHelpers
21
21
  fixtures_dir.glob(glob)
22
22
  end
23
23
 
24
+ def simple_ast
25
+ Gisele::ast(fixtures_dir/:tasks/"simple.gis")
26
+ end
27
+
28
+ def complete_ast
29
+ Gisele::ast(fixtures_dir/:tasks/"complete.gis")
30
+ end
31
+
24
32
  end
25
33
 
26
34
  RSpec.configure do |c|
@@ -21,6 +21,14 @@ module Gisele::Language::AST
21
21
 
22
22
  end # children
23
23
 
24
+ describe 'markers' do
25
+
26
+ it 'defaults to an empty hash' do
27
+ node([:hello]).markers.should eq({})
28
+ end
29
+
30
+ end # markers
31
+
24
32
  describe 'copy' do
25
33
 
26
34
  it 'collects block results ala `inject`' do
@@ -40,6 +48,13 @@ module Gisele::Language::AST
40
48
  target.should be_a(Unit)
41
49
  end
42
50
 
51
+ it 'keeps the markers unchanged' do
52
+ node = node([:unit], {:hello => "World"})
53
+ copy = node.copy do |memo,child| end
54
+ copy.markers.should eq({:hello => "World"})
55
+ copy.markers.object_id.should_not eq(node.markers.object_id)
56
+ end
57
+
43
58
  end
44
59
 
45
60
  describe 'dup' do
@@ -55,6 +70,13 @@ module Gisele::Language::AST
55
70
  node(arr).dup.should be_a(Node)
56
71
  end
57
72
 
73
+ it 'keeps the markers unchanged' do
74
+ node = node([:unit, "etc."], {:hello => "World"})
75
+ copy = node.dup
76
+ copy.markers.should eq({:hello => "World"})
77
+ copy.markers.object_id.should_not eq(node.markers.object_id)
78
+ end
79
+
58
80
  end # dup
59
81
 
60
82
  end
@@ -16,7 +16,9 @@ module Gisele::Language
16
16
  source = ast("if goodCond Task1 end")
17
17
  expected = \
18
18
  [:case_st,
19
- [:when_clause, [:var_ref, "goodCond"], [:task_call_st, "Task1"] ]]
19
+ [:when_clause,
20
+ [:bool_expr, [:var_ref, "goodCond"]],
21
+ [:task_call_st, "Task1"] ]]
20
22
  rewrite(source).should eq(expected)
21
23
  end
22
24
 
@@ -24,8 +26,12 @@ module Gisele::Language
24
26
  source = ast("if goodCond Task1 else Task2 end")
25
27
  expected = \
26
28
  [: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
+ [:when_clause,
30
+ [:bool_expr, [:var_ref, "goodCond"]],
31
+ [:task_call_st, "Task1"] ],
32
+ [:when_clause,
33
+ [:bool_expr, [:bool_not, [:var_ref, "goodCond"]]],
34
+ [:task_call_st, "Task2"] ]
29
35
  ]
30
36
  rewrite(source).should eq(expected)
31
37
  end
@@ -41,26 +47,26 @@ module Gisele::Language
41
47
  expected = \
42
48
  [:case_st,
43
49
  [:when_clause,
44
- [:var_ref, "c1"],
50
+ [:bool_expr, [:var_ref, "c1"]],
45
51
  [:task_call_st, "Task1"] ],
46
52
  [:when_clause,
47
- [:bool_and,
53
+ [:bool_expr, [:bool_and,
48
54
  [:var_ref, "c2"],
49
- [:bool_not, [:var_ref, "c1"]] ],
55
+ [:bool_not, [:var_ref, "c1"]] ]],
50
56
  [:task_call_st, "Task2"] ],
51
57
  [:when_clause,
52
- [:bool_and,
58
+ [:bool_expr, [:bool_and,
53
59
  [:var_ref, "c3"],
54
60
  [:bool_and,
55
61
  [:bool_not, [:var_ref, "c2"]],
56
- [:bool_not, [:var_ref, "c1"]] ]],
62
+ [:bool_not, [:var_ref, "c1"]] ]]],
57
63
  [:task_call_st, "Task3"] ],
58
64
  [:when_clause,
59
- [:bool_and,
65
+ [:bool_expr, [:bool_and,
60
66
  [:bool_not, [:var_ref, "c3"]],
61
67
  [:bool_and,
62
68
  [:bool_not, [:var_ref, "c2"]],
63
- [:bool_not, [:var_ref, "c1"]]]],
69
+ [:bool_not, [:var_ref, "c1"]]]]],
64
70
  [:task_call_st, "Task4"] ],
65
71
  ]
66
72
  rewrite(source).should eq(expected)
@@ -71,10 +77,10 @@ module Gisele::Language
71
77
  expected = \
72
78
  [:case_st,
73
79
  [:when_clause,
74
- [:bool_not, [:var_ref, "goodCond"]],
80
+ [:bool_expr, [:bool_not, [:var_ref, "goodCond"]]],
75
81
  [:task_call_st, "Task1"] ],
76
82
  [:when_clause,
77
- [:var_ref, "goodCond"],
83
+ [:bool_expr, [:var_ref, "goodCond"]],
78
84
  [:task_call_st, "Task2"] ] ]
79
85
  rewrite(source).should eq(expected)
80
86
  end
@@ -85,6 +91,20 @@ module Gisele::Language
85
91
  rewrite([:unit, if_st]).should eq([:unit, rw_st])
86
92
  end
87
93
 
94
+ it 'keeps traceability markers on a single if' do
95
+ if_st = ast("if goodCond Task1 end")
96
+ rw_st = rewrite(if_st)
97
+ rw_st.markers.should eq(if_st.markers)
98
+ rw_st.last.markers.should eq(if_st.markers)
99
+ end
100
+
101
+ it 'keeps traceability markers when a else if present' do
102
+ if_st = ast("if goodCond Task1 else Task2 end")
103
+ rw_st = rewrite(if_st)
104
+ rw_st.markers.should eq(if_st.markers)
105
+ rw_st.last.markers.should eq(if_st.last.markers)
106
+ end
107
+
88
108
  end
89
109
  end
90
110
  end
@@ -11,20 +11,27 @@ module Gisele::Language::Syntax
11
11
  describe "the bool_expr rule" do
12
12
 
13
13
  it 'returns expected ast on simple expressions' do
14
- expected = [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]]
14
+ expected = \
15
+ [:bool_expr, [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]]]
15
16
  ast("diagKnown and platLow", :bool_expr).should eq(expected)
16
17
  end
17
18
 
18
19
  it 'respects priorities' do
19
- expected = [:bool_or, [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]], [:var_ref, "platHigh"]]
20
+ expected = [:bool_expr,
21
+ [:bool_or, [:bool_and, [:var_ref, "diagKnown"], [:var_ref, "platLow"]], [:var_ref, "platHigh"]]]
20
22
  ast("diagKnown and platLow or platHigh", :bool_expr).should eq(expected)
21
23
  end
22
24
 
23
25
  it 'supports double negations' do
24
- expected = [:bool_not, [:bool_not, [:var_ref, "diagKnown"]]]
26
+ expected = [:bool_expr, [:bool_not, [:bool_not, [:var_ref, "diagKnown"]]]]
25
27
  ast("not not(diagKnown)", :bool_expr).should eq(expected)
26
28
  end
27
29
 
30
+ it 'makes boolean literals explicit' do
31
+ ast("true", :bool_expr).should eq([:bool_expr, [:bool_lit, true]])
32
+ ast("false", :bool_expr).should eq([:bool_expr, [:bool_lit, false]])
33
+ end
34
+
28
35
  end # bool_expr
29
36
 
30
37
  describe 'the event_set rule' do
@@ -131,7 +138,7 @@ module Gisele::Language::Syntax
131
138
  expr = "while goodCond Task1 end"
132
139
  expected = \
133
140
  [:while_st,
134
- [:var_ref, "goodCond"],
141
+ [:bool_expr, [:var_ref, "goodCond"]],
135
142
  [:task_call_st, "Task1"]]
136
143
  ast(expr, :while_st).should eq(expected)
137
144
  end
@@ -140,7 +147,7 @@ module Gisele::Language::Syntax
140
147
  expr = "while goodCond Task1 Task2 end"
141
148
  expected = \
142
149
  [:while_st,
143
- [:var_ref, "goodCond"],
150
+ [:bool_expr, [:var_ref, "goodCond"]],
144
151
  [:seq_st, [:task_call_st, "Task1"], [:task_call_st, "Task2"]]]
145
152
  ast(expr, :while_st).should eq(expected)
146
153
  end
@@ -163,7 +170,9 @@ module Gisele::Language::Syntax
163
170
  it 'parses as expected' do
164
171
  expr = "elsif goodCond Task1 "
165
172
  expected = \
166
- [:elsif_clause, [:var_ref, "goodCond"], [:task_call_st, "Task1"]]
173
+ [:elsif_clause,
174
+ [:bool_expr, [:var_ref, "goodCond"]],
175
+ [:task_call_st, "Task1"]]
167
176
  ast(expr, :elsif_clause).should eq(expected)
168
177
  end
169
178
 
@@ -174,7 +183,9 @@ module Gisele::Language::Syntax
174
183
  it 'parses as expected' do
175
184
  expr = "if goodCond Task1 end"
176
185
  expected = \
177
- [:if_st, [:var_ref, "goodCond"], [:task_call_st, "Task1"]]
186
+ [:if_st,
187
+ [:bool_expr, [:var_ref, "goodCond"]],
188
+ [:task_call_st, "Task1"]]
178
189
  ast(expr, :if_st).should eq(expected)
179
190
  end
180
191
 
@@ -182,7 +193,8 @@ module Gisele::Language::Syntax
182
193
  expr = "if goodCond Task1 else Task2 end"
183
194
  expected = \
184
195
  [:if_st,
185
- [:var_ref, "goodCond"], [:task_call_st, "Task1"],
196
+ [:bool_expr, [:var_ref, "goodCond"]],
197
+ [:task_call_st, "Task1"],
186
198
  [:else_clause, [:task_call_st, "Task2"]] ]
187
199
  ast(expr, :if_st).should eq(expected)
188
200
  end
@@ -191,11 +203,11 @@ module Gisele::Language::Syntax
191
203
  expr = "if goodCond Task1 elsif otherCond Task2 elsif stillAnother Task3 else Task4 end"
192
204
  expected = \
193
205
  [:if_st,
194
- [:var_ref, "goodCond"], [:task_call_st, "Task1"],
206
+ [:bool_expr, [:var_ref, "goodCond"]], [:task_call_st, "Task1"],
195
207
  [:elsif_clause,
196
- [:var_ref, "otherCond"], [:task_call_st, "Task2"]],
208
+ [:bool_expr, [:var_ref, "otherCond"]], [:task_call_st, "Task2"]],
197
209
  [:elsif_clause,
198
- [:var_ref, "stillAnother"], [:task_call_st, "Task3"]],
210
+ [:bool_expr, [:var_ref, "stillAnother"]], [:task_call_st, "Task3"]],
199
211
  [:else_clause,
200
212
  [:task_call_st, "Task4"]] ]
201
213
  ast(expr, :if_st).should eq(expected)
@@ -30,6 +30,11 @@ module Gisele::Language
30
30
  ast.first.should eq(:if_st)
31
31
  end
32
32
 
33
+ it 'sets traceability marks correctly' do
34
+ ast = ast("if goodCond Task1 end", :root => :if_st)
35
+ ast.markers[:match].should_not be_nil
36
+ end
37
+
33
38
  fixture_files('tasks/**/*.gis').each do |file|
34
39
  it "works on #{file}" do
35
40
  parsed = Syntax.ast(file)
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ module Gisele::Language
3
+ describe ToGraph do
4
+
5
+ it 'returns a Digraph' do
6
+ ToGraph.new.call(complete_ast).should be_a(Yargi::Digraph)
7
+ end
8
+
9
+ end
10
+ end