use crate::ast::*;
use crate::opcodes::*;
pub struct Compiler<'a> {
module: &'a ModuleSoA,
buf: Vec<u8>,
}
impl<'a> Compiler<'a> {
pub fn new(module: &'a ModuleSoA) -> Self {
Self {
module,
buf: Vec::with_capacity(module.hierarchy.len() * 32 + std::mem::size_of_val(&MAGIC_HEADER)),
}
}
pub fn compile(mut self, root_nodes: &[NodeId]) -> Vec<u8> {
self.buf.extend_from_slice(&MAGIC_HEADER);
self.compile_span(root_nodes);
self.buf
}
#[inline]
fn compile_span(&mut self, nodes: &[NodeId]) {
for &node in nodes {
match node {
NodeId::Element(id) => self.compile_element(id),
NodeId::Directive(id) => self.compile_directive(id),
}
}
}
fn compile_block(&mut self, span: (u32, u32)) {
let start = span.0 as usize;
let len = span.1 as usize;
let nodes = &self.module.hierarchy[start..start + len];
self.compile_span(nodes);
self.buf.push(OP_END_BLOCK);
}
fn compile_element(&mut self, id: u32) {
let idx = id as usize;
self.buf.push(OP_ELEM_PUSH);
self.write_string(&self.module.elem_types[idx]);
let (p_start, p_len) = self.module.elem_prop_spans[idx];
let p_start = p_start as usize;
let p_end = p_start + p_len as usize;
for i in p_start..p_end {
self.compile_property(&self.module.prop_keys[i], &self.module.prop_values[i]);
}
if let Some(content) = &self.module.elem_content[idx] {
self.buf.push(OP_CONTENT);
self.compile_value(content);
}
let child_span = self.module.elem_child_spans[idx];
if child_span.1 > 0 {
let start = child_span.0 as usize;
let len = child_span.1 as usize;
let nodes = &self.module.hierarchy[start..start + len];
self.compile_span(nodes);
}
self.buf.push(OP_ELEM_POP);
}
fn compile_directive(&mut self, id: u32) {
match &self.module.directives[id as usize] {
Directive::Version(v) => {
self.buf.push(OP_VERSION);
self.write_i64(*v);
}
Directive::Style(s) => {
self.buf.push(OP_STYLE);
self.write_string(s);
}
Directive::Global { name, value } => {
self.buf.push(OP_GLOBAL);
self.write_string(name);
self.compile_value(value);
}
Directive::Singleton { name, prop_span } => {
self.buf.push(OP_SINGLETON);
self.write_string(name);
self.write_u32(prop_span.1);
let start = prop_span.0 as usize;
let end = start + prop_span.1 as usize;
for i in start..end {
self.write_string(&self.module.prop_keys[i]);
self.compile_value(&self.module.prop_values[i]);
}
}
Directive::Component { name, params, child_span } => {
self.buf.push(OP_COMPONENT);
self.write_string(name);
self.write_u32(params.len() as u32);
for (pname, ptype) in params {
self.write_string(pname);
self.write_string(ptype);
}
self.compile_block(*child_span);
}
Directive::Let { name, value } => {
self.buf.push(OP_LET);
self.write_string(name);
self.compile_value(value);
}
Directive::If { condition, child_span, else_span } => {
self.buf.push(OP_IF);
self.compile_value(condition);
self.compile_block(*child_span);
if let Some(es) = else_span {
self.buf.push(1);
self.compile_block(*es);
} else {
self.buf.push(0);
}
}
Directive::Each { item, collection, child_span } => {
self.buf.push(OP_EACH);
self.write_string(item);
self.compile_value(collection);
self.compile_block(*child_span);
}
Directive::On { event, args, child_span } => {
self.buf.push(OP_ON);
self.write_string(event);
self.write_u32(args.len() as u32);
for (k, v) in args {
self.write_string(k);
self.compile_value(v);
}
self.compile_block(*child_span);
}
Directive::RheiBlock(code) => {
self.buf.push(OP_RHEI_BLK);
self.write_string(code);
}
Directive::StyleRule { selector, prop_span } => {
self.buf.push(OP_STYLE_RULE);
self.write_string(selector);
self.write_u32(prop_span.1);
let start = prop_span.0 as usize;
let end = start + prop_span.1 as usize;
for i in start..end {
self.write_string(&self.module.prop_keys[i]);
self.compile_value(&self.module.prop_values[i]);
}
}
Directive::StyleAnim { name, frames } => {
self.buf.push(OP_STYLE_ANIM);
self.write_string(name);
self.write_u32(frames.len() as u32);
for (step, span) in frames {
self.write_string(step);
self.write_u32(span.1);
let start = span.0 as usize;
let end = start + span.1 as usize;
for i in start..end {
self.write_string(&self.module.prop_keys[i]);
self.compile_value(&self.module.prop_values[i]);
}
}
}
}
}
#[inline]
fn compile_property(&mut self, key: &str, val: &Value) {
let opcode = match val {
Value::String(_) => OP_PROP_STR,
Value::Int(_) => OP_PROP_INT,
Value::Float(_) => OP_PROP_FLOAT,
Value::Bool(_) => OP_PROP_BOOL,
Value::Color(_) => OP_PROP_COLOR,
Value::FsPath(_) => OP_PROP_FSPATH,
Value::Variable(_) => OP_PROP_VAR,
Value::Rhei(_) => OP_PROP_RHEI,
Value::Null => OP_PROP_NULL,
Value::Array(_) => OP_PROP_ARRAY,
Value::Call(_, _) => OP_PROP_CALL,
Value::Unit(_, _) => OP_PROP_UNIT,
Value::Ident(_) => OP_PROP_IDENT,
};
self.buf.push(opcode);
self.write_string(key);
match val {
Value::Null => {}
_ => self.compile_value_data(val),
}
}
#[inline]
fn compile_value(&mut self, val: &Value) {
let opcode = match val {
Value::String(_) => OP_PROP_STR,
Value::Int(_) => OP_PROP_INT,
Value::Float(_) => OP_PROP_FLOAT,
Value::Bool(_) => OP_PROP_BOOL,
Value::Color(_) => OP_PROP_COLOR,
Value::FsPath(_) => OP_PROP_FSPATH,
Value::Variable(_) => OP_PROP_VAR,
Value::Rhei(_) => OP_PROP_RHEI,
Value::Null => OP_PROP_NULL,
Value::Array(_) => OP_PROP_ARRAY,
Value::Call(_, _) => OP_PROP_CALL,
Value::Unit(_, _) => OP_PROP_UNIT,
Value::Ident(_) => OP_PROP_IDENT,
};
self.buf.push(opcode);
self.compile_value_data(val);
}
#[inline]
fn compile_value_data(&mut self, val: &Value) {
match val {
Value::String(s) | Value::Color(s) | Value::FsPath(s) | Value::Variable(s) | Value::Rhei(s) => {
self.write_string(s);
}
Value::Int(i) => self.write_i64(*i),
Value::Float(f) => self.write_f64(*f),
Value::Bool(b) => self.buf.push(*b as u8),
Value::Null => {}
Value::Array(arr) => {
self.write_u32(arr.len() as u32);
for v in arr {
self.compile_value(v);
}
}
Value::Ident(s) => self.write_string(s),
Value::Unit(num, unit) => {
self.write_f64(*num);
self.write_string(unit);
}
Value::Call(name, args) => {
self.write_string(name);
self.write_u32(args.len() as u32);
for arg in args {
self.compile_value(arg);
}
}
}
}
#[inline(always)]
fn write_string(&mut self, s: &str) {
let bytes = s.as_bytes();
self.write_u32(bytes.len() as u32);
self.buf.extend_from_slice(bytes);
}
#[inline(always)]
fn write_u32(&mut self, v: u32) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
#[inline(always)]
fn write_i64(&mut self, v: i64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
#[inline(always)]
fn write_f64(&mut self, v: f64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
}