1
0
Fork 0

Add error handling for tokenizer errors

This commit is contained in:
Vili Sinervä 2025-02-26 21:50:08 +02:00
parent 340de3c984
commit 9a13d0b9b6
No known key found for this signature in database
GPG key ID: DF8FEAF54EFAC996
5 changed files with 125 additions and 96 deletions

View file

@ -1,4 +1,4 @@
use std::io; use std::{error::Error, io};
use assembler::assemble; use assembler::assemble;
use assembly_generator::generate_assembly; use assembly_generator::generate_assembly;
@ -23,22 +23,23 @@ mod tokenizer;
mod type_checker; mod type_checker;
mod variable; mod variable;
pub fn compile(code: &str) -> String { pub fn compile(code: &str) -> Result<String, Box<dyn Error>> {
let tokens = tokenize(code); let tokens = tokenize(code)?;
let mut ast = parse(&tokens); let mut ast = parse(&tokens);
type_check(&mut ast, &mut SymTab::new_type_table()); type_check(&mut ast, &mut SymTab::new_type_table());
let ir = generate_ir(&ast); let ir = generate_ir(&ast);
let assembly = generate_assembly(&ir); let assembly = generate_assembly(&ir);
general_purpose::STANDARD.encode(&assemble(assembly)) Ok(general_purpose::STANDARD.encode(assemble(assembly)))
} }
pub fn start_compiler() { pub fn start_compiler() {
let lines = io::stdin().lines(); let lines = io::stdin().lines();
for line in lines.map_while(Result::ok) { for line in lines.map_while(Result::ok) {
println!(); match compile(&line) {
println!("{:?}", compile(&line)); Ok(_) => println!("\nCompilation OK :)\n"),
println!(); Err(e) => println!("\n{}\n", e),
}
} }
} }
@ -47,7 +48,7 @@ pub fn start_interpreter() {
#[allow(clippy::manual_flatten)] #[allow(clippy::manual_flatten)]
for line in lines { for line in lines {
if let Ok(code) = line { if let Ok(code) = line {
let tokens = tokenize(&code); let tokens = tokenize(&code).unwrap();
let ast = parse(&tokens); let ast = parse(&tokens);
let val = interpret(&ast, &mut SymTab::new_val_table()); let val = interpret(&ast, &mut SymTab::new_val_table());

View file

@ -139,39 +139,39 @@ fn test_empty() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_invalid_start() { fn test_invalid_start() {
parse(&tokenize("1 2 + 3")); parse(&tokenize("1 2 + 3").unwrap());
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn test_invalid_middle() { fn test_invalid_middle() {
parse(&tokenize("1 + 2 2 + 3")); parse(&tokenize("1 + 2 2 + 3").unwrap());
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn test_invalid_end() { fn test_invalid_end() {
parse(&tokenize("1 + 2 3")); parse(&tokenize("1 + 2 3").unwrap());
} }
#[test] #[test]
fn test_binary_op_basic() { fn test_binary_op_basic() {
let result = parse(&tokenize("1 + 23")); let result = parse(&tokenize("1 + 23").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "+", int_ast_b!(23))); assert_eq!(result, bin_ast!(int_ast_b!(1), "+", int_ast_b!(23)));
let result = parse(&tokenize("4 - 56")); let result = parse(&tokenize("4 - 56").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(4), "-", int_ast_b!(56))); assert_eq!(result, bin_ast!(int_ast_b!(4), "-", int_ast_b!(56)));
let result = parse(&tokenize("1 * 2")); let result = parse(&tokenize("1 * 2").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "*", int_ast_b!(2))); assert_eq!(result, bin_ast!(int_ast_b!(1), "*", int_ast_b!(2)));
let result = parse(&tokenize("1 / 2")); let result = parse(&tokenize("1 / 2").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "/", int_ast_b!(2))); assert_eq!(result, bin_ast!(int_ast_b!(1), "/", int_ast_b!(2)));
} }
#[test] #[test]
fn test_binary_op_all_levels() { fn test_binary_op_all_levels() {
let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7")); let result = parse(&tokenize("1 * 2 + 3 < 4 == 5 and 6 or 7").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -200,16 +200,16 @@ fn test_binary_op_all_levels() {
#[test] #[test]
fn test_binary_op_identifier() { fn test_binary_op_identifier() {
let result = parse(&tokenize("a + 1")); let result = parse(&tokenize("a + 1").unwrap());
assert_eq!(result, bin_ast!(id_ast_b!("a"), "+", int_ast_b!(1))); assert_eq!(result, bin_ast!(id_ast_b!("a"), "+", int_ast_b!(1)));
let result = parse(&tokenize("1 - a")); let result = parse(&tokenize("1 - a").unwrap());
assert_eq!(result, bin_ast!(int_ast_b!(1), "-", id_ast_b!("a"))); assert_eq!(result, bin_ast!(int_ast_b!(1), "-", id_ast_b!("a")));
} }
#[test] #[test]
fn test_binary_op_multiple() { fn test_binary_op_multiple() {
let result = parse(&tokenize("1 + 2 - 3")); let result = parse(&tokenize("1 + 2 - 3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -222,7 +222,7 @@ fn test_binary_op_multiple() {
#[test] #[test]
fn test_binary_op_precedence() { fn test_binary_op_precedence() {
let result = parse(&tokenize("1 + 2 * 3")); let result = parse(&tokenize("1 + 2 * 3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -232,7 +232,7 @@ fn test_binary_op_precedence() {
) )
); );
let result = parse(&tokenize("1 - 2 / 3")); let result = parse(&tokenize("1 - 2 / 3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -245,7 +245,7 @@ fn test_binary_op_precedence() {
#[test] #[test]
fn test_assignment_basic() { fn test_assignment_basic() {
let result = parse(&tokenize("a = 1 + 2")); let result = parse(&tokenize("a = 1 + 2").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -258,7 +258,7 @@ fn test_assignment_basic() {
#[test] #[test]
fn test_assignment_chain() { fn test_assignment_chain() {
let result = parse(&tokenize("a = b = 1 + 2")); let result = parse(&tokenize("a = b = 1 + 2").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -276,21 +276,21 @@ fn test_assignment_chain() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_assignment_invalid() { fn test_assignment_invalid() {
parse(&tokenize("a =")); parse(&tokenize("a =").unwrap());
} }
#[test] #[test]
fn test_unary_basic() { fn test_unary_basic() {
let result = parse(&tokenize("not x")); let result = parse(&tokenize("not x").unwrap());
assert_eq!(result, un_ast!("not", id_ast_b!("x"))); assert_eq!(result, un_ast!("not", id_ast_b!("x")));
let result = parse(&tokenize("-x")); let result = parse(&tokenize("-x").unwrap());
assert_eq!(result, un_ast!("-", id_ast_b!("x"))); assert_eq!(result, un_ast!("-", id_ast_b!("x")));
let result = parse(&tokenize("-1")); let result = parse(&tokenize("-1").unwrap());
assert_eq!(result, un_ast!("-", int_ast_b!(1))); assert_eq!(result, un_ast!("-", int_ast_b!(1)));
let result = parse(&tokenize("-1 + 2")); let result = parse(&tokenize("-1 + 2").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!(un_ast_b!("-", int_ast_b!(1)), "+", int_ast_b!(2)) bin_ast!(un_ast_b!("-", int_ast_b!(1)), "+", int_ast_b!(2))
@ -299,16 +299,16 @@ fn test_unary_basic() {
#[test] #[test]
fn test_unary_chain() { fn test_unary_chain() {
let result = parse(&tokenize("not not x")); let result = parse(&tokenize("not not x").unwrap());
assert_eq!(result, un_ast!("not", un_ast_b!("not", id_ast_b!("x")))); assert_eq!(result, un_ast!("not", un_ast_b!("not", id_ast_b!("x"))));
let result = parse(&tokenize("--x")); let result = parse(&tokenize("--x").unwrap());
assert_eq!(result, un_ast!("-", un_ast_b!("-", id_ast_b!("x")))); assert_eq!(result, un_ast!("-", un_ast_b!("-", id_ast_b!("x"))));
let result = parse(&tokenize("--1")); let result = parse(&tokenize("--1").unwrap());
assert_eq!(result, un_ast!("-", un_ast_b!("-", int_ast_b!(1)))); assert_eq!(result, un_ast!("-", un_ast_b!("-", int_ast_b!(1))));
let result = parse(&tokenize("--1 + 2")); let result = parse(&tokenize("--1 + 2").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -321,7 +321,7 @@ fn test_unary_chain() {
#[test] #[test]
fn test_parenthesized() { fn test_parenthesized() {
let result = parse(&tokenize("(1+2)*3")); let result = parse(&tokenize("(1+2)*3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -334,7 +334,7 @@ fn test_parenthesized() {
#[test] #[test]
fn test_parenthesized_nested() { fn test_parenthesized_nested() {
let result = parse(&tokenize("((1 - 2))/3")); let result = parse(&tokenize("((1 - 2))/3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -344,7 +344,7 @@ fn test_parenthesized_nested() {
) )
); );
let result = parse(&tokenize("((1 + 2)*3) / 4")); let result = parse(&tokenize("((1 + 2)*3) / 4").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -362,12 +362,12 @@ fn test_parenthesized_nested() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_parenthesized_mismatched() { fn test_parenthesized_mismatched() {
parse(&tokenize("(1+2*3")); parse(&tokenize("(1+2*3").unwrap());
} }
#[test] #[test]
fn test_if_then() { fn test_if_then() {
let result = parse(&tokenize("if 1 + 2 then 3")); let result = parse(&tokenize("if 1 + 2 then 3").unwrap());
assert_eq!( assert_eq!(
result, result,
con_ast!( con_ast!(
@ -380,7 +380,7 @@ fn test_if_then() {
#[test] #[test]
fn test_if_then_else() { fn test_if_then_else() {
let result = parse(&tokenize("if a then b + c else 1 * 2")); let result = parse(&tokenize("if a then b + c else 1 * 2").unwrap());
assert_eq!( assert_eq!(
result, result,
con_ast!( con_ast!(
@ -393,7 +393,7 @@ fn test_if_then_else() {
#[test] #[test]
fn test_if_then_else_embedded() { fn test_if_then_else_embedded() {
let result = parse(&tokenize("1 + if true then 2 else 3")); let result = parse(&tokenize("1 + if true then 2 else 3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -406,7 +406,7 @@ fn test_if_then_else_embedded() {
#[test] #[test]
fn test_if_then_else_nested() { fn test_if_then_else_nested() {
let result = parse(&tokenize("if true then if false then 1 else 2 else 3")); let result = parse(&tokenize("if true then if false then 1 else 2 else 3").unwrap());
assert_eq!( assert_eq!(
result, result,
con_ast!( con_ast!(
@ -420,15 +420,15 @@ fn test_if_then_else_nested() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_if_no_then() { fn test_if_no_then() {
parse(&tokenize("if true")); parse(&tokenize("if true").unwrap());
} }
#[test] #[test]
fn test_func_basic() { fn test_func_basic() {
let result = parse(&tokenize("f(a, b)")); let result = parse(&tokenize("f(a, b)").unwrap());
assert_eq!(result, fun_ast!("f", vec![id_ast!("a"), id_ast!("b"),])); assert_eq!(result, fun_ast!("f", vec![id_ast!("a"), id_ast!("b"),]));
let result = parse(&tokenize("f(a, 1 + 2)")); let result = parse(&tokenize("f(a, 1 + 2)").unwrap());
assert_eq!( assert_eq!(
result, result,
fun_ast!( fun_ast!(
@ -437,13 +437,13 @@ fn test_func_basic() {
) )
); );
let result = parse(&tokenize("f()")); let result = parse(&tokenize("f()").unwrap());
assert_eq!(result, fun_ast!("f", vec![])); assert_eq!(result, fun_ast!("f", vec![]));
} }
#[test] #[test]
fn test_func_embedded() { fn test_func_embedded() {
let result = parse(&tokenize("1 + f(a)")); let result = parse(&tokenize("1 + f(a)").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!(int_ast_b!(1), "+", fun_ast_b!("f", vec![id_ast!("a")])) bin_ast!(int_ast_b!(1), "+", fun_ast_b!("f", vec![id_ast!("a")]))
@ -452,7 +452,7 @@ fn test_func_embedded() {
#[test] #[test]
fn test_func_nested() { fn test_func_nested() {
let result = parse(&tokenize("f(a, g(b))")); let result = parse(&tokenize("f(a, g(b))").unwrap());
assert_eq!( assert_eq!(
result, result,
fun_ast!("f", vec![id_ast!("a"), fun_ast!("g", vec![id_ast!("b")]),]) fun_ast!("f", vec![id_ast!("a"), fun_ast!("g", vec![id_ast!("b")]),])
@ -462,18 +462,18 @@ fn test_func_nested() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_func_missing_comma() { fn test_func_missing_comma() {
parse(&tokenize("f(a b)")); parse(&tokenize("f(a b)").unwrap());
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn test_func_missing_close() { fn test_func_missing_close() {
parse(&tokenize("f(a")); parse(&tokenize("f(a").unwrap());
} }
#[test] #[test]
fn test_block_basic() { fn test_block_basic() {
let result = parse(&tokenize("{ a = 1; b; }")); let result = parse(&tokenize("{ a = 1; b; }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -483,7 +483,7 @@ fn test_block_basic() {
]) ])
); );
let result = parse(&tokenize("{ a = 1; b }")); let result = parse(&tokenize("{ a = 1; b }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -495,7 +495,7 @@ fn test_block_basic() {
#[test] #[test]
fn test_block_embedded() { fn test_block_embedded() {
let result = parse(&tokenize("{ 1 + 2 } * 3")); let result = parse(&tokenize("{ 1 + 2 } * 3").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -508,7 +508,7 @@ fn test_block_embedded() {
#[test] #[test]
fn test_block_nested() { fn test_block_nested() {
let result = parse(&tokenize("{ a = { 1 + 2}}")); let result = parse(&tokenize("{ a = { 1 + 2}}").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![bin_ast!( block_ast!(vec![bin_ast!(
@ -522,21 +522,21 @@ fn test_block_nested() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_block_unmatched() { fn test_block_unmatched() {
parse(&tokenize("{ a = 1 ")); parse(&tokenize("{ a = 1 ").unwrap());
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn test_block_missing_semicolon() { fn test_block_missing_semicolon() {
parse(&tokenize("{ a = 1\nb }")); parse(&tokenize("{ a = 1\nb }").unwrap());
} }
#[test] #[test]
fn test_var_basic() { fn test_var_basic() {
let result = parse(&tokenize("var x = 1")); let result = parse(&tokenize("var x = 1").unwrap());
assert_eq!(result, var_ast!("x", int_ast_b!(1), None)); assert_eq!(result, var_ast!("x", int_ast_b!(1), None));
let result = parse(&tokenize("{ var x = 1; x = 2; }")); let result = parse(&tokenize("{ var x = 1; x = 2; }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -549,7 +549,7 @@ fn test_var_basic() {
#[test] #[test]
fn test_var_typed() { fn test_var_typed() {
let result = parse(&tokenize("var x: Int = 1")); let result = parse(&tokenize("var x: Int = 1").unwrap());
assert_eq!( assert_eq!(
result, result,
var_ast!( var_ast!(
@ -562,7 +562,7 @@ fn test_var_typed() {
) )
); );
let result = parse(&tokenize("var x: Bool = true")); let result = parse(&tokenize("var x: Bool = true").unwrap());
assert_eq!( assert_eq!(
result, result,
var_ast!( var_ast!(
@ -579,18 +579,18 @@ fn test_var_typed() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_var_chain() { fn test_var_chain() {
parse(&tokenize("var x = var y = 1")); parse(&tokenize("var x = var y = 1").unwrap());
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn test_var_embedded() { fn test_var_embedded() {
parse(&tokenize("if true then var x = 3")); parse(&tokenize("if true then var x = 3").unwrap());
} }
#[test] #[test]
fn test_omitting_semicolons() { fn test_omitting_semicolons() {
let result = parse(&tokenize("{ { a } { b } }")); let result = parse(&tokenize("{ { a } { b } }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -599,7 +599,7 @@ fn test_omitting_semicolons() {
]) ])
); );
let result = parse(&tokenize("{ if true then { a } b }")); let result = parse(&tokenize("{ if true then { a } b }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -608,7 +608,7 @@ fn test_omitting_semicolons() {
]) ])
); );
let result = parse(&tokenize("{ if true then { a }; b }")); let result = parse(&tokenize("{ if true then { a }; b }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -617,7 +617,7 @@ fn test_omitting_semicolons() {
]) ])
); );
let result = parse(&tokenize("{ if true then { a } else { b } c }")); let result = parse(&tokenize("{ if true then { a } else { b } c }").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
@ -630,7 +630,7 @@ fn test_omitting_semicolons() {
]) ])
); );
let result = parse(&tokenize("x = { { f(a) } { b } }")); let result = parse(&tokenize("x = { { f(a) } { b } }").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -647,12 +647,12 @@ fn test_omitting_semicolons() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_omitting_semicolons_invalid() { fn test_omitting_semicolons_invalid() {
parse(&tokenize("{ if true then { a } b c }")); parse(&tokenize("{ if true then { a } b c }").unwrap());
} }
#[test] #[test]
fn test_while_do() { fn test_while_do() {
let result = parse(&tokenize("while 1 + 2 do 3")); let result = parse(&tokenize("while 1 + 2 do 3").unwrap());
assert_eq!( assert_eq!(
result, result,
while_ast!(bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)), int_ast_b!(3)) while_ast!(bin_ast_b!(int_ast_b!(1), "+", int_ast_b!(2)), int_ast_b!(3))
@ -661,7 +661,7 @@ fn test_while_do() {
#[test] #[test]
fn test_while_do_embedded() { fn test_while_do_embedded() {
let result = parse(&tokenize("1 + while true do 2")); let result = parse(&tokenize("1 + while true do 2").unwrap());
assert_eq!( assert_eq!(
result, result,
bin_ast!( bin_ast!(
@ -674,7 +674,7 @@ fn test_while_do_embedded() {
#[test] #[test]
fn test_while_do_nested() { fn test_while_do_nested() {
let result = parse(&tokenize("while true do while false do 1")); let result = parse(&tokenize("while true do while false do 1").unwrap());
assert_eq!( assert_eq!(
result, result,
while_ast!( while_ast!(
@ -687,18 +687,18 @@ fn test_while_do_nested() {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_while_no_do() { fn test_while_no_do() {
parse(&tokenize("while true")); parse(&tokenize("while true").unwrap());
} }
#[test] #[test]
fn test_multiple_top_levels() { fn test_multiple_top_levels() {
let result = parse(&tokenize("a;")); let result = parse(&tokenize("a;").unwrap());
assert_eq!(result, block_ast!(vec![id_ast!("a"), empty_ast!()])); assert_eq!(result, block_ast!(vec![id_ast!("a"), empty_ast!()]));
let result = parse(&tokenize("a; b")); let result = parse(&tokenize("a; b").unwrap());
assert_eq!(result, block_ast!(vec![id_ast!("a"), id_ast!("b")])); assert_eq!(result, block_ast!(vec![id_ast!("a"), id_ast!("b")]));
let result = parse(&tokenize("{}{}")); let result = parse(&tokenize("{}{}").unwrap());
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![block_ast!(vec![]), block_ast!(vec![])]) block_ast!(vec![block_ast!(vec![]), block_ast!(vec![])])
@ -707,7 +707,8 @@ fn test_multiple_top_levels() {
#[test] #[test]
fn test_large() { fn test_large() {
let result = parse(&tokenize( let result = parse(
&tokenize(
" "
{ {
while f() do { while f() do {
@ -723,7 +724,9 @@ fn test_large() {
123 123
} }
", ",
)); )
.unwrap(),
);
assert_eq!( assert_eq!(
result, result,

View file

@ -1,7 +1,22 @@
use std::{error::Error, fmt::Display};
use crate::compiler::token::{CodeLocation, Token, TokenType}; use crate::compiler::token::{CodeLocation, Token, TokenType};
use regex::Regex; use regex::Regex;
pub fn tokenize(code: &str) -> Vec<Token> { #[derive(Debug)]
pub struct TokenizeError {
message: String,
}
impl Display for TokenizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TokenizerError: {}", self.message)
}
}
impl Error for TokenizeError {}
pub fn tokenize(code: &str) -> Result<Vec<Token>, TokenizeError> {
// We only want to compile the regexes once // We only want to compile the regexes once
// The ordering of these is important! // The ordering of these is important!
let regexes = vec![ let regexes = vec![
@ -48,16 +63,19 @@ pub fn tokenize(code: &str) -> Vec<Token> {
} }
if !valid_token { if !valid_token {
panic!( return Err(TokenizeError {
"Invalid token on line {} in position {}", message: format!(
"Invalid token starting with '{}' on line {} in position {}",
&line[pos..pos + 1],
line_number + 1, line_number + 1,
pos + 1 pos + 1
); ),
});
} }
} }
} }
tokens Ok(tokens)
} }
#[cfg(test)] #[cfg(test)]
@ -66,7 +84,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_basic() { fn test_tokenize_basic() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("if 3 \n\twhile"); let result = tokenize("if 3 \n\twhile").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -81,7 +99,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_code_location() { fn test_tokenize_code_location() {
let result = tokenize("if 3\n while"); let result = tokenize("if 3\n while").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -113,7 +131,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_comment() { fn test_tokenize_comment() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("if 3 \n\n//Comment\n#Another\n\twhile //Comment2"); let result = tokenize("if 3 \n\n//Comment\n#Another\n\twhile //Comment2").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -129,7 +147,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_operators_basic() { fn test_tokenize_operators_basic() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("var = 1 + 2"); let result = tokenize("var = 1 + 2").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -147,7 +165,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_operators_all() { fn test_tokenize_operators_all() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("var 1 + - * 1/2 = == != < <= > >= 2 %"); let result = tokenize("var 1 + - * 1/2 = == != < <= > >= 2 %").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -177,7 +195,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_punctuation_basic() { fn test_tokenize_punctuation_basic() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("{var = (1 + 2, 3);:}"); let result = tokenize("{var = (1 + 2, 3);:}").unwrap();
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -203,6 +221,6 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_tokenize_wrong_token() { fn test_tokenize_wrong_token() {
tokenize("if 3\n while @"); tokenize("if 3\n while @").unwrap();
} }
} }

View file

@ -160,7 +160,10 @@ mod tests {
use Type::*; use Type::*;
fn get_type(code: &str) -> Type { fn get_type(code: &str) -> Type {
type_check(&mut parse(&tokenize(code)), &mut SymTab::new_type_table()) type_check(
&mut parse(&tokenize(code).unwrap()),
&mut SymTab::new_type_table(),
)
} }
#[test] #[test]
@ -317,14 +320,14 @@ mod tests {
#[test] #[test]
fn test_function() { fn test_function() {
let mut tokens = tokenize("foo(1)"); let mut tokens = tokenize("foo(1)").unwrap();
let mut ast = parse(&tokens); let mut ast = parse(&tokens);
let mut symtab = SymTab::new_type_table(); let mut symtab = SymTab::new_type_table();
symtab.insert("foo", Func(vec![Int], Box::new(Int))); symtab.insert("foo", Func(vec![Int], Box::new(Int)));
let result = type_check(&mut ast, &mut symtab); let result = type_check(&mut ast, &mut symtab);
assert_eq!(result, Int); assert_eq!(result, Int);
tokens = tokenize("foo(1);"); tokens = tokenize("foo(1);").unwrap();
ast = parse(&tokens); ast = parse(&tokens);
symtab = SymTab::new_type_table(); symtab = SymTab::new_type_table();
symtab.insert("foo", Func(vec![Int], Box::new(Int))); symtab.insert("foo", Func(vec![Int], Box::new(Int)));
@ -335,7 +338,7 @@ mod tests {
#[test] #[test]
#[should_panic] #[should_panic]
fn test_function_wrong_arg() { fn test_function_wrong_arg() {
let tokens = tokenize("foo(true)"); let tokens = tokenize("foo(true)").unwrap();
let mut ast = parse(&tokens); let mut ast = parse(&tokens);
let mut symtab = SymTab::new_type_table(); let mut symtab = SymTab::new_type_table();
symtab.insert("foo", Func(vec![Int], Box::new(Int))); symtab.insert("foo", Func(vec![Int], Box::new(Int)));
@ -344,7 +347,7 @@ mod tests {
#[test] #[test]
fn test_node_type() { fn test_node_type() {
let tokens = tokenize("1"); let tokens = tokenize("1").unwrap();
let mut ast = parse(&tokens); let mut ast = parse(&tokens);
let mut symtab = SymTab::new_type_table(); let mut symtab = SymTab::new_type_table();

View file

@ -37,7 +37,11 @@ fn handle_connection(mut stream: TcpStream) {
let program = json_request["code"].as_str().unwrap(); let program = json_request["code"].as_str().unwrap();
let output = compiler::compile(program); let output = compiler::compile(program);
let response = format!("{{\"program\": \"{output}\"}}"); let response = match output {
Ok(output) => format!("{{\"program\": \"{output}\"}}"),
Err(e) => format!("{{\"error\": \"{e}\"}}"),
};
stream.write_all(response.as_bytes()).unwrap(); stream.write_all(response.as_bytes()).unwrap();
} }
_ => panic!("Unexpected command!"), _ => panic!("Unexpected command!"),