Skip to content

feat: type checking for functions #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 20, 2024
3 changes: 2 additions & 1 deletion compiler/oxidate/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ mod tests {
fn exp_compile_str(inp: &str) -> Vec<ByteCode> {
let parser = Parser::new_from_string(inp);
let parsed = parser.parse().expect("Should parse");
dbg!(inp);
dbg!("parsed:", &parsed);
let comp = Compiler::new(parsed);
comp.compile().expect("Should compile")
}

fn test_comp(inp: &str, exp: Vec<ByteCode>) {
let res = exp_compile_str(inp);
dbg!(&res);
// dbg!(&res[28]);
assert_eq!(res, exp);
}

Expand Down
6 changes: 5 additions & 1 deletion example/concurrency-01.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Expected: prints in order

fn loop_and_print(x: int) {
let count = 0;

Expand All @@ -12,6 +14,8 @@ fn loop_and_print(x: int) {
}
}

print("Spawning 3 threads");

let thread_id_1 = spawn loop_and_print(1);
let thread_id_2 = spawn loop_and_print(2);
let thread_id_3 = spawn loop_and_print(3);
Expand All @@ -20,4 +24,4 @@ join thread_id_3;
join thread_id_2;
join thread_id_1;

println(true);
println("Done");
3 changes: 1 addition & 2 deletions example/concurrency-03.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Expected: inconsistent count on each run due to race on count
// When running with a small quantum e.g 1
// Expected: count != 3000 on each run, though we want 3000

let count = 0;

Expand Down
2 changes: 1 addition & 1 deletion example/concurrency-04.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Expected: count = 3000 on each run

let count = 0;
let sem = sem_create();
let sem : sem = sem_create();

fn increment(times: int) {
let i = 0;
Expand Down
5 changes: 2 additions & 3 deletions example/garbage-collection-02.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Compile untyped: hof type annotation not added yet
fn higher_order(x) {
fn g(y) {
fn higher_order(x: int) -> fn(int) -> int {
fn g(y:int) -> int {
x + y
}

Expand Down
12 changes: 7 additions & 5 deletions example/higher-order-fn-01.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// Compile untyped: hof type annotation not added yet

fn f(x) {
fn f(x: int) -> fn(int) -> int {
let z = 3;
fn g(y) {
fn g(y: int) -> int {
return x + y + z;
}

g
}

let hof = f(2);
// try uncommenting this line to get type error when compiling
// let hof : fn(int) -> bool = f(2);

let hof : fn(int) -> int = f(2);

// Expected: 9
hof(4)
3 changes: 0 additions & 3 deletions example/loop-04.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// Doesn't do anything in particular, I asked ChatGPT to generate this and verified
// by converting to Rust

let count = 0;
let x = 0;

Expand Down
14 changes: 9 additions & 5 deletions example/simple-spawn.rst
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
fn func() {
let j = 500;
loop j < 600 {
println(j);
let j = 0;
loop j < 100 {
println("in func");
j = j + 1;
}

println("func is done");
}



let tid = spawn func();

let i = 0;
loop i < 100 {
println(i);
loop i < 200 {
println("in main");
i = i + 1;
}

// Uncomment to ensure func finishes before main
//join tid;

println("main is done");
21 changes: 19 additions & 2 deletions src/parser/src/fn_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl<'inp> Parser<'inp> {
param_ty.replace(ty);

// to go past last token of type_ann, so peek is at comma or close paren
self.advance();
// self.advance();
}

// Comma or CloseParen
Expand Down Expand Up @@ -98,7 +98,7 @@ impl<'inp> Parser<'inp> {
if self.consume_opt_token_type(Token::FnDeclReturn) {
// peek is now at type_ann first token
let ret_ty_ann = self.parse_type_annotation()?;
self.advance(); // go past last token of ty_ann
// self.advance(); // go past last token of ty_ann

ret_ty = ret_ty_ann;
}
Expand Down Expand Up @@ -426,4 +426,21 @@ mod tests {
"fn fac (n:int) -> int { if (n==0) { return 1; };return (n*fac((n-1))); };",
);
}

#[test]
fn test_parse_fn_decl_hof_ret() {
let t = r"
fn adder(x : int) -> fn(int) -> bool {
fn f(y:int) -> bool {
x+y > 0
}

adder
}
";
test_parse(
t,
"fn adder (x:int) -> fn(int) -> bool { fn f (y:int) -> bool { ((x+y)>0) };adder };",
);
}
}
2 changes: 1 addition & 1 deletion src/parser/src/let_stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl<'inp> Parser<'inp> {
type_ann.replace(ty);

// call advance so peek is at equals
self.advance();
// self.advance();
}

self.consume_token_type(Token::Eq, "Expected '='")?;
Expand Down
69 changes: 2 additions & 67 deletions src/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod ident;
pub mod if_else;
pub mod let_stmt;
pub mod parse_loop;
pub mod parse_type_ann;
pub mod seq;
pub mod structs;

Expand Down Expand Up @@ -135,7 +136,7 @@ impl<'inp> Parser<'inp> {
fn expect_token_for_type_ann(token: Option<&Result<Token, ()>>) -> Result<(), ParseError> {
if let Some(Ok(tok)) = token {
match tok {
Token::Ident(_) | Token::OpenParen => Ok(()),
Token::Ident(_) | Token::OpenParen | Token::Fn => Ok(()),
_ => {
let e = format!(
"Expected identifier or '(' for type annotation, got '{}'",
Expand All @@ -150,39 +151,6 @@ impl<'inp> Parser<'inp> {
))
}
}

/// Parse and return type annotation. Expect lexer.peek() to be at Colon before call
// Should only consume tokens belonging to the annotation, starting peek at first token and ending
// peek at the last token of the annotation
fn parse_type_annotation(&mut self) -> Result<Type, ParseError> {
// self.consume_token_type(Token::Colon, "Expected a colon")?;
// expect_token_body!(self.lexer.peek(), Ident, "identifier")?;
Parser::expect_token_for_type_ann(self.lexer.peek())?;

// if ident, get the string and try to convert type. else, handle specially
let peek = self
.lexer
.peek()
.unwrap()
.to_owned()
.expect("Lexer should not fail"); // would have erred earlier

let type_ann = match peek {
Token::Ident(id) => Type::from_string(&id),
Token::OpenParen => {
self.advance();
if let Some(Ok(Token::CloseParen)) = self.lexer.peek() {
Ok(Type::Unit)
} else {
Err(ParseError::new("Expected '()' for unit type annotation"))
}
}
_ => unreachable!(),
}?;

Ok(type_ann)
}

/* Precedence */

// Return (left bp, right bp)
Expand Down Expand Up @@ -384,39 +352,6 @@ mod tests {
);
}

#[test]
fn test_parse_type_annotations() {
test_parse("let x : int = 2;", "let x : int = 2;");
test_parse("let x : bool = true;", "let x : bool = true;");
test_parse("let x : float = true;", "let x : float = true;");
test_parse("let x : () = true;", "let x : () = true;");
}

#[test]
fn test_parse_type_annotations_errs() {
// test_parse("let x : int = 2;", "");
test_parse_err(
"let x : let ",
"Expected identifier or '(' for type annotation, got 'let'",
true,
);
test_parse_err(
"let x : 2 ",
"Expected identifier or '(' for type annotation, got '2'",
true,
);
test_parse_err(
"let x : ",
"Expected identifier or '(' for type annotation, got end of input",
true,
);
test_parse_err(
"let x : (2 ",
"Expected '()' for unit type annotation",
true,
);
}

#[test]
fn test_parse_concurrency() {
let t = r"
Expand Down
Loading
Loading