use doge_compiler as dc;
use doge_runtime::{
assert_error, bonk_error, error_value, index_set, iter_value, unpack_value, DogeResult, Value,
};
use crate::{cell, Flow, Interp, Scope};
impl Interp {
pub(crate) fn exec_stmts(
&mut self,
stmts: &[dc::Stmt],
frame: &Scope,
fid: u32,
) -> DogeResult<Flow> {
for stmt in stmts {
match self.exec_stmt(stmt, frame, fid)? {
Flow::Normal => {}
escaped => return Ok(escaped),
}
}
Ok(Flow::Normal)
}
pub(crate) fn exec_stmt(
&mut self,
stmt: &dc::Stmt,
frame: &Scope,
fid: u32,
) -> DogeResult<Flow> {
self.mark(fid, stmt.span());
match stmt {
dc::Stmt::Decl {
names, rest, expr, ..
} => {
let value = self.eval(expr, frame, fid)?;
self.bind_destructure(names, rest.as_deref(), value, frame, fid)?;
Ok(Flow::Normal)
}
dc::Stmt::ConstDecl { name, expr, .. } => {
let value = self.eval(expr, frame, fid)?;
self.bind_name(frame, name, value);
Ok(Flow::Normal)
}
dc::Stmt::Import { .. } => Ok(Flow::Normal),
dc::Stmt::Assign {
targets,
rest,
expr,
op,
..
} => {
self.exec_assign(targets, rest.as_ref(), expr, *op, frame, fid)?;
Ok(Flow::Normal)
}
dc::Stmt::Bark { expr, .. } => {
let value = self.eval(expr, frame, fid)?;
doge_runtime::bark(&value);
Ok(Flow::Normal)
}
dc::Stmt::If {
branches,
else_body,
..
} => {
for (cond, body) in branches {
if self.eval(cond, frame, fid)?.truthy() {
return self.exec_stmts(body, frame, fid);
}
}
if let Some(body) = else_body {
return self.exec_stmts(body, frame, fid);
}
Ok(Flow::Normal)
}
dc::Stmt::For {
vars,
rest,
iter,
body,
..
} => self.exec_for(vars, rest.as_deref(), iter, body, frame, fid),
dc::Stmt::While { cond, body, .. } => {
loop {
self.mark(fid, cond.span());
if !self.eval(cond, frame, fid)?.truthy() {
break;
}
match self.exec_stmts(body, frame, fid)? {
Flow::Normal | Flow::Continue => {}
Flow::Break => break,
ret @ Flow::Return(_) => return Ok(ret),
}
}
Ok(Flow::Normal)
}
dc::Stmt::FuncDef { name, span, .. } => {
let value = self.make_function(*span, name, frame, fid);
self.bind_name(frame, name, value);
Ok(Flow::Normal)
}
dc::Stmt::ObjDef { .. } => Ok(Flow::Normal),
dc::Stmt::Try {
body,
err_name,
handler,
..
} => match self.exec_stmts(body, frame, fid) {
Ok(flow) => Ok(flow),
Err(err) => {
let value = error_value(&err, &self.cur_path(), self.cur_line);
self.bind_name(frame, err_name, value);
self.exec_stmts(handler, frame, fid)
}
},
dc::Stmt::Return { expr, .. } => {
let value = match expr {
Some(expr) => self.eval(expr, frame, fid)?,
None => Value::None,
};
Ok(Flow::Return(value))
}
dc::Stmt::Bonk { expr, .. } => {
let value = self.eval(expr, frame, fid)?;
Err(bonk_error(&value))
}
dc::Stmt::Amaze { cond, message, .. } => {
if self.eval(cond, frame, fid)?.truthy() {
return Ok(Flow::Normal);
}
let message = match message {
Some(message) => Some(self.eval(message, frame, fid)?),
None => None,
};
Err(assert_error(message.as_ref()))
}
dc::Stmt::Bork { .. } => Ok(Flow::Break),
dc::Stmt::Continue { .. } => Ok(Flow::Continue),
dc::Stmt::ExprStmt { expr } => {
self.eval(expr, frame, fid)?;
Ok(Flow::Normal)
}
}
}
fn exec_for(
&mut self,
vars: &[String],
rest: Option<&str>,
iter: &dc::Expr,
body: &[dc::Stmt],
frame: &Scope,
fid: u32,
) -> DogeResult<Flow> {
let iterable = self.eval(iter, frame, fid)?;
for item in iter_value(&iterable)? {
self.bind_destructure(vars, rest, item, frame, fid)?;
match self.exec_stmts(body, frame, fid)? {
Flow::Normal | Flow::Continue => {}
Flow::Break => break,
ret @ Flow::Return(_) => return Ok(ret),
}
}
Ok(Flow::Normal)
}
fn bind_destructure(
&mut self,
names: &[String],
rest: Option<&str>,
value: Value,
frame: &Scope,
fid: u32,
) -> DogeResult<()> {
if names.len() == 1 && rest.is_none() {
self.bind_name(frame, &names[0], value);
return Ok(());
}
let mut values = unpack_value(&value, names.len(), rest.is_some())?;
if let Some(rest) = rest {
let collector = values
.pop()
.expect("unpack_value returns the collector last");
self.bind_name(frame, rest, collector);
}
for (name, value) in names.iter().zip(values) {
self.bind_name(frame, name, value);
}
let _ = fid;
Ok(())
}
fn exec_assign(
&mut self,
targets: &[dc::Expr],
rest: Option<&dc::Expr>,
expr: &dc::Expr,
op: Option<dc::BinOp>,
frame: &Scope,
fid: u32,
) -> DogeResult<()> {
if let Some(op) = op {
return self.exec_augmented(&targets[0], op, expr, frame, fid);
}
let value = self.eval(expr, frame, fid)?;
if targets.len() == 1 && rest.is_none() {
return self.assign_target(&targets[0], value, frame, fid);
}
let mut values = unpack_value(&value, targets.len(), rest.is_some())?;
if let Some(rest) = rest {
let collector = values
.pop()
.expect("unpack_value returns the collector last");
self.assign_target(rest, collector, frame, fid)?;
}
for (target, value) in targets.iter().zip(values) {
self.assign_target(target, value, frame, fid)?;
}
Ok(())
}
fn exec_augmented(
&mut self,
target: &dc::Expr,
op: dc::BinOp,
rhs: &dc::Expr,
frame: &Scope,
fid: u32,
) -> DogeResult<()> {
let rhs = self.eval(rhs, frame, fid)?;
match target {
dc::Expr::Ident { name, .. } => {
let current = self.read_name(frame, fid, name)?;
let updated = self.binop(op, current, rhs)?;
self.store_name(frame, fid, name, updated);
Ok(())
}
dc::Expr::Index { obj, index, .. } => {
let recv = self.eval(obj, frame, fid)?;
let idx = self.eval(index, frame, fid)?;
let current = doge_runtime::index_get(&recv, &idx)?;
let updated = self.binop(op, current, rhs)?;
index_set(&recv, &idx, updated)
}
dc::Expr::Attr { obj, name, .. } => {
let recv = self.eval(obj, frame, fid)?;
let current = doge_runtime::attr_get(&recv, name)?;
let updated = self.binop(op, current, rhs)?;
doge_runtime::attr_set(&recv, name, updated)
}
_ => Ok(()),
}
}
fn assign_target(
&mut self,
target: &dc::Expr,
value: Value,
frame: &Scope,
fid: u32,
) -> DogeResult<()> {
match target {
dc::Expr::Ident { name, .. } => {
self.store_name(frame, fid, name, value);
Ok(())
}
dc::Expr::Index { obj, index, .. } => {
let recv = self.eval(obj, frame, fid)?;
let idx = self.eval(index, frame, fid)?;
index_set(&recv, &idx, value)
}
dc::Expr::Attr { obj, name, .. } => {
let recv = self.eval(obj, frame, fid)?;
doge_runtime::attr_set(&recv, name, value)
}
_ => Ok(()),
}
}
pub(crate) fn bind_name(&self, frame: &Scope, name: &str, value: Value) {
if let Some(existing) = frame.borrow().get(name) {
*existing.borrow_mut() = value;
return;
}
frame.borrow_mut().insert(name.to_string(), cell(value));
}
fn store_name(&self, frame: &Scope, fid: u32, name: &str, value: Value) {
if let Some(existing) = self.lookup(frame, fid, name) {
*existing.borrow_mut() = value;
} else {
frame.borrow_mut().insert(name.to_string(), cell(value));
}
}
}