commit - 11a12b204a216d8d8e25fdac1103c8beef3027e3
commit + faf55781ee6fb6fdcf94f474767adb31a8b78081
blob - aa69daf3d651955eac5e92fe1316dc2a7d997967
blob + a9c854949dc2e7fee21727040a19261ab0f262a7
--- src/error.rs
+++ src/error.rs
span,
}
}
+
+ pub fn render(
+ &self,
+ source: &str,
+ filename: &str,
+ ) -> String {
+ let (line, col) =
+ self.span.line_col(source);
+ let line_text =
+ source.lines().nth(line - 1).unwrap_or("");
+ let underline_len = if self.span.end
+ > self.span.start
+ {
+ self.span.end - self.span.start
+ } else {
+ 1
+ };
+
+ let line_num = format!("{}", line);
+ let padding = " ".repeat(line_num.len());
+
+ format!(
+ "error: {}\n \
+ --> {}:{}:{}\n\
+ {padding} |\n\
+ {line_num} | {line_text}\n\
+ {padding} | {}{} {}",
+ self.message,
+ filename,
+ line,
+ col,
+ " ".repeat(col - 1),
+ "^".repeat(underline_len),
+ self.message,
+ )
+ }
}
impl fmt::Display for OlangError {
blob - fa4dce2b2edbef541e42a742ef2aeeaff7999c20
blob + f05d168da63de62d6a140f7a61955207b6c1340c
--- src/main.rs
+++ src/main.rs
use std::fs;
use std::process;
+use error::OlangError;
+
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprintln!("usage: ol <command> <file>");
- eprintln!("commands: run, tokenize, parse, compile");
+ eprintln!(
+ "commands: run, tokenize, parse, compile"
+ );
process::exit(1);
}
let source = match fs::read_to_string(filename) {
Ok(s) => s,
Err(e) => {
- eprintln!("error reading '{filename}': {e}");
+ eprintln!(
+ "error reading '{filename}': {e}"
+ );
process::exit(1);
}
};
match command.as_str() {
- "run" => cmd_run(&source),
- "tokenize" => cmd_tokenize(&source),
- "parse" => cmd_parse(&source),
- "compile" => cmd_compile(&source),
+ "run" => cmd_run(&source, filename),
+ "tokenize" => cmd_tokenize(&source, filename),
+ "parse" => cmd_parse(&source, filename),
+ "compile" => cmd_compile(&source, filename),
_ => {
eprintln!("unknown command: {command}");
process::exit(1);
}
}
-fn cmd_run(source: &str) {
+fn die(
+ e: &OlangError,
+ source: &str,
+ filename: &str,
+) -> ! {
+ eprintln!("{}", e.render(source, filename));
+ process::exit(1);
+}
+
+fn cmd_run(source: &str, filename: &str) {
let mut lexer = lexer::Lexer::new(source);
let tokens = match lexer.tokenize() {
Ok(t) => t,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
};
+
let mut parser = parser::Parser::new(tokens);
let program = match parser.parse_program() {
Ok(p) => p,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
};
+
let comp = compiler::Compiler::new();
- let chunk = match comp.compile(&program) {
- Ok(c) => c,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ let result = match comp.compile(&program) {
+ Ok(r) => r,
+ Err(e) => die(&e, source, filename),
};
- let mut machine = vm::VM::new(chunk);
+
+ let mut machine = vm::VM::new(
+ result.functions,
+ result.main_index,
+ );
if let Err(e) = machine.run() {
eprintln!("runtime error: {e}");
process::exit(1);
}
}
-fn cmd_compile(source: &str) {
+fn cmd_compile(source: &str, filename: &str) {
let mut lexer = lexer::Lexer::new(source);
let tokens = match lexer.tokenize() {
Ok(t) => t,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
};
+
let mut parser = parser::Parser::new(tokens);
let program = match parser.parse_program() {
Ok(p) => p,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
};
+
let comp = compiler::Compiler::new();
match comp.compile(&program) {
- Ok(chunk) => chunk.disassemble("main"),
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Ok(result) => {
+ for chunk in &result.functions {
+ chunk.disassemble(&chunk.name);
+ println!();
+ }
+ }
+ Err(e) => die(&e, source, filename),
}
}
-fn cmd_parse(source: &str) {
+fn cmd_parse(source: &str, filename: &str) {
let mut lexer = lexer::Lexer::new(source);
let tokens = match lexer.tokenize() {
Ok(t) => t,
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
};
+
let mut parser = parser::Parser::new(tokens);
match parser.parse_program() {
Ok(program) => println!("{:#?}", program),
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
}
}
-fn cmd_tokenize(source: &str) {
+fn cmd_tokenize(source: &str, filename: &str) {
let mut lexer = lexer::Lexer::new(source);
+
match lexer.tokenize() {
Ok(tokens) => {
let parts: Vec<String> = tokens
.collect();
println!("{}", parts.join(" "));
}
- Err(e) => { eprintln!("{e}"); process::exit(1); }
+ Err(e) => die(&e, source, filename),
}
}
blob - 5a454a528486b4073ba49646dfbb0384bb8001f4
blob + f22795feb03848eb60c5260becd0e6239170139f
--- src/span.rs
+++ src/span.rs
pub fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
+
+ pub fn line_col(
+ &self,
+ source: &str,
+ ) -> (usize, usize) {
+ let mut line = 1;
+ let mut col = 1;
+ for (i, ch) in source.char_indices() {
+ if i >= self.start {
+ break;
+ }
+ if ch == '\n' {
+ line += 1;
+ col = 1;
+ } else {
+ col += 1;
+ }
+ }
+ (line, col)
+ }
}