///|
/// Statement evaluation: Figures 4.3 and 4.4.
///
/// A sequence's result is the bindings the SEQUENCE made, not the whole
/// environment: `eval-seq` composes `assigns ρ'` with what follows, and the
/// caller overrides its own environment with the result. That is what keeps a
/// function body's locals out of its caller.
pub async fn Interp::eval_seq(
self : Interp,
items : ArrayView[@analysis.Statement],
env : Env,
) -> StmtResult noraise {
let mut accumulated = StmtResult::Assigns(@value.empty_env())
let mut current = env
for item in items {
let r = self.eval_statement(item, current)
match r {
Assigns(delta) => {
accumulated = accumulated.then(Assigns(delta))
current = @value.override_env(current, delta)
}
_ => return accumulated.then(r)
}
}
accumulated
}
///|
pub async fn Interp::eval_body(
self : Interp,
body : Array[@ast.Stmt],
env : Env,
) -> StmtResult noraise {
self.eval_seq(@analysis.statements(body)[:], env)
}
///|
async fn Interp::eval_statement(
self : Interp,
item : @analysis.Statement,
env : Env,
) -> StmtResult noraise {
match item {
Single(s) => self.eval_stmt(s, env)
// eval-def: a whole region at once, each definition closing over the
// environment the region starts in.
Region(defs) => {
let entries : Array[(String, Value)] = []
for i, d in defs {
match d {
FunctionDef(name~, ..) =>
entries.push(
(
name,
Value::Def({
env,
region: defs,
index: i,
in_module: self.in_module,
}),
),
)
_ => ()
}
}
Assigns(@value.env_of(entries))
}
}
}
///|
async fn Interp::eval_stmt(
self : Interp,
s : @ast.Stmt,
env : Env,
) -> StmtResult noraise {
match s {
// eval-pass
Pass(..) => Assigns(@value.empty_env())
// eval-assign
Assign(targets~, value~, ..) =>
match self.eval_expr(value, env) {
Val(v) =>
match targets[0] {
Name(id~, ..) => Assigns(@value.env_of([(id, v)]))
_ => ResultStuck("an assignment target that is not a name")
}
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
// eval-expr-stmt
ExprStmt(value~, ..) =>
match self.eval_expr(value, env) {
Val(_) => Assigns(@value.empty_env())
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
// eval-return, eval-return-none
Return(value~, ..) =>
match value {
None => Returns(Value::None)
Some(e) =>
match self.eval_expr(e, env) {
Val(v) => Returns(v)
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
}
// eval-assert and its four companions. The message is evaluated only
// when the condition is False, and it must be a string.
Assert(cond~, msg~, ..) =>
match self.eval_expr(cond, env) {
Val(Bool(true)) => Assigns(@value.empty_env())
Val(Bool(false)) =>
match msg {
None => {
self.record(s.span())
ResultAborts(AssertionError(None))
}
Some(m) =>
match self.eval_expr(m, env) {
Val(Str(w)) => {
self.record(s.span())
ResultAborts(AssertionError(Some(w)))
}
Val(v) =>
ResultStuck("an assertion message of type " + v.kind_name())
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
}
Val(v) => ResultStuck("an assertion on " + v.kind_name())
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
// eval-if, eval-if-none, eval-if-else, eval-else
If(cond~, body~, or_else~, ..) =>
match self.eval_expr(cond, env) {
Val(Bool(true)) => self.eval_body(body, env)
Val(Bool(false)) =>
if or_else.is_empty() {
Assigns(@value.empty_env())
} else {
self.eval_body(or_else, env)
}
Val(v) => ResultStuck("an if condition on " + v.kind_name())
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
// eval-match: the result carries the pattern's bindings as well as the
// body's, composed in that order.
Match(subject~, cases~, ..) =>
match self.eval_expr(subject, env) {
Val(v) =>
match self.dispatch(env, v, cases) {
FellThrough => Assigns(@value.empty_env())
DispatchStuck(op) => ResultStuck(op)
Taken(bindings, body) => {
let inner = @value.override_env(env, bindings)
StmtResult::Assigns(bindings).then(self.eval_body(body, inner))
}
}
Aborts(k) => ResultAborts(k)
Stuck(op) => ResultStuck(op)
}
// eval-class, eval-class-extend
ClassDef(name~, bases~, ..) => {
let base = if bases.is_empty() {
None
} else {
match bases[0] {
Name(id~, ..) => Some(id)
_ => return ResultStuck("a base class that is not a name")
}
}
// The class entry carries the context its base can be resolved in. At
// run time that context is built from the environment, where the base
// class is already a value.
let declaring = match base {
None => @context.empty_context()
Some(b) =>
match env.get(b) {
Some(Class(be)) =>
@context.context_of([(b, @context.Entry::Class(be))])
_ => return ResultStuck("a base class that is not a class")
}
}
let module_name = match env.get("__name__") {
Some(Str(q)) => q
_ => "__main__"
}
let entry : @context.ClassEntry = {
context: declaring,
name: module_name + "." + name,
own_fields: @analysis.own_fields(s),
base,
}
Assigns(@value.env_of([(name, Value::Class(entry))]))
}
_ => ResultStuck("statement " + s.kind_name())
}
}