lucia-lang 0.2.0

Lucia programming language
Documentation
//! The Code that runs in LVM.

use std::fmt;

use gc_arena::Collect;

pub use super::ast::FunctionKind;
use super::{
    analyzer,
    codegen::{self, SyntaxError},
    lexer,
    opcode::OpCode,
    parser,
};

/// A Code, generated by codegen and executed in LVM.
#[derive(Debug, Clone, Collect)]
#[collect(require_static)]
pub struct Code {
    /// Name of parameters.
    pub params: Vec<String>,
    /// Name of variadic parameter.
    pub variadic: Option<String>,
    /// Function kind.
    pub kind: FunctionKind,
    /// Bytecode, a list of OpCodes.
    pub code: Vec<OpCode>,

    /// List of constants used in the bytecode.
    pub consts: Vec<ConstValue>,
    /// List of local names.
    pub local_names: Vec<String>,
    /// List of global names.
    pub global_names: Vec<String>,
    /// List of Upvalue information.
    pub upvalue_names: Vec<(String, usize, usize)>,

    /// The count of upvalues defined in the function.
    pub def_upvalue_count: usize,
    /// The required virtual machine stack space.
    pub stack_size: usize,
}

impl fmt::Display for Code {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(v) = &self.variadic {
            writeln!(f, "params: ({}), *{}", self.params.join(", "), v)?;
        } else {
            writeln!(f, "params: ({})", self.params.join(", "))?;
        }
        writeln!(f, "kind: {}", self.kind)?;
        writeln!(f, "stack_size: {}", self.stack_size)?;
        let mut code_str = String::new();
        for (i, code) in self.code.iter().enumerate() {
            code_str.push_str(&format!(
                "{:>12} {}{}\n",
                i,
                code,
                match code {
                    OpCode::LoadLocal(i) | OpCode::StoreLocal(i) =>
                        format!(" ({})", self.local_names[*i]),
                    OpCode::LoadGlobal(i) | OpCode::StoreGlobal(i) =>
                        format!(" ({})", self.global_names[*i]),
                    OpCode::LoadUpvalue(i) | OpCode::StoreUpvalue(i) => {
                        let t = &self.upvalue_names[*i];
                        format!(" ({}, {}, {})", t.0, t.1, t.2)
                    }
                    OpCode::LoadConst(i) | OpCode::Import(i) | OpCode::ImportFrom(i) =>
                        format!(" ({})", self.consts[*i]),
                    _ => "".to_string(),
                }
            ));
        }
        write!(f, "code:\n{}", code_str)
    }
}

impl PartialEq for Code {
    fn eq(&self, _: &Self) -> bool {
        false
    }
}

impl fmt::Display for FunctionKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FunctionKind::Function => write!(f, "Function"),
            FunctionKind::Closure => write!(f, "Closure"),
            FunctionKind::Do => write!(f, "Do"),
        }
    }
}

/// The const value.
#[derive(Debug, Clone, PartialEq)]
pub enum ConstValue {
    /// "null"
    Null,
    /// "true", "false"
    Bool(bool),
    /// "12", "0o100", "0b110"
    Int(i64),
    /// "12.34", "0b100.100"
    Float(f64),
    /// ""abc"", ""abc"
    Str(String),
    /// A function.
    Func(Code),
}

impl fmt::Display for ConstValue {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Null => write!(f, "null"),
            Self::Bool(v) => write!(f, "{}", v),
            Self::Int(v) => write!(f, "{}", v),
            Self::Float(v) => write!(f, "{}", v),
            Self::Str(v) => write!(f, "{}", v),
            Self::Func(_) => write!(f, "<code>"),
        }
    }
}

impl TryFrom<&str> for Code {
    type Error = SyntaxError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        codegen::gen_code(analyzer::analyze(
            parser::Parser::new(&mut lexer::tokenize(value)).parse()?,
        ))
    }
}

impl TryFrom<&String> for Code {
    type Error = SyntaxError;

    fn try_from(value: &String) -> Result<Self, Self::Error> {
        codegen::gen_code(analyzer::analyze(
            parser::Parser::new(&mut lexer::tokenize(value)).parse()?,
        ))
    }
}