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
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
|
|
)
|
|
}
|