Created
October 24, 2024 09:12
-
-
Save kmizu/1bea87223f942f4dad47e174f1325251 to your computer and use it in GitHub Desktop.
インタプリタの実装からプログラムを導出する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Here is my program lang. Could you provide number guessing game in the lang? | |
const readline = require('readline'); | |
const fs = require('fs'); | |
const readLineSync = require('readline-sync'); | |
function input(prompt) { | |
return readLineSync.question(prompt); | |
} | |
//プログラム全体 | |
class Program { | |
constructor(defs, ...expressions){ | |
this.defs = defs; | |
this.expressions = expressions; | |
} | |
} | |
//関数定義を表すクラス | |
class FunDef { | |
constructor(name, args, body) { | |
this.name = name; | |
this.args = args; | |
this.body = body; | |
} | |
} | |
// 式を表すクラス | |
class Expression {} | |
// 代入を表すクラス | |
class Assignment extends Expression { | |
constructor(name, expr) { | |
super(); | |
this.name = name; | |
this.expr = expr; | |
} | |
} | |
// 二項演算子(+、-、*、/)のためのクラス | |
class BinExpr extends Expression { | |
constructor(operator, lhs, rhs) { | |
super(); | |
this.operator = operator; | |
this.lhs = lhs; | |
this.rhs = rhs; | |
} | |
} | |
// 単項演算子(not)のためのクラス | |
class UnaryExpr extends Expression { | |
constructor(operator, expr) { | |
super(); | |
this.operator = operator; | |
this.expr = expr; | |
} | |
} | |
// 整数値のためのクラス | |
class Num extends Expression { | |
constructor(value) { | |
super(); | |
this.value = value; | |
} | |
} | |
// 真偽値のためのクラス | |
class Bool extends Expression { | |
constructor(value) { | |
super(); | |
this.value = value; | |
} | |
} | |
// 文字列のためのクラス | |
class Str extends Expression { | |
constructor(value) { | |
super(); | |
this.value = value; | |
} | |
} | |
// 変数参照のためのクラス | |
class VarRef extends Expression { | |
constructor(name){ | |
super(); | |
this.name = name; | |
} | |
} | |
// 関数呼び出しのためのクラス | |
class FunCall extends Expression { | |
constructor(name, ...args) { | |
super(); | |
this.name = name; | |
this.args = args; | |
} | |
} | |
// 条件分岐のためのクラス | |
class If extends Expression { | |
constructor(cond, thenExpr, elseExpr) { | |
super(); | |
this.cond = cond; | |
this.thenExpr = thenExpr; | |
this.elseExpr = elseExpr; | |
} | |
} | |
// 繰り返しのためのクラス | |
class While extends Expression { | |
constructor(cond, body) { | |
super(); | |
this.cond = cond; | |
this.body = body; | |
} | |
} | |
// 連接のためのクラス | |
class Seq extends Expression { | |
constructor(...bodies) { | |
super(); | |
this.bodies = bodies; | |
} | |
} | |
// 組み込み関数のためのクラス | |
class BuiltinFun { | |
constructor(name, func) { | |
this.name = name; | |
this.func = func; | |
} | |
} | |
function translateExpr(jsonObject) { | |
if(Array.isArray(jsonObject)) { | |
const operator = jsonObject[0]; | |
switch(operator) { | |
case "+": | |
return new BinExpr("+", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "-": | |
return new BinExpr("-", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "*": | |
return new BinExpr("*", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "/": | |
return new BinExpr("/", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "%": | |
return new BinExpr("%", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "<": | |
return new BinExpr("<", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case ">": | |
return new BinExpr(">", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "<=": | |
return new BinExpr("<=", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case ">=": | |
return new BinExpr(">=", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "==": | |
return new BinExpr("==", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "!=": | |
return new BinExpr("!=", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "and": | |
return new BinExpr("and", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "or": | |
return new BinExpr("or", translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "not": | |
return new UnaryExpr("not", translateExpr(jsonObject[1])); | |
case "seq": | |
return new Seq(...jsonObject.slice(1).map(translateExpr)); | |
case "if": | |
return new If(translateExpr(jsonObject[1]), translateExpr(jsonObject[2]), translateExpr(jsonObject[3])); | |
case "while": | |
return new While(translateExpr(jsonObject[1]), translateExpr(jsonObject[2])); | |
case "<-": | |
return new Assignment(jsonObject[1], translateExpr(jsonObject[2])); | |
case "ref": | |
return new VarRef(jsonObject[1]); | |
case "call": | |
return new FunCall(jsonObject[1], ...jsonObject.slice(2).map(translateExpr)); | |
} | |
} | |
switch (typeof jsonObject) { | |
case "number": | |
return new Num(jsonObject); | |
case "string": | |
return new Str(jsonObject); | |
case "boolean": | |
return new Bool(jsonObject); | |
} | |
throw new Error("Not implemented for: " + JSON.stringify(jsonObject)); | |
} | |
/* | |
* {"functions": [ | |
* ["add", ["x", "y"] , ["+", x, y]], ... | |
* ], | |
* "body": ["call", 1, 2] | |
* } | |
*/ | |
function translateProgram(jsonProgram) { | |
const defs = jsonProgram['functions'].map(f => | |
new FunDef(f[0], f[1], translateExpr(f[2])) | |
); | |
const body = translateExpr(jsonProgram['body']); | |
return new Program(defs, body); | |
} | |
function evalProgram(program) { | |
const env = {}; | |
const funEnv = {}; | |
const builtinFunEnv = { | |
'print': new BuiltinFun('print', (args) => { | |
console.log(...args); | |
return args[0]; | |
}), | |
'input': new BuiltinFun('input', (prompt) => { | |
return input(prompt); | |
}), | |
'parseInt': new BuiltinFun('parseInt', (a) => { | |
return parseInt(a) | |
}), | |
'add': new BuiltinFun('add', (args) => args.reduce((a, b) => a + b, 0)), | |
'mul': new BuiltinFun('mul', (args) => args.reduce((a, b) => a * b, 1)), | |
'length': new BuiltinFun('length', (args) => args[0].length), | |
'chatAt': new BuiltinFun('chatAt', (args) => args[0].charAt(args[1])), | |
'slice': new BuiltinFun('slice', (args) => args[0].slice(args[1], args[2])), | |
}; | |
let result = null; | |
program.defs.forEach((d) => { | |
funEnv[d.name] = d; | |
}); | |
program.expressions.forEach((e) => { | |
result = eval(e, env, funEnv, builtinFunEnv); | |
}); | |
return result; | |
} | |
function evalJsonProgram(jsonString) { | |
const jsonProgram = JSON.parse(jsonString); | |
const program = translateProgram(jsonProgram); | |
return evalProgram(program); | |
} | |
function eval(expr, env, funEnv, builtinFunEnv) { | |
if(expr instanceof BinExpr) { | |
const resultL = eval(expr.lhs, env, funEnv, builtinFunEnv); | |
const resultR = eval(expr.rhs, env, funEnv, builtinFunEnv); | |
switch(expr.operator) { | |
case "+": | |
return resultL + resultR; | |
case "-": | |
return resultL - resultR; | |
case "*": | |
return resultL * resultR; | |
case "/": | |
return resultL / resultR; | |
case "%": | |
return resultL % resultR; | |
case "<": | |
return resultL < resultR; | |
case ">": | |
return resultL > resultR; | |
case "<=": | |
return resultL <= resultR; | |
case ">=": | |
return resultL >= resultR; | |
case "==": | |
return resultL === resultR; | |
case "!=": | |
return resultL !== resultR; | |
case "and": | |
return resultL && resultR; | |
case "or": | |
return resultL || resultR; | |
} | |
} else if(expr instanceof UnaryExpr) { | |
const result = eval(expr.expr, env, funEnv, builtinFunEnv); | |
switch(expr.operator) { | |
case "not": | |
return !result; | |
} | |
} else if(expr instanceof Num) { | |
return expr.value; | |
} else if(expr instanceof Bool) { | |
return expr.value; | |
} else if(expr instanceof Str) { | |
return expr.value; | |
} else if(expr instanceof VarRef) { | |
if (!(expr.name in env)) { | |
throw new Error(variable ${expr.name} is not defined); | |
} | |
return env[expr.name]; | |
} else if(expr instanceof Assignment) { | |
const result = eval(expr.expr, env, funEnv, builtinFunEnv); | |
env[expr.name] = result; | |
return result; | |
} else if(expr instanceof Seq) { | |
let result = null; | |
expr.bodies.forEach((e) => { | |
result = eval(e, env, funEnv, builtinFunEnv); | |
}); | |
return result; | |
} else if(expr instanceof If) { | |
if(eval(expr.cond, env, funEnv, builtinFunEnv)) { | |
return eval(expr.thenExpr, env, funEnv, builtinFunEnv); | |
}else { | |
return eval(expr.elseExpr, env, funEnv, builtinFunEnv); | |
} | |
} else if(expr instanceof While) { | |
while(eval(expr.cond, env, funEnv, builtinFunEnv)) { | |
eval(expr.body, env, funEnv, builtinFunEnv); | |
} | |
return 0; | |
} else if(expr instanceof FunCall) { | |
const def = funEnv[expr.name] || builtinFunEnv[expr.name]; | |
if(!def) throw function ${expr.name} is not defined; | |
const args = expr.args.map((a) => eval(a, env, funEnv, builtinFunEnv)); | |
if (def instanceof BuiltinFun) { | |
return def.func(args); | |
} | |
const newEnv = {} | |
for(let i = 0; i < def.args.length; i++) { | |
newEnv[def.args[i]] = args[i]; | |
} | |
return eval(def.body, newEnv, funEnv, builtinFunEnv); | |
} else { | |
console.assert(false, should not reach here ${expr}); | |
} | |
} | |
try { | |
const program = fs.readFileSync(process.argv[2], 'utf8'); | |
evalJsonProgram(program); | |
} catch (err) { | |
console.error('Error reading the file:', err); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment