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")