Skip to content

Commit

Permalink
ES6: all optionals and self-hosting.
Browse files Browse the repository at this point in the history
  • Loading branch information
kanaka committed Aug 2, 2015
1 parent e5c4e65 commit afa7931
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 59 deletions.
41 changes: 37 additions & 4 deletions es6/core.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { _equal_Q, _list_Q, _clone, Sym, Atom } from './types';
import { _equal_Q, _clone, _list_Q, _sequential_Q,
_keyword, _keyword_Q, _vector, _vector_Q,
_hash_map, _hash_map_Q, _assoc_BANG, _dissoc_BANG,
Sym, Atom } from './types';
import { pr_str } from './printer';
import { readline } from './node_readline';
import { read_str } from './reader';
Expand Down Expand Up @@ -47,7 +50,23 @@ function nth(lst, idx) {
}

function conj(lst, ...args) {
return b.slice(1).reverse().concat(lst);
if (_list_Q(lst)) {
return args.reverse().concat(lst);
} else {
return _vector(...lst.concat(args));
}
}

function keys(hm) {
let ks = [];
for (let k of hm.keys()) { ks.push(k) };
return ks;
}

function vals(hm) {
let vs = [];
for (let v of hm.values()) { vs.push(v) };
return vs;
}

// Metadata functions
Expand All @@ -65,11 +84,14 @@ function with_meta(obj, m) {
export const core_ns = new Map([
['=', _equal_Q],
['throw', mal_throw],

['nil?', a => a === null],
['true?', a => a === true],
['false?', a => a === false],
['symbol', a => new Sym(a)],
['symbol?', a => a instanceof Sym],
['keyword', a => _keyword(a)],
['keyword?', a => _keyword_Q(a)],

['pr-str', do_pr_str],
['str', str],
Expand All @@ -91,7 +113,18 @@ export const core_ns = new Map([

['list', (...a) => a],
['list?', _list_Q],

['vector', _vector],
['vector?', _vector_Q],
['hash-map', _hash_map],
['map?', _hash_map_Q],
['assoc', (m,...a) => _assoc_BANG(_clone(m), ...a)],
['dissoc', (m,...a) => _dissoc_BANG(_clone(m), ...a)],
['get', (m,a) => m === null ? null : m.has(a) ? m.get(a) : null],
['contains?', (m,a) => m.has(a)],
['keys', keys],
['vals', vals],

['sequential?', _sequential_Q],
['cons', (a,b) => [a].concat(b)],
['concat', (...a) => a.reduce((x,y) => x.concat(y), [])],
['nth', nth],
Expand All @@ -110,5 +143,5 @@ export const core_ns = new Map([
['atom?', a => a instanceof Atom],
['deref', atm => atm.val],
['reset!', (atm,a) => atm.val = a],
['swap!', (atm,f,args) => atm.val = f(...[atm.val].concat(args))]
['swap!', (atm,f,...args) => atm.val = f(...[atm.val].concat(args))]
]);
15 changes: 12 additions & 3 deletions es6/printer.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { Sym, Atom } from './types';
import { Sym, _list_Q, _vector_Q, _hash_map_Q, Atom } from './types';

export function pr_str(obj, print_readably) {
if (typeof print_readably === 'undefined') { print_readably = true; }
var _r = print_readably;
if (obj instanceof Array) {
if (_list_Q(obj)) {
var ret = obj.map(function(e) { return pr_str(e,_r); });
return "(" + ret.join(' ') + ")";
} else if (_vector_Q(obj)) {
var ret = obj.map(function(e) { return pr_str(e,_r); });
return "[" + ret.join(' ') + "]";
} else if (_hash_map_Q(obj)) {
var ret = [];
for (let [k,v] of obj) {
ret.push(pr_str(k,_r), pr_str(v,_r));
}
return "{" + ret.join(' ') + "}";
} else if (typeof obj === "string") {
if (obj[0] === '\u029e') {
return ':' + obj.slice(1);
Expand All @@ -19,7 +28,7 @@ export function pr_str(obj, print_readably) {
} else if (obj === null) {
return "nil";
} else if (obj instanceof Atom) {
return "(atom " + obj.val + ")";
return "(atom " + pr_str(obj.val,_r) + ")";
} else {
return obj.toString();
}
Expand Down
36 changes: 27 additions & 9 deletions es6/reader.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as types from './types';
import { _symbol, _keyword, _vector, _hash_map } from './types';

export class BlankException extends Error {}

Expand Down Expand Up @@ -34,15 +34,15 @@ function read_atom (reader) {
.replace(/\\"/g, '"')
.replace(/\\n/g, "\n"); // string
} else if (token[0] === ":") {
return types._keyword(token.slice(1));
return _keyword(token.slice(1));
} else if (token === "nil") {
return null;
} else if (token === "true") {
return true;
} else if (token === "false") {
return false;
} else {
return types._symbol(token); // symbol
return _symbol(token); // symbol
}
}

Expand All @@ -65,29 +65,47 @@ function read_list(reader, start, end) {
return ast;
}

// read vector of tokens
function read_vector(reader) {
return _vector(...read_list(reader, '[', ']'));
}

// read hash-map key/value pairs
function read_hash_map(reader) {
return _hash_map(...read_list(reader, '{', '}'));
}

function read_form(reader) {
var token = reader.peek();
switch (token) {
// reader macros/transforms
case ';': return null; // Ignore comments
case '\'': reader.next();
return [types._symbol('quote'), read_form(reader)];
return [_symbol('quote'), read_form(reader)];
case '`': reader.next();
return [types._symbol('quasiquote'), read_form(reader)];
return [_symbol('quasiquote'), read_form(reader)];
case '~': reader.next();
return [types._symbol('unquote'), read_form(reader)];
return [_symbol('unquote'), read_form(reader)];
case '~@': reader.next();
return [types._symbol('splice-unquote'), read_form(reader)];
return [_symbol('splice-unquote'), read_form(reader)];
case '^': reader.next();
var meta = read_form(reader);
return [types._symbol('with-meta'), read_form(reader), meta];
return [_symbol('with-meta'), read_form(reader), meta];
case '@': reader.next();
return [types._symbol('deref'), read_form(reader)];
return [_symbol('deref'), read_form(reader)];

// list
case ')': throw new Error("unexpected ')'");
case '(': return read_list(reader);

// vector
case ']': throw new Error("unexpected ']'");
case '[': return read_vector(reader);

// hash-map
case '}': throw new Error("unexpected '}'");
case '{': return read_hash_map(reader);

// atom
default: return read_atom(reader);
}
Expand Down
10 changes: 9 additions & 1 deletion es6/step2_eval.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readline } from './node_readline';
import { Sym, _list_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_Q } from './types';
import { BlankException, read_str } from './reader';
import { pr_str } from './printer';

Expand All @@ -16,6 +16,14 @@ const eval_ast = (ast, env) => {
}
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down
10 changes: 9 additions & 1 deletion es6/step3_env.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readline } from './node_readline';
import { Sym, _list_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_Q } from './types';
import { BlankException, read_str } from './reader';
import { pr_str } from './printer';
import { new_env, env_set, env_get } from './env';
Expand All @@ -13,6 +13,14 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down
11 changes: 10 additions & 1 deletion es6/step4_if_fn_do.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { readline } from './node_readline';
import { Sym, _list_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_Q } from './types';
import { BlankException, read_str } from './reader';
import { pr_str } from './printer';
import { new_env, env_set, env_get } from './env';
Expand All @@ -14,12 +14,21 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
}

const EVAL = (ast, env) => {
//console.log("EVAL:", pr_str(ast, true));
if (!_list_Q(ast)) { return eval_ast(ast, env) }

let [{ name: a0sym }, a1, a2, a3] = ast;
Expand Down
11 changes: 10 additions & 1 deletion es6/step5_tco.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readline } from './node_readline';
import { Sym, _list_Q, _malfunc, _malfunc_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_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';
Expand All @@ -14,6 +15,14 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down
13 changes: 11 additions & 2 deletions es6/step6_file.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readline } from './node_readline';
import { Sym, _list_Q, _malfunc, _malfunc_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_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';
Expand All @@ -14,6 +15,14 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down Expand Up @@ -82,7 +91,7 @@ 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] + '")');
REP('(load-file "' + process.argv[2] + '")');
process.exit(0);
}

Expand Down
13 changes: 11 additions & 2 deletions es6/step7_quote.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readline } from './node_readline';
import { Sym, _list_Q, _malfunc, _malfunc_Q, _sequential_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_Q, _sequential_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';
Expand Down Expand Up @@ -28,6 +29,14 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down Expand Up @@ -101,7 +110,7 @@ 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] + '")');
REP('(load-file "' + process.argv[2] + '")');
process.exit(0);
}

Expand Down
13 changes: 11 additions & 2 deletions es6/step8_macros.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readline } from './node_readline';
import { Sym, _list_Q, _malfunc, _malfunc_Q, _sequential_Q } from './types';
import { Sym, _list_Q, _vector, _vector_Q, _hash_map_Q, _sequential_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';
Expand Down Expand Up @@ -44,6 +45,14 @@ const eval_ast = (ast, env) => {
return env_get(env, ast)
} else if (_list_Q(ast)) {
return ast.map((x) => EVAL(x, env));
} else if (_vector_Q(ast)) {
return _vector(...ast.map((x) => EVAL(x, env)));
} else if (_hash_map_Q(ast)) {
let new_hm = new Map();
for (let [k, v] of ast) {
new_hm.set(EVAL(k, env), EVAL(v, env));
}
return new_hm;
} else {
return ast;
}
Expand Down Expand Up @@ -128,7 +137,7 @@ REP("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first x

if (process.argv.length > 2) {
env_set(repl_env, '*ARGV*', process.argv.slice(3));
REPL('(load-file "' + process.argv[2] + '")');
REP('(load-file "' + process.argv[2] + '")');
process.exit(0);
}

Expand Down
Loading

0 comments on commit afa7931

Please sign in to comment.