about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--grammar.ebnf4
-rw-r--r--src/bootstrap/main.src5
-rw-r--r--src/bootstrap/tokenizer.src6
-rw-r--r--src/codegen.zig13
-rw-r--r--src/main.zig15
-rw-r--r--src/parser.zig58
-rw-r--r--src/tokenizer.zig16
7 files changed, 101 insertions, 16 deletions
diff --git a/grammar.ebnf b/grammar.ebnf
index 18d71da..70a1bfd 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -1,9 +1,11 @@
 Program      ::= Statement+
 
-Statement    ::= (AssignmentStatement | ExternDeclaration | FunctionCallStatement | IfStatement | WhileStatement | ReturnStatement) SEMICOLON
+Statement    ::= (AssignmentStatement | ImportDeclaration | ExternDeclaration | FunctionCallStatement | IfStatement | WhileStatement | ReturnStatement) SEMICOLON
 
 AssignmentStatement ::= ("let")? ("*")? Expression EQUALS Expression
 
+ImportDeclaration ::= "import" STRING
+
 ExternDeclaration ::= "extern" IDENTIFIER EQUALS Type
 
 FunctionCallStatement ::= (IDENTIFIER | FunctionDefinition) LPAREN FunctionArguments? RPAREN
diff --git a/src/bootstrap/main.src b/src/bootstrap/main.src
index b953e3d..22af2d1 100644
--- a/src/bootstrap/main.src
+++ b/src/bootstrap/main.src
@@ -1,4 +1,3 @@
-extern printf = (*i8, varargs) => void;
 extern fopen = (*i8, *i8) => *i8;
 extern fgets = (*i8, i64, *i8) => void;
 extern feof = (*i8) => bool;
@@ -9,6 +8,8 @@ extern fclose = (*i8) => *i8;
 extern malloc = (i64) => *i8;
 extern free = (*i8) => void;
 
+import "tokenizer.src";
+
 let file_size = 0;
 let file = 0;
 let buf = 0;
@@ -55,5 +56,7 @@ let main = (argc: i64, argv: **i8) => i64 {
 	free(buf);
 	fclose(file);
 
+	printf("TEST: %d\n", test());
+
 	return 0;
 };
diff --git a/src/bootstrap/tokenizer.src b/src/bootstrap/tokenizer.src
new file mode 100644
index 0000000..751a0ac
--- /dev/null
+++ b/src/bootstrap/tokenizer.src
@@ -0,0 +1,6 @@
+extern printf = (*i8, varargs) => void;
+
+let test = () => i64 {
+	printf("HELLO\n");
+	return 2;
+};
diff --git a/src/codegen.zig b/src/codegen.zig
index a6d8ed0..38febe6 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -116,6 +116,7 @@ pub const CodeGen = struct {
             .RETURN_STATEMENT => |*return_statement| return try self.generate_return_statement(@ptrCast(return_statement)),
             .IF_STATEMENT => |*if_statement| return try self.generate_if_statement(@ptrCast(if_statement)),
             .WHILE_STATEMENT => |*while_statement| return try self.generate_while_statement(@ptrCast(while_statement)),
+            .IMPORT_DECLARATION => |*import_declaration| return try self.generate_import_declaration(@ptrCast(import_declaration)),
             else => unreachable,
         }
     }
@@ -170,6 +171,7 @@ pub const CodeGen = struct {
             if (assignment_statement.is_declaration) {
                 try self.environment.add_variable(identifier.name, new_variable);
             } else {
+                // TODO: Dont allow changing types of variables if its not declaration
                 try self.environment.set_variable(identifier.name, new_variable);
             }
         } else {
@@ -288,6 +290,15 @@ pub const CodeGen = struct {
         llvm.LLVMPositionBuilderAtEnd(self.builder, outer_block);
     }
 
+    fn generate_import_declaration(self: *CodeGen, declaration: *parser.Node) !void {
+        errdefer std.debug.print("Error generating import declaration\n", .{});
+        std.debug.assert(declaration.* == parser.Node.IMPORT_DECLARATION);
+
+        const import_declaration = declaration.IMPORT_DECLARATION;
+
+        try self.generate(import_declaration.program);
+    }
+
     fn generate_expression_value(self: *CodeGen, expression: *parser.Node, name: ?[]const u8) !*Variable {
         errdefer std.debug.print("Error generating statement value\n", .{});
         return switch (expression.*) {
@@ -328,7 +339,7 @@ pub const CodeGen = struct {
                     },
                 });
 
-                // // Needed for recursive functions
+                // Needed for recursive functions
                 if (name != null) {
                     try self.environment.add_variable(name.?, try self.create_variable(.{
                         .value = function,
diff --git a/src/main.zig b/src/main.zig
index 5ddd750..79def61 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -25,26 +25,19 @@ pub fn main() !void {
     defer source_codegen.deinit();
     try process_buf(
         buf,
-        allocator,
         arena.allocator(),
         source_codegen,
+        path,
     );
     source_codegen.compile();
 }
 
-fn process_buf(buf: []u8, allocator: std.mem.Allocator, arena: std.mem.Allocator, source_codegen: ?*codegen.CodeGen) !void {
+fn process_buf(buf: []u8, arena: std.mem.Allocator, source_codegen: ?*codegen.CodeGen, filename: []const u8) !void {
     std.debug.print("Buf:\n{s}\n", .{buf});
 
-    var token_list = std.ArrayList(tokenizer.Token).init(allocator);
-    defer token_list.deinit();
-
     var source_tokenizer = try tokenizer.Tokenizer.init(buf, arena);
-    while (try source_tokenizer.next()) |token| {
-        std.debug.print("{any}\n", .{token});
-        try token_list.append(token);
-    }
-
-    const source_parser = try parser.Parser.init(token_list.items, arena);
+    const token_list = try source_tokenizer.tokenize();
+    const source_parser = try parser.Parser.init(token_list, arena, filename);
     const ast = try source_parser.parse();
     std.debug.print("AST: {any}\n", .{ast});
 
diff --git a/src/parser.zig b/src/parser.zig
index 7ed1cb3..748d08b 100644
--- a/src/parser.zig
+++ b/src/parser.zig
@@ -19,6 +19,10 @@ pub const Node = union(enum) {
         lhs: *Node,
         rhs: *Node,
     },
+    IMPORT_DECLARATION: struct {
+        filename: []const u8,
+        program: *Node,
+    },
     FUNCTION_CALL_STATEMENT: struct {
         expression: *Node,
         arguments: []*Node,
@@ -103,6 +107,8 @@ pub const MultiplicativeExpressionType = enum {
 };
 
 pub const Parser = struct {
+    filename: []const u8,
+
     tokens: []tokenizer.Token,
     offset: u32,
 
@@ -110,9 +116,10 @@ pub const Parser = struct {
 
     try_context: bool, //TODO: I dont like this
 
-    pub fn init(tokens: []tokenizer.Token, arena_allocator: std.mem.Allocator) ParserError!*Parser {
+    pub fn init(tokens: []tokenizer.Token, arena_allocator: std.mem.Allocator, filename: []const u8) ParserError!*Parser {
         const parser = try arena_allocator.create(Parser);
         parser.* = .{
+            .filename = filename,
             .tokens = tokens,
             .offset = 0,
             .arena = arena_allocator,
@@ -146,6 +153,7 @@ pub const Parser = struct {
             self.accept_parse(parse_while_statement) orelse
             self.accept_parse(parse_return_statement) orelse
             self.accept_parse(parse_assignment_statement) orelse
+            self.accept_parse(parse_import_declaration) orelse
             try self.parse_extern_declaration();
 
         _ = try self.parse_token(tokenizer.TokenType.SEMICOLON);
@@ -187,6 +195,54 @@ pub const Parser = struct {
         });
     }
 
+    // ImportDeclaration ::= "import" STRING
+    fn parse_import_declaration(self: *Parser) ParserError!*Node {
+        errdefer if (!self.try_context) std.debug.print("Error parsing import declaration {any}\n", .{self.peek_token()});
+
+        _ = try self.parse_token(.IMPORT);
+
+        const expr = try self.parse_primary_expression();
+
+        std.debug.assert(expr.PRIMARY_EXPRESSION == .STRING);
+
+        const filename = expr.PRIMARY_EXPRESSION.STRING.value;
+
+        // Open the directory containing self.filename
+        const dir_path = std.fs.path.dirname(self.filename) orelse ".";
+        var x = std.fs.cwd().openDir(dir_path, .{}) catch {
+            std.debug.print("COULDNT OPEN DIR {s}\n", .{self.filename});
+            return ParserError.OutOfMemory;
+        };
+        defer x.close();
+
+        // Open the target file
+        const file = x.openFile(filename, .{}) catch {
+            std.debug.print("COULDNT OPEN FILENAME {s}\n", .{filename});
+            return ParserError.OutOfMemory;
+        };
+        defer file.close();
+
+        // Read file contents
+        const buf = file.readToEndAlloc(self.arena, 1 * 1024 * 1024) catch return ParserError.OutOfMemory;
+
+        // Initialize tokenizer and parse
+        var inner_tokenizer = try tokenizer.Tokenizer.init(buf, self.arena);
+        const tokens = inner_tokenizer.tokenize() catch return ParserError.OutOfMemory;
+
+        // Resolve the full path of the imported file
+        const full_path = try std.fs.path.resolve(self.arena, &.{ dir_path, filename });
+
+        const inner_parser = try Parser.init(tokens, self.arena, full_path);
+        const ast = try inner_parser.parse();
+
+        return self.create_node(.{
+            .IMPORT_DECLARATION = .{
+                .filename = filename,
+                .program = ast,
+            },
+        });
+    }
+
     // ExternDeclaration ::= "extern" IDENTIFIER EQUALS Type
     fn parse_extern_declaration(self: *Parser) ParserError!*Node {
         errdefer if (!self.try_context) std.debug.print("Error parsing extern declaration {any}\n", .{self.peek_token()});
diff --git a/src/tokenizer.zig b/src/tokenizer.zig
index 2b57b8d..53e5c63 100644
--- a/src/tokenizer.zig
+++ b/src/tokenizer.zig
@@ -6,6 +6,7 @@ const TokenizerError = error{
 
 pub const TokenType = union(enum) {
     // Keywords
+    IMPORT: void,
     LET: void,
     EXTERN: void,
     IF: void,
@@ -64,13 +65,26 @@ pub const Tokenizer = struct {
         return Tokenizer{ .buf = buf, .offset = 0, .arena = arena };
     }
 
-    pub fn next(self: *Tokenizer) TokenizerError!?Token {
+    pub fn tokenize(self: *Tokenizer) ![]Token {
+        var token_list = std.ArrayList(Token).init(self.arena);
+
+        while (try self.next()) |token| {
+            std.debug.print("{any}\n", .{token});
+            try token_list.append(token);
+        }
+
+        return token_list.items;
+    }
+
+    fn next(self: *Tokenizer) TokenizerError!?Token {
         self.skip_whitespace();
         self.skip_comments();
         self.skip_whitespace();
 
         if (self.offset >= self.buf.len) return null;
 
+        if (self.accept_string("import")) return self.create_token(.{ .IMPORT = void{} });
+
         if (self.accept_string("let")) return self.create_token(.{ .LET = void{} });
         if (self.accept_string("extern")) return self.create_token(.{ .EXTERN = void{} });
         if (self.accept_string("if")) return self.create_token(.{ .IF = void{} });