/
Compile-Statement-Control.pm6
executable file
路129 lines (119 loc) 路 3.71 KB
/
Compile-Statement-Control.pm6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
need Spit::SAST;
need Spit::Exceptions;
use Spit::Metamodel;
use Spit::Sh::ShellElement;
unit role Compile-Statement-Control;
#!If
multi method node(SAST::If:D $_, :$else) {
($else ?? 'elif' !! 'if'),' ',
|self.compile-topic(
.topic-var,
(.cond, .then, (.else if .else ~~ SAST::Stmts))
),
|self.cond(.cond),"; then\n",
|self.node(.then,:indent,:no-empty),
|(with .else {
when SAST::Empty { Empty }
when SAST::If { "\n{$*pad}",|self.node($_,:else) }
when SAST::Stmts { "\n{$*pad}else\n",|self.node($_,:indent,:no-empty) }
} elsif .type ~~ tBool {
# if false; then false; fi; actually exits 0 (?!)
# So we have to make sure it exits 1 if the cond is false
"\n{$*pad}else\n{$*pad} false"
}),
( "\n{$*pad}fi" unless $else );
}
# turns stuff like:
# if test "$(cat $file)"; do ...
# into:
# if _1="$(cat $file)"; if test "$_1"; do ...
method compile-topic($topic-var, @associated-sast) {
if not $topic-var.defined {
Empty
}
elsif $topic-var.references > 1 {
|self.node($topic-var),'; '
}
elsif $topic-var.references == 1 {
search-and-replace(
$topic-var.references[0],
$topic-var.assign,
@associated-sast,
)
?? Empty
!! SX::Bug.new(desc => "Unable to find reference to topic variable in if statement").throw
}
else {
Empty;
}
}
sub search-and-replace($target, $replacement, @places-to-look) {
for @places-to-look <-> $thing {
$thing.descend({ $_ === $target and $_ = $replacement }) and return True;
}
return False;
}
multi method arg(SAST::If:D $_) {
nextsame if .type ~~ tBool;
if not .else
and (my $stmt = .then.one-stmt)
and not (.topic-var andthen .depended) {
# in some limited circumstances we can simplify
# if cond { action } to cond && action
my $neg = .cond ~~ SAST::Neg;
my $cond = $neg ?? .cond[0] !! .cond;
if $cond ~~ SAST::Var && (my $var = $cond)
or
$cond ~~ SAST::Cmd && $cond.nodes == 2
&& $cond[0].compile-time ~~ 'test'
&& $cond[1] ~~ SAST::Var
&& ($var = $cond[1])
{
dq '${',self.gen-name($var), ($neg ?? ':-' !! ':+'),
|self.arg($stmt).itemize($stmt.itemize),'}';
} else {
cs |self.cond($cond),
($neg ?? ' || ' !! ' && '),
|self.node(.then, :one-line);
}
} else {
callsame;
}
}
multi method cap-stdout(SAST::If:D $_) {
nextsame if .type ~~ tBool;
self.node($_);
}
#!Loop
multi method node(SAST::Loop:D $_) {
|(.init andthen |self.node($_),'; '),
'while ', |self.cond(.cond),"; do\n",
|self.node(.block, :indent, :no-empty),
|(.incr andthen "\n", |self.compile-nodes([$_], :indent)),
"\n{$*pad}done";
}
multi method cap-stdout(SAST::Loop:D $_) { self.node($_) }
#!While
multi method node(SAST::While:D $_) {
.until ?? 'until' !! 'while',' ',
|self.compile-topic(.topic-var, (.cond, .block)),
|self.cond(.cond),"; do\n",
|self.node(.block,:indent,:no-empty),
"\n{$*pad}done";
}
multi method cap-stdout(SAST::While:D $_) { self.node($_) }
#!Given
multi method node(SAST::Given:D $_) {
|self.compile-topic(.topic-var, (.block,)),
|self.node(.block,:curlies)
}
multi method cond(SAST::Given:D $_) { self.node($_) }
multi method arg(SAST::Given:D $_) { cs self.node($_) }
#!For
multi method node(SAST::For:D $_) {
'for ', self.gen-name(.iter-var), ' in ', |self.arglist(.list.children)
,"; do\n",
|self.node(.block,:indent,:no-empty),
"\n{$*pad}done"
}
multi method cap-stdout(SAST::For:D $_) { self.node($_) }