Skip to content

Commit

Permalink
Add concept of bound vs unbound variables
Browse files Browse the repository at this point in the history
Rework variable name binding to have a notion of pre-compilation
declarations (UnboundNames) and post-compilation bound names (Names).
  • Loading branch information
rdaum committed Aug 12, 2024
1 parent 16819cf commit 1493536
Show file tree
Hide file tree
Showing 18 changed files with 446 additions and 249 deletions.
20 changes: 10 additions & 10 deletions crates/compiler/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::fmt::Display;
use moor_values::var::Var;

/// The abstract syntax tree produced by the parser and converted by codegen into opcodes.
use crate::names::Name;
use crate::names::UnboundName;
use crate::opcode::Op;

#[derive(Debug, Eq, PartialEq, Clone)]
Expand All @@ -37,7 +37,7 @@ pub enum ScatterKind {
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ScatterItem {
pub kind: ScatterKind,
pub id: Name,
pub id: UnboundName,
pub expr: Option<Expr>,
}

Expand Down Expand Up @@ -130,7 +130,7 @@ pub enum Expr {
args: Vec<Arg>,
},
Value(Var),
Id(Name),
Id(UnboundName),
Binary(BinaryOp, Box<Expr>, Box<Expr>),
And(Box<Expr>, Box<Expr>),
Or(Box<Expr>, Box<Expr>),
Expand Down Expand Up @@ -177,7 +177,7 @@ pub struct CondArm {

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct ExceptArm {
pub id: Option<Name>,
pub id: Option<UnboundName>,
pub codes: CatchCodes,
pub statements: Vec<Stmt>,
}
Expand Down Expand Up @@ -213,23 +213,23 @@ pub enum StmtNode {
otherwise: Vec<Stmt>,
},
ForList {
id: Name,
id: UnboundName,
expr: Expr,
body: Vec<Stmt>,
},
ForRange {
id: Name,
id: UnboundName,
from: Expr,
to: Expr,
body: Vec<Stmt>,
},
While {
id: Option<Name>,
id: Option<UnboundName>,
condition: Expr,
body: Vec<Stmt>,
},
Fork {
id: Option<Name>,
id: Option<UnboundName>,
time: Expr,
body: Vec<Stmt>,
},
Expand All @@ -242,10 +242,10 @@ pub enum StmtNode {
handler: Vec<Stmt>,
},
Break {
exit: Option<Name>,
exit: Option<UnboundName>,
},
Continue {
exit: Option<Name>,
exit: Option<UnboundName>,
},
Return(Option<Expr>),
Expr(Expr),
Expand Down
14 changes: 6 additions & 8 deletions crates/compiler/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ use ArgCount::{Q, U};
use ArgType::{Any, AnyNum, Typed};
use VarType::{TYPE_FLOAT, TYPE_INT, TYPE_LIST, TYPE_OBJ, TYPE_STR};

use crate::names::Name;

lazy_static! {
pub static ref BUILTIN_DESCRIPTORS: Vec<Builtin> = mk_builtin_table();
}
Expand Down Expand Up @@ -951,18 +949,18 @@ fn mk_builtin_table() -> Vec<Builtin> {
]
}

pub fn make_builtin_labels() -> HashMap<Symbol, Name> {
pub fn make_builtin_offsets() -> HashMap<Symbol, usize> {
let mut b = HashMap::new();
for (i, builtin) in BUILTIN_DESCRIPTORS.iter().enumerate() {
b.insert(builtin.name, Name(i as u16));
for (offset, builtin) in BUILTIN_DESCRIPTORS.iter().enumerate() {
b.insert(builtin.name, offset);
}

b
}
pub fn make_labels_builtins() -> HashMap<Name, Symbol> {
pub fn make_offsets_builtins() -> HashMap<usize, Symbol> {
let mut b = HashMap::new();
for (i, builtin) in BUILTIN_DESCRIPTORS.iter().enumerate() {
b.insert(Name(i as u16), builtin.name);
for (offset, builtin) in BUILTIN_DESCRIPTORS.iter().enumerate() {
b.insert(offset, builtin.name);
}

b
Expand Down
74 changes: 45 additions & 29 deletions crates/compiler/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ use moor_values::var::Variant;
use crate::ast::{
Arg, BinaryOp, CatchCodes, Expr, ScatterItem, ScatterKind, Stmt, StmtNode, UnaryOp,
};
use crate::builtins::make_builtin_labels;
use crate::builtins::make_builtin_offsets;
use crate::labels::{JumpLabel, Label, Offset};
use crate::names::{Name, Names};
use crate::names::{Name, Names, UnboundName};
use crate::opcode::Op::Jump;
use crate::opcode::{Op, ScatterArgs, ScatterLabel};
use crate::parse::parse_program;
Expand All @@ -47,21 +47,27 @@ pub struct CodegenState {
pub(crate) ops: Vec<Op>,
pub(crate) jumps: Vec<JumpLabel>,
pub(crate) var_names: Names,
pub(crate) binding_mappings: HashMap<UnboundName, Name>,
pub(crate) literals: Vec<Var>,
pub(crate) loops: Vec<Loop>,
pub(crate) saved_stack: Option<Offset>,
pub(crate) cur_stack: usize,
pub(crate) max_stack: usize,
pub(crate) builtins: HashMap<Symbol, Name>,
pub(crate) builtins: HashMap<Symbol, usize>,
pub(crate) fork_vectors: Vec<Vec<Op>>,
pub(crate) line_number_spans: Vec<(usize, usize)>,
}

impl CodegenState {
pub fn new(var_names: Names, builtins: HashMap<Symbol, Name>) -> Self {
pub fn new(
var_names: Names,
binding_mappings: HashMap<UnboundName, Name>,
builtins: HashMap<Symbol, usize>,
) -> Self {
Self {
ops: vec![],
jumps: vec![],
binding_mappings,
var_names,
literals: vec![],
loops: vec![],
Expand Down Expand Up @@ -186,7 +192,7 @@ impl CodegenState {
continue;
}
Expr::Id(name) => {
self.emit(Op::Put(*name));
self.emit(Op::Put(self.binding_mappings[name]));
break;
}
Expr::Prop {
Expand Down Expand Up @@ -220,16 +226,16 @@ impl CodegenState {
.iter()
.map(|s| {
let kind_label = match s.kind {
ScatterKind::Required => ScatterLabel::Required(s.id),
ScatterKind::Required => ScatterLabel::Required(self.binding_mappings[&s.id]),
ScatterKind::Optional => ScatterLabel::Optional(
s.id,
self.binding_mappings[&s.id],
if s.expr.is_some() {
Some(self.make_jump_label(None))
} else {
None
},
),
ScatterKind::Rest => ScatterLabel::Rest(s.id),
ScatterKind::Rest => ScatterLabel::Rest(self.binding_mappings[&s.id]),
};
(s, kind_label)
})
Expand All @@ -246,7 +252,7 @@ impl CodegenState {
}
self.commit_jump_label(label);
self.generate_expr(s.expr.as_ref().unwrap())?;
self.emit(Op::Put(s.id));
self.emit(Op::Put(self.binding_mappings[&s.id]));
self.emit(Op::Pop);
self.pop_stack(1);
}
Expand Down Expand Up @@ -276,7 +282,7 @@ impl CodegenState {
}
Expr::Id(id) => {
if indexed_above {
self.emit(Op::Push(*id));
self.emit(Op::Push(self.binding_mappings[id]));
self.push_stack(1);
}
}
Expand Down Expand Up @@ -335,7 +341,7 @@ impl CodegenState {
self.push_stack(1);
}
Expr::Id(ident) => {
self.emit(Op::Push(*ident));
self.emit(Op::Push(self.binding_mappings[ident]));
self.push_stack(1);
}
Expr::And(left, right) => {
Expand Down Expand Up @@ -538,12 +544,15 @@ impl CodegenState {
// we use 0 here to make it easier to implement the ForList instruction.
self.emit(Op::ImmInt(0)); /* loop list index... */
self.push_stack(1);
let loop_top = self.make_jump_label(Some(*id));
let loop_top = self.make_jump_label(Some(self.binding_mappings[id]));
self.commit_jump_label(loop_top);
let end_label = self.make_jump_label(Some(*id));
self.emit(Op::ForList { id: *id, end_label });
let end_label = self.make_jump_label(Some(self.binding_mappings[id]));
self.emit(Op::ForList {
id: self.binding_mappings[id],
end_label,
});
self.loops.push(Loop {
loop_name: Some(*id),
loop_name: Some(self.binding_mappings[id]),
top_label: loop_top,
top_stack: self.cur_stack.into(),
bottom_label: end_label,
Expand All @@ -560,12 +569,15 @@ impl CodegenState {
StmtNode::ForRange { from, to, id, body } => {
self.generate_expr(from)?;
self.generate_expr(to)?;
let loop_top = self.make_jump_label(Some(*id));
let end_label = self.make_jump_label(Some(*id));
let loop_top = self.make_jump_label(Some(self.binding_mappings[id]));
let end_label = self.make_jump_label(Some(self.binding_mappings[id]));
self.commit_jump_label(loop_top);
self.emit(Op::ForRange { id: *id, end_label });
self.emit(Op::ForRange {
id: self.binding_mappings[id],
end_label,
});
self.loops.push(Loop {
loop_name: Some(*id),
loop_name: Some(self.binding_mappings[id]),
top_label: loop_top,
top_stack: self.cur_stack.into(),
bottom_label: end_label,
Expand All @@ -584,21 +596,23 @@ impl CodegenState {
condition,
body,
} => {
let loop_start_label = self.make_jump_label(*id);
let loop_start_label =
self.make_jump_label(id.as_ref().map(|id| self.binding_mappings[id]));
self.commit_jump_label(loop_start_label);

let loop_end_label = self.make_jump_label(*id);
let loop_end_label =
self.make_jump_label(id.as_ref().map(|id| self.binding_mappings[id]));
self.generate_expr(condition)?;
match id {
None => self.emit(Op::While(loop_end_label)),
Some(id) => self.emit(Op::WhileId {
id: *id,
id: self.binding_mappings[id],
end_label: loop_end_label,
}),
}
self.pop_stack(1);
self.loops.push(Loop {
loop_name: *id,
loop_name: id.as_ref().map(|id| self.binding_mappings[id]),
top_label: loop_start_label,
top_stack: self.cur_stack.into(),
bottom_label: loop_end_label,
Expand Down Expand Up @@ -626,7 +640,7 @@ impl CodegenState {
let fv_id = self.add_fork_vector(forked_ops);
self.ops = stashed_ops;
self.emit(Op::Fork {
id: *id,
id: id.as_ref().map(|id| self.binding_mappings[id]),
fv_offset: fv_id,
});
self.pop_stack(1);
Expand All @@ -651,7 +665,7 @@ impl CodegenState {
self.commit_jump_label(labels[i]);
self.push_stack(1);
if ex.id.is_some() {
self.emit(Op::Put(ex.id.unwrap()));
self.emit(Op::Put(self.binding_mappings[ex.id.as_ref().unwrap()]));
}
self.emit(Op::Pop);
self.pop_stack(1);
Expand Down Expand Up @@ -689,7 +703,8 @@ impl CodegenState {
})
}
StmtNode::Break { exit: Some(l) } => {
let l = self.find_loop(l).expect("invalid loop for break/continue");
let l = self.binding_mappings[l];
let l = self.find_loop(&l).expect("invalid loop for break/continue");
self.emit(Op::ExitId(l.bottom_label));
}
StmtNode::Continue { exit: None } => {
Expand All @@ -700,7 +715,8 @@ impl CodegenState {
})
}
StmtNode::Continue { exit: Some(l) } => {
let l = self.find_loop(l).expect("invalid loop for break/continue");
let l = self.binding_mappings[l];
let l = self.find_loop(&l).expect("invalid loop for break/continue");
self.emit(Op::ExitId(l.top_label));
}
StmtNode::Return(Some(expr)) => {
Expand Down Expand Up @@ -756,11 +772,11 @@ pub fn compile(program: &str) -> Result<Program, CompileError> {
let compile_span = tracing::trace_span!("compile");
let _compile_guard = compile_span.enter();

let builtins = make_builtin_labels();
let builtins = make_builtin_offsets();
let parse = parse_program(program)?;

// Generate the code into 'cg_state'.
let mut cg_state = CodegenState::new(parse.names, builtins);
let mut cg_state = CodegenState::new(parse.names, parse.names_mapping, builtins);
for x in parse.stmts {
cg_state.generate_stmt(&x)?;
}
Expand Down
8 changes: 2 additions & 6 deletions crates/compiler/src/codegen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ mod tests {
use crate::builtins::BUILTIN_DESCRIPTORS;
use crate::codegen::compile;
use crate::labels::{Label, Offset};
use crate::names::Name;
use moor_values::model::CompileError;
use moor_values::var::Error::{E_INVARG, E_INVIND, E_PERM, E_PROPNF, E_RANGE};
use moor_values::var::Objid;
Expand Down Expand Up @@ -434,7 +433,6 @@ mod tests {
fn test_and_or() {
let program = "a = (1 && 2 || 3);";
let binary = compile(program).unwrap();

let a = binary.find_var("a");

/*
Expand Down Expand Up @@ -495,7 +493,7 @@ mod tests {
MakeSingletonList,
Imm(test),
ListAddTail,
FuncCall { id: Name(0) },
FuncCall { id: 0 },
Pop,
Done
]
Expand Down Expand Up @@ -932,9 +930,7 @@ mod tests {
},
ImmErr(E_INVARG),
MakeSingletonList,
FuncCall {
id: Name(raise_num as u16)
},
FuncCall { id: raise_num },
EndCatch(1.into()),
ImmInt(1),
Ref,
Expand Down
Loading

0 comments on commit 1493536

Please sign in to comment.