gisele 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/CHANGELOG.md +73 -44
  2. data/Gemfile.lock +1 -0
  3. data/examples/meeting-scheduler/MeetingScheduling.gis +23 -23
  4. data/examples/rectal-cancer-pathway/RectalCancerPathway.gis +64 -0
  5. data/gisele.noespec +1 -1
  6. data/lib/gisele/command.rb +6 -4
  7. data/lib/gisele/language/ast/case_st.rb +14 -0
  8. data/lib/gisele/language/ast/else_clause.rb +14 -0
  9. data/lib/gisele/language/ast/node.rb +3 -1
  10. data/lib/gisele/language/ast/{unit.rb → unit_def.rb} +2 -2
  11. data/lib/gisele/language/dot.yml +1 -1
  12. data/lib/gisele/language/elsif_flattener.rb +35 -0
  13. data/lib/gisele/language/if_to_case.rb +63 -0
  14. data/lib/gisele/language/rewriter/helper.rb +41 -0
  15. data/lib/gisele/language/rewriter/scoping.rb +34 -0
  16. data/lib/gisele/language/rewriter/work_on_nodes.rb +30 -0
  17. data/lib/gisele/language/rewriter.rb +60 -0
  18. data/lib/gisele/language/sugar_removal.rb +2 -64
  19. data/lib/gisele/language/syntax/case_st.rb +19 -0
  20. data/lib/gisele/language/syntax/grammar.citrus +61 -28
  21. data/lib/gisele/language/syntax/node.rb +3 -3
  22. data/lib/gisele/language/syntax/task_def.rb +5 -4
  23. data/lib/gisele/language/syntax/{unit.rb → unit_def.rb} +3 -3
  24. data/lib/gisele/language/syntax/when_clause.rb +16 -0
  25. data/lib/gisele/language/to_graph.rb +43 -16
  26. data/lib/gisele/language.rb +25 -1
  27. data/lib/gisele/version.rb +1 -1
  28. data/spec/command/main/gisele_ast_ruby.stdout +29 -35
  29. data/spec/command/main/gisele_graph.stdout +24 -14
  30. data/spec/command/main/gisele_help.stdout +1 -1
  31. data/spec/command/main/gisele_no_sugar.cmd +1 -1
  32. data/spec/command/main/gisele_no_sugar.stdout +119 -25
  33. data/spec/command/main/gisele_version.stdout +1 -1
  34. data/spec/fixtures/tasks/complete.gis +24 -2
  35. data/spec/fixtures/tasks/simple.ast +29 -35
  36. data/spec/fixtures/tasks/simple.gis +7 -2
  37. data/spec/spec_helper.rb +10 -0
  38. data/spec/unit/language/ast/test_node.rb +10 -11
  39. data/spec/unit/language/rewriter/test_helper.rb +87 -0
  40. data/spec/unit/language/rewriter/test_scoping.rb +46 -0
  41. data/spec/unit/language/rewriter/test_work_on_nodes.rb +45 -0
  42. data/spec/unit/language/syntax/grammar/test_bool_expr.rb +34 -0
  43. data/spec/unit/language/syntax/grammar/test_boolean_literal.rb +17 -0
  44. data/spec/unit/language/syntax/grammar/test_case_st.rb +60 -0
  45. data/spec/unit/language/syntax/grammar/test_event.rb +18 -0
  46. data/spec/unit/language/syntax/grammar/test_event_name.rb +21 -0
  47. data/spec/unit/language/syntax/grammar/test_event_set.rb +27 -0
  48. data/spec/unit/language/syntax/grammar/test_fluent_def.rb +21 -0
  49. data/spec/unit/language/syntax/grammar/test_if_st.rb +21 -0
  50. data/spec/unit/language/syntax/grammar/test_par_st.rb +11 -0
  51. data/spec/unit/language/syntax/grammar/test_process_statement.rb +19 -0
  52. data/spec/unit/language/syntax/grammar/test_seq_st.rb +11 -0
  53. data/spec/unit/language/syntax/grammar/test_spaces.rb +19 -0
  54. data/spec/unit/language/syntax/grammar/test_spacing.rb +18 -0
  55. data/spec/unit/language/syntax/grammar/test_task_def.rb +35 -0
  56. data/spec/unit/language/syntax/grammar/test_task_name.rb +19 -0
  57. data/spec/unit/language/syntax/grammar/test_task_start_or_end.rb +17 -0
  58. data/spec/unit/language/syntax/grammar/test_trackvar_def.rb +21 -0
  59. data/spec/unit/language/syntax/grammar/test_unit_def.rb +29 -0
  60. data/spec/unit/language/syntax/grammar/test_variable_name.rb +30 -0
  61. data/spec/unit/language/syntax/grammar/test_when_clause.rb +21 -0
  62. data/spec/unit/language/syntax/grammar/test_while_st.rb +11 -0
  63. data/spec/unit/language/syntax/to_ast/test_bool_expr.rb +28 -0
  64. data/spec/unit/language/syntax/to_ast/test_case_st.rb +47 -0
  65. data/spec/unit/language/syntax/to_ast/test_else_clause.rb +13 -0
  66. data/spec/unit/language/syntax/to_ast/test_elsif_clause.rb +15 -0
  67. data/spec/unit/language/syntax/to_ast/test_event_set.rb +24 -0
  68. data/spec/unit/language/syntax/to_ast/test_fluent_def.rb +26 -0
  69. data/spec/unit/language/syntax/to_ast/test_if_st.rb +39 -0
  70. data/spec/unit/language/syntax/to_ast/test_par_st.rb +12 -0
  71. data/spec/unit/language/syntax/to_ast/test_seq_st.rb +12 -0
  72. data/spec/unit/language/syntax/to_ast/test_task_call_st.rb +10 -0
  73. data/spec/unit/language/syntax/to_ast/test_task_def.rb +44 -0
  74. data/spec/unit/language/syntax/to_ast/test_trackvar_def.rb +26 -0
  75. data/spec/unit/language/syntax/to_ast/test_unit_def.rb +28 -0
  76. data/spec/unit/language/syntax/to_ast/test_var_ref.rb +12 -0
  77. data/spec/unit/language/syntax/to_ast/test_when_clause.rb +15 -0
  78. data/spec/unit/language/syntax/to_ast/test_while_st.rb +24 -0
  79. data/spec/unit/language/test_ast.rb +4 -4
  80. data/spec/unit/language/test_elsif_flattener.rb +90 -0
  81. data/spec/unit/language/test_if_to_case.rb +107 -0
  82. data/spec/unit/language/{test_transformer.rb → test_rewriter.rb} +24 -11
  83. data/spec/unit/language/test_syntax.rb +1 -1
  84. data/spec/unit/language/test_to_graph.rb +9 -2
  85. metadata +120 -34
  86. data/lib/gisele/language/syntax/task_refinement.rb +0 -15
  87. data/lib/gisele/language/syntax/task_signature.rb +0 -15
  88. data/lib/gisele/language/transformer.rb +0 -38
  89. data/spec/unit/language/sugar_removal/test_if_to_guarded_commands.rb +0 -110
  90. data/spec/unit/language/syntax/test_grammar.rb +0 -317
  91. data/spec/unit/language/syntax/test_to_ast.rb +0 -257
data/CHANGELOG.md CHANGED
@@ -1,55 +1,84 @@
1
+ # 0.3.0 / 2012-02-20
2
+
3
+ * Major enhancements
4
+
5
+ * The ugly "refinement ... end" construction has been removed. See info in the breaking
6
+ changes section below.
7
+ * The grammar now allows a task definition to contain the definition of sub tasks. The
8
+ idiomatic place is after fluent and trackvar definitions and before the refinement.
9
+
10
+ * Minor enhancements
11
+
12
+ * The --graph option now outputs one graph for each task_def in the unit. Previously only
13
+ the graph of the last task definition was printed.
14
+
15
+ * Bugfixes
16
+
17
+ * Missing edges for absent else clauses are now added to the graph generated by
18
+ the --graph option (ToGraph).
19
+
20
+ * Breaking changes
21
+
22
+ * The syntax and AST of a task definition has changed. The "refinement ... end" construct
23
+ has been removed and replaced by an explicit process statement. Accordingly, the nodes
24
+ task_refinement and task_signature have been removed as well. The AST signature of a task
25
+ definition is now as follows:
26
+
27
+ [:task_def, "TaskName", (fluent_def, trackvar_def, task_def)*, explicit_statement]
28
+
1
29
  # 0.2.0 / 2012-02-17
2
30
 
3
- ## Enhancements
31
+ * Enhancements
4
32
 
5
- * A --graph option has been added to the main `gisele` shell command. It outputs a graph in
6
- the graphviz/dot format representing a process as a box-and-arrow workflow.
33
+ * A --graph option has been added to the main `gisele` shell command. It outputs a graph
34
+ in the graphviz/dot format representing a process as a box-and-arrow workflow.
7
35
 
8
- ## Breaking changes
36
+ * Breaking changes
9
37
 
10
- * Boolean literals (true, false) are now explicitly represented in boolean expressions,
11
- under a :bool_lit AST node.
12
- * All statements and clauses relying on boolean conditions (if_st, while_st, elsif_clause,
13
- when_clause) have now an explicit :bool_expr node as first child. Previously, a subnode
14
- of the boolean grammar was used.
38
+ * Boolean literals (true, false) are now explicitly represented in boolean expressions,
39
+ under a :bool_lit AST node.
40
+ * All statements and clauses relying on boolean conditions (if_st, while_st, elsif_clause,
41
+ when_clause) have now an explicit :bool_expr node as first child. Previously, a subnode
42
+ of the boolean grammar was used.
15
43
 
16
44
  # 0.1.0 / 2012-02-17
17
45
 
18
- ## Enhancements
19
-
20
- * The main `gisele` command now accepts a --no-sugar option that removes syntactic
21
- sugar. This option is limited to the rewriting of `if` statements as guarded `case`
22
- commands for now. Additional rewriting could be added in the future.
23
- * All AST nodes (obtained via `Gisele.ast` or similar) now include the module
24
- `Gisele::Language::AST::Node` which contains a few utilities.
25
-
26
- ## Breaking changes
27
-
28
- * The language package has been reorganized and is now considered as belonging to
29
- the private API. Further changes there will therefore not b considered as Breaking
30
- changes in the future (expect the structure of the AST, of course).
31
- The `Gisele.parse` and `Gisele.ast` methods belong to the public API and are therefore
32
- the way to (parse / get an AST) from a process file / source.
33
-
34
- * The top AST element of a process file is always a :unit. The latter may contain
35
- one ore more task definitions. Import/include nodes will probably be added later
36
- and authorized at the beginning of the file.
37
-
38
- * The following syntax nodes have been renamed:
39
- :varref -> :var_ref
40
- :or -> :bool_or
41
- :and -> :bool_and
42
- :not -> :bool_not
43
- :if -> :if_st
44
- :else -> :else_clause
45
- :elsif -> :elsif_clause
46
- :while -> :while_st
47
- :seq -> :seq_st
48
- :par -> :par_st
49
- :task_call -> :task_call_st
50
- :task -> :task_def
51
- :signature -> :task_signature
52
- :refinement -> :task_refinement
46
+ * Enhancements
47
+
48
+ * The main `gisele` command now accepts a --no-sugar option that removes syntactic
49
+ sugar. This option is limited to the rewriting of `if` statements as guarded `case`
50
+ commands for now. Additional rewriting could be added in the future.
51
+ * All AST nodes (obtained via `Gisele.ast` or similar) now include the module
52
+ `Gisele::Language::AST::Node` which contains a few utilities.
53
+
54
+ * Breaking changes
55
+
56
+ * The language package has been reorganized and is now considered as belonging to
57
+ the private API. Further changes there will therefore not b considered as Breaking
58
+ changes in the future (expect the structure of the AST, of course).
59
+ The `Gisele.parse` and `Gisele.ast` methods belong to the public API and are therefore
60
+ the way to (parse / get an AST) from a process file / source.
61
+
62
+ * The top AST element of a process file is always a :unit. The latter may contain
63
+ one ore more task definitions. Import/include nodes will probably be added later
64
+ and authorized at the beginning of the file.
65
+
66
+ * The following syntax nodes have been renamed:
67
+
68
+ :varref -> :var_ref
69
+ :or -> :bool_or
70
+ :and -> :bool_and
71
+ :not -> :bool_not
72
+ :if -> :if_st
73
+ :else -> :else_clause
74
+ :elsif -> :elsif_clause
75
+ :while -> :while_st
76
+ :seq -> :seq_st
77
+ :par -> :par_st
78
+ :task_call -> :task_call_st
79
+ :task -> :task_def
80
+ :signature -> :task_signature
81
+ :refinement -> :task_refinement
53
82
 
54
83
  # 0.0.1 / 2012-02-16
55
84
 
data/Gemfile.lock CHANGED
@@ -19,6 +19,7 @@ GEM
19
19
  yargi (0.2.0)
20
20
 
21
21
  PLATFORMS
22
+ java
22
23
  ruby
23
24
 
24
25
  DEPENDENCIES
@@ -1,34 +1,34 @@
1
1
  #
2
- # This is a Gisele process for the Meeting Scheduler example, similar to what can be found
3
- # in [Lam11]. The main difference between this model and the one defined there is the use
2
+ # This is a Gisele process for the Meeting Scheduler example, similar to what can be found
3
+ # in [Lam11]. The main difference between this model and the one defined there is the use
4
4
  # of tracking variables. Tracking variables are introduced and explained in [Dam11].
5
5
  #
6
6
  # The Meeting Scheduler examplar [Fea97]
7
7
  #
8
- # A meeting initiator issues a meeting request, specifying the expected participants and
9
- # the date range within which the meeting should take place. The scheduler then sends an
10
- # electronic invitation to each participant, requesting them to provide their date
11
- # constraints.
12
- #
13
- # A date conflict occurs when no date can be found that fits all participant constraints.
14
- # In such case, the initiator may extend the date range or request some participants to
15
- # weaken their constraints; a new scheduling cycle is then required. Otherwise, the
8
+ # A meeting initiator issues a meeting request, specifying the expected participants and
9
+ # the date range within which the meeting should take place. The scheduler then sends an
10
+ # electronic invitation to each participant, requesting them to provide their date
11
+ # constraints.
12
+ #
13
+ # A date conflict occurs when no date can be found that fits all participant constraints.
14
+ # In such case, the initiator may extend the date range or request some participants to
15
+ # weaken their constraints; a new scheduling cycle is then required. Otherwise, the
16
16
  # meeting is planned at a date meeting all constraints.
17
- #
18
- # A soft goal requires meetings to be scheduled as quickly as possible once initiated;
19
- # another one requires interactions with participants to be minimized. In the simplified
20
- # version considered here, only two scheduling cycles are allowed; the meeting is planned
21
- # after that. In such case, we will assume that the best date is chosen so as to maximize
22
- # the number of participants attending. We also ignore features like meeting cancellations,
17
+ #
18
+ # A soft goal requires meetings to be scheduled as quickly as possible once initiated;
19
+ # another one requires interactions with participants to be minimized. In the simplified
20
+ # version considered here, only two scheduling cycles are allowed; the meeting is planned
21
+ # after that. In such case, we will assume that the best date is chosen so as to maximize
22
+ # the number of participants attending. We also ignore features like meeting cancellations,
23
23
  # meeting locations, etc.
24
24
  #
25
25
  # References
26
26
  #
27
- # * [Dam11] Christophe Damas, Analyzing Multi-View Models of Software Systems, PhD thesis,
27
+ # * [Dam11] Christophe Damas, Analyzing Multi-View Models of Software Systems, PhD thesis,
28
28
  # University of Louvain, November 2011
29
- # * [Fea97] M.S. Feather, S. Fickas, A. Finkelstein and A. van Lamsweerde, Requirements and
29
+ # * [Fea97] M.S. Feather, S. Fickas, A. Finkelstein and A. van Lamsweerde, Requirements and
30
30
  # Specification Exemplars, Automated Software Engineering, 1997
31
- # * [Lam11] Bernard Lambeau, Synthesizing Multi-View Models of Software Systems, PhD thesis,
31
+ # * [Lam11] Bernard Lambeau, Synthesizing Multi-View Models of Software Systems, PhD thesis,
32
32
  # University of Louvain, November 2011
33
33
  #
34
34
  task MeetingScheduling
@@ -37,17 +37,17 @@ task MeetingScheduling
37
37
  # have been weakened or the date range extended.
38
38
  fluent secondCycle
39
39
  {WeakenConstraints:end, ExtendDateRange:end},
40
- {InitiateMeeting:end}
40
+ {InitiateMeeting:end}
41
41
  initially false
42
42
 
43
43
  # A date conflict may only occur when constraints are acquired or weakened. Being a tracking
44
44
  # variable and not fluent, we do not know the exact value of this variable when these tasks
45
45
  # occur.
46
- trackvar dateConflict
46
+ trackvar dateConflict
47
47
  {AcquireConstraints:end, WeakenConstraints:end}
48
48
  initially false
49
49
 
50
- # The arbitrator decides to resolve a conflict during the Arbitrate task. The following
50
+ # The arbitrator decides to resolve a conflict during the Arbitrate task. The following
51
51
  # definition does not capture the precise value of the variable but only that the value
52
52
  # can only change during Arbitrate.
53
53
  trackvar resolveByWeakening
@@ -55,7 +55,7 @@ task MeetingScheduling
55
55
  initially false
56
56
 
57
57
  # This is the task refinement
58
- refinement
58
+ seq
59
59
  InitiateMeeting
60
60
  AcquireConstraints
61
61
  while dateConflict and not(secondCycle)
@@ -0,0 +1,64 @@
1
+ #
2
+ # This is a Gisele process for the example of a rectal cancer care process, similar to the
3
+ # case study that can be found in [Dam11].
4
+ #
5
+ # References
6
+ #
7
+ # * [Dam11] Christophe Damas, Analyzing Multi-View Models of Software Systems, PhD thesis,
8
+ # University of Louvain, November 2011
9
+ task RectalCancerPathway
10
+
11
+ # This variable tracks if the cancer is treated in emergency
12
+ # in the initial state
13
+ trackvar emergency
14
+ {RectalCancerPathway:start}
15
+
16
+ # Is the cancer confirmed by the diagnosis?
17
+ trackvar cancerConfirmed
18
+ {Diagnosis:start}
19
+ initially false
20
+
21
+ # Is the emergency treatment envisioned by the
22
+ # multi-disciplinary staff?
23
+ trackvar surgeryEnvisioned
24
+ {StaffMeeting:end}
25
+ initially false
26
+
27
+ # The diagnosis task
28
+ task Diagnosis
29
+
30
+ fluent diagKnown
31
+ {EndoBio:end}, {}
32
+
33
+ fluent extensionKnown
34
+ {SpreadEvaluation:end}, {}
35
+
36
+ while not(diagKnown and extensionKnown)
37
+ Consultation
38
+ if diagKnown
39
+ SpreadEvaluation
40
+ else
41
+ EndoBio
42
+ end
43
+ end
44
+
45
+ end # Diagnosis
46
+
47
+ # main task refinement
48
+ if emergency
49
+ Emergency
50
+ Surgery
51
+ PostTreatment
52
+ else
53
+ Diagnosis
54
+ if cancerConfirmed
55
+ StaffMeeting
56
+ if surgeryEnvisioned
57
+ PreTreatment
58
+ Surgery
59
+ PostTreatment
60
+ end
61
+ end
62
+ end
63
+
64
+ end
data/gisele.noespec CHANGED
@@ -12,7 +12,7 @@ variables:
12
12
  upper:
13
13
  Gisele
14
14
  version:
15
- 0.2.0
15
+ 0.3.0
16
16
  summary: |-
17
17
  Gisele is a Process Analyzer Toolset
18
18
  description: |-
@@ -24,7 +24,7 @@ module Gisele
24
24
  # array for automatic processing.
25
25
  #
26
26
  # When --graph is used, the command parses the process file. It then converts the AST into
27
- # a directed graph representing the process as a box-and-arrow workflow and outputs it on
27
+ # a directed graph representing the process as a box-and-arrow workflow and outputs it on
28
28
  # standard output. For now, the only output format available is dot (from graphviz).
29
29
  #
30
30
  class Gisele::Command < Quickl::Command(__FILE__, __LINE__)
@@ -59,7 +59,7 @@ module Gisele
59
59
  end
60
60
 
61
61
  ast = Gisele.ast(file)
62
- ast = Gisele::Language::SugarRemoval.new.call(ast) unless @sugar
62
+ ast = Language::SugarRemoval.new.call(ast) unless @sugar
63
63
 
64
64
  print_ast(ast, @print_ast) if @print_ast
65
65
  print_graph(ast, @print_graph) if @print_graph
@@ -79,8 +79,10 @@ module Gisele
79
79
  end
80
80
 
81
81
  def print_graph(ast, option)
82
- graph = Gisele::Language::ToGraph.new.call(ast)
83
- puts graph.to_dot
82
+ graphs = Gisele::Language::ToGraph.new.call(ast)
83
+ graphs.each do |graph|
84
+ puts graph.to_dot
85
+ end
84
86
  end
85
87
 
86
88
  end # class Command
@@ -0,0 +1,14 @@
1
+ module Gisele
2
+ module Language
3
+ module AST
4
+ module CaseSt
5
+ include Node
6
+
7
+ def label
8
+ (self[1] && self[1].label) || ""
9
+ end
10
+
11
+ end # module CaseSt
12
+ end # module AST
13
+ end # module Language
14
+ end # module Gisele
@@ -0,0 +1,14 @@
1
+ module Gisele
2
+ module Language
3
+ module AST
4
+ module ElseClause
5
+ include Node
6
+
7
+ def label
8
+ "[else]"
9
+ end
10
+
11
+ end # module ElseClause
12
+ end # module AST
13
+ end # module Language
14
+ end # module Gisele
@@ -77,11 +77,13 @@ module Gisele
77
77
  end # module AST
78
78
  end # module Language
79
79
  end # module Gisele
80
- require_relative 'unit'
80
+ require_relative 'unit_def'
81
81
  require_relative 'task_call_st'
82
82
  require_relative 'while_st'
83
83
  require_relative 'if_st'
84
+ require_relative 'else_clause'
84
85
  require_relative 'elsif_clause'
86
+ require_relative 'case_st'
85
87
  require_relative 'when_clause'
86
88
  require_relative 'bool_expr'
87
89
  require_relative 'bool_and'
@@ -1,10 +1,10 @@
1
1
  module Gisele
2
2
  module Language
3
3
  module AST
4
- module Unit
4
+ module UnitDef
5
5
  include Node
6
6
 
7
- end # module Node
7
+ end # module UnitDef
8
8
  end # module AST
9
9
  end # module Language
10
10
  end # module Gisele
@@ -1,4 +1,4 @@
1
- task_refinement:
1
+ task_def:
2
2
  shape: circle
3
3
  style: filled
4
4
  fillcolor: black
@@ -0,0 +1,35 @@
1
+ module Gisele
2
+ module Language
3
+ class ElsifFlattener < Rewriter
4
+ alias :on_missing :copy_and_applyall
5
+
6
+ def on_if_st(node)
7
+ condition, dost, *clauses = node.children
8
+
9
+ base = [:if_st, condition, dost]
10
+ base = node(base, node.markers.dup)
11
+
12
+ clauses.inject base do |cur_if, clause|
13
+ rw_clause = call(clause)
14
+ cur_if << rw_clause
15
+ rw_clause.last
16
+ end
17
+
18
+ base
19
+ end
20
+
21
+ def on_elsif_clause(node)
22
+ base = \
23
+ [:else_clause,
24
+ [:if_st, node[1], mainflow.call(node[2])] ]
25
+ base = node(base, node.markers.dup)
26
+ end
27
+
28
+ def on_else_clause(node)
29
+ [:else_clause, mainflow.call(node.last)]
30
+ end
31
+
32
+ end # class ElsifFlattener
33
+ end # module Language
34
+ end # module Gisele
35
+
@@ -0,0 +1,63 @@
1
+ module Gisele
2
+ module Language
3
+ class IfToCase < Rewriter
4
+ alias :on_missing :copy_and_applyall
5
+
6
+ def on_if_st(node)
7
+ condition, dost, *clauses = node.children
8
+
9
+ # create case_st with same markers as the if_st
10
+ when_clause = [:when_clause, condition, mainflow.call(dost)]
11
+ when_clause = node(when_clause, node.markers.dup)
12
+ base = [:case_st, nil, when_clause]
13
+ base = node(base, node.markers.dup)
14
+
15
+ # this is the condition for elsif clauses
16
+ @condition = negate(condition.last)
17
+
18
+ # make injection now
19
+ clauses.inject base do |memo,clause|
20
+ memo << call(clause)
21
+ end
22
+ end
23
+
24
+ def on_elsif_clause(node)
25
+ condition, dost, = node.children
26
+
27
+ # install new conditions for me and next elsif clauses
28
+ condition = condition.last
29
+ previous = @condition
30
+ @condition = [:bool_and, negate(condition), @condition]
31
+
32
+ # convert elsif to when and keep the markers
33
+ base = \
34
+ [:when_clause,
35
+ [:bool_expr, [:bool_and, condition, previous]],
36
+ mainflow.call(dost) ]
37
+ node(base, node.markers.dup)
38
+ end
39
+
40
+ def on_else_clause(node)
41
+ dost, = node.children
42
+
43
+ # convert else to when and keep the markers
44
+ base = \
45
+ [:when_clause,
46
+ [:bool_expr, @condition],
47
+ mainflow.call(dost)]
48
+ node(base, node.markers.dup)
49
+ end
50
+
51
+ private
52
+
53
+ def negate(cond)
54
+ if cond.rule_name == :bool_not
55
+ cond.last
56
+ else
57
+ [:bool_not, cond]
58
+ end
59
+ end
60
+
61
+ end # class IfToCase
62
+ end # module Language
63
+ end # module Gisele
@@ -0,0 +1,41 @@
1
+ module Gisele
2
+ module Language
3
+ class Rewriter
4
+ class Helper
5
+
6
+ attr_accessor :next_in_chain
7
+
8
+ def self.install_on(rewriter)
9
+ methods = const_get(:Methods)
10
+ rewriter.module_eval do
11
+ include methods
12
+ end
13
+ rewriter.add_helper(new)
14
+ end
15
+
16
+ def call(rw, node, &bl)
17
+ meth = :"on_#{node.first}"
18
+ meth = :"on_missing" unless respond_to?(meth)
19
+ send(meth, rw, node) do |r,n|
20
+ next_call(r, n, bl)
21
+ end
22
+ end
23
+
24
+ def on_missing(rw, node)
25
+ yield(rw, node)
26
+ end
27
+
28
+ private
29
+
30
+ def next_call(rw, node, toplevel)
31
+ if nic = next_in_chain
32
+ nic.call(rw, node, &toplevel)
33
+ else
34
+ toplevel.call(rw, node)
35
+ end
36
+ end
37
+
38
+ end # class Helper
39
+ end # class Rewriter
40
+ end # module Language
41
+ end # module Gisele
@@ -0,0 +1,34 @@
1
+ module Gisele
2
+ module Language
3
+ class Rewriter
4
+ class Scoping < Helper
5
+
6
+ module Methods
7
+
8
+ def scope_stack
9
+ @scope_stack ||= []
10
+ end
11
+
12
+ def with_scope(scope)
13
+ scope_stack.push(scope)
14
+ result = yield
15
+ scope_stack.pop
16
+ result
17
+ end
18
+
19
+ end # module Methods
20
+
21
+ def on_missing(rw, node)
22
+ if node.rule_name.to_s =~ /_def/
23
+ rw.with_scope(node) do
24
+ yield(rw, node)
25
+ end
26
+ else
27
+ yield(rw, node)
28
+ end
29
+ end
30
+
31
+ end # module Scoping
32
+ end # class Rewriter
33
+ end # module Language
34
+ end # module Gisele
@@ -0,0 +1,30 @@
1
+ module Gisele
2
+ module Language
3
+ class Rewriter
4
+ class WorkOnNodes < Helper
5
+ include AST::Helpers
6
+
7
+ def call(rw, node, &bl)
8
+ node = pre_call(node)
9
+ tran = next_call(rw, node, bl)
10
+ post_call(tran)
11
+ end
12
+
13
+ protected
14
+
15
+ def pre_call(node)
16
+ unless looks_a_node?(node)
17
+ msg = "AST node expected, got #{node.inspect}"
18
+ raise ArgumentError, msg, caller
19
+ end
20
+ node(node)
21
+ end
22
+
23
+ def post_call(t)
24
+ looks_a_node?(t) ? node(t) : t
25
+ end
26
+
27
+ end # module WorkOnNodes
28
+ end # class Rewriter
29
+ end # module Language
30
+ end # module Gisele
@@ -0,0 +1,60 @@
1
+ module Gisele
2
+ module Language
3
+ class Rewriter
4
+ include AST::Helpers
5
+
6
+ ### class methods
7
+
8
+ def self.helpers
9
+ @helpers ||= [ WorkOnNodes.new ]
10
+ end
11
+
12
+ def self.add_helper(helper)
13
+ unless helpers.empty?
14
+ helpers.last.next_in_chain = helper
15
+ end
16
+ helpers << helper
17
+ end
18
+
19
+ ### instance methods
20
+
21
+ attr_reader :mainflow
22
+
23
+ def initialize(options = {})
24
+ @options = options
25
+ @mainflow = options[:mainflow] || self
26
+ end
27
+
28
+ def call(node)
29
+ help(node) do |n|
30
+ meth = :"on_#{node.first}"
31
+ meth = :"on_missing" unless respond_to?(meth)
32
+ send(meth, node)
33
+ end
34
+ end
35
+
36
+ def on_missing(node)
37
+ raise UnexpectedNodeError, "Unexpected node: #{node.inspect}"
38
+ end
39
+
40
+ def copy_and_applyall(node)
41
+ node.copy do |memo,child|
42
+ memo << (child.is_a?(AST::Node) ? call(child) : child)
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def help(node)
49
+ @first_helper ||= self.class.helpers.first
50
+ @first_helper.call(self, node) do |_,n|
51
+ yield(n)
52
+ end
53
+ end
54
+
55
+ end # class Rewriter
56
+ end # module Language
57
+ end # module Gisele
58
+ require_relative 'rewriter/helper'
59
+ require_relative 'rewriter/work_on_nodes'
60
+ require_relative 'rewriter/scoping'