Successful conversion to lists. Yay.

This commit is contained in:
Elf M. Sternberg 2015-04-04 16:26:51 -07:00
parent 6558843e9b
commit 4089f80770
9 changed files with 50 additions and 74 deletions

View File

@ -1,7 +1,4 @@
lib/lisp_parser.js: lib/lisp_parser.peg tests:
node_modules/.bin/pegjs $< $@
tests: lib/lisp_parser.js
node_modules/.bin/coffee bin/lisp test.scm node_modules/.bin/coffee bin/lisp test.scm

View File

@ -1,17 +1,16 @@
lookup = require './lookup' lookup = require './lookup'
{car, cdr} = require './lists' {car, cdr, cadr, caadr, cdadr} = require './lists'
lispeval = (element, scope) -> lispeval = (element, scope) ->
switch element.type switch (car element)
when 'number' then parseInt(element.value, 10) when 'number' then parseInt (cadr element), 10
when 'symbol' when 'symbol' then lookup scope, (cadr element)
lookup(scope, element.value)
when 'list' when 'list'
proc = lispeval((car element.value), scope) proc = lispeval (caadr element), scope
args = (cdr element.value) args = cdadr element
proc args, scope proc args, scope
else throw new Error ("Unrecognized type in parse: #{element.type}") else throw new Error ("Unrecognized type in parse: #{(car element)}")
module.exports = lispeval module.exports = lispeval

View File

@ -4,19 +4,33 @@ lispeval = require './eval'
module.exports = module.exports =
create_vm_expression_evaluator: (defining_scope, params, body) -> create_vm_expression_evaluator: (defining_scope, params, body) ->
(cells, scope) -> (cells, scope) ->
args = listToVector(cells).map (i) -> lispeval i, scope args = (amap = (cells, accum) ->
return accum if nilp cells
amap((cdr cells), accum.concat(lispeval (car cells), scope)))(cells, [])
body.apply null, args body.apply null, args
create_lisp_expression_evaluator: (defining_scope, params, body) -> create_lisp_expression_evaluator: (defining_scope, params, body) ->
(cells, scope) -> (cells, scope) ->
# Takes the current scope, which has been passed in during the
# execution phase, and evaluate the contents of the parameters
# in the context in which this call is made (i.e. when the
# function is *called*, rather than defined.
new_scope = (cmap = (cells, params, nscope) -> new_scope = (cmap = (cells, params, nscope) ->
return nscope if (nilp cells) or (nilp params) return nscope if (nilp cells) or (nilp params)
nscope[(car params)] = lispeval (car cells), scope nscope[(car params)] = lispeval (car cells), scope
cmap((cdr cells), (cdr params), nscope))(cells, params, {}) cmap((cdr cells), (cdr params), nscope))(cells, params, {})
inner = cons(new_scope, defining_scope)
# Execute and evaluate the body, creating an inner scope that
# consists of all the bound variables (the parameters) evaluated
# in the context of the function call, and all of free variables
# evaluated in the context of the defining scope.
inner_scope = cons(new_scope, defining_scope)
(nval = (body, memo) -> (nval = (body, memo) ->
return memo if nilp body return memo if nilp body
nval((cdr body), lispeval((car body), inner)))(body) nval((cdr body), lispeval((car body), inner_scope)))(body)
create_special_form_evaluator: (defining_scope, params, body) -> create_special_form_evaluator: (defining_scope, params, body) ->
(cells, scope) -> body(cells, scope) (cells, scope) -> body(cells, scope)

View File

@ -2,16 +2,15 @@ fs = require 'fs'
{readForms} = require './reader' {readForms} = require './reader'
lispeval = require './eval' lispeval = require './eval'
scope = require './scope' scope = require './scope'
{car, cdr, nilp} = require './lists' {car, cdr, nilp, cadr} = require './lists'
module.exports = module.exports =
run: (pathname) -> run: (pathname) ->
text = fs.readFileSync(pathname, 'utf8') text = fs.readFileSync(pathname, 'utf8')
root = scope
ast = readForms(text) ast = readForms(text)
(nval = (body, memo) -> (nval = (body, memo) ->
return memo if nilp body return memo if nilp body
nval((cdr body), lispeval((car body), root)))(ast.value) nval((cdr body), lispeval((car body), scope)))(cadr ast)

View File

@ -1,34 +0,0 @@
lisp
= cell*
cell
= _* datum:datum _*
{ return datum }
datum
= list / boolean / number / symbol
list
= "(" items:cell* ")"
{ return { type: "list", value: items } }
boolean
= b:("#t" / "#f")
{ return { type: 'boolean', value: b } }
delim
= paren / _
number
= b:( [0-9]+ )
{ return { type: 'number', value: b.join("") } }
symbol
= b:(!delim c:. { return c })+
{ return { type: 'symbol', value: b.join("") } }
paren
= "(" / ")"
_
= w:[ \t\n\r]+

View File

@ -1,6 +1,8 @@
vectorp = (a) -> toString.call(a) == '[object Array]' vectorp = (a) -> toString.call(a) == '[object Array]'
listp = (a) -> vectorp(a) and a.__list == true pairp = (a) -> vectorp(a) and a.__list == true
listp = (a) -> (pairp a) and (pairp cdr a)
recordp = (a) -> Object.prototype.toString.call(a) == '[object Object]' recordp = (a) -> Object.prototype.toString.call(a) == '[object Object]'
@ -16,7 +18,7 @@ car = (a) -> a[0]
cdr = (a) -> a[1] cdr = (a) -> a[1]
nilp = (a) -> listp(a) and a.length == 0 nilp = (a) -> pairp(a) and a.length == 0
vectorToList = (v, p) -> vectorToList = (v, p) ->
p = if p? then p else 0 p = if p? then p else 0
@ -25,14 +27,17 @@ vectorToList = (v, p) ->
# have to be intercepted first. The use of duck-typing here is # have to be intercepted first. The use of duck-typing here is
# frustrating, but I suppose the eventual runtime will be doing # frustrating, but I suppose the eventual runtime will be doing
# something like this anyway for base types. # something like this anyway for base types.
item = if listp(v[p]) then v[p] else if vectorp(v[p]) then vectorToList(v[p]) else v[p] item = if pairp(v[p]) then v[p] else if vectorp(v[p]) then vectorToList(v[p]) else v[p]
cons(item, vectorToList(v, p + 1)) cons(item, vectorToList(v, p + 1))
list = (v...) -> vectorToList v list = (v...) ->
ln = v.length;
(nl = (a) ->
cons(v[a], if (a < ln) then (nl(a + 1)) else nil))(0)
listToVector = (l, v = []) -> listToVector = (l, v = []) ->
return v if nilp l return v if nilp l
v.push if listp (car l) then listToVector(car l) else (car l) v.push if pairp (car l) then listToVector(car l) else (car l)
listToVector (cdr l), v listToVector (cdr l), v
# This is the simplified version. It can't be used stock with reader, # This is the simplified version. It can't be used stock with reader,
@ -41,7 +46,7 @@ listToVector = (l, v = []) ->
listToString = (l) -> listToString = (l) ->
return "" if nilp l return "" if nilp l
if listp (car l) if pairp (car l)
"(" + (listToString(car l)).replace(/\ *$/, "") + ") " + listToString(cdr l) "(" + (listToString(car l)).replace(/\ *$/, "") + ") " + listToString(cdr l)
else else
p = if typeof (car l) == 'string' then '"' else '' p = if typeof (car l) == 'string' then '"' else ''
@ -54,7 +59,7 @@ module.exports =
cdr: cdr cdr: cdr
list: list list: list
nilp: nilp nilp: nilp
listp: listp pairp: pairp
vectorp: vectorp vectorp: vectorp
recordp: recordp recordp: recordp
vectorToList: vectorToList vectorToList: vectorToList

View File

@ -1,4 +1,4 @@
{car, cdr, cons, nil, nilp, listp} = require './lists' {car, cdr, cons, nil, nilp, pairp, vectorToList} = require './lists'
NEWLINES = ["\n", "\r", "\x0B", "\x0C"] NEWLINES = ["\n", "\r", "\x0B", "\x0C"]
WHITESPACE = [" ", "\t"].concat(NEWLINES) WHITESPACE = [" ", "\t"].concat(NEWLINES)
@ -32,10 +32,7 @@ skipWS = (inStream) ->
# (type, value, line, column) -> (node {type, value, line, column)} # (type, value, line, column) -> (node {type, value, line, column)}
makeObj = (type, value, line, column) -> makeObj = (type, value, line, column) ->
'type': type vectorToList([type, value, line, column])
'value': value
'line': line
'column': column
# msg -> (IO -> Node => Error) # msg -> (IO -> Node => Error)
handleError = (message) -> handleError = (message) ->

View File

@ -1,4 +1,4 @@
{car, cdr, cons, listp, nilp, nil, list, listToString} = require './lists' {car, cdr, cons, pairp, nilp, nil, list, listToString} = require './lists'
reduce = (lst, iteratee, memo, context) -> reduce = (lst, iteratee, memo, context) ->
count = 0 count = 0

View File

@ -1,5 +1,5 @@
lispeval = require './eval' lispeval = require './eval'
{cons, car, cdr, nilp, nil} = require './lists' {cons, car, cdr, nilp, nil, cadar, cadr, caddr} = require './lists'
{create_lisp_expression_evaluator, create_vm_expression_evaluator, create_special_form_evaluator} = require './fn' {create_lisp_expression_evaluator, create_vm_expression_evaluator, create_special_form_evaluator} = require './fn'
scope = cons scope = cons
@ -13,20 +13,19 @@ scope = cons
'define': create_special_form_evaluator scope, [], (nodes, scope) -> 'define': create_special_form_evaluator scope, [], (nodes, scope) ->
current = (car scope) current = (car scope)
current[(car nodes).value] = lispeval((car cdr nodes), scope) current[(cadar nodes)] = lispeval((cadr nodes), scope)
'lambda': create_special_form_evaluator scope, [], (nodes, scope) -> 'lambda': create_special_form_evaluator scope, [], (nodes, scope) ->
param_nodes = (car nodes).value param_nodes = cadar nodes
reducer = (l) -> reducer = (l) ->
if (nilp l) then nil else cons((car l).value, reducer(cdr l)) if (nilp l) then nil else cons (cadar l), reducer(cdr l)
param_names = reducer(param_nodes) param_names = reducer(param_nodes)
create_lisp_expression_evaluator scope, param_names, (cdr nodes)
create_lisp_expression_evaluator(scope, param_names, (cdr nodes))
'if': create_special_form_evaluator scope, [], (nodes, scope) -> 'if': create_special_form_evaluator scope, [], (nodes, scope) ->
if lispeval((car nodes), scope) if lispeval (car nodes), scope
lispeval((car cdr nodes), scope) lispeval (cadr nodes), scope
else else
lispeval((car cdr cdr nodes), scope) lispeval (caddr nodes), scope
module.exports = scope module.exports = scope