[refactor] Struggling to get self-evaluating components working.

This commit is contained in:
Elf M. Sternberg 2015-07-26 14:59:49 -07:00
parent 5bba101ee2
commit 02f79c4255
2 changed files with 105 additions and 97 deletions

View File

@ -24,7 +24,7 @@ test: clean node_modules
--reporter mocha-jenkins-reporter --compilers coffee:coffee-script/register || true
ltest: node_modules
@node_modules/.bin/mocha --compilers coffee:coffee-script/register
node_modules/.bin/mocha --compilers coffee:coffee-script/register
watch:
while inotifywait $(SOURCES) ; do make test; done

View File

@ -2,19 +2,15 @@
cdar, cadr, caadr, cadar, caddr, nilp, nil, setcdr, metacadr} = require "cons-lists/lists"
readline = require "readline"
{inspect} = require "util"
{Symbol} = require "./reader_types"
class LispInterpreterError extends Error
name: 'LispInterpreterError'
constructor: (@message) ->
constructor: (@message, position = null) ->
env_init = nil
env_global = env_init
defpredicate = (name, nativ, arity) ->
defprimitive name, ((a, b) -> if nativ.call(null, a, b) then true else the_false_value), arity
definitial = (name, value = nil) ->
env_global = (cons (cons name, value), env_global)
name
@ -27,6 +23,9 @@ defprimitive = (name, nativ, arity) ->
else
throw (new LispInterpreterError "Incorrect arity"))
defpredicate = (name, nativ, arity) ->
defprimitive name, ((a, b) -> if nativ.call(null, a, b) then true else the_false_value), arity
definitial "#t", true
definitial "#f", the_false_value
definitial "nil", nil
@ -47,31 +46,6 @@ defpredicate "eq?", ((a, b) -> a == b), 2
the_false_value = (cons "false", "boolean")
cadddr = metacadr('cadddr')
metadata_evaluation =
listp: (node) -> (car node) == 'list'
symbolp: (node) -> (car node) == 'symbol'
numberp: (node) -> (car node) == 'number'
stringp: (node) -> (car node) == 'string'
nvalu: (node) -> cadr node
straight_evaluation =
listp: (node) -> node.__type == 'list'
symbolp: (node) -> node instanceOf Symbol
commentp: (node) -> node instanceOf Comment
numberp: (node) -> typeof node == 'number'
stringp: (node) -> typeof node == 'string'
boolp: (node) -> typeof node == 'boolean'
nullp: (node) -> node == null
vectorp: (node) -> (not listp node) and toString.call(node) == '[object Array]'
recordp: (node) -> (not x._prototype?) and toSTring.call(node) == '[object Object]')
objectp: (node) -> (x._prototype?) and toString.call(node) == '[object Object]')
nilp: (node) -> node == nilp
nvalu: (node) -> node
makeEvaluator = (ix = straight_evaluation) ->
(exp, env) ->
extend = (env, variables, values) ->
if (pairp variables)
if (pairp values)
@ -133,11 +107,8 @@ makeEvaluator = (ix = straight_evaluation) ->
else
nil
# This really ought to be the only place where the AST meets the
# interpreter core. I can't help but think that this design precludes
# pluggable interpreter core.
astSymbolsToLispSymbols = (node) ->
nvalu = (node) -> cadr node
return nil if nilp node
throw (new LispInterpreterError "Not a list of variable names") if not (ntype(node) is 'list')
handler = (node) ->
@ -145,18 +116,49 @@ makeEvaluator = (ix = straight_evaluation) ->
cons (nvalu car node), (handler cdr node)
handler(nvalu node)
cadddr = metacadr('cadddr')
# This is really the only thing that changes behavior between "reader
# nodes" (nodes loaded with debugging metadata) and a standard cons
# object. TODO: astSymbolsToLispSymbols should be deprecated in
# favor of normalizeForm (s?) and Symbol extraction
metadata_evaluation =
listp: (node) -> (car node) == 'list'
symbolp: (node) -> (car node) == 'symbol'
numberp: (node) -> (car node) == 'number'
stringp: (node) -> (car node) == 'string'
nvalu: (node) -> cadr node
straight_evaluation =
listp: (node) -> node.__type == 'list'
symbolp: (node) -> node instanceof Symbol
commentp: (node) -> node instanceof Comment
numberp: (node) -> typeof node == 'number'
stringp: (node) -> typeof node == 'string'
boolp: (node) -> typeof node == 'boolean'
nullp: (node) -> node == null
vectorp: (node) -> (not straight_evaluation.listp node) and toString.call(node) == '[object Array]'
recordp: (node) -> (not node._prototype?) and toSTring.call(node) == '[object Object]'
objectp: (node) -> (node._prototype?) and toString.call(node) == '[object Object]'
nilp: (node) -> nilp(node)
nvalu: (node) -> node
makeEvaluator = (ix = straight_evaluation, ty="straight") ->
(exp, env) ->
console.log("Type:", ty)
# Takes an AST node and evaluates it and its contents. A node may be
# ("list" (... contents ...)) or ("number" 42) or ("symbol" x), etc.
[type, exp] = [(ntype e), (nvalu e)]
if type == "symbol"
return lookup exp, env
else if type in ["number", "string", "boolean", "vector"]
if ix.symbolp(exp)
return lookup (ix.nvalu exp).v, env
else if ([ix.numberp, ix.stringp].filter (i) -> i(exp)).length > 0
return exp
else if type == "list"
head = car exp
if (ntype head) == 'symbol'
switch (nvalu head)
else if ix.listp(exp)
head = car ix.nvalu exp
if ix.symbolp(head)
switch (ix.nvalu head).v
when "quote" then cdr exp
when "if"
unless (evaluate (cadr exp), env) == the_false_value
@ -164,7 +166,7 @@ makeEvaluator = (ix = straight_evaluation) ->
else
evaluate (cadddr exp), env
when "begin" then eprogn (cdr exp), env
when "set!" then update (nvalu cadr exp), env, (evaluate (caddr exp), env)
when "set!" then update (ix.nvalu cadr exp).v, env, (evaluate (caddr exp), env)
when "lambda" then make_function (astSymbolsToLispSymbols cadr exp), (cddr exp), env
else invoke (evaluate (car exp), env), (evlis (cdr exp), env)
else
@ -172,4 +174,10 @@ makeEvaluator = (ix = straight_evaluation) ->
else
throw new LispInterpreterError "Can't handle a #{type}"
nodeEval = makeEvaluator(metadata_evaluation, "node")
lispEval = makeEvaluator(straight_evaluation, "lisp")
evaluate = (exp, env) ->
(if exp? and exp.__node then nodeEval else lispEval)(exp, env)
module.exports = (c) -> evaluate c, env_global