mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-01 19:02:13 +00:00
Block Expressions and give
This commit is contained in:
@@ -12,8 +12,8 @@ import {
|
||||
}
|
||||
|
||||
proc main() {
|
||||
var x = ~(0 as u32);
|
||||
var y = x << 32;
|
||||
fmt.println(y);
|
||||
var a, b, c = {
|
||||
give 1, 2, 123*321;
|
||||
};
|
||||
fmt.println(a, b, c);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,26 +6,26 @@ import {
|
||||
|
||||
const PRINT_BUF_SIZE = 1<<12;
|
||||
|
||||
proc fprint(f ^os.File, args ..any) -> int {
|
||||
proc fprint(fd os.Handle, args ..any) -> int {
|
||||
var data [PRINT_BUF_SIZE]byte;
|
||||
var buf = data[:0];
|
||||
bprint(^buf, ..args);
|
||||
os.write(f, buf);
|
||||
os.write(fd, buf);
|
||||
return buf.count;
|
||||
}
|
||||
|
||||
proc fprintln(f ^os.File, args ..any) -> int {
|
||||
proc fprintln(fd os.Handle, args ..any) -> int {
|
||||
var data [PRINT_BUF_SIZE]byte;
|
||||
var buf = data[:0];
|
||||
bprintln(^buf, ..args);
|
||||
os.write(f, buf);
|
||||
os.write(fd, buf);
|
||||
return buf.count;
|
||||
}
|
||||
proc fprintf(f ^os.File, fmt string, args ..any) -> int {
|
||||
proc fprintf(fd os.Handle, fmt string, args ..any) -> int {
|
||||
var data [PRINT_BUF_SIZE]byte;
|
||||
var buf = data[:0];
|
||||
bprintf(^buf, fmt, ..args);
|
||||
os.write(f, buf);
|
||||
os.write(fd, buf);
|
||||
return buf.count;
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ proc printf(fmt string, args ..any) -> int {
|
||||
|
||||
|
||||
|
||||
proc fprint_type(f ^os.File, info ^Type_Info) {
|
||||
proc fprint_type(fd os.Handle, info ^Type_Info) {
|
||||
var data [PRINT_BUF_SIZE]byte;
|
||||
var buf = data[:0];
|
||||
bprint_type(^buf, info);
|
||||
os.write(f, buf);
|
||||
os.write(fd, buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,73 +3,191 @@ import {
|
||||
"fmt.odin";
|
||||
}
|
||||
|
||||
|
||||
type {
|
||||
Handle uint;
|
||||
File_Time u64;
|
||||
Error int;
|
||||
}
|
||||
|
||||
File_Handle raw_union {
|
||||
p rawptr;
|
||||
i int;
|
||||
const INVALID_HANDLE Handle = ~(0 as Handle);
|
||||
|
||||
const {
|
||||
O_RDONLY = 0x00000;
|
||||
O_WRONLY = 0x00001;
|
||||
O_RDWR = 0x00002;
|
||||
O_CREAT = 0x00040;
|
||||
O_EXCL = 0x00080;
|
||||
O_NOCTTY = 0x00100;
|
||||
O_TRUNC = 0x00200;
|
||||
O_NONBLOCK = 0x00800;
|
||||
O_APPEND = 0x00400;
|
||||
O_SYNC = 0x01000;
|
||||
O_ASYNC = 0x02000;
|
||||
O_CLOEXEC = 0x80000;
|
||||
}
|
||||
|
||||
const {
|
||||
ERROR_NONE Error = 0;
|
||||
ERROR_FILE_NOT_FOUND Error = 2;
|
||||
ERROR_PATH_NOT_FOUND Error = 3;
|
||||
ERROR_ACCESS_DENIED Error = 5;
|
||||
ERROR_NO_MORE_FILES Error = 18;
|
||||
ERROR_HANDLE_EOF Error = 38;
|
||||
ERROR_NETNAME_DELETED Error = 64;
|
||||
ERROR_FILE_EXISTS Error = 80;
|
||||
ERROR_BROKEN_PIPE Error = 109;
|
||||
ERROR_BUFFER_OVERFLOW Error = 111;
|
||||
ERROR_INSUFFICIENT_BUFFER Error = 122;
|
||||
ERROR_MOD_NOT_FOUND Error = 126;
|
||||
ERROR_PROC_NOT_FOUND Error = 127;
|
||||
ERROR_DIR_NOT_EMPTY Error = 145;
|
||||
ERROR_ALREADY_EXISTS Error = 183;
|
||||
ERROR_ENVVAR_NOT_FOUND Error = 203;
|
||||
ERROR_MORE_DATA Error = 234;
|
||||
ERROR_OPERATION_ABORTED Error = 995;
|
||||
ERROR_IO_PENDING Error = 997;
|
||||
ERROR_NOT_FOUND Error = 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD Error = 1314;
|
||||
WSAEACCES Error = 10013;
|
||||
WSAECONNRESET Error = 10054;
|
||||
}
|
||||
|
||||
const { // Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE Error = 1<<29 + iota;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
proc open(path string, mode int, perm u32) -> (Handle, Error) {
|
||||
using win32;
|
||||
if path.count == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
File struct {
|
||||
handle File_Handle;
|
||||
last_write_time File_Time;
|
||||
var access u32;
|
||||
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= FILE_GENERIC_WRITE;
|
||||
access |= FILE_APPEND_DATA;
|
||||
}
|
||||
|
||||
var share_mode = (FILE_SHARE_READ|FILE_SHARE_WRITE) as u32;
|
||||
var sa ^SECURITY_ATTRIBUTES = nil;
|
||||
var sa_inherit = SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = ^sa_inherit;
|
||||
}
|
||||
|
||||
var create_mode u32;
|
||||
match {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
create_mode = CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = TRUNCATE_EXISTING;
|
||||
default:
|
||||
create_mode = OPEN_EXISTING;
|
||||
}
|
||||
}
|
||||
|
||||
proc open(name string) -> (File, bool) {
|
||||
using win32;
|
||||
var buf [300]byte;
|
||||
var f File;
|
||||
copy(buf[:], name as []byte);
|
||||
f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil) as rawptr;
|
||||
var success = f.handle.p != INVALID_HANDLE_VALUE;
|
||||
f.last_write_time = last_write_time(^f);
|
||||
return f, success;
|
||||
copy(buf[:], path as []byte);
|
||||
|
||||
var handle = CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil) as Handle;
|
||||
if handle == INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
var err = GetLastError();
|
||||
return INVALID_HANDLE, err as Error;
|
||||
}
|
||||
|
||||
proc create(name string) -> (File, bool) {
|
||||
using win32;
|
||||
var buf [300]byte;
|
||||
var f File;
|
||||
copy(buf[:], name as []byte);
|
||||
f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil) as rawptr;
|
||||
var success = f.handle.p != INVALID_HANDLE_VALUE;
|
||||
f.last_write_time = last_write_time(^f);
|
||||
return f, success;
|
||||
proc close(fd Handle) {
|
||||
win32.CloseHandle(fd as win32.HANDLE);
|
||||
}
|
||||
|
||||
proc close(using f ^File) {
|
||||
win32.CloseHandle(handle.p as win32.HANDLE);
|
||||
}
|
||||
|
||||
proc write(using f ^File, buf []byte) -> bool {
|
||||
proc write(fd Handle, data []byte) -> (int, Error) {
|
||||
var bytes_written i32;
|
||||
return win32.WriteFile(handle.p as win32.HANDLE, buf.data, buf.count as i32, ^bytes_written, nil) != 0;
|
||||
}
|
||||
|
||||
proc file_has_changed(f ^File) -> bool {
|
||||
var last_write_time = last_write_time(f);
|
||||
if f.last_write_time != last_write_time {
|
||||
f.last_write_time = last_write_time;
|
||||
return true;
|
||||
var e = win32.WriteFile(fd as win32.HANDLE, data.data, data.count as i32, ^bytes_written, nil);
|
||||
if e != 0 {
|
||||
return 0, e as Error;
|
||||
}
|
||||
return false;
|
||||
return bytes_written as int, ERROR_NONE;
|
||||
}
|
||||
|
||||
proc read(fd Handle, data []byte) -> (int, Error) {
|
||||
var bytes_read i32;
|
||||
var e = win32.ReadFile(fd as win32.HANDLE, data.data, data.count as u32, ^bytes_read, nil);
|
||||
if e != win32.FALSE {
|
||||
var err = win32.GetLastError();
|
||||
return 0, err as Error;
|
||||
}
|
||||
return bytes_read as int, ERROR_NONE;
|
||||
}
|
||||
|
||||
proc seek(fd Handle, offset i64, whence int) -> (i64, Error) {
|
||||
using win32;
|
||||
var w u32;
|
||||
match whence {
|
||||
case 0: w = FILE_BEGIN;
|
||||
case 1: w = FILE_CURRENT;
|
||||
case 2: w = FILE_END;
|
||||
}
|
||||
var hi = (offset>>32) as i32;
|
||||
var lo = offset as i32;
|
||||
var ft = GetFileType(fd as HANDLE);
|
||||
if ft == FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
var dw_ptr = SetFilePointer(fd as HANDLE, lo, ^hi, w);
|
||||
if dw_ptr == INVALID_SET_FILE_POINTER {
|
||||
var err = GetLastError();
|
||||
return 0, err as Error;
|
||||
}
|
||||
return (hi as i64)<<32 + (dw_ptr as i64), ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
var {
|
||||
stdin = get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout = get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr = get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
}
|
||||
|
||||
proc get_std_handle(h int) -> Handle {
|
||||
var fd = win32.GetStdHandle(h as i32);
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return fd as Handle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
proc last_write_time(f ^File) -> File_Time {
|
||||
|
||||
|
||||
|
||||
proc last_write_time(fd Handle) -> File_Time {
|
||||
var file_info win32.BY_HANDLE_FILE_INFORMATION;
|
||||
win32.GetFileInformationByHandle(f.handle.p as win32.HANDLE, ^file_info);
|
||||
var l = file_info.last_write_time.low_date_time as File_Time;
|
||||
var h = file_info.last_write_time.high_date_time as File_Time;
|
||||
return l | h << 32;
|
||||
win32.GetFileInformationByHandle(fd as win32.HANDLE, ^file_info);
|
||||
var lo = file_info.last_write_time.lo as File_Time;
|
||||
var hi = file_info.last_write_time.hi as File_Time;
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
proc last_write_time_by_name(name string) -> File_Time {
|
||||
var last_write_time win32.FILETIME;
|
||||
var data win32.WIN32_FILE_ATTRIBUTE_DATA;
|
||||
var data win32.FILE_ATTRIBUTE_DATA;
|
||||
var buf [1024]byte;
|
||||
|
||||
assert(buf.count > name.count);
|
||||
@@ -80,46 +198,27 @@ proc last_write_time_by_name(name string) -> File_Time {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
var l = last_write_time.low_date_time as File_Time;
|
||||
var h = last_write_time.high_date_time as File_Time;
|
||||
var l = last_write_time.lo as File_Time;
|
||||
var h = last_write_time.hi as File_Time;
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const {
|
||||
FILE_STANDARD_INPUT = iota;
|
||||
FILE_STANDARD_OUTPUT;
|
||||
FILE_STANDARD_ERROR;
|
||||
|
||||
FILE_STANDARD_COUNT;
|
||||
}
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
var {
|
||||
__std_files = [FILE_STANDARD_COUNT]File{
|
||||
{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE) transmute File_Handle },
|
||||
{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE) transmute File_Handle },
|
||||
{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE) transmute File_Handle },
|
||||
};
|
||||
|
||||
stdin = ^__std_files[FILE_STANDARD_INPUT];
|
||||
stdout = ^__std_files[FILE_STANDARD_OUTPUT];
|
||||
stderr = ^__std_files[FILE_STANDARD_ERROR];
|
||||
}
|
||||
|
||||
|
||||
proc read_entire_file(name string) -> ([]byte, bool) {
|
||||
var buf [300]byte;
|
||||
copy(buf[:], name as []byte);
|
||||
|
||||
var f, file_ok = open(name);
|
||||
if !file_ok {
|
||||
var fd, err = open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(^f);
|
||||
defer close(fd);
|
||||
|
||||
var length i64;
|
||||
var file_size_ok = win32.GetFileSizeEx(f.handle.p as win32.HANDLE, ^length) != 0;
|
||||
var file_size_ok = win32.GetFileSizeEx(fd as win32.HANDLE, ^length) != 0;
|
||||
if !file_size_ok {
|
||||
return nil, false;
|
||||
}
|
||||
@@ -142,7 +241,7 @@ proc read_entire_file(name string) -> ([]byte, bool) {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(f.handle.p as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
win32.ReadFile(fd as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data.data);
|
||||
return nil, false;
|
||||
@@ -178,3 +277,5 @@ proc current_thread_id() -> int {
|
||||
return GetCurrentThreadId() as int;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ type {
|
||||
const {
|
||||
INVALID_HANDLE_VALUE = (-1 as int) as HANDLE;
|
||||
|
||||
FALSE BOOL = 0;
|
||||
TRUE BOOL = 1;
|
||||
|
||||
CS_VREDRAW = 0x0001;
|
||||
CS_HREDRAW = 0x0002;
|
||||
CS_OWNDC = 0x0020;
|
||||
@@ -88,7 +91,7 @@ type {
|
||||
}
|
||||
|
||||
FILETIME struct #ordered {
|
||||
low_date_time, high_date_time u32;
|
||||
lo, hi u32;
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION struct #ordered {
|
||||
@@ -104,7 +107,7 @@ type {
|
||||
file_index_low u32;
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA struct #ordered {
|
||||
FILE_ATTRIBUTE_DATA struct #ordered {
|
||||
file_attributes u32;
|
||||
creation_time,
|
||||
last_access_time,
|
||||
@@ -174,8 +177,8 @@ proc GetCurrentThreadId() -> u32 #foreign #dll_import
|
||||
proc CloseHandle (h HANDLE) -> i32 #foreign #dll_import
|
||||
proc GetStdHandle(h i32) -> HANDLE #foreign #dll_import
|
||||
proc CreateFileA (filename ^u8, desired_access, share_mode u32,
|
||||
security rawptr,
|
||||
creation, flags_and_attribs u32, template_file HANDLE) -> HANDLE #foreign #dll_import
|
||||
security rawptr,
|
||||
creation, flags_and_attribs u32, template_file HANDLE) -> HANDLE #foreign #dll_import
|
||||
proc ReadFile (h HANDLE, buf rawptr, to_read u32, bytes_read ^i32, overlapped rawptr) -> BOOL #foreign #dll_import
|
||||
proc WriteFile (h HANDLE, buf rawptr, len i32, written_result ^i32, overlapped rawptr) -> i32 #foreign #dll_import
|
||||
|
||||
@@ -183,6 +186,23 @@ proc GetFileSizeEx (file_handle HANDLE, file_size ^i64) -> BOOL #for
|
||||
proc GetFileAttributesExA (filename ^u8, info_level_id GET_FILEEX_INFO_LEVELS, file_info rawptr) -> BOOL #foreign #dll_import
|
||||
proc GetFileInformationByHandle(file_handle HANDLE, file_info ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign #dll_import
|
||||
|
||||
proc GetFileType(file_handle HANDLE) -> u32 #foreign #dll_import
|
||||
proc SetFilePointer(file_handle HANDLE, distance_to_move i32, distance_to_move_high ^i32, move_method u32) -> u32 #foreign #dll_import
|
||||
|
||||
proc SetHandleInformation(obj HANDLE, mask, flags u32) -> BOOL #foreign #dll_import
|
||||
|
||||
const {
|
||||
HANDLE_FLAG_INHERIT = 1;
|
||||
HANDLE_FLAG_PROTECT_FROM_CLOSE = 2;
|
||||
}
|
||||
|
||||
|
||||
const {
|
||||
FILE_BEGIN = 0;
|
||||
FILE_CURRENT = 1;
|
||||
FILE_END = 2;
|
||||
}
|
||||
|
||||
const {
|
||||
FILE_SHARE_READ = 0x00000001;
|
||||
FILE_SHARE_WRITE = 0x00000002;
|
||||
@@ -192,6 +212,8 @@ const {
|
||||
FILE_GENERIC_WRITE = 0x40000000;
|
||||
FILE_GENERIC_READ = 0x80000000;
|
||||
|
||||
FILE_APPEND_DATA = 0x0004;
|
||||
|
||||
STD_INPUT_HANDLE = -10;
|
||||
STD_OUTPUT_HANDLE = -11;
|
||||
STD_ERROR_HANDLE = -12;
|
||||
@@ -201,6 +223,27 @@ const {
|
||||
OPEN_EXISTING = 3;
|
||||
OPEN_ALWAYS = 4;
|
||||
TRUNCATE_EXISTING = 5;
|
||||
|
||||
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;
|
||||
|
||||
INVALID_SET_FILE_POINTER = ~(0 as u32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,16 @@ typedef struct TypeAndValue {
|
||||
ExactValue value;
|
||||
} TypeAndValue;
|
||||
|
||||
bool is_operand_value(Operand o) {
|
||||
switch (o.mode) {
|
||||
case Addressing_Value:
|
||||
case Addressing_Variable:
|
||||
case Addressing_Constant:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct DeclInfo {
|
||||
@@ -92,6 +102,14 @@ typedef enum ExprKind {
|
||||
Expr_Stmt,
|
||||
} ExprKind;
|
||||
|
||||
// Statements and Declarations
|
||||
typedef enum StmtFlag {
|
||||
Stmt_BreakAllowed = 1<<0,
|
||||
Stmt_ContinueAllowed = 1<<1,
|
||||
Stmt_FallthroughAllowed = 1<<2,
|
||||
Stmt_GiveAllowed = 1<<3,
|
||||
} StmtFlag;
|
||||
|
||||
typedef enum BuiltinProcId {
|
||||
BuiltinProc_Invalid,
|
||||
|
||||
@@ -357,9 +375,11 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) {
|
||||
|
||||
void check_open_scope(Checker *c, AstNode *node) {
|
||||
GB_ASSERT(node != NULL);
|
||||
node = unparen_expr(node);
|
||||
GB_ASSERT(node->kind == AstNode_Invalid ||
|
||||
is_ast_node_stmt(node) ||
|
||||
is_ast_node_type(node));
|
||||
is_ast_node_type(node) ||
|
||||
node->kind == AstNode_BlockExpr);
|
||||
Scope *scope = make_scope(c->context.scope, c->allocator);
|
||||
add_scope(c, node, scope);
|
||||
if (node->kind == AstNode_ProcType) {
|
||||
|
||||
@@ -92,7 +92,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
}
|
||||
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
|
||||
if (lhs[0]->kind == Entity_Variable &&
|
||||
lhs[0]->Variable.is_let) {
|
||||
if (lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
@@ -350,6 +357,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
if (proc_type->Proc.calling_convention != ProcCC_Odin) {
|
||||
error_node(d->proc_decl, "An internal procedure may only have the Odin calling convention");
|
||||
proc_type->Proc.calling_convention = ProcCC_Odin;
|
||||
}
|
||||
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
|
||||
|
||||
@@ -14,6 +14,10 @@ void check_entity_decl (Checker *c, Entity *e, DeclInfo *decl, Type
|
||||
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr, Type *named_type);
|
||||
void check_proc_body (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
|
||||
void update_expr_type (Checker *c, AstNode *e, Type *type, bool final);
|
||||
bool check_is_terminating (AstNode *node);
|
||||
bool check_has_break (AstNode *stmt, bool implicit);
|
||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
||||
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
|
||||
|
||||
|
||||
gb_inline Type *check_type(Checker *c, AstNode *expression) {
|
||||
@@ -3652,6 +3656,42 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool check_is_giving(AstNode *node, AstNode **give_expr);
|
||||
|
||||
bool check_giving_list(AstNodeArray stmts, AstNode **give_expr) {
|
||||
// Iterate backwards
|
||||
for (isize n = stmts.count-1; n >= 0; n--) {
|
||||
AstNode *stmt = stmts.e[n];
|
||||
if (stmt->kind != AstNode_EmptyStmt) {
|
||||
return check_is_giving(stmt, give_expr);
|
||||
}
|
||||
}
|
||||
|
||||
if (give_expr) *give_expr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_is_giving(AstNode *node, AstNode **give_expr) {
|
||||
switch (node->kind) {
|
||||
case_ast_node(es, ExprStmt, node);
|
||||
return check_is_giving(es->expr, give_expr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ye, GiveExpr, node);
|
||||
if (give_expr) *give_expr = node;
|
||||
return true;
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BlockExpr, node);
|
||||
return check_giving_list(be->stmts, give_expr);
|
||||
case_end;
|
||||
}
|
||||
|
||||
if (give_expr) *give_expr = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
|
||||
ExprKind kind = Expr_Stmt;
|
||||
|
||||
@@ -3700,7 +3740,105 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
||||
|
||||
o->mode = Addressing_Value;
|
||||
o->type = proc_type;
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BlockExpr, node);
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(node, "A block expression is only allowed withing a procedure");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (isize i = be->stmts.count-1; i >= 0; i--) {
|
||||
if (be->stmts.e[i]->kind != AstNode_EmptyStmt) {
|
||||
break;
|
||||
}
|
||||
be->stmts.count--;
|
||||
}
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_stmt_list(c, be->stmts, Stmt_GiveAllowed);
|
||||
check_close_scope(c);
|
||||
|
||||
|
||||
if (be->stmts.count == 0) {
|
||||
error_node(node, "Empty block expression");
|
||||
goto error;
|
||||
}
|
||||
AstNode *give_node = NULL;
|
||||
if (!check_is_giving(node, &give_node) || give_node == NULL) {
|
||||
error_node(node, "Missing give statement at");
|
||||
goto error;
|
||||
}
|
||||
|
||||
GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr);
|
||||
be->give_node = give_node;
|
||||
|
||||
o->type = type_of_expr(&c->info, give_node);
|
||||
o->mode = Addressing_Value;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ye, GiveExpr, node);
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(node, "A give expression is only allowed withing a procedure");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ye->results.count == 0) {
|
||||
error_node(node, "No give values");
|
||||
goto error;
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
Array(Operand) operands;
|
||||
array_init_reserve(&operands, c->tmp_allocator, 2*ye->results.count);
|
||||
|
||||
for_array(i, ye->results) {
|
||||
AstNode *rhs = ye->results.e[i];
|
||||
Operand o = {0};
|
||||
check_multi_expr(c, &o, rhs);
|
||||
if (!is_operand_value(o)) {
|
||||
error_node(rhs, "Expected a value for a `give`");
|
||||
continue;
|
||||
}
|
||||
if (o.type->kind != Type_Tuple) {
|
||||
array_add(&operands, o);
|
||||
} else {
|
||||
TypeTuple *tuple = &o.type->Tuple;
|
||||
for (isize j = 0; j < tuple->variable_count; j++) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(&operands, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type *give_type = NULL;
|
||||
|
||||
if (operands.count == 0) {
|
||||
error_node(node, "`give` has no type");
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
goto error;
|
||||
} else if (operands.count == 1) {
|
||||
give_type = default_type(operands.e[0].type);
|
||||
} else {
|
||||
Type *tuple = make_type_tuple(c->allocator);
|
||||
|
||||
Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count);
|
||||
isize variable_index = 0;
|
||||
for_array(i, operands) {
|
||||
Type *type = default_type(operands.e[i].type);
|
||||
variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
|
||||
}
|
||||
tuple->Tuple.variables = variables;
|
||||
tuple->Tuple.variable_count = operands.count;
|
||||
|
||||
give_type = tuple;
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
|
||||
o->type = give_type;
|
||||
o->mode = Addressing_Value;
|
||||
case_end;
|
||||
|
||||
case_ast_node(cl, CompoundLit, node);
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
bool check_is_terminating(AstNode *node);
|
||||
bool check_has_break (AstNode *stmt, bool implicit);
|
||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
||||
|
||||
|
||||
// Statements and Declarations
|
||||
typedef enum StmtFlag {
|
||||
Stmt_BreakAllowed = GB_BIT(0),
|
||||
Stmt_ContinueAllowed = GB_BIT(1),
|
||||
Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
|
||||
} StmtFlag;
|
||||
|
||||
|
||||
void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
if (stmts.count == 0) {
|
||||
return;
|
||||
@@ -40,6 +27,10 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
|
||||
if (n->kind == AstNode_ReturnStmt && i+1 < max) {
|
||||
error_node(n, "Statements after this `return` are never executed");
|
||||
} else if (n->kind == AstNode_ExprStmt && i+1 < max) {
|
||||
if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
|
||||
error_node(n, "A `give` must be the last statement in a block");
|
||||
}
|
||||
}
|
||||
|
||||
check_stmt(c, n, new_flags);
|
||||
@@ -48,7 +39,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
}
|
||||
|
||||
bool check_is_terminating_list(AstNodeArray stmts) {
|
||||
|
||||
// Iterate backwards
|
||||
for (isize n = stmts.count-1; n >= 0; n--) {
|
||||
AstNode *stmt = stmts.e[n];
|
||||
@@ -355,6 +345,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
if (operand.expr->kind == AstNode_CallExpr) {
|
||||
return;
|
||||
}
|
||||
if (operand.expr->kind == AstNode_GiveExpr) {
|
||||
if ((flags&Stmt_GiveAllowed) != 0) {
|
||||
return;
|
||||
}
|
||||
error_node(node, "Illegal use of `give`");
|
||||
}
|
||||
gbString expr_str = expr_to_string(operand.expr);
|
||||
error_node(node, "Expression is not used: `%s`", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
|
||||
@@ -775,6 +775,65 @@ Type *default_type(Type *type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// NOTE(bill): Valid Compile time execution #run type
|
||||
bool is_type_cte_safe(Type *type) {
|
||||
type = default_type(base_type(type));
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_rawptr:
|
||||
case Basic_any:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Pointer:
|
||||
return false;
|
||||
|
||||
case Type_Array:
|
||||
return is_type_cte_safe(type->Array.elem);
|
||||
|
||||
case Type_Vector: // NOTE(bill): This should always to be true but this is for sanity reasons
|
||||
return is_type_cte_safe(type->Vector.elem);
|
||||
|
||||
case Type_Slice:
|
||||
return false;
|
||||
|
||||
case Type_Maybe:
|
||||
return is_type_cte_safe(type->Maybe.elem);
|
||||
|
||||
case Type_Record: {
|
||||
if (type->Record.kind != TypeRecord_Struct) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < type->Record.field_count; i++) {
|
||||
Entity *v = type->Record.fields[i];
|
||||
if (!is_type_cte_safe(v->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Type_Tuple: {
|
||||
for (isize i = 0; i < type->Tuple.variable_count; i++) {
|
||||
Entity *v = type->Tuple.variables[i];
|
||||
if (!is_type_cte_safe(v->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Type_Proc:
|
||||
// TODO(bill): How should I handle procedures in the CTE stage?
|
||||
// return type->Proc.calling_convention == ProcCC_Odin;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
71
src/parser.c
71
src/parser.c
@@ -144,6 +144,15 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
bool triple_indexed; \
|
||||
}) \
|
||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||
AST_NODE_KIND(BlockExpr, "block expr", struct { \
|
||||
AstNodeArray stmts; \
|
||||
Token open, close; \
|
||||
AstNode *give_node; \
|
||||
}) \
|
||||
AST_NODE_KIND(GiveExpr, "give expression", struct { \
|
||||
Token token; \
|
||||
AstNodeArray results; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ExprEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||
@@ -432,6 +441,10 @@ Token ast_node_token(AstNode *node) {
|
||||
return node->DerefExpr.op;
|
||||
case AstNode_DemaybeExpr:
|
||||
return node->DemaybeExpr.op;
|
||||
case AstNode_BlockExpr:
|
||||
return node->BlockExpr.open;
|
||||
case AstNode_GiveExpr:
|
||||
return node->GiveExpr.token;
|
||||
case AstNode_BadStmt:
|
||||
return node->BadStmt.begin;
|
||||
case AstNode_EmptyStmt:
|
||||
@@ -729,6 +742,24 @@ AstNode *make_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AstNode *make_block_expr(AstFile *f, AstNodeArray stmts, Token open, Token close) {
|
||||
AstNode *result = make_node(f, AstNode_BlockExpr);
|
||||
result->BlockExpr.stmts = stmts;
|
||||
result->BlockExpr.open = open;
|
||||
result->BlockExpr.close = close;
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *make_give_expr(AstFile *f, Token token, AstNodeArray results) {
|
||||
AstNode *result = make_node(f, AstNode_GiveExpr);
|
||||
result->GiveExpr.token = token;
|
||||
result->GiveExpr.results = results;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) {
|
||||
AstNode *result = make_node(f, AstNode_BadStmt);
|
||||
result->BadStmt.begin = begin;
|
||||
@@ -1508,6 +1539,9 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeArray parse_lhs_expr_list(AstFile *f);
|
||||
AstNodeArray parse_rhs_expr_list(AstFile *f);
|
||||
|
||||
AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
AstNode *operand = NULL; // Operand
|
||||
switch (f->curr_token.kind) {
|
||||
@@ -1623,6 +1657,17 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
return type;
|
||||
}
|
||||
|
||||
case Token_OpenBrace: {
|
||||
AstNodeArray stmts = {0};
|
||||
Token open, close;
|
||||
open = expect_token(f, Token_OpenBrace);
|
||||
f->expr_level++;
|
||||
stmts = parse_stmt_list(f);
|
||||
f->expr_level--;
|
||||
close = expect_token(f, Token_CloseBrace);
|
||||
return make_block_expr(f, stmts, open, close);
|
||||
}
|
||||
|
||||
default: {
|
||||
AstNode *type = parse_identifier_or_type(f);
|
||||
if (type != NULL) {
|
||||
@@ -2619,12 +2664,15 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
|
||||
return make_bad_stmt(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
if (f->expr_level > 0) {
|
||||
syntax_error(f->curr_token, "You cannot use a return statement within an expression");
|
||||
return make_bad_stmt(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
|
||||
Token token = expect_token(f, Token_return);
|
||||
AstNodeArray results = make_ast_node_array(f);
|
||||
|
||||
if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace &&
|
||||
f->curr_token.pos.line == token.pos.line) {
|
||||
if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) {
|
||||
results = parse_rhs_expr_list(f);
|
||||
}
|
||||
|
||||
@@ -2632,6 +2680,23 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
return make_return_stmt(f, token, results);
|
||||
}
|
||||
|
||||
|
||||
AstNode *parse_give_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(f->curr_token, "You cannot use a give statement in the file scope");
|
||||
return make_bad_stmt(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
if (f->expr_level == 0) {
|
||||
syntax_error(f->curr_token, "A give statement must be used within an expression");
|
||||
return make_bad_stmt(f, f->curr_token, f->curr_token);
|
||||
}
|
||||
|
||||
Token token = expect_token(f, Token_give);
|
||||
AstNodeArray results = parse_rhs_expr_list(f);
|
||||
expect_semicolon(f, results.e[0]);
|
||||
return make_expr_stmt(f, make_give_expr(f, token, results));
|
||||
}
|
||||
|
||||
AstNode *parse_for_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(f->curr_token, "You cannot use a for statement in the file scope");
|
||||
@@ -2861,7 +2926,6 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
expect_semicolon(f, s);
|
||||
return s;
|
||||
|
||||
// TODO(bill): other keywords
|
||||
case Token_if: return parse_if_stmt(f);
|
||||
case Token_when: return parse_when_stmt(f);
|
||||
case Token_for: return parse_for_stmt(f);
|
||||
@@ -2869,6 +2933,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
case Token_defer: return parse_defer_stmt(f);
|
||||
case Token_asm: return parse_asm_stmt(f);
|
||||
case Token_return: return parse_return_stmt(f);
|
||||
case Token_give: return parse_give_stmt(f);
|
||||
|
||||
case Token_break:
|
||||
case Token_continue:
|
||||
|
||||
66
src/ssa.c
66
src/ssa.c
@@ -2571,7 +2571,7 @@ ssaValue *ssa_find_implicit_value_backing(ssaProcedure *proc, ImplicitValueId id
|
||||
return *value;
|
||||
}
|
||||
|
||||
|
||||
void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts);
|
||||
|
||||
ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
|
||||
expr = unparen_expr(expr);
|
||||
@@ -2626,6 +2626,70 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
return ssa_addr_load(proc, ssa_build_addr(proc, expr));
|
||||
case_end;
|
||||
|
||||
case_ast_node(be, BlockExpr, expr);
|
||||
ssa_emit_comment(proc, str_lit("BlockExpr"));
|
||||
ssa_open_scope(proc);
|
||||
|
||||
AstNodeArray stmts = be->stmts;
|
||||
stmts.count--;
|
||||
ssa_build_stmt_list(proc, stmts);
|
||||
|
||||
AstNode *give_stmt = be->stmts.e[be->stmts.count-1];
|
||||
GB_ASSERT(give_stmt->kind == AstNode_ExprStmt);
|
||||
AstNode *give_expr = give_stmt->ExprStmt.expr;
|
||||
GB_ASSERT(give_expr->kind == AstNode_GiveExpr);
|
||||
ssaValue *value = ssa_build_expr(proc, give_expr);
|
||||
|
||||
ssa_close_scope(proc, ssaDeferExit_Default, NULL);
|
||||
|
||||
return value;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ge, GiveExpr, expr);
|
||||
ssa_emit_comment(proc, str_lit("GiveExpr"));
|
||||
|
||||
ssaValue *v = NULL;
|
||||
Type *give_type = type_of_expr(proc->module->info, expr);
|
||||
GB_ASSERT(give_type != NULL);
|
||||
if (give_type->kind != Type_Tuple) {
|
||||
v = ssa_emit_conv(proc, ssa_build_expr(proc, ge->results.e[0]), give_type);
|
||||
} else {
|
||||
TypeTuple *tuple = &give_type->Tuple;
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
|
||||
ssaValueArray results;
|
||||
array_init_reserve(&results, proc->module->tmp_allocator, tuple->variable_count);
|
||||
|
||||
for_array(res_index, ge->results) {
|
||||
ssaValue *res = ssa_build_expr(proc, ge->results.e[res_index]);
|
||||
Type *t = ssa_type(res);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
ssaValue *v = ssa_emit_struct_ev(proc, res, i);
|
||||
array_add(&results, v);
|
||||
}
|
||||
} else {
|
||||
array_add(&results, res);
|
||||
}
|
||||
}
|
||||
|
||||
v = ssa_add_local_generated(proc, give_type);
|
||||
for_array(i, results) {
|
||||
Entity *e = tuple->variables[i];
|
||||
ssaValue *res = ssa_emit_conv(proc, results.e[i], e->type);
|
||||
ssaValue *field = ssa_emit_struct_ep(proc, v, i);
|
||||
ssa_emit_store(proc, field, res);
|
||||
}
|
||||
|
||||
v = ssa_emit_load(proc, v);
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
return v;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ue, UnaryExpr, expr);
|
||||
switch (ue->op.kind) {
|
||||
case Token_Pointer:
|
||||
|
||||
@@ -104,6 +104,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_range, "range"), \
|
||||
TOKEN_KIND(Token_defer, "defer"), \
|
||||
TOKEN_KIND(Token_return, "return"), \
|
||||
TOKEN_KIND(Token_give, "give"), \
|
||||
TOKEN_KIND(Token_struct, "struct"), \
|
||||
TOKEN_KIND(Token_union, "union"), \
|
||||
TOKEN_KIND(Token_raw_union, "raw_union"), \
|
||||
|
||||
Reference in New Issue
Block a user