feat: stash
This commit is contained in:
440
src/compile.rs
Normal file
440
src/compile.rs
Normal file
@@ -0,0 +1,440 @@
|
||||
use crate::bytecode::*;
|
||||
use crate::ir;
|
||||
use crate::ty::internal::Const;
|
||||
|
||||
pub struct Compiler {
|
||||
opcodes: Vec<OpCode>,
|
||||
}
|
||||
|
||||
pub fn compile(downgraded: ir::Downgraded) -> Program {
|
||||
Program {
|
||||
top_level: Compiler::new().compile(downgraded.top_level),
|
||||
thunks: downgraded
|
||||
.thunks
|
||||
.into_iter()
|
||||
.map(|thunk| Thunk {
|
||||
opcodes: Compiler::new().compile(thunk),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
opcodes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compile(mut self, ir: ir::Ir) -> OpCodes {
|
||||
ir.compile(&mut self);
|
||||
self.opcodes()
|
||||
}
|
||||
|
||||
fn push(&mut self, code: OpCode) {
|
||||
self.opcodes.push(code);
|
||||
}
|
||||
|
||||
fn idx(&self) -> usize {
|
||||
self.opcodes.len()
|
||||
}
|
||||
|
||||
fn modify(&mut self, idx: usize, code: OpCode) {
|
||||
self.opcodes[idx] = code;
|
||||
}
|
||||
|
||||
fn last(&self) -> Option<OpCode> {
|
||||
self.opcodes.last().cloned()
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<OpCode> {
|
||||
self.opcodes.pop()
|
||||
}
|
||||
|
||||
fn opcodes(self) -> OpCodes {
|
||||
self.opcodes.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Compile {
|
||||
fn compile(self, comp: &mut Compiler);
|
||||
}
|
||||
|
||||
pub trait CompileWithLength {
|
||||
fn compile_with_length(self, comp: &mut Compiler) -> usize;
|
||||
}
|
||||
|
||||
impl<T: Compile> CompileWithLength for T {
|
||||
fn compile_with_length(self, comp: &mut Compiler) -> usize {
|
||||
let start = comp.idx();
|
||||
self.compile(comp);
|
||||
let end = comp.idx();
|
||||
end - start
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Const {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::Const { value: self.value });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Var {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::LookUp { sym: self.sym });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Thunk {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::LoadThunk { idx: self.idx });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Attrs {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(if self.rec {
|
||||
OpCode::RecAttrSet
|
||||
} else {
|
||||
OpCode::AttrSet
|
||||
});
|
||||
for stc in self.stcs {
|
||||
stc.1.compile(comp);
|
||||
comp.push(OpCode::PushStaticAttr { name: stc.0 });
|
||||
}
|
||||
for dynamic in self.dyns {
|
||||
dynamic.0.compile(comp);
|
||||
dynamic.1.compile(comp);
|
||||
comp.push(OpCode::PushDynamicAttr)
|
||||
}
|
||||
if self.rec {
|
||||
comp.push(OpCode::LeaveEnv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::List {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::List);
|
||||
for item in self.items {
|
||||
item.compile(comp);
|
||||
comp.push(OpCode::PushElem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::UnOp {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
use ir::UnOpKind::*;
|
||||
match self.kind {
|
||||
Neg => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__sub".into(),
|
||||
});
|
||||
comp.push(OpCode::Const {
|
||||
value: Const::Int(0),
|
||||
});
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
Not => {
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::BinOp {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
use ir::BinOpKind::*;
|
||||
match self.kind {
|
||||
Add => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Add });
|
||||
}
|
||||
Mul => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__mul".into(),
|
||||
});
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
Div => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__div".into(),
|
||||
});
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
And => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::And });
|
||||
}
|
||||
Or => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||
}
|
||||
Eq => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||
}
|
||||
Lt => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__lessThan".into(),
|
||||
});
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
Con => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Con });
|
||||
}
|
||||
Upd => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Upd });
|
||||
}
|
||||
|
||||
Sub => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__sub".into(),
|
||||
});
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
Impl => {
|
||||
self.lhs.compile(comp);
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Or });
|
||||
}
|
||||
Neq => {
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::BinOp { op: BinOp::Eq });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
Gt => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__lessThan".into(),
|
||||
});
|
||||
self.rhs.compile(comp);
|
||||
self.lhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
}
|
||||
Leq => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__lessThan".into(),
|
||||
});
|
||||
self.rhs.compile(comp);
|
||||
self.lhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
Geq => {
|
||||
comp.push(OpCode::LookUp {
|
||||
sym: "__lessThan".into(),
|
||||
});
|
||||
self.lhs.compile(comp);
|
||||
self.rhs.compile(comp);
|
||||
comp.push(OpCode::Call { arity: 2 });
|
||||
comp.push(OpCode::UnOp { op: UnOp::Not });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::HasAttr {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.lhs.compile(comp);
|
||||
for attr in self.rhs {
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
}
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile(comp);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::HasAttr { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Select {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.expr.compile(comp);
|
||||
for attr in self.attrpath {
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
}
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile(comp);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.default {
|
||||
Some(default) => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
default.compile(comp);
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::Select { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamic),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::ConcatStrings {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
let mut iter = self.parts.into_iter();
|
||||
iter.next().unwrap().compile(comp);
|
||||
for item in iter {
|
||||
item.compile(comp);
|
||||
comp.push(OpCode::ConcatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::If {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.cond.compile(comp);
|
||||
|
||||
let idx_jmp_if_false = comp.idx();
|
||||
// place holder
|
||||
comp.push(OpCode::Illegal);
|
||||
|
||||
let consq_length = self.consq.compile_with_length(comp);
|
||||
|
||||
let idx_jmp = comp.idx();
|
||||
// place holder
|
||||
comp.push(OpCode::Illegal);
|
||||
|
||||
let alter_length = self.alter.compile_with_length(comp);
|
||||
|
||||
comp.modify(
|
||||
idx_jmp_if_false,
|
||||
OpCode::JmpIfFalse {
|
||||
step: consq_length + 1,
|
||||
},
|
||||
);
|
||||
comp.modify(idx_jmp, OpCode::Jmp { step: alter_length });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Let {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.attrs.compile(comp);
|
||||
comp.push(OpCode::EnterEnv);
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::LeaveEnv);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::With {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.namespace.compile(comp);
|
||||
comp.push(OpCode::EnterEnv);
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::LeaveEnv);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Assert {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.assertion.compile(comp);
|
||||
comp.push(OpCode::Assert);
|
||||
self.expr.compile(comp);
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Func {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
comp.push(OpCode::Func { idx: self.body.idx });
|
||||
use ir::Param::*;
|
||||
match self.param {
|
||||
Ident(sym) => comp.push(OpCode::PushIdentParam { sym }),
|
||||
Formals {
|
||||
formals,
|
||||
ellipsis,
|
||||
alias,
|
||||
} => {
|
||||
for (sym, default) in formals {
|
||||
comp.push(OpCode::PushFormalParam { sym });
|
||||
if let Some(ir::Thunk { idx }) = default {
|
||||
comp.push(OpCode::PushDefaultParam { idx });
|
||||
}
|
||||
}
|
||||
if ellipsis {
|
||||
comp.push(OpCode::SetEllipsis);
|
||||
}
|
||||
if let Some(sym) = alias {
|
||||
comp.push(OpCode::SetAlias { sym });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Call {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
let arity = self.args.len();
|
||||
self.func.compile(comp);
|
||||
self.args.into_iter().for_each(|arg| {
|
||||
arg.compile(comp);
|
||||
});
|
||||
comp.push(OpCode::Call { arity });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Path {
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::Path);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user