use crate::ast::Span;
use crate::collections::HashMap;
use crate::compile::{CompileError, CompileErrorKind, Location};
use crate::runtime::{Inst, Label};
use crate::{Hash, SourceId};
#[derive(Debug, Clone)]
pub(crate) enum AssemblyInst {
Jump { label: Label },
JumpIf { label: Label },
JumpIfOrPop { label: Label },
JumpIfNotOrPop { label: Label },
JumpIfBranch { branch: i64, label: Label },
PopAndJumpIfNot { count: usize, label: Label },
IterNext { offset: usize, label: Label },
Raw { raw: Inst },
}
#[derive(Debug, Clone, Default)]
pub(crate) struct Assembly {
location: Location,
pub(crate) labels: HashMap<Label, usize>,
pub(crate) labels_rev: HashMap<usize, Label>,
pub(crate) instructions: Vec<(AssemblyInst, Span)>,
pub(crate) comments: HashMap<usize, Vec<Box<str>>>,
pub(crate) label_count: usize,
pub(crate) required_functions: HashMap<Hash, Vec<(Span, SourceId)>>,
}
impl Assembly {
pub(crate) fn new(location: Location, label_count: usize) -> Self {
Self {
location,
labels: Default::default(),
labels_rev: Default::default(),
instructions: Default::default(),
comments: Default::default(),
label_count,
required_functions: Default::default(),
}
}
pub(crate) fn new_label(&mut self, name: &'static str) -> Label {
let label = Label::new(name, self.label_count);
self.label_count += 1;
label
}
pub(crate) fn label(&mut self, label: Label) -> Result<Label, CompileError> {
let offset = self.instructions.len();
if self.labels.insert(label, offset).is_some() {
return Err(CompileError::new(
self.location.span,
CompileErrorKind::DuplicateLabel { label },
));
}
self.labels_rev.insert(offset, label);
Ok(label)
}
pub(crate) fn jump(&mut self, label: Label, span: Span) {
self.instructions.push((AssemblyInst::Jump { label }, span));
}
pub(crate) fn jump_if(&mut self, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::JumpIf { label }, span));
}
pub(crate) fn jump_if_or_pop(&mut self, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::JumpIfOrPop { label }, span));
}
pub(crate) fn jump_if_not_or_pop(&mut self, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::JumpIfNotOrPop { label }, span));
}
pub(crate) fn jump_if_branch(&mut self, branch: i64, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::JumpIfBranch { branch, label }, span));
}
pub(crate) fn pop_and_jump_if_not(&mut self, count: usize, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::PopAndJumpIfNot { count, label }, span));
}
pub(crate) fn iter_next(&mut self, offset: usize, label: Label, span: Span) {
self.instructions
.push((AssemblyInst::IterNext { offset, label }, span));
}
pub(crate) fn push(&mut self, raw: Inst, span: Span) {
if let Inst::Call { hash, .. } = raw {
self.required_functions
.entry(hash)
.or_default()
.push((span, self.location.source_id));
}
self.instructions.push((AssemblyInst::Raw { raw }, span));
}
pub(crate) fn push_with_comment<C>(&mut self, raw: Inst, span: Span, comment: C)
where
C: AsRef<str>,
{
let pos = self.instructions.len();
self.comments
.entry(pos)
.or_default()
.push(comment.as_ref().into());
self.push(raw, span);
}
}