Skip to content

Commit

Permalink
Implement function definition / call
Browse files Browse the repository at this point in the history
- in JSON grammar
- in JSON Array grammar
  • Loading branch information
Kota Mizushima committed Feb 7, 2024
1 parent a9f461d commit d9335cd
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 2,303 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
yarn-error.log
node_modules/
.idea/
*.log
Expand Down
40 changes: 38 additions & 2 deletions __tests__/minis_json_array_evaluator.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";
const {evaluateJsonArray} = require("../minis/minis_json_array_evaluator");
const {evaluateJsonArray, evaluateJsonProgram} = require("../minis/minis_json_array_evaluator");
const {tProgram, tFunction, tAdd, tId, tCall, tInt} = require("../minis/minis_ast");

test(`1 ==> 1` ,() => {
const e = `1`;
Expand Down Expand Up @@ -82,4 +83,39 @@ test(`
["id", "i"]
]`;
expect(evaluateJsonArray(e)).toBe(10);
})
})

test(`
def add(a, b) {
return a + b;
},
add(1, 2)
==> 3`, () => {
const program = `[
["def", "add", ["a", "b"], ["+", ["id", "a"], ["id", "b"]]],
["call", "add", 1, 2]
]`;
expect(evaluateJsonProgram(program)).toBe(3);
});

test(`
def factorial(n) {
if(n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
},
factorial(5)
==> 120`, () => {
const program = `[
["def", "factorial", ["n"],
["if", ["==", ["id", "n"], 0],
1,
["*", ["id", "n"], ["call", "factorial", ["-", ["id", "n"], 1]]]
]
],
["call", "factorial", 5]
]`;
expect(evaluateJsonProgram(program)).toBe(120);
});
17 changes: 15 additions & 2 deletions __tests__/minis_json_evaluator.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use strict";
const {evaluateJson} = require("../minis/minis_json_evaluator");
const {evaluateJson, evaluateJsonProgram} = require("../minis/minis_json_evaluator");

test(`evaluateJson("1") == 1` ,() => {
const e = `1`;
Expand Down Expand Up @@ -91,4 +91,17 @@ test(`
]
}`;
expect(evaluateJson(e)).toBe(10);
})
})

test(`
def add(a, b) {
return a + b;
},
add(1, 2)
==> 3`, () => {
const program = `[
{"type": "def", "name": "add", "params": ["a", "b"], "body": {"type": "+", "operands": [{"type": "id", "name": "a"}, {"type": "id", "name": "b"}]}},
{"type": "call", "name": "add", "args": [1, 2]}
]`;
expect(evaluateJsonProgram(program)).toBe(3);
});
143 changes: 66 additions & 77 deletions minis/minis_json_array_evaluator.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,73 @@
const {MBinExpr, MInt, MSeq, MCall, MAssignment, MIf, MWhile, MIdent} = require('./minis_ast');
const {evaluate, evaluateProgram} = require("../minis/minis_evaluator");
const {tProgram, tFunction, tAdd, tSub, tMul, tDiv, tInt, tLt, tLte, tGt, tGte, tEq, tNe, tId, tAssign, tSeq, tWhile, tCall, tIf} = require('../minis/minis_ast');
function evaluateJsonArray(jsonString) {
const jsonObject = JSON.parse(jsonString);
function translateToAst(jsonObject) {
if(Array.isArray(jsonObject)) {
const operator = jsonObject[0];
switch(operator) {
case "+":
return tAdd(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "-":
return tSub(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "*":
return tMul(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "/":
return tDiv(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "<":
return tLt(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case ">":
return tGt(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "<=":
return tLte(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case ">=":
return tGte(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "==":
return tEq(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "!=":
return tNe(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "seq":
return tSeq(...jsonObject.slice(1).map(translateToAst));
case "if":
return tIf(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]), translateToAst(jsonObject[3]));
case "while":
return tWhile(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "assign":
return tAssign(jsonObject[1], translateToAst(jsonObject[2]));
case "id":
return tId(jsonObject[1]);
}
}
switch (typeof jsonObject) {
case "number":
return tInt(jsonObject);
case "object":
const type = jsonObject['type'];
switch (type) {
case '+':
return tAdd(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '-':
return tSub(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '*':
return tMul(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '/':
return tDiv(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<':
return tLt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>':
return tGt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<=':
return tLte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>=':
return tGte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '==':
return tEq(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '!=':
return tNe(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case "if":
return tIf(translateToAst(jsonObject['condition']), translateToAst(jsonObject['then']), translateToAst(jsonObject['else']));
case "seq":
return tSeq(...jsonObject['expressions'].map(translateToAst));
case "while":
return tWhile(translateToAst(jsonObject['condition']), translateToAst(jsonObject['body']));
case "assign":
return tAssign(jsonObject['name'], translateToAst(jsonObject['value']));
case "id":
return tId(jsonObject['name']);
}
function translateToAst(jsonObject) {
if(Array.isArray(jsonObject)) {
const operator = jsonObject[0];
switch(operator) {
case "+":
return tAdd(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "-":
return tSub(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "*":
return tMul(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "/":
return tDiv(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "<":
return tLt(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case ">":
return tGt(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "<=":
return tLte(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case ">=":
return tGte(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "==":
return tEq(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "!=":
return tNe(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "seq":
return tSeq(...jsonObject.slice(1).map(translateToAst));
case "if":
return tIf(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]), translateToAst(jsonObject[3]));
case "while":
return tWhile(translateToAst(jsonObject[1]), translateToAst(jsonObject[2]));
case "assign":
return tAssign(jsonObject[1], translateToAst(jsonObject[2]));
case "id":
return tId(jsonObject[1]);
case "call":
return tCall(jsonObject[1], ...jsonObject.slice(2).map(translateToAst));
}
throw new Error("Not implemented for: " + JSON.stringify(jsonObject));
}
switch (typeof jsonObject) {
case "number":
return tInt(jsonObject);
}
throw new Error("Not implemented for: " + JSON.stringify(jsonObject));
}
function evaluateJsonArray(jsonString) {
const jsonObject = JSON.parse(jsonString);
return evaluate(translateToAst(jsonObject), {});
}
module.exports = {evaluateJsonArray};
function evaluateJsonProgram(jsonString) {
const program = JSON.parse(jsonString);
const environment = {};
const bodies = [];
const functions = [];
program.forEach(t => {
if(t[0] === "def") {
functions.push(t);
} else {
bodies.push(translateToAst(t));
}
});
functions.forEach(f => {
environment[f[1]] = tFunction(f[1], f[2], translateToAst(f[3]));
});
let result;
bodies.forEach(body => {
result = evaluate(body, environment);
});
return result;
}
module.exports = {evaluateJsonArray, evaluateJsonProgram};
109 changes: 67 additions & 42 deletions minis/minis_json_evaluator.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,74 @@
const {MBinExpr, MInt, MSeq, MCall, MAssignment, MIf, MWhile, MIdent} = require('./minis_ast');
const {evaluate, evaluateProgram} = require("../minis/minis_evaluator");
const {tProgram, tFunction, tAdd, tSub, tMul, tDiv, tInt, tLt, tLte, tGt, tGte, tEq, tNe, tId, tAssign, tSeq, tWhile, tCall, tIf} = require('../minis/minis_ast');

function translateToAst(jsonObject) {
switch (typeof jsonObject) {
case "number":
return tInt(jsonObject);
case "object":
const type = jsonObject['type'];
switch (type) {
case '+':
return tAdd(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '-':
return tSub(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '*':
return tMul(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '/':
return tDiv(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<':
return tLt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>':
return tGt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<=':
return tLte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>=':
return tGte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '==':
return tEq(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '!=':
return tNe(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case "if":
return tIf(translateToAst(jsonObject['condition']), translateToAst(jsonObject['then']), translateToAst(jsonObject['else']));
case "seq":
return tSeq(...jsonObject['expressions'].map(translateToAst));
case "while":
return tWhile(translateToAst(jsonObject['condition']), translateToAst(jsonObject['body']));
case "assign":
return tAssign(jsonObject['name'], translateToAst(jsonObject['value']));
case "id":
return tId(jsonObject['name']);
case "call":
return tCall(jsonObject['name'], ...jsonObject['args'].map(translateToAst));
}
}
throw new Error("Not implemented for: " + JSON.stringify(jsonObject));
}
function evaluateJson(jsonString) {
const jsonObject = JSON.parse(jsonString);
function translateToAst(jsonObject) {
switch (typeof jsonObject) {
case "number":
return tInt(jsonObject);
case "object":
const type = jsonObject['type'];
switch (type) {
case '+':
return tAdd(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '-':
return tSub(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '*':
return tMul(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '/':
return tDiv(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<':
return tLt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>':
return tGt(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '<=':
return tLte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '>=':
return tGte(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '==':
return tEq(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case '!=':
return tNe(translateToAst(jsonObject['operands'][0]), translateToAst(jsonObject['operands'][1]));
case "if":
return tIf(translateToAst(jsonObject['condition']), translateToAst(jsonObject['then']), translateToAst(jsonObject['else']));
case "seq":
return tSeq(...jsonObject['expressions'].map(translateToAst));
case "while":
return tWhile(translateToAst(jsonObject['condition']), translateToAst(jsonObject['body']));
case "assign":
return tAssign(jsonObject['name'], translateToAst(jsonObject['value']));
case "id":
return tId(jsonObject['name']);
}
}
throw new Error("Not implemented for: " + JSON.stringify(jsonObject));
}
return evaluate(translateToAst(jsonObject), {});
}
module.exports = {evaluateJson};
function evaluateJsonProgram(jsonString) {
const program = JSON.parse(jsonString);
const environment = {};
const bodies = [];
const functions = [];
program.forEach(t => {
if(t['type'] === "def") {
functions.push(t);
} else {
bodies.push(translateToAst(t));
}
});
functions.forEach(f => {
environment[f['name']] = tFunction(f['name'], f['params'], translateToAst(f['body']));
});
let result;
bodies.forEach(body => {
result = evaluate(body, environment);
});
return result;
}

module.exports = {evaluateJson, evaluateJsonProgram};
Loading

0 comments on commit d9335cd

Please sign in to comment.