resin 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/amber/bin/amberc +10 -350
- data/amber/js/Benchfib.deploy.js +80 -89
- data/amber/js/Benchfib.js +80 -89
- data/amber/js/Canvas.deploy.js +558 -545
- data/amber/js/Canvas.js +563 -545
- data/amber/js/Compiler-AST.deploy.js +431 -243
- data/amber/js/Compiler-AST.js +487 -244
- data/amber/js/Compiler-Core.deploy.js +201 -1045
- data/amber/js/Compiler-Core.js +208 -1207
- data/amber/js/Compiler-Exceptions.deploy.js +37 -18
- data/amber/js/Compiler-Exceptions.js +42 -18
- data/amber/js/Compiler-IR.deploy.js +1071 -774
- data/amber/js/Compiler-IR.js +1194 -848
- data/amber/js/Compiler-Inlining.deploy.js +395 -373
- data/amber/js/Compiler-Inlining.js +395 -373
- data/amber/js/Compiler-Interpreter.deploy.js +1202 -0
- data/amber/js/Compiler-Interpreter.js +1631 -0
- data/amber/js/Compiler-Semantic.deploy.js +695 -600
- data/amber/js/Compiler-Semantic.js +721 -611
- data/amber/js/Compiler-Tests.deploy.js +699 -376
- data/amber/js/Compiler-Tests.js +834 -381
- data/amber/js/Compiler.deploy.js +8563 -1805
- data/amber/js/Compiler.js +11476 -2633
- data/amber/js/Examples.deploy.js +29 -29
- data/amber/js/Examples.js +29 -29
- data/amber/js/IDE.deploy.js +3292 -2649
- data/amber/js/IDE.js +3318 -2710
- data/amber/js/Importer-Exporter.deploy.js +393 -349
- data/amber/js/Importer-Exporter.js +398 -354
- data/amber/js/Kernel-Announcements.deploy.js +53 -44
- data/amber/js/Kernel-Announcements.js +55 -44
- data/amber/js/Kernel-Classes.deploy.js +566 -368
- data/amber/js/Kernel-Classes.js +660 -402
- data/amber/js/Kernel-Collections.deploy.js +1149 -1098
- data/amber/js/Kernel-Collections.js +1183 -1116
- data/amber/js/Kernel-Exceptions.deploy.js +173 -75
- data/amber/js/Kernel-Exceptions.js +215 -77
- data/amber/js/Kernel-Methods.deploy.js +530 -313
- data/amber/js/Kernel-Methods.js +632 -338
- data/amber/js/Kernel-Objects.deploy.js +1734 -1577
- data/amber/js/Kernel-Objects.js +1867 -1654
- data/amber/js/Kernel-Tests.deploy.js +1416 -973
- data/amber/js/Kernel-Tests.js +1495 -981
- data/amber/js/Kernel-Transcript.deploy.js +23 -24
- data/amber/js/Kernel-Transcript.js +25 -26
- data/amber/js/SUnit-Tests.deploy.js +402 -0
- data/amber/js/SUnit-Tests.js +518 -0
- data/amber/js/SUnit.deploy.js +535 -237
- data/amber/js/SUnit.js +634 -246
- data/amber/js/amber.js +90 -53
- data/amber/js/boot.js +441 -255
- data/amber/js/init.js +1 -3
- data/amber/js/lib/CodeMirror/codemirror.css +3 -0
- data/amber/js/lib/CodeMirror/codemirror.js +104 -55
- data/amber/js/lib/peg-0.7.0.min.js +9 -0
- data/amber/js/parser.js +1504 -802
- data/amber/js/parser.pegjs +170 -165
- data/amber/st/Canvas.st +6 -0
- data/amber/st/Compiler-AST.st +54 -3
- data/amber/st/Compiler-Core.st +6 -551
- data/amber/st/Compiler-Exceptions.st +4 -0
- data/amber/st/Compiler-IR.st +205 -87
- data/amber/st/Compiler-Interpreter.st +597 -0
- data/amber/st/Compiler-Semantic.st +46 -21
- data/amber/st/Compiler-Tests.st +254 -7
- data/amber/st/Compiler.st +3172 -1541
- data/amber/st/IDE.st +57 -93
- data/amber/st/Importer-Exporter.st +4 -7
- data/amber/st/Kernel-Announcements.st +8 -0
- data/amber/st/Kernel-Classes.st +149 -40
- data/amber/st/Kernel-Collections.st +43 -32
- data/amber/st/Kernel-Exceptions.st +70 -1
- data/amber/st/Kernel-Methods.st +165 -27
- data/amber/st/Kernel-Objects.st +215 -140
- data/amber/st/Kernel-Tests.st +195 -10
- data/amber/st/Kernel-Transcript.st +1 -3
- data/amber/st/SUnit-Tests.st +186 -0
- data/amber/st/SUnit.st +186 -14
- data/bin/resin +6 -0
- data/lib/resin/cli.rb +19 -0
- metadata +41 -25
- data/amber/js/lib/peg-0.6.2.min.js +0 -2
- data/bin/resin-compile +0 -6
- data/bin/runresin +0 -12
data/amber/js/parser.pegjs
CHANGED
@@ -8,61 +8,64 @@ varIdentifier = first:[a-z] others:[a-zA-Z0-9]* {return first + others.join("")
|
|
8
8
|
keyword = first:identifier last:[:] {return first + last}
|
9
9
|
className = first:[A-Z] others:[a-zA-Z0-9]* {return first + others.join("")}
|
10
10
|
string = ['] val:(("''" {return "'"} / [^'])*) ['] {
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
return smalltalk.ValueNode._new()
|
12
|
+
._value_(val.join("").replace(/\"/ig, '"'))
|
13
|
+
}
|
14
14
|
|
15
15
|
symbol = "#"val:(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
digits:[a-zA-Z0-9\:]+ {return digits.join("")}
|
17
|
+
/ node:string {return node._value()})*
|
18
|
+
{
|
19
|
+
return smalltalk.ValueNode._new()
|
20
|
+
._value_(smalltalk.symbolFor(val.join("").replace(/\"/ig, '"')))
|
21
|
+
}
|
22
|
+
number = n:(hex / float / integer) {
|
23
|
+
return smalltalk.ValueNode._new()
|
24
|
+
._value_(n)
|
25
|
+
}
|
26
|
+
hex = neg:[-]? "16r" num:[0-9a-fA-F]+ {return parseInt((neg + num.join("")), 16)}
|
25
27
|
float = neg:[-]?int:[0-9]+ "." dec:[0-9]+ {return parseFloat((neg + int.join("") + "." + dec.join("")), 10)}
|
26
28
|
integer = neg:[-]?digits:[0-9]+ {return (parseInt(neg+digits.join(""), 10))}
|
27
29
|
literalArray = "#(" ws lits:(lit:literal ws {return lit._value()})* ws ")" {
|
28
|
-
|
29
|
-
|
30
|
-
|
30
|
+
return smalltalk.ValueNode._new()
|
31
|
+
._value_(lits)
|
32
|
+
}
|
31
33
|
dynamicArray = "{" ws expressions:expressions? ws "."? "}" {
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
return smalltalk.DynamicArrayNode._new()
|
35
|
+
._nodes_(expressions)
|
36
|
+
}
|
35
37
|
dynamicDictionary = "#{" ws expressions: expressions? ws "}" {
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
return smalltalk.DynamicDictionaryNode._new()
|
39
|
+
._nodes_(expressions)
|
40
|
+
}
|
39
41
|
pseudoVariable = val:(
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
'true' {return true}
|
43
|
+
/ 'false' {return false}
|
44
|
+
/ 'nil' {return nil}) {
|
45
|
+
return smalltalk.ValueNode._new()
|
46
|
+
._value_(val)
|
47
|
+
}
|
45
48
|
literal = pseudoVariable / number / literalArray / dynamicDictionary / dynamicArray / string / symbol / block
|
46
49
|
|
47
50
|
|
48
51
|
variable = identifier:varIdentifier {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
return smalltalk.VariableNode._new()
|
53
|
+
._value_(identifier)
|
54
|
+
}
|
52
55
|
classReference = className:className {
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
return smalltalk.ClassReferenceNode._new()
|
57
|
+
._value_(className)
|
58
|
+
}
|
56
59
|
|
57
60
|
reference = variable / classReference
|
58
61
|
|
59
62
|
keywordPair = key:keyword ws arg:binarySend ws {return {key:key, arg: arg}}
|
60
63
|
|
61
|
-
binarySelector = bin:[\\+*/=><,@%~|&-]+ {return bin.join("")
|
64
|
+
binarySelector = bin:[\\+*/=><,@%~|&-]+ {return bin.join("")}
|
62
65
|
unarySelector = identifier
|
63
66
|
|
64
67
|
keywordPattern = pairs:(ws key:keyword ws arg:identifier {return {key:key, arg: arg}})+ {
|
65
|
-
|
68
|
+
var keywords = [];
|
66
69
|
var params = [];
|
67
70
|
for(var i=0;i<pairs.length;i++){
|
68
71
|
keywords.push(pairs[i].key);
|
@@ -70,32 +73,32 @@ keywordPattern = pairs:(ws key:keyword ws arg:identifier {return {key:key, arg:
|
|
70
73
|
for(var i=0;i<pairs.length;i++){
|
71
74
|
params.push(pairs[i].arg);
|
72
75
|
}
|
73
|
-
|
74
|
-
|
76
|
+
return [keywords.join(""), params]
|
77
|
+
}
|
75
78
|
binaryPattern = ws selector:binarySelector ws arg:identifier {return [selector, [arg]]}
|
76
79
|
unaryPattern = ws selector:unarySelector {return [selector, []]}
|
77
80
|
|
78
|
-
expression = assignment / cascade / keywordSend / binarySend
|
81
|
+
expression = assignment / cascade / keywordSend / binarySend
|
79
82
|
|
80
83
|
expressionList = ws "." ws expression:expression {return expression}
|
81
84
|
expressions = first:expression others:expressionList* {
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
var result = [first];
|
86
|
+
for(var i=0;i<others.length;i++) {
|
87
|
+
result.push(others[i]);
|
88
|
+
}
|
89
|
+
return result;
|
90
|
+
}
|
88
91
|
|
89
92
|
assignment = variable:variable ws ':=' ws expression:expression {
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
93
|
+
return smalltalk.AssignmentNode._new()
|
94
|
+
._left_(variable)
|
95
|
+
._right_(expression)
|
96
|
+
}
|
94
97
|
|
95
98
|
ret = '^' ws expression:expression ws '.'? {
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
+
return smalltalk.ReturnNode._new()
|
100
|
+
._nodes_([expression])
|
101
|
+
}
|
99
102
|
|
100
103
|
temps = "|" vars:(ws variable:identifier ws {return variable})* "|" {return vars}
|
101
104
|
|
@@ -103,121 +106,123 @@ blockParamList = params:((ws ":" ws param:identifier {return param})+) ws "|" {r
|
|
103
106
|
|
104
107
|
subexpression = '(' ws expression:expression ws ')' {return expression}
|
105
108
|
|
106
|
-
statements
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
sequence
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
109
|
+
statements = ret:ret [.]* {return [ret]}
|
110
|
+
/ exps:expressions ws [.]+ ws ret:ret [.]* {
|
111
|
+
var expressions = exps;
|
112
|
+
expressions.push(ret);
|
113
|
+
return expressions
|
114
|
+
}
|
115
|
+
/ expressions:expressions? [.]* {
|
116
|
+
return expressions || []
|
117
|
+
}
|
118
|
+
|
119
|
+
sequence = jsSequence / stSequence
|
120
|
+
|
121
|
+
stSequence = temps:temps? ws statements:statements? ws {
|
122
|
+
return smalltalk.SequenceNode._new()
|
123
|
+
._temps_(temps || [])
|
124
|
+
._nodes_(statements || [])
|
125
|
+
}
|
126
|
+
|
127
|
+
jsSequence = jsStatement
|
128
|
+
|
129
|
+
block = '[' ws params:blockParamList? ws sequence:sequence? ws ']' {
|
130
|
+
return smalltalk.BlockNode._new()
|
131
|
+
._parameters_(params || [])
|
132
|
+
._nodes_([sequence._asBlockSequenceNode()])
|
133
|
+
}
|
134
|
+
|
135
|
+
operand = literal / reference / subexpression
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
unaryMessage = ws selector:unarySelector ![:] {
|
140
|
+
return smalltalk.SendNode._new()
|
141
|
+
._selector_(selector)
|
142
|
+
}
|
143
|
+
|
144
|
+
unaryTail = message:unaryMessage ws tail:unaryTail? ws {
|
145
|
+
if(tail) {
|
146
|
+
return tail._valueForReceiver_(message);
|
147
|
+
}
|
148
|
+
else {
|
149
|
+
return message;
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
unarySend = receiver:operand ws tail:unaryTail? {
|
154
|
+
if(tail) {
|
155
|
+
return tail._valueForReceiver_(receiver);
|
156
|
+
}
|
157
|
+
else {
|
158
|
+
return receiver;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
binaryMessage = ws selector:binarySelector ws arg:(unarySend / operand) {
|
163
|
+
return smalltalk.SendNode._new()
|
164
|
+
._selector_(selector)
|
165
|
+
._arguments_([arg])
|
166
|
+
}
|
167
|
+
|
168
|
+
binaryTail = message:binaryMessage tail:binaryTail? {
|
169
|
+
if(tail) {
|
170
|
+
return tail._valueForReceiver_(message);
|
171
|
+
}
|
172
|
+
else {
|
173
|
+
return message;
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
binarySend = receiver:unarySend tail:binaryTail? {
|
178
|
+
if(tail) {
|
179
|
+
return tail._valueForReceiver_(receiver);
|
180
|
+
}
|
181
|
+
else {
|
182
|
+
return receiver;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
179
186
|
|
180
187
|
keywordMessage = ws pairs:(pair:keywordPair ws {return pair})+ {
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
keywordSend
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
message
|
197
|
-
|
198
|
-
cascade
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
._nodes_([sequence])
|
222
|
-
}
|
188
|
+
var selector = [];
|
189
|
+
var args = [];
|
190
|
+
for(var i=0;i<pairs.length;i++) {
|
191
|
+
selector.push(pairs[i].key);
|
192
|
+
args.push(pairs[i].arg);
|
193
|
+
}
|
194
|
+
return smalltalk.SendNode._new()
|
195
|
+
._selector_(selector.join(""))
|
196
|
+
._arguments_(args)
|
197
|
+
}
|
198
|
+
|
199
|
+
keywordSend = receiver:binarySend tail:keywordMessage {
|
200
|
+
return tail._valueForReceiver_(receiver);
|
201
|
+
}
|
202
|
+
|
203
|
+
message = binaryMessage / unaryMessage / keywordMessage
|
204
|
+
|
205
|
+
cascade = ws send:(keywordSend / binarySend) messages:(ws ";" ws mess:message ws {return mess})+ {
|
206
|
+
var cascade = [];
|
207
|
+
cascade.push(send);
|
208
|
+
for(var i=0;i<messages.length;i++) {
|
209
|
+
cascade.push(messages[i]);
|
210
|
+
}
|
211
|
+
return smalltalk.CascadeNode._new()
|
212
|
+
._receiver_(send._receiver())
|
213
|
+
._nodes_(cascade)
|
214
|
+
}
|
215
|
+
|
216
|
+
jsStatement = "<" val:((">>" {return ">"} / [^>])*) ">" {
|
217
|
+
return smalltalk.JSStatementNode._new()
|
218
|
+
._source_(val.join(""))
|
219
|
+
}
|
220
|
+
|
221
|
+
|
222
|
+
method = ws pattern:(keywordPattern / binaryPattern / unaryPattern) ws sequence:sequence? ws {
|
223
|
+
return smalltalk.MethodNode._new()
|
224
|
+
._selector_(pattern[0])
|
225
|
+
._arguments_(pattern[1])
|
226
|
+
._nodes_([sequence])
|
227
|
+
}
|
223
228
|
|
data/amber/st/Canvas.st
CHANGED
data/amber/st/Compiler-AST.st
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
Smalltalk current createPackage: 'Compiler-AST' properties: #{}!
|
2
2
|
Object subclass: #Node
|
3
|
-
instanceVariableNames: 'nodes shouldBeInlined shouldBeAliased'
|
3
|
+
instanceVariableNames: 'position nodes shouldBeInlined shouldBeAliased'
|
4
4
|
package: 'Compiler-AST'!
|
5
5
|
!Node commentStamp!
|
6
|
-
I am the abstract root class of the abstract syntax tree
|
6
|
+
I am the abstract root class of the abstract syntax tree.
|
7
|
+
|
8
|
+
position: holds a point containing lline- and column number of the symbol location in the original source file!
|
7
9
|
|
8
10
|
!Node methodsFor: 'accessing'!
|
9
11
|
|
@@ -15,6 +17,10 @@ nodes
|
|
15
17
|
^nodes ifNil: [nodes := Array new]
|
16
18
|
!
|
17
19
|
|
20
|
+
position
|
21
|
+
^position ifNil: [position := 0@0]
|
22
|
+
!
|
23
|
+
|
18
24
|
shouldBeAliased
|
19
25
|
^ shouldBeAliased ifNil: [ false ]
|
20
26
|
!
|
@@ -35,6 +41,10 @@ shouldBeInlined: aBoolean
|
|
35
41
|
|
36
42
|
nodes: aCollection
|
37
43
|
nodes := aCollection
|
44
|
+
!
|
45
|
+
|
46
|
+
position: aPosition
|
47
|
+
position := aPosition
|
38
48
|
! !
|
39
49
|
|
40
50
|
!Node methodsFor: 'testing'!
|
@@ -51,6 +61,14 @@ isBlockSequenceNode
|
|
51
61
|
^false
|
52
62
|
!
|
53
63
|
|
64
|
+
isImmutable
|
65
|
+
^false
|
66
|
+
!
|
67
|
+
|
68
|
+
isNode
|
69
|
+
^ true
|
70
|
+
!
|
71
|
+
|
54
72
|
isReturnNode
|
55
73
|
^false
|
56
74
|
!
|
@@ -61,6 +79,11 @@ isSendNode
|
|
61
79
|
|
62
80
|
isValueNode
|
63
81
|
^false
|
82
|
+
!
|
83
|
+
|
84
|
+
subtreeNeedsAliasing
|
85
|
+
^(self shouldBeAliased or: [ self shouldBeInlined ]) or: [
|
86
|
+
(self nodes detect: [ :each | each subtreeNeedsAliasing ] ifNone: [ false ]) ~= false ]
|
64
87
|
! !
|
65
88
|
|
66
89
|
!Node methodsFor: 'visiting'!
|
@@ -133,6 +156,10 @@ scope: aLexicalScope
|
|
133
156
|
|
134
157
|
isBlockNode
|
135
158
|
^true
|
159
|
+
!
|
160
|
+
|
161
|
+
subtreeNeedsAliasing
|
162
|
+
^ self shouldBeAliased or: [ self shouldBeInlined ]
|
136
163
|
! !
|
137
164
|
|
138
165
|
!BlockNode methodsFor: 'visiting'!
|
@@ -202,7 +229,7 @@ accept: aVisitor
|
|
202
229
|
! !
|
203
230
|
|
204
231
|
Node subclass: #MethodNode
|
205
|
-
instanceVariableNames: 'selector arguments source scope classReferences messageSends'
|
232
|
+
instanceVariableNames: 'selector arguments source scope classReferences messageSends superSends'
|
206
233
|
package: 'Compiler-AST'!
|
207
234
|
|
208
235
|
!MethodNode methodsFor: 'accessing'!
|
@@ -253,6 +280,14 @@ source
|
|
253
280
|
|
254
281
|
source: aString
|
255
282
|
source := aString
|
283
|
+
!
|
284
|
+
|
285
|
+
superSends
|
286
|
+
^ superSends
|
287
|
+
!
|
288
|
+
|
289
|
+
superSends: aCollection
|
290
|
+
superSends := aCollection
|
256
291
|
! !
|
257
292
|
|
258
293
|
!MethodNode methodsFor: 'visiting'!
|
@@ -446,6 +481,10 @@ value: anObject
|
|
446
481
|
|
447
482
|
!ValueNode methodsFor: 'testing'!
|
448
483
|
|
484
|
+
isImmutable
|
485
|
+
^true
|
486
|
+
!
|
487
|
+
|
449
488
|
isValueNode
|
450
489
|
^true
|
451
490
|
! !
|
@@ -487,6 +526,12 @@ binding: aScopeVar
|
|
487
526
|
binding := aScopeVar
|
488
527
|
! !
|
489
528
|
|
529
|
+
!VariableNode methodsFor: 'testing'!
|
530
|
+
|
531
|
+
isImmutable
|
532
|
+
^false
|
533
|
+
! !
|
534
|
+
|
490
535
|
!VariableNode methodsFor: 'visiting'!
|
491
536
|
|
492
537
|
accept: aVisitor
|
@@ -503,3 +548,9 @@ accept: aVisitor
|
|
503
548
|
^ aVisitor visitClassReferenceNode: self
|
504
549
|
! !
|
505
550
|
|
551
|
+
!Object methodsFor: '*Compiler-AST'!
|
552
|
+
|
553
|
+
isNode
|
554
|
+
^ false
|
555
|
+
! !
|
556
|
+
|