/ src / main.rs
main.rs
  1  //! A compiler for the Olea programming language.
  2  
  3  #![warn(missing_docs)]
  4  #![allow(
  5      clippy::cast_possible_truncation,
  6      clippy::missing_panics_doc,
  7      clippy::option_if_let_else,
  8      clippy::similar_names,
  9      clippy::too_many_lines,
 10      clippy::type_complexity,
 11      clippy::wildcard_imports,
 12      clippy::cognitive_complexity
 13  )]
 14  
 15  mod arborist;
 16  pub mod ast;
 17  mod codegen_fox32;
 18  pub mod compiler_types;
 19  pub mod ir;
 20  mod ir_builder;
 21  mod ir_desugar;
 22  mod ir_display;
 23  pub mod ir_liveness;
 24  // TODO: rewrite to account for `used_regs` not including phi arguments.
 25  // mod ir_optimizer;
 26  mod ir_opt;
 27  mod lexer;
 28  mod parser;
 29  #[allow(dead_code)]
 30  mod ttree_visualize;
 31  mod typechecker;
 32  
 33  use annotate_snippets::{renderer::Style, Level, Message, Renderer, Snippet};
 34  use compiler_types::Spanned;
 35  use std::process::ExitCode;
 36  
 37  fn error(message: &str) {
 38      // Don't render message in bold, as is default.
 39      let error_renderer = Renderer::styled().emphasis(Style::default());
 40      let error = Level::Error.title(message);
 41      anstream::eprintln!("{}", error_renderer.render(error));
 42  }
 43  
 44  fn error_snippet(message: Message) {
 45      anstream::eprintln!("{}", Renderer::styled().render(message));
 46  }
 47  
 48  fn main() -> ExitCode {
 49      let args: Vec<_> = std::env::args().collect();
 50      if args.len() != 2 {
 51          error(&format!(
 52              "{} <file>",
 53              args.first()
 54                  .map_or(env!("CARGO_CRATE_NAME"), String::as_ref)
 55          ));
 56          return ExitCode::FAILURE;
 57      };
 58      let file_path = &args[1];
 59      let src = match std::fs::read_to_string(file_path) {
 60          Ok(x) => x,
 61          Err(e) => {
 62              // We can probably do better error reporting, especially for common errors like file not found.
 63              error(&format!("could not open `{file_path}`: {e}"));
 64              return ExitCode::FAILURE;
 65          }
 66      };
 67      eprintln!("# Source code:\n{src}");
 68  
 69      let tokens = lexer::tokenize(&src);
 70      /*
 71      dbg!(tokens.has_error);
 72      for i in 0..tokens.len() {
 73          let lexer::Spanned {
 74              token,
 75              span: lexer::Span { start, len },
 76          } = tokens.get(i).unwrap();
 77          eprintln!("{:?} {:?}", &src[start..start + len], token);
 78      }
 79      */
 80  
 81      let ttree = match arborist::arborize(&tokens) {
 82          Ok(x) => x,
 83          Err(Spanned { kind, span }) => {
 84              use arborist::ErrorKind as E;
 85              let title = match kind {
 86                  E::Unexpected(c) => format!("unexpected {c:?}"),
 87                  E::Expected(c) => format!("expected {c:?}"),
 88                  E::Custom(msg) => msg.to_owned(),
 89              };
 90              error_snippet(
 91                  Level::Error.title(&title).snippet(
 92                      Snippet::source(&src)
 93                          .origin(file_path)
 94                          .fold(true)
 95                          .annotation(Level::Error.span(span)),
 96                  ),
 97              );
 98              return ExitCode::FAILURE;
 99          }
100      };
101      // eprintln!("# Token tree:");
102      // ttree_visualize::visualize(&ttree, &src);
103      // eprintln!();
104  
105      let ast = match parser::parse(&ttree, &src) {
106          Ok(x) => x,
107          Err(Spanned { kind, span }) => {
108              let parser::ErrorKind::Custom(title) = kind;
109              error_snippet(
110                  Level::Error.title(title).snippet(
111                      Snippet::source(&src)
112                          .origin(file_path)
113                          .fold(true)
114                          .annotation(Level::Error.span(span)),
115                  ),
116              );
117              return ExitCode::FAILURE;
118          }
119      };
120      // eprintln!("#Syntax tree:\n{ast:?}\n");
121  
122      let mut ir = match ir_builder::build(&ast) {
123          Ok(x) => x,
124          Err(Spanned { kind, span }) => {
125              use ir_builder::ErrorKind as E;
126              let (title, note) = match kind {
127                  E::NotFound(kind, v) => (format!("could not find {kind} `{v}`"), None),
128                  E::NameConflict(kind, span) => (
129                      format!("a {kind} with this name has already been defined"),
130                      span.map(|span| ("previously defined here".to_owned(), span)),
131                  ),
132                  E::DoesNotYield(span) => (
133                      "this expression needs to yield a value but doesn't".to_string(),
134                      Some(("required by this outer context".to_owned(), span)),
135                  ),
136                  E::CantAssignToConstant => ("can't assign to constant".to_owned(), None),
137                  E::CantCastToTy(ty) => (format!("can't cast a value to type {ty}"), None),
138                  E::UnknownIntLiteralSuffix => ("unknown int literal suffix".to_owned(), None),
139                  E::Todo(msg) => (format!("not yet implemented: {msg}"), None),
140              };
141              let mut e = Snippet::source(&src)
142                  .origin(file_path)
143                  .fold(true)
144                  .annotation(Level::Error.span(span));
145              if let Some((message, span)) = &note {
146                  e = e.annotation(Level::Info.span(span.clone()).label(message));
147              }
148              error_snippet(Level::Error.title(&title).snippet(e));
149              return ExitCode::FAILURE;
150          }
151      };
152      eprintln!("#IR:\n{ir}\n");
153  
154      match typechecker::typecheck(&ir) {
155          Ok(()) => {}
156          Err((fn_name, e)) => {
157              use typechecker::ErrorKind as E;
158              let fun = ir.functions.get(&fn_name).unwrap();
159              let snippet = Snippet::source(&src).origin(file_path).fold(true);
160              let (title, snippet): (String, _) = match e {
161                  E::NotInt(reg) => (
162                      format!("expected integer, got {}", fun.tys.get(&reg).unwrap()),
163                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
164                  ),
165                  E::NotPointer(reg) => (
166                      format!(
167                          "cannot dereference a value of type {}",
168                          fun.tys.get(&reg).unwrap()
169                      ),
170                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
171                  ),
172                  E::NotFunction(reg) => (
173                      format!("expected function, got {}", fun.tys.get(&reg).unwrap()),
174                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
175                  ),
176                  E::NotStruct(reg) => (
177                      format!(
178                          "type {} does not support field access",
179                          fun.tys.get(&reg).unwrap()
180                      ),
181                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
182                  ),
183                  E::NoFieldNamed(reg, field) => (
184                      format!("{} does not have field {field}", fun.tys.get(&reg).unwrap()),
185                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
186                  ),
187                  E::Expected(reg, given_ty) => (
188                      format!("expected {given_ty}, got {}", fun.tys.get(&reg).unwrap()),
189                      snippet.annotation(Level::Error.span(fun.spans.get(&reg).unwrap().clone())),
190                  ),
191              };
192              error_snippet(Level::Error.title(&title).snippet(snippet));
193              return ExitCode::FAILURE;
194          }
195      }
196      eprintln!("#Desugaring phase");
197      ir_desugar::desugar_program(&mut ir);
198      eprintln!("{ir}\n");
199  
200      if false {
201          eprintln!("#Optimizer phase");
202          ir_opt::STACK_TO_REGISTER.run_program(&mut ir);
203          ir_opt::CONSTANT_PROPAGATION.run_program(&mut ir);
204          ir_opt::NOP_ELIMINATION.run_program(&mut ir);
205          eprintln!("{ir}\n");
206      }
207  
208      if true {
209          for (name, f) in &ir.functions {
210              let live = ir_liveness::calculate_liveness(f);
211              eprintln!("{name}:");
212              live.pretty_print();
213          }
214          eprintln!();
215  
216          let asm = codegen_fox32::gen_program(&ir);
217          eprintln!("#Codegen");
218          print!("{asm}");
219      }
220      ExitCode::SUCCESS
221  }