mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-04 12:07:45 +00:00
min, max, abs
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#load "file.odin"
|
||||
|
||||
print_string :: proc(s: string) {
|
||||
file_write(file_get_standard(FileStandard.OUTPUT), s as []byte)
|
||||
file_write(file_get_standard(File_Standard.OUTPUT), s as []byte)
|
||||
}
|
||||
|
||||
byte_reverse :: proc(b: []byte) {
|
||||
|
||||
@@ -4,24 +4,17 @@
|
||||
|
||||
main :: proc() {
|
||||
|
||||
print_int(min(1, 2)); nl()
|
||||
print_int(max(1, 2)); nl()
|
||||
print_int(abs(-1337)); nl()
|
||||
|
||||
a, b, c := 1, 2, -1337
|
||||
print_int(min(a, b)); nl()
|
||||
print_int(max(a, b)); nl()
|
||||
print_int(abs(c) as int); nl()
|
||||
|
||||
match x := "1"; x {
|
||||
case "1":
|
||||
print_string("1!\n")
|
||||
case "2":
|
||||
print_string("2!\n")
|
||||
if true {
|
||||
break
|
||||
}
|
||||
case "3", "4":
|
||||
print_string("3 or 4!\n")
|
||||
fallthrough
|
||||
default:
|
||||
print_string("default!\n")
|
||||
}
|
||||
|
||||
nl()
|
||||
|
||||
/*
|
||||
Vec3 :: type struct { x, y, z: f32 }
|
||||
Entity :: type struct {
|
||||
|
||||
@@ -34,7 +34,7 @@ file_write :: proc(f: ^File, buf: []byte) -> bool {
|
||||
return WriteFile(f.handle, ^buf[0], len(buf) as i32, ^bytes_written, null) != 0
|
||||
}
|
||||
|
||||
FileStandard :: type enum {
|
||||
File_Standard :: type enum {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
ERROR,
|
||||
@@ -42,12 +42,12 @@ FileStandard :: type enum {
|
||||
}
|
||||
|
||||
__std_file_set := false;
|
||||
__std_files: [FileStandard.COUNT as int]File;
|
||||
__std_files: [File_Standard.COUNT as int]File;
|
||||
|
||||
file_get_standard :: proc(std: FileStandard) -> ^File {
|
||||
// using FileStandard;
|
||||
file_get_standard :: proc(std: File_Standard) -> ^File {
|
||||
// using File_Standard;
|
||||
if (!__std_file_set) {
|
||||
using FileStandard
|
||||
using File_Standard
|
||||
__std_files[INPUT] .handle = GetStdHandle(STD_INPUT_HANDLE)
|
||||
__std_files[OUTPUT].handle = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
__std_files[ERROR] .handle = GetStdHandle(STD_ERROR_HANDLE)
|
||||
|
||||
@@ -29,17 +29,10 @@ fsqrt :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
|
||||
fsin :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
|
||||
fcos :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
|
||||
flerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t }
|
||||
fclamp :: proc(x, lower, upper: f32) -> f32 { return fmin(fmax(x, lower), upper) }
|
||||
fclamp :: proc(x, lower, upper: f32) -> f32 { return min(max(x, lower), upper) }
|
||||
fclamp01 :: proc(x: f32) -> f32 { return fclamp(x, 0, 1) }
|
||||
fabs :: proc(x: f32) -> f32 { if x < 0 { x = -x } return x }
|
||||
fsign :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 }
|
||||
|
||||
fmin :: proc(a, b: f32) -> f32 { if a < b { return a } return b }
|
||||
fmax :: proc(a, b: f32) -> f32 { if a > b { return a } return b }
|
||||
fmin3 :: proc(a, b, c: f32) -> f32 { return fmin(fmin(a, b), c) }
|
||||
fmax3 :: proc(a, b, c: f32) -> f32 { return fmax(fmax(a, b), c) }
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := x transmute u32
|
||||
iy := y transmute u32
|
||||
@@ -76,8 +69,8 @@ remainder :: proc(x, y: f32) -> f32 {
|
||||
}
|
||||
|
||||
fmod :: proc(x, y: f32) -> f32 {
|
||||
y = fabs(y)
|
||||
result := remainder(fabs(x), y)
|
||||
y = abs(y)
|
||||
result := remainder(abs(x), y)
|
||||
if fsign(result) < 0 {
|
||||
result += y
|
||||
}
|
||||
|
||||
@@ -11,8 +11,15 @@ heap_free :: proc(ptr: rawptr) {
|
||||
}
|
||||
|
||||
|
||||
memory_zero :: proc(data: rawptr, len: int) {
|
||||
d := slice_ptr(data as ^byte, len)
|
||||
for i := 0; i < len; i++ {
|
||||
d[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
memory_compare :: proc(dst, src: rawptr, len: int) -> int {
|
||||
s1, s2: ^u8 = dst, src
|
||||
s1, s2: ^byte = dst, src
|
||||
for i := 0; i < len; i++ {
|
||||
a := ptr_offset(s1, i)^
|
||||
b := ptr_offset(s2, i)^
|
||||
@@ -31,7 +38,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
|
||||
v128b :: type {4}u32
|
||||
static_assert(align_of(v128b) == 16)
|
||||
|
||||
d, s: ^u8 = dst, src
|
||||
d, s: ^byte = dst, src
|
||||
|
||||
for ; s as uint % 16 != 0 && n != 0; n-- {
|
||||
d^ = s^
|
||||
@@ -158,7 +165,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
|
||||
}
|
||||
|
||||
memory_move :: proc(dst, src: rawptr, n: int) #inline {
|
||||
d, s: ^u8 = dst, src
|
||||
d, s: ^byte = dst, src
|
||||
if d == s {
|
||||
return
|
||||
}
|
||||
@@ -264,7 +271,7 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >
|
||||
|
||||
|
||||
|
||||
AllocationMode :: type enum {
|
||||
Allocation_Mode :: type enum {
|
||||
ALLOC,
|
||||
DEALLOC,
|
||||
DEALLOC_ALL,
|
||||
@@ -273,12 +280,12 @@ AllocationMode :: type enum {
|
||||
|
||||
|
||||
|
||||
AllocatorProc :: type proc(allocator_data: rawptr, mode: AllocationMode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr
|
||||
Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr
|
||||
|
||||
Allocator :: type struct {
|
||||
procedure: AllocatorProc;
|
||||
procedure: Allocator_Proc;
|
||||
data: rawptr
|
||||
}
|
||||
|
||||
@@ -309,18 +316,18 @@ alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_AL
|
||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
return a.procedure(a.data, AllocationMode.ALLOC, size, alignment, null, 0, 0)
|
||||
return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
|
||||
}
|
||||
|
||||
dealloc :: proc(ptr: rawptr) #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
_ = a.procedure(a.data, AllocationMode.DEALLOC, 0, 0, ptr, 0, 0)
|
||||
_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
|
||||
}
|
||||
dealloc_all :: proc(ptr: rawptr) #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
_ = a.procedure(a.data, AllocationMode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
|
||||
_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -328,7 +335,7 @@ resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { r
|
||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
return a.procedure(a.data, AllocationMode.RESIZE, new_size, alignment, ptr, old_size, 0)
|
||||
return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -355,19 +362,25 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
||||
if new_memory == null {
|
||||
return null
|
||||
}
|
||||
_ = copy(slice_ptr(new_memory as ^u8, new_size), slice_ptr(old_memory as ^u8, old_size))
|
||||
min_size := old_size;
|
||||
if min_size > new_size {
|
||||
min_size = new_size;
|
||||
}
|
||||
memory_copy(new_memory, old_memory, min_size);
|
||||
dealloc(old_memory)
|
||||
return new_memory
|
||||
}
|
||||
|
||||
|
||||
__default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocationMode,
|
||||
__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using AllocationMode
|
||||
using Allocation_Mode
|
||||
match mode {
|
||||
case ALLOC:
|
||||
return heap_alloc(size)
|
||||
data := heap_alloc(size)
|
||||
memory_zero(data, size)
|
||||
return data
|
||||
case RESIZE:
|
||||
return default_resize_align(old_memory, old_size, size, alignment)
|
||||
case DEALLOC:
|
||||
|
||||
@@ -145,6 +145,10 @@ enum BuiltinProcId {
|
||||
BuiltinProc_ptr_sub,
|
||||
BuiltinProc_slice_ptr,
|
||||
|
||||
BuiltinProc_min,
|
||||
BuiltinProc_max,
|
||||
BuiltinProc_abs,
|
||||
|
||||
BuiltinProc_Count,
|
||||
};
|
||||
struct BuiltinProc {
|
||||
@@ -178,6 +182,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
||||
{STR_LIT("ptr_offset"), 2, false, Expr_Expr},
|
||||
{STR_LIT("ptr_sub"), 2, false, Expr_Expr},
|
||||
{STR_LIT("slice_ptr"), 2, true, Expr_Expr},
|
||||
|
||||
{STR_LIT("min"), 2, false, Expr_Expr},
|
||||
{STR_LIT("max"), 2, false, Expr_Expr},
|
||||
{STR_LIT("abs"), 1, false, Expr_Expr},
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
|
||||
@@ -878,10 +878,11 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac
|
||||
} else if (is_type_string(type)) {
|
||||
return in_value.kind == ExactValue_String;
|
||||
} else if (is_type_integer(type)) {
|
||||
if (in_value.kind != ExactValue_Integer)
|
||||
ExactValue v = exact_value_to_integer(in_value);
|
||||
if (v.kind != ExactValue_Integer)
|
||||
return false;
|
||||
if (out_value) *out_value = in_value;
|
||||
i64 i = in_value.value_integer;
|
||||
if (out_value) *out_value = v;
|
||||
i64 i = v.value_integer;
|
||||
i64 s = 8*type_size_of(c->sizes, c->allocator, type);
|
||||
u64 umax = ~0ull;
|
||||
if (s < 64) {
|
||||
@@ -1282,7 +1283,6 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
|
||||
|
||||
Type *base_type = get_base_type(type);
|
||||
if (is_const_expr && is_type_constant_type(base_type)) {
|
||||
|
||||
if (base_type->kind == Type_Basic) {
|
||||
if (check_value_is_expressible(c, x->value, base_type, &x->value)) {
|
||||
can_convert = true;
|
||||
@@ -1295,10 +1295,12 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
|
||||
|
||||
if (!can_convert) {
|
||||
gbString expr_str = expr_to_string(x->expr);
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString to_type = type_to_string(type);
|
||||
gbString from_type = type_to_string(x->type);
|
||||
defer (gb_string_free(expr_str));
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s`", expr_str, type_str);
|
||||
defer (gb_string_free(to_type));
|
||||
defer (gb_string_free(from_type));
|
||||
error(&c->error_collector, ast_node_token(x->expr), "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type);
|
||||
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
@@ -2342,6 +2344,157 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
operand->mode = Addressing_Value;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_min: {
|
||||
// min :: proc(a, b: comparable) -> comparable
|
||||
Type *type = get_base_type(operand->type);
|
||||
if (!is_type_comparable(type) || !is_type_numeric(type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Expected a comparable numeric type to `min`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *other_arg = ce->arg_list->next;
|
||||
Operand a = *operand;
|
||||
Operand b = {};
|
||||
check_expr(c, &b, other_arg);
|
||||
if (b.mode == Addressing_Invalid)
|
||||
return false;
|
||||
if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
|
||||
gbString type_str = type_to_string(b.type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Expected a comparable numeric type to `min`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (a.mode == Addressing_Constant &&
|
||||
b.mode == Addressing_Constant) {
|
||||
ExactValue x = a.value;
|
||||
ExactValue y = b.value;
|
||||
Token lt = {Token_Lt};
|
||||
|
||||
operand->mode = Addressing_Constant;
|
||||
if (compare_exact_values(lt, x, y)) {
|
||||
operand->value = x;
|
||||
operand->type = a.type;
|
||||
} else {
|
||||
operand->value = y;
|
||||
operand->type = b.type;
|
||||
}
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = type;
|
||||
|
||||
if (!are_types_identical(operand->type, b.type)) {
|
||||
gbString type_a = type_to_string(a.type);
|
||||
gbString type_b = type_to_string(b.type);
|
||||
defer (gb_string_free(type_a));
|
||||
defer (gb_string_free(type_b));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Mismatched types to `min`, `%s` vs `%s`",
|
||||
type_a, type_b);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case BuiltinProc_max: {
|
||||
// min :: proc(a, b: comparable) -> comparable
|
||||
Type *type = get_base_type(operand->type);
|
||||
if (!is_type_comparable(type) || !is_type_numeric(type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Expected a comparable numeric type to `max`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *other_arg = ce->arg_list->next;
|
||||
Operand a = *operand;
|
||||
Operand b = {};
|
||||
check_expr(c, &b, other_arg);
|
||||
if (b.mode == Addressing_Invalid)
|
||||
return false;
|
||||
if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
|
||||
gbString type_str = type_to_string(b.type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Expected a comparable numeric type to `max`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (a.mode == Addressing_Constant &&
|
||||
b.mode == Addressing_Constant) {
|
||||
ExactValue x = a.value;
|
||||
ExactValue y = b.value;
|
||||
Token gt = {Token_Gt};
|
||||
|
||||
operand->mode = Addressing_Constant;
|
||||
if (compare_exact_values(gt, x, y)) {
|
||||
operand->value = x;
|
||||
operand->type = a.type;
|
||||
} else {
|
||||
operand->value = y;
|
||||
operand->type = b.type;
|
||||
}
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = type;
|
||||
|
||||
if (!are_types_identical(operand->type, b.type)) {
|
||||
gbString type_a = type_to_string(a.type);
|
||||
gbString type_b = type_to_string(b.type);
|
||||
defer (gb_string_free(type_a));
|
||||
defer (gb_string_free(type_b));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Mismatched types to `max`, `%s` vs `%s`",
|
||||
type_a, type_b);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case BuiltinProc_abs: {
|
||||
// abs :: proc(n: numeric) -> numeric
|
||||
Type *type = get_base_type(operand->type);
|
||||
if (!is_type_numeric(type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Expected a numeric type to `abs`, got `%s`",
|
||||
type_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_Constant) {
|
||||
switch (operand->value.kind) {
|
||||
case ExactValue_Integer:
|
||||
operand->value.value_integer = gb_abs(operand->value.value_integer);
|
||||
break;
|
||||
case ExactValue_Float:
|
||||
operand->value.value_float = gb_abs(operand->value.value_float);
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Invalid numeric constant");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
operand->type = type;
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1569,7 +1569,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
case_ast_node(i, Ident, expr);
|
||||
Entity *e = *map_get(&proc->module->info->uses, hash_pointer(expr));
|
||||
if (e->kind == Entity_Builtin) {
|
||||
GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin");
|
||||
GB_PANIC("TODO(bill): ssa_build_single_expr Entity_Builtin `%.*s`", LIT(builtin_procs[e->Builtin.id].name));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2068,6 +2068,36 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int), cap);
|
||||
return ssa_emit_load(proc, slice);
|
||||
} break;
|
||||
|
||||
case BuiltinProc_min: {
|
||||
ssaValue *x = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
|
||||
Type *t = get_base_type(ssa_type(x));
|
||||
Token lt = {Token_Lt};
|
||||
ssaValue *cond = ssa_emit_comp(proc, lt, x, y);
|
||||
return ssa_emit_select(proc, cond, x, y);
|
||||
} break;
|
||||
|
||||
case BuiltinProc_max: {
|
||||
ssaValue *x = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
|
||||
Type *t = get_base_type(ssa_type(x));
|
||||
Token gt = {Token_Gt};
|
||||
ssaValue *cond = ssa_emit_comp(proc, gt, x, y);
|
||||
return ssa_emit_select(proc, cond, x, y);
|
||||
} break;
|
||||
|
||||
case BuiltinProc_abs: {
|
||||
Token lt = {Token_Lt};
|
||||
Token sub = {Token_Sub};
|
||||
|
||||
ssaValue *x = ssa_build_expr(proc, ce->arg_list);
|
||||
Type *t = ssa_type(x);
|
||||
|
||||
ssaValue *neg_x = ssa_emit_arith(proc, sub, v_zero, x, t);
|
||||
ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero);
|
||||
return ssa_emit_select(proc, cond, neg_x, x);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -702,6 +702,10 @@ extern "C++" {
|
||||
#define gb_is_between(x, lower, upper) (((x) >= (lower)) && ((x) <= (upper)))
|
||||
#endif
|
||||
|
||||
#ifndef gb_abs
|
||||
#define gb_abs(x) ((x) < 0 ? -(x) : (x))
|
||||
#endif
|
||||
|
||||
/* NOTE(bill): Very useful bit setting */
|
||||
#ifndef GB_MASK_SET
|
||||
#define GB_MASK_SET(var, set, mask) do { \
|
||||
|
||||
@@ -164,6 +164,20 @@ AST_NODE_KIND(_ComplexStmtBegin, "", struct{}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
AstNode *operand; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmStmt, "assembly statement", struct { \
|
||||
Token token; \
|
||||
b32 is_volatile; \
|
||||
Token open, close; \
|
||||
Token code_string; \
|
||||
AstNode *output_list; \
|
||||
AstNode *input_list; \
|
||||
AstNode *clobber_list; \
|
||||
isize output_count, input_count, clobber_count; \
|
||||
}) \
|
||||
\
|
||||
AST_NODE_KIND(_ComplexStmtEnd, "", struct{}) \
|
||||
AST_NODE_KIND(_StmtEnd, "", struct{}) \
|
||||
@@ -348,6 +362,8 @@ Token ast_node_token(AstNode *node) {
|
||||
return node->BranchStmt.token;
|
||||
case AstNode_UsingStmt:
|
||||
return node->UsingStmt.token;
|
||||
case AstNode_AsmStmt:
|
||||
return node->AsmStmt.token;
|
||||
case AstNode_BadDecl:
|
||||
return node->BadDecl.begin;
|
||||
case AstNode_VarDecl:
|
||||
@@ -684,6 +700,35 @@ gb_inline AstNode *make_using_stmt(AstFile *f, Token token, AstNode *node) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_asm_operand(AstFile *f, Token string, AstNode *operand) {
|
||||
AstNode *result = make_node(f, AstNode_AsmOperand);
|
||||
result->AsmOperand.string = string;
|
||||
result->AsmOperand.operand = operand;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_asm_stmt(AstFile *f, Token token, b32 is_volatile, Token open, Token close, Token code_string,
|
||||
AstNode *output_list, AstNode *input_list, AstNode *clobber_list,
|
||||
isize output_count, isize input_count, isize clobber_count) {
|
||||
AstNode *result = make_node(f, AstNode_AsmStmt);
|
||||
result->AsmStmt.token = token;
|
||||
result->AsmStmt.is_volatile = is_volatile;
|
||||
result->AsmStmt.open = open;
|
||||
result->AsmStmt.close = close;
|
||||
result->AsmStmt.code_string = code_string;
|
||||
result->AsmStmt.output_list = output_list;
|
||||
result->AsmStmt.input_list = input_list;
|
||||
result->AsmStmt.clobber_list = clobber_list;
|
||||
result->AsmStmt.output_count = output_count;
|
||||
result->AsmStmt.input_count = input_count;
|
||||
result->AsmStmt.clobber_count = clobber_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
|
||||
AstNode *result = make_node(f, AstNode_BadDecl);
|
||||
@@ -2219,6 +2264,37 @@ AstNode *parse_defer_stmt(AstFile *f) {
|
||||
return make_defer_stmt(f, token, statement);
|
||||
}
|
||||
|
||||
AstNode *parse_asm_stmt(AstFile *f) {
|
||||
Token token = expect_token(f, Token_asm);
|
||||
b32 is_volatile = false;
|
||||
if (allow_token(f, Token_volatile)) {
|
||||
is_volatile = true;
|
||||
}
|
||||
Token open, close, code_string;
|
||||
open = expect_token(f, Token_OpenBrace);
|
||||
code_string = expect_token(f, Token_String);
|
||||
AstNode *output_list = NULL;
|
||||
AstNode *input_list = NULL;
|
||||
AstNode *clobber_list = NULL;
|
||||
isize output_count = 0;
|
||||
isize input_count = 0;
|
||||
isize clobber_count = 0;
|
||||
|
||||
// TODO(bill): Finish asm statement and determine syntax
|
||||
|
||||
// if (f->cursor[0].kind != Token_CloseBrace) {
|
||||
// expect_token(f, Token_Colon);
|
||||
// }
|
||||
|
||||
close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_asm_stmt(f, token, is_volatile, open, close, code_string,
|
||||
output_list, input_list, clobber_list,
|
||||
output_count, input_count, clobber_count);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
AstNode *parse_stmt(AstFile *f) {
|
||||
AstNode *s = NULL;
|
||||
@@ -2246,6 +2322,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
case Token_for: return parse_for_stmt(f);
|
||||
case Token_match: return parse_match_stmt(f);
|
||||
case Token_defer: return parse_defer_stmt(f);
|
||||
case Token_asm: return parse_asm_stmt(f);
|
||||
|
||||
case Token_break:
|
||||
case Token_continue:
|
||||
@@ -2255,6 +2332,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
expect_semicolon_after_stmt(f, s);
|
||||
return s;
|
||||
|
||||
|
||||
case Token_using: {
|
||||
AstNode *node = NULL;
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
|
||||
TOKEN_KIND(Token_Period, "."), \
|
||||
TOKEN_KIND(Token_Comma, ","), \
|
||||
TOKEN_KIND(Token_Ellipsis, ".."), \
|
||||
TOKEN_KIND(Token_RangeExclusive, "..<"), \
|
||||
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
@@ -93,6 +94,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_if, "if"), \
|
||||
TOKEN_KIND(Token_else, "else"), \
|
||||
TOKEN_KIND(Token_for, "for"), \
|
||||
TOKEN_KIND(Token_range, "range"), \
|
||||
TOKEN_KIND(Token_defer, "defer"), \
|
||||
TOKEN_KIND(Token_return, "return"), \
|
||||
TOKEN_KIND(Token_struct, "struct"), \
|
||||
@@ -100,6 +102,8 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_raw_union, "raw_union"), \
|
||||
TOKEN_KIND(Token_enum, "enum"), \
|
||||
TOKEN_KIND(Token_using, "using"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_volatile, "volatile"), \
|
||||
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
|
||||
TOKEN_KIND(Token_Count, "")
|
||||
|
||||
@@ -711,6 +715,10 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
} else if (t->curr_rune == '.') { // Could be an ellipsis
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Ellipsis;
|
||||
if (t->curr_rune == '<') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_RangeExclusive;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user