Basic arithmetic works!

This commit is contained in:
Elf M. Sternberg 2015-08-29 21:45:36 -07:00
parent e0b6b44178
commit 356d4561b2
1 changed files with 111 additions and 79 deletions

View File

@ -3,12 +3,17 @@
metacadr, setcar} = require "cons-lists/lists" metacadr, setcar} = require "cons-lists/lists"
{map} = require "cons-lists/reduce" {map} = require "cons-lists/reduce"
{length} = require "cons-lists/reduce" {length} = require "cons-lists/reduce"
{normalizeForms, normalizeForm} = require "../chapter1/astToList" {Node, Comment, Symbol} = require '../chapter5/reader_types'
{Node, Comment, Symbol} = require '../chapter1/reader_types'
{inspect} = require 'util' {inspect} = require 'util'
itap = (a) -> return inspect a, true, null, false itap = (a) -> return inspect a, true, null, false
class Value
constructor: (@v) ->
inValue = (f) ->
new Value(f)
class LispInterpreterError extends Error class LispInterpreterError extends Error
name: 'LispInterpreterError' name: 'LispInterpreterError'
constructor: (@message) -> constructor: (@message) ->
@ -40,26 +45,61 @@ consp = (e) ->
convert = (exp, store) -> convert = (exp, store) ->
conv = (e) -> conv = (e) ->
if consp e if consp e
cons (conv content.v, (store (car e))), (conv content.v, (store (cadr e))) cons (conv (store (car e)).v), (conv (store (cadr e)).v)
else else
e e
conv (content.v e) conv exp.v
# 5.2.4
# f[y z] = λx . if y = x then z else f(x) endif
#
# Accepts a parent function, and ID and a value. Returns a function
# that takes a request ID. If the request ID equals the ID above,
# return the value, else call the parent function with the request
# ID.
#
# Calls allocate
extend = (next, id, value) ->
(x) -> if (eq x, id) then value else (next x)
# f[y* z*] = if #y>0 then f[y*1 z*1][y*1 z*1] else f endif
#
# Helper. Builds a stack of extend() functions, at tail of which it
# appends the parent function.
#
#
lextends = (fn, ids, values) ->
if (pairp pts)
extend (@lextends fn, (cdr pts), (cdr ims)), (car pts), (car ims)
else
fn
translate = (exp, store, qont) -> translate = (exp, store, qont) ->
if (pairp exp) if (pairp exp)
translate (car exp), store, (val1, store1) -> translate (car exp), store, (val1, store1) ->
translate (cdr exp), store1, (val2, store2) -> translate (cdr exp), store1, (val2, store2) ->
allocate store2, 2, (store, addrs) ->
qont (inValue addrs), (extend (extend store, (car addrs), val1), (cadr addrs), val2)
else
qont (inValue exp), store
# Allocate is a function that takes a store, a number of addresses to
# allocate within that store, and a continuation; at the end, it calls
# the continuation with the store object and the new addresses.
allocate = (-> allocate = (->
loc = 0 loc = 0
(store, num, qont) -> (store, num, qont) ->
addrs = cons() aloop = (n, a) ->
n = num if (n > 0)
until n <= 0 loc = loc - 1
loc = loc + 1 aloop (n - 1), (cons loc, a)
n = n - 1 else
cons loc, addrs qont store, a
qont store, addrs)() aloop(num, cons()))()
sBehavior = new Symbol 'behavior' sBehavior = new Symbol 'behavior'
sBoolean = new Symbol 'boolean' sBoolean = new Symbol 'boolean'
@ -67,11 +107,17 @@ sBoolify = new Symbol 'boolify'
sFunction = new Symbol 'function' sFunction = new Symbol 'function'
sSymbol = new Symbol 'symbol' sSymbol = new Symbol 'symbol'
sString = new Symbol 'string' sString = new Symbol 'string'
sValue = new Symbol 'chars' sQuote = new Symbol 'quote'
sLambda = new Symbol 'lambda'
sIf = new Symbol 'if'
sValue = new Symbol 'value'
sChars = new Symbol 'chars'
sBegin = new Symbol 'begin'
sName = new Symbol 'name' sName = new Symbol 'name'
sNumber = new Symbol 'number' sNumber = new Symbol 'number'
sNull = new Symbol 'null' sNull = new Symbol 'null'
sTag = new Symbol 'tag' sTag = new Symbol 'tag'
sSet = new Symbol 'set'
sType = new Symbol 'type' sType = new Symbol 'type'
sValue = new Symbol 'value' sValue = new Symbol 'value'
sPair = new Symbol 'pair' sPair = new Symbol 'pair'
@ -80,24 +126,20 @@ sCdr = new Symbol 'cdr'
sSetCar = new Symbol 'setcar' sSetCar = new Symbol 'setcar'
sSetCdr = new Symbol 'setcdr' sSetCdr = new Symbol 'setcdr'
class Value
constructor: (@content) ->
inValue = (f) ->
new Value(f)
ValueToFunction = (e) -> ValueToFunction = (e) ->
c = e.content c = e.v
if (typeof c == 'function') then c else throw new LispInterpreterError("Not a function: " + Object.toString(c)) if (typeof c == 'function') then c else throw new LispInterpreterError("Not a function: " + Object.toString(c))
ValueToPair = (e) -> ValueToPair = (e) ->
c = e.content c = e.v
if pairp c then c else throw new LispInterpreterError("Not a pair: " + Object.toString(c)) if pairp c then c else throw new LispInterpreterError("Not a pair: " + Object.toString(c))
ValueToNumber = (e) -> ValueToNumber = (e) ->
c = e.content c = parseInt(e.v, 10)
if (typeof c == 'number') then c else throw new LispInterpreterError("Not a number: " + Object.toString(c)) if (typeof c == 'number') then c else throw new LispInterpreterError("Not a number: " + Object.toString(c))
store_init = (a) -> throw new LispInterpreterError "No such address"
env_init = (a) -> throw new LispInterpreterError "No such variable"
class Interpreter class Interpreter
constructor: -> constructor: ->
@ -109,7 +151,7 @@ class Interpreter
@definitial "cons", inValue arity_check "cons", 2, (values, kont, store) => @definitial "cons", inValue arity_check "cons", 2, (values, kont, store) =>
allocate store, 2, (store, addrs) => allocate store, 2, (store, addrs) =>
kont (inValue (cons (car addr), (cadr addr))), (@extends store, addrs, values) kont (inValue (cons (car addr), (cadr addr))), (@lextends store, addrs, values)
@definitial "car", inValue arity_check "car", 1, (values, kont, store) => @definitial "car", inValue arity_check "car", 1, (values, kont, store) =>
kont (store car @valueToPair (car values)), store kont (store car @valueToPair (car values)), store
@ -117,15 +159,15 @@ class Interpreter
@definitial "cdr", inValue arity_check "car", 1, (values, kont, store) => @definitial "cdr", inValue arity_check "car", 1, (values, kont, store) =>
kont (store cadr @valueToPair (car values)), store kont (store cadr @valueToPair (car values)), store
@defprimitive "pair?", ((v) -> inValue (consp v.content)), 1 @defprimitive "pair?", ((v) -> inValue (consp v.v)), 1
@defprimitive "eq?", ((v1, v2) -> inValue (eq v1.content, v2.content)), 2 @defprimitive "eq?", ((v1, v2) -> inValue (eq v1.v, v2.v)), 2
@defprimitive "symbol?", ((v) -> inValue (symbolp v.content)), 1 @defprimitive "symbol?", ((v) -> inValue (symbolp v.v)), 1
@definitial "set-car!", inValue arity_check, "set-car!", 2, (values, kont, store) => @definitial "set-car!", inValue arity_check, "set-car!", 2, (values, kont, store) ->
kont (car values), (@extend store, (car (ValueToPair (car values))), (cadr values)) kont (car values), (extend store, (car (ValueToPair (car values))), (cadr values))
@definitial "set-cdr!", inValue arity_check, "set-cdr!", 2, (values, kont, store) => @definitial "set-cdr!", inValue arity_check, "set-cdr!", 2, (values, kont, store) ->
kont (car values), (@extend store, (cadr (ValueToPair (car values))), (cadr values)) kont (car values), (extend store, (cadr (ValueToPair (car values))), (cadr values))
@defarithmetic "+", ((x, y) -> x + y), 2 @defarithmetic "+", ((x, y) -> x + y), 2
@defarithmetic "-", ((x, y) -> x - y), 2 @defarithmetic "-", ((x, y) -> x - y), 2
@ -140,7 +182,7 @@ class Interpreter
@definitial "apply", arity_check "apply", 2, inValue (values, kont, store) -> @definitial "apply", arity_check "apply", 2, inValue (values, kont, store) ->
flat = (v) -> flat = (v) ->
if pairp v.content if pairp v.v
cons (store (car (ValueToPair v))), (flat (store (cadr (ValueToPair v)))) cons (store (car (ValueToPair v))), (flat (store (cadr (ValueToPair v))))
else else
cons() cons()
@ -171,12 +213,11 @@ class Interpreter
@definitial "visit", null @definitial "visit", null
@definitial "length", null @definitial "length", null
@definitial "primes", null @definitial "primes", null
@loc = 0
loc: 0 # For allocate
listp: (cell) -> cell.__type == 'list' listp: (cell) -> cell.__type == 'list'
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"] atomp: (cell) -> not (cell.__type?) or (not cell.__type == 'list')
symbolp: (cell) -> cell instanceof Symbol
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";" commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
numberp: (cell) -> typeof cell == 'number' numberp: (cell) -> typeof cell == 'number'
stringp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == "\"" stringp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == "\""
@ -190,20 +231,19 @@ class Interpreter
mksymbols: (cell) -> cell mksymbols: (cell) -> cell
meaning: (e) -> meaning: (e) ->
meaningTable =
"'": ((e) => @meaningQuotation (cadr e))
'lambda': ((e) => @meaningAbstraction (cadr e), (cddr e))
'if': ((e) => @meaningAlternative (cadr e), (caddr e), (cadddr e))
'begin': ((e) => @meaningSequence (cdr e))
'set!': ((e) => @meaningAssignment (cadr e), (caddr e))
meaningTable = [ if (@atomp e)
[sQuote, ((e) => @meaningQuotation (cadr e))] if (@symbolp e) then (@meaningReference e.name) else (@meaningQuotation e)
[sLambda, ((e) => @meaningAbstraction (cadr e), (cddr e))] else if meaningTable[(car e)]?
[sIf, ((e) => @meaningAlternative (cadr e), (caddr e), (cadddr e))] meaningTable[(car e)](e)
[sBegin, ((e) => @meaningSequence (cdr e))]
[sSet, ((e) => @meaningAssignment (cadr e), (caddr e))]
]
if @atomp e
if @symbolp e then (@meaningReference e) else (@meaningQuotation e)
else else
found = (form[1] for form in forms when form[0](e)) @meaningApplication (car e), (cdr e)
if found.length == 1 then found[0](e) else @meaningApplication (car e), (cdr e)
meaningQuotation: (val) -> meaningQuotation: (val) ->
(env, kont, store) -> (env, kont, store) ->
@ -244,23 +284,23 @@ class Interpreter
if not (eq (length vals), (length names)) if not (eq (length vals), (length names))
throw new LispInterpreterError("Incorrect Arity.") throw new LispInterpreterError("Incorrect Arity.")
functostore = (store2, addrs) => functostore = (store2, addrs) =>
(@meaningsSequence exps) (@extends env, names, addrs), kont1, (@extends store2, addrs, vals) (@meaningsSequence exps) (@lextends env, names, addrs), kont1, (@lextends store2, addrs, vals)
allocate store1, (length names), functostore allocate store1, (length names), functostore
kont inValue, funcrep kont inValue, funcrep
meaningVariable: (name) -> meaningVariable: (name) ->
(m) => (m) ->
(vals, env, kont, store) => (vals, env, kont, store) ->
allocate store, 1, (store, addrs) => allocate store, 1, (store, addrs) ->
addr = (car addrs) addr = (car addrs)
m (cdr vals), (@extend env, names, addr), kont, (@extend store, addr, (car vals)) m (cdr vals), (extend env, names, addr), kont, (extend store, addr, (car vals))
meaningApplication: (exp, exps) -> meaningApplication: (exp, exps) ->
(env, kont, store) => (env, kont, store) =>
hkont = (func, store1) => hkont = (func, store1) =>
kont2 = (values, store2) -> kont2 = (values, store2) ->
(ValueToFunction func) values, kont, store2 (ValueToFunction func) values, kont, store2
(@meaning exps) env, kont2, store1 (@meanings exps) env, kont2, store1
(@meaning exp) env, hkont, store (@meaning exp) env, hkont, store
meaningSequence: (exps) -> meaningSequence: (exps) ->
@ -285,52 +325,44 @@ class Interpreter
meanings: (exps) => meanings: (exps) =>
meaningSomeArguments = (exp, exps) => meaningSomeArguments = (exp, exps) =>
(env, kont, store) => (env, kont, store) =>
hkont = (value, store1) -> hkont = (value, store1) =>
hkont2 = (values, store2) -> hkont2 = (values, store2) ->
kont (cons value, values), store2 kont (cons value, values), store2
(@meanings exps) env, khont2, store1 (@meanings exps) env, hkont2, store1
(@meaning exp) env, hkont, store (@meaning exp) env, hkont, store
meaningNoArguments = (env, kont, store) -> (k (cons()), store) meaningNoArguments = ->
(env, kont, store) ->
kont (cons()), store
if pairp exps if pairp exps
meaningSomeArguments (car exps), (cdr exps) meaningSomeArguments (car exps), (cdr exps)
else else
meaningNoArgument() meaningNoArguments()
extend: (fn, pt, im) ->
(x) -> if (eq pt, x) then im else (fn x)
extends: (fn, pts, ims) ->
if (pairp pts)
@extend (@extends fn, (cdr pts), (cdr ims)), (car pts), (car ims)
else
fn
store_init: (a) -> throw new LispInterpreterError "No such address"
env_init: (a) -> throw new LispInterpreterError "No such variable"
definitial: (name, value) -> definitial: (name, value) ->
allocate @store_init, 1, (store, addrs) => allocate store_init, 1, (store, addrs) ->
@env_init = @extend @env_init, name, (car addrs) env_init = extend env_init, name, (car addrs)
@store_init = @extend store, (car addrs), value store_init = extend store, (car addrs), value
name
defprimitive: (name, value, arity) -> defprimitive: (name, value, arity) ->
callable = (values, kont, store) => callable = (values, kont, store) =>
if not eq(arity, (length values)) if not eq arity, (length values)
throw new LispInterpreterError "Incorrect Arity for #{name}" throw new LispInterpreterError "Incorrect Arity for #{name}"
kont (inValue (value.apply(@, [ValueToNumber(v) for v in values]))), store kont (inValue (value.apply(null, listToVector(values)))), store
@definitial name, (inValue callable) @definitial name, (inValue callable)
defarithmetic: (name, value, arity) -> defarithmetic: (name, value, arity) ->
callable = (values, kont, store) -> callable = (values, kont, store) ->
if not eq arity, (length values) if not eq arity, (length values)
throw new LispInterpreterError "Incorrect Arity for #{name}" throw new LispInterpreterError "Incorrect Arity for #{name}"
kont (inValue (apply value, (map ValueToIngeter, values))), store kont (inValue (value.apply(null, listToVector(map values, ValueToNumber)))), store
(@defprimitive name, value, arity) (name), inValue callable @definitial name, (inValue callable)
module.exports = (ast, kont) -> module.exports = (ast, kont) ->
interpreter = new Interpreter() interpreter = new Interpreter()
(meaning ast) @interpreter.env_init, (value, store_final) -> store_current = store_init
kont (convert value, store_final) (interpreter.meaning ast)(env_init,
((value, store_final) -> kont (convert value, store_final)), store_current)