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.

274 lines
8.9 KiB

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<PathBuf>,
output: Option<PathBuf>,
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
)
}