diff --git a/examples/sqlite_todo.zr b/examples/sqlite_todo.zr index f05bd6b..99ffe0b 100644 --- a/examples/sqlite_todo.zr +++ b/examples/sqlite_todo.zr @@ -10,6 +10,9 @@ extern sqlite3_column_int extern sqlite3_column_text extern sqlite3_finalize +const SQLITE_ROW = 100 +const SQLITE_DONE = 101 + func main[] : i64 rc := 0 db := 0 @@ -38,7 +41,7 @@ func main[] : i64 io.println("============") sqlite3_prepare_v2(db, "SELECT * FROM todo", -1, ^stmt, 0) - while sqlite3_step(stmt) == 100 + while sqlite3_step(stmt) == SQLITE_ROW id := sqlite3_column_int(stmt, 0) as i64 task := sqlite3_column_text(stmt, 1) as str io.printf("%d - %s\n", id, task) @@ -50,7 +53,7 @@ func main[] : i64 sqlite3_prepare_v2(db, "INSERT INTO todo(task) VALUES (?);", -1, ^stmt, 0) sqlite3_bind_text(stmt, 1, task, -1, 0) - if sqlite3_step(stmt) != 101 + if sqlite3_step(stmt) != SQLITE_DONE panic(sqlite3_errmsg(db)) io.println("\nTask added\n") @@ -60,7 +63,7 @@ func main[] : i64 sqlite3_prepare_v2(db, "DELETE FROM todo WHERE id = ?;", -1, ^stmt, 0) sqlite3_bind_int(stmt, 1, id, -1, 0) - if sqlite3_step(stmt) != 101 + if sqlite3_step(stmt) != SQLITE_DONE panic(sqlite3_errmsg(db)) io.println("\nTask deleted\n") diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index c62e94e..988fd97 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -351,7 +351,8 @@ _builtin_environ: if self.emit_debug || *exported || name == "main" { emit!( &mut self.output, - "global {name}:function (__end_{name} - {name})", + "global {0}:function ({0}.end - {0})", + name ); } emit!(&mut self.output, "section .text.{}", name); @@ -405,9 +406,7 @@ _builtin_environ: emit!(&mut self.output, " ret"); } - if self.emit_debug || *exported || name == "main" { - emit!(&mut self.output, "__end_{}:", name); - } + emit!(&mut self.output, ".end:"); // patch the stack size after we know how much we actually need let patch = format!(" sub rsp, {:<10}", (env.next_offset + 15) & !15); @@ -481,7 +480,10 @@ _builtin_environ: Stmt::Extern(name) => { emit!(&mut self.output, "extern {}", name.lexeme); } - Stmt::Struct { name: _, fields: _ } => { + Stmt::Struct { .. } => { + // handled in SymbolTable + } + Stmt::Enum { .. } => { // handled in SymbolTable } } diff --git a/src/parser.rs b/src/parser.rs index 71f2850..e3d3ced 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -77,6 +77,10 @@ pub enum Stmt { name: Token, fields: Vec, }, + Enum { + name: Token, + variants: Vec, + }, } pub static NEXT_EXPR_ID: AtomicUsize = AtomicUsize::new(0); @@ -191,6 +195,9 @@ impl Parser { if self.match_token(&[TokenType::KeywordStruct]) { return self.struct_declaration(); } + if self.match_token(&[TokenType::KeywordEnum]) { + return self.enum_declaration(); + } return error!( self.peek().loc, "statements not allowed outside function body" @@ -269,11 +276,26 @@ impl Parser { fields.push(Param { var_type, var_name }); } - self.consume(TokenType::Dedent, "expected dedent after the struct fields")?; + self.consume(TokenType::Dedent, "expected dedent after struct fields")?; Ok(Stmt::Struct { name, fields }) } + fn enum_declaration(&mut self) -> Result { + let name = self.consume(TokenType::Identifier, "expected enum name")?; + + self.consume(TokenType::Indent, "expected indent after enum name")?; + + let mut variants = vec![]; + while !self.eof() && !self.check(&TokenType::Dedent) { + variants.push(self.consume(TokenType::Identifier, "expected variant name")?); + } + + self.consume(TokenType::Dedent, "expected dedent after enum variants")?; + + Ok(Stmt::Enum { name, variants }) + } + fn const_declaration(&mut self) -> Result { let name = self.consume(TokenType::Identifier, "expected const name")?; self.consume(TokenType::Equal, "expected '=' after const name")?; diff --git a/src/symbol_table.rs b/src/symbol_table.rs index deacdb4..183442b 100644 --- a/src/symbol_table.rs +++ b/src/symbol_table.rs @@ -140,6 +140,16 @@ impl SymbolTable { self.structs.insert(name.lexeme.clone(), fields_map); } + Stmt::Enum { name, variants } => { + // TODO: distinct enum types + for (i, v) in variants.iter().enumerate() { + let variant = format!("{}.{}", name.lexeme, v.lexeme); + if self.is_name_defined(&variant) { + return error!(name.loc, format!("tried to redefine '{}'", variant)); + } + self.constants.insert(variant, i as i64); + } + } _ => {} } Ok(()) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index a5a4322..583e790 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -57,6 +57,7 @@ pub enum TokenType { KeywordStruct, KeywordNew, KeywordAs, + KeywordEnum, Indent, Dedent, @@ -376,6 +377,7 @@ impl Tokenizer { "struct" => TokenType::KeywordStruct, "new" => TokenType::KeywordNew, "as" => TokenType::KeywordAs, + "enum" => TokenType::KeywordEnum, "true" => TokenType::True, "false" => TokenType::False, _ => TokenType::Identifier, diff --git a/src/typechecker.rs b/src/typechecker.rs index 9a33150..57ae6e4 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -355,6 +355,7 @@ impl<'a> TypeChecker<'a> { } } } + Stmt::Enum { .. } => {} } Ok(()) } diff --git a/test.zr b/test.zr index d4864d1..3747676 100644 --- a/test.zr +++ b/test.zr @@ -31,6 +31,7 @@ func run_test[x: str] : void run_start_time := os.time() ~status, ok := os.run_shell_command(run_cmd) if !ok || status != 0 + io.println("Test failed.") os.exit(1) run_end_time := os.time() @@ -50,3 +51,4 @@ func main[] : i64 run_directory("examples/") run_directory("examples/euler/") + io.println("DONE")