You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
14 KiB

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<String>,
}
#[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<Decoration>,
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Instruction {
pub result_id: Option<String>,
pub op: String,
pub operands: Vec<String>,
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Function {
pub name: String,
pub return_type: String,
pub arguments: Vec<String>,
pub body: Option<Vec<Instruction>>,
pub ast: Option<Ast>,
}
#[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<Capability>,
pub entry_points: Vec<EntryPoint>,
pub globals: Vec<GlobalVariable>,
pub functions: Vec<Function>,
pub memory_model: Memory,
pub imports: Vec<Import>,
}
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::<Vec<&str>>()[0].to_string();
let typ: String = name_and_type.split(":").collect::<Vec<&str>>()[1].to_string();
let bind = match &list[2] {
Ast::List(l) => l,
_ => panic!("Expected list! {:?}", list[2]),
};
let bind: Vec<String> = 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::<u32>().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::<Vec<&str>>()[0].to_string();
let typ: String = name_and_type.split(":").collect::<Vec<&str>>()[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<String> = 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
}