mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 01:43:50 +03:00
ES6: add step6 and step7 basics.
This commit is contained in:
parent
5024b69400
commit
73bd649ff4
@ -1,6 +1,6 @@
|
||||
|
||||
SOURCES = step0_repl.js step1_read_print.js step2_eval.js step3_env.js \
|
||||
step4_if_fn_do.js step5_tco.js
|
||||
step4_if_fn_do.js step5_tco.js step6_file.js step7_quote.js
|
||||
|
||||
all: $(foreach s,$(SOURCES),build/$(s))
|
||||
|
||||
@ -8,15 +8,13 @@ build/%.js: %.js
|
||||
babel --source-maps true $< --out-file $@
|
||||
|
||||
build/step0_repl.js: step0_repl.js build/node_readline.js
|
||||
|
||||
build/step1_read_print.js: step1_read_print.js build/node_readline.js build/types.js build/reader.js build/printer.js
|
||||
|
||||
build/step2_eval.js: step2_eval.js build/node_readline.js build/types.js build/reader.js build/printer.js
|
||||
|
||||
build/step3_env.js: step3_env.js build/node_readline.js build/types.js build/reader.js build/printer.js build/env.js
|
||||
|
||||
build/step4_if_fn_do.js: step4_if_fn_do.js build/node_readline.js build/types.js build/reader.js build/printer.js build/env.js build/core.js
|
||||
build/step5_tco.js: step5_tco.js build/node_readline.js build/types.js build/reader.js build/printer.js build/env.js build/core.js
|
||||
build/step6_file.js: step6_file.js build/node_readline.js build/types.js build/reader.js build/printer.js build/env.js build/core.js
|
||||
build/step7_quote.js: step7_quote.js build/node_readline.js build/types.js build/reader.js build/printer.js build/env.js build/core.js
|
||||
|
||||
clean:
|
||||
rm -f build/*
|
||||
|
27
es6/core.js
27
es6/core.js
@ -1,5 +1,6 @@
|
||||
import * as types from './types';
|
||||
import { pr_str } from './printer';
|
||||
import { read_str } from './reader';
|
||||
|
||||
// String functions
|
||||
function do_pr_str(...args) {
|
||||
@ -20,6 +21,28 @@ function println(...args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function slurp(f) {
|
||||
if (typeof require !== 'undefined') {
|
||||
return require('fs').readFileSync(f, 'utf-8');
|
||||
} else {
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", f, false);
|
||||
req.send();
|
||||
if (req.status == 200) {
|
||||
return req.responseText;
|
||||
} else {
|
||||
throw new Error("Failed to slurp file: " + f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence functions
|
||||
function cons(a, b) { return [a].concat(b); }
|
||||
|
||||
function concat(lst) {
|
||||
lst = lst || [];
|
||||
return lst.concat.apply(lst, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
|
||||
// types.ns is namespace of type functions
|
||||
export const core_ns = new Map([
|
||||
@ -29,6 +52,8 @@ export const core_ns = new Map([
|
||||
['str', str],
|
||||
['prn', prn],
|
||||
['println', println],
|
||||
['read-string',read_str],
|
||||
['slurp', slurp],
|
||||
|
||||
['<' , (a,b) => a<b],
|
||||
['<=', (a,b) => a<=b],
|
||||
@ -43,5 +68,7 @@ export const core_ns = new Map([
|
||||
['list', (...a) => a],
|
||||
['list?', types._list_Q],
|
||||
|
||||
['cons', (a,b) => [a].concat(b)],
|
||||
['concat', (...a) => a.reduce((x,y) => x.concat(y), [])],
|
||||
['empty?', a => a.length === 0],
|
||||
['count', a => a === null ? 0 : a.length]]);
|
||||
|
100
es6/step6_file.js
Normal file
100
es6/step6_file.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { readline } from './node_readline';
|
||||
import { Sym, _list_Q, _malfunc, _malfunc_Q } from './types';
|
||||
import { BlankException, read_str } from './reader';
|
||||
import { pr_str } from './printer';
|
||||
import { new_env, env_set, env_get } from './env';
|
||||
import { core_ns } from './core';
|
||||
|
||||
// read
|
||||
const READ = (str) => read_str(str);
|
||||
|
||||
// eval
|
||||
const eval_ast = (ast, env) => {
|
||||
if (ast instanceof Sym) {
|
||||
return env_get(env, ast)
|
||||
} else if (_list_Q(ast)) {
|
||||
return ast.map((x) => EVAL(x, env));
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
const EVAL = (ast, env) => {
|
||||
while (true) {
|
||||
//console.log("EVAL:", pr_str(ast, true));
|
||||
if (!_list_Q(ast)) { return eval_ast(ast, env) }
|
||||
|
||||
let [{ name: a0sym }, a1, a2, a3] = ast;
|
||||
switch (a0sym) {
|
||||
case 'def!':
|
||||
return env_set(env, a1, EVAL(a2, env));
|
||||
case 'let*':
|
||||
let let_env = new_env(env);
|
||||
for (let i=0; i < a1.length; i+=2) {
|
||||
env_set(let_env, a1[i], EVAL(a1[i+1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = a2;
|
||||
break; // continue TCO loop
|
||||
case "do":
|
||||
eval_ast(ast.slice(1,ast.length-1), env);
|
||||
ast = ast[ast.length-1];
|
||||
break; // continue TCO loop
|
||||
case "if":
|
||||
let cond = EVAL(a1, env);
|
||||
if (cond === null || cond === false) {
|
||||
ast = (typeof a3 !== "undefined") ? a3 : null;
|
||||
} else {
|
||||
ast = a2;
|
||||
}
|
||||
break; // continue TCO loop
|
||||
case "fn*":
|
||||
return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),
|
||||
a2, env, a1);
|
||||
default:
|
||||
let [f, ...args] = eval_ast(ast, env);
|
||||
if (_malfunc_Q(f)) {
|
||||
env = new_env(f.env, f.params, args);
|
||||
ast = f.ast;
|
||||
break; // continue TCO loop
|
||||
} else {
|
||||
return f(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
const PRINT = (exp) => pr_str(exp, true);
|
||||
|
||||
// repl
|
||||
let repl_env = new_env();
|
||||
const REP = (str) => PRINT(EVAL(READ(str), repl_env));
|
||||
|
||||
// core.EXT: defined using ES6
|
||||
for (let [k, v] of core_ns) { env_set(repl_env, new Sym(k), v) }
|
||||
env_set(repl_env, new Sym('eval'), a => EVAL(a, repl_env));
|
||||
env_set(repl_env, new Sym('*ARGV*'), []);
|
||||
|
||||
// core.mal: defined using language itself
|
||||
REP("(def! not (fn* (a) (if a false true)))")
|
||||
REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
env_set(repl_env, '*ARGV*', process.argv.slice(3));
|
||||
REPL('(load-file "' + process.argv[2] + '")');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
let line = readline("user> ");
|
||||
if (line == null) break;
|
||||
try {
|
||||
if (line) { console.log(REP(line)); }
|
||||
} catch (exc) {
|
||||
if (exc instanceof BlankException) { continue; }
|
||||
if (exc.stack) { console.log(exc.stack); }
|
||||
else { console.log("Error: " + exc); }
|
||||
}
|
||||
}
|
119
es6/step7_quote.js
Normal file
119
es6/step7_quote.js
Normal file
@ -0,0 +1,119 @@
|
||||
import { readline } from './node_readline';
|
||||
import { Sym, _list_Q, _malfunc, _malfunc_Q, _sequential_Q } from './types';
|
||||
import { BlankException, read_str } from './reader';
|
||||
import { pr_str } from './printer';
|
||||
import { new_env, env_set, env_get } from './env';
|
||||
import { core_ns } from './core';
|
||||
|
||||
// read
|
||||
const READ = (str) => read_str(str);
|
||||
|
||||
// eval
|
||||
const is_pair = x => _sequential_Q(x) && x.length > 0
|
||||
|
||||
const quasiquote = ast => {
|
||||
if (!is_pair(ast)) {
|
||||
return [new Sym("quote"), ast];
|
||||
} else if (ast[0].name === 'unquote') {
|
||||
return ast[1];
|
||||
} else if (is_pair(ast[0]) && ast[0][0].name === 'splice-unquote') {
|
||||
return [new Sym("concat"), ast[0][1], quasiquote(ast.slice(1))];
|
||||
} else {
|
||||
return [new Sym("cons"), quasiquote(ast[0]), quasiquote(ast.slice(1))];
|
||||
}
|
||||
}
|
||||
|
||||
const eval_ast = (ast, env) => {
|
||||
if (ast instanceof Sym) {
|
||||
return env_get(env, ast)
|
||||
} else if (_list_Q(ast)) {
|
||||
return ast.map((x) => EVAL(x, env));
|
||||
} else {
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
const EVAL = (ast, env) => {
|
||||
while (true) {
|
||||
//console.log("EVAL:", pr_str(ast, true));
|
||||
if (!_list_Q(ast)) { return eval_ast(ast, env) }
|
||||
|
||||
let [{ name: a0sym }, a1, a2, a3] = ast;
|
||||
switch (a0sym) {
|
||||
case 'def!':
|
||||
return env_set(env, a1, EVAL(a2, env));
|
||||
case 'let*':
|
||||
let let_env = new_env(env);
|
||||
for (let i=0; i < a1.length; i+=2) {
|
||||
env_set(let_env, a1[i], EVAL(a1[i+1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = a2;
|
||||
break; // continue TCO loop
|
||||
case "quote":
|
||||
return a1;
|
||||
case "quasiquote":
|
||||
ast = quasiquote(a1);
|
||||
break; // continue TCO loop
|
||||
case "do":
|
||||
eval_ast(ast.slice(1,ast.length-1), env);
|
||||
ast = ast[ast.length-1];
|
||||
break; // continue TCO loop
|
||||
case "if":
|
||||
let cond = EVAL(a1, env);
|
||||
if (cond === null || cond === false) {
|
||||
ast = (typeof a3 !== "undefined") ? a3 : null;
|
||||
} else {
|
||||
ast = a2;
|
||||
}
|
||||
break; // continue TCO loop
|
||||
case "fn*":
|
||||
return _malfunc((...args) => EVAL(a2, new_env(env, a1, args)),
|
||||
a2, env, a1);
|
||||
default:
|
||||
let [f, ...args] = eval_ast(ast, env);
|
||||
if (_malfunc_Q(f)) {
|
||||
env = new_env(f.env, f.params, args);
|
||||
ast = f.ast;
|
||||
break; // continue TCO loop
|
||||
} else {
|
||||
return f(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print
|
||||
const PRINT = (exp) => pr_str(exp, true);
|
||||
|
||||
// repl
|
||||
let repl_env = new_env();
|
||||
const REP = (str) => PRINT(EVAL(READ(str), repl_env));
|
||||
|
||||
// core.EXT: defined using ES6
|
||||
for (let [k, v] of core_ns) { env_set(repl_env, new Sym(k), v) }
|
||||
env_set(repl_env, new Sym('eval'), a => EVAL(a, repl_env));
|
||||
env_set(repl_env, new Sym('*ARGV*'), []);
|
||||
|
||||
// core.mal: defined using language itself
|
||||
REP("(def! not (fn* (a) (if a false true)))")
|
||||
REP("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))");
|
||||
|
||||
if (process.argv.length > 2) {
|
||||
env_set(repl_env, '*ARGV*', process.argv.slice(3));
|
||||
REPL('(load-file "' + process.argv[2] + '")');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
let line = readline("user> ");
|
||||
if (line == null) break;
|
||||
try {
|
||||
if (line) { console.log(REP(line)); }
|
||||
} catch (exc) {
|
||||
if (exc instanceof BlankException) { continue; }
|
||||
if (exc.stack) { console.log(exc.stack); }
|
||||
else { console.log("Error: " + exc); }
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ export function _equal_Q (a, b) {
|
||||
return false;
|
||||
}
|
||||
switch (ota) {
|
||||
case 'symbol': return a.value === b.value;
|
||||
case 'symbol': return a.name === b.name;
|
||||
case 'list':
|
||||
case 'vector':
|
||||
if (a.length !== b.length) { return false; }
|
||||
|
Loading…
Reference in New Issue
Block a user