use crate::parser::Ast; pub mod backend; use std::fmt; #[cfg(test)] mod tests; #[derive(Debug, PartialEq, Default, Clone)] pub enum Capability { #[default] Shader } #[derive(Debug, PartialEq, Default, Clone)] pub enum ExecutionMode { #[default] OriginUpperLeft } #[derive(Debug, PartialEq, Default, Clone)] pub enum ExecutionModel { #[default] Fragment, Vertex, } #[derive(Debug, PartialEq, Default, Clone)] pub struct EntryPoint { pub execution_model: ExecutionModel, pub execution_mode: ExecutionMode, pub name: String, pub interface: Vec, } #[derive(Debug, PartialEq, Default, Clone)] pub enum AddressingModel { #[default] Logical, Physical32, Physical64, PhysicalStorageBuffer64 } #[derive(Debug, PartialEq, Default, Clone)] pub enum MemoryModel { #[default] GLSL450, OpenCL, VulkanKHR, Simple } #[derive(Debug, PartialEq, Default, Clone)] pub struct Memory { pub addressing_model: AddressingModel, pub memory_model: MemoryModel, } #[derive(Debug, PartialEq, Default, Clone)] pub enum BuiltinDecoration { #[default] FragCoord, } #[derive(Debug, PartialEq, Clone)] pub enum Decoration { BuiltIn(BuiltinDecoration), Location(u32), } #[derive(Debug, PartialEq, Default, Clone)] pub enum StorageClass { #[default] Undefined, Input, Output, } impl fmt::Display for StorageClass { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { StorageClass::Undefined => write!(f, "Undefined"), StorageClass::Input => write!(f, "Input"), StorageClass::Output => write!(f, "Output"), } } } #[derive(Debug, PartialEq, Default, Clone)] pub struct GlobalVariable { pub name: String, pub typ: String, pub storage_class: StorageClass, pub decorations: Vec, } #[derive(Debug, PartialEq, Default, Clone)] pub struct Instruction { pub result_id: Option, pub op: String, pub operands: Vec, } #[derive(Debug, PartialEq, Default, Clone)] pub struct Function { pub name: String, pub return_type: String, pub arguments: Vec, pub body: Option>, pub ast: Option, } #[derive(Debug, PartialEq, Default, Clone)] pub struct Import { pub name: String, pub value: String, } #[derive(Debug, PartialEq, Default, Clone)] pub struct Module { pub capabilities: Vec, pub entry_points: Vec, pub globals: Vec, pub functions: Vec, pub memory_model: Memory, pub imports: Vec, } pub fn meta_compile(ast: &mut Ast) -> Module { let mut module: Module = Module::default(); match ast { Ast::Root(root) => { for node in root { match node { Ast::List(list) => { let keyword = list[0].clone(); match keyword { Ast::Symbol(sym) => { match sym.as_str() { "module" => { let cap = list[1].clone(); let exec = list[2].clone(); let memory = list[3].clone(); module.memory_model = Memory { addressing_model: AddressingModel::Logical, memory_model: MemoryModel::GLSL450, }; module.capabilities.push(Capability::Shader); assert_eq!(exec, Ast::Symbol("Logical".to_string())); assert_eq!(memory, Ast::Symbol("GLSL450".to_string())); assert_eq!(cap, Ast::Symbol("Shader".to_string())); } "import" => { let name = match &list[1] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[1]), }; let value = match &list[2] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[2]), }; module.imports.push(Import { name: name.to_string(), value: value.to_string(), }); } "bind" => { let name_and_type = match &list[1] { Ast::List(l) => l, _ => panic!("Expected list! {:?}", list[1]), }; let name_and_type: String = name_and_type.iter().map(|x| { match x { Ast::Symbol(s) => s.to_string(), _ => panic!("Expected symbol! {:?}", x), } }).collect(); // name_and_type is of the name:type format, like foo:f32 let name: String = name_and_type.split(":").collect::>()[0].to_string(); let typ: String = name_and_type.split(":").collect::>()[1].to_string(); let bind = match &list[2] { Ast::List(l) => l, _ => panic!("Expected list! {:?}", list[2]), }; let bind: Vec = bind.iter().map(|x| { match x { Ast::Symbol(s) => s.to_string(), _ => panic!("Expected symbol! {:?}", x), } }).collect(); let bind_name = match bind[0].as_str() { "BuiltIn" => Decoration::BuiltIn(BuiltinDecoration::FragCoord), "Location" => Decoration::Location(bind[1].parse::().unwrap()), _ => panic!("Unknown bind! {:?}", bind), }; let mut exists = false; for var in &module.globals { if var.name == name { exists = true; } } if !exists { module.globals.push(GlobalVariable { name: name, typ: typ, storage_class: StorageClass::Undefined, decorations: vec![bind_name], }); } } "dec" => { let name_and_type = match &list[1] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[1]), }; let name: String = name_and_type.split(":").collect::>()[0].to_string(); let typ: String = name_and_type.split(":").collect::>()[1].to_string(); let storage_class = match &list[2] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[2]), }; let mut exists = false; for var in &module.globals { if var.name.as_str() == name.as_str() { exists = true; } } let storage_class = match storage_class.as_str() { "Input" => StorageClass::Input, "Output" => StorageClass::Output, _ => StorageClass::Undefined, }; if !exists { module.globals.push(GlobalVariable { name: name.to_string(), typ: typ.to_string(), storage_class: storage_class.clone(), decorations: vec![], }); } if exists { module.globals.iter_mut() .find(|x| x.name.as_str() == name.as_str()) .unwrap().storage_class = storage_class.clone(); } } "entry" => { let name = match &list[1] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[1]), }; let exec_model = match &list[2] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[2]), }; let exec_mode = match &list[3] { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", list[3]), }; let interface = match &list[4] { Ast::List(l) => l, _ => panic!("Expected list! {:?}", list[4]), }; let interface: Vec = interface.iter().map(|x| { match x { Ast::Symbol(s) => { s.to_string().replace(":", "") }, _ => panic!("Expected symbol! {:?}", x), } }).collect(); module.entry_points.push(EntryPoint { execution_model: ExecutionModel::Fragment, execution_mode: ExecutionMode::OriginUpperLeft, name: name.to_string(), interface: interface, }); assert_eq!(exec_model, "Fragment"); assert_eq!(exec_mode, "OriginUpperLeft"); } "fun" => { let name = match &list[1] { Ast::List(l) => l[0].clone(), _ => panic!("Expected list! {:?}", list[1]), }; let name = match name { Ast::Symbol(s) => s, _ => panic!("Expected symbol! {:?}", name), }; let body = list[2..].to_vec(); let fun = Function { name: name.to_string(), return_type: "void".to_string(), arguments: vec![], body: Some(vec![]), ast: Some(Ast::List(body)), }; module.functions.push(fun); } _ => panic!("Unknown keyword! {:?}", sym), } } _ => panic!("List where a keyword was expected! {:?}", keyword), } } _ => panic!("Top-level symbol! {:?}", node), } } } _ => panic!("Non-root ast") } module }