diff options
-rw-r--r-- | doc/DESIGN.md | 113 | ||||
-rw-r--r-- | include/tokenizer.h | 3 | ||||
-rw-r--r-- | src/parser.c | 31 | ||||
-rw-r--r-- | src/tokenizer.c | 6 | ||||
-rw-r--r-- | tests/main.amal | 6 |
5 files changed, 94 insertions, 65 deletions
diff --git a/doc/DESIGN.md b/doc/DESIGN.md index e3d7bf1..37ca82a 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -12,15 +12,15 @@ const main = fn { ## Conditions ``` const main = fn { - var value = 23 + 50 + var value = 23 + 50; if value < 23 - stderr.writeln("less!") + stderr.writeln("less!"); else - stderr.writeln("more!") + stderr.writeln("more!"); while value > 0 { - stderr.writeln("value: {}", value) - value -= 1 + stderr.writeln("value: {}", value); + value -= 1; } } ``` @@ -29,63 +29,63 @@ const main = fn { Parentheses after `fn` is only required when the closure has parameters or returns data ``` const apply = fn(func: () bool) { - const result = func() + const result = func(); } const main = fn { // Return type is automatically deduced. If function returns multiple different types at different points, // then you get an error and are required to specify the return type apply(fn { - return true + return true; }) apply(fn() bool { - return true + return true; }) // Or store in a variable and use it const func = fn { - return true + return true; } - apply(func) + apply(func); } ``` ## Data types ``` const main = fn() !void { - var v1: i32 = 50 - var v2: u32 = 50 + var v1: i32 = 50; + var v2: u32 = 50; - v1 = v2 // error, v2 can't be implicitly cast to v1 because i32 can't represent the same values as u32 - v1 = @cast(i32, v2) // ok, explicitly cast u32 to i32 + v1 = v2; // error, v2 can't be implicitly cast to v1 because i32 can't represent the same values as u32 + v1 = @cast(i32, v2); // ok, explicitly cast u32 to i32 - var str1 = "hello" - var str2 = "world" - var str3 = try str1 + " " + str2 - var str4 = try str1 + 20 // error, can't add number to string. Preferable use str.fmt or explicitly cast to string - var str5 = try str1 + str(20) // ok, number explicitly cast to string + var str1 = "hello"; + var str2 = "world"; + var str3 = try str1 + " " + str2; + var str4 = try str1 + 20; // error, can't add number to string. Preferable use str.fmt or explicitly cast to string + var str5 = try str1 + str(20); // ok, number explicitly cast to string - const str6 = "hello" - const str7 = "world" - const str8 = str6 + " " + str7 // ok, all variable involved are const. They can be combined at compile-time + const str6 = "hello"; + const str7 = "world"; + const str8 = str6 + " " + str7; // ok, all variable involved are const. They can be combined at compile-time - stderr.writeln("{}, {} | {}", str1, str2, str3) // prints hello world | hello world + stderr.writeln("{}, {} | {}", str1, str2, str3); // prints hello world | hello world } ``` ## Dynamic allocation (array) ``` -const ArrayList = @import("std.array.ArrayList") +const ArrayList = @import("std.array.ArrayList"); const main = fn() !void { - var list = ArrayList(i32) - try list.add(23) - try list.add(50) - var value = list.get(40) + var list = ArrayList(i32); + try list.add(23); + try list.add(50); + var value = list.get(40); for val in list { - stdout.writeln("value: {}", val) + stdout.writeln("value: {}", val); } } ``` @@ -99,7 +99,7 @@ struct User { } const levelUp = fn(self: &User) { - self.level += 1 + self.level += 1; } const main = fn { @@ -114,10 +114,10 @@ const main = fn { level: 100 } - levelUp(user2) + levelUp(user2); // syntax sugar for calling a function with the first argument as // the variable before the dot (same thing as levelUp(user2)) - user2.levelUp() + user2.levelUp(); } ``` @@ -139,20 +139,20 @@ const createUser = fn(name: str, age: i32, level: i32 = 1) User { } const main = fn { - createUser(name: "John", level: 30, age: 30) - createUser(age: 40, name: "Titor") + createUser(name: "John", level: 30, age: 30); + createUser(age: 40, name: "Titor"); } ``` ## Generic programming ``` const add = fn(comptime T: type, a: T, b: T) !T { - return try a + b + return try a + b; } const main = fn { - var numberValue = add(20, 40) - var stringValue = add("hello", "world") + var numberValue = add(20, 40); + var stringValue = add("hello", "world"); } ``` @@ -176,40 +176,40 @@ const addUserToList = fn(list: &ArrayList(User), user: User) { } const main = fn { - var users = ArrayList(User) + var users = ArrayList(User); users.add(User { name: "John", level: 34 - }) + }); const user1 = User { name: "David", level: 55 } // error, addUserToList expects user1 to be moved or copied - // addUserToList(users, user1) + // addUserToList(users, user1); - // addUserToList(users, clone user1) // ok, user1 has been copied to function scope - addUserToList(users, move user1) // ok, user1 has been moved to function scope + // addUserToList(users, clone user1); // ok, user1 has been copied to function scope + addUserToList(users, move user1); // ok, user1 has been moved to function scope - next(move users) + next(move users); } const getUserAtIndex = fn(list: &ArrayList(User), index: usize) User { - return list.get(index) + return list.get(index); } const next = fn(users: ArrayList(User)) { - const user = getUserAtIndex(users, 0) + const user = getUserAtIndex(users, 0); // Reallocatable example: addUserToList(users, User { name: "John", level: 34 - }) + }); // error, "user" can't be safely used because addUserToList on line XXX can reallocate "users" which "user" belongs to - stdout.writeln("user name: {}", user.name) + stdout.writeln("user name: {}", user.name); } ``` @@ -224,8 +224,7 @@ const main = fn { "doggy" ] } - - printMap(values) // stdout.writeln("{}", values) can also be used directly as it supports tables + printMap(values); // stdout.writeln("{}", values) can also be used directly as it supports tables } const printTable = fn(value: TableValue) { @@ -233,21 +232,21 @@ const printTable = fn(value: TableValue) { array => { // value type is automatically cast to array here, same with other cases in the switch for index, val in value { - stdout.write("[{}] = ", index) - printTable(val) - stdout.writeln(",") + stdout.write("[{}] = ", index); + printTable(val); + stdout.writeln(","); } } map => { stdout.writeln("{") for key, val in value { - stdout.write("'{}': ", key) - printTable(val) - stdout.writeln(",") + stdout.write("'{}': ", key); + printTable(val); + stdout.writeln(","); } - stdout.writeln("}") + stdout.writeln("}"); } - else => stdout.write(value) + else => stdout.write(value); } } ```
\ No newline at end of file diff --git a/include/tokenizer.h b/include/tokenizer.h index 6f00704..c58917f 100644 --- a/include/tokenizer.h +++ b/include/tokenizer.h @@ -25,7 +25,8 @@ typedef enum { TOK_CLOSING_BRACE, TOK_IMPORT, TOK_NUMBER, - TOK_DOT + TOK_DOT, + TOK_SEMICOLON } Token; typedef struct { diff --git a/src/parser.c b/src/parser.c index babb2d9..7588bda 100644 --- a/src/parser.c +++ b/src/parser.c @@ -266,18 +266,36 @@ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { } /* -BODY = (LHS '=' RHS_START) | - RHS_START +BODY = LHS ';' | + (LHS '=' RHS_START ';') | + (RHS_START ';') + +Note: Semicolon is not required for closures, structs and tables */ int parser_parse_body(Parser *self, Ast *ast) { + bool match; LhsExpr *lhs_expr; Ast rhs_expr; rhs_expr = ast_none(); return_if_error(parser_parse_lhs(self, &lhs_expr)); - if(lhs_expr) - return_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS)); + if(lhs_expr) { + return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); + if(match) + goto rhs; + + return_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); + if(!match) { + self->error = tokenizer_create_error(&self->tokenizer, "Expected '=' or ';'"); + return PARSER_UNEXPECTED_TOKEN; + } + + ast->type = AST_LHS; + ast->value.lhs_expr = lhs_expr; + return PARSER_OK; + } + rhs: return_if_error(parser_parse_rhs_start(self, &rhs_expr)); if(lhs_expr) { lhs_expr->rhs_expr = rhs_expr; @@ -286,6 +304,11 @@ int parser_parse_body(Parser *self, Ast *ast) { } else { *ast = rhs_expr; } + + /* TODO: Check for struct and tables */ + if(rhs_expr.type != AST_FUNCTION_DECL) + return_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); + return PARSER_OK; } diff --git a/src/tokenizer.c b/src/tokenizer.c index b996354..afaeb8e 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -247,6 +247,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } else if(c == '}') { ++self->index; *token = TOK_CLOSING_BRACE; + } else if(c == ';') { + ++self->index; + *token = TOK_SEMICOLON; } else if(c == '@') { const char *err_msg; ++self->index; @@ -361,6 +364,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_DOT: str = "."; break; + case TOK_SEMICOLON: + str = ";"; + break; default: str = "Unknown token"; break; diff --git a/tests/main.amal b/tests/main.amal index 955806f..26da7d6 100644 --- a/tests/main.amal +++ b/tests/main.amal @@ -1,11 +1,11 @@ -const io = @import("tests/io.amal") +const io = @import("tests/io.amal"); const main = fn { var hello = fn { } - const value = "hello" - print(value, "world", 356) + const value = "hello"; + print(value, "world", 356); } const print = fn { |