1
1
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:
Joel Martin 2015-07-31 00:29:44 -05:00
parent 5024b69400
commit 73bd649ff4
5 changed files with 250 additions and 6 deletions

View File

@ -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/*

View File

@ -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
View 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
View 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); }
}
}

View File

@ -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; }