Guest User

Untitled

a guest
Apr 21st, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.59 KB | None | 0 0
  1. class Env {
  2. constructor(p, e = {}) {
  3. this.p = p;
  4. this.e = e;
  5. }
  6.  
  7. get(name) {
  8. let val = this.e[name];
  9.  
  10. if (typeof val !== 'undefined')
  11. return val;
  12.  
  13. if (this.p)
  14. return this.p.get(name);
  15.  
  16. throw `Identifier ${name} is not defined`;
  17. }
  18.  
  19. set(name, val) {
  20. let exists = !!this.e[name];
  21.  
  22. if (!exists)
  23. this.e[name] = val;
  24. else
  25. throw `Identifier ${name} is already defined`;
  26. }
  27. }
  28.  
  29. function Func(params, body, _env) {
  30. return (x) => {
  31. let env = new Env(_env, params.reduce((acc, p, i) => {
  32. acc[p.v] = x[i];
  33. return acc;
  34. }, {}));
  35. return eval(body, env);
  36. };
  37. }
  38.  
  39. let proc = process,
  40. con = console,
  41. cmpop = (x, f) => x.slice(1).every(f),
  42. globals = {
  43. '+': (x) => x.reduce((acc, val) => acc + val),
  44. '-': (x) => x.reduce((acc, val) => acc - val),
  45. '/': (x) => x.reduce((acc, val) => { if (val === 0) throw "Cannot divide by 0"; return acc / val; }),
  46. '*': (x) => x.reduce((acc, val) => acc * val),
  47. '**': (x) => x.reduce((acc, val) => Math.pow(acc, val)),
  48. '>': (x) => cmpop(x, (v) => x[0] > v),
  49. '>=': (x) => cmpop(x, (v) => x[0] >= v),
  50. '<': (x) => cmpop(x, (v) => x[0] < v),
  51. '<=': (x) => cmpop(x, (v) => x[0] <= v),
  52. '==': (x) => cmpop(x, (v) => x[0] === v),
  53. 'car': (x) => x[0],
  54. 'cdr': (x) => x.slice(1),
  55. 'cons': (x) => [x[0]].concat(x.slice(1))
  56. },
  57. genv = new Env(null, globals),
  58. rl = require('readline').createInterface({
  59. input: proc.stdin,
  60. output: proc.stdout,
  61. prompt: '\u039b> '
  62. });
  63.  
  64.  
  65. function lex(source) {
  66. return source.replace(/\(|\)/g, ' $& ').match(/".*?"|\S+/g);
  67. }
  68.  
  69. function parse(tokens) {
  70. if (tokens.length === 0) throw 'Unexpected EOF';
  71.  
  72. let token = tokens.shift();
  73. switch (token) {
  74. case '(':
  75. let list = [];
  76. while (tokens[0] !== ')') list.push(parse(tokens));
  77.  
  78. tokens.shift();
  79. return { t: 'expression', args: list };
  80. case ')':
  81. throw 'Unmatched delimiter )';
  82. default:
  83. if (token[0] === '"') return { t: 'string', v: token.substr(1, token.length-2) };
  84.  
  85. let numberVal = Number(token);
  86. return isNaN(numberVal) ? { t: 'identifier', v: token } : { t: 'number', v: numberVal };
  87. }
  88. }
  89.  
  90. function eval(exp, env) {
  91. switch (exp.t) {
  92. case 'expression':
  93. let args = exp.args,
  94. v = args[0].v;
  95.  
  96. if (v === 'if') {
  97. return eval(args[1], env) ? eval(args[2], env) : eval(args[3], env);
  98. } else if (v === 'define') {
  99. env.set(args[1].v, eval(args[2], env));
  100. } else if (v === 'lambda') {
  101. return Func(args[1].args, args[2], env);
  102. } else if (v === 'quote') {
  103. return args[1];
  104. }
  105. else {
  106. let f = eval(args[0], env),
  107. vals = args.slice(1).map((e) => eval(e, env));
  108. return f(vals);
  109. }
  110. case 'string':
  111. case 'number':
  112. return exp.v;
  113. case 'identifier':
  114. return env.get(exp.v);
  115. default:
  116. return exp.v;
  117. }
  118.  
  119. }
  120.  
  121. rl.prompt();
  122. rl.on('line', (l) => {
  123. if (l) {
  124. try {
  125. let r = eval(parse(lex(l.trim())), genv);
  126. if (typeof r !== 'undefined') con.log(r);
  127. } catch (e) {
  128. con.error(`Error: ${e}`);
  129. }
  130. }
  131. rl.prompt();
  132. }).on('close', () => proc.exit(0));
Add Comment
Please, Sign In to add comment