glyph_runtime/
compiler.rs

1//! High-level compiler that orchestrates the compilation pipeline
2//!
3//! This module provides the main interface for compiling Glyph source code
4//! into executable bytecode and running it on the VM.
5
6use crate::{
7    ast_to_ir::ast_to_ir,
8    ir_to_bytecode::ir_to_bytecode,
9    vm::{VMConfig, VMError, VM},
10    CapabilitySet,
11};
12use glyph_parser::parse_glyph;
13use glyph_types::Value;
14use thiserror::Error;
15
16#[derive(Debug, Error)]
17pub enum CompileError {
18    #[error("Parse error: {0}")]
19    ParseError(String),
20
21    #[error("IR generation error: {0}")]
22    IrError(String),
23
24    #[error("Bytecode generation error: {0}")]
25    BytecodeError(String),
26
27    #[error("VM error: {0}")]
28    VmError(#[from] VMError),
29}
30
31/// Result type for compilation operations
32pub type CompileResult<T> = Result<T, CompileError>;
33
34/// Compiled Glyph program ready for execution
35#[derive(Debug)]
36pub struct CompiledProgram {
37    /// Program name
38    pub name: String,
39    /// Required capabilities
40    pub requires: Vec<String>,
41    /// Bytecode for all functions
42    pub bytecode: Vec<Vec<crate::Instruction>>,
43}
44
45/// Compile Glyph source code to bytecode
46pub fn compile(source: &str) -> CompileResult<CompiledProgram> {
47    // Parse source code
48    let module = parse_glyph(source).map_err(|e| CompileError::ParseError(format!("{e:?}")))?;
49
50    // Convert AST to IR
51    let ir_module = ast_to_ir(&module);
52
53    // Compile IR to bytecode
54    let bytecode = ir_to_bytecode(&ir_module);
55
56    Ok(CompiledProgram {
57        name: ir_module.program.name,
58        requires: ir_module.program.requires,
59        bytecode,
60    })
61}
62
63/// Compile and run Glyph source code with default configuration
64pub fn compile_and_run(source: &str) -> CompileResult<Value> {
65    let program = compile(source)?;
66
67    // Create VM with required capabilities
68    let mut capabilities = CapabilitySet::new();
69    for cap in &program.requires {
70        // Map capability names to capability types
71        match cap.as_str() {
72            "audio.speak" => capabilities.grant(crate::Capability::AudioSpeak),
73            "display.visual" => capabilities.grant(crate::Capability::DisplayChart),
74            "network.fetch" => capabilities.grant(crate::Capability::NetworkFetch("*".to_string())),
75            "ui.interact" => {} // No direct UI interact capability yet
76            _ => {}             // Unknown capability - skip
77        }
78    }
79
80    let config = VMConfig {
81        capabilities,
82        ..Default::default()
83    };
84
85    let mut vm = VM::new(config);
86    vm.load_bytecode(program.bytecode);
87
88    vm.execute().map_err(CompileError::from)
89}
90
91/// Compile and run with custom VM configuration
92pub fn compile_and_run_with_config(source: &str, config: VMConfig) -> CompileResult<Value> {
93    let program = compile(source)?;
94
95    let mut vm = VM::new(config);
96    vm.load_bytecode(program.bytecode);
97
98    vm.execute().map_err(CompileError::from)
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_compile_simple_program() {
107        let source = r#"
108@program(name="test")
109
110def main():
111    return 42
112"#;
113
114        let program = compile(source).unwrap();
115        assert_eq!(program.name, "test");
116        assert!(!program.bytecode.is_empty());
117    }
118
119    #[test]
120    fn test_compile_and_run() {
121        let source = r#"
122@program(name="test")
123
124def main():
125    return 42
126"#;
127
128        let result = compile_and_run(source).unwrap();
129        assert_eq!(result, Value::Int(42));
130    }
131
132    #[test]
133    fn test_arithmetic_program() {
134        let source = r#"
135@program(name="calc")
136
137def main():
138    let x = 10
139    let y = 5
140    return x + y
141"#;
142
143        let result = compile_and_run(source).unwrap();
144        assert_eq!(result, Value::Int(15));
145    }
146
147    #[test]
148    fn test_function_call() {
149        let source = r#"
150@program(name="funcs")
151
152def add(a: int, b: int) -> int:
153    return a + b
154
155def main():
156    return add(10, 20)
157"#;
158
159        let result = compile_and_run(source).unwrap();
160        assert_eq!(result, Value::Int(30));
161    }
162}