forked from itycodes/shaderc
				
			
							parent
							
								
									b67795cdfb
								
							
						
					
					
						commit
						10fb87de09
					
				| @ -0,0 +1,213 @@ | ||||
| use std::fmt::{self, Display, Write}; | ||||
| 
 | ||||
| use crate::{ | ||||
|     compiler::CompilerError, | ||||
|     parser::{Location, ParseError}, | ||||
| }; | ||||
| 
 | ||||
| pub fn print_error<E: FormattedLocatedError>(err: E, src: Option<&str>) { | ||||
|     let mut buf = String::new(); | ||||
|     match src { | ||||
|         Some(src) => { | ||||
|             err.fmt_with_src(&mut buf, &src).unwrap(); | ||||
|         } | ||||
|         None => err.fmt(&mut buf).unwrap(), | ||||
|     } | ||||
|     print!("{buf}"); | ||||
| } | ||||
| 
 | ||||
| pub trait FormattedLocatedError { | ||||
|     fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result; | ||||
|     fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result; | ||||
| } | ||||
| 
 | ||||
| impl Display for Location { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         match self { | ||||
|             Location::Char { line, col } => write!(f, "{line}:{col}"), | ||||
|             Location::String { lines, cols } => write!(f, "{}:{}", lines.start(), cols.start()), | ||||
|             Location::All => write!(f, "Entire file"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FormattedLocatedError for ParseError { | ||||
|     fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result { | ||||
|         match self { | ||||
|             ParseError::UnexpectedParenClose(location) => { | ||||
|                 writeln!(w, "Unexpected closing paren at {location}")? | ||||
|             } | ||||
|             ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?, | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result { | ||||
|         match self { | ||||
|             ParseError::UnexpectedParenClose(location) => { | ||||
|                 writeln!(w, "Unexpected closing paren at {location}")?; | ||||
|                 here(location, src, Some("Unexpected ')'".to_string()), w)?; | ||||
|             } | ||||
|             ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?, | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FormattedLocatedError for CompilerError { | ||||
|     fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result { | ||||
|         match self { | ||||
|             CompilerError::ExpectedSymbol(location) => { | ||||
|                 writeln!( | ||||
|                     w, | ||||
|                     "Expected a symbol but got a list or nothing at {location}" | ||||
|                 ) | ||||
|             } | ||||
|             CompilerError::ExpectedList(location) => { | ||||
|                 writeln!( | ||||
|                     w, | ||||
|                     "Expected a list but got a symbol or nothing at {location}" | ||||
|                 ) | ||||
|             } | ||||
|             CompilerError::UnknownKeyword(k, location) => { | ||||
|                 writeln!(w, "Got unexpected keyword {k} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: module, import, bind, dec, entry, fun") | ||||
|             } | ||||
|             CompilerError::ExpectedOneOf(vec, k, location) => { | ||||
|                 writeln!(w, "Got unexpected token {k} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: {}", vec.join(", ")) | ||||
|             } | ||||
|             CompilerError::TrailingTokens(location) => { | ||||
|                 writeln!(w, "Got unexpected trailing tokens at {location}")?; | ||||
|                 writeln!(w, "Expected end of list") | ||||
|             } | ||||
|             CompilerError::UnknownBind(b, location) => { | ||||
|                 writeln!(w, "Got unexpected keyword {b} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: BuiltIn, Location") | ||||
|             } | ||||
|             CompilerError::ExpectedType(location) => { | ||||
|                 writeln!(w, "Expected name:type but got name at {location}") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result { | ||||
|         match self { | ||||
|             CompilerError::ExpectedSymbol(location) => { | ||||
|                 writeln!( | ||||
|                     w, | ||||
|                     "Expected a symbol but got a list or nothing at {location}" | ||||
|                 )?; | ||||
|                 here(location, src, Some("Expected symbol".to_string()), w) | ||||
|             } | ||||
|             CompilerError::ExpectedList(location) => { | ||||
|                 writeln!( | ||||
|                     w, | ||||
|                     "Expected a list but got a symbol or nothing at {location}" | ||||
|                 )?; | ||||
|                 here(location, src, Some("Expected List".to_string()), w) | ||||
|             } | ||||
|             CompilerError::UnknownKeyword(k, location) => { | ||||
|                 writeln!(w, "Got unexpected keyword {k} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: module, import, bind, dec, entry, fun")?; | ||||
|                 here(location, src, Some("Unexpected keyword".to_string()), w) | ||||
|             } | ||||
|             CompilerError::ExpectedOneOf(vec, k, location) => { | ||||
|                 writeln!(w, "Got unexpected token {k} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: {}", vec.join(", "))?; | ||||
|                 here(location, src, Some("Unexpected token".to_string()), w) | ||||
|             } | ||||
|             CompilerError::TrailingTokens(location) => { | ||||
|                 writeln!(w, "Got unexpected trailing tokens at {location}")?; | ||||
|                 writeln!(w, "Expected end of list")?; | ||||
|                 here(location, src, Some("Unexpected token".to_string()), w) | ||||
|             } | ||||
|             CompilerError::UnknownBind(b, location) => { | ||||
|                 writeln!(w, "Got unexpected keyword {b} at {location}")?; | ||||
|                 writeln!(w, "Expected one of: BuiltIn, Location")?; | ||||
|                 here(location, src, Some("Unexpected keyword".to_string()), w) | ||||
|             } | ||||
|             CompilerError::ExpectedType(location) => { | ||||
|                 writeln!(w, "Expected name:type but got name at {location}")?; | ||||
|                 here(location, src, None, w) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn here<W: fmt::Write>( | ||||
|     loc: &Location, | ||||
|     src: &str, | ||||
|     comment: Option<String>, | ||||
|     w: &mut W, | ||||
| ) -> fmt::Result { | ||||
|     writeln!(w, "Here:")?; | ||||
|     writeln!( | ||||
|         w, | ||||
|         "{}", | ||||
|         annotate_src(&loc, src, comment.unwrap_or_default()) | ||||
|     )?; | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn annotate_src(loc: &Location, src: &str, comment: String) -> String { | ||||
|     let src_lines = src.split('\n').collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let mut buf = String::new(); | ||||
| 
 | ||||
|     match loc { | ||||
|         Location::Char { line, col } => { | ||||
|             writeln!(buf, "{}|{}", line_num(*line - 1), src_lines[*line - 2]).unwrap(); | ||||
|             writeln!(buf, "{}|{}", line_num(*line), src_lines[*line - 1]).unwrap(); | ||||
|             writeln!(buf, "{}|{}^ {}", "      ", " ".repeat(col - 1), comment).unwrap(); | ||||
|             writeln!(buf, "{}|{}", line_num(*line + 1), src_lines[*line]).unwrap(); | ||||
|         } | ||||
|         Location::String { lines, cols } => { | ||||
|             writeln!( | ||||
|                 buf, | ||||
|                 "{}|{}", | ||||
|                 line_num(*lines.start() - 1), | ||||
|                 src_lines[*lines.start() - 2] | ||||
|             ) | ||||
|             .unwrap(); | ||||
|             writeln!( | ||||
|                 buf, | ||||
|                 "{}", | ||||
|                 src_lines[(lines.start() - 1)..*lines.end()] | ||||
|                     .iter() | ||||
|                     .zip(lines.clone()) | ||||
|                     .map(|(l, n)| format!("{}|{}", line_num(n), l)) | ||||
|                     .collect::<Vec<_>>() | ||||
|                     .join("\n") | ||||
|             ) | ||||
|             .unwrap(); | ||||
|             writeln!( | ||||
|                 buf, | ||||
|                 "{}|{}{} {}", | ||||
|                 "      ", | ||||
|                 " ".repeat(cols.start() - 1), | ||||
|                 "^".repeat(cols.end() - cols.start() + 1), | ||||
|                 comment | ||||
|             ) | ||||
|             .unwrap(); | ||||
|             writeln!( | ||||
|                 buf, | ||||
|                 "{}|{}", | ||||
|                 line_num(*lines.end() + 1), | ||||
|                 src_lines[*lines.end()] | ||||
|             ) | ||||
|             .unwrap(); | ||||
|         } | ||||
|         Location::All => todo!(), | ||||
|     } | ||||
| 
 | ||||
|     buf | ||||
| } | ||||
| 
 | ||||
| fn line_num(num: usize) -> String { | ||||
|     let num = format!("{num}"); | ||||
|     format!("{num}{}", " ".repeat(6 - num.len())) | ||||
| } | ||||
| @ -1,115 +1,219 @@ | ||||
| use std::fmt::Write; | ||||
| use compiler::{backend::spirv_meta, meta_compile}; | ||||
| use error::print_error; | ||||
| use std::{env, fs, path::PathBuf}; | ||||
| 
 | ||||
| use parser::parse_string; | ||||
| pub mod compiler; | ||||
| mod error; | ||||
| pub mod parser; | ||||
| 
 | ||||
| #[derive(Debug, Default)] | ||||
| struct CompilerArgs { | ||||
|     input: Option<PathBuf>, | ||||
|     output: Option<PathBuf>, | ||||
|     print_help: bool, | ||||
|     print_version: bool, | ||||
| } | ||||
| 
 | ||||
| const SPIRV_VERSION: &'static str = "1.0"; | ||||
| 
 | ||||
| fn main() { | ||||
|     let mut ops: Vec<(Option<String>, Vec<String>)> = Vec::new(); | ||||
| 
 | ||||
|     //    OpMemoryModel Logical GLSL450
 | ||||
|     //    OpEntryPoint Fragment %main "main"
 | ||||
|     //    OpExecutionMode %main OriginUpperLeft
 | ||||
|     //    OpSource GLSL 450
 | ||||
|     //    OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
 | ||||
|     //    OpSourceExtension "GL_GOOGLE_include_directive"
 | ||||
|     //    OpName %main "main"
 | ||||
|     //%void = OpTypeVoid
 | ||||
|     //%3 = OpTypeFunction %void
 | ||||
|     //%main = OpFunction %void None %3
 | ||||
|     //%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("%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("%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 { | ||||
|         if op.0.is_some() { | ||||
|             write!(out, "{} = ", op.0.unwrap()).unwrap(); | ||||
|     let args = parse_args(); | ||||
|     if args.print_version || args.print_help { | ||||
|         if args.print_help { | ||||
|             print_help(); | ||||
|         } | ||||
|         for arg in op.1 { | ||||
|             write!(out, "{} ", arg).unwrap(); | ||||
|         if args.print_version { | ||||
|             print_version(); | ||||
|         } | ||||
|         writeln!(out).unwrap(); | ||||
|         return; | ||||
|     } | ||||
|     println!("{}", out); | ||||
| 
 | ||||
|     let Some(input) = args.input else { | ||||
|         eprintln!("No input specified"); | ||||
|         return; | ||||
|     }; | ||||
| 
 | ||||
|     let output = args.output.unwrap_or(PathBuf::from("./out.spv")); | ||||
| 
 | ||||
|     let src = match fs::read_to_string(input) { | ||||
|         Ok(i) => i, | ||||
|         Err(e) => { | ||||
|             eprintln!("{e}"); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let ast = match parse_string(&src) { | ||||
|         Ok(i) => i, | ||||
|         Err(e) => { | ||||
|             print_error(e, Some(&src)); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let module = match meta_compile(ast) { | ||||
|         Ok(m) => m, | ||||
|         Err(e) => { | ||||
|             print_error(e, Some(&src)); | ||||
|             return; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let res = spirv_meta(module); | ||||
| 
 | ||||
|     fs::write(output, res).unwrap(); | ||||
| } | ||||
| 
 | ||||
| fn parse_args() -> CompilerArgs { | ||||
|     let mut args = env::args(); | ||||
| 
 | ||||
|     let _prog = args.next().unwrap(); | ||||
| 
 | ||||
|     let mut parsed_args = CompilerArgs::default(); | ||||
| 
 | ||||
|     while let Some(arg) = args.next() { | ||||
|         if let Some(arg) = arg.strip_prefix("--") { | ||||
|             match arg { | ||||
|                 "help" => parsed_args.print_help = true, | ||||
|                 "version" => parsed_args.print_version = true, | ||||
|                 "input" => { | ||||
|                     if parsed_args.input.is_none() { | ||||
|                         let Some(input) = args.next() else { | ||||
|                             eprintln!("input needs a file"); | ||||
|                             eprintln!(); | ||||
|                             parsed_args.print_help = true; | ||||
|                             return parsed_args; | ||||
|                         }; | ||||
| 
 | ||||
|                         parsed_args.input = Some(PathBuf::from(input)); | ||||
|                     } else { | ||||
|                         eprintln!("input can be passed only once"); | ||||
|                         eprintln!(); | ||||
|                         parsed_args.print_help = true; | ||||
|                         return parsed_args; | ||||
|                     } | ||||
|                 } | ||||
|                 "output" => { | ||||
|                     if parsed_args.output.is_none() { | ||||
|                         let Some(output) = args.next() else { | ||||
|                             parsed_args.print_help = true; | ||||
|                             eprintln!("output needs a file"); | ||||
|                             eprintln!(); | ||||
|                             return parsed_args; | ||||
|                         }; | ||||
| 
 | ||||
|                         parsed_args.output = Some(PathBuf::from(output)); | ||||
|                     } else { | ||||
|                         eprintln!("output can be passed only once"); | ||||
|                         eprintln!(); | ||||
|                         parsed_args.print_help = true; | ||||
|                         return parsed_args; | ||||
|                     } | ||||
|                 } | ||||
|                 a => { | ||||
|                     eprintln!("unknown arg --{a}"); | ||||
|                     eprintln!(); | ||||
|                     parsed_args.print_help = true; | ||||
|                     return parsed_args; | ||||
|                 } | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         if let Some(arg) = arg.strip_prefix("-") { | ||||
|             let mut chars = arg.chars(); | ||||
|             while let Some(arg) = chars.next() { | ||||
|                 match arg { | ||||
|                     'i' => { | ||||
|                         if chars.next().is_some() { | ||||
|                             parsed_args.print_help = true; | ||||
|                             eprintln!("-i needs to be last in combined args"); | ||||
|                             return parsed_args; | ||||
|                         } | ||||
| 
 | ||||
|                         if parsed_args.input.is_none() { | ||||
|                             let Some(input) = args.next() else { | ||||
|                                 eprintln!("input needs a file"); | ||||
|                                 eprintln!(); | ||||
|                                 parsed_args.print_help = true; | ||||
|                                 return parsed_args; | ||||
|                             }; | ||||
| 
 | ||||
|                             parsed_args.input = Some(PathBuf::from(input)); | ||||
|                         } else { | ||||
|                             eprintln!("input can be passed only once"); | ||||
|                             eprintln!(); | ||||
|                             parsed_args.print_help = true; | ||||
|                             return parsed_args; | ||||
|                         } | ||||
|                     } | ||||
|                     'o' => { | ||||
|                         if chars.next().is_some() { | ||||
|                             parsed_args.print_help = true; | ||||
|                             eprintln!("-o needs to be last in combined args"); | ||||
|                             eprintln!(); | ||||
|                             return parsed_args; | ||||
|                         } | ||||
| 
 | ||||
|                         if parsed_args.output.is_none() { | ||||
|                             let Some(output) = args.next() else { | ||||
|                                 parsed_args.print_help = true; | ||||
|                                 eprintln!("output needs a file"); | ||||
|                                 return parsed_args; | ||||
|                             }; | ||||
| 
 | ||||
|                             parsed_args.output = Some(PathBuf::from(output)); | ||||
|                         } else { | ||||
|                             eprintln!("output can be passed only once"); | ||||
|                             eprintln!(); | ||||
|                             parsed_args.print_help = true; | ||||
|                             return parsed_args; | ||||
|                         } | ||||
|                     } | ||||
|                     'h' => parsed_args.print_help = true, | ||||
|                     'v' => parsed_args.print_version = true, | ||||
|                     a => { | ||||
|                         eprintln!("unknown arg -{a}"); | ||||
|                         eprintln!(); | ||||
|                         parsed_args.print_help = true; | ||||
|                         return parsed_args; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         eprintln!("unknown arg {arg}"); | ||||
|         eprintln!(); | ||||
|         parsed_args.print_help = true; | ||||
|         return parsed_args; | ||||
|     } | ||||
| 
 | ||||
|     parsed_args | ||||
| } | ||||
| 
 | ||||
| fn print_help() { | ||||
|     println!("-- ShaderC shader compiler --"); | ||||
|     println!(); | ||||
|     println!("Arguments:"); | ||||
|     println!("\t-i --input:"); | ||||
|     println!("\t\tThe shader to be parsed and compiled, manadory argument"); | ||||
|     println!(); | ||||
|     println!("\t-o --output:"); | ||||
|     println!("\t\tWhere to output the compiled spirv assembly to, default: out.spv"); | ||||
|     println!(); | ||||
|     println!("\t-h --help:"); | ||||
|     println!("\t\tPrint this and exit"); | ||||
|     println!("\t-v --version:"); | ||||
|     println!("\t\tPrint the compilers version"); | ||||
|     println!(); | ||||
| } | ||||
| 
 | ||||
| fn print_version() { | ||||
|     println!( | ||||
|         "ShaderC version: {}; SPIR-V version: {}", | ||||
|         env!("CARGO_PKG_VERSION"), | ||||
|         SPIRV_VERSION | ||||
|     ) | ||||
| } | ||||
|  | ||||
					Loading…
					
					
				
		Reference in new issue