glt 0.1.4

Glint compiler library
Documentation
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,
            // glts
            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,
            // glts
            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());
    }
}