[feat] The interpreter works and all the tests run without crashing.
This commit is contained in:
parent
edf8cd2c3c
commit
38fa5ae125
|
@ -138,7 +138,7 @@ metadata_evaluation =
|
||||||
nvalu: (node) -> node.value
|
nvalu: (node) -> node.value
|
||||||
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
||||||
|
|
||||||
straight_evaluation =
|
straight_evaluation =
|
||||||
listp: (cell) -> cell.__type == 'list'
|
listp: (cell) -> cell.__type == 'list'
|
||||||
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
||||||
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{car, cdr, listp} = require 'cons-lists/lists'
|
{car, cdr, listp} = require 'cons-lists/lists'
|
||||||
{Node, Symbol} = require "./reader_types"
|
{Node, Symbol} = require "./reader_types"
|
||||||
|
|
||||||
module.exports = ops =
|
module.exports = ops =
|
||||||
astObject: (form) -> form instanceof Node
|
astObject: (form) -> form instanceof Node
|
||||||
aValue: (form) -> form.value
|
aValue: (form) -> form.value
|
||||||
aSymbol: (form) -> form.value
|
aSymbol: (form) -> form.value
|
||||||
|
|
|
@ -133,7 +133,7 @@ metadata_evaluation =
|
||||||
nvalu: (node) -> node.value
|
nvalu: (node) -> node.value
|
||||||
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
||||||
|
|
||||||
straight_evaluation =
|
straight_evaluation =
|
||||||
listp: (cell) -> cell.__type == 'list'
|
listp: (cell) -> cell.__type == 'list'
|
||||||
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
||||||
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
||||||
|
|
|
@ -317,8 +317,8 @@ defprimitive = (name, nativ, arity) ->
|
||||||
vmargs = listToVector(args)
|
vmargs = listToVector(args)
|
||||||
if (vmargs.length == arity)
|
if (vmargs.length == arity)
|
||||||
kont.resume (nativ.apply null, vmargs)
|
kont.resume (nativ.apply null, vmargs)
|
||||||
else
|
else
|
||||||
throw new LispInterpreterError "Incorrect arity"
|
throw new LispInterpreterError "Incorrect arity"
|
||||||
|
|
||||||
defpredicate = (name, nativ, arity) ->
|
defpredicate = (name, nativ, arity) ->
|
||||||
defprimitive name, ((a, b) -> if nativ.call(null, a, b) then true else the_false_value), arity
|
defprimitive name, ((a, b) -> if nativ.call(null, a, b) then true else the_false_value), arity
|
||||||
|
@ -367,10 +367,10 @@ definitial "apply", new Primitive "apply", (values, env, kont) ->
|
||||||
f.invoke args, env, kont
|
f.invoke args, env, kont
|
||||||
|
|
||||||
definitial "funcall", new Primitive "funcall", (args, env, kont) ->
|
definitial "funcall", new Primitive "funcall", (args, env, kont) ->
|
||||||
if not nilp cdr args
|
if not nilp cdr args
|
||||||
@kont.invoke (car args), (cdr args)
|
@kont.invoke (car args), (cdr args)
|
||||||
else
|
else
|
||||||
throw new LispInterpreterError "Invoke requires a function name and arguments"
|
throw new LispInterpreterError "Invoke requires a function name and arguments"
|
||||||
|
|
||||||
definitial "list", new Primitive "list", (values, env, kont) ->
|
definitial "list", new Primitive "list", (values, env, kont) ->
|
||||||
(values, env, kont) -> kont.resume(values)
|
(values, env, kont) -> kont.resume(values)
|
||||||
|
@ -394,7 +394,7 @@ metadata_evaluation =
|
||||||
nvalu: (node) -> node.value
|
nvalu: (node) -> node.value
|
||||||
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
mksymbols: (list) -> astSymbolsToLispSymbols(list)
|
||||||
|
|
||||||
straight_evaluation =
|
straight_evaluation =
|
||||||
listp: (cell) -> cell.__type == 'list'
|
listp: (cell) -> cell.__type == 'list'
|
||||||
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
symbolp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] not in ["\"", ";"]
|
||||||
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
commentp: (cell) -> typeof cell == 'string' and cell.length > 0 and cell[0] == ";"
|
||||||
|
|
|
@ -32,9 +32,11 @@ class Continuation
|
||||||
else
|
else
|
||||||
throw new LispInterpreterError "Continuations expect one argument"
|
throw new LispInterpreterError "Continuations expect one argument"
|
||||||
unwind: (value, ktarget) ->
|
unwind: (value, ktarget) ->
|
||||||
if (@kont == ktarget) then (@kont.resume value) else (@kont.unwind value, ktarget)
|
if (@ == ktarget) then (@kont.resume value) else (@kont.unwind value, ktarget)
|
||||||
catchLookup: (tag, kk) ->
|
catchLookup: (tag, kk) ->
|
||||||
@kont.catchLookup tag, kk
|
@kont.catchLookup tag, kk
|
||||||
|
resume: (value) ->
|
||||||
|
throw new LispInterpreterError "Wrong continuation for #{@_type}"
|
||||||
|
|
||||||
# Abstract class representing the environment
|
# Abstract class representing the environment
|
||||||
|
|
||||||
|
@ -258,6 +260,7 @@ evaluateReturnFrom = (label, form, env, kont) ->
|
||||||
|
|
||||||
class ReturnFromCont extends Continuation
|
class ReturnFromCont extends Continuation
|
||||||
constructor: (@kont, @env, @label) ->
|
constructor: (@kont, @env, @label) ->
|
||||||
|
@_type = "ReturnFromCont"
|
||||||
resume: (v) ->
|
resume: (v) ->
|
||||||
@env.blockLookup @label, @kont, v
|
@env.blockLookup @label, @kont, v
|
||||||
|
|
||||||
|
@ -266,11 +269,13 @@ evaluateCatch = (tag, body, env, kont) ->
|
||||||
|
|
||||||
class CatchCont extends Continuation
|
class CatchCont extends Continuation
|
||||||
constructor: (@kont, @body, @env) ->
|
constructor: (@kont, @body, @env) ->
|
||||||
|
@_type = "CatchFromCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
evaluateBegin @body, @env, (new LabeledCont @kont, value)
|
evaluateBegin @body, @env, (new LabeledCont @kont, value)
|
||||||
|
|
||||||
class LabeledCont extends Continuation
|
class LabeledCont extends Continuation
|
||||||
constructor: (@kont, @tag) ->
|
constructor: (@kont, @tag) ->
|
||||||
|
@_type = "LabeledFromCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
@kont.resume value
|
@kont.resume value
|
||||||
catchLookup: (tag, kk) ->
|
catchLookup: (tag, kk) ->
|
||||||
|
@ -281,6 +286,7 @@ class LabeledCont extends Continuation
|
||||||
|
|
||||||
class ThrowCont extends Continuation
|
class ThrowCont extends Continuation
|
||||||
constructor: (@kont, @form, @env) ->
|
constructor: (@kont, @form, @env) ->
|
||||||
|
@_type = "ThrowCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
@catchLookup value, @
|
@catchLookup value, @
|
||||||
|
|
||||||
|
@ -289,17 +295,21 @@ evaluateThrow = (tag, form, env, kont) ->
|
||||||
|
|
||||||
class UnwindCont extends Continuation
|
class UnwindCont extends Continuation
|
||||||
constructor: (@kont, @value, @target) ->
|
constructor: (@kont, @value, @target) ->
|
||||||
|
resume: (value) ->
|
||||||
|
@kont.unwind @value, @target
|
||||||
|
|
||||||
class ThrowingCont extends Continuation
|
class ThrowingCont extends Continuation
|
||||||
constructor: (@kont, @tag, @resumecont) ->
|
constructor: (@kont, @tag, @resumecont) ->
|
||||||
|
@_type = "ThrowingCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
@kont.unwind @resumecont.resume value
|
@kont.unwind value, @resumecont
|
||||||
|
|
||||||
evaluateUnwindProtect = (form, cleanup, env, kont) ->
|
evaluateUnwindProtect = (form, cleanup, env, kont) ->
|
||||||
evaluate form, env, (new UnwindProtectCont kont, cleanup, env)
|
evaluate form, env, (new UnwindProtectCont kont, cleanup, env)
|
||||||
|
|
||||||
class UnwindProtectCont extends Continuation
|
class UnwindProtectCont extends Continuation
|
||||||
constructor: (@kont, @cleanup, @env) ->
|
constructor: (@kont, @cleanup, @env) ->
|
||||||
|
@_type = "UnwindProtectCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
evaluateBegin @cleanup, @env, (new ProtectReturnCont @kont, value)
|
evaluateBegin @cleanup, @env, (new ProtectReturnCont @kont, value)
|
||||||
unwind: (value, target) ->
|
unwind: (value, target) ->
|
||||||
|
@ -307,6 +317,7 @@ class UnwindProtectCont extends Continuation
|
||||||
|
|
||||||
class ProtectReturnCont extends Continuation
|
class ProtectReturnCont extends Continuation
|
||||||
constructor: (@kont, @value) ->
|
constructor: (@kont, @value) ->
|
||||||
|
@_type = "ProtectReturnCont"
|
||||||
resume: (value) ->
|
resume: (value) ->
|
||||||
@kont.resume @value
|
@kont.resume @value
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
olisp = require '../chapter3/interpreter'
|
olisp = require '../chapter3g/interpreter'
|
||||||
{read, readForms} = require '../chapter1/reader'
|
{read, readForms} = require '../chapter1/reader'
|
||||||
{inspect} = require 'util'
|
{inspect} = require 'util'
|
||||||
|
|
||||||
|
@ -9,6 +9,4 @@ lisp = (ast) ->
|
||||||
|
|
||||||
# console.log lisp read "(catch 2 (* 7 (catch 1 (* 3 (catch 2 (throw 1 (throw 2 5)) )) )))"
|
# console.log lisp read "(catch 2 (* 7 (catch 1 (* 3 (catch 2 (throw 1 (throw 2 5)) )) )))"
|
||||||
|
|
||||||
console.log lisp read "(catch foo (throw foo 33))"
|
console.log lisp read "((lambda (c) (catch 111 (* 2 (unwind-protect (* 3 (throw 111 5)) (set! c 1) ))) ) 0)"
|
||||||
console.log lisp read "(catch 'bar (throw 'bar 3))"
|
|
||||||
console.log lisp read "(catch 1 (throw 1 7))"
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
This doesn't really look like the read/analyze/compile passes that one
|
||||||
|
expects of a modern Lisp.
|
||||||
|
|
||||||
|
Reading converts the source code into a list of immutable values in the
|
||||||
|
low-level AST of the system. Reading and analysis must be combined if
|
||||||
|
there are to be reader macros (which I want to support).
|
||||||
|
|
||||||
|
... and then a miracle occurs ...
|
||||||
|
|
||||||
|
Compilation is the process of turning the AST into javascript.
|
||||||
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
chai = require 'chai'
|
|
||||||
chai.should()
|
|
||||||
expect = chai.expect
|
|
||||||
|
|
||||||
{cons} = require "cons-lists/lists"
|
|
||||||
olisp = require '../chapter3g/interpreter'
|
|
||||||
{read, readForms} = require '../chapter1/reader'
|
|
||||||
|
|
||||||
the_false_value = (cons "false", "boolean")
|
|
||||||
|
|
||||||
lisp = (ast) ->
|
|
||||||
ret = undefined
|
|
||||||
olisp ast, (i) -> ret = i
|
|
||||||
return ret
|
|
||||||
|
|
||||||
describe "Core interpreter #3: Unwind-Protect", ->
|
|
||||||
it "protects the value correctly", ->
|
|
||||||
expect(lisp read "(unwind-protect 1 2").to.equal(1)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (c) (unwind-protect 1 (set! c 2)) c ) 0 ").to.equal(2)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (c) (catch 111 (* 2 (unwind-protect (* 3 (throw 111 5)) (set! c 1) ))) ) 0 ").to.equal(5)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (c) (catch 111 (* 2 (unwind-protect (* 3 (throw 111 5)) (set! c 1) ))) c ) 0 ").to.equal(1)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (c) (block A (* 2 (unwind-protect (* 3 (return-from A 5)) (set! c 1) ))) ) 0 ").to.equal(5)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (c) (block A (* 2 (unwind-protect (* 3 (return-from A 5)) (set! c 1) ))) c ) 0 ").to.equal(1)
|
|
||||||
|
|
||||||
|
|
||||||
describe "Core interpreter #3: Try/Catch with Throw as a function", ->
|
|
||||||
contain = (fcall) ->
|
|
||||||
return "(begin ((lambda () (begin (set! funcall (lambda (g . args) (apply g args))) #{fcall}))))"
|
|
||||||
|
|
||||||
it "", ->
|
|
||||||
expect(-> lisp read "(funcall throw 'bar 33").to.throw("bar")
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "(catch 'bar (funcall throw 'bar 11))").to.equal(11)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "(catch 'bar (* 2 (funcall throw 'bar 5)))").to.equal(5)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (f) (catch 'bar (* 2 (f 5))) ) (lambda (x) (funcall throw 'bar x))) ").to.equal(5)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "((lambda (f) (catch 'bar (* 2 (catch 'bar (* 3 (f 5))))) ) (lambda (x) (funcall throw 'bar x)))) ").to.equal(10)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "(catch 2 (* 7 (catch 1 (* 3 (catch 2 (funcall throw 1 (funcall throw 2 5)) )) ))) ").to.equal(105)
|
|
||||||
it "", ->
|
|
||||||
expect(lisp read "(catch 2 (* 7 (funcall throw 1 (funcall throw 2 3))))").to.equal(3)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
chai = require 'chai'
|
||||||
|
chai.should()
|
||||||
|
expect = chai.expect
|
||||||
|
|
||||||
|
{cons} = require "cons-lists/lists"
|
||||||
|
olisp = require '../chapter3g/interpreter'
|
||||||
|
{read, readForms} = require '../chapter1/reader'
|
||||||
|
|
||||||
|
the_false_value = (cons "false", "boolean")
|
||||||
|
|
||||||
|
lisp = (ast) ->
|
||||||
|
ret = undefined
|
||||||
|
olisp ast, (i) -> ret = i
|
||||||
|
return ret
|
||||||
|
|
||||||
|
describe "Core interpreter #3: Unwind-Protect", ->
|
||||||
|
it "unwinds but returns the value of the form", ->
|
||||||
|
expect(lisp read "(unwind-protect 1 2").to.equal(1)
|
||||||
|
it "unwinds within an iffe to correctly evaluate the side-effect", ->
|
||||||
|
expect(lisp read "((lambda (c) (unwind-protect 1 (set! c 2)) c ) 0 ").to.equal(2)
|
||||||
|
it "Unwinds inside an unevaluated definition", ->
|
||||||
|
expect(lisp read "((lambda (c) (catch 111 (* 2 (unwind-protect (* 3 (throw 111 5)) (set! c 1) ))) ) 0)").to.equal(5)
|
||||||
|
it "Unwinds inside the evaluated definition, triggering the side effect", ->
|
||||||
|
expect(lisp read "((lambda (c) (catch 111 (* 2 (unwind-protect (* 3 (throw 111 5)) (set! c 1) ))) c ) 0)").to.equal(1)
|
||||||
|
it "Same story, using block/return", ->
|
||||||
|
expect(lisp read "((lambda (c) (block A (* 2 (unwind-protect (* 3 (return-from A 5)) (set! c 1) ))) ) 0)").to.equal(5)
|
||||||
|
it "Same story, using block/return with a triggered side-effect", ->
|
||||||
|
expect(lisp read "((lambda (c) (block A (* 2 (unwind-protect (* 3 (return-from A 5)) (set! c 1) ))) c ) 0)").to.equal(1)
|
||||||
|
|
||||||
|
|
||||||
|
#describe "Core interpreter #3: Try/Catch with Throw as a function", ->
|
||||||
|
# contain = (fcall) ->
|
||||||
|
# return "(begin ((lambda () (begin (set! funcall (lambda (g . args) (apply g args))) #{fcall}))))"
|
||||||
|
#
|
||||||
|
# it "", ->
|
||||||
|
# expect(-> lisp read "(funcall throw 'bar 33").to.throw("bar")
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "(catch 'bar (funcall throw 'bar 11))").to.equal(11)
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "(catch 'bar (* 2 (funcall throw 'bar 5)))").to.equal(5)
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "((lambda (f) (catch 'bar (* 2 (f 5))) ) (lambda (x) (funcall throw 'bar x))) ").to.equal(5)
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "((lambda (f) (catch 'bar (* 2 (catch 'bar (* 3 (f 5))))) ) (lambda (x) (funcall throw 'bar x)))) ").to.equal(10)
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "(catch 2 (* 7 (catch 1 (* 3 (catch 2 (funcall throw 1 (funcall throw 2 5)) )) ))) ").to.equal(105)
|
||||||
|
# it "", ->
|
||||||
|
# expect(lisp read "(catch 2 (* 7 (funcall throw 1 (funcall throw 2 3))))").to.equal(3)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue