From 38fc11590bac73bdc8000f5dbab77a146737a664 Mon Sep 17 00:00:00 2001 From: Avery Date: Thu, 6 Mar 2025 11:25:39 +0100 Subject: [PATCH 1/2] Add error handling and location tracking to the tokenizer and parser --- src/main.rs | 93 +++++++++++++++++--- src/parser/mod.rs | 190 ++++++++++++++++++++++++++++++++++------ src/parser/tests.rs | 206 ++++++++++++++++++++++++-------------------- 3 files changed, 354 insertions(+), 135 deletions(-) diff --git a/src/main.rs b/src/main.rs index ebebce3..2374721 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,10 @@ use std::fmt::Write; -pub mod parser; pub mod compiler; +pub mod parser; fn main() { let mut ops: Vec<(Option, Vec)> = Vec::new(); - + // OpMemoryModel Logical GLSL450 // OpEntryPoint Fragment %main "main" // OpExecutionMode %main OriginUpperLeft @@ -18,23 +18,88 @@ fn main() { //%5 = OpLabel // OpReturn // OpFunctionEnd - + ops.push((None, vec!["OpCapability".to_string(), "Shader".to_string()])); - ops.push((Some("%1".to_string()), vec!["OpExtInstImport".to_string(), "\"GLSL.std.450\"".to_string()])); - ops.push((None, vec!["OpMemoryModel".to_string(), "Logical".to_string(), "GLSL450".to_string()])); - ops.push((None, vec!["OpEntryPoint".to_string(), "Fragment".to_string(), "%main".to_string(), "\"main\"".to_string()])); - ops.push((None, vec!["OpExecutionMode".to_string(), "%main".to_string(), "OriginUpperLeft".to_string()])); - ops.push((None, vec!["OpSource".to_string(), "GLSL".to_string(), "450".to_string()])); - ops.push((None, vec!["OpSourceExtension".to_string(), "\"GL_GOOGLE_cpp_style_line_directive\"".to_string()])); - ops.push((None, vec!["OpSourceExtension".to_string(), "\"GL_GOOGLE_include_directive\"".to_string()])); - ops.push((None, vec!["OpName".to_string(), "%main".to_string(), "\"main\"".to_string()])); + ops.push(( + Some("%1".to_string()), + vec![ + "OpExtInstImport".to_string(), + "\"GLSL.std.450\"".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpMemoryModel".to_string(), + "Logical".to_string(), + "GLSL450".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpEntryPoint".to_string(), + "Fragment".to_string(), + "%main".to_string(), + "\"main\"".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpExecutionMode".to_string(), + "%main".to_string(), + "OriginUpperLeft".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpSource".to_string(), + "GLSL".to_string(), + "450".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpSourceExtension".to_string(), + "\"GL_GOOGLE_cpp_style_line_directive\"".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpSourceExtension".to_string(), + "\"GL_GOOGLE_include_directive\"".to_string(), + ], + )); + ops.push(( + None, + vec![ + "OpName".to_string(), + "%main".to_string(), + "\"main\"".to_string(), + ], + )); ops.push((Some("%void".to_string()), vec!["OpTypeVoid".to_string()])); - ops.push((Some("%3".to_string()), vec!["OpTypeFunction".to_string(), "%void".to_string()])); - ops.push((Some("%main".to_string()), vec!["OpFunction".to_string(), "%void".to_string(), "None".to_string(), "%3".to_string()])); + ops.push(( + Some("%3".to_string()), + vec!["OpTypeFunction".to_string(), "%void".to_string()], + )); + ops.push(( + Some("%main".to_string()), + vec![ + "OpFunction".to_string(), + "%void".to_string(), + "None".to_string(), + "%3".to_string(), + ], + )); ops.push((Some("%5".to_string()), vec!["OpLabel".to_string()])); ops.push((None, vec!["OpReturn".to_string()])); ops.push((None, vec!["OpFunctionEnd".to_string()])); - + let mut out: String = String::new(); for op in ops { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c56d16f..427f1f6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,9 +2,94 @@ mod tests; use std::iter::Peekable; +use std::ops::RangeInclusive; use std::vec::IntoIter; -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug)] +enum Location { + Char { + line: usize, + col: usize, + }, + String { + lines: RangeInclusive, + cols: RangeInclusive, + }, +} + +#[derive(Debug)] +pub enum ParseError { + UnexpectedParenClose(Location), + UnexpectedEof, +} + +impl Location { + /// Since [`Localised`] doesn't test for location match, allow for creating simple dummy + /// locations for testing + #[cfg(test)] + pub fn dummy() -> Location { + Self::Char { line: 0, col: 0 } + } + + pub fn range(start: &Location, end: &Location) -> Location { + Location::String { + lines: start.line_start()..=end.line_end(), + cols: start.col_start()..=end.col_end(), + } + } + + pub fn line_start(&self) -> usize { + match self { + Location::Char { line, .. } => *line, + Location::String { lines, .. } => *lines.start(), + } + } + + pub fn col_start(&self) -> usize { + match self { + Location::Char { col, .. } => *col, + Location::String { cols, .. } => *cols.start(), + } + } + + pub fn line_end(&self) -> usize { + match self { + Location::Char { line, .. } => *line, + Location::String { lines, .. } => *lines.end(), + } + } + + pub fn col_end(&self) -> usize { + match self { + Location::Char { col, .. } => *col, + Location::String { cols, .. } => *cols.end(), + } + } +} + +#[derive(Debug, Clone)] +struct Localised { + location: Location, + item: T, +} + +impl Localised { + #[cfg(test)] + pub fn dummy_location(item: T) -> Self { + Self { + location: Location::dummy(), + item, + } + } +} + +impl PartialEq for Localised { + fn eq(&self, other: &Self) -> bool { + self.item.eq(&other.item) + } +} + +#[derive(Debug, PartialEq, Clone)] pub enum Token { LeftParen, RightParen, @@ -13,56 +98,109 @@ pub enum Token { #[derive(Debug, PartialEq, Clone)] pub enum Ast { - Symbol(String), - List(Vec), + Symbol(Localised), + List(Localised>), Root(Vec), } -pub fn tokenize(input: &str) -> Vec { +pub fn tokenize(input: &str) -> Vec> { let mut tokens = Vec::new(); - let mut chars = input.chars().peekable(); - while let Some(c) = chars.next() { + // let mut chars = input.chars().peekable(); + let mut chars = (1..) + .zip(input.split('\n')) + .flat_map(|(l_num, l)| { + (1..).zip(l.chars()).map(move |(c_num, c)| Localised { + location: Location::Char { + line: l_num, + col: c_num, + }, + item: c, + }) + }) + .peekable(); + while let Some(Localised { location, item: c }) = chars.next() { match c { - '(' => tokens.push(Token::LeftParen), - ')' => tokens.push(Token::RightParen), + '(' => tokens.push(Localised { + location, + item: Token::LeftParen, + }), + ')' => tokens.push(Localised { + location, + item: Token::RightParen, + }), _ if c.is_whitespace() => (), _ => { + let start = location.clone(); + let mut end = location; let mut symbol = c.to_string(); - while let Some(&c) = chars.peek() { - if c.is_whitespace() || c == '(' || c == ')' { + while let Some(Localised { item: c, .. }) = chars.peek() { + if c.is_whitespace() || *c == '(' || *c == ')' { break; } - symbol.push(c); - chars.next(); + symbol.push(*c); + let Localised { location, .. } = chars.next().unwrap(); + end = location; } - tokens.push(Token::Symbol(symbol)); + tokens.push(Localised { + location: Location::range(&start, &end), + item: Token::Symbol(symbol), + }); } } } tokens } -fn parse_expr(tokens: &mut Peekable>) -> Ast { +fn parse_expr(tokens: &mut Peekable>>) -> Result { match tokens.next() { - Some(Token::LeftParen) => { + Some(Localised { + location: start, + item: Token::LeftParen, + }) => { let mut list = Vec::new(); - while tokens.peek() != Some(&Token::RightParen) { - list.push(parse_expr(tokens)); + while !matches!( + tokens.peek(), + Some(Localised { + item: Token::RightParen, + .. + }) + ) { + list.push(parse_expr(tokens)?); } - tokens.next(); - Ast::List(list) + let Some(Localised { + location: end, + item: Token::RightParen, + }) = tokens.next() + else { + unreachable!() + }; + Ok(Ast::List(Localised { + location: Location::range(&start, &end), + item: list, + })) } - Some(Token::RightParen) => panic!("unexpected )"), - Some(Token::Symbol(s)) => Ast::Symbol(s), - None => panic!("unexpected EOF"), + Some(Localised { + location, + item: Token::RightParen, + }) => Err(ParseError::UnexpectedParenClose(location)), + Some(Localised { + location, + item: Token::Symbol(s), + }) => Ok(Ast::Symbol(Localised { location, item: s })), + None => Err(ParseError::UnexpectedEof), } } -pub fn parse(tokens: Vec) -> Ast { +pub fn parse(tokens: Vec>) -> Result { let mut tokens = tokens.into_iter().peekable(); let mut ast: Vec = Vec::new(); while tokens.peek().is_some() { - ast.push(parse_expr(&mut tokens)); + ast.push(parse_expr(&mut tokens)?); } - Ast::Root(ast) -} \ No newline at end of file + Ok(Ast::Root(ast)) +} + +pub fn parse_string(src: &str) -> Result { + let tokens = tokenize(src); + parse(tokens) +} diff --git a/src/parser/tests.rs b/src/parser/tests.rs index dd6a947..88f38cf 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -1,22 +1,38 @@ -use crate::parser::{tokenize, parse, Token, Ast}; +use crate::parser::{Ast, Localised, Token, parse, tokenize}; #[test] fn test_tokenize() { let input = "(+ (* 1:u8 2) 2)"; let expected = vec![ - Token::LeftParen, - Token::Symbol("+".to_string()), - Token::LeftParen, - Token::Symbol("*".to_string()), - Token::Symbol("1:u8".to_string()), - Token::Symbol("2".to_string()), - Token::RightParen, - Token::Symbol("2".to_string()), - Token::RightParen, + Localised::dummy_location(Token::LeftParen), + Localised::dummy_location(Token::Symbol("+".to_string())), + Localised::dummy_location(Token::LeftParen), + Localised::dummy_location(Token::Symbol("*".to_string())), + Localised::dummy_location(Token::Symbol("1:u8".to_string())), + Localised::dummy_location(Token::Symbol("2".to_string())), + Localised::dummy_location(Token::RightParen), + Localised::dummy_location(Token::Symbol("2".to_string())), + Localised::dummy_location(Token::RightParen), ]; assert_eq!(tokenize(input), expected); } +#[test] +#[should_panic] +fn test_unexpected_pclose() { + let input = "())"; + let tokens = tokenize(input); + let _ast = parse(tokens).unwrap(); +} + +#[test] +#[should_panic] +fn test_unexpected_eof() { + let input = "(1 2 3 4"; + let tokens = tokenize(input); + let _ast = parse(tokens).unwrap(); +} + #[test] fn test_parse() { let src = " @@ -34,92 +50,92 @@ fn test_parse() { 1.0 1.0))) "; - let ast = parse(tokenize(src)); + let ast = parse(tokenize(src)).unwrap(); println!("{:?}", ast); let test_ast: Ast = Ast::Root(vec![ - Ast::List(vec![ - Ast::Symbol("module".to_string()), - Ast::Symbol("Shader".to_string()), - Ast::Symbol("Logical".to_string()), - Ast::Symbol("GLSL450".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("import".to_string()), - Ast::Symbol(":std".to_string()), - Ast::Symbol("GLSL.std.450".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("bind".to_string()), - Ast::List(vec![ - Ast::Symbol("frag-coord:*v4f32i".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("BuiltIn".to_string()), - Ast::Symbol("FragCoord".to_string()), - ]), - ]), - Ast::List(vec![ - Ast::Symbol("bind".to_string()), - Ast::List(vec![ - Ast::Symbol("out-color:*v4f32o".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("Location".to_string()), - Ast::Symbol("0".to_string()), - ]), - ]), - Ast::List(vec![ - Ast::Symbol("dec".to_string()), - Ast::Symbol("frag-coord:*v4f32i".to_string()), - Ast::Symbol("Input".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("dec".to_string()), - Ast::Symbol("out-color:*v4f32o".to_string()), - Ast::Symbol("Output".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("entry".to_string()), - Ast::Symbol("main".to_string()), - Ast::Symbol("Fragment".to_string()), - Ast::Symbol("OriginUpperLeft".to_string()), - Ast::List(vec![ - Ast::Symbol(":frag-coord".to_string()), - Ast::Symbol(":out-color".to_string()), - ]), - ]), - Ast::List(vec![ - Ast::Symbol("fun".to_string()), - Ast::List(vec![ - Ast::Symbol("main".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("store-ptr".to_string()), - Ast::List(vec![ - Ast::Symbol("out-color".to_string()), - ]), - Ast::List(vec![ - Ast::Symbol("v4f32i".to_string()), - Ast::List(vec![ - Ast::Symbol("/".to_string()), - Ast::List(vec![ - Ast::Symbol(".xy".to_string()), - Ast::List(vec![ - Ast::Symbol("load-ptr".to_string()), - Ast::Symbol("frag-coord".to_string()), - ]), - ]), - Ast::List(vec![ - Ast::Symbol("v2f32".to_string()), - Ast::Symbol("1920.0".to_string()), - Ast::Symbol("1080.0".to_string()), - ]), - ]), - Ast::Symbol("1.0".to_string()), - Ast::Symbol("1.0".to_string()), - ]), - ]), - ]), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("module".to_string())), + Ast::Symbol(Localised::dummy_location("Shader".to_string())), + Ast::Symbol(Localised::dummy_location("Logical".to_string())), + Ast::Symbol(Localised::dummy_location("GLSL450".to_string())), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("import".to_string())), + Ast::Symbol(Localised::dummy_location(":std".to_string())), + Ast::Symbol(Localised::dummy_location("GLSL.std.450".to_string())), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("bind".to_string())), + Ast::List(Localised::dummy_location(vec![Ast::Symbol( + Localised::dummy_location("frag-coord:*v4f32i".to_string()), + )])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("Builtin".to_string())), + Ast::Symbol(Localised::dummy_location("FragCoord".to_string())), + ])), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("bind".to_string())), + Ast::List(Localised::dummy_location(vec![Ast::Symbol( + Localised::dummy_location("out-color:*v4f32o".to_string()), + )])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("Location".to_string())), + Ast::Symbol(Localised::dummy_location("0".to_string())), + ])), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("dec".to_string())), + Ast::Symbol(Localised::dummy_location("frag-coord:*v4f32i".to_string())), + Ast::Symbol(Localised::dummy_location("Input".to_string())), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("dec".to_string())), + Ast::Symbol(Localised::dummy_location("out-color:*v4f32o".to_string())), + Ast::Symbol(Localised::dummy_location("Output".to_string())), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("entry".to_string())), + Ast::Symbol(Localised::dummy_location("main".to_string())), + Ast::Symbol(Localised::dummy_location("Fragment".to_string())), + Ast::Symbol(Localised::dummy_location("OriginUpperLeft".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location(":frag-coord".to_string())), + Ast::Symbol(Localised::dummy_location(":out-color".to_string())), + ])), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("fun".to_string())), + Ast::List(Localised::dummy_location(vec![Ast::Symbol( + Localised::dummy_location("main".to_string()), + )])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("store-ptr".to_string())), + Ast::List(Localised::dummy_location(vec![Ast::Symbol( + Localised::dummy_location("out-color".to_string()), + )])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("v4f23i".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("/".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location(".xy".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("load-ptr".to_string())), + Ast::Symbol(Localised::dummy_location("frag-coord".to_string())), + ])), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("v2f32".to_string())), + Ast::Symbol(Localised::dummy_location("1920.0".to_string())), + Ast::Symbol(Localised::dummy_location("1080.0".to_string())), + ])), + ])), + Ast::Symbol(Localised::dummy_location("1.0".to_string())), + Ast::Symbol(Localised::dummy_location("1.0".to_string())), + ])), + ])), + ])), ]); assert_eq!(ast, test_ast); -} \ No newline at end of file +} From 1bb4206a3b6ee8206782f3ddb602b33c437d4cb7 Mon Sep 17 00:00:00 2001 From: Avery Date: Thu, 6 Mar 2025 11:57:57 +0100 Subject: [PATCH 2/2] Fix test dealing with `Localized` --- src/compiler/backend/tests.rs | 32 +++++++---- src/compiler/mod.rs | 100 ++++++++++++++++++++++------------ src/compiler/tests.rs | 58 ++++++++++++-------- src/parser/mod.rs | 35 ++++++++++-- src/parser/tests.rs | 7 ++- 5 files changed, 156 insertions(+), 76 deletions(-) diff --git a/src/compiler/backend/tests.rs b/src/compiler/backend/tests.rs index 0f21e2f..4252995 100644 --- a/src/compiler/backend/tests.rs +++ b/src/compiler/backend/tests.rs @@ -1,8 +1,8 @@ #[test] fn test_emit() { + use crate::compiler::backend::*; use crate::compiler::*; use crate::parser::*; - use crate::compiler::backend::*; let src = " (module Shader Logical GLSL450) (import :std GLSL.std.450) @@ -18,8 +18,8 @@ fn test_emit() { 1.0 1.0))) "; - let mut ast = parse(tokenize(src)); - let module = meta_compile(&mut ast); + let ast = parse(tokenize(src)); + let module = meta_compile(&mut ast.unwrap()); let res = spirv_meta(module); println!("{}", res); } @@ -28,12 +28,24 @@ fn test_emit() { fn test_type_parse() { use crate::compiler::backend::*; use Type::*; - assert_eq!(parse_type(&"*v4f32i".to_string()), - Pointer(Box::new(Vector(Box::new(Float(32)), 4)), StorageClass::Input)); - assert_eq!(parse_type(&"*v4f32o".to_string()), - Pointer(Box::new(Vector(Box::new(Float(32)), 4)), StorageClass::Output)); - assert_eq!(parse_type(&"v2f32".to_string()), - Vector(Box::new(Float(32)), 2)); + assert_eq!( + parse_type(&"*v4f32i".to_string()), + Pointer( + Box::new(Vector(Box::new(Float(32)), 4)), + StorageClass::Input + ) + ); + assert_eq!( + parse_type(&"*v4f32o".to_string()), + Pointer( + Box::new(Vector(Box::new(Float(32)), 4)), + StorageClass::Output + ) + ); + assert_eq!( + parse_type(&"v2f32".to_string()), + Vector(Box::new(Float(32)), 2) + ); assert_eq!(parse_type(&"f32".to_string()), Float(32)); assert_eq!(parse_type(&"s32".to_string()), Int(32)); -} \ No newline at end of file +} diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 140a57a..8635d0c 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -1,4 +1,4 @@ -use crate::parser::Ast; +use crate::parser::{Ast, Localised, Location}; pub mod backend; @@ -10,13 +10,13 @@ mod tests; #[derive(Debug, PartialEq, Default, Clone)] pub enum Capability { #[default] - Shader + Shader, } #[derive(Debug, PartialEq, Default, Clone)] pub enum ExecutionMode { #[default] - OriginUpperLeft + OriginUpperLeft, } #[derive(Debug, PartialEq, Default, Clone)] @@ -40,7 +40,7 @@ pub enum AddressingModel { Logical, Physical32, Physical64, - PhysicalStorageBuffer64 + PhysicalStorageBuffer64, } #[derive(Debug, PartialEq, Default, Clone)] pub enum MemoryModel { @@ -48,7 +48,7 @@ pub enum MemoryModel { GLSL450, OpenCL, VulkanKHR, - Simple + Simple, } #[derive(Debug, PartialEq, Default, Clone)] @@ -147,9 +147,9 @@ pub fn meta_compile(ast: &mut Ast) -> Module { 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())); + assert_eq!(exec.symbol(), Some("Logical".to_string())); + assert_eq!(memory.symbol(), Some("GLSL450".to_string())); + assert_eq!(cap.symbol(), Some("Shader".to_string())); } "import" => { let name = match &list[1] { @@ -170,28 +170,38 @@ pub fn meta_compile(ast: &mut Ast) -> Module { Ast::List(l) => l, _ => panic!("Expected list! {:?}", list[1]), }; - let name_and_type: String = name_and_type.iter().map(|x| { - match x { + let name_and_type: String = name_and_type + .iter() + .map(|x| match x { Ast::Symbol(s) => s.to_string(), _ => panic!("Expected symbol! {:?}", x), - } - }).collect(); + }) + .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 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 { + let bind: Vec = bind + .iter() + .map(|x| match x { Ast::Symbol(s) => s.to_string(), _ => panic!("Expected symbol! {:?}", x), - } - }).collect(); + }) + .collect(); let bind_name = match bind[0].as_str() { - "BuiltIn" => Decoration::BuiltIn(BuiltinDecoration::FragCoord), - "Location" => Decoration::Location(bind[1].parse::().unwrap()), + "BuiltIn" => { + Decoration::BuiltIn(BuiltinDecoration::FragCoord) + } + "Location" => Decoration::Location( + bind[1].parse::().unwrap(), + ), _ => panic!("Unknown bind! {:?}", bind), }; let mut exists = false; @@ -214,8 +224,12 @@ pub fn meta_compile(ast: &mut Ast) -> Module { 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 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]), @@ -240,9 +254,12 @@ pub fn meta_compile(ast: &mut Ast) -> Module { }); } if exists { - module.globals.iter_mut() + module + .globals + .iter_mut() .find(|x| x.name.as_str() == name.as_str()) - .unwrap().storage_class = storage_class.clone(); + .unwrap() + .storage_class = storage_class.clone(); } } "entry" => { @@ -262,22 +279,21 @@ pub fn meta_compile(ast: &mut Ast) -> Module { 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(":", "") - }, + let interface: Vec = interface + .iter() + .map(|x| match x { + Ast::Symbol(s) => s.to_string().replace(":", ""), _ => panic!("Expected symbol! {:?}", x), - } - }).collect(); + }) + .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"); + assert_eq!(**exec_model, "Fragment"); + assert_eq!(**exec_mode, "OriginUpperLeft"); } "fun" => { let name = match &list[1] { @@ -289,12 +305,23 @@ pub fn meta_compile(ast: &mut Ast) -> Module { _ => panic!("Expected symbol! {:?}", name), }; let body = list[2..].to_vec(); + let location = if let (Some(s), Some(e)) = ( + body.first().map(|a| a.location()).flatten(), + body.last().map(|a| a.location()).flatten(), + ) { + Location::range(s, e) + } else { + Location::Char { line: 0, col: 0 } + }; let fun = Function { name: name.to_string(), return_type: "void".to_string(), arguments: vec![], body: Some(vec![]), - ast: Some(Ast::List(body)), + ast: Some(Ast::List(Localised { + location, + item: body, + })), }; module.functions.push(fun); } @@ -308,7 +335,8 @@ pub fn meta_compile(ast: &mut Ast) -> Module { } } } - _ => panic!("Non-root ast") + _ => panic!("Non-root ast"), } module -} \ No newline at end of file +} + diff --git a/src/compiler/tests.rs b/src/compiler/tests.rs index 33398bd..a7c7e13 100644 --- a/src/compiler/tests.rs +++ b/src/compiler/tests.rs @@ -1,4 +1,4 @@ -use crate::parser::{tokenize, parse}; +use crate::parser::{parse, tokenize}; #[test] fn test_compile() { @@ -18,9 +18,9 @@ fn test_compile() { 1.0 1.0))) "; - let mut ast = parse(tokenize(src)); + let ast = parse(tokenize(src)); println!("{:#?}", ast); - let module = meta_compile(&mut ast); + let module = meta_compile(&mut ast.unwrap()); println!("{:#?}", module); let test_module = Module { capabilities: vec![Capability::Shader], @@ -49,25 +49,36 @@ fn test_compile() { return_type: "void".to_string(), arguments: vec![], body: Some(vec![]), - ast: Some(Ast::List(vec![ - Ast::List(vec![ - Ast::Symbol("store-ptr".to_string()), - Ast::List(vec![Ast::Symbol("out-color".to_string())]), - Ast::List(vec![ - Ast::Symbol("v4f32i".to_string()), - Ast::List(vec![ - Ast::Symbol("/".to_string()), - Ast::List(vec![ - Ast::Symbol(".xy".to_string()), - Ast::List(vec![Ast::Symbol("load-ptr".to_string()), Ast::Symbol("frag-coord".to_string())]), - ]), - Ast::List(vec![Ast::Symbol("v2f32".to_string()), Ast::Symbol("1920.0".to_string()), Ast::Symbol("1080.0".to_string())]), - ]), - Ast::Symbol("1.0".to_string()), - Ast::Symbol("1.0".to_string()), - ]), + ast: Some(Ast::List(Localised::dummy_location(vec![Ast::List( + Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("store-ptr".to_string())), + Ast::List(Localised::dummy_location(vec![Ast::Symbol( + Localised::dummy_location("out-color".to_string()), + )])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("v4f32i".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("/".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location(".xy".to_string())), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("load-ptr".to_string())), + Ast::Symbol(Localised::dummy_location( + "frag-coord".to_string(), + )), + ])), + ])), + Ast::List(Localised::dummy_location(vec![ + Ast::Symbol(Localised::dummy_location("v2f32".to_string())), + Ast::Symbol(Localised::dummy_location("1920.0".to_string())), + Ast::Symbol(Localised::dummy_location("1080.0".to_string())), + ])), + ])), + Ast::Symbol(Localised::dummy_location("1.0".to_string())), + Ast::Symbol(Localised::dummy_location("1.0".to_string())), + ])), ]), - ])), + )]))), }], memory_model: Memory { addressing_model: AddressingModel::Logical, @@ -78,5 +89,8 @@ fn test_compile() { value: "GLSL.std.450".to_string(), }], }; + dbg!(&module); + dbg!(&test_module); assert_eq!(module, test_module); -} \ No newline at end of file +} + diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 427f1f6..f7b9a58 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2,11 +2,11 @@ mod tests; use std::iter::Peekable; -use std::ops::RangeInclusive; +use std::ops::{Deref, RangeInclusive}; use std::vec::IntoIter; #[derive(Clone, Debug)] -enum Location { +pub enum Location { Char { line: usize, col: usize, @@ -68,9 +68,9 @@ impl Location { } #[derive(Debug, Clone)] -struct Localised { - location: Location, - item: T, +pub struct Localised { + pub location: Location, + pub item: T, } impl Localised { @@ -83,6 +83,14 @@ impl Localised { } } +impl Deref for Localised { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.item + } +} + impl PartialEq for Localised { fn eq(&self, other: &Self) -> bool { self.item.eq(&other.item) @@ -103,6 +111,23 @@ pub enum Ast { Root(Vec), } +impl Ast { + pub fn symbol(self) -> Option { + match self { + Ast::Symbol(Localised { item, .. }) => Some(item), + _ => None, + } + } + + pub fn location(&self) -> Option<&Location> { + match self { + Ast::Symbol(Localised { location, .. }) => Some(location), + Ast::List(Localised { location, .. }) => Some(location), + Ast::Root(_) => None, + } + } +} + pub fn tokenize(input: &str) -> Vec> { let mut tokens = Vec::new(); // let mut chars = input.chars().peekable(); diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 88f38cf..07bde87 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -51,7 +51,6 @@ fn test_parse() { 1.0))) "; let ast = parse(tokenize(src)).unwrap(); - println!("{:?}", ast); let test_ast: Ast = Ast::Root(vec![ Ast::List(Localised::dummy_location(vec![ Ast::Symbol(Localised::dummy_location("module".to_string())), @@ -70,7 +69,7 @@ fn test_parse() { Localised::dummy_location("frag-coord:*v4f32i".to_string()), )])), Ast::List(Localised::dummy_location(vec![ - Ast::Symbol(Localised::dummy_location("Builtin".to_string())), + Ast::Symbol(Localised::dummy_location("BuiltIn".to_string())), Ast::Symbol(Localised::dummy_location("FragCoord".to_string())), ])), ])), @@ -115,7 +114,7 @@ fn test_parse() { Localised::dummy_location("out-color".to_string()), )])), Ast::List(Localised::dummy_location(vec![ - Ast::Symbol(Localised::dummy_location("v4f23i".to_string())), + Ast::Symbol(Localised::dummy_location("v4f32i".to_string())), Ast::List(Localised::dummy_location(vec![ Ast::Symbol(Localised::dummy_location("/".to_string())), Ast::List(Localised::dummy_location(vec![ @@ -137,5 +136,7 @@ fn test_parse() { ])), ])), ]); + println!("ast = {:#?}", ast); + println!("test_ast = {:#?}", test_ast); assert_eq!(ast, test_ast); }