gisele 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +73 -44
- data/Gemfile.lock +1 -0
- data/examples/meeting-scheduler/MeetingScheduling.gis +23 -23
- data/examples/rectal-cancer-pathway/RectalCancerPathway.gis +64 -0
- data/gisele.noespec +1 -1
- data/lib/gisele/command.rb +6 -4
- data/lib/gisele/language/ast/case_st.rb +14 -0
- data/lib/gisele/language/ast/else_clause.rb +14 -0
- data/lib/gisele/language/ast/node.rb +3 -1
- data/lib/gisele/language/ast/{unit.rb → unit_def.rb} +2 -2
- data/lib/gisele/language/dot.yml +1 -1
- data/lib/gisele/language/elsif_flattener.rb +35 -0
- data/lib/gisele/language/if_to_case.rb +63 -0
- data/lib/gisele/language/rewriter/helper.rb +41 -0
- data/lib/gisele/language/rewriter/scoping.rb +34 -0
- data/lib/gisele/language/rewriter/work_on_nodes.rb +30 -0
- data/lib/gisele/language/rewriter.rb +60 -0
- data/lib/gisele/language/sugar_removal.rb +2 -64
- data/lib/gisele/language/syntax/case_st.rb +19 -0
- data/lib/gisele/language/syntax/grammar.citrus +61 -28
- data/lib/gisele/language/syntax/node.rb +3 -3
- data/lib/gisele/language/syntax/task_def.rb +5 -4
- data/lib/gisele/language/syntax/{unit.rb → unit_def.rb} +3 -3
- data/lib/gisele/language/syntax/when_clause.rb +16 -0
- data/lib/gisele/language/to_graph.rb +43 -16
- data/lib/gisele/language.rb +25 -1
- data/lib/gisele/version.rb +1 -1
- data/spec/command/main/gisele_ast_ruby.stdout +29 -35
- data/spec/command/main/gisele_graph.stdout +24 -14
- data/spec/command/main/gisele_help.stdout +1 -1
- data/spec/command/main/gisele_no_sugar.cmd +1 -1
- data/spec/command/main/gisele_no_sugar.stdout +119 -25
- data/spec/command/main/gisele_version.stdout +1 -1
- data/spec/fixtures/tasks/complete.gis +24 -2
- data/spec/fixtures/tasks/simple.ast +29 -35
- data/spec/fixtures/tasks/simple.gis +7 -2
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/language/ast/test_node.rb +10 -11
- data/spec/unit/language/rewriter/test_helper.rb +87 -0
- data/spec/unit/language/rewriter/test_scoping.rb +46 -0
- data/spec/unit/language/rewriter/test_work_on_nodes.rb +45 -0
- data/spec/unit/language/syntax/grammar/test_bool_expr.rb +34 -0
- data/spec/unit/language/syntax/grammar/test_boolean_literal.rb +17 -0
- data/spec/unit/language/syntax/grammar/test_case_st.rb +60 -0
- data/spec/unit/language/syntax/grammar/test_event.rb +18 -0
- data/spec/unit/language/syntax/grammar/test_event_name.rb +21 -0
- data/spec/unit/language/syntax/grammar/test_event_set.rb +27 -0
- data/spec/unit/language/syntax/grammar/test_fluent_def.rb +21 -0
- data/spec/unit/language/syntax/grammar/test_if_st.rb +21 -0
- data/spec/unit/language/syntax/grammar/test_par_st.rb +11 -0
- data/spec/unit/language/syntax/grammar/test_process_statement.rb +19 -0
- data/spec/unit/language/syntax/grammar/test_seq_st.rb +11 -0
- data/spec/unit/language/syntax/grammar/test_spaces.rb +19 -0
- data/spec/unit/language/syntax/grammar/test_spacing.rb +18 -0
- data/spec/unit/language/syntax/grammar/test_task_def.rb +35 -0
- data/spec/unit/language/syntax/grammar/test_task_name.rb +19 -0
- data/spec/unit/language/syntax/grammar/test_task_start_or_end.rb +17 -0
- data/spec/unit/language/syntax/grammar/test_trackvar_def.rb +21 -0
- data/spec/unit/language/syntax/grammar/test_unit_def.rb +29 -0
- data/spec/unit/language/syntax/grammar/test_variable_name.rb +30 -0
- data/spec/unit/language/syntax/grammar/test_when_clause.rb +21 -0
- data/spec/unit/language/syntax/grammar/test_while_st.rb +11 -0
- data/spec/unit/language/syntax/to_ast/test_bool_expr.rb +28 -0
- data/spec/unit/language/syntax/to_ast/test_case_st.rb +47 -0
- data/spec/unit/language/syntax/to_ast/test_else_clause.rb +13 -0
- data/spec/unit/language/syntax/to_ast/test_elsif_clause.rb +15 -0
- data/spec/unit/language/syntax/to_ast/test_event_set.rb +24 -0
- data/spec/unit/language/syntax/to_ast/test_fluent_def.rb +26 -0
- data/spec/unit/language/syntax/to_ast/test_if_st.rb +39 -0
- data/spec/unit/language/syntax/to_ast/test_par_st.rb +12 -0
- data/spec/unit/language/syntax/to_ast/test_seq_st.rb +12 -0
- data/spec/unit/language/syntax/to_ast/test_task_call_st.rb +10 -0
- data/spec/unit/language/syntax/to_ast/test_task_def.rb +44 -0
- data/spec/unit/language/syntax/to_ast/test_trackvar_def.rb +26 -0
- data/spec/unit/language/syntax/to_ast/test_unit_def.rb +28 -0
- data/spec/unit/language/syntax/to_ast/test_var_ref.rb +12 -0
- data/spec/unit/language/syntax/to_ast/test_when_clause.rb +15 -0
- data/spec/unit/language/syntax/to_ast/test_while_st.rb +24 -0
- data/spec/unit/language/test_ast.rb +4 -4
- data/spec/unit/language/test_elsif_flattener.rb +90 -0
- data/spec/unit/language/test_if_to_case.rb +107 -0
- data/spec/unit/language/{test_transformer.rb → test_rewriter.rb} +24 -11
- data/spec/unit/language/test_syntax.rb +1 -1
- data/spec/unit/language/test_to_graph.rb +9 -2
- metadata +120 -34
- data/lib/gisele/language/syntax/task_refinement.rb +0 -15
- data/lib/gisele/language/syntax/task_signature.rb +0 -15
- data/lib/gisele/language/transformer.rb +0 -38
- data/spec/unit/language/sugar_removal/test_if_to_guarded_commands.rb +0 -110
- data/spec/unit/language/syntax/test_grammar.rb +0 -317
- 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
|
-
|
31
|
+
* Enhancements
|
4
32
|
|
5
|
-
* A --graph option has been added to the main `gisele` shell command. It outputs a graph
|
6
|
-
|
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
|
-
|
36
|
+
* Breaking changes
|
9
37
|
|
10
|
-
* Boolean literals (true, false) are now explicitly represented in boolean expressions,
|
11
|
-
|
12
|
-
* All statements and clauses relying on boolean conditions (if_st, while_st, elsif_clause,
|
13
|
-
|
14
|
-
|
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
|
-
|
19
|
-
|
20
|
-
* The main `gisele` command now accepts a --no-sugar option that removes syntactic
|
21
|
-
|
22
|
-
|
23
|
-
* All AST nodes (obtained via `Gisele.ast` or similar) now include the module
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
* The language package has been reorganized and is now considered as belonging to
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
* The top AST element of a process file is always a :unit. The latter may contain
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
* The following syntax nodes have been renamed:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
@@ -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
|
-
|
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
data/lib/gisele/command.rb
CHANGED
@@ -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 =
|
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
|
-
|
83
|
-
|
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
|
@@ -77,11 +77,13 @@ module Gisele
|
|
77
77
|
end # module AST
|
78
78
|
end # module Language
|
79
79
|
end # module Gisele
|
80
|
-
require_relative '
|
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'
|
data/lib/gisele/language/dot.yml
CHANGED
@@ -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'
|