#![feature(result_option_inspect)] #![feature(box_patterns)] #![feature(default_free_fn)] #![allow(non_snake_case)] use std::{fmt::Display, path::Path, process::exit}; use ast::IDLModule; use codegen::generate; use codespan_reporting::{ diagnostic::{Diagnostic, Label, Severity}, files::SimpleFile, term::{ emit, termcolor::{StandardStream, StandardStreamLock}, Config, }, }; use lexer::{NumberSuffix, Token}; use parser::TokenIterator; use crate::lexer::Spanned; mod ast; mod codegen; mod lexer; mod parser; fn precheck>( writer: &mut StandardStreamLock<'_>, config: &Config, file: &SimpleFile, ) { let mut lexer = TokenIterator::new(file.source().as_ref()); let mut diagnostics = vec![]; let mut previous = lexer.peek().ok().map(|Spanned(a, b)| Spanned(a.clone(), b)); while let Ok(Spanned(token, span)) = lexer.next() { let prev = Spanned(token.clone(), span.clone()); match token { Token::Ident(lexer::Ident::Other(t)) if t == "Type" => { diagnostics.push( Diagnostic::error() .with_labels(vec![Label::primary((), span.0)]) .with_message("`Type` is not supported anymore.") .with_notes(vec!["use `Alias` instead of `Type`".into()]), ); } Token::Ident(lexer::Ident::Other(ident)) if lexer .peek() .is_ok_and(|Spanned(a, _)| matches!(a, Token::LeftCurly)) && previous.is_some_and(|Spanned(ref a, _)| matches!(a, Token::Equals)) => { diagnostics.push( Diagnostic::error() .with_message("Unknown expression") .with_labels(vec![Label::primary((), span.0.clone())]) .with_notes(vec![ format!("add `Make` before the structure name to create a Make expression that will construct the `{ident}` structure" ), ]), ); } Token::Ident(lexer::Ident::Other(ident)) if NumberSuffix::ALL_SUFFIXES.contains(&ident.to_lowercase().as_str()) => { diagnostics.push( Diagnostic::warning() .with_message("Potentially invalid use of an uppercased number type") .with_labels(vec![Label::primary((), span.0)]) .with_notes(vec![ format!("Replace {ident} with {}", ident.to_lowercase()), "Code generation might fail".into(), ]), ); } _ => {} } previous = Some(prev); } if !diagnostics.is_empty() { let mut was_fatal = false; for diagnostic in diagnostics { if let Severity::Error | Severity::Bug = &diagnostic.severity { was_fatal = true; } emit(writer, config, file, &diagnostic).unwrap(); } if was_fatal { exit(1); } } } fn main() { let mut args = std::env::args(); args.next().unwrap(); let mut ast: Option = None; if let Some(file) = args.next() { let path = Path::new(&file); let codespan_file = codespan_reporting::files::SimpleFile::new( &file, std::fs::read_to_string(path).unwrap(), ); let writer = StandardStream::stdout(codespan_reporting::term::termcolor::ColorChoice::Auto); let config = Config { tab_width: 2, ..Default::default() }; precheck(&mut writer.lock(), &config, &codespan_file); match parser::parse(codespan_file.source()) { Ok(ast_) => { println!("{:#?}", ast_); ast = Some(ast_); } Err(e) => { let msg = e.to_string(); let label = match e { parser::ParserError::UnexpectedEOF => Label::primary( (), (codespan_file.source().len() - 1)..codespan_file.source().len(), ) .with_message("Unexpected end of file here"), parser::ParserError::Unexpected(expected, Spanned(got, span)) => { Label::primary((), span.0) .with_message(format!("Unexpected `{got}`, expected {expected}")) } parser::ParserError::PleaseStopParsingUse => unsafe { std::hint::unreachable_unchecked() }, }; let diagnostic = codespan_reporting::diagnostic::Diagnostic::error() .with_message(msg) .with_labels(vec![label]); codespan_reporting::term::emit( &mut writer.lock(), &config, &codespan_file, &diagnostic, ) .unwrap(); } } } else { eprintln!("No file given. Aborting."); } let rust = generate(ast.unwrap()); println!("{}", rust); } #[macro_export] macro_rules! unwrap_match { ($x:expr, $m:pat => $a:expr) => { match $x { $m => $a, _ => unreachable!(), } }; }