[e-lang] A simple interpreter for Kernel-E
Kevin Reid
kpreid at attglobal.net
Sun Feb 12 13:41:34 EST 2006
This is a mostly-correct mostly-absorptive interpreter for Kernel-E
(except for ObjectExprs).
I wrote it as an experiment to see if using it to execute the
expressions in Updoc scripts in E-on-CL would be more efficient than
compiling each expression. (It isn't, at the moment, but I intend to
add ObjectExpr and see whether that's an improvement.)
It would require some changes to run on E-on-Java, due to lack of
methods for manipulating Scopes directly.
# Copyright 2006 Kevin Reid, under the terms of the MIT X license
# found at http://www.opensource.org/licenses/mit-
license.html ................
pragma.enable("easy-return")
pragma.disable("explicit-result-guard")
pragma.enable("accumulator")
def Nothing implements DeepFrozen {
to coerce(_, optEjector) {
throw.eject(optEjector, "Nothing")
}
}
def makeInterpreter implements DeepFrozen {
to run() {
def interpreter
def InterpMatches(pattern, &scope) {
return def matcher {
to coerce(specimen, optEjector) {
interpreter."match"(pattern, &scope, specimen, optEjector)
return specimen
}
}
}
bind interpreter {
# XXX if this is a privileged interpreter (can approve audits)
then the scope must be an ungimmicked var slot or the user could make
misbehaving objects
# separate issue: a maximally POLA interpreter would use
dynamic-extent scope passing so that side effects can't happen at
surprising later times
to run(expr, &scope) {
def hide() {
return __makeVarSlot(scope.nestOuter())
}
return expr.welcome(def visitor {
to visitAssignExpr(_, left, right) {
def value := interpreter(right, &scope)
scope.getSlot(left.getName()).setValue(value)
return value
}
to visitCallExpr(_, recipient, verb, args) {
# XXX replace inefficient with-chain with a presized list
# xxx it would be nice to avoid allocating the arglist here
return E.call(interpreter(recipient, &scope),
verb,
accum [] for argExpr in args {
_.with(interpreter(argExpr, &scope)) })
}
to visitCatchExpr(_, body, catchPatt, catchExpr) {
try {
return interpreter(body, hide())
} catch p :InterpMatches(catchPatt, def &catchScope :=
hide()) {
return interpreter(catchExpr, &catchScope)
}
}
to visitEscapeExpr(_, ejPattern, body, catchPattern,
catchExpr) {
def &catchScope := hide()
return escape ejector {
def name := ejPattern.getOptPrincipalNoun()
# This renaming is a mess.
def renamedEjector {
to __printOn(out :TextWriter) {
out.write("<")
out.print(name)
out.write(" ejector>")
}
to run() { renamedEjector.run(null) }
to run(value) {
try {
ejector(value)
} catch _ { # only possible cause of failure
# XXX typed exception
throw(`ejector ${E.toQuote(name)} no longer in
scope`)
}
}
}
def &tryScope := hide()
interpreter."match"(ejPattern, &tryScope,
renamedEjector, null)
interpreter(body, &tryScope)
} catch escapeValue :if (catchPattern != null)
{InterpMatches(catchPattern, &catchScope)} else {Nothing} {
interpreter(catchExpr, &catchScope)
}
}
to visitDefineExpr(_, left, right, ejectorExpr) {
# XXX left-does-not-refer-to-right check
def value := interpreter(right, &scope)
def ejector := if (ejectorExpr != null) {
interpreter(ejectorExpr, &scope)
}
interpreter."match"(left, &scope, value, ejector)
return value
}
to visitFinallyExpr(_, body, unwinder) {
return try {
interpreter(body, hide())
} finally {
interpreter(unwinder, hide())
}
}
to visitHideExpr(_, sub) {
return interpreter(sub, hide())
}
to visitIfExpr(_, test, thenExpr, elseExpr) {
def &trueScope := hide()
return if (interpreter(test, &trueScope)) {
interpreter(thenExpr, &trueScope)
} else {
interpreter(elseExpr, hide())
}
}
to visitLiteralExpr(_, value) {
return value
}
to visitMatchBindExpr(_, specimenExpr, pattern) {
def specimen := interpreter(specimenExpr, &scope)
escape fail {
interpreter."match"(pattern, &scope, specimen, fail)
return true
} catch patternFailure {
def smashed := Ref.broken(patternFailure)
for name => _ in pattern.staticScope().varNames() {
scope := scope.withSlot(name, smashed)
}
for name => _ in pattern.staticScope().defNames() {
scope := scope.with(name, smashed)
}
return false
}
}
to visitMetaContextExpr(_) {
def prefix :String := scope.getFQNPrefix()
return def metaContext implements DeepFrozen {
to __printOn(out :TextWriter) {
out.print("<static context>")
}
to getFQNPrefix() { return prefix }
}
}
to visitMetaStateExpr(_) {
# XXX variously wrong
return scope
}
to visitNounExpr(_, noun) {
return scope.fetch(noun, thunk { throw(`undefined
variable: $noun`) })
}
to visitObjectExpr(objectExpr, _, _, _, _) {
# punt
def [value, newScope] := objectExpr.evalToPair(scope)
scope := newScope
return value
}
to visitSeqExpr(_, subs :List) {
var lastValue := "can't happen"
require(subs.size().aboveZero(), thunk {`SeqExpr with no
subexpressions`})
for sub in subs {
lastValue := interpreter(sub, &scope)
}
return lastValue
}
to visitSlotExpr(_, nounExpr) {
def noun := nounExpr.getName()
return if (scope.maps(noun)) {
scope.getSlot(noun)
} else {
throw(`undefined variable: $noun`)
}
}
})
}
to "match"(pattern, &scope, specimen, ejector) {
return pattern.welcome(def visitor {
to visitCdrPattern(_, listPattern, restPattern) {
def list := List.coerce(specimen, ejector)
def subs := listPattern.getSubs()
if ((def ss := subs.size()) > (def ls := list.size())) {
throw.eject(ejector, `a $ls size list doesn't match a
>= $ss size list pattern`)
}
for i => subPattern in subs {
interpreter."match"(subPattern, &scope, list[i], ejector)
}
interpreter."match"(restPattern, &scope, list.run
(subs.size()), ejector)
}
to visitFinalPattern(_, nounExpr, optGuardExpr) {
if (optGuardExpr =~ guardExpr :notNull) {
scope := scope.with(nounExpr.getName(),
interpreter(guardExpr, &scope) \
.coerce(specimen, ejector))
} else {
scope := scope.with(nounExpr.getName(), specimen)
}
}
to visitIgnorePattern(_) {
# Nothing happens.
}
to visitListPattern(_, subs) {
def list := List.coerce(specimen, ejector)
if ((def ss := subs.size()) != (def ls := list.size())) {
throw.eject(ejector, `a $ls size list doesn't match a
$ss size list pattern`)
}
for i => subPattern in subs {
interpreter."match"(subPattern, &scope, list[i], ejector)
}
}
to visitSlotPattern(_, nounExpr, optGuardExpr) {
if (optGuardExpr =~ guardExpr :notNull) {
scope := scope.withSlot(nounExpr.getName(),
interpreter(guardExpr, &scope) \
.coerce(specimen, ejector))
} else {
scope := scope.withSlot(nounExpr.getName(), specimen)
}
}
to visitSuchThatPattern(_, subPattern, test) {
interpreter."match"(subPattern, &scope, specimen, ejector)
def ok :boolean := interpreter(test, &scope)
if (!ok) {
# XXX should throw the Lisp-side exception type such-
that-error
throw.eject(ejector, "<such-that-error: " + E.toString
(test) + " was false for " + E.toQuote(specimen) + ">")
}
}
to visitVarPattern(_, nounExpr, optGuardExpr) {
if (optGuardExpr =~ guardExpr :notNull) {
scope := scope.withSlot(nounExpr.getName(),
__makeGuardedSlot(interpreter(guardExpr, &scope),
specimen,
ejector))
} else {
scope := scope.withSlot(nounExpr.getName(),
__makeVarSlot(specimen))
}
}
})
}
}
return interpreter
}
}
--
Kevin Reid <http://homepage.mac.com/kpreid/>
More information about the e-lang
mailing list