aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-03-02 22:40:34 +0100
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitd640066d9390e3ba1abca183db9263b775c000d4 (patch)
tree4389abf1244ab930a225e4c33b2b5071d4d0d70f
parentaf74ddb119e3c5167a10bf5468af3960c8db42c0 (diff)
Readd requirement for semicolon, makes things much simpler (for user as well)
-rw-r--r--doc/DESIGN.md113
-rw-r--r--include/tokenizer.h3
-rw-r--r--src/parser.c31
-rw-r--r--src/tokenizer.c6
-rw-r--r--tests/main.amal6
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 {