Skip to content

Commit cadbc89

Browse files
Merge pull request #42 from JMU-CS/dev
Add array create expressions
2 parents 04be052 + 3994057 commit cadbc89

File tree

8 files changed

+204
-26
lines changed

8 files changed

+204
-26
lines changed

src/ast.js

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ export function createExecutable(tree) {
336336
});
337337
return new Praxly_array_literal(args, tree);
338338

339+
case NODETYPES.ARRAY_CREATE:
340+
return new Praxly_array_create(tree.varType, tree.name, tree.elemType, createExecutable(tree.arrayLength), tree);
341+
339342
case NODETYPES.ARRAY_REFERENCE:
340343
return new Praxly_array_reference(tree.name, createExecutable(tree.index), tree);
341344

@@ -353,7 +356,6 @@ export function createExecutable(tree) {
353356
class Praxly_single_line_comment {
354357

355358
constructor(value, node) {
356-
this.jsonType = 'Praxly_single_line_comment';
357359
this.json = node;
358360
this.value = value;
359361
}
@@ -365,7 +367,6 @@ class Praxly_single_line_comment {
365367
class Praxly_comment {
366368

367369
constructor(value, node) {
368-
this.jsonType = 'Praxly_comment';
369370
this.json = node;
370371
this.value = value;
371372
}
@@ -377,7 +378,6 @@ class Praxly_comment {
377378
class Praxly_int {
378379

379380
constructor(value, node) {
380-
this.jsonType = 'Praxly_int';
381381
this.json = node;
382382
this.value = Math.floor(value);
383383
this.realType = TYPES.INT;
@@ -391,7 +391,6 @@ class Praxly_int {
391391
class Praxly_short {
392392

393393
constructor(value, node) {
394-
this.jsonType = 'Praxly_int';
395394
this.json = node;
396395
this.value = Math.floor(value);
397396
this.realType = TYPES.SHORT;
@@ -405,7 +404,6 @@ class Praxly_short {
405404
class Praxly_double {
406405

407406
constructor(value, node) {
408-
this.jsonType = 'Praxly_double';
409407
this.json = node;
410408
this.value = parseFloat(value);
411409
this.realType = TYPES.DOUBLE;
@@ -419,7 +417,6 @@ class Praxly_double {
419417
class Praxly_float {
420418

421419
constructor(value, node) {
422-
this.jsonType = 'Praxly_double';
423420
this.json = node;
424421
this.value = parseFloat(value);
425422
this.realType = TYPES.FLOAT;
@@ -434,7 +431,6 @@ class Praxly_boolean {
434431

435432
constructor(value, node) {
436433
this.json = node;
437-
this.jsonType = 'Praxly_boolean';
438434
this.value = value;
439435
this.realType = TYPES.BOOLEAN;
440436
}
@@ -449,7 +445,6 @@ class Praxly_char {
449445
constructor(value, node) {
450446
this.value = value;
451447
this.json = node;
452-
this.jsonType = 'Praxly_String';
453448
this.realType = TYPES.CHAR;
454449
}
455450

@@ -461,7 +456,6 @@ class Praxly_char {
461456
class Praxly_String {
462457

463458
constructor(value, node) {
464-
this.jsonType = 'Praxly_String';
465459
this.json = node;
466460
this.value = value;
467461
this.realType = TYPES.STRING;
@@ -475,7 +469,6 @@ class Praxly_String {
475469
class Praxly_null {
476470

477471
constructor(value, node) {
478-
this.jsonType = 'Praxly_null';
479472
this.json = node;
480473
this.value = value;
481474
this.realType = TYPES.NULL;
@@ -488,35 +481,91 @@ class Praxly_null {
488481

489482
class Praxly_array_literal {
490483

491-
constructor(elements, node) {
484+
constructor(elements, node, elemType) {
492485
this.elements = elements;
493486
this.json = node;
494-
this.jsonType = 'Praxly_array';
495-
496-
// set array type to "largest type" of element
497-
let types = ["boolean", "char", "short", "int", "float", "double", "String"];
498-
let max_type = -1;
499-
for (let i = 0; i < elements.length; i++) {
500-
let cur_type = types.indexOf(elements[i].realType);
501-
if (cur_type > max_type) {
502-
max_type = cur_type;
487+
488+
if (elemType) {
489+
this.realType = elemType + "[]";
490+
} else {
491+
// set array type to "largest type" of element
492+
let types = ["boolean", "char", "short", "int", "float", "double", "String"];
493+
let max_type = -1;
494+
for (let i = 0; i < elements.length; i++) {
495+
let cur_type = types.indexOf(elements[i].realType);
496+
if (cur_type > max_type) {
497+
max_type = cur_type;
498+
}
503499
}
500+
this.realType = types[max_type] + "[]";
504501
}
505-
this.realType = types[max_type] + "[]";
506502
}
507503

508504
async evaluate(environment) {
509505
return this;
510506
}
511507
}
512508

509+
class Praxly_array_create {
510+
511+
constructor(varType, varName, elemType, arrayLength, node) {
512+
this.varType = varType;
513+
this.varName = varName;
514+
this.elemType = elemType;
515+
this.arrayLength = arrayLength;
516+
this.json = node;
517+
}
518+
519+
async evaluate(environment) {
520+
// type checking
521+
if (!can_assign(this.varType, this.elemType, this.json.line)) {
522+
throw new PraxlyError(`Array element did not match declared type ` +
523+
`(Expected: ${this.varType}, Actual: ${this.elemType})`, this.json.line);
524+
}
525+
let length = await this.arrayLength.evaluate(environment);
526+
if (length.realType != NODETYPES.INT) {
527+
throw new PraxlyError(`Array length must be an integer ` +
528+
`(Actual: ${length.realType})`, this.json.line);
529+
}
530+
length = length.value;
531+
if (length < 0) {
532+
throw new PraxlyError(`Array length must be nonnegative ` +
533+
`(Actual: ${length})`, this.json.line);
534+
}
535+
// default value for new array of n elements
536+
let value;
537+
switch (this.elemType) {
538+
case TYPES.BOOLEAN:
539+
value = false;
540+
break;
541+
case TYPES.CHAR:
542+
value = "0"; // null character not in language
543+
break;
544+
case TYPES.STRING:
545+
value = ""; // null reference not implemented
546+
break;
547+
default:
548+
value = 0;
549+
break;
550+
}
551+
// construct the array of n default values
552+
let elements = [];
553+
for (let i = 0; i < length; i++) {
554+
elements.push(litNode_new(this.elemType, value, this.json));
555+
}
556+
// array is being declared and initialized
557+
let array = new Praxly_array_literal(elements, this.json, this.elemType);
558+
environment.variableList[this.varName] = array;
559+
}
560+
}
561+
513562
export function valueToString(child, quotes, line) {
514563
if (child === "Exit_Success") {
515564
throw new PraxlyError("no value returned from void procedure", line);
516565
}
517566
var result;
518-
if (child.jsonType === 'Praxly_array') {
519-
// always show quote marks for arrays
567+
if (child instanceof Praxly_array_literal) {
568+
// always show curly braces for arrays
520569
let values = child.elements.map((element) => valueToString(element, true, line));
521570
result = '{' + values.join(", ") + '}';
522571
} else {

src/blocks2tree.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,23 @@ export const makeGenerator = () => {
241241
})
242242
}
243243

244+
praxlyGenerator['praxly_array_create_block'] = (block) => {
245+
var varType = block.getFieldValue('VARTYPE');
246+
var variableName = block.getFieldValue("VARIABLENAME");
247+
var elemType = block.getFieldValue('ELEMTYPE');
248+
var arrayLength = block.getInputTargetBlock('LENGTH');
249+
250+
return customizeMaybe(block, {
251+
type: NODETYPES.ARRAY_CREATE,
252+
varType: varType,
253+
name: variableName,
254+
elemType: elemType,
255+
arrayLength: praxlyGenerator[arrayLength?.type](arrayLength),
256+
isArray: true,
257+
blockID: block.id,
258+
});
259+
}
260+
244261
// NOTE: praxly_literal_block and praxly_variable_block work the same way.
245262
// The only differences are the color of the outline and the error message
246263
// when invalid. Ideally this code would not be duplicated.
@@ -641,7 +658,7 @@ export const makeGenerator = () => {
641658
const index = block.getInputTargetBlock('INDEX');
642659
return customizeMaybe(block, {
643660
blockID: block.id,
644-
name : procedureName,
661+
name: procedureName,
645662
type: NODETYPES.SPECIAL_STRING_FUNCCALL,
646663
left: praxlyGenerator[expression?.type](expression),
647664
right: {

src/common.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const NODETYPES = {
8080
FUNCCALL: "FUNCTION_CALL",
8181
RETURN: "RETURN",
8282
ARRAY_LITERAL: "ARRAY_LITERAL",
83+
ARRAY_CREATE: "ARRAY_CREATE",
8384
ARRAY_REFERENCE: "ARRAY_REFERENCE",
8485
ARRAY_REFERENCE_ASSIGNMENT: "ARRAY_REFERENCE_ASSIGNMENT", // remove?
8586
SPECIAL_STRING_FUNCCALL: "SPECIAL_STRING_FUNCCALL",

src/newBlocks.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,53 @@ export function definePraxlyBlocks(workspace) {
288288
"tooltip": "Declares and initializes an array",
289289
"helpUrl": ""
290290
},
291+
{
292+
"type": "praxly_array_create_block",
293+
"message0": "%1[] %2 ⬅ %3[%4]",
294+
"args0": [
295+
{
296+
"type": "field_dropdown",
297+
"name": "VARTYPE",
298+
"options": [
299+
["int", NODETYPES.INT],
300+
["boolean", NODETYPES.BOOLEAN],
301+
["char", NODETYPES.CHAR],
302+
["double", NODETYPES.DOUBLE],
303+
["float", NODETYPES.FLOAT],
304+
["short", NODETYPES.SHORT],
305+
["String", NODETYPES.STRING],
306+
]
307+
},
308+
{
309+
"type": "field_input",
310+
"name": "VARIABLENAME",
311+
"text": "arrayName"
312+
},
313+
{
314+
"type": "field_dropdown",
315+
"name": "ELEMTYPE",
316+
"options": [
317+
["int", NODETYPES.INT],
318+
["boolean", NODETYPES.BOOLEAN],
319+
["char", NODETYPES.CHAR],
320+
["double", NODETYPES.DOUBLE],
321+
["float", NODETYPES.FLOAT],
322+
["short", NODETYPES.SHORT],
323+
["String", NODETYPES.STRING],
324+
]
325+
},
326+
{
327+
"type": "input_value",
328+
"name": "LENGTH",
329+
}
330+
],
331+
"inputsInline": true,
332+
"previousStatement": null,
333+
"nextStatement": null,
334+
"style": 'variable_blocks',
335+
"tooltip": "Initialize and declare an array of desired length", // of length n?
336+
"helpUrl": ""
337+
},
291338
{ // variables 6
292339
"type": "praxly_array_reference_reassignment_block",
293340
"message0": "%1[%2] ⬅%3 %4",

src/text2tree.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,41 @@ class Parser {
688688

689689
// built-in function call
690690
case NODETYPES.BUILTIN_FUNCTION_CALL:
691-
case 'Type': // type conversion function
691+
case 'Type':
692692
if (this.has_ahead('(')) {
693+
// type conversion function
693694
return this.parse_builtin_function_call(line);
694695
}
696+
if (this.has_ahead('[')) {
697+
// create array expression
698+
var elemType = this.advance().value;
699+
// opening bracket
700+
this.advance();
701+
if (this.has(']')) {
702+
textError('parsing', "missing array length", this.getCurrentLine());
703+
this.advance();
704+
} else {
705+
var arrayLength = this.parse_expression();
706+
// closing bracket
707+
if (this.has(']')) {
708+
this.advance();
709+
} else {
710+
textError('parsing', "didn't detect closing bracket in the array length", this.getCurrentLine());
711+
}
712+
}
713+
return {
714+
type: NODETYPES.ARRAY_CREATE,
715+
varType: "type",
716+
name: "name",
717+
elemType: elemType,
718+
arrayLength: arrayLength,
719+
isArray: true,
720+
blockID: "code",
721+
line: line,
722+
startIndex: startIndex,
723+
endIndex: this.getCurrentToken().endIndex,
724+
};
725+
}
695726
else {
696727
// parse as 'Location' instead (Ex: variable named max)
697728
}
@@ -813,7 +844,7 @@ class Parser {
813844
// this one kinda a mess ngl, but my goal is to support variable initialization and assignment all together
814845
parse_funcdecl_or_vardecl() {
815846

816-
// return type
847+
// variable/return type
817848
var isArray = false;
818849
var type = NODETYPES.VARDECL;
819850
var vartype = this.getCurrentToken().value;
@@ -846,6 +877,13 @@ class Parser {
846877
this.advance();
847878
result.value = this.parse_expression();
848879
result.endIndex = this.tokens[this.i - 1].endIndex;
880+
881+
// special case: array initialization
882+
if (result.value?.type == NODETYPES.ARRAY_CREATE) {
883+
result.value.varType = result.varType;
884+
result.value.name = result.name;
885+
return result.value;
886+
}
849887
}
850888

851889
// procedure definition

src/toolbox.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ export const toolbox = {
9494
}
9595
}
9696
},
97+
{
98+
'kind': 'block',
99+
'type': 'praxly_array_create_block',
100+
'inputs': {
101+
'LENGTH': {
102+
'shadow': {
103+
'type': 'praxly_literal_block',
104+
'fields': {
105+
'LITERAL': 10,
106+
}
107+
}
108+
}
109+
}
110+
},
97111
{
98112
'kind': 'block',
99113
'type': 'praxly_array_reference_reassignment_block',

src/tree2blocks.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,15 @@ export const tree2blocks = (workspace, node) => {
473473
var result = params;
474474
break;
475475

476+
case NODETYPES.ARRAY_CREATE: // similar to ARRAY_ASSIGNMENT
477+
var arrayLength = tree2blocks(workspace, node?.arrayLength);
478+
var result = workspace.newBlock('praxly_array_create_block');
479+
result.setFieldValue(node?.varType, 'VARTYPE');
480+
result.setFieldValue(node?.name, "VARIABLENAME");
481+
result.setFieldValue(node?.elemType, "ELEMTYPE");
482+
result.getInput("LENGTH").connection.connect(arrayLength?.outputConnection);
483+
break;
484+
476485
case NODETYPES.ARRAY_REFERENCE_ASSIGNMENT:
477486
var result = workspace.newBlock('praxly_array_reference_reassignment_block');
478487
result.setFieldValue(node.location.name, "VARIABLENAME");

0 commit comments

Comments
 (0)