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::new(),
}
}
pub fn compile(mut self, root_nodes: &[NodeId]) -> Vec<u8> {
self.buf.extend_from_slice(&MAGIC_HEADER);
self.compile_span(root_nodes);
self.buf
}
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;
let typ = &self.module.elem_types[idx];
self.buf.push(OP_ELEM_PUSH);
self.write_string(typ);
let (p_start, p_len) = self.module.elem_prop_spans[idx];
for i in p_start..(p_start + p_len) {
let key = &self.module.prop_keys[i as usize];
let val = &self.module.prop_values[i as usize];
self.compile_property(key, val);
}
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 {
self.compile_block(child_span);
}
self.buf.push(OP_ELEM_POP);
}
fn compile_directive(&mut self, id: u32) {
let dir = &self.module.directives[id as usize];
match dir {
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, len) = *prop_span;
for i in start..(start + len) {
let key = &self.module.prop_keys[i as usize];
let val = &self.module.prop_values[i as usize];
self.write_string(key);
self.compile_value(val);
}
}
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);
}
}
}
fn compile_property(&mut self, key: &str, val: &Value) {
match val {
Value::String(s) => {
self.buf.push(OP_PROP_STR);
self.write_string(key);
self.write_string(s);
}
Value::Int(i) => {
self.buf.push(OP_PROP_INT);
self.write_string(key);
self.write_i64(*i);
}
Value::Float(f) => {
self.buf.push(OP_PROP_FLOAT);
self.write_string(key);
self.write_f64(*f);
}
Value::Bool(b) => {
self.buf.push(OP_PROP_BOOL);
self.write_string(key);
self.buf.push(if *b { 1 } else { 0 });
}
Value::Color(c) => {
self.buf.push(OP_PROP_COLOR);
self.write_string(key);
self.write_string(c);
}
Value::FsPath(p) => {
self.buf.push(OP_PROP_FSPATH);
self.write_string(key);
self.write_string(p);
}
Value::Variable(v) => {
self.buf.push(OP_PROP_VAR);
self.write_string(key);
self.write_string(v);
}
Value::Rhei(r) => {
self.buf.push(OP_PROP_RHEI);
self.write_string(key);
self.write_string(r);
}
}
}
fn compile_value(&mut self, val: &Value) {
match val {
Value::String(s) => {
self.buf.push(OP_PROP_STR);
self.write_string(s);
}
Value::Int(i) => {
self.buf.push(OP_PROP_INT);
self.write_i64(*i);
}
Value::Float(f) => {
self.buf.push(OP_PROP_FLOAT);
self.write_f64(*f);
}
Value::Bool(b) => {
self.buf.push(OP_PROP_BOOL);
self.buf.push(if *b { 1 } else { 0 });
}
Value::Color(c) => {
self.buf.push(OP_PROP_COLOR);
self.write_string(c);
}
Value::FsPath(p) => {
self.buf.push(OP_PROP_FSPATH);
self.write_string(p);
}
Value::Variable(v) => {
self.buf.push(OP_PROP_VAR);
self.write_string(v);
}
Value::Rhei(r) => {
self.buf.push(OP_PROP_RHEI);
self.write_string(r);
}
}
}
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);
}
fn write_u32(&mut self, v: u32) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
fn write_i64(&mut self, v: i64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
fn write_f64(&mut self, v: f64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
}