Browse Source

foreign_library allow for Pascal-style grouping

Ginger Bill 8 years ago
parent
commit
a8e458339b
10 changed files with 422 additions and 550 deletions
  1. 4 2
      core/opengl.odin
  2. 4 2
      core/os_linux.odin
  3. 5 3
      core/os_x.odin
  4. 5 4
      core/strconv.odin
  5. 1 1
      core/sys/wgl.odin
  6. 196 181
      core/sys/windows.odin
  7. 2 8
      src/check_expr.cpp
  8. 85 100
      src/checker.cpp
  9. 118 249
      src/parser.cpp
  10. 2 0
      src/tokenizer.cpp

+ 4 - 2
core/opengl.odin

@@ -1,5 +1,7 @@
-#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
-#foreign_system_library lib "gl" when ODIN_OS == "linux";
+foreign_system_library (
+	lib "opengl32.lib" when ODIN_OS == "windows";
+	lib "gl" when ODIN_OS == "linux";
+)
 import (
 	win32 "sys/windows.odin" when ODIN_OS == "windows";
 	"sys/wgl.odin" when ODIN_OS == "windows";

+ 4 - 2
core/os_linux.odin

@@ -1,5 +1,7 @@
-#foreign_system_library dl   "dl";
-#foreign_system_library libc "c";
+foreign_system_library (
+	dl   "dl";
+	libc "c";
+)
 import "strings.odin";
 
 type (

+ 5 - 3
core/os_x.odin

@@ -1,6 +1,8 @@
-#foreign_system_library dl   "dl";
-#foreign_system_library libc "c";
-import "fmt.odin";
+foreign_system_library (
+	dl   "dl";
+	libc "c";
+)
+
 import "strings.odin";
 
 type (

+ 5 - 4
core/strconv.odin

@@ -223,10 +223,11 @@ type Float_Info struct {
 	bias:     int,
 }
 
-var _f16_info = Float_Info{10, 5,   -15};
-var _f32_info = Float_Info{23, 8,  -127};
-var _f64_info = Float_Info{52, 11, -1023};
-
+var (
+	_f16_info = Float_Info{10, 5,   -15};
+	_f32_info = Float_Info{23, 8,  -127};
+	_f64_info = Float_Info{52, 11, -1023};
+)
 
 proc generic_ftoa(buf: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 {
 	var bits: u64;

+ 1 - 1
core/sys/wgl.odin

@@ -1,4 +1,4 @@
-#foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
+foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
 import . "windows.odin";
 
 const CONTEXT_MAJOR_VERSION_ARB          = 0x2091;

+ 196 - 181
core/sys/windows.odin

@@ -1,89 +1,96 @@
-#foreign_system_library "kernel32.lib" when ODIN_OS == "windows";
-#foreign_system_library "user32.lib"   when ODIN_OS == "windows";
-#foreign_system_library "gdi32.lib"    when ODIN_OS == "windows";
-#foreign_system_library "winmm.lib"    when ODIN_OS == "windows";
-#foreign_system_library "shell32.lib"  when ODIN_OS == "windows";
-
-type Handle    rawptr;
-type Hwnd      Handle;
-type Hdc       Handle;
-type Hinstance Handle;
-type Hicon     Handle;
-type Hcursor   Handle;
-type Hmenu     Handle;
-type Hbrush    Handle;
-type Hgdiobj   Handle;
-type Hmodule   Handle;
-type Wparam    uint;
-type Lparam    int;
-type Lresult   int;
-type Bool      i32;
-type WndProc   proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
-
+foreign_system_library (
+	"kernel32.lib" when ODIN_OS == "windows";
+	"user32.lib"   when ODIN_OS == "windows";
+	"gdi32.lib"    when ODIN_OS == "windows";
+	"winmm.lib"    when ODIN_OS == "windows";
+	"shell32.lib"  when ODIN_OS == "windows";
+)
+
+type (
+	Handle    rawptr;
+	Hwnd      Handle;
+	Hdc       Handle;
+	Hinstance Handle;
+	Hicon     Handle;
+	Hcursor   Handle;
+	Hmenu     Handle;
+	Hbrush    Handle;
+	Hgdiobj   Handle;
+	Hmodule   Handle;
+	Wparam    uint;
+	Lparam    int;
+	Lresult   int;
+	Bool      i32;
+	WndProc   proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
+)
 
 const INVALID_HANDLE = Handle(~int(0));
-
-const FALSE: Bool = 0;
-const TRUE:  Bool = 1;
-
-const CS_VREDRAW    = 0x0001;
-const CS_HREDRAW    = 0x0002;
-const CS_OWNDC      = 0x0020;
-const CW_USEDEFAULT = -0x80000000;
-
-const WS_OVERLAPPED       = 0;
-const WS_MAXIMIZEBOX      = 0x00010000;
-const WS_MINIMIZEBOX      = 0x00020000;
-const WS_THICKFRAME       = 0x00040000;
-const WS_SYSMENU          = 0x00080000;
-const WS_BORDER           = 0x00800000;
-const WS_CAPTION          = 0x00C00000;
-const WS_VISIBLE          = 0x10000000;
-const WS_POPUP            = 0x80000000;
-const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
-const WS_POPUPWINDOW      = WS_POPUP | WS_BORDER | WS_SYSMENU;
-
-const WM_DESTROY           = 0x0002;
-const WM_SIZE	             = 0x0005;
-const WM_CLOSE             = 0x0010;
-const WM_ACTIVATEAPP       = 0x001C;
-const WM_QUIT              = 0x0012;
-const WM_KEYDOWN           = 0x0100;
-const WM_KEYUP             = 0x0101;
-const WM_SIZING            = 0x0214;
-const WM_SYSKEYDOWN        = 0x0104;
-const WM_SYSKEYUP          = 0x0105;
-const WM_WINDOWPOSCHANGED  = 0x0047;
-const WM_SETCURSOR         = 0x0020;
-const WM_CHAR              = 0x0102;
-const WM_ACTIVATE          = 0x0006;
-const WM_SETFOCUS          = 0x0007;
-const WM_KILLFOCUS         = 0x0008;
-const WM_USER              = 0x0400;
-
-const WM_MOUSEWHEEL    = 0x020A;
-const WM_MOUSEMOVE     = 0x0200;
-const WM_LBUTTONDOWN   = 0x0201;
-const WM_LBUTTONUP     = 0x0202;
-const WM_LBUTTONDBLCLK = 0x0203;
-const WM_RBUTTONDOWN   = 0x0204;
-const WM_RBUTTONUP     = 0x0205;
-const WM_RBUTTONDBLCLK = 0x0206;
-const WM_MBUTTONDOWN   = 0x0207;
-const WM_MBUTTONUP     = 0x0208;
-const WM_MBUTTONDBLCLK = 0x0209;
-
-const PM_NOREMOVE = 0x0000;
-const PM_REMOVE   = 0x0001;
-const PM_NOYIELD  = 0x0002;
+const (
+	FALSE: Bool = 0;
+	TRUE        = 1;
+)
+
+const (
+	CS_VREDRAW    = 0x0001;
+	CS_HREDRAW    = 0x0002;
+	CS_OWNDC      = 0x0020;
+	CW_USEDEFAULT = -0x80000000;
+
+	WS_OVERLAPPED       = 0;
+	WS_MAXIMIZEBOX      = 0x00010000;
+	WS_MINIMIZEBOX      = 0x00020000;
+	WS_THICKFRAME       = 0x00040000;
+	WS_SYSMENU          = 0x00080000;
+	WS_BORDER           = 0x00800000;
+	WS_CAPTION          = 0x00C00000;
+	WS_VISIBLE          = 0x10000000;
+	WS_POPUP            = 0x80000000;
+	WS_OVERLAPPEDWINDOW = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
+	WS_POPUPWINDOW      = WS_POPUP | WS_BORDER | WS_SYSMENU;
+
+	WM_DESTROY           = 0x0002;
+	WM_SIZE	             = 0x0005;
+	WM_CLOSE             = 0x0010;
+	WM_ACTIVATEAPP       = 0x001C;
+	WM_QUIT              = 0x0012;
+	WM_KEYDOWN           = 0x0100;
+	WM_KEYUP             = 0x0101;
+	WM_SIZING            = 0x0214;
+	WM_SYSKEYDOWN        = 0x0104;
+	WM_SYSKEYUP          = 0x0105;
+	WM_WINDOWPOSCHANGED  = 0x0047;
+	WM_SETCURSOR         = 0x0020;
+	WM_CHAR              = 0x0102;
+	WM_ACTIVATE          = 0x0006;
+	WM_SETFOCUS          = 0x0007;
+	WM_KILLFOCUS         = 0x0008;
+	WM_USER              = 0x0400;
+
+	WM_MOUSEWHEEL    = 0x020A;
+	WM_MOUSEMOVE     = 0x0200;
+	WM_LBUTTONDOWN   = 0x0201;
+	WM_LBUTTONUP     = 0x0202;
+	WM_LBUTTONDBLCLK = 0x0203;
+	WM_RBUTTONDOWN   = 0x0204;
+	WM_RBUTTONUP     = 0x0205;
+	WM_RBUTTONDBLCLK = 0x0206;
+	WM_MBUTTONDOWN   = 0x0207;
+	WM_MBUTTONUP     = 0x0208;
+	WM_MBUTTONDBLCLK = 0x0209;
+
+	PM_NOREMOVE = 0x0000;
+	PM_REMOVE   = 0x0001;
+	PM_NOYIELD  = 0x0002;
+
+	BLACK_BRUSH = 4;
+
+	SM_CXSCREEN = 0;
+	SM_CYSCREEN = 1;
+
+	SW_SHOW = 5;
+)
 
 const COLOR_BACKGROUND = Hbrush(int(1));
-const BLACK_BRUSH = 4;
-
-const SM_CXSCREEN = 0;
-const SM_CYSCREEN = 1;
-
-const SW_SHOW = 5;
 
 
 type Point struct #ordered {
@@ -151,23 +158,24 @@ type FileAttributeData struct #ordered {
 }
 
 type FindData struct #ordered {
-    file_attributes     : u32,
-    creation_time       : Filetime,
-    last_access_time    : Filetime,
-    last_write_time     : Filetime,
-    file_size_high      : u32,
-    file_size_low       : u32,
-    reserved0           : u32,
-    reserved1           : u32,
-    file_name           : [MAX_PATH]u8,
-    alternate_file_name : [14]u8,
+    file_attributes:     u32,
+    creation_time:       Filetime,
+    last_access_time:    Filetime,
+    last_write_time:     Filetime,
+    file_size_high:      u32,
+    file_size_low:       u32,
+    reserved0:           u32,
+    reserved1:           u32,
+    file_name:           [MAX_PATH]u8,
+    alternate_file_name: [14]u8,
 }
 
 
 type GET_FILEEX_INFO_LEVELS i32;
-
-const GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS = 0;
-const GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS = 1;
+const (
+	GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS = 0;
+	GetFileExMaxInfoLevel                         = 1;
+)
 
 proc get_last_error     () -> i32                            #foreign kernel32 "GetLastError";
 proc exit_process       (exit_code: u32)                     #foreign kernel32 "ExitProcess";
@@ -257,56 +265,58 @@ proc find_first_file_a(file_name : ^u8, data : ^FindData) -> Handle #foreign ker
 proc find_next_file_a (file : Handle, data : ^FindData) -> Bool       #foreign kernel32 "FindNextFileA";
 proc find_close       (file : Handle) -> Bool                         #foreign kernel32 "FindClose";
 
-const MAX_PATH = 0x00000104;
-
-const HANDLE_FLAG_INHERIT = 1;
-const HANDLE_FLAG_PROTECT_FROM_CLOSE = 2;
-
-
-const FILE_BEGIN   = 0;
-const FILE_CURRENT = 1;
-const FILE_END     = 2;
-
-const FILE_SHARE_READ      = 0x00000001;
-const FILE_SHARE_WRITE     = 0x00000002;
-const FILE_SHARE_DELETE    = 0x00000004;
-const FILE_GENERIC_ALL     = 0x10000000;
-const FILE_GENERIC_EXECUTE = 0x20000000;
-const FILE_GENERIC_WRITE   = 0x40000000;
-const FILE_GENERIC_READ    = 0x80000000;
-
-const FILE_APPEND_DATA = 0x0004;
-
-const STD_INPUT_HANDLE  = -10;
-const STD_OUTPUT_HANDLE = -11;
-const STD_ERROR_HANDLE  = -12;
-
-const CREATE_NEW        = 1;
-const CREATE_ALWAYS     = 2;
-const OPEN_EXISTING     = 3;
-const OPEN_ALWAYS       = 4;
-const TRUNCATE_EXISTING = 5;
-
-const INVALID_FILE_ATTRIBUTES  = -1;
-
-const FILE_ATTRIBUTE_READONLY             = 0x00000001;
-const FILE_ATTRIBUTE_HIDDEN               = 0x00000002;
-const FILE_ATTRIBUTE_SYSTEM               = 0x00000004;
-const FILE_ATTRIBUTE_DIRECTORY            = 0x00000010;
-const FILE_ATTRIBUTE_ARCHIVE              = 0x00000020;
-const FILE_ATTRIBUTE_DEVICE               = 0x00000040;
-const FILE_ATTRIBUTE_NORMAL               = 0x00000080;
-const FILE_ATTRIBUTE_TEMPORARY            = 0x00000100;
-const FILE_ATTRIBUTE_SPARSE_FILE          = 0x00000200;
-const FILE_ATTRIBUTE_REPARSE_Point        = 0x00000400;
-const FILE_ATTRIBUTE_COMPRESSED           = 0x00000800;
-const FILE_ATTRIBUTE_OFFLINE              = 0x00001000;
-const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  = 0x00002000;
-const FILE_ATTRIBUTE_ENCRYPTED            = 0x00004000;
-
-const FILE_TYPE_DISK = 0x0001;
-const FILE_TYPE_CHAR = 0x0002;
-const FILE_TYPE_PIPE = 0x0003;
+
+const (
+	MAX_PATH = 0x00000104;
+
+	HANDLE_FLAG_INHERIT = 1;
+	HANDLE_FLAG_PROTECT_FROM_CLOSE = 2;
+
+	FILE_BEGIN   = 0;
+	FILE_CURRENT = 1;
+	FILE_END     = 2;
+
+	FILE_SHARE_READ      = 0x00000001;
+	FILE_SHARE_WRITE     = 0x00000002;
+	FILE_SHARE_DELETE    = 0x00000004;
+	FILE_GENERIC_ALL     = 0x10000000;
+	FILE_GENERIC_EXECUTE = 0x20000000;
+	FILE_GENERIC_WRITE   = 0x40000000;
+	FILE_GENERIC_READ    = 0x80000000;
+
+	FILE_APPEND_DATA = 0x0004;
+
+	STD_INPUT_HANDLE  = -10;
+	STD_OUTPUT_HANDLE = -11;
+	STD_ERROR_HANDLE  = -12;
+
+	CREATE_NEW        = 1;
+	CREATE_ALWAYS     = 2;
+	OPEN_EXISTING     = 3;
+	OPEN_ALWAYS       = 4;
+	TRUNCATE_EXISTING = 5;
+
+	INVALID_FILE_ATTRIBUTES  = -1;
+
+	FILE_ATTRIBUTE_READONLY             = 0x00000001;
+	FILE_ATTRIBUTE_HIDDEN               = 0x00000002;
+	FILE_ATTRIBUTE_SYSTEM               = 0x00000004;
+	FILE_ATTRIBUTE_DIRECTORY            = 0x00000010;
+	FILE_ATTRIBUTE_ARCHIVE              = 0x00000020;
+	FILE_ATTRIBUTE_DEVICE               = 0x00000040;
+	FILE_ATTRIBUTE_NORMAL               = 0x00000080;
+	FILE_ATTRIBUTE_TEMPORARY            = 0x00000100;
+	FILE_ATTRIBUTE_SPARSE_FILE          = 0x00000200;
+	FILE_ATTRIBUTE_REPARSE_Point        = 0x00000400;
+	FILE_ATTRIBUTE_COMPRESSED           = 0x00000800;
+	FILE_ATTRIBUTE_OFFLINE              = 0x00001000;
+	FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  = 0x00002000;
+	FILE_ATTRIBUTE_ENCRYPTED            = 0x00004000;
+
+	FILE_TYPE_DISK = 0x0001;
+	FILE_TYPE_CHAR = 0x0002;
+	FILE_TYPE_PIPE = 0x0003;
+)
 
 const INVALID_SET_FILE_POINTER = ~u32(0);
 
@@ -363,16 +373,18 @@ const GWL_STYLE = -16;
 
 const Hwnd_TOP = Hwnd(uint(0));
 
-const MONITOR_DEFAULTTONULL    = 0x00000000;
-const MONITOR_DEFAULTTOPRIMARY = 0x00000001;
-const MONITOR_DEFAULTTONEAREST = 0x00000002;
-
-const SWP_FRAMECHANGED  = 0x0020;
-const SWP_NOOWNERZORDER = 0x0200;
-const SWP_NOZORDER      = 0x0004;
-const SWP_NOSIZE        = 0x0001;
-const SWP_NOMOVE        = 0x0002;
-
+const (
+	MONITOR_DEFAULTTONULL    = 0x00000000;
+	MONITOR_DEFAULTTOPRIMARY = 0x00000001;
+	MONITOR_DEFAULTTONEAREST = 0x00000002;
+)
+const (
+	SWP_FRAMECHANGED  = 0x0020;
+	SWP_NOOWNERZORDER = 0x0200;
+	SWP_NOZORDER      = 0x0004;
+	SWP_NOSIZE        = 0x0001;
+	SWP_NOMOVE        = 0x0002;
+)
 
 type MonitorInfo struct #ordered {
 	size:      u32,
@@ -458,28 +470,29 @@ proc get_proc_address( h: Hmodule, c_str: ^u8) -> Proc #foreign kernel32 "GetPro
 proc get_client_rect (hwnd: Hwnd, rect: ^Rect) -> Bool #foreign user32 "GetClientRect";
 
 // Windows OpenGL
-const PFD_TYPE_RGBA             = 0;
-const PFD_TYPE_COLORINDEX       = 1;
-const PFD_MAIN_PLANE            = 0;
-const PFD_OVERLAY_PLANE         = 1;
-const PFD_UNDERLAY_PLANE        = -1;
-const PFD_DOUBLEBUFFER          = 1;
-const PFD_STEREO                = 2;
-const PFD_DRAW_TO_WINDOW        = 4;
-const PFD_DRAW_TO_BITMAP        = 8;
-const PFD_SUPPORT_GDI           = 16;
-const PFD_SUPPORT_OPENGL        = 32;
-const PFD_GENERIC_FORMAT        = 64;
-const PFD_NEED_PALETTE          = 128;
-const PFD_NEED_SYSTEM_PALETTE   = 0x00000100;
-const PFD_SWAP_EXCHANGE         = 0x00000200;
-const PFD_SWAP_COPY             = 0x00000400;
-const PFD_SWAP_LAYER_BUFFERS    = 0x00000800;
-const PFD_GENERIC_ACCELERATED   = 0x00001000;
-const PFD_DEPTH_DONTCARE        = 0x20000000;
-const PFD_DOUBLEBUFFER_DONTCARE = 0x40000000;
-const PFD_STEREO_DONTCARE       = 0x80000000;
-
+const (
+	PFD_TYPE_RGBA             = 0;
+	PFD_TYPE_COLORINDEX       = 1;
+	PFD_MAIN_PLANE            = 0;
+	PFD_OVERLAY_PLANE         = 1;
+	PFD_UNDERLAY_PLANE        = -1;
+	PFD_DOUBLEBUFFER          = 1;
+	PFD_STEREO                = 2;
+	PFD_DRAW_TO_WINDOW        = 4;
+	PFD_DRAW_TO_BITMAP        = 8;
+	PFD_SUPPORT_GDI           = 16;
+	PFD_SUPPORT_OPENGL        = 32;
+	PFD_GENERIC_FORMAT        = 64;
+	PFD_NEED_PALETTE          = 128;
+	PFD_NEED_SYSTEM_PALETTE   = 0x00000100;
+	PFD_SWAP_EXCHANGE         = 0x00000200;
+	PFD_SWAP_COPY             = 0x00000400;
+	PFD_SWAP_LAYER_BUFFERS    = 0x00000800;
+	PFD_GENERIC_ACCELERATED   = 0x00001000;
+	PFD_DEPTH_DONTCARE        = 0x20000000;
+	PFD_DOUBLEBUFFER_DONTCARE = 0x40000000;
+	PFD_STEREO_DONTCARE       = 0x80000000;
+)
 
 type PixelFormatDescriptor struct #ordered {
 	size,
@@ -521,10 +534,12 @@ proc release_dc         (wnd: Hwnd, hdc: Hdc) -> i32
 
 type Proc proc() #cc_c;
 
-const MAPVK_VK_TO_CHAR   = 2;
-const MAPVK_VK_TO_VSC    = 0;
-const MAPVK_VSC_TO_VK    = 1;
-const MAPVK_VSC_TO_VK_EX = 3;
+const (
+	MAPVK_VK_TO_CHAR   = 2;
+	MAPVK_VK_TO_VSC    = 0;
+	MAPVK_VSC_TO_VK    = 1;
+	MAPVK_VSC_TO_VK_EX = 3;
+)
 
 proc map_virtual_key(scancode : u32, map_type : u32) -> u32 #foreign user32 "MapVirtualKeyA";
 

+ 2 - 8
src/check_expr.cpp

@@ -1123,13 +1123,10 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			AstNode *name = p->names[j];
 			if (ast_node_expect(name, AstNode_Ident)) {
 				Entity *param = make_entity_param(c->allocator, scope, name->Ident, type,
-				                                  (p->flags&FieldFlag_using) != 0, (p->flags&FieldFlag_immutable) != 0);
+				                                  (p->flags&FieldFlag_using) != 0, false);
 				if (p->flags&FieldFlag_no_alias) {
 					param->flags |= EntityFlag_NoAlias;
 				}
-				if (p->flags&FieldFlag_immutable) {
-					param->Variable.is_immutable = true;
-				}
 				param->Variable.default_value = value;
 				param->Variable.default_is_nil = default_is_nil;
 
@@ -6568,11 +6565,8 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		if (f->flags&FieldFlag_using) {
 			str = gb_string_appendc(str, "using ");
 		}
-		if (f->flags&FieldFlag_immutable) {
-			str = gb_string_appendc(str, "immutable ");
-		}
 		if (f->flags&FieldFlag_no_alias) {
-			str = gb_string_appendc(str, "no_alias ");
+			str = gb_string_appendc(str, "#no_alias ");
 		}
 
 		for_array(i, f->names) {

+ 85 - 100
src/checker.cpp

@@ -1451,27 +1451,62 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 		case_end;
 
 		case_ast_node(gd, GenDecl, decl);
+			AstNodeValueSpec empty_spec = {};
+			AstNodeValueSpec *last_spec = NULL;
 			for_array(i, gd->specs) {
 				AstNode *spec = gd->specs[i];
 				switch (gd->token.kind) {
+				case Token_const: {
+					ast_node(vs, ValueSpec, spec);
+
+					if (vs->type != NULL || vs->values.count > 0) {
+						last_spec = vs;
+					} else if (last_spec == NULL) {
+						last_spec = &empty_spec;
+					}
+
+					for_array(i, vs->names) {
+						AstNode *name = vs->names[i];
+						if (name->kind != AstNode_Ident) {
+							error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
+							continue;
+						}
+
+						AstNode *init = NULL;
+						if (i < vs->values.count) {
+							init = vs->values[i];
+						}
+
+						DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
+						Entity *e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, empty_exact_value);
+						d->type_expr = last_spec->type;
+						d->init_expr = init;
+						e->identifier = name;
+
+						add_entity_and_decl_info(c, name, e, d);
+					}
+
+					check_arity_match(c, vs);
+				} break;
+
 				case Token_var:
 				case Token_let: {
 					if (!c->context.scope->is_file) {
 						// NOTE(bill): local scope -> handle later and in order
 						break;
 					}
-					ast_node(vd, ValueSpec, spec);
+					ast_node(vs, ValueSpec, spec);
 
 					// NOTE(bill): You need to store the entity information here unline a constant declaration
-					isize entity_cap = vd->names.count;
+					isize entity_cap = vs->names.count;
 					isize entity_count = 0;
 					Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
 					DeclInfo *di = NULL;
-					if (vd->values.count > 0) {
+					if (vs->values.count > 0) {
 						di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
 						di->entities = entities;
-						di->type_expr = vd->type;
-						di->init_expr = vd->values[0];
+						di->type_expr = vs->type;
+						di->init_expr = vs->values[0];
 
 
 						if (gd->flags & VarDeclFlag_thread_local) {
@@ -1480,11 +1515,11 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 					}
 
 
-					for_array(i, vd->names) {
-						AstNode *name = vd->names[i];
+					for_array(i, vs->names) {
+						AstNode *name = vs->names[i];
 						AstNode *value = NULL;
-						if (i < vd->values.count) {
-							value = vd->values[i];
+						if (i < vs->values.count) {
+							value = vs->values[i];
 						}
 						if (name->kind != AstNode_Ident) {
 							error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
@@ -1504,7 +1539,7 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 						if (d == NULL) {
 							AstNode *init_expr = value;
 							d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
-							d->type_expr = vd->type;
+							d->type_expr = vs->type;
 							d->init_expr = init_expr;
 						}
 
@@ -1515,65 +1550,13 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 						di->entity_count = entity_count;
 					}
 
-					check_arity_match(c, vd);
-				} break;
-
-				case Token_const: {
-					ast_node(vd, ValueSpec, spec);
-
-					for_array(i, vd->names) {
-						AstNode *name = vd->names[i];
-						if (name->kind != AstNode_Ident) {
-							error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
-							continue;
-						}
-
-						AstNode *init = NULL;
-						if (i < vd->values.count) {
-							init = vd->values[i];
-						}
-
-						DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
-						Entity *e = NULL;
-
-						AstNode *up_init = unparen_expr(init);
-						// if (up_init != NULL && is_ast_node_type(up_init)) {
-						// 	AstNode *type = up_init;
-						// 	e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
-						// 	// TODO(bill): What if vd->type != NULL??? How to handle this case?
-						// 	d->type_expr = type;
-						// 	d->init_expr = type;
-						// } else if (up_init != NULL && up_init->kind == AstNode_Alias) {
-						// #if 1
-						// 	error_node(up_init, "#alias declarations are not yet supported");
-						// 	continue;
-						// #else
-						// 	e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, EntityAlias_Invalid, NULL);
-						// 	d->type_expr = vd->type;
-						// 	d->init_expr = up_init->Alias.expr;
-						// #endif
-						// // } else if (init != NULL && up_init->kind == AstNode_ProcLit) {
-						// 	// e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
-						// 	// d->proc_lit = up_init;
-						// 	// d->type_expr = vd->type;
-						// } else {
-							e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, empty_exact_value);
-							d->type_expr = vd->type;
-							d->init_expr = init;
-						// }
-						GB_ASSERT(e != NULL);
-						e->identifier = name;
-
-						add_entity_and_decl_info(c, name, e, d);
-					}
-
-					check_arity_match(c, vd);
+					check_arity_match(c, vs);
 				} break;
 
 				case Token_type: {
-					ast_node(td, TypeSpec, spec);
+					ast_node(ts, TypeSpec, spec);
 
-					AstNode *name = td->name;
+					AstNode *name = ts->name;
 					if (name->kind != AstNode_Ident) {
 						error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
 						break;
@@ -1583,9 +1566,8 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 					DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
 					Entity *e = NULL;
 
-					AstNode *type = unparen_expr(td->type);
+					AstNode *type = unparen_expr(ts->type);
 					e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
-					// TODO(bill): What if vd->type != NULL??? How to handle this case?
 					d->type_expr = type;
 					d->init_expr = type;
 
@@ -1595,9 +1577,9 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 
 				case Token_import:
 				case Token_import_load: {
-					ast_node(id, ImportSpec, spec);
+					ast_node(ts, ImportSpec, spec);
 					if (!c->context.scope->is_file) {
-						if (id->is_import) {
+						if (ts->is_import) {
 							error_node(decl, "import declarations are only allowed in the file scope");
 						} else {
 							error_node(decl, "import_load declarations are only allowed in the file scope");
@@ -1609,6 +1591,37 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 					DelayedDecl di = {c->context.scope, spec};
 					array_add(&c->delayed_imports, di);
 				} break;
+
+				case Token_foreign_library:
+				case Token_foreign_system_library:  {
+					ast_node(fl, ForeignLibrarySpec, spec);
+					if (!c->context.scope->is_file) {
+						if (fl->is_system) {
+							error_node(spec, "foreign_system_library declarations are only allowed in the file scope");
+						} else {
+							error_node(spec, "foreign_library declarations are only allowed in the file scope");
+						}
+						// NOTE(bill): _Should_ be caught by the parser
+						// TODO(bill): Better error handling if it isn't
+						continue;
+					}
+
+					if (fl->cond != NULL) {
+						Operand operand = {Addressing_Invalid};
+						check_expr(c, &operand, fl->cond);
+						if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
+							error_node(fl->cond, "Non-constant boolean `when` condition");
+							continue;
+						}
+						if (operand.value.kind == ExactValue_Bool &&
+							!operand.value.value_bool) {
+							continue;
+						}
+					}
+
+					DelayedDecl di = {c->context.scope, spec};
+					array_add(&c->delayed_foreign_libraries, di);
+				} break;
 				}
 			}
 		case_end;
@@ -1631,34 +1644,6 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
 			add_entity_and_decl_info(c, name, e, d);
 		case_end;
 
-		case_ast_node(fl, ForeignLibrary, decl);
-			if (!c->context.scope->is_file) {
-				if (fl->is_system) {
-					error_node(decl, "#foreign_system_library declarations are only allowed in the file scope");
-				} else {
-					error_node(decl, "#foreign_library declarations are only allowed in the file scope");
-				}
-				// NOTE(bill): _Should_ be caught by the parser
-				// TODO(bill): Better error handling if it isn't
-				continue;
-			}
-
-			if (fl->cond != NULL) {
-				Operand operand = {Addressing_Invalid};
-				check_expr(c, &operand, fl->cond);
-				if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
-					error_node(fl->cond, "Non-constant boolean `when` condition");
-					continue;
-				}
-				if (operand.value.kind == ExactValue_Bool &&
-					!operand.value.value_bool) {
-					continue;
-				}
-			}
-
-			DelayedDecl di = {c->context.scope, decl};
-			array_add(&c->delayed_foreign_libraries, di);
-		case_end;
 		default:
 			if (c->context.scope->is_file) {
 				error_node(decl, "Only declarations are allowed at file scope");
@@ -1998,8 +1983,8 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 
 	for_array(i, c->delayed_foreign_libraries) {
 		Scope *parent_scope = c->delayed_foreign_libraries[i].parent;
-		AstNode *decl = c->delayed_foreign_libraries[i].decl;
-		ast_node(fl, ForeignLibrary, decl);
+		AstNode *spec = c->delayed_foreign_libraries[i].decl;
+		ast_node(fl, ForeignLibrarySpec, spec);
 
 		String file_str = fl->filepath.string;
 		String base_dir = fl->base_dir;
@@ -2034,7 +2019,7 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 
 		String library_name = path_to_entity_name(fl->library_name.string, file_str);
 		if (library_name == "_") {
-			error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
+			error_node(spec, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
 		} else {
 			GB_ASSERT(fl->library_name.pos.line != 0);
 			fl->library_name.string = library_name;

+ 118 - 249
src/parser.cpp

@@ -100,9 +100,8 @@ enum FieldFlag {
 	FieldFlag_ellipsis  = 1<<0,
 	FieldFlag_using     = 1<<1,
 	FieldFlag_no_alias  = 1<<2,
-	FieldFlag_immutable = 1<<3,
 
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias,
 };
 
 enum StmtAllowFlag {
@@ -307,12 +306,12 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 		String   foreign_name;    \
 		String   link_name;       \
 	}) \
-	AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
-		Token token, filepath; \
-		Token library_name;     \
-		String base_dir;       \
+	AST_NODE_KIND(ForeignLibrarySpec, "foreign library specification", struct { \
+		Token    filepath;     \
+		Token    library_name; \
+		String   base_dir;     \
 		AstNode *cond;         \
-		bool is_system;        \
+		bool     is_system;    \
 	}) \
 	AST_NODE_KIND(Label, "label", struct { 	\
 		Token token; \
@@ -541,15 +540,15 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_PushAllocator: return node->PushAllocator.token;
 	case AstNode_PushContext:   return node->PushContext.token;
 
-	case AstNode_BadDecl:        return node->BadDecl.begin;
-	case AstNode_ProcDecl:       return node->ProcDecl.token;
-	case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
-	case AstNode_Label:          return node->Label.token;
+	case AstNode_BadDecl:            return node->BadDecl.begin;
+	case AstNode_ProcDecl:           return node->ProcDecl.token;
+	case AstNode_ForeignLibrarySpec: return node->ForeignLibrarySpec.filepath;
+	case AstNode_Label:              return node->Label.token;
 
-	case AstNode_GenDecl:        return node->GenDecl.token;
-	case AstNode_ValueSpec:      return ast_node_token(node->ValueSpec.names[0]);
-	case AstNode_ImportSpec:     return node->ImportSpec.import_name;
-	case AstNode_TypeSpec:       return ast_node_token(node->TypeSpec.name);
+	case AstNode_GenDecl:            return node->GenDecl.token;
+	case AstNode_ValueSpec:          return ast_node_token(node->ValueSpec.names[0]);
+	case AstNode_ImportSpec:         return node->ImportSpec.import_name;
+	case AstNode_TypeSpec:           return ast_node_token(node->TypeSpec.name);
 
 
 	case AstNode_Field:
@@ -766,8 +765,8 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 		break;
 
 	case AstNode_BadDecl: break;
-	case AstNode_ForeignLibrary:
-		n->ForeignLibrary.cond = clone_ast_node(a, n->ForeignLibrary.cond);
+	case AstNode_ForeignLibrarySpec:
+		n->ForeignLibrarySpec.cond = clone_ast_node(a, n->ForeignLibrarySpec.cond);
 		break;
 	case AstNode_Label:
 		n->Label.name = clone_ast_node(a, n->Label.name);
@@ -1438,13 +1437,12 @@ AstNode *ast_proc_decl(AstFile *f, Token token, AstNode *name, AstNode *type, As
 	return result;
 }
 
-AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token library_name, AstNode *cond, bool is_system) {
-	AstNode *result = make_ast_node(f, AstNode_ForeignLibrary);
-	result->ForeignLibrary.token = token;
-	result->ForeignLibrary.filepath = filepath;
-	result->ForeignLibrary.library_name = library_name;
-	result->ForeignLibrary.cond = cond;
-	result->ForeignLibrary.is_system = is_system;
+AstNode *ast_foreign_library_spec(AstFile *f, Token filepath, Token library_name, AstNode *cond, bool is_system) {
+	AstNode *result = make_ast_node(f, AstNode_ForeignLibrarySpec);
+	result->ForeignLibrarySpec.filepath = filepath;
+	result->ForeignLibrarySpec.library_name = library_name;
+	result->ForeignLibrarySpec.cond = cond;
+	result->ForeignLibrarySpec.is_system = is_system;
 	return result;
 }
 
@@ -2690,6 +2688,72 @@ PARSE_SPEC_FUNC(parse_import_spec) {
 	}
 }
 
+PARSE_SPEC_FUNC(parse_foreign_library_spec) {
+	if (token.kind == Token_foreign_system_library) {
+		AstNode *cond = NULL;
+		Token lib_name = {};
+
+		switch (f->curr_token.kind) {
+		case Token_Ident:
+			lib_name = f->curr_token;
+			next_token(f);
+			break;
+		default:
+			lib_name.pos = f->curr_token.pos;
+			break;
+		}
+
+		if (lib_name.string == "_") {
+			syntax_error(lib_name, "Illegal foreign_library name: `_`");
+		}
+		Token file_path = expect_token(f, Token_String);
+
+		if (allow_token(f, Token_when)) {
+			cond = parse_expr(f, false);
+		}
+
+		AstNode *spec = NULL;
+		if (f->curr_proc == NULL) {
+			spec = ast_foreign_library_spec(f, file_path, lib_name, cond, true);
+		} else {
+			syntax_error(lib_name, "You cannot use foreign_system_library within a procedure. This must be done at the file scope");
+			spec = ast_bad_decl(f, lib_name, file_path);
+		}
+		return spec;
+	} else {
+		AstNode *cond = NULL;
+		Token lib_name = {};
+
+		switch (f->curr_token.kind) {
+		case Token_Ident:
+			lib_name = f->curr_token;
+			next_token(f);
+			break;
+		default:
+			lib_name.pos = f->curr_token.pos;
+			break;
+		}
+
+		if (lib_name.string == "_") {
+			syntax_error(lib_name, "Illegal foreign_library name: `_`");
+		}
+		Token file_path = expect_token(f, Token_String);
+
+		if (allow_token(f, Token_when)) {
+			cond = parse_expr(f, false);
+		}
+
+		AstNode *spec = NULL;
+		if (f->curr_proc == NULL) {
+			spec = ast_foreign_library_spec(f, file_path, lib_name, cond, false);
+		} else {
+			syntax_error(lib_name, "You cannot use foreign_library within a procedure. This must be done at the file scope");
+			spec = ast_bad_decl(f, lib_name, file_path);
+		}
+		return spec;
+	}
+}
+
 AstNode *parse_decl(AstFile *f) {
 	ParseSpecFunc *func = NULL;
 	switch (f->curr_token.kind) {
@@ -2708,6 +2772,11 @@ AstNode *parse_decl(AstFile *f) {
 		func = parse_import_spec;
 		break;
 
+	case Token_foreign_library:
+	case Token_foreign_system_library:
+		func = parse_foreign_library_spec;
+		break;
+
 	case Token_proc:
 		return parse_proc_decl(f);
 
@@ -2913,7 +2982,6 @@ enum FieldPrefixKind {
 	FieldPrefix_Invalid,
 
 	FieldPrefix_Using,
-	FieldPrefix_Immutable,
 	FieldPrefix_NoAlias,
 };
 
@@ -2925,9 +2993,6 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 	case Token_using:
 		return FieldPrefix_Using;
 
-	case Token_let:
-		return FieldPrefix_Immutable;
-
 	case Token_Hash: {
 		next_token(f);
 		switch (f->curr_token.kind) {
@@ -2944,9 +3009,8 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 
 
 u32 parse_field_prefixes(AstFile *f) {
-	i32 using_count     = 0;
-	i32 no_alias_count  = 0;
-	i32 immutable_count = 0;
+	i32 using_count    = 0;
+	i32 no_alias_count = 0;
 
 	for (;;) {
 		FieldPrefixKind kind = is_token_field_prefix(f);
@@ -2954,20 +3018,17 @@ u32 parse_field_prefixes(AstFile *f) {
 			break;
 		}
 		switch (kind) {
-		case FieldPrefix_Using:     using_count     += 1; next_token(f); break;
-		case FieldPrefix_Immutable: immutable_count += 1; next_token(f); break;
-		case FieldPrefix_NoAlias:   no_alias_count  += 1; next_token(f); break;
+		case FieldPrefix_Using:     using_count    += 1; next_token(f); break;
+		case FieldPrefix_NoAlias:   no_alias_count += 1; next_token(f); break;
 		}
 	}
-	if (using_count     > 1) syntax_error(f->curr_token, "Multiple `using` in this field list");
-	if (immutable_count > 1) syntax_error(f->curr_token, "Multiple `immutable` in this field list");
+	if (using_count     > 1) syntax_error(f->curr_token, "Multiple `using`     in this field list");
 	if (no_alias_count  > 1) syntax_error(f->curr_token, "Multiple `#no_alias` in this field list");
 
 
 	u32 field_flags = 0;
 	if (using_count     > 0) field_flags |= FieldFlag_using;
 	if (no_alias_count  > 0) field_flags |= FieldFlag_no_alias;
-	if (immutable_count > 0) field_flags |= FieldFlag_immutable;
 	return field_flags;
 }
 
@@ -2985,10 +3046,6 @@ u32 check_field_prefixes(AstFile *f, isize name_count, u32 allowed_flags, u32 se
 		syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
 		set_flags &= ~FieldFlag_no_alias;
 	}
-	if ((allowed_flags&FieldFlag_immutable) == 0 && (set_flags&FieldFlag_immutable)) {
-		syntax_error(f->curr_token, "`immutable` is not allowed within this field list");
-		set_flags &= ~FieldFlag_immutable;
-	}
 	return set_flags;
 }
 
@@ -3833,6 +3890,8 @@ AstNode *parse_stmt(AstFile *f) {
 	case Token_type:
 	case Token_import:
 	case Token_import_load:
+	case Token_foreign_library:
+	case Token_foreign_system_library:
 		return parse_decl(f);
 
 	case Token_if:     return parse_if_stmt(f);
@@ -3899,24 +3958,6 @@ AstNode *parse_stmt(AstFile *f) {
 		return ast_bad_stmt(f, token, f->curr_token);
 	} break;
 
-#if 0
-	case Token_immutable: {
-		Token token = expect_token(f, Token_immutable);
-		AstNode *node = parse_stmt(f);
-
-		if (node->kind == AstNode_ValueDecl) {
-			if (node->ValueDecl.token.kind == Token_const) {
-				syntax_error(token, "`immutable` may not be applied to constant declarations");
-			} else {
-				node->ValueDecl.flags |= VarDeclFlag_immutable;
-			}
-			return node;
-		}
-		syntax_error(token, "`immutable` may only be applied to a variable declaration");
-		return ast_bad_stmt(f, token, f->curr_token);
-	} break;
-#endif
-
 	case Token_push_allocator: {
 		next_token(f);
 		isize prev_level = f->expr_level;
@@ -3939,70 +3980,6 @@ AstNode *parse_stmt(AstFile *f) {
 		return ast_push_context(f, token, expr, body);
 	} break;
 
-#if 0
-	case Token_import: {
-		Token token = expect_token(f, Token_import);
-		AstNode *cond = NULL;
-		Token import_name = {};
-
-		switch (f->curr_token.kind) {
-		case Token_Period:
-			import_name = f->curr_token;
-			import_name.kind = Token_Ident;
-			next_token(f);
-			break;
-		case Token_Ident:
-			import_name = f->curr_token;
-			next_token(f);
-			break;
-		default:
-			import_name.pos = f->curr_token.pos;
-			break;
-		}
-
-		if (import_name.string == "_") {
-			syntax_error(import_name, "Illegal import name: `_`");
-		}
-
-		Token file_path = expect_token_after(f, Token_String, "import");
-		if (allow_token(f, Token_when)) {
-			cond = parse_expr(f, false);
-		}
-
-		AstNode *decl = NULL;
-		if (f->curr_proc != NULL) {
-			syntax_error(import_name, "You cannot use `import` within a procedure. This must be done at the file scope");
-			decl = ast_bad_decl(f, import_name, file_path);
-		} else {
-			decl = ast_import_decl(f, token, true, file_path, import_name, cond);
-		}
-		expect_semicolon(f, decl);
-		return decl;
-	}
-
-	case Token_import_load: {
-		Token token = expect_token(f, Token_import_load);
-		AstNode *cond = NULL;
-		Token file_path = expect_token_after(f, Token_String, "import_load");
-		Token import_name = file_path;
-		import_name.string = str_lit(".");
-
-		if (allow_token(f, Token_when)) {
-			cond = parse_expr(f, false);
-		}
-
-		AstNode *decl = NULL;
-		if (f->curr_proc != NULL) {
-			syntax_error(import_name, "You cannot use `import_load` within a procedure. This must be done at the file scope");
-			decl = ast_bad_decl(f, import_name, file_path);
-		} else {
-			decl = ast_import_decl(f, token, false, file_path, import_name, cond);
-		}
-		expect_semicolon(f, decl);
-		return decl;
-	}
-#endif
-
 	case Token_Hash: {
 		AstNode *s = NULL;
 		Token hash_token = expect_token(f, Token_Hash);
@@ -4019,68 +3996,6 @@ AstNode *parse_stmt(AstFile *f) {
 			}
 			expect_semicolon(f, s);
 			return s;
-		} else if (tag == "foreign_system_library") {
-			AstNode *cond = NULL;
-			Token lib_name = {};
-
-			switch (f->curr_token.kind) {
-			case Token_Ident:
-				lib_name = f->curr_token;
-				next_token(f);
-				break;
-			default:
-				lib_name.pos = f->curr_token.pos;
-				break;
-			}
-
-			if (lib_name.string == "_") {
-				syntax_error(lib_name, "Illegal #foreign_library name: `_`");
-			}
-			Token file_path = expect_token(f, Token_String);
-
-			if (allow_token(f, Token_when)) {
-				cond = parse_expr(f, false);
-			}
-
-			if (f->curr_proc == NULL) {
-				s = ast_foreign_library(f, hash_token, file_path, lib_name, cond, true);
-			} else {
-				syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope");
-				s = ast_bad_decl(f, token, file_path);
-			}
-			expect_semicolon(f, s);
-			return s;
-		} else if (tag == "foreign_library") {
-			AstNode *cond = NULL;
-			Token lib_name = {};
-
-			switch (f->curr_token.kind) {
-			case Token_Ident:
-				lib_name = f->curr_token;
-				next_token(f);
-				break;
-			default:
-				lib_name.pos = f->curr_token.pos;
-				break;
-			}
-
-			if (lib_name.string == "_") {
-				syntax_error(lib_name, "Illegal #foreign_library name: `_`");
-			}
-			Token file_path = expect_token(f, Token_String);
-
-			if (allow_token(f, Token_when)) {
-				cond = parse_expr(f, false);
-			}
-
-			if (f->curr_proc == NULL) {
-				s = ast_foreign_library(f, hash_token, file_path, lib_name, cond, false);
-			} else {
-				syntax_error(token, "You cannot use #foreign_library within a procedure. This must be done at the file scope");
-				s = ast_bad_decl(f, token, file_path);
-			}
-			expect_semicolon(f, s);
-			return s;
 		} else if (tag == "thread_local") {
 			AstNode *s = parse_stmt(f);
 
@@ -4328,55 +4243,6 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNod
 					gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
 					String import_file = {};
 
-				#if 0
-					isize colon_pos = -1;
-					for (isize j = 0; j < file_str.len; j++) {
-						if (file_str[j] == ':') {
-							colon_pos = j;
-							break;
-						}
-					}
-					if (colon_pos > 0) {
-						collection_name = make_string(file_str.text, colon_pos);
-						file_str.text += colon_pos+1;
-						file_str.len  -= colon_pos+1;
-					}
-
-					if (collection_name.len == 0) {
-						syntax_error_node(node, "Missing import collection for path: `%.*s`", LIT(oirignal_string));
-						decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
-						continue;
-					}
-
-
-					if (collection_name == "core") {
-						String abs_path = get_fullpath_core(allocator, file_str);
-						if (gb_file_exists(cast(char *)abs_path.text)) { // NOTE(bill): This should be null terminated
-							import_file = abs_path;
-						}
-					} else if (collection_name == "local") {
-						String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
-						if (gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-							import_file = rel_path;
-						}
-					} else {
-						syntax_error_node(node, "Unknown import collection: `%.*s`", LIT(collection_name));
-						decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
-						continue;
-					}
-
-					if (!is_import_path_valid(file_str)) {
-						if (id->is_import) {
-							syntax_error_node(node, "Invalid import path: `%.*s`", LIT(file_str));
-						} else {
-							syntax_error_node(node, "Invalid include path: `%.*s`", LIT(file_str));
-						}
-						// NOTE(bill): It's a naughty name
-						decls[i] = ast_bad_decl(f, id->relpath, id->relpath);
-						continue;
-					}
-
-				#else
 					if (!is_import_path_valid(file_str)) {
 						if (id->is_import) {
 							syntax_error_node(node, "Invalid import path: `%.*s`", LIT(file_str));
@@ -4397,28 +4263,31 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<AstNod
 							import_file = abs_path;
 						}
 					}
-				#endif
 
 					id->fullpath = import_file;
 					try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
 				}
-			}
-		} else if (node->kind == AstNode_ForeignLibrary) {
-			AstNodeForeignLibrary *fl = &node->ForeignLibrary;
-			String file_str = fl->filepath.string;
+			} else if (gd->token.kind == Token_foreign_library ||
+			           gd->token.kind == Token_foreign_system_library) {
+				for_array(spec_index, gd->specs) {
+					AstNode *spec = gd->specs[spec_index];
+					ast_node(fl, ForeignLibrarySpec, spec);
+					String file_str = fl->filepath.string;
 
-			if (!is_import_path_valid(file_str)) {
-				if (fl->is_system) {
-					syntax_error_node(node, "Invalid `foreign_system_library` path");
-				} else {
-					syntax_error_node(node, "Invalid `foreign_library` path");
+					if (!is_import_path_valid(file_str)) {
+						if (fl->is_system) {
+							syntax_error_node(node, "Invalid `foreign_system_library` path");
+						} else {
+							syntax_error_node(node, "Invalid `foreign_library` path");
+						}
+						// NOTE(bill): It's a naughty name
+						gd->specs[spec_index] = ast_bad_decl(f, fl->filepath, fl->filepath);
+						continue;
+					}
+
+					fl->base_dir = base_dir;
 				}
-				// NOTE(bill): It's a naughty name
-				f->decls[i] = ast_bad_decl(f, fl->token, fl->token);
-				continue;
 			}
-
-			fl->base_dir = base_dir;
 		}
 	}
 }

+ 2 - 0
src/tokenizer.cpp

@@ -88,6 +88,8 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_type,                   "type"),                   \
 	TOKEN_KIND(Token_import,                 "import"),                 \
 	TOKEN_KIND(Token_import_load,            "import_load"),            \
+	TOKEN_KIND(Token_foreign_library,        "foreign_library"),        \
+	TOKEN_KIND(Token_foreign_system_library, "foreign_system_library"), \
 	TOKEN_KIND(Token_when,                   "when"),                   \
 	TOKEN_KIND(Token_if,                     "if"),                     \
 	TOKEN_KIND(Token_else,                   "else"),                   \