Skip to content

Commit 28e1d6f

Browse files
committed
Add while and until loops
1 parent e2b81a0 commit 28e1d6f

File tree

7 files changed

+838
-353
lines changed

7 files changed

+838
-353
lines changed

examples/tests/loops.tab

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
x = 1
2+
while (x == 2) {
3+
assert(false)
4+
}
5+
6+
x = 1
7+
y = while (x < 10) {
8+
x += 1
9+
}
10+
assert(x == 10)
11+
assert(y == 10)
12+
13+
x = 1
14+
until (x == 1) {
15+
assert(false)
16+
}
17+
18+
x = 1
19+
y = until (x == 10) {
20+
x += 1
21+
}
22+
assert(x == 10)
23+
assert(y == 10)

src/expressions/assignment.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,32 @@ import { expressionTypes } from './types';
2323
const operators = {
2424
'=': (context, leftHandSideValue, leftValue, value) => {
2525
leftHandSideValue.assignFrom(context, value);
26+
return value;
2627
},
2728
'+=': (context, leftHandSideValue, leftValue, value) => {
28-
leftHandSideValue.assignFrom(context, leftValue.add(context, value));
29+
const result = leftValue.add(context, value);
30+
leftHandSideValue.assignFrom(context, result);
31+
return result;
2932
},
3033
'-=': (context, leftHandSideValue, leftValue, value) => {
31-
leftHandSideValue.assignFrom(context, leftValue.subtract(context, value));
34+
const result = leftValue.subtract(context, value);
35+
leftHandSideValue.assignFrom(context, result);
36+
return result;
3237
},
3338
'*=': (context, leftHandSideValue, leftValue, value) => {
34-
leftHandSideValue.assignFrom(context, leftValue.multiplyBy(context, value));
39+
const result = leftValue.multiplyBy(context, value);
40+
leftHandSideValue.assignFrom(context, result);
41+
return result;
3542
},
3643
'/=': (context, leftHandSideValue, leftValue, value) => {
37-
leftHandSideValue.assignFrom(context, leftValue.divideBy(context, value));
44+
const result = leftValue.divideBy(context, value);
45+
leftHandSideValue.assignFrom(context, result);
46+
return result;
3847
},
3948
'%=': (context, leftHandSideValue, leftValue, value) => {
40-
leftHandSideValue.assignFrom(context, leftValue.modulo(context, value));
49+
const result = leftValue.modulo(context, value);
50+
leftHandSideValue.assignFrom(context, result);
51+
return result;
4152
},
4253
};
4354

@@ -50,11 +61,9 @@ const evaluate = (location, leftHandSideExpression, operator, valueExpression) =
5061
const value = await valueExpression.evaluate(context);
5162
if (operators[operator]) {
5263
const leftValue = (operator === '=') ? undefined : await leftHandSideExpression.evaluate(context);
53-
operators[operator](context, leftHandSideValue, leftValue, value);
54-
} else {
55-
throwRuntimeError(`Unknown operator ${operator}`, context);
64+
return operators[operator](context, leftHandSideValue, leftValue, value);
5665
}
57-
return value;
66+
throwRuntimeError(`Unknown operator ${operator}`, context);
5867
};
5968

6069
export const createAssignmentExpression = (location, leftHandSideExpression, operator, valueExpression) => {

src/expressions/types.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ export const expressionTypes = {
3737
TEMPLATE_STRING: Symbol('TEMPLATE_STRING'),
3838
UNARY: Symbol('UNARY'),
3939
UNDEFINED: Symbol('UNDEFINED'),
40+
UNTIL: Symbol('UNTIL'),
4041
VARIABLE: Symbol('VARIABLE'),
42+
WHILE: Symbol('WHILE'),
4143
};
4244

4345
export const expressionTypeName = type => {
@@ -84,8 +86,12 @@ export const expressionTypeName = type => {
8486
return 'unary';
8587
case expressionTypes.UNDEFINED:
8688
return 'undefined';
89+
case expressionTypes.UNTIL:
90+
return 'until';
8791
case expressionTypes.VARIABLE:
8892
return 'variable';
93+
case expressionTypes.WHILE:
94+
return 'while';
8995
default:
9096
return '<TYPE UNSET>';
9197
}

src/expressions/until.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2017 Jamie Hale
2+
//
3+
// This file is part of Tablescript.js.
4+
//
5+
// Tablescript.js is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Tablescript.js is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Tablescript.js. If not, see <http://www.gnu.org/licenses/>.
17+
18+
import { createExpression } from './default';
19+
import { expressionTypes } from './types';
20+
21+
const evaluate = (location, condition, loopBlock) => async context => {
22+
context.setLocation(location);
23+
let expressionValue = await condition.evaluate(context);
24+
let result = context.factory.createUndefined();
25+
while (!expressionValue.asNativeBoolean(context)) {
26+
result = await loopBlock.evaluate(context);
27+
expressionValue = await condition.evaluate(context);
28+
}
29+
return result;
30+
};
31+
32+
export const createUntilExpression = (
33+
location,
34+
condition,
35+
loopBlock
36+
) => createExpression(
37+
expressionTypes.UNTIL,
38+
evaluate(location, condition, loopBlock)
39+
);

src/expressions/while.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2017 Jamie Hale
2+
//
3+
// This file is part of Tablescript.js.
4+
//
5+
// Tablescript.js is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// Tablescript.js is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with Tablescript.js. If not, see <http://www.gnu.org/licenses/>.
17+
18+
import { createExpression } from './default';
19+
import { expressionTypes } from './types';
20+
21+
const evaluate = (location, condition, loopBlock) => async context => {
22+
context.setLocation(location);
23+
let expressionValue = await condition.evaluate(context);
24+
let result = context.factory.createUndefined();
25+
while (expressionValue.asNativeBoolean(context)) {
26+
result = await loopBlock.evaluate(context);
27+
expressionValue = await condition.evaluate(context);
28+
}
29+
return result;
30+
};
31+
32+
export const createWhileExpression = (
33+
location,
34+
condition,
35+
loopBlock
36+
) => createExpression(
37+
expressionTypes.WHILE,
38+
evaluate(location, condition, loopBlock)
39+
);

src/grammar/tablescript.pegjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
const { createTemplateStringLiteral } = require('../expressions/template-string-literal');
5252
const { createUndefinedLiteral } = require('../expressions/undefined-literal');
5353
const { createIfExpression } = require('../expressions/if');
54+
const { createWhileExpression } = require('../expressions/while');
55+
const { createUntilExpression } = require('../expressions/until');
5456
const { createSpreadExpression } = require('../expressions/spread');
5557

5658
const composeBinaryExpression = (context, head, tail) => {
@@ -302,6 +304,22 @@ IfBlock "if block"
302304
}
303305
/ Block
304306

307+
WhileExpression "while expression"
308+
= WhileToken __ '(' __ e:Expression __ ')' __ loopBlock:LoopBlock {
309+
return createWhileExpression(createLocation(location(), options), e, loopBlock);
310+
}
311+
312+
UntilExpression "until expression"
313+
= UntilToken __ '(' __ e:Expression __ ')' __ loopBlock:LoopBlock {
314+
return createUntilExpression(createLocation(location(), options), e, loopBlock);
315+
}
316+
317+
LoopBlock "loop block"
318+
= e:AssignmentExpression {
319+
return createBlockExpression(createLocation(location(), options), [e]);
320+
}
321+
/ Block
322+
305323
SpreadExpression "spread"
306324
= '...' e:Expression {
307325
return createSpreadExpression(createLocation(location(), options), e);
@@ -319,6 +337,8 @@ PrimaryExpression
319337
return e;
320338
}
321339
/ IfExpression
340+
/ WhileExpression
341+
/ UntilExpression
322342
/ SpreadExpression
323343

324344
Literal
@@ -505,6 +525,8 @@ ReservedWord
505525
/ FalseToken
506526
/ IfToken
507527
/ ElseToken
528+
/ WhileToken
529+
/ UntilToken
508530
/ AndToken
509531
/ OrToken
510532
/ NotToken
@@ -517,6 +539,8 @@ TrueToken = "true" !IdentifierPart
517539
FalseToken = "false" !IdentifierPart
518540
IfToken = "if" !IdentifierPart
519541
ElseToken = "else" !IdentifierPart
542+
WhileToken = "while" !IdentifierPart
543+
UntilToken = "until" !IdentifierPart
520544
AndToken = $("and" !IdentifierPart)
521545
OrToken = $("or" !IdentifierPart)
522546
NotToken = "not" !IdentifierPart { return 'not'; }

0 commit comments

Comments
 (0)