gisele 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +16 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/gisele.gemspec +1 -0
- data/gisele.noespec +2 -1
- data/lib/gisele/command.rb +21 -6
- data/lib/gisele/language/ast/bool_and.rb +14 -0
- data/lib/gisele/language/ast/bool_expr.rb +14 -0
- data/lib/gisele/language/ast/bool_not.rb +14 -0
- data/lib/gisele/language/ast/bool_or.rb +14 -0
- data/lib/gisele/language/ast/elsif_clause.rb +14 -0
- data/lib/gisele/language/ast/helpers.rb +15 -9
- data/lib/gisele/language/ast/if_st.rb +14 -0
- data/lib/gisele/language/ast/node.rb +39 -3
- data/lib/gisele/language/ast/task_call_st.rb +14 -0
- data/lib/gisele/language/ast/var_ref.rb +14 -0
- data/lib/gisele/language/ast/when_clause.rb +14 -0
- data/lib/gisele/language/ast/while_st.rb +14 -0
- data/lib/gisele/language/ast.rb +2 -2
- data/lib/gisele/language/dot.yml +19 -0
- data/lib/gisele/language/sugar_removal.rb +30 -9
- data/lib/gisele/language/syntax/bool_expr.rb +14 -0
- data/lib/gisele/language/syntax/bool_lit.rb +1 -1
- data/lib/gisele/language/syntax/grammar.citrus +4 -3
- data/lib/gisele/language/syntax/node.rb +2 -1
- data/lib/gisele/language/to_graph.rb +119 -0
- data/lib/gisele/language.rb +4 -1
- data/lib/gisele/loader.rb +1 -0
- data/lib/gisele/version.rb +1 -1
- data/spec/command/main/gisele_ast_ruby.stdout +6 -3
- data/spec/command/main/gisele_graph.cmd +1 -0
- data/spec/command/main/gisele_graph.stdout +30 -0
- data/spec/command/main/gisele_help.stdout +10 -5
- data/spec/command/main/gisele_no_sugar.stdout +11 -5
- data/spec/command/main/gisele_version.stdout +1 -1
- data/spec/fixtures/tasks/complete.gis +18 -0
- data/spec/fixtures/tasks/simple.ast +57 -19
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/language/ast/test_node.rb +22 -0
- data/spec/unit/language/sugar_removal/test_if_to_guarded_commands.rb +32 -12
- data/spec/unit/language/syntax/test_to_ast.rb +23 -11
- data/spec/unit/language/test_syntax.rb +5 -0
- data/spec/unit/language/test_to_graph.rb +10 -0
- metadata +50 -18
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,19 @@
|
|
1
|
-
# 0.
|
1
|
+
# 0.2.0 / 2012-02-17
|
2
|
+
|
3
|
+
## Enhancements
|
4
|
+
|
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.
|
7
|
+
|
8
|
+
## Breaking changes
|
9
|
+
|
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.
|
15
|
+
|
16
|
+
# 0.1.0 / 2012-02-17
|
2
17
|
|
3
18
|
## Enhancements
|
4
19
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/gisele.gemspec
CHANGED
@@ -131,6 +131,7 @@ Gem::Specification.new do |s|
|
|
131
131
|
s.add_dependency("epath", "~> 0.0.1")
|
132
132
|
s.add_dependency("quickl", "~> 0.4.3")
|
133
133
|
s.add_dependency("awesome_print", "~> 1.0")
|
134
|
+
s.add_dependency("yargi", "~> 0.2.0")
|
134
135
|
|
135
136
|
# The version of ruby required by this gem
|
136
137
|
#
|
data/gisele.noespec
CHANGED
@@ -12,7 +12,7 @@ variables:
|
|
12
12
|
upper:
|
13
13
|
Gisele
|
14
14
|
version:
|
15
|
-
0.
|
15
|
+
0.2.0
|
16
16
|
summary: |-
|
17
17
|
Gisele is a Process Analyzer Toolset
|
18
18
|
description: |-
|
@@ -29,6 +29,7 @@ variables:
|
|
29
29
|
- {name: epath, version: "~> 0.0.1", groups: [runtime]}
|
30
30
|
- {name: quickl, version: "~> 0.4.3", groups: [runtime]}
|
31
31
|
- {name: awesome_print, version: "~> 1.0", groups: [runtime]}
|
32
|
+
- {name: yargi, version: "~> 0.2.0", groups: [runtime]}
|
32
33
|
#
|
33
34
|
- {name: rake, version: "~> 0.9.2", groups: [development]}
|
34
35
|
- {name: bundler, version: "~> 1.0", groups: [development]}
|
data/lib/gisele/command.rb
CHANGED
@@ -5,7 +5,7 @@ module Gisele
|
|
5
5
|
#
|
6
6
|
# SYNOPSIS
|
7
7
|
# gisele [--version] [--help]
|
8
|
-
# gisele [--ast] PROCESS_FILE
|
8
|
+
# gisele [--ast | --graph] PROCESS_FILE
|
9
9
|
#
|
10
10
|
# OPTIONS
|
11
11
|
# #{summarized_options}
|
@@ -14,14 +14,18 @@ module Gisele
|
|
14
14
|
# The Gisele process analyzer toolset provides tools and technique to model and analyze
|
15
15
|
# complex process models such as care processes.
|
16
16
|
#
|
17
|
-
# When --
|
17
|
+
# When --no-sugar is specified, syntactic sugar is first removed before making any other
|
18
|
+
# transformation. For now, this rewrites all `if` statements as explicit `case` guarded
|
19
|
+
# commands.
|
20
|
+
#
|
21
|
+
# When --ast is used, the command parses the process file and prints its Abstract Syntax
|
18
22
|
# Tree (AST) on standard output. By default, this option prints the AST for manual
|
19
23
|
# debugging, that is with colors and extra information. Use --ast=ruby to get a ruby
|
20
24
|
# array for automatic processing.
|
21
25
|
#
|
22
|
-
# When --
|
23
|
-
#
|
24
|
-
#
|
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
|
28
|
+
# standard output. For now, the only output format available is dot (from graphviz).
|
25
29
|
#
|
26
30
|
class Gisele::Command < Quickl::Command(__FILE__, __LINE__)
|
27
31
|
|
@@ -35,6 +39,10 @@ module Gisele
|
|
35
39
|
opt.on('--no-sugar', 'Apply syntactic sugar removal') do
|
36
40
|
@sugar = false
|
37
41
|
end
|
42
|
+
@print_graph = nil
|
43
|
+
opt.on('--graph=[MODE]', 'Converts and print a graph (dot)') do |value|
|
44
|
+
@print_graph = (value || "dot").to_sym
|
45
|
+
end
|
38
46
|
opt.on_tail('--help', "Show this help message") do
|
39
47
|
raise Quickl::Help
|
40
48
|
end
|
@@ -52,7 +60,9 @@ module Gisele
|
|
52
60
|
|
53
61
|
ast = Gisele.ast(file)
|
54
62
|
ast = Gisele::Language::SugarRemoval.new.call(ast) unless @sugar
|
63
|
+
|
55
64
|
print_ast(ast, @print_ast) if @print_ast
|
65
|
+
print_graph(ast, @print_graph) if @print_graph
|
56
66
|
end
|
57
67
|
|
58
68
|
private
|
@@ -68,5 +78,10 @@ module Gisele
|
|
68
78
|
ap ast, options
|
69
79
|
end
|
70
80
|
|
71
|
-
|
81
|
+
def print_graph(ast, option)
|
82
|
+
graph = Gisele::Language::ToGraph.new.call(ast)
|
83
|
+
puts graph.to_dot
|
84
|
+
end
|
85
|
+
|
86
|
+
end # class Command
|
72
87
|
end # module Gisele
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Gisele
|
2
|
+
module Language
|
3
|
+
module AST
|
4
|
+
module BoolAnd
|
5
|
+
include Node
|
6
|
+
|
7
|
+
def label
|
8
|
+
markers[:match] ? markers[:match].to_s : "(#{self[1].label} and #{self[2].label})"
|
9
|
+
end
|
10
|
+
|
11
|
+
end # module BoolAnd
|
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 BoolNot
|
5
|
+
include Node
|
6
|
+
|
7
|
+
def label
|
8
|
+
markers[:match] ? markers[:match].to_s : "not(#{last.label})"
|
9
|
+
end
|
10
|
+
|
11
|
+
end # module BoolNot
|
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 BoolOr
|
5
|
+
include Node
|
6
|
+
|
7
|
+
def label
|
8
|
+
markers[:match] ? markers[:match].to_s : "(#{self[1].label} or #{self[2].label})"
|
9
|
+
end
|
10
|
+
|
11
|
+
end # module BoolOr
|
12
|
+
end # module AST
|
13
|
+
end # module Language
|
14
|
+
end # module Gisele
|
@@ -3,17 +3,17 @@ module Gisele
|
|
3
3
|
module AST
|
4
4
|
module Helpers
|
5
5
|
|
6
|
-
def ast(arg)
|
7
|
-
return node(arg) if looks_a_node?(arg)
|
8
|
-
Syntax.ast(arg)
|
6
|
+
def ast(arg, markers = {})
|
7
|
+
return node(arg, markers) if looks_a_node?(arg)
|
8
|
+
ast(Syntax.ast(arg), markers)
|
9
9
|
end
|
10
10
|
|
11
|
-
def node(arg)
|
11
|
+
def node(arg, markers = {})
|
12
12
|
return arg if arg.is_a?(Node)
|
13
13
|
unless looks_a_node?(arg)
|
14
14
|
raise ArgumentError, "Array expected, #{arg.inspect} found."
|
15
15
|
end
|
16
|
-
extend_node(arg).tap do |node|
|
16
|
+
extend_node(arg, markers).tap do |node|
|
17
17
|
node.children.each{|c| node(c) if looks_a_node?(c)}
|
18
18
|
end
|
19
19
|
end
|
@@ -24,10 +24,16 @@ module Gisele
|
|
24
24
|
arg.is_a?(Node) or (arg.is_a?(Array) and arg.first.is_a?(Symbol))
|
25
25
|
end
|
26
26
|
|
27
|
-
def extend_node(arg)
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def extend_node(arg, markers)
|
28
|
+
mod = ast_module(arg)
|
29
|
+
arg.extend(mod).tap do |node|
|
30
|
+
node.markers = markers
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ast_module(node)
|
35
|
+
modname = Language.rule2mod(node.first)
|
36
|
+
AST.const_get(modname) rescue Node
|
31
37
|
end
|
32
38
|
|
33
39
|
extend(self)
|
@@ -3,6 +3,16 @@ module Gisele
|
|
3
3
|
module AST
|
4
4
|
module Node
|
5
5
|
|
6
|
+
# Returns the node markers
|
7
|
+
def markers
|
8
|
+
@markers ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sets node markers
|
12
|
+
def markers=(markers)
|
13
|
+
@markers = markers
|
14
|
+
end
|
15
|
+
|
6
16
|
# Returns the rule name, that is, the first Symbol element
|
7
17
|
# of the node array.
|
8
18
|
#
|
@@ -24,6 +34,11 @@ module Gisele
|
|
24
34
|
self[1..-1]
|
25
35
|
end
|
26
36
|
|
37
|
+
# Returns the associated ast_module
|
38
|
+
def ast_module
|
39
|
+
AST::Helpers.send(:ast_module, self)
|
40
|
+
end
|
41
|
+
|
27
42
|
# Applies copy-and-transform to this node.
|
28
43
|
#
|
29
44
|
# Example:
|
@@ -34,7 +49,7 @@ module Gisele
|
|
34
49
|
# # => [:something, ...]
|
35
50
|
#
|
36
51
|
def copy(&block)
|
37
|
-
base = AST.node([rule_name])
|
52
|
+
base = AST.node([rule_name], markers.dup)
|
38
53
|
children.inject(base, &block)
|
39
54
|
end
|
40
55
|
|
@@ -44,11 +59,32 @@ module Gisele
|
|
44
59
|
# will correctly be applied to the duplicated array.
|
45
60
|
#
|
46
61
|
def dup
|
47
|
-
AST.node(super)
|
62
|
+
AST.node(super, markers.dup)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a label for this AST node
|
66
|
+
def label
|
67
|
+
""
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns attributes to use for dot printing
|
71
|
+
def dot_attributes
|
72
|
+
attrs = Language::DOT_ATTRIBUTES[rule_name.to_s] || {}
|
73
|
+
attrs.merge(:label => label)
|
48
74
|
end
|
49
75
|
|
50
76
|
end # module Node
|
51
77
|
end # module AST
|
52
78
|
end # module Language
|
53
79
|
end # module Gisele
|
54
|
-
require_relative 'unit'
|
80
|
+
require_relative 'unit'
|
81
|
+
require_relative 'task_call_st'
|
82
|
+
require_relative 'while_st'
|
83
|
+
require_relative 'if_st'
|
84
|
+
require_relative 'elsif_clause'
|
85
|
+
require_relative 'when_clause'
|
86
|
+
require_relative 'bool_expr'
|
87
|
+
require_relative 'bool_and'
|
88
|
+
require_relative 'bool_or'
|
89
|
+
require_relative 'bool_not'
|
90
|
+
require_relative 'var_ref'
|
data/lib/gisele/language/ast.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
task_refinement:
|
2
|
+
shape: circle
|
3
|
+
style: filled
|
4
|
+
fillcolor: black
|
5
|
+
fixed: true
|
6
|
+
width: 0.1
|
7
|
+
task_call_st:
|
8
|
+
shape: box
|
9
|
+
while_st:
|
10
|
+
shape: diamond
|
11
|
+
if_st:
|
12
|
+
shape: diamond
|
13
|
+
case_st:
|
14
|
+
shape: diamond
|
15
|
+
par_st:
|
16
|
+
shape: box
|
17
|
+
height: 0.1
|
18
|
+
style: filled
|
19
|
+
fillcolor: black
|
@@ -15,8 +15,17 @@ module Gisele
|
|
15
15
|
|
16
16
|
def on_if_st(node)
|
17
17
|
condition, dost, *clauses = node.children
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
# create case_st with same markers as the if_st
|
20
|
+
when_clause = [:when_clause, condition, @main.call(dost)]
|
21
|
+
when_clause = node(when_clause, node.markers.dup)
|
22
|
+
base = [:case_st, when_clause]
|
23
|
+
base = node(base, node.markers.dup)
|
24
|
+
|
25
|
+
# this is the condition for elsif clauses
|
26
|
+
@condition = negate(condition.last)
|
27
|
+
|
28
|
+
# make injection now
|
20
29
|
clauses.inject base do |memo,clause|
|
21
30
|
memo << call(clause)
|
22
31
|
end
|
@@ -24,17 +33,29 @@ module Gisele
|
|
24
33
|
|
25
34
|
def on_elsif_clause(node)
|
26
35
|
condition, dost, = node.children
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
36
|
+
|
37
|
+
# install new conditions for me and next elsif clauses
|
38
|
+
condition = condition.last
|
39
|
+
previous = @condition
|
40
|
+
@condition = [:bool_and, negate(condition), @condition]
|
41
|
+
|
42
|
+
# convert elsif to when and keep the markers
|
43
|
+
base = \
|
44
|
+
[:when_clause,
|
45
|
+
[:bool_expr, [:bool_and, condition, previous]],
|
46
|
+
@main.call(dost) ]
|
47
|
+
node(base, node.markers.dup)
|
31
48
|
end
|
32
49
|
|
33
50
|
def on_else_clause(node)
|
34
51
|
dost, = node.children
|
35
|
-
|
36
|
-
|
37
|
-
|
52
|
+
|
53
|
+
# convert else to when and keep the markers
|
54
|
+
base = \
|
55
|
+
[:when_clause,
|
56
|
+
[:bool_expr, @condition],
|
57
|
+
@main.call(dost)]
|
58
|
+
node(base, node.markers.dup)
|
38
59
|
end
|
39
60
|
|
40
61
|
private
|
@@ -90,7 +90,8 @@ grammar Gisele::Language::Syntax::Grammar
|
|
90
90
|
### Boolean expressions
|
91
91
|
|
92
92
|
rule bool_expr
|
93
|
-
bool_or
|
93
|
+
(spacing theexpr:bool_or)
|
94
|
+
<Gisele::Language::Syntax::BoolExpr>
|
94
95
|
end
|
95
96
|
|
96
97
|
rule bool_or
|
@@ -112,11 +113,11 @@ grammar Gisele::Language::Syntax::Grammar
|
|
112
113
|
end
|
113
114
|
|
114
115
|
rule bool_term
|
115
|
-
bool_paren |
|
116
|
+
bool_paren | bool_lit | bool_varref
|
116
117
|
end
|
117
118
|
|
118
119
|
rule bool_paren
|
119
|
-
('(' spacing expr:
|
120
|
+
('(' spacing expr:bool_or spacing ')')
|
120
121
|
<Gisele::Language::Syntax::BoolParen>
|
121
122
|
end
|
122
123
|
|
@@ -4,7 +4,7 @@ module Gisele
|
|
4
4
|
module Node
|
5
5
|
|
6
6
|
def to_ast
|
7
|
-
Language::AST.node(_to_ast)
|
7
|
+
Language::AST.node(_to_ast, {:match => self})
|
8
8
|
end
|
9
9
|
|
10
10
|
end # module Node
|
@@ -20,6 +20,7 @@ require_relative 'bool_paren'
|
|
20
20
|
require_relative 'bool_not'
|
21
21
|
require_relative 'bool_and'
|
22
22
|
require_relative 'bool_or'
|
23
|
+
require_relative 'bool_expr'
|
23
24
|
require_relative 'st_list'
|
24
25
|
require_relative 'implicit_seq_st'
|
25
26
|
require_relative 'task_call_st'
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Gisele
|
2
|
+
module Language
|
3
|
+
class ToGraph < Transformer
|
4
|
+
module Connector; end
|
5
|
+
|
6
|
+
def recurse_on_last(node)
|
7
|
+
call(node.last)
|
8
|
+
end
|
9
|
+
alias :on_unit :recurse_on_last
|
10
|
+
|
11
|
+
def on_task_def(node)
|
12
|
+
@graph = Yargi::Digraph.new
|
13
|
+
call(SugarRemoval.new.call(node.last))
|
14
|
+
@graph.vertices(Connector).each do |vertex|
|
15
|
+
next unless vertex.out_edges.size == 1
|
16
|
+
target = vertex.out_edges.first.target
|
17
|
+
@graph.reconnect(vertex.in_edges, nil, target)
|
18
|
+
@graph.remove_vertex(vertex)
|
19
|
+
end
|
20
|
+
@graph
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_task_refinement(node)
|
24
|
+
entry, exit = add_vertex(node), add_vertex(node)
|
25
|
+
c_entry, c_exit = call(node.last)
|
26
|
+
connect(entry, c_entry)
|
27
|
+
connect(c_exit, exit)
|
28
|
+
[entry, exit]
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_seq_st(node)
|
32
|
+
mine = entry_and_exit(node)
|
33
|
+
current = mine.first
|
34
|
+
node.children.each do |child|
|
35
|
+
c_entry, c_exit = call(child)
|
36
|
+
connect(current, c_entry)
|
37
|
+
current = c_exit
|
38
|
+
end
|
39
|
+
connect(current, mine.last)
|
40
|
+
mine
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_par_st(node)
|
44
|
+
entry, exit = add_vertex(node), add_vertex(node)
|
45
|
+
node.children.each do |child|
|
46
|
+
c_entry, c_exit = call(child)
|
47
|
+
connect(entry, c_entry)
|
48
|
+
connect(c_exit, exit)
|
49
|
+
end
|
50
|
+
[entry, exit]
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_case_st(node)
|
54
|
+
entry, exit = entry_and_exit(node)
|
55
|
+
|
56
|
+
diamond = add_vertex(node)
|
57
|
+
connect(entry, diamond)
|
58
|
+
|
59
|
+
node.children.each do |when_clause|
|
60
|
+
c_entry, c_exit = call(when_clause.last)
|
61
|
+
connect(diamond, c_entry, when_clause)
|
62
|
+
connect(c_exit, exit)
|
63
|
+
end
|
64
|
+
|
65
|
+
[entry, exit]
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def on_while_st(node)
|
70
|
+
cond, dost, = node.children
|
71
|
+
|
72
|
+
entry, exit = entry_and_exit(node)
|
73
|
+
|
74
|
+
diamond = add_vertex(node)
|
75
|
+
connect(entry, diamond)
|
76
|
+
|
77
|
+
c_entry, c_exit = call(node.last)
|
78
|
+
|
79
|
+
connect(diamond, exit, false_ast_node)
|
80
|
+
connect(diamond, c_entry, true_ast_node)
|
81
|
+
connect(c_exit, diamond)
|
82
|
+
|
83
|
+
[entry, exit]
|
84
|
+
end
|
85
|
+
|
86
|
+
def on_task_call_st(node)
|
87
|
+
entry, exit = entry_and_exit(node)
|
88
|
+
task = add_vertex(node)
|
89
|
+
connect(entry, task)
|
90
|
+
connect(task, exit)
|
91
|
+
[entry, exit]
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def add_vertex(node)
|
97
|
+
@graph.add_vertex(node.dot_attributes)
|
98
|
+
end
|
99
|
+
|
100
|
+
def entry_and_exit(node, tag = Connector)
|
101
|
+
@graph.add_n_vertices(2, tag)
|
102
|
+
end
|
103
|
+
|
104
|
+
def connect(source, target, node = nil)
|
105
|
+
marks = node.nil? ? {} : node.dot_attributes
|
106
|
+
@graph.connect(source, target, marks)
|
107
|
+
end
|
108
|
+
|
109
|
+
def false_ast_node
|
110
|
+
Syntax.ast("false", :root => :bool_expr)
|
111
|
+
end
|
112
|
+
|
113
|
+
def true_ast_node
|
114
|
+
Syntax.ast("true", :root => :bool_expr)
|
115
|
+
end
|
116
|
+
|
117
|
+
end # class SugarRemoval
|
118
|
+
end # module Language
|
119
|
+
end # module Gisele
|