use crate::{
ast_to_ir::ast_to_ir,
ir_to_bytecode::ir_to_bytecode,
vm::{VMConfig, VMError, VM},
CapabilitySet,
};
use glyph_parser::parse_glyph;
use glyph_types::Value;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum CompileError {
#[error("Parse error: {0}")]
ParseError(String),
#[error("IR generation error: {0}")]
IrError(String),
#[error("Bytecode generation error: {0}")]
BytecodeError(String),
#[error("VM error: {0}")]
VmError(#[from] VMError),
}
pub type CompileResult<T> = Result<T, CompileError>;
#[derive(Debug)]
pub struct CompiledProgram {
pub name: String,
pub requires: Vec<String>,
pub bytecode: Vec<Vec<crate::Instruction>>,
}
pub fn compile(source: &str) -> CompileResult<CompiledProgram> {
let module = parse_glyph(source).map_err(|e| CompileError::ParseError(format!("{e:?}")))?;
let ir_module = ast_to_ir(&module);
let bytecode = ir_to_bytecode(&ir_module);
Ok(CompiledProgram {
name: ir_module.program.name,
requires: ir_module.program.requires,
bytecode,
})
}
pub fn compile_and_run(source: &str) -> CompileResult<Value> {
let program = compile(source)?;
let mut capabilities = CapabilitySet::new();
for cap in &program.requires {
match cap.as_str() {
"audio.speak" => capabilities.grant(crate::Capability::AudioSpeak),
"display.visual" => capabilities.grant(crate::Capability::DisplayChart),
"network.fetch" => capabilities.grant(crate::Capability::NetworkFetch("*".to_string())),
"ui.interact" => {} _ => {} }
}
let config = VMConfig {
capabilities,
..Default::default()
};
let mut vm = VM::new(config);
vm.load_bytecode(program.bytecode);
vm.execute().map_err(CompileError::from)
}
pub fn compile_and_run_with_config(source: &str, config: VMConfig) -> CompileResult<Value> {
let program = compile(source)?;
let mut vm = VM::new(config);
vm.load_bytecode(program.bytecode);
vm.execute().map_err(CompileError::from)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compile_simple_program() {
let source = r#"
@program(name="test")
def main():
return 42
"#;
let program = compile(source).unwrap();
assert_eq!(program.name, "test");
assert!(!program.bytecode.is_empty());
}
#[test]
fn test_compile_and_run() {
let source = r#"
@program(name="test")
def main():
return 42
"#;
let result = compile_and_run(source).unwrap();
assert_eq!(result, Value::Int(42));
}
#[test]
fn test_arithmetic_program() {
let source = r#"
@program(name="calc")
def main():
let x = 10
let y = 5
return x + y
"#;
let result = compile_and_run(source).unwrap();
assert_eq!(result, Value::Int(15));
}
#[test]
fn test_function_call() {
let source = r#"
@program(name="funcs")
def add(a: int, b: int) -> int:
return a + b
def main():
return add(10, 20)
"#;
let result = compile_and_run(source).unwrap();
assert_eq!(result, Value::Int(30));
}
}