glyph_runtime/
compiler.rs1use 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
31pub type CompileResult<T> = Result<T, CompileError>;
33
34#[derive(Debug)]
36pub struct CompiledProgram {
37 pub name: String,
39 pub requires: Vec<String>,
41 pub bytecode: Vec<Vec<crate::Instruction>>,
43}
44
45pub fn compile(source: &str) -> CompileResult<CompiledProgram> {
47 let module = parse_glyph(source).map_err(|e| CompileError::ParseError(format!("{e:?}")))?;
49
50 let ir_module = ast_to_ir(&module);
52
53 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
63pub fn compile_and_run(source: &str) -> CompileResult<Value> {
65 let program = compile(source)?;
66
67 let mut capabilities = CapabilitySet::new();
69 for cap in &program.requires {
70 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" => {} _ => {} }
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
91pub 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}