use compiler::{backend::spirv_meta, meta_compile}; use error::print_error; use std::{env, fs, path::PathBuf}; use parser::{parse_string, tokenize, Token}; pub mod compiler; mod error; pub mod parser; #[derive(Debug, Default, PartialEq)] enum Trace { Tokens, AST, #[default] Normal } #[derive(Debug, Default)] struct CompilerArgs { input: Option, output: Option, trace: Trace, print_help: bool, print_version: bool, } const SPIRV_VERSION: &'static str = "1.0"; fn main() { let args = parse_args(); if args.print_version || args.print_help { if args.print_help { print_help(); } if args.print_version { print_version(); } return; } 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; } }; if args.trace == Trace::Tokens { let tokens = tokenize(&src); for tok in tokens { match tok.item { Token::LeftParen => println!("{:>3}:{:<3} LeftParen",tok.location.line_start(), tok.location.col_start()), Token::RightParen => println!("{:>3}:{:<3} RightParen",tok.location.line_start(), tok.location.col_start()), Token::Symbol( val ) => println!("{:>3}:{:<3} Symbol `{}`",tok.location.line_start(), tok.location.col_start(), val) } } return; } let ast = match parse_string(&src) { Ok(i) => i, Err(e) => { print_error(e, Some(&src)); return; } }; if args.trace == Trace::AST { ast.pretty_print(None,None); 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; } } "trace" => { if parsed_args.trace == Trace::Normal { let Some(output) = args.next() else { parsed_args.print_help = true; eprintln!("trace needs a file"); eprintln!(); return parsed_args; }; match output.as_str() { "ast" | "AST" | "Ast" => { parsed_args.trace = Trace::AST; } "token" | "tokens" => { parsed_args.trace = Trace::Tokens; } a => { eprintln!("unknown trace value {a}"); eprintln!(); parsed_args.print_help = true; return parsed_args; } } } else { eprintln!("trace 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 ) }