mod card;
mod compilation_error;
mod compile_options;
mod function;
mod module;
mod function_ir;
#[cfg(test)]
mod tests;
use crate::{
bytecode::{encode_str, write_to_vec},
collections::{handle_table::Handle, hash_map::CaoHashMap},
compiled_program::{CaoCompiledProgram, Label},
prelude::Trace,
Instruction, VariableId,
};
use core::slice;
use std::borrow::Cow;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::mem;
use std::{convert::TryInto, str::FromStr};
pub use card::*;
pub use compilation_error::*;
pub use compile_options::*;
pub use function::*;
pub use module::*;
use self::function_ir::FunctionIr;
pub type CompilationResult<T> = Result<T, CompilationError>;
pub(crate) type FunctionSlice<'a> = &'a [FunctionIr];
pub(crate) type NameSpace = smallvec::SmallVec<[Box<str>; 1]>;
pub(crate) type ImportsIr = std::collections::HashMap<String, String>;
pub(crate) type Locals<'a> = arrayvec::ArrayVec<Local<'a>, 255>;
type Upvalues = arrayvec::ArrayVec<Upvalue, 255>;
pub struct Compiler<'a> {
options: CompileOptions,
program: CaoCompiledProgram,
next_var: VariableId,
jump_table: CaoHashMap<String, FunctionMeta>,
current_namespace: Cow<'a, NameSpace>,
current_imports: Cow<'a, ImportsIr>,
locals: Vec<Locals<'a>>,
upvalues: Vec<Upvalues>,
scope_depth: Vec<i32>,
current_index: CardIndex,
function_id: usize,
}
#[derive(Debug, Clone, Copy)]
struct FunctionMeta {
pub hash_key: Handle,
pub arity: u32,
}
#[derive(Debug)]
pub(crate) struct Local<'a> {
pub name: &'a str,
pub depth: i32,
pub captured: bool,
}
pub fn compile(
compilation_unit: CaoProgram,
compile_options: impl Into<Option<CompileOptions>>,
) -> CompilationResult<CaoCompiledProgram> {
let options = compile_options.into().unwrap_or_default();
let compilation_unit = compilation_unit
.into_ir_stream(options.recursion_limit)
.map_err(|err| CompilationError::with_loc(err, Trace::default()))?;
let mut compiler = Compiler::new();
compiler.compile(&compilation_unit, options)
}
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
Self::new()
}
}
enum Variable {
Global,
Local(usize),
Upvalue(usize),
}
#[derive(Default, Clone, Copy, Debug)]
struct Upvalue {
is_local: bool,
index: u8,
}
impl<'a> Compiler<'a> {
pub fn new() -> Self {
Compiler {
options: Default::default(),
program: CaoCompiledProgram::default(),
next_var: VariableId(0),
jump_table: Default::default(),
current_namespace: Default::default(),
locals: vec![Default::default()],
upvalues: vec![Default::default()],
scope_depth: vec![0],
current_index: CardIndex::default(),
current_imports: Default::default(),
function_id: 0,
}
}
fn trace(&self) -> Trace {
Trace {
namespace: self.current_namespace.clone().into_owned(),
index: self.current_index.clone(),
}
}
pub fn compile(
&mut self,
compilation_unit: FunctionSlice<'a>,
compile_options: CompileOptions,
) -> CompilationResult<CaoCompiledProgram> {
self.options = compile_options;
if compilation_unit.is_empty() {
return Err(CompilationError::with_loc(
CompilationErrorPayload::EmptyProgram,
self.trace(),
));
}
self.program = CaoCompiledProgram::default();
self.next_var = VariableId(0);
self.compile_stage_1(compilation_unit)?;
self.compile_stage_2(compilation_unit)?;
self.current_imports = Default::default();
self.push_instruction(Instruction::Exit);
Ok(mem::take(&mut self.program))
}
fn error(&self, pl: CompilationErrorPayload) -> CompilationError {
CompilationError::with_loc(pl, self.trace())
}
fn compile_stage_1(&mut self, compilation_unit: FunctionSlice) -> CompilationResult<()> {
let mut num_cards = 0usize;
for n in compilation_unit.iter() {
num_cards += n.cards.len();
self.add_function(n)?;
}
self.program.labels.0.reserve(num_cards).expect("reserve");
Ok(())
}
fn add_function(&mut self, n: &FunctionIr) -> CompilationResult<()> {
let metadata = FunctionMeta {
hash_key: n.handle,
arity: n.arguments.len() as u32,
};
if self.jump_table.contains(n.name.as_ref()) {
return Err(self.error(CompilationErrorPayload::DuplicateName(n.name.to_string())));
}
self.jump_table.insert(n.full_name(), metadata).unwrap();
Ok(())
}
fn compile_stage_2(&mut self, compilation_unit: FunctionSlice<'a>) -> CompilationResult<()> {
let mut functions = compilation_unit.iter();
if let Some(main_function) = functions.next() {
let il = main_function.function_index;
let len: u32 = match main_function.cards.len().try_into() {
Ok(i) => i,
Err(_) => return Err(self.error(CompilationErrorPayload::TooManyCards(il))),
};
self.current_index = CardIndex::new(il, 0);
self.scope_begin();
self.process_function(main_function)?;
self.current_index = CardIndex {
function: il,
card_index: FunctionCardIndex {
indices: smallvec::smallvec![len],
},
};
self.scope_end();
self.process_card(&Card::Abort)?;
}
for function in functions {
let il = function.function_index;
self.current_index = CardIndex::function(il);
let nodeid_handle = function.handle;
let handle = u32::try_from(self.program.bytecode.len())
.expect("bytecode length to fit into 32 bits");
self.program
.labels
.0
.insert(nodeid_handle, Label::new(handle))
.unwrap();
self.scope_begin();
self.process_function(function)?;
self.scope_end();
self.push_instruction(Instruction::ScalarNil);
self.emit_return()?;
}
Ok(())
}
fn compile_begin(&mut self) {
self.function_id += 1;
self.locals.push(Default::default());
self.upvalues.push(Default::default());
self.scope_depth.push(0);
}
fn compile_end(&mut self) {
self.function_id -= 1;
self.locals.pop();
self.upvalues.pop();
self.scope_depth.pop();
}
fn scope_begin(&mut self) {
*self.scope_depth.last_mut().unwrap() += 1;
}
fn scope_depth(&self) -> i32 {
*self.scope_depth.last().unwrap()
}
fn scope_depth_mut(&mut self) -> &mut i32 {
self.scope_depth.last_mut().unwrap()
}
fn scope_end(&mut self) {
*self.scope_depth_mut() -= 1;
let scope_depth = self.scope_depth();
let locals = &mut self.locals[self.function_id];
while locals
.last()
.map(|l| l.depth > scope_depth)
.unwrap_or(false)
{
let var = locals.pop().unwrap();
if var.captured {
self.program.bytecode.push(Instruction::CloseUpvalue as u8);
} else {
self.program.bytecode.push(Instruction::Pop as u8);
}
}
}
fn add_local(&mut self, name: &'a str) -> CompilationResult<u32> {
self.validate_var_name(name)?;
self.add_local_unchecked(name)
}
fn add_upvalue(
&mut self,
index: u8,
is_local: bool,
function_id: usize,
) -> CompilationResult<usize> {
let upvalues = &mut self.upvalues[function_id];
for (i, val) in upvalues.iter().enumerate() {
if val.index == index && val.is_local == is_local {
return Ok(i);
}
}
let i = upvalues.len();
upvalues.push(Upvalue { is_local, index });
Ok(i)
}
fn add_local_unchecked(&mut self, name: &'a str) -> CompilationResult<u32> {
let depth = self.scope_depth();
let locals = self.locals.last_mut().unwrap();
let result = locals.len();
locals
.try_push(Local {
name,
depth,
captured: false,
})
.map_err(|_| self.error(CompilationErrorPayload::TooManyLocals))?;
Ok(result as u32)
}
fn process_function(
&mut self,
FunctionIr {
arguments,
cards,
namespace,
imports,
..
}: &'a FunctionIr,
) -> CompilationResult<()> {
self.current_namespace = Cow::Borrowed(namespace);
self.current_imports = Cow::Borrowed(imports);
for param in arguments.iter().rev() {
self.add_local(param.as_str())?;
}
for (ic, card) in cards.iter().enumerate() {
self.current_index.pop_subindex();
self.current_index.push_subindex(ic as u32);
self.process_card(card)?;
}
Ok(())
}
fn emit_return(&mut self) -> CompilationResult<()> {
self.push_instruction(Instruction::Return);
Ok(())
}
fn encode_if_then(
&mut self,
skip_instr: Instruction,
then: impl FnOnce(&mut Self) -> CompilationResult<()>,
) -> CompilationResult<()> {
type Pos = i32;
debug_assert!(
matches!(
skip_instr,
Instruction::GotoIfTrue | Instruction::GotoIfFalse
),
"invalid skip instruction"
);
self.push_instruction(skip_instr);
let idx = self.program.bytecode.len();
write_to_vec(0 as Pos, &mut self.program.bytecode);
then(self)?;
unsafe {
let ptr = self.program.bytecode.as_mut_ptr().add(idx) as *mut Pos;
std::ptr::write_unaligned(ptr, self.program.bytecode.len() as Pos);
};
Ok(())
}
fn encode_jump(&mut self, function: &str) -> CompilationResult<()> {
let to = self.resolve_function(&self.jump_table, function)?;
write_to_vec(to.hash_key, &mut self.program.bytecode);
write_to_vec(to.arity, &mut self.program.bytecode);
Ok(())
}
fn resolve_function<'b>(
&self,
jump_table: &'b CaoHashMap<String, FunctionMeta>,
function: &str,
) -> CompilationResult<&'b FunctionMeta> {
let mut to = jump_table.get(function);
if to.is_none() {
let name = self
.current_namespace
.iter()
.flat_map(|x| [x.as_ref(), "."])
.chain(std::iter::once(function))
.collect::<String>();
to = jump_table.get(&name);
}
if to.is_none() {
if let Some((_, alias)) = self
.current_imports
.iter()
.find(|(import, _)| *import == function)
{
let (super_depth, suffix) = super_depth(alias);
let name = self
.current_namespace
.iter()
.take(self.current_namespace.len() - super_depth)
.flat_map(|x| [x.as_ref(), "."])
.chain(std::iter::once(suffix.unwrap_or(alias)))
.collect::<String>();
to = jump_table.get(&name);
}
}
if to.is_none() {
if let Some((prefix, suffix)) = function.split_once('.') {
if let Some((_, alias)) = self
.current_imports
.iter()
.find(|(import, _)| *import == prefix)
{
let (super_depth, s) = super_depth(alias);
let name = self
.current_namespace
.iter()
.take(self.current_namespace.len() - super_depth)
.flat_map(|x| [x.as_ref(), "."])
.chain([alias, ".", s.unwrap_or(suffix)].iter().copied())
.collect::<String>();
to = jump_table.get(&name);
}
}
}
to.ok_or_else(|| {
self.error(CompilationErrorPayload::InvalidJump {
dst: function.to_string(),
msg: None,
})
})
}
fn push_str(&mut self, data: &str) {
let handle = self.program.data.len() as u32;
write_to_vec(handle, &mut self.program.bytecode);
encode_str(data, &mut self.program.data);
}
fn resolve_upvalue(&mut self, name: &str, function_id: usize) -> CompilationResult<Variable> {
if function_id == 0 {
return Ok(Variable::Global);
}
let locals = &mut self.locals[function_id - 1];
for (i, local) in locals.iter_mut().enumerate() {
if local.name == name {
local.captured = true;
return self
.add_upvalue(i as u8, true, function_id)
.map(Variable::Upvalue);
}
}
if function_id == 0 {
Ok(Variable::Global)
} else {
let val = self.resolve_upvalue(name, function_id - 1)?;
if let Variable::Upvalue(i) = &val {
let i = self.add_upvalue(*i as u8, false, function_id)?;
return Ok(Variable::Upvalue(i));
}
Ok(val)
}
}
fn resolve_var(&mut self, name: &str) -> CompilationResult<Variable> {
self.validate_var_name(name)?;
for (i, local) in self.locals[self.function_id].iter_mut().enumerate().rev() {
if local.name == name {
return Ok(Variable::Local(i));
}
}
self.resolve_upvalue(name, self.function_id)
}
fn process_card(&mut self, card: &'a Card) -> CompilationResult<()> {
let card_byte_index = u32::try_from(self.program.bytecode.len())
.expect("Expected bytecode length to fit into 32 bits");
let nodeid_hash = self.current_index.as_handle();
self.program
.labels
.0
.insert(nodeid_hash, Label::new(card_byte_index))
.unwrap();
match card {
Card::CompositeCard(comp) => {
for (i, card) in comp.cards.iter().enumerate() {
self.current_index.push_subindex(i as u32);
self.process_card(card)?;
self.current_index.pop_subindex()
}
}
Card::ForEach(fe) => {
let ForEach {
i,
k,
v,
iterable: variable,
body,
} = fe.as_ref();
self.current_index.push_subindex(0);
self.process_card(variable)?;
self.current_index.pop_subindex();
self.scope_begin();
let loop_var = self.add_local_unchecked("")?;
let loop_item = self.add_local_unchecked("")?;
let v_index = self.add_local_unchecked("")?;
let k_index = self.add_local_unchecked("")?;
let i_index = self.add_local_unchecked("")?;
self.push_instruction(Instruction::BeginForEach);
write_to_vec(loop_var, &mut self.program.bytecode);
write_to_vec(loop_item, &mut self.program.bytecode);
write_to_vec(i_index, &mut self.program.bytecode);
write_to_vec(k_index, &mut self.program.bytecode);
write_to_vec(v_index, &mut self.program.bytecode);
let block_begin = self.program.bytecode.len() as i32;
self.push_instruction(Instruction::ForEach);
write_to_vec(loop_var, &mut self.program.bytecode);
write_to_vec(loop_item, &mut self.program.bytecode);
write_to_vec(i_index, &mut self.program.bytecode);
write_to_vec(k_index, &mut self.program.bytecode);
write_to_vec(v_index, &mut self.program.bytecode);
self.encode_if_then(Instruction::GotoIfFalse, |c| {
c.scope_begin();
if let Some(v) = v {
let v = c.add_local(v)?;
c.read_local_var(v_index);
c.write_local_var(v);
}
if let Some(k) = k {
let k = c.add_local(k)?;
c.read_local_var(k_index);
c.write_local_var(k);
}
if let Some(i) = i {
let i = c.add_local(i)?;
c.read_local_var(i_index);
c.write_local_var(i);
}
c.current_index.push_subindex(1);
c.process_card(body)?;
c.current_index.pop_subindex();
c.scope_end();
c.push_instruction(Instruction::Goto);
write_to_vec(block_begin, &mut c.program.bytecode);
Ok(())
})?;
self.scope_end();
}
Card::While(children) => {
let [condition, body] = &**children;
let block_begin = self.program.bytecode.len() as i32;
self.current_index.push_subindex(0);
self.process_card(condition)?;
self.current_index.pop_subindex();
self.current_index.push_subindex(1);
self.encode_if_then(Instruction::GotoIfFalse, |c| {
c.process_card(body)?;
c.push_instruction(Instruction::Goto);
write_to_vec(block_begin, &mut c.program.bytecode);
Ok(())
})?;
self.current_index.pop_subindex();
}
Card::Repeat(rep) => {
self.current_index.push_subindex(0);
self.compile_subexpr(slice::from_ref(&rep.n))?;
self.current_index.pop_subindex();
let i = &rep.i;
let repeat = &rep.body;
self.scope_begin();
let loop_n_index = self.add_local_unchecked("")?;
let loop_counter_index = self.add_local_unchecked("")?;
self.write_local_var(loop_n_index);
self.process_card(&Card::ScalarInt(0))?;
self.write_local_var(loop_counter_index);
let block_begin = self.program.bytecode.len() as i32;
self.read_local_var(loop_counter_index);
self.read_local_var(loop_n_index);
self.push_instruction(Instruction::Less);
self.encode_if_then(Instruction::GotoIfFalse, |c| {
c.scope_begin();
if let Some(var) = i {
let i_index = c.add_local(var)?;
c.read_local_var(loop_counter_index);
c.write_local_var(i_index);
}
c.current_index.push_subindex(1);
c.process_card(repeat)?;
c.current_index.pop_subindex();
c.scope_end();
c.process_card(&Card::ScalarInt(1))?;
c.read_local_var(loop_counter_index);
c.push_instruction(Instruction::Add);
c.write_local_var(loop_counter_index);
c.push_instruction(Instruction::Goto);
write_to_vec(block_begin, &mut c.program.bytecode);
Ok(())
})?;
self.scope_end();
}
Card::ReadVar(variable) => {
self.read_var_card(variable)?;
}
Card::SetVar(var) => {
self.compile_subexpr(slice::from_ref(&var.value))?;
let var = var.name.as_str();
match var.rsplit_once('.') {
Some((read_props, set_prop)) => {
self.read_var_card(read_props)?;
self.push_instruction(Instruction::StringLiteral);
self.push_str(set_prop);
self.push_instruction(Instruction::SetProperty);
}
None => {
let index = match self.resolve_var(var)? {
Variable::Local(i) => i as u32,
Variable::Global => self.add_local(var)?,
Variable::Upvalue(i) => {
self.write_upvalue(i as u32);
return Ok(());
}
};
self.write_local_var(index);
}
}
}
Card::SetGlobalVar(var) => {
self.compile_subexpr(slice::from_ref(&var.value))?;
self.push_instruction(Instruction::SetGlobalVar);
let variable = var.name.as_str();
let next_var = &mut self.next_var;
if variable.is_empty() {
return Err(self.error(CompilationErrorPayload::EmptyVariable));
}
let varhash = Handle::from_bytes(variable.as_bytes());
let id = self
.program
.variables
.ids
.entry(varhash)
.or_insert_with(move || {
let id = *next_var;
*next_var = VariableId(id.0 + 1);
id
});
self.program
.variables
.names
.entry(Handle::from_u32(id.0))
.or_insert_with(move || variable.to_string());
write_to_vec(*id, &mut self.program.bytecode);
}
Card::IfElse(children) => {
let [condition, then_card, else_card] = &**children;
self.compile_subexpr(slice::from_ref(condition))?;
let mut idx = 0;
self.current_index.push_subindex(1);
self.encode_if_then(Instruction::GotoIfFalse, |c| {
c.process_card(then_card)?;
c.push_instruction(Instruction::Goto);
idx = c.program.bytecode.len();
write_to_vec(0xEEFi32, &mut c.program.bytecode);
Ok(())
})?;
self.current_index.pop_subindex();
self.current_index.push_subindex(2);
self.process_card(else_card)?;
self.current_index.pop_subindex();
unsafe {
let ptr = self.program.bytecode.as_mut_ptr().add(idx) as *mut i32;
std::ptr::write_unaligned(ptr, self.program.bytecode.len() as i32);
}
}
Card::IfFalse(jmp) => {
let [cond, body] = &**jmp;
self.compile_subexpr(slice::from_ref(cond))?;
self.current_index.push_subindex(1);
self.encode_if_then(Instruction::GotoIfTrue, |c| c.process_card(body))?;
self.current_index.pop_subindex();
}
Card::IfTrue(jmp) => {
let [cond, body] = &**jmp;
self.compile_subexpr(slice::from_ref(cond))?;
self.current_index.push_subindex(1);
self.encode_if_then(Instruction::GotoIfFalse, |c| c.process_card(body))?;
self.current_index.pop_subindex();
}
Card::Call(jmp) => {
self.compile_subexpr(&jmp.args.0)?;
self.push_instruction(Instruction::FunctionPointer);
self.encode_jump(jmp.function_name.as_str())?;
self.push_instruction(Instruction::CallFunction);
}
Card::StringLiteral(c) => {
self.push_instruction(Instruction::StringLiteral);
self.push_str(c.as_str())
}
Card::CallNative(c) => {
self.compile_subexpr(&c.args.0)?;
let name = &c.name;
let key = Handle::from_str(name.as_str()).unwrap();
self.push_instruction(Instruction::CallNative);
write_to_vec(key, &mut self.program.bytecode);
}
Card::ScalarInt(s) => {
self.push_instruction(Instruction::ScalarInt);
write_to_vec(*s, &mut self.program.bytecode);
}
Card::ScalarFloat(s) => {
self.push_instruction(Instruction::ScalarFloat);
write_to_vec(*s, &mut self.program.bytecode);
}
Card::Function(fname) => {
self.push_instruction(Instruction::FunctionPointer);
self.encode_jump(fname)?;
}
Card::Closure(embedded_function) => {
self.push_instruction(Instruction::Goto);
let goto_index = self.program.bytecode.len();
write_to_vec(0xEEFi32, &mut self.program.bytecode);
self.compile_begin();
const CLOSURE_MASK: u64 = 0xEFEFEFEF;
let function_handle =
self.current_index.as_handle() + Handle::from_u64(CLOSURE_MASK);
let arity = embedded_function.arguments.len() as u32;
let handle = u32::try_from(self.program.bytecode.len())
.expect("bytecode length to fit into 32 bits");
self.program
.labels
.0
.insert(function_handle, Label::new(handle))
.unwrap();
self.scope_begin();
for param in embedded_function.arguments.iter().rev() {
self.add_local(param.as_str())?;
}
self.compile_subexpr(&embedded_function.cards)?;
self.scope_end();
self.push_instruction(Instruction::ScalarNil);
self.emit_return()?;
unsafe {
let ptr = self.program.bytecode.as_mut_ptr().add(goto_index) as *mut i32;
std::ptr::write_unaligned(ptr, self.program.bytecode.len() as i32);
}
self.push_instruction(Instruction::Closure);
write_to_vec(function_handle, &mut self.program.bytecode);
write_to_vec(arity, &mut self.program.bytecode);
let upvalues = std::mem::take(&mut self.upvalues[self.function_id]);
for upvalue in upvalues {
self.push_instruction(Instruction::CopyLast);
self.push_instruction(Instruction::RegisterUpvalue);
write_to_vec(upvalue.index, &mut self.program.bytecode);
write_to_vec(upvalue.is_local as u8, &mut self.program.bytecode);
}
self.compile_end();
}
Card::NativeFunction(fname) => {
self.push_instruction(Instruction::NativeFunctionPointer);
self.push_str(fname.as_str());
}
Card::Array(expressions) => {
self.push_instruction(Instruction::InitTable);
let table_var = self.add_local_unchecked("")?;
self.write_local_var(table_var);
for (i, card) in expressions.iter().enumerate() {
self.push_instruction(Instruction::ScalarNil);
self.current_index.push_subindex(i as u32);
self.process_card(card)?;
self.current_index.pop_subindex();
self.read_local_var(table_var);
self.push_instruction(Instruction::AppendTable);
}
self.read_local_var(table_var);
}
Card::Len(expr) => {
self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
self.push_instruction(Instruction::Len);
}
Card::Return(expr) => {
self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
self.emit_return()?;
}
Card::Not(expr) => {
self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
self.push_instruction(Instruction::Not);
}
Card::Get(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::NthRow);
}
Card::And(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::And);
}
Card::Or(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Or);
}
Card::Xor(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Xor);
}
Card::Equals(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Equals);
}
Card::Less(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Less);
}
Card::LessOrEq(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::LessOrEq);
}
Card::NotEquals(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::NotEquals);
}
Card::Add(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Add);
}
Card::Sub(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Sub);
}
Card::Mul(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Mul);
}
Card::Div(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::Div);
}
Card::GetProperty(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::GetProperty);
}
Card::SetProperty(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::SetProperty);
}
Card::AppendTable(expr) => {
self.compile_subexpr(expr.as_ref())?;
self.push_instruction(Instruction::AppendTable);
}
Card::PopTable(expr) => {
self.compile_subexpr(slice::from_ref(expr.card.as_ref()))?;
self.push_instruction(Instruction::PopTable);
}
Card::DynamicCall(jump) => {
self.compile_subexpr(jump.args.0.as_slice())?;
self.current_index.push_subindex(jump.args.0.len() as u32);
self.process_card(&jump.function)?;
self.current_index.pop_subindex();
self.push_instruction(Instruction::CallFunction);
}
Card::ScalarNil => {
self.push_instruction(Instruction::ScalarNil);
}
Card::Abort => {
self.push_instruction(Instruction::Exit);
}
Card::CreateTable => {
self.push_instruction(Instruction::InitTable);
}
Card::Comment(_) => {
}
}
Ok(())
}
fn read_var_card(&mut self, mut variable: &str) -> CompilationResult<()> {
let props = match variable.split_once('.') {
Some((v, props)) => {
variable = v;
props
}
None => "",
};
let scope = self.resolve_var(variable)?;
match scope {
Variable::Local(index) => {
self.read_local_var(index as u32);
}
Variable::Upvalue(index) => {
self.read_upvalue(index as u32);
}
Variable::Global => {
let next_var = &mut self.next_var;
let varhash = Handle::from_bytes(variable.as_bytes());
let id = *self
.program
.variables
.ids
.entry(varhash)
.or_insert_with(move || {
let id = *next_var;
*next_var = VariableId(id.0 + 1);
id
});
self.program
.variables
.names
.entry(Handle::from_u32(id.0))
.or_insert_with(|| variable.to_string());
self.push_instruction(Instruction::ReadGlobalVar);
write_to_vec(id, &mut self.program.bytecode);
}
}
for prop in props.split('.').filter(|p| !p.is_empty()) {
self.push_instruction(Instruction::StringLiteral);
self.push_str(prop);
self.push_instruction(Instruction::GetProperty);
}
Ok(())
}
fn read_local_var(&mut self, index: u32) {
self.push_instruction(Instruction::ReadLocalVar);
write_to_vec(index, &mut self.program.bytecode);
}
fn write_local_var(&mut self, index: u32) {
self.push_instruction(Instruction::SetLocalVar);
write_to_vec(index, &mut self.program.bytecode);
}
fn read_upvalue(&mut self, index: u32) {
self.push_instruction(Instruction::ReadUpvalue);
write_to_vec(index, &mut self.program.bytecode);
}
fn write_upvalue(&mut self, index: u32) {
self.push_instruction(Instruction::SetUpvalue);
write_to_vec(index, &mut self.program.bytecode);
}
fn validate_var_name(&self, name: &str) -> CompilationResult<()> {
if name.is_empty() {
return Err(self.error(CompilationErrorPayload::EmptyVariable));
}
Ok(())
}
fn push_instruction(&mut self, instruction: Instruction) {
self.program
.trace
.insert(self.program.bytecode.len() as u32, self.trace())
.unwrap();
self.program.bytecode.push(instruction as u8);
}
fn compile_subexpr(&mut self, cards: &'a [Card]) -> CompilationResult<()> {
for (i, card) in cards.iter().enumerate() {
self.current_index.push_subindex(i as u32);
self.process_card(card)?;
self.current_index.pop_subindex();
}
Ok(())
}
}
fn super_depth(import: &str) -> (usize, Option<&str>) {
let mut super_pog = import.split_once("super.");
let mut super_cnt = 0;
let mut suffix = None;
while let Some((_sup_pre, sup_post)) = super_pog {
super_cnt += 1;
super_pog = sup_post.split_once("super.");
suffix = Some(sup_post);
}
(super_cnt, suffix)
}