This commit is contained in:
jockus
2021-05-27 12:01:28 +01:00
66 changed files with 5644 additions and 2820 deletions
@@ -7,6 +7,8 @@ assignees: ''
---
# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS"
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016-2020 Ginger Bill. All rights reserved.
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+21 -16
View File
@@ -8,26 +8,31 @@ CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
+8
View File
@@ -526,6 +526,14 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
return;
}
remove :: proc(s, key: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
return replace(s, key, {}, n, allocator);
}
remove_all :: proc(s, key: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
return remove(s, key, -1, allocator);
}
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+3 -3
View File
@@ -641,9 +641,9 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
return;
}
pad_byte: byte = '0';
if fi.space {
pad_byte = ' ';
pad_byte: byte = ' ';
if !fi.space {
pad_byte = '0';
}
for i := 0; i < width; i += 1 {
+8
View File
@@ -31,6 +31,13 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
mem_copy :: proc(dst, src: rawptr, len: int) ---
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
mem_zero :: proc(ptr: rawptr, len: int) ---
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
@@ -159,6 +166,7 @@ type_is_simd_vector :: proc($T: typeid) -> bool ---
type_has_nil :: proc($T: typeid) -> bool ---
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
type_has_field :: proc($T: typeid, $name: string) -> bool ---
+2 -2
View File
@@ -6,9 +6,9 @@ Rand :: struct {
}
@(private, static)
@(private)
_GLOBAL_SEED_DATA := 1234567890;
@(private, static)
@(private)
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
set_global_seed :: proc(seed: u64) {
+7 -11
View File
@@ -16,16 +16,12 @@ Proc_Inlining :: enum u32 {
No_Inline = 2,
}
Proc_Calling_Convention :: enum i32 {
Invalid = 0,
Odin,
Contextless,
C_Decl,
Std_Call,
Fast_Call,
None,
Foreign_Block_Default = -1,
Proc_Calling_Convention_Extra :: enum i32 {
Foreign_Block_Default,
}
Proc_Calling_Convention :: union {
string,
Proc_Calling_Convention_Extra,
}
Node_State_Flag :: enum {
@@ -69,7 +65,7 @@ File :: struct {
pkg: ^Package,
fullpath: string,
src: []byte,
src: string,
docs: ^Comment_Group,
+41
View File
@@ -0,0 +1,41 @@
package odin_format
import "core:odin/printer"
import "core:odin/parser"
import "core:odin/ast"
default_style := printer.default_style;
simplify :: proc(file: ^ast.File) {
}
format :: proc(filepath: string, source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) {
config := config;
pkg := ast.Package {
kind = .Normal,
};
file := ast.File {
pkg = &pkg,
src = source,
fullpath = filepath,
};
config.newline_limit = clamp(config.newline_limit, 0, 16);
config.spaces = clamp(config.spaces, 1, 16);
config.align_length_break = clamp(config.align_length_break, 0, 64);
p := parser.default_parser(parser_flags);
ok := parser.parse_file(&p, &file);
if !ok || file.syntax_error_count > 0 {
return {}, false;
}
prnt := printer.make_printer(config, allocator);
return printer.print(&prnt, &file), true;
}
+1 -1
View File
@@ -39,7 +39,7 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
}
file := ast.new(ast.File, NO_POS, NO_POS);
file.pkg = pkg;
file.src = src;
file.src = string(src);
file.fullpath = fullpath;
pkg.files[fullpath] = file;
}
+79 -50
View File
@@ -8,10 +8,21 @@ import "core:fmt"
Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
Flag :: enum u32 {
Optional_Semicolons,
}
Flags :: distinct bit_set[Flag; u32];
Parser :: struct {
file: ^ast.File,
tok: tokenizer.Tokenizer,
// If .Optional_Semicolons is true, semicolons are completely as statement terminators
// different to .Insert_Semicolon in tok.flags
flags: Flags,
warn: Warning_Handler,
err: Error_Handler,
@@ -100,8 +111,9 @@ end_pos :: proc(tok: tokenizer.Token) -> tokenizer.Pos {
return pos;
}
default_parser :: proc() -> Parser {
default_parser :: proc(flags := Flags{}) -> Parser {
return Parser {
flags = flags,
err = default_error_handler,
warn = default_warning_handler,
};
@@ -128,6 +140,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
p.line_comment = nil;
}
if .Optional_Semicolons in p.flags {
p.tok.flags += {.Insert_Semicolon};
}
p.file = file;
tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
if p.tok.ch <= 0 {
@@ -400,6 +416,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if node == nil {
return false;
}
if .Optional_Semicolons in p.flags {
return true;
}
switch n in node.derived {
case ast.Empty_Stmt, ast.Block_Stmt:
return true;
@@ -439,14 +460,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return false;
}
expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) {
if .Optional_Semicolons not_in p.flags && .Insert_Semicolon in p.tok.flags && token.text == "\n" {
#partial switch token.kind {
case .Close_Brace:
case .Close_Paren:
case .Else:
return;
}
if is_semicolon_optional_for_node(p, s) {
return;
}
tok := token;
tok.pos.column -= 1;
error(p, tok.pos, "expected ';', got newline");
}
}
expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if allow_token(p, .Semicolon) {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true;
}
prev := p.prev_tok;
if prev.kind == .Semicolon {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true;
}
@@ -615,7 +656,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
cond = parse_expr(p, false);
} else {
init = parse_simple_stmt(p, nil);
if allow_token(p, .Semicolon) {
if parse_control_statement_semicolon_separator(p) {
cond = parse_expr(p, false);
} else {
cond = convert_stmt_to_expr(p, init, "boolean expression");
@@ -668,6 +709,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
return if_stmt;
}
parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool {
tok := peek_token(p);
if tok.kind != .Open_Brace {
return allow_token(p, .Semicolon);
}
if tok.text == ";" {
return allow_token(p, .Semicolon);
}
return false;
}
parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_proc == nil {
error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope");
@@ -716,7 +769,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
}
if !is_range && allow_token(p, .Semicolon) {
if !is_range && parse_control_statement_semicolon_separator(p) {
init = cond;
cond = nil;
if p.curr_tok.kind != .Semicolon {
@@ -820,7 +873,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
is_type_switch = true;
} else if allow_token(p, .Semicolon) {
} else if parse_control_statement_semicolon_separator(p) {
init = tag;
tag = nil;
if p.curr_tok.kind != .Open_Brace {
@@ -831,6 +884,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
skip_possible_newline(p);
open := expect_token(p, .Open_Brace);
for p.curr_tok.kind == .Case {
@@ -958,6 +1012,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
defer p.in_foreign_block = prev_in_foreign_block;
p.in_foreign_block = true;
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
decl := parse_foreign_block_decl(p);
@@ -1287,7 +1342,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
#partial switch kind {
case .Question, .If, .When:
return 1;
case .Ellipsis, .Range_Half:
case .Ellipsis, .Range_Half, .Range_Full:
if !p.allow_range {
return 0;
}
@@ -1884,24 +1939,12 @@ parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) {
string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention {
if s[0] != '"' && s[0] != '`' {
return .Invalid;
return nil;
}
switch s[1:len(s)-1] {
case "odin":
return .Odin;
case "contextless":
return .Contextless;
case "cdecl", "c":
return .C_Decl;
case "stdcall", "std":
return .Std_Call;
case "fast", "fastcall":
return .Fast_Call;
case "none":
return .None;
if len(s) == 2 {
return nil;
}
return .Invalid;
return s;
}
parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
@@ -1926,21 +1969,17 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
}
parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
cc := ast.Proc_Calling_Convention.Invalid;
cc: ast.Proc_Calling_Convention;
if p.curr_tok.kind == .String {
str := expect_token(p, .String);
cc = string_to_calling_convention(str.text);
if cc == ast.Proc_Calling_Convention.Invalid {
if cc == nil {
error(p, str.pos, "unknown calling convention '%s'", str.text);
}
}
if cc == ast.Proc_Calling_Convention.Invalid {
if p.in_foreign_block {
cc = ast.Proc_Calling_Convention.Foreign_Block_Default;
} else {
cc = ast.Proc_Calling_Convention.Odin;
}
if cc == nil && p.in_foreign_block {
cc = .Foreign_Block_Default;
}
expect_token(p, .Open_Paren);
@@ -1976,23 +2015,6 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
return pt;
}
check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: tokenizer.Token) {
if poly_params == nil {
return;
}
for field in poly_params.list {
for name in field.names {
if name == nil {
continue;
}
if _, ok := name.derived.(ast.Poly_Type); ok {
error(p, name.pos, "polymorphic names are not needed for %s parameters", tok.text);
return;
}
}
}
}
parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr {
expr := parse_unary_expr(p, lhs);
@@ -2224,6 +2246,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
p.expr_level = prev_level;
tags = parse_proc_tags(p);
}
if p.allow_type && p.expr_level < 0 {
if where_token.kind != .Invalid {
@@ -2233,6 +2256,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
body: ^ast.Stmt;
skip_possible_newline_for_literal(p);
if allow_token(p, .Undef) {
body = nil;
if where_token.kind != .Invalid {
@@ -2358,7 +2383,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
poly_params = nil;
}
expect_token_after(p, .Close_Paren, "parameter list");
check_poly_params_for_type(p, poly_params, tok);
}
prev_level := p.expr_level;
@@ -2405,6 +2429,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = where_prev_level;
}
skip_possible_newline_for_literal(p);
expect_token(p, .Open_Brace);
fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct);
close := expect_token(p, .Close_Brace);
@@ -2434,7 +2459,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
poly_params = nil;
}
expect_token_after(p, .Close_Paren, "parameter list");
check_poly_params_for_type(p, poly_params, tok);
}
prev_level := p.expr_level;
@@ -2473,6 +2497,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
variants: [dynamic]^ast.Expr;
skip_possible_newline_for_literal(p);
expect_token_after(p, .Open_Brace, "union");
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
@@ -2503,6 +2528,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
if p.curr_tok.kind != .Open_Brace {
base_type = parse_type(p);
}
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
fields := parse_elem_list(p);
close := expect_token(p, .Close_Brace);
@@ -2601,6 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
}
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
asm_string := parse_expr(p, false);
expect_token(p, .Comma);
@@ -2811,7 +2839,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
open := expect_token(p, .Open_Bracket);
#partial switch p.curr_tok.kind {
case .Colon, .Ellipsis, .Range_Half:
case .Colon, .Ellipsis, .Range_Half, .Range_Full:
// NOTE(bill): Do not err yet
break;
case:
@@ -2819,7 +2847,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
}
#partial switch p.curr_tok.kind {
case .Ellipsis, .Range_Half:
case .Ellipsis, .Range_Half, .Range_Full:
error(p, p.curr_tok.pos, "expected a colon, not a range");
fallthrough;
case .Colon:
@@ -3150,6 +3178,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
case ast.For_Stmt: n.label = label;
case ast.Switch_Stmt: n.label = label;
case ast.Type_Switch_Stmt: n.label = label;
case ast.Range_Stmt: n.label = label;
}
}
+924
View File
@@ -0,0 +1,924 @@
package odin_printer
import "core:odin/ast"
import "core:odin/tokenizer"
import "core:strings"
import "core:runtime"
import "core:fmt"
import "core:unicode/utf8"
import "core:mem"
Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit};
Line_Type :: bit_set[Type_Enum];
/*
Represents an unwrapped line
*/
Line :: struct {
format_tokens: [dynamic]Format_Token,
finalized: bool,
used: bool,
depth: int,
types: Line_Type, //for performance, so you don't have to verify what types are in it by going through the tokens - might give problems when adding linebreaking
}
/*
Represents a singular token in a unwrapped line
*/
Format_Token :: struct {
kind: tokenizer.Token_Kind,
text: string,
type: Type_Enum,
spaces_before: int,
parameter_count: int,
}
Printer :: struct {
string_builder: strings.Builder,
config: Config,
depth: int, //the identation depth
comments: [dynamic]^ast.Comment_Group,
latest_comment_index: int,
allocator: mem.Allocator,
file: ^ast.File,
source_position: tokenizer.Pos,
last_source_position: tokenizer.Pos,
lines: [dynamic]Line, //need to look into a better data structure, one that can handle inserting lines rather than appending
skip_semicolon: bool,
current_line: ^Line,
current_line_index: int,
last_line_index: int,
last_token: ^Format_Token,
merge_next_token: bool,
space_next_token: bool,
debug: bool,
}
Config :: struct {
spaces: int, //Spaces per indentation
newline_limit: int, //The limit of newlines between statements and declarations.
tabs: bool, //Enable or disable tabs
convert_do: bool, //Convert all do statements to brace blocks
semicolons: bool, //Enable semicolons
split_multiple_stmts: bool,
align_switch: bool,
brace_style: Brace_Style,
align_assignments: bool,
align_structs: bool,
align_style: Alignment_Style,
align_enums: bool,
align_length_break: int,
indent_cases: bool,
newline_style: Newline_Style,
}
Brace_Style :: enum {
_1TBS,
Allman,
Stroustrup,
K_And_R,
}
Block_Type :: enum {
None,
If_Stmt,
Proc,
Generic,
Comp_Lit,
Switch_Stmt,
}
Alignment_Style :: enum {
Align_On_Type_And_Equals,
Align_On_Colon_And_Equals,
}
Newline_Style :: enum {
CRLF,
LF,
}
default_style := Config {
spaces = 4,
newline_limit = 2,
convert_do = false,
semicolons = true,
tabs = true,
brace_style = ._1TBS,
split_multiple_stmts = true,
align_assignments = true,
align_style = .Align_On_Type_And_Equals,
indent_cases = false,
align_switch = true,
align_structs = true,
align_enums = true,
newline_style = .CRLF,
align_length_break = 9,
};
make_printer :: proc(config: Config, allocator := context.allocator) -> Printer {
return {
config = config,
allocator = allocator,
debug = false,
};
}
print :: proc(p: ^Printer, file: ^ast.File) -> string {
p.comments = file.comments;
if len(file.decls) > 0 {
p.lines = make([dynamic]Line, 0, (file.decls[len(file.decls) - 1].end.line - file.decls[0].pos.line) * 2, context.temp_allocator);
}
set_source_position(p, file.pkg_token.pos);
p.last_source_position.line = 1;
set_line(p, 0);
push_generic_token(p, .Package, 0);
push_ident_token(p, file.pkg_name, 1);
for decl in file.decls {
visit_decl(p, cast(^ast.Decl)decl);
}
if len(p.comments) > 0 {
infinite := p.comments[len(p.comments) - 1].end;
infinite.offset = 9999999;
push_comments(p, infinite);
}
fix_lines(p);
builder := strings.make_builder(0, mem.megabytes(5), p.allocator);
last_line := 0;
newline: string;
if p.config.newline_style == .LF {
newline = "\n";
} else {
newline = "\r\n";
}
for line, line_index in p.lines {
diff_line := line_index - last_line;
for i := 0; i < diff_line; i += 1 {
strings.write_string(&builder, newline);
}
if p.config.tabs {
for i := 0; i < line.depth; i += 1 {
strings.write_byte(&builder, '\t');
}
} else {
for i := 0; i < line.depth * p.config.spaces; i += 1 {
strings.write_byte(&builder, ' ');
}
}
if p.debug {
strings.write_string(&builder, fmt.tprintf("line %v: ", line_index));
}
for format_token in line.format_tokens {
for i := 0; i < format_token.spaces_before; i += 1 {
strings.write_byte(&builder, ' ');
}
strings.write_string(&builder, format_token.text);
}
last_line = line_index;
}
strings.write_string(&builder, newline);
return strings.to_string(builder);
}
fix_lines :: proc(p: ^Printer) {
align_var_decls(p);
format_generic(p);
align_comments(p); //align them last since they rely on the other alignments
}
format_value_decl :: proc(p: ^Printer, index: int) {
eq_found := false;
eq_token: Format_Token;
eq_line: int;
largest := 0;
found_eq: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if format_token.kind == .Eq {
eq_token = format_token;
eq_line = line_index + index;
eq_found = true;
break found_eq;
}
}
}
if !eq_found {
return;
}
align_next := false;
//check to see if there is a binary operator in the last token(this is guaranteed by the ast visit), otherwise it's not multilined
for line, line_index in p.lines[eq_line:] {
if len(line.format_tokens) == 0 {
break;
}
if align_next {
line.format_tokens[0].spaces_before = largest + 1;
align_next = false;
}
kind := find_last_token(line.format_tokens).kind;
if tokenizer.Token_Kind.B_Operator_Begin < kind && kind <= tokenizer.Token_Kind.Cmp_Or {
align_next = true;
}
if !align_next {
break;
}
}
}
find_last_token :: proc(format_tokens: [dynamic]Format_Token) -> Format_Token {
for i := len(format_tokens) - 1; i >= 0; i -= 1 {
if format_tokens[i].kind != .Comment {
return format_tokens[i];
}
}
panic("not possible");
}
format_assignment :: proc(p: ^Printer, index: int) {
}
format_call :: proc(p: ^Printer, line_index: int, format_index: int) {
paren_found := false;
paren_token: Format_Token;
paren_line: int;
paren_token_index: int;
largest := 0;
found_paren: for line, i in p.lines[line_index:] {
for format_token, j in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if i == 0 && j < format_index {
continue;
}
if format_token.kind == .Open_Paren && format_token.type == .Call {
paren_token = format_token;
paren_line = line_index + i;
paren_found = true;
paren_token_index = j;
break found_paren;
}
}
}
if !paren_found {
panic("Should not be possible");
}
paren_count := 1;
done := false;
for line, line_index in p.lines[paren_line:] {
if len(line.format_tokens) == 0 {
continue;
}
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
continue;
}
if line_index == 0 && i <= paren_token_index {
continue;
}
if format_token.kind == .Open_Paren {
paren_count += 1;
} else if format_token.kind == .Close_Paren {
paren_count -= 1;
}
if paren_count == 0 {
done = true;
}
}
if line_index != 0 {
line.format_tokens[0].spaces_before = largest;
}
if done {
return;
}
}
}
format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword: tokenizer.Token_Kind) {
keyword_found := false;
keyword_token: Format_Token;
keyword_line: int;
largest := 0;
brace_count := 0;
done := false;
found_keyword: for line, i in p.lines[line_index:] {
for format_token in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if format_token.kind == keyword {
keyword_token = format_token;
keyword_line = line_index + i;
keyword_found = true;
break found_keyword;
}
}
}
if !keyword_found {
panic("Should not be possible");
}
for line, line_index in p.lines[keyword_line:] {
if len(line.format_tokens) == 0 {
continue;
}
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
} else if format_token.kind == .Undef {
return;
}
if line_index == 0 && i <= format_index {
continue;
}
if format_token.kind == .Open_Brace {
brace_count += 1;
} else if format_token.kind == .Close_Brace {
brace_count -= 1;
}
if brace_count == 1 {
done = true;
}
}
if line_index != 0 {
line.format_tokens[0].spaces_before = largest + 1;
}
if done {
return;
}
}
}
format_generic :: proc(p: ^Printer) {
next_struct_line := 0;
for line, line_index in p.lines {
if len(line.format_tokens) <= 0 {
continue;
}
for format_token, token_index in line.format_tokens {
#partial switch format_token.kind {
case .For, .If, .When, .Switch:
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
case .Proc:
if format_token.type == .Proc_Lit {
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
}
case:
if format_token.type == .Call {
format_call(p, line_index, token_index);
}
}
}
if .Switch_Stmt in line.types && p.config.align_switch {
align_switch_stmt(p, line_index);
}
if .Enum in line.types && p.config.align_enums {
align_enum(p, line_index);
}
if .Struct in line.types && p.config.align_structs && next_struct_line <= 0 {
next_struct_line = align_struct(p, line_index);
}
if .Value_Decl in line.types {
format_value_decl(p, line_index);
}
if .Assign in line.types {
format_assignment(p, line_index);
}
next_struct_line -= 1;
}
}
align_var_decls :: proc(p: ^Printer) {
current_line: int;
current_typed: bool;
current_not_mutable: bool;
largest_lhs := 0;
largest_rhs := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
colon_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
type_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
equal_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
for line, line_index in p.lines {
//It is only possible to align value decls that are one one line, otherwise just ignore them
if .Value_Decl not_in line.types {
continue;
}
typed := true;
not_mutable := false;
continue_flag := false;
for i := 0; i < len(line.format_tokens); i += 1 {
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Eq {
typed = false;
}
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Colon {
not_mutable = true;
}
if line.format_tokens[i].kind == .Union ||
line.format_tokens[i].kind == .Enum ||
line.format_tokens[i].kind == .Struct ||
line.format_tokens[i].kind == .For ||
line.format_tokens[i].kind == .If ||
line.format_tokens[i].kind == .Comment {
continue_flag = true;
}
//enforced undef is always on the last line, if it exists
if line.format_tokens[i].kind == .Proc && line.format_tokens[len(line.format_tokens)-1].kind != .Undef {
continue_flag = true;
}
}
if continue_flag {
continue;
}
if line_index != current_line + 1 || typed != current_typed || not_mutable != current_not_mutable {
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
for colon_token in colon_tokens {
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
}
} else if p.config.align_style == .Align_On_Type_And_Equals {
for type_token in type_tokens {
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
}
}
if current_typed {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
}
} else {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = 0;
}
}
clear(&colon_tokens);
clear(&type_tokens);
clear(&equal_tokens);
largest_rhs = 0;
largest_lhs = 0;
current_typed = typed;
current_not_mutable = not_mutable;
}
current_line = line_index;
current_token_index := 0;
lhs_length := 0;
rhs_length := 0;
//calcuate the length of lhs of a value decl i.e. `a, b:`
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
lhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
if line.format_tokens[current_token_index].kind == .Colon {
append(&colon_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = lhs_length});
if len(line.format_tokens) > current_token_index && line.format_tokens[current_token_index + 1].kind != .Eq {
append(&type_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index + 1], length = lhs_length});
}
current_token_index += 1;
largest_lhs = max(largest_lhs, lhs_length);
break;
}
}
//calcuate the length of the rhs i.e. `[dynamic]int = 123123`
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
rhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
if line.format_tokens[current_token_index].kind == .Eq {
append(&equal_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = rhs_length});
largest_rhs = max(largest_rhs, rhs_length);
break;
}
}
}
//repeating myself, move to sub procedure
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
for colon_token in colon_tokens {
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
}
} else if p.config.align_style == .Align_On_Type_And_Equals {
for type_token in type_tokens {
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
}
}
if current_typed {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
}
} else {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = 0;
}
}
}
align_switch_stmt :: proc(p: ^Printer, index: int) {
switch_found := false;
brace_token: Format_Token;
brace_line: int;
found_switch_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && switch_found {
brace_token = format_token;
brace_line = line_index + index;
break found_switch_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Switch {
switch_found = true;
}
}
}
if !switch_found {
return;
}
largest := 0;
case_count := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
//find all the switch cases that are one lined
for line, line_index in p.lines[brace_line + 1:] {
case_found := false;
colon_found := false;
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
}
//this will only happen if the case is one lined
if case_found && colon_found {
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
largest = max(length, largest);
break;
}
if format_token.kind == .Case {
case_found = true;
case_count += 1;
} else if format_token.kind == .Colon {
colon_found = true;
}
length += len(format_token.text) + format_token.spaces_before;
}
if case_count >= brace_token.parameter_count {
break;
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
}
align_enum :: proc(p: ^Printer, index: int) {
enum_found := false;
brace_token: Format_Token;
brace_line: int;
found_enum_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && enum_found {
brace_token = format_token;
brace_line = line_index + index;
break found_enum_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Enum {
enum_found = true;
}
}
}
if !enum_found {
return;
}
largest := 0;
comma_count := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
for line, line_index in p.lines[brace_line + 1:] {
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
}
if format_token.kind == .Eq {
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
largest = max(length, largest);
break;
} else if format_token.kind == .Comma {
comma_count += 1;
}
length += len(format_token.text) + format_token.spaces_before;
}
if comma_count >= brace_token.parameter_count {
break;
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
}
align_struct :: proc(p: ^Printer, index: int) -> int {
struct_found := false;
brace_token: Format_Token;
brace_line: int;
found_struct_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && struct_found {
brace_token = format_token;
brace_line = line_index + index;
break found_struct_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Struct {
struct_found = true;
}
}
}
if !struct_found {
return 0;
}
largest := 0;
colon_count := 0;
nested := false;
seen_brace := false;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator);
if brace_token.parameter_count == 0 {
return 0;
}
end_line_index := 0;
for line, line_index in p.lines[brace_line + 1:] {
length := 0;
for format_token, i in line.format_tokens {
//give up on nested structs
if format_token.kind == .Comment {
break;
} else if format_token.kind == .Open_Paren {
break;
} else if format_token.kind == .Open_Brace {
seen_brace = true;
} else if format_token.kind == .Close_Brace {
seen_brace = false;
} else if seen_brace {
continue;
}
if format_token.kind == .Colon {
format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length};
if format_tokens[colon_count].format_token.kind == .Struct {
nested = true;
}
colon_count += 1;
largest = max(length, largest);
}
length += len(format_token.text) + format_token.spaces_before;
}
if nested {
end_line_index = line_index + brace_line + 1;
}
if colon_count >= brace_token.parameter_count {
break;
}
}
//give up aligning nested, it never looks good
if nested {
for line, line_index in p.lines[end_line_index:] {
for format_token in line.format_tokens {
if format_token.kind == .Close_Brace {
return end_line_index + line_index - index;
}
}
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
return 0;
}
align_comments :: proc(p: ^Printer) {
Comment_Align_Info :: struct {
length: int,
begin: int,
end: int,
depth: int,
};
comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator);
current_info: Comment_Align_Info;
for line, line_index in p.lines {
if len(line.format_tokens) <= 0 {
continue;
}
if .Line_Comment in line.types {
if current_info.end + 1 != line_index || current_info.depth != line.depth ||
(current_info.begin == current_info.end && current_info.length == 0) {
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
append(&comment_infos, current_info);
}
current_info.begin = line_index;
current_info.end = line_index;
current_info.depth = line.depth;
current_info.length = 0;
}
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
current_info.length = max(current_info.length, length);
current_info.end = line_index;
}
length += format_token.spaces_before + len(format_token.text);
}
}
}
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
append(&comment_infos, current_info);
}
for info in comment_infos {
if info.begin == info.end || info.length == 0 {
continue;
}
for i := info.begin; i <= info.end; i += 1 {
l := p.lines[i];
length := 0;
for format_token, i in l.format_tokens {
if format_token.kind == .Comment {
if len(l.format_tokens) == 1 {
l.format_tokens[i].spaces_before = info.length + 1;
} else {
l.format_tokens[i].spaces_before = info.length - length + 1;
}
}
length += format_token.spaces_before + len(format_token.text);
}
}
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -107,6 +107,7 @@ Token_Kind :: enum u32 {
Comma, // ,
Ellipsis, // ..
Range_Half, // ..<
Range_Full, // ..=
Back_Slash, // \
B_Operator_End,
@@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string {
",",
"..",
"..<",
"..=",
"\\",
"",
+7 -4
View File
@@ -14,7 +14,7 @@ Flags :: distinct bit_set[Flag; u32];
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
src: string,
err: Error_Handler,
flags: Flags,
@@ -31,7 +31,7 @@ Tokenizer :: struct {
error_count: int,
}
init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) {
init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) {
t.src = src;
t.err = err;
t.ch = ' ';
@@ -87,7 +87,7 @@ advance_rune :: proc(using t: ^Tokenizer) {
case r == 0:
error(t, t.offset, "illegal character NUL");
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(src[read_offset:]);
r, w = utf8.decode_rune_in_string(src[read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding");
} else if r == utf8.RUNE_BOM && offset > 0 {
@@ -608,7 +608,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
}
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = .Xor;
case '~': kind = switch2(t, .Xor, .Xor_Eq);
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
@@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
if t.ch == '<' {
advance_rune(t);
kind = .Range_Half;
} else if t.ch == '=' {
advance_rune(t);
kind = .Range_Full;
}
}
}
+2 -2
View File
@@ -273,7 +273,7 @@ is_file :: proc(path: string) -> bool {
attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY;
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0;
}
return false;
}
@@ -283,7 +283,7 @@ is_dir :: proc(path: string) -> bool {
attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY;
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0;
}
return false;
}
+18 -47
View File
@@ -1,11 +1,8 @@
package os2
Platform_Error_Min_Bits :: 32;
import "core:io"
Error :: enum u64 {
None = 0,
// General Errors
General_Error :: enum u32 {
Invalid_Argument,
Permission_Denied,
@@ -13,43 +10,20 @@ Error :: enum u64 {
Not_Exist,
Closed,
// Timeout Errors
Timeout,
// I/O Errors
// EOF is the error returned by `read` when no more input is available
EOF,
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
Unexpected_EOF,
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
Short_Write,
// Invalid_Write means that a write returned an impossible count
Invalid_Write,
// Short_Buffer means that a read required a longer buffer than was provided
Short_Buffer,
// No_Progress is returned by some implementations of `io.Reader` when many calls
// to `read` have failed to return any data or error.
// This is usually a signed of a broken `io.Reader` implementation
No_Progress,
Invalid_Whence,
Invalid_Offset,
Invalid_Unread,
Negative_Read,
Negative_Write,
Negative_Count,
Buffer_Full,
// Platform Specific Errors
Platform_Minimum = 1<<Platform_Error_Min_Bits,
}
Platform_Error :: struct {
err: i32,
}
Error :: union {
General_Error,
io.Error,
Platform_Error,
}
#assert(size_of(Error) == size_of(u64));
Path_Error :: struct {
op: string,
path: string,
@@ -83,20 +57,17 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
if ferr >= .Platform_Minimum {
err = i32(u64(ferr)>>Platform_Error_Min_Bits);
ok = true;
v: Platform_Error;
if v, ok = ferr.(Platform_Error); ok {
err = v.err;
}
return;
}
error_from_platform_error :: proc(errno: i32) -> Error {
return Error(u64(errno) << Platform_Error_Min_Bits);
}
error_string :: proc(ferr: Error) -> string {
#partial switch ferr {
case .None: return "";
switch ferr {
case nil: return "";
case .Invalid_Argument: return "invalid argument";
case .Permission_Denied: return "permission denied";
case .Exist: return "file already exists";
+7 -16
View File
@@ -10,23 +10,14 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
@(private)
error_to_io_error :: proc(ferr: Error) -> io.Error {
#partial switch ferr {
case .None: return .None;
case .EOF: return .EOF;
case .Unexpected_EOF: return .Unexpected_EOF;
case .Short_Write: return .Short_Write;
case .Invalid_Write: return .Invalid_Write;
case .Short_Buffer: return .Short_Buffer;
case .No_Progress: return .No_Progress;
case .Invalid_Whence: return .Invalid_Whence;
case .Invalid_Offset: return .Invalid_Offset;
case .Invalid_Unread: return .Invalid_Unread;
case .Negative_Read: return .Negative_Read;
case .Negative_Write: return .Negative_Write;
case .Negative_Count: return .Negative_Count;
case .Buffer_Full: return .Buffer_Full;
if ferr == nil {
return .None;
}
return .Unknown;
err, ok := ferr.(io.Error);
if !ok {
err = .Unknown;
}
return err;
}
+1
View File
@@ -1,6 +1,7 @@
package os2
import "core:mem"
import "core:io"
import "core:strconv"
import "core:unicode/utf8"
+10 -10
View File
@@ -5,19 +5,19 @@ import "core:io"
import "core:time"
_create :: proc(name: string) -> (Handle, Error) {
return 0, .None;
return 0, nil;
}
_open :: proc(name: string) -> (Handle, Error) {
return 0, .None;
return 0, nil;
}
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
return 0, .None;
return 0, nil;
}
_close :: proc(fd: Handle) -> Error {
return .None;
return nil;
}
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
@@ -58,11 +58,11 @@ _file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
_sync :: proc(fd: Handle) -> Error {
return .None;
return nil;
}
_flush :: proc(fd: Handle) -> Error {
return .None;
return nil;
}
_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
@@ -92,20 +92,20 @@ _read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
_chdir :: proc(fd: Handle) -> Error {
return .None;
return nil;
}
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
return .None;
return nil;
}
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
return .None;
return nil;
}
_lchown :: proc(name: string, uid, gid: int) -> Error {
return .None;
return nil;
}
+1 -1
View File
@@ -6,7 +6,7 @@ import win32 "core:sys/windows"
_pipe :: proc() -> (r, w: Handle, err: Error) {
p: [2]win32.HANDLE;
if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
return 0, 0, error_from_platform_error(i32(win32.GetLastError()));
return 0, 0, Platform_Error{i32(win32.GetLastError())};
}
return Handle(p[0]), Handle(p[1]), nil;
}
+3 -3
View File
@@ -40,7 +40,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
_stat_errno :: proc(errno: win32.DWORD) -> Path_Error {
return Path_Error{err = error_from_platform_error(i32(errno))};
return Path_Error{err = Platform_Error{i32(errno)}};
}
@@ -89,7 +89,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
fd: win32.WIN32_FIND_DATAW;
sh := win32.FindFirstFileW(wname, &fd);
if sh == win32.INVALID_HANDLE_VALUE {
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))};
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
return;
}
win32.FindClose(sh);
@@ -99,7 +99,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil);
if h == win32.INVALID_HANDLE_VALUE {
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))};
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
return;
}
defer win32.CloseHandle(h);
+2 -2
View File
@@ -4,11 +4,11 @@ package os2
import win32 "core:sys/windows"
_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
return 0, .None;
return 0, nil;
}
_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
return "", .None;
return "", nil;
}
_temp_dir :: proc(allocator := context.allocator) -> string {
+1 -1
View File
@@ -10,7 +10,7 @@ import "core:c"
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct i32;
Syscall :: distinct int;
Syscall :: distinct i32;
INVALID_HANDLE :: ~Handle(0);
+3 -3
View File
@@ -11,7 +11,7 @@ import "core:strconv"
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct i32;
Syscall :: distinct int;
Syscall :: distinct i32;
INVALID_HANDLE :: ~Handle(0);
@@ -269,7 +269,7 @@ SYS_GETTID: Syscall : 186;
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> i32 ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
@@ -595,7 +595,7 @@ exit :: proc "contextless" (code: int) -> ! {
}
current_thread_id :: proc "contextless" () -> int {
return syscall(SYS_GETTID);
return cast(int)syscall(SYS_GETTID);
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+41 -2
View File
@@ -1,6 +1,6 @@
package runtime
when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" || ODIN_OS == "js" {
// mem.nil_allocator reimplementation
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
@@ -15,7 +15,46 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
data = nil,
};
}
} else when ODIN_OS != "windows" {
} else when ODIN_OS == "windows" {
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
switch mode {
case .Alloc:
return _windows_default_alloc(size, alignment);
case .Free:
_windows_default_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
case .Resize:
return _windows_default_resize(old_memory, old_size, size, alignment);
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return nil, nil;
case .Query_Info:
return nil, nil;
}
return nil, nil;
}
default_allocator :: proc() -> Allocator {
return Allocator{
procedure = default_allocator_proc,
data = nil,
};
}
} else {
// TODO(bill): reimplement these procedures in the os_specific stuff
import "core:os"
+12 -33
View File
@@ -97,7 +97,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
if len < 0 {
return data;
}
memset(data, 0, len);
intrinsics.mem_zero(data, len);
return data;
}
@@ -105,17 +105,9 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil {
return dst;
}
// NOTE(bill): This _must_ be implemented like C's memmove
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
}
llvm_memmove(dst, src, len);
intrinsics.mem_copy(dst, src, len);
return dst;
}
@@ -123,17 +115,9 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
if src == nil {
return dst;
}
// NOTE(bill): This _must_ be implemented like C's memcpy
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
}
llvm_memcpy(dst, src, len);
intrinsics.mem_copy_non_overlapping(dst, src, len);
return dst;
}
@@ -409,11 +393,6 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
@(default_calling_convention = "none")
foreign {
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
}
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
return -x if x < 0 else x;
}
@@ -445,27 +424,27 @@ max_f64 :: proc(a, b: f64) -> f64 {
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
r, i := real(x), imag(x);
return f16(_sqrt_f32(f32(r*r + i*i)));
return f16(intrinsics.sqrt(f32(r*r + i*i)));
}
abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 {
r, i := real(x), imag(x);
return _sqrt_f32(r*r + i*i);
return intrinsics.sqrt(r*r + i*i);
}
abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 {
r, i := real(x), imag(x);
return _sqrt_f64(r*r + i*i);
return intrinsics.sqrt(r*r + i*i);
}
abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return f16(_sqrt_f32(f32(r*r + i*i + j*j + k*k)));
return f16(intrinsics.sqrt(f32(r*r + i*i + j*j + k*k)));
}
abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f32(r*r + i*i + j*j + k*k);
return intrinsics.sqrt(r*r + i*i + j*j + k*k);
}
abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f64(r*r + i*i + j*j + k*k);
return intrinsics.sqrt(r*r + i*i + j*j + k*k);
}
@@ -644,7 +623,7 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
}
if (e > 30) {
f := 1e12;
f := i64(1e12);
for j := 0; j < 10; j += 1 {
/* NOTE(bill): Cause overflow */
g := intrinsics.volatile_load(&f);
+8
View File
@@ -1,3 +1,11 @@
package runtime
_OS_Errno :: distinct int;
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
return _os_write(data);
}
current_thread_id :: proc "contextless" () -> int {
return _current_thread_id();
}
+2 -2
View File
@@ -6,12 +6,12 @@ import "core:os"
// TODO(bill): reimplement `os.write` so that it does not rely on package os
// NOTE: Use os_specific_linux.odin, os_specific_darwin.odin, etc
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
context = default_context();
n, err := os.write(os.stderr, data);
return int(n), _OS_Errno(err);
}
current_thread_id :: proc "contextless" () -> int {
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id();
}
+2 -2
View File
@@ -2,10 +2,10 @@
package runtime
// TODO(bill): reimplement `os.write`
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
return 0, -1;
}
current_thread_id :: proc "contextless" () -> int {
_current_thread_id :: proc "contextless" () -> int {
return 0;
}
+48 -78
View File
@@ -1,3 +1,4 @@
//+private
//+build windows
package runtime
@@ -24,7 +25,7 @@ foreign kernel32 {
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
}
os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
_os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
if len(data) == 0 {
return 0, 0;
}
@@ -58,7 +59,7 @@ os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
return;
}
current_thread_id :: proc "contextless" () -> int {
_current_thread_id :: proc "contextless" () -> int {
return int(GetCurrentThreadId());
}
@@ -86,89 +87,58 @@ heap_free :: proc "contextless" (ptr: rawptr) {
HeapFree(GetProcessHeap(), 0, ptr);
}
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
//
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
// padding. We also store the original pointer returned by heap_alloc right before
// the pointer we return to the user.
//
//
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
// padding. We also store the original pointer returned by heap_alloc right before
// the pointer we return to the user.
//
aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
a := max(alignment, align_of(rawptr));
space := size + a - 1;
allocated_mem: rawptr;
if old_ptr != nil {
original_old_ptr := ptr_offset((^rawptr)(old_ptr), -1)^;
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
} else {
allocated_mem = heap_alloc(space+size_of(rawptr));
}
aligned_mem := rawptr(ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
ptr := uintptr(aligned_mem);
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
diff := int(aligned_ptr - ptr);
if (size + diff) > space {
return nil, .Out_Of_Memory;
}
aligned_mem = rawptr(aligned_ptr);
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return byte_slice(aligned_mem, size), nil;
}
aligned_free :: proc "contextless" (p: rawptr) {
if p != nil {
heap_free(ptr_offset((^rawptr)(p), -1)^);
}
}
aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
if p == nil {
return nil, nil;
}
return aligned_alloc(new_size, new_alignment, p);
}
switch mode {
case .Alloc:
return aligned_alloc(size, alignment);
case .Free:
aligned_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
case .Resize:
if old_memory == nil {
return aligned_alloc(size, alignment);
}
return aligned_resize(old_memory, old_size, size, alignment);
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return nil, nil;
case .Query_Info:
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
if size == 0 {
_windows_default_free(old_ptr);
return nil, nil;
}
return nil, nil;
a := max(alignment, align_of(rawptr));
space := size + a - 1;
allocated_mem: rawptr;
if old_ptr != nil {
original_old_ptr := ptr_offset((^rawptr)(old_ptr), -1)^;
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
} else {
allocated_mem = heap_alloc(space+size_of(rawptr));
}
aligned_mem := rawptr(ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
ptr := uintptr(aligned_mem);
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
diff := int(aligned_ptr - ptr);
if (size + diff) > space {
return nil, .Out_Of_Memory;
}
aligned_mem = rawptr(aligned_ptr);
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return byte_slice(aligned_mem, size), nil;
}
default_allocator :: proc() -> Allocator {
return Allocator{
procedure = default_allocator_proc,
data = nil,
};
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(size, alignment, nil);
}
_windows_default_free :: proc "contextless" (ptr: rawptr) {
if ptr != nil {
heap_free(ptr_offset((^rawptr)(ptr), -1)^);
}
}
_windows_default_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(new_size, new_alignment, p);
}
+9 -5
View File
@@ -1,18 +1,22 @@
package runtime
import "core:sys/es"
@(link_name="memset")
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
return es.CRTmemset(ptr, val, len);
addr := 0x1000 + 196 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, i32, int) -> rawptr)(fp))(ptr, val, len);
}
@(link_name="memmove")
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
return es.CRTmemmove(dst, src, len);
addr := 0x1000 + 195 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
}
@(link_name="memcpy")
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
return es.CRTmemcpy(dst, src, len);
addr := 0x1000 + 194 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
}
+1 -1
View File
@@ -221,7 +221,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
}
@(private, static)
@(private)
DIGITS_LOWER := "0123456789abcdefx";
write_quoted_string :: proc{
+8
View File
@@ -541,6 +541,14 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
return;
}
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, key, "", n, allocator);
}
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return remove(s, key, -1, allocator);
}
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+2
View File
@@ -56,6 +56,7 @@ atomic_exchange_release :: intrinsics.atomic_xchg_rel;
atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel;
atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed;
// Returns value and optional ok boolean
atomic_compare_exchange_strong :: intrinsics.atomic_cxchg;
atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq;
atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel;
@@ -66,6 +67,7 @@ atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_fa
atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed;
atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed;
// Returns value and optional ok boolean
atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak;
atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq;
atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel;
-886
View File
@@ -1,886 +0,0 @@
package sync2
// TODO(bill): The Channel implementation needs a complete rewrite for this new package sync design
// Especially how the `select` things work
import "core:mem"
import "core:time"
import "core:math/rand"
_, _ :: time, rand;
Channel_Direction :: enum i8 {
Both = 0,
Send = +1,
Recv = -1,
}
Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
using _internal: ^Raw_Channel,
}
channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_destroy :: proc(ch: $C/Channel($T, $D)) {
raw_channel_destroy(ch._internal);
}
channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
res._internal = ch._internal;
return;
}
channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
res._internal = ch._internal;
return;
}
channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.len if ch._internal != nil else 0;
}
channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.cap if ch._internal != nil else 0;
}
channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
msg := msg;
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
}
channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
msg := msg;
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
}
channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
c := ch._internal;
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc);
}
mutex_lock(&c.mutex);
raw_channel_recv_impl(c, &msg, loc);
mutex_unlock(&c.mutex);
return;
}
channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c != nil && mutex_try_lock(&c.mutex) {
if c.len > 0 {
raw_channel_recv_impl(c, &msg, loc);
ok = true;
}
mutex_unlock(&c.mutex);
}
return;
}
channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
res: T;
res, ok = channel_try_recv(ch, loc);
if ok && msg != nil {
msg^ = res;
}
return;
}
channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
return ch._internal == nil;
}
channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
c := ch._internal;
return c != nil && !c.closed;
}
channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal == b._internal;
}
channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal != b._internal;
}
channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
return raw_channel_can_send(ch._internal);
}
channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
return raw_channel_can_recv(ch._internal);
}
channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
c := ch._internal;
if c == nil {
return -1;
}
if atomic_load(&c.closed) {
return -1;
}
return atomic_load(&c.len);
}
channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
raw_channel_close(ch._internal, loc);
}
channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c == nil {
return;
}
if !c.closed || c.len > 0 {
msg, ok = channel_recv(ch), true;
}
return;
}
channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
raw_channel_drain(ch._internal);
}
channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
for msg in channel_iterator(src) {
channel_send(dst, msg);
}
}
Raw_Channel_Wait_Queue :: struct {
next: ^Raw_Channel_Wait_Queue,
state: ^uintptr,
}
Raw_Channel :: struct {
closed: bool,
ready: bool, // ready to recv
data_offset: u16, // data is stored at the end of this data structure
elem_size: u32,
len, cap: int,
read, write: int,
mutex: Mutex,
cond: Cond,
allocator: mem.Allocator,
sendq: ^Raw_Channel_Wait_Queue,
recvq: ^Raw_Channel_Wait_Queue,
}
raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
val.next = head^;
head^ = val;
}
raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
p := head;
for p^ != nil && p^ != val {
p = &p^.next;
}
if p != nil {
p^ = p^.next;
}
}
raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
assert(int(u32(elem_size)) == elem_size);
s := size_of(Raw_Channel);
s = mem.align_forward_int(s, elem_align);
data_offset := uintptr(s);
s += elem_size * max(cap, 1);
a := max(elem_align, align_of(Raw_Channel));
c := (^Raw_Channel)(mem.alloc(s, a));
if c == nil {
return nil;
}
c.data_offset = u16(data_offset);
c.elem_size = u32(elem_size);
c.len, c.cap = 0, max(cap, 0);
c.read, c.write = 0, 0;
c.allocator = context.allocator;
c.closed = false;
return c;
}
raw_channel_destroy :: proc(c: ^Raw_Channel) {
if c == nil {
return;
}
context.allocator = c.allocator;
atomic_store(&c.closed, true);
free(c);
}
raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
if c == nil {
panic(message="cannot close nil channel", loc=loc);
}
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
atomic_store(&c.closed, true);
// Release readers and writers
raw_channel_wait_queue_broadcast(c.recvq);
raw_channel_wait_queue_broadcast(c.sendq);
cond_broadcast(&c.cond);
}
raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
send :: proc(c: ^Raw_Channel, src: rawptr) {
data := uintptr(c) + uintptr(c.data_offset);
dst := data + uintptr(c.write * int(c.elem_size));
mem.copy(rawptr(dst), src, int(c.elem_size));
c.len += 1;
c.write = (c.write + 1) % max(c.cap, 1);
}
switch {
case c == nil:
panic(message="cannot send message; channel is nil", loc=loc);
case c.closed:
panic(message="cannot send message; channel is closed", loc=loc);
}
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
if c.cap > 0 {
if !block && c.len >= c.cap {
return false;
}
for c.len >= c.cap {
cond_wait(&c.cond, &c.mutex);
}
} else if c.len > 0 { // TODO(bill): determine correct behaviour
if !block {
return false;
}
cond_wait(&c.cond, &c.mutex);
} else if c.len == 0 && !block {
return false;
}
send(c, msg);
cond_signal(&c.cond);
raw_channel_wait_queue_signal(c.recvq);
return true;
}
raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
if c.len < 1 {
panic(message="cannot recv message; channel is empty", loc=loc);
}
c.len -= 1;
data := uintptr(c) + uintptr(c.data_offset);
src := data + uintptr(c.read * int(c.elem_size));
mem.copy(dst, rawptr(src), int(c.elem_size));
c.read = (c.read + 1) % max(c.cap, 1);
}
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc);
}
atomic_store(&c.ready, true);
for c.len < 1 {
raw_channel_wait_queue_signal(c.sendq);
cond_wait(&c.cond, &c.mutex);
}
atomic_store(&c.ready, false);
recv(c, res, loc);
if c.cap > 0 {
if c.len == c.cap - 1 {
// NOTE(bill): Only signal on the last one
cond_signal(&c.cond);
}
} else {
cond_signal(&c.cond);
}
}
raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false;
}
mutex_lock(&c.mutex);
switch {
case c.closed:
ok = false;
case c.cap > 0:
ok = c.ready && c.len < c.cap;
case:
ok = c.ready && c.len == 0;
}
mutex_unlock(&c.mutex);
return;
}
raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false;
}
mutex_lock(&c.mutex);
ok = c.len > 0;
mutex_unlock(&c.mutex);
return;
}
raw_channel_drain :: proc(c: ^Raw_Channel) {
if c == nil {
return;
}
mutex_lock(&c.mutex);
c.len = 0;
c.read = 0;
c.write = 0;
mutex_unlock(&c.mutex);
}
MAX_SELECT_CHANNELS :: 64;
SELECT_MAX_TIMEOUT :: max(time.Duration);
Select_Command :: enum {
Recv,
Send,
}
Select_Channel :: struct {
channel: ^Raw_Channel,
command: Select_Command,
}
select :: proc(channels: ..Select_Channel) -> (index: int) {
return select_timeout(SELECT_MAX_TIMEOUT, ..channels);
}
select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if c.channel == nil {
continue;
}
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 {
wait_state: uintptr = 0;
for _, i in channels {
q := &queues[i];
q.state = &wait_state;
}
for c, i in channels {
if c.channel == nil {
continue;
}
q := &queues[i];
switch c.command {
case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q);
case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q);
}
}
raw_channel_wait_queue_wait_on(&wait_state, timeout);
for c, i in channels {
if c.channel == nil {
continue;
}
q := &queues[i];
switch c.command {
case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q);
case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q);
}
}
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 && timeout == SELECT_MAX_TIMEOUT {
index = -1;
return;
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
msg = channel_recv(channels[index]);
return;
}
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
if msg != nil {
channel_send(channels[index], msg);
}
return;
}
select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.sendq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.sendq, q);
}
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try :: proc(channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
index = -1;
return;
case 1:
index = -1;
if raw_channel_can_recv(channels[0]) {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
switch len(channels) {
case 0:
return -1;
case 1:
if raw_channel_can_send(channels[0]) {
return 0;
}
return -1;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
index = -1;
return;
case 1:
ok: bool;
if msg, ok = channel_try_recv(channels[0]); ok {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
msg = channel_recv(channels[index]);
return;
}
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
index = -1;
switch len(channels) {
case 0:
return;
case 1:
if channel_try_send(channels[0], msg) {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
channel_send(channels[index], msg);
return;
}
-17
View File
@@ -1,17 +0,0 @@
//+build linux, darwin, freebsd
//+private
package sync2
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
// stub
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}
-34
View File
@@ -1,34 +0,0 @@
//+build windows
//+private
package sync2
import win32 "core:sys/windows"
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
ms: win32.DWORD = win32.INFINITE;
if max(time.Duration) != SELECT_MAX_TIMEOUT {
ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000);
}
v := atomic_load(state);
for v == 0 {
win32.WaitOnAddress(state, &v, size_of(state^), ms);
v = atomic_load(state);
}
atomic_store(state, 0);
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
atomic_add(x.state, 1);
win32.WakeByAddressSingle(x.state);
}
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
atomic_add(x.state, 1);
win32.WakeByAddressAll(x.state);
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ mutex_lock :: proc(m: ^Mutex) {
_mutex_lock(m);
}
// mutex_lock unlocks m
// mutex_unlock unlocks m
mutex_unlock :: proc(m: ^Mutex) {
_mutex_unlock(m);
}
@@ -103,7 +103,7 @@ rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
// A Recusrive_Mutex is a recursive mutual exclusion lock
// A Recursive_Mutex is a recursive mutual exclusion lock
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// A Recursive_Mutex must not be copied after first use
+269 -185
View File
@@ -1,159 +1,193 @@
//+build linux, darwin, freebsd
//+private
package sync2
when !#config(ODIN_SYNC_USE_PTHREADS, true) {
import "core:time"
import "core:runtime"
_Mutex_State :: enum i32 {
Atomic_Mutex_State :: enum i32 {
Unlocked = 0,
Locked = 1,
Waiting = 2,
}
_Mutex :: struct {
state: _Mutex_State,
// An Atomic_Mutex is a mutual exclusion lock
// The zero value for a Atomic_Mutex is an unlocked mutex
//
// An Atomic_Mutex must not be copied after first use
Atomic_Mutex :: struct {
state: Atomic_Mutex_State,
}
_mutex_lock :: proc(m: ^Mutex) {
if atomic_xchg_rel(&m.impl.state, .Unlocked) != .Unlocked {
_mutex_unlock_slow(m);
// atomic_mutex_lock locks m
atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
@(cold)
lock_slow :: proc(m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
new_state := curr_state; // Make a copy of it
spin_lock: for spin in 0..<i32(100) {
state, ok := atomic_compare_exchange_weak_acquire(&m.state, .Unlocked, new_state);
if ok {
return;
}
if state == .Waiting {
break spin_lock;
}
for i := min(spin+1, 32); i > 0; i -= 1 {
cpu_relax();
}
}
for {
if atomic_exchange_acquire(&m.state, .Waiting) == .Unlocked {
return;
}
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
cpu_relax();
}
}
switch v := atomic_exchange_acquire(&m.state, .Locked); v {
case .Unlocked:
// Okay
case: fallthrough;
case .Locked, .Waiting:
lock_slow(m, v);
}
}
_mutex_unlock :: proc(m: ^Mutex) {
switch atomic_xchg_rel(&m.impl.state, .Unlocked) {
// atomic_mutex_unlock unlocks m
atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
@(cold)
unlock_slow :: proc(m: ^Atomic_Mutex) {
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
}
switch atomic_exchange_release(&m.state, .Unlocked) {
case .Unlocked:
unreachable();
case .Locked:
// Okay
case .Waiting:
_mutex_unlock_slow(m);
unlock_slow(m);
}
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
_, ok := atomic_cxchg_acq(&m.impl.state, .Unlocked, .Locked);
// atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
_, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked);
return ok;
}
@(cold)
_mutex_lock_slow :: proc(m: ^Mutex, curr_state: _Mutex_State) {
new_state := curr_state; // Make a copy of it
// Example:
//
// if atomic_mutex_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_mutex_unlock)
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_lock(m);
return true;
}
spin_lock: for spin in 0..<i32(100) {
state, ok := atomic_cxchgweak_acq(&m.impl.state, .Unlocked, new_state);
if ok {
return;
}
if state == .Waiting {
break spin_lock;
}
Atomic_RW_Mutex_State :: distinct uint;
Atomic_RW_Mutex_State_Half_Width :: size_of(Atomic_RW_Mutex_State)*8/2;
Atomic_RW_Mutex_State_Is_Writing :: Atomic_RW_Mutex_State(1);
Atomic_RW_Mutex_State_Writer :: Atomic_RW_Mutex_State(1)<<1;
Atomic_RW_Mutex_State_Reader :: Atomic_RW_Mutex_State(1)<<Atomic_RW_Mutex_State_Half_Width;
for i := min(spin+1, 32); i > 0; i -= 1 {
cpu_relax();
}
}
Atomic_RW_Mutex_State_Writer_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << 1;
Atomic_RW_Mutex_State_Reader_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << Atomic_RW_Mutex_State_Half_Width;
for {
if atomic_xchg_acq(&m.impl.state, .Waiting) == .Unlocked {
return;
}
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
cpu_relax();
// An Atomic_RW_Mutex is a reader/writer mutual exclusion lock
// The lock can be held by any arbitrary number of readers or a single writer
// The zero value for an Atomic_RW_Mutex is an unlocked mutex
//
// An Atomic_RW_Mutex must not be copied after first use
Atomic_RW_Mutex :: struct {
state: Atomic_RW_Mutex_State,
mutex: Atomic_Mutex,
sema: Atomic_Sema,
}
// atomic_rw_mutex_lock locks rw for writing (with a single writer)
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer);
atomic_mutex_lock(&rw.mutex);
state := atomic_or(&rw.state, Atomic_RW_Mutex_State_Writer);
if state & Atomic_RW_Mutex_State_Reader_Mask != 0 {
atomic_sema_wait(&rw.sema);
}
}
@(cold)
_mutex_unlock_slow :: proc(m: ^Mutex) {
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
// atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
atomic_rw_mutex_unlock :: proc(rw: ^Atomic_RW_Mutex) {
_ = atomic_and(&rw.state, ~Atomic_RW_Mutex_State_Is_Writing);
atomic_mutex_unlock(&rw.mutex);
}
RW_Mutex_State :: distinct uint;
RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2;
RW_Mutex_State_Is_Writing :: RW_Mutex_State(1);
RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1;
RW_Mutex_State_Reader :: RW_Mutex_State(1)<<RW_Mutex_State_Half_Width;
RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1;
RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width;
_RW_Mutex :: struct {
state: RW_Mutex_State,
mutex: Mutex,
sema: Sema,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer);
mutex_lock(&rw.impl.mutex);
state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer);
if state & RW_Mutex_State_Reader_Mask != 0 {
sema_wait(&rw.impl.sema);
}
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
_ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing);
mutex_unlock(&rw.impl.mutex);
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
if mutex_try_lock(&rw.impl.mutex) {
state := atomic_load(&rw.impl.state);
if state & RW_Mutex_State_Reader_Mask == 0 {
_ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing);
// atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
if atomic_mutex_try_lock(&rw.mutex) {
state := atomic_load(&rw.state);
if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
_ = atomic_or(&rw.state, Atomic_RW_Mutex_State_Is_Writing);
return true;
}
mutex_unlock(&rw.impl.mutex);
atomic_mutex_unlock(&rw.mutex);
}
return false;
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
state := atomic_load(&rw.impl.state);
for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
// atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
state := atomic_load(&rw.state);
for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
ok: bool;
state, ok = atomic_cxchgweak(&rw.impl.state, state, state + RW_Mutex_State_Reader);
state, ok = atomic_compare_exchange_weak(&rw.state, state, state + Atomic_RW_Mutex_State_Reader);
if ok {
return;
}
}
mutex_lock(&rw.impl.mutex);
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader);
mutex_unlock(&rw.impl.mutex);
atomic_mutex_lock(&rw.mutex);
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader);
atomic_mutex_unlock(&rw.mutex);
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader);
// atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
state := atomic_sub(&rw.state, Atomic_RW_Mutex_State_Reader);
if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
(state & RW_Mutex_State_Is_Writing != 0) {
sema_post(&rw.impl.sema);
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
(state & Atomic_RW_Mutex_State_Is_Writing != 0) {
atomic_sema_post(&rw.sema);
}
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
state := atomic_load(&rw.impl.state);
if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
_, ok := atomic_cxchg(&rw.impl.state, state, state + RW_Mutex_State_Reader);
// atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
state := atomic_load(&rw.state);
if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader);
if ok {
return true;
}
}
if mutex_try_lock(&rw.impl.mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader);
mutex_unlock(&rw.impl.mutex);
if atomic_mutex_try_lock(&rw.mutex) {
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader);
atomic_mutex_unlock(&rw.mutex);
return true;
}
@@ -161,127 +195,177 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
}
_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
// Example:
//
// if atomic_rw_mutex_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_rw_mutex_unlock)
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_lock(m);
return true;
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := runtime.current_thread_id();
if tid != m.impl.owner {
mutex_lock(&m.impl.mutex);
}
// inside the lock
m.impl.owner = tid;
m.impl.recursion += 1;
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
tid := runtime.current_thread_id();
assert(tid == m.impl.owner);
m.impl.recursion -= 1;
recursion := m.impl.recursion;
if recursion == 0 {
m.impl.owner = 0;
}
if recursion == 0 {
mutex_unlock(&m.impl.mutex);
}
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := runtime.current_thread_id();
if m.impl.owner == tid {
return mutex_try_lock(&m.impl.mutex);
}
if !mutex_try_lock(&m.impl.mutex) {
return false;
}
// inside the lock
m.impl.owner = tid;
m.impl.recursion += 1;
// Example:
//
// if atomic_rw_mutex_shared_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_rw_mutex_shared_unlock)
atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_shared_lock(m);
return true;
}
// An Atomic_Recursive_Mutex is a recursive mutual exclusion lock
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// An Atomic_Recursive_Mutex must not be copied after first use
Atomic_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
}
atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
tid := runtime.current_thread_id();
if tid != m.owner {
mutex_lock(&m.mutex);
}
// inside the lock
m.owner = tid;
m.recursion += 1;
}
atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
tid := runtime.current_thread_id();
assert(tid == m.owner);
m.recursion -= 1;
recursion := m.recursion;
if recursion == 0 {
m.owner = 0;
}
if recursion == 0 {
mutex_unlock(&m.mutex);
}
// outside the lock
}
atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
tid := runtime.current_thread_id();
if m.owner == tid {
return mutex_try_lock(&m.mutex);
}
if !mutex_try_lock(&m.mutex) {
return false;
}
// inside the lock
m.owner = tid;
m.recursion += 1;
return true;
}
// Example:
//
// if atomic_recursive_mutex_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_recursive_mutex_unlock)
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_lock(m);
return true;
}
@(private="file")
Queue_Item :: struct {
next: ^Queue_Item,
futex: i32,
}
@(private="file")
queue_item_wait :: proc(item: ^Queue_Item) {
for atomic_load_acq(&item.futex) == 0 {
for atomic_load_acquire(&item.futex) == 0 {
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
cpu_relax();
}
}
@(private="file")
queue_item_signal :: proc(item: ^Queue_Item) {
atomic_store_rel(&item.futex, 1);
atomic_store_release(&item.futex, 1);
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
}
_Cond :: struct {
queue_mutex: Mutex,
// Atomic_Cond implements a condition variable, a rendezvous point for threads
// waiting for signalling the occurence of an event
//
// An Atomic_Cond must not be copied after first use
Atomic_Cond :: struct {
queue_mutex: Atomic_Mutex,
queue_head: ^Queue_Item,
pending: bool,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
waiter := &Queue_Item{};
mutex_lock(&c.impl.queue_mutex);
waiter.next = c.impl.queue_head;
c.impl.queue_head = waiter;
atomic_mutex_lock(&c.queue_mutex);
waiter.next = c.queue_head;
c.queue_head = waiter;
atomic_store(&c.impl.pending, true);
mutex_unlock(&c.impl.queue_mutex);
atomic_store(&c.pending, true);
atomic_mutex_unlock(&c.queue_mutex);
mutex_unlock(m);
atomic_mutex_unlock(m);
queue_item_wait(waiter);
mutex_lock(m);
atomic_mutex_lock(m);
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, timeout: time.Duration) -> bool {
atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, timeout: time.Duration) -> bool {
// TODO(bill): _cond_wait_with_timeout for unix
return false;
}
_cond_signal :: proc(c: ^Cond) {
if !atomic_load(&c.impl.pending) {
atomic_cond_signal :: proc(c: ^Atomic_Cond) {
if !atomic_load(&c.pending) {
return;
}
mutex_lock(&c.impl.queue_mutex);
waiter := c.impl.queue_head;
if c.impl.queue_head != nil {
c.impl.queue_head = c.impl.queue_head.next;
atomic_mutex_lock(&c.queue_mutex);
waiter := c.queue_head;
if c.queue_head != nil {
c.queue_head = c.queue_head.next;
}
atomic_store(&c.impl.pending, c.impl.queue_head != nil);
mutex_unlock(&c.impl.queue_mutex);
atomic_store(&c.pending, c.queue_head != nil);
atomic_mutex_unlock(&c.queue_mutex);
if waiter != nil {
queue_item_signal(waiter);
}
}
_cond_broadcast :: proc(c: ^Cond) {
if !atomic_load(&c.impl.pending) {
atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
if !atomic_load(&c.pending) {
return;
}
atomic_store(&c.impl.pending, false);
atomic_store(&c.pending, false);
mutex_lock(&c.impl.queue_mutex);
waiters := c.impl.queue_head;
c.impl.queue_head = nil;
mutex_unlock(&c.impl.queue_mutex);
atomic_mutex_lock(&c.queue_mutex);
waiters := c.queue_head;
c.queue_head = nil;
atomic_mutex_unlock(&c.queue_mutex);
for waiters != nil {
queue_item_signal(waiters);
@@ -289,35 +373,35 @@ _cond_broadcast :: proc(c: ^Cond) {
}
}
_Sema :: struct {
mutex: Mutex,
cond: Cond,
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
// Posting to the semaphore increases the count by one, or the provided amount.
//
// An Atomic_Sema must not be copied after first use
Atomic_Sema :: struct {
mutex: Atomic_Mutex,
cond: Atomic_Cond,
count: int,
}
_sema_wait :: proc(s: ^Sema) {
mutex_lock(&s.impl.mutex);
defer mutex_unlock(&s.impl.mutex);
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
atomic_mutex_lock(&s.mutex);
defer atomic_mutex_unlock(&s.mutex);
for s.impl.count == 0 {
cond_wait(&s.impl.cond, &s.impl.mutex);
for s.count == 0 {
atomic_cond_wait(&s.cond, &s.mutex);
}
s.impl.count -= 1;
if s.impl.count > 0 {
cond_signal(&s.impl.cond);
s.count -= 1;
if s.count > 0 {
atomic_cond_signal(&s.cond);
}
}
_sema_post :: proc(s: ^Sema, count := 1) {
mutex_lock(&s.impl.mutex);
defer mutex_unlock(&s.impl.mutex);
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
atomic_mutex_lock(&s.mutex);
defer atomic_mutex_unlock(&s.mutex);
s.impl.count += count;
cond_signal(&s.impl.cond);
s.count += count;
atomic_cond_signal(&s.cond);
}
} // !ODIN_SYNC_USE_PTHREADS
+1 -1
View File
@@ -1,4 +1,4 @@
//+build linux, darwin, freebsd
//+build linux, freebsd
//+private
package sync2
-10
View File
@@ -12,7 +12,6 @@ package unicode
@(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case.
@(private) pLmask :: pLo;
@(static)
char_properties := [MAX_LATIN1+1]u8{
0x00 = pC, // '\x00'
0x01 = pC, // '\x01'
@@ -273,7 +272,6 @@ char_properties := [MAX_LATIN1+1]u8{
};
@(static)
alpha_ranges := [?]i32{
0x00d8, 0x00f6,
0x00f8, 0x01f5,
@@ -429,7 +427,6 @@ alpha_ranges := [?]i32{
0xffda, 0xffdc,
};
@(static)
alpha_singlets := [?]i32{
0x00aa,
0x00b5,
@@ -465,7 +462,6 @@ alpha_singlets := [?]i32{
0xfe74,
};
@(static)
space_ranges := [?]i32{
0x0009, 0x000d, // tab and newline
0x0020, 0x0020, // space
@@ -481,7 +477,6 @@ space_ranges := [?]i32{
0xfeff, 0xfeff,
};
@(static)
unicode_spaces := [?]i32{
0x0009, // tab
0x000a, // LF
@@ -499,7 +494,6 @@ unicode_spaces := [?]i32{
0xfeff, // unknown
};
@(static)
to_upper_ranges := [?]i32{
0x0061, 0x007a, 468, // a-z A-Z
0x00e0, 0x00f6, 468,
@@ -538,7 +532,6 @@ to_upper_ranges := [?]i32{
0xff41, 0xff5a, 468,
};
@(static)
to_upper_singlets := [?]i32{
0x00ff, 621,
0x0101, 499,
@@ -882,7 +875,6 @@ to_upper_singlets := [?]i32{
0x1ff3, 509,
};
@(static)
to_lower_ranges := [?]i32{
0x0041, 0x005a, 532, // A-Z a-z
0x00c0, 0x00d6, 532, // - -
@@ -922,7 +914,6 @@ to_lower_ranges := [?]i32{
0xff21, 0xff3a, 532, // - -
};
@(static)
to_lower_singlets := [?]i32{
0x0100, 501,
0x0102, 501,
@@ -1259,7 +1250,6 @@ to_lower_singlets := [?]i32{
0x1ffc, 491,
};
@(static)
to_title_singlets := [?]i32{
0x01c4, 501,
0x01c6, 499,
+29 -7
View File
@@ -1352,8 +1352,8 @@ bit_set_type :: proc() {
d: Days;
d = {Sunday, Monday};
e := d | WEEKEND;
e |= {Monday};
e := d + WEEKEND;
e += {Monday};
fmt.println(d, e);
ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
@@ -1372,12 +1372,12 @@ bit_set_type :: proc() {
fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
incl(&x, 'F');
x += {'F'};
assert('F' in x);
excl(&x, 'F');
x -= {'F'};
assert('F' not_in x);
y |= {1, 4, 2};
y += {1, 4, 2};
assert(2 in y);
}
{
@@ -1760,8 +1760,6 @@ range_statements_with_multiple_return_values :: proc() {
soa_struct_layout :: proc() {
// IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
// NOTE(bill): Most likely #soa [N]T
fmt.println("\n#SOA Struct Layout");
{
@@ -1858,6 +1856,30 @@ soa_struct_layout :: proc() {
fmt.println(cap(d));
fmt.println(d[:]);
}
{ // soa_zip and soa_unzip
fmt.println("\nsoa_zip and soa_unzip");
x := []i32{1, 3, 9};
y := []f32{2, 4, 16};
z := []b32{true, false, true};
// produce an #soa slice the normal slices passed
s := soa_zip(a=x, b=y, c=z);
// iterate over the #soa slice
for v, i in s {
fmt.println(v, i); // exactly the same as s[i]
// NOTE: 'v' is NOT a temporary value but has a specialized addressing mode
// which means that when accessing v.a etc, it does the correct transformation
// internally:
// s[i].a === s.a[i]
fmt.println(v.a, v.b, v.c);
}
// Recover the slices from the #soa slice
a, b, c := soa_unzip(s);
fmt.println(a, b, c);
}
}
constant_literal_expressions :: proc() {
+5 -5
View File
@@ -1347,8 +1347,8 @@ bit_set_type :: proc() {
d: Days
d = {Sunday, Monday}
e := d | WEEKEND
e |= {Monday}
e := d + WEEKEND
e += {Monday}
fmt.println(d, e)
ok := Saturday in e // `in` is only allowed for `map` and `bit_set` types
@@ -1367,12 +1367,12 @@ bit_set_type :: proc() {
fmt.println(typeid_of(type_of(x))) // bit_set[A..Z]
fmt.println(typeid_of(type_of(y))) // bit_set[0..8; u16]
incl(&x, 'F')
x += {'F'};
assert('F' in x)
excl(&x, 'F')
x -= {'F'};
assert('F' not_in x)
y |= {1, 4, 2}
y += {1, 4, 2}
assert(2 in y)
}
{
+5
View File
@@ -200,8 +200,10 @@ struct BuildContext {
bool disallow_do;
bool insert_semicolon;
bool ignore_warnings;
bool warnings_as_errors;
bool show_error_line;
bool use_subsystem_windows;
bool ignore_microsoft_magic;
@@ -746,6 +748,9 @@ String get_fullpath_core(gbAllocator a, String path) {
return path_to_fullpath(a, res);
}
bool show_error_line(void) {
return build_context.show_error_line;
}
void init_build_context(TargetMetrics *cross_target) {
+164 -2
View File
@@ -87,7 +87,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name;
String name = bd->name.string;
if (name == "defined") {
break;
}
@@ -124,7 +124,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name;
String name = bd->name.string;
if (name == "location") {
if (ce->args.count > 1) {
error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
@@ -2026,6 +2026,128 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
case BuiltinProc_sqrt:
{
Operand x = {};
check_expr(c, &x, ce->args[0]);
if (x.mode == Addressing_Invalid) {
return false;
}
if (!is_type_float(x.type)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
gb_string_free(xts);
return false;
}
if (x.mode == Addressing_Constant) {
f64 v = exact_value_to_f64(x.value);
operand->mode = Addressing_Constant;
operand->type = x.type;
operand->value = exact_value_float(gb_sqrt(v));
break;
}
operand->mode = Addressing_Value;
operand->type = default_type(x.type);
}
break;
case BuiltinProc_mem_copy:
case BuiltinProc_mem_copy_non_overlapping:
{
operand->mode = Addressing_NoValue;
operand->type = t_invalid;
Operand dst = {};
Operand src = {};
Operand len = {};
check_expr(c, &dst, ce->args[0]);
check_expr(c, &src, ce->args[1]);
check_expr(c, &len, ce->args[2]);
if (dst.mode == Addressing_Invalid) {
return false;
}
if (src.mode == Addressing_Invalid) {
return false;
}
if (len.mode == Addressing_Invalid) {
return false;
}
if (!is_type_pointer(dst.type)) {
gbString str = type_to_string(dst.type);
error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_pointer(src.type)) {
gbString str = type_to_string(src.type);
error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_integer(len.type)) {
gbString str = type_to_string(len.type);
error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (len.mode == Addressing_Constant) {
i64 n = exact_value_to_i64(len.value);
if (n < 0) {
gbString str = expr_to_string(len.expr);
error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
}
}
}
break;
case BuiltinProc_mem_zero:
{
operand->mode = Addressing_NoValue;
operand->type = t_invalid;
Operand ptr = {};
Operand len = {};
check_expr(c, &ptr, ce->args[0]);
check_expr(c, &len, ce->args[1]);
if (ptr.mode == Addressing_Invalid) {
return false;
}
if (len.mode == Addressing_Invalid) {
return false;
}
if (!is_type_pointer(ptr.type)) {
gbString str = type_to_string(ptr.type);
error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_integer(len.type)) {
gbString str = type_to_string(len.type);
error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (len.mode == Addressing_Constant) {
i64 n = exact_value_to_i64(len.value);
if (n < 0) {
gbString str = expr_to_string(len.expr);
error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
}
}
}
break;
case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel:
@@ -2429,6 +2551,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
case BuiltinProc_type_is_variant_of:
{
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
return false;
}
Type *u = operand->type;
if (!is_type_union(u)) {
error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
return false;
}
Type *v = check_type(c, ce->args[1]);
u = base_type(u);
GB_ASSERT(u->kind == Type_Union);
bool is_variant = false;
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
is_variant = true;
break;
}
}
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
operand->value = exact_value_bool(is_variant);
}
break;
case BuiltinProc_type_struct_field_count:
operand->value = exact_value_i64(0);
if (operand->mode != Addressing_Type) {
+18 -14
View File
@@ -289,17 +289,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (ac.atom_op_table != nullptr) {
Type *bt = base_type(e->type);
switch (bt->kind) {
case Type_Struct:
bt->Struct.atom_op_table = ac.atom_op_table;
break;
default:
error(e->token, "Only struct types can have custom atom operations");
break;
}
}
}
@@ -376,6 +365,7 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Constant);
init = unparen_expr(init);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
@@ -409,6 +399,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
e->kind = Entity_TypeName;
e->type = nullptr;
if (entity != nullptr && entity->type != nullptr &&
is_type_polymorphic_record_unspecialized(entity->type)) {
DeclInfo *decl = decl_info_of_entity(e);
if (decl != nullptr) {
if (decl->attributes.count > 0) {
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
}
}
override_entity_in_scope(e, entity);
return;
}
check_type_decl(ctx, e, ctx->decl->init_expr, named_type);
return;
}
@@ -897,10 +899,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
e->Variable.thread_local_model = ac.thread_local_model;
e->Variable.is_export = ac.is_export;
e->flags &= ~EntityFlag_Static;
if (ac.is_static) {
e->flags |= EntityFlag_Static;
} else {
e->flags &= ~EntityFlag_Static;
error(e->token, "@(static) is not supported for global variables, nor required");
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
@@ -933,6 +934,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
}
if (ac.link_section.len > 0) {
e->Variable.link_section = ac.link_section;
}
if (e->Variable.is_foreign || e->Variable.is_export) {
String name = e->token.string;
+96 -96
View File
@@ -621,7 +621,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
PolyProcData poly_proc_data = {};
if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) {
add_entity_use(c, operand->expr, poly_proc_data.gen_entity);
Entity *e = poly_proc_data.gen_entity;
add_type_and_value(c->info, operand->expr, Addressing_Value, e->type, {});
add_entity_use(c, operand->expr, e);
return 4;
}
}
@@ -1113,6 +1115,7 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
error(curr->token, "\t%.*s refers to", LIT(curr->token.string));
}
error(curr->token, "\t%.*s", LIT(curr->token.string));
curr->type = t_invalid;
}
return true;
}
@@ -1130,7 +1133,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
Entity *e = scope_lookup(c->scope, name);
if (e == nullptr) {
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value type");
error(n, "'_' cannot be used as a value");
} else {
error(n, "Undeclared name: %.*s", LIT(name));
}
@@ -2213,6 +2216,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
return;
}
if (is_type_untyped(y->type)) {
convert_to_typed(c, y, t_uint);
}
x->mode = Addressing_Value;
}
@@ -2380,9 +2387,15 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
if (core_type(bt)->kind == Type_Basic) {
if (check_representable_as_constant(c, x->value, bt, &x->value)) {
return true;
} else if (is_type_pointer(type) && check_is_castable_to(c, x, type)) {
return true;
} else if (check_is_castable_to(c, x, type)) {
if (is_type_pointer(type)) {
return true;
}
}
} else if (check_is_castable_to(c, x, type)) {
x->value = {};
x->mode = Addressing_Value;
return true;
}
} else if (check_is_castable_to(c, x, type)) {
if (x->mode != Addressing_Constant) {
@@ -2392,6 +2405,9 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
} else if (is_type_union(type)) {
x->mode = Addressing_Value;
}
if (x->mode == Addressing_Value) {
x->value = {};
}
return true;
}
return false;
@@ -2874,7 +2890,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
if (token_is_comparison(be->op.kind)) {
// NOTE(bill): Do nothing as the types are fine
} else if (token_is_shift(be->op.kind)) {
update_expr_type(c, be->left, type, final);
update_expr_type(c, be->left, type, final);
} else {
update_expr_type(c, be->left, type, final);
update_expr_type(c, be->right, type, final);
@@ -3188,8 +3204,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
break;
}
operand->type = target_type;
update_expr_type(c, operand->expr, target_type, true);
operand->type = target_type;
}
bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) {
@@ -3376,7 +3392,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
GB_ASSERT(bt->kind == Type_EnumeratedArray);
corrected_index = index + exact_value_to_i64(bt->EnumeratedArray.min_value);
}
if (op == Token_Ellipsis) {
if (op != Token_RangeHalf) {
if (lo <= corrected_index && corrected_index <= hi) {
TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true;
@@ -3938,6 +3954,16 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
}
if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
// NOTE(bill): Used only for optimizations in the backend
if (is_blank_ident(lhs[0].expr)) {
expr->TypeAssertion.ignores[0] = true;
}
if (is_blank_ident(lhs[1].expr)) {
expr->TypeAssertion.ignores[1] = true;
}
}
array_add(operands, val0);
array_add(operands, val1);
optional_ok = true;
@@ -4052,6 +4078,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
}
if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
// NOTE(bill): Used only for optimizations in the backend
if (is_blank_ident(lhs[0]->token)) {
expr->TypeAssertion.ignores[0] = true;
}
if (is_blank_ident(lhs[1]->token)) {
expr->TypeAssertion.ignores[1] = true;
}
}
array_add(operands, val0);
array_add(operands, val1);
optional_ok = true;
@@ -4076,6 +4112,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
}
bool is_expr_constant_zero(Ast *expr) {
GB_ASSERT(expr != nullptr);
auto v = exact_value_to_integer(expr->tav.value);
if (v.kind == ExactValue_Integer) {
return big_int_cmp_zero(&v.value_integer) == 0;
}
return false;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
@@ -4250,6 +4296,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
err = CallArgumentError_WrongTypes;
}
}
} else if (show_error) {
check_assignment(c, &o, t, str_lit("argument"));
}
score += s;
@@ -4265,7 +4313,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
}
}
if (variadic) {
@@ -4303,6 +4354,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
check_assignment(c, &o, t, str_lit("argument"));
}
err = CallArgumentError_WrongTypes;
} else if (show_error) {
check_assignment(c, &o, t, str_lit("argument"));
}
score += s;
if (is_type_any(elem)) {
@@ -4311,6 +4364,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
if (o.mode == Addressing_Type && is_type_typeid(t)) {
add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
}
}
}
@@ -4525,6 +4580,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
err = CallArgumentError_NoneConstantParameter;
}
}
} else if (show_error) {
check_assignment(c, o, e->type, str_lit("procedure argument"));
}
score += s;
}
@@ -5459,7 +5516,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
String name = bd->name;
String name = bd->name.string;
if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -5563,9 +5620,15 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
check_expr(c, operand, arg);
if (operand->mode != Addressing_Invalid) {
check_cast(c, operand, t);
if (is_type_polymorphic(t)) {
error(call, "A polymorphic type cannot be used in a type conversion");
} else {
// NOTE(bill): Otherwise the compiler can override the polymorphic type
// as it assumes it is determining the type
check_cast(c, operand, t);
}
}
operand->type = t;
break;
}
}
@@ -5715,7 +5778,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
break;
case Addressing_Type:
if (t == nullptr || !is_type_typeid(t)) {
err_str = "is not an expression but a type";
err_str = "is not an expression but a type, in this context it is ambiguous";
}
break;
case Addressing_Builtin:
@@ -5903,8 +5966,9 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
TokenKind op = Token_Lt;
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_RangeHalf: op = Token_Lt; break;
case Token_Ellipsis: op = Token_LtEq; break; // ..
case Token_RangeFull: op = Token_LtEq; break; // ..=
case Token_RangeHalf: op = Token_Lt; break; // ..<
default: error(ie->op, "Invalid range operator"); break;
}
bool ok = compare_exact_values(op, a, b);
@@ -5915,7 +5979,7 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
}
ExactValue inline_for_depth = exact_value_sub(b, a);
if (ie->op.kind == Token_Ellipsis) {
if (ie->op.kind != Token_RangeHalf) {
inline_for_depth = exact_value_increment_one(inline_for_depth);
}
@@ -6127,13 +6191,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_ast_node(bd, BasicDirective, node);
o->mode = Addressing_Constant;
if (bd->name == "file") {
String name = bd->name.string;
if (name == "file") {
o->type = t_untyped_string;
o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
} else if (bd->name == "line") {
} else if (name == "line") {
o->type = t_untyped_integer;
o->value = exact_value_i64(bd->token.pos.line);
} else if (bd->name == "procedure") {
} else if (name == "procedure") {
if (c->curr_proc_decl == nullptr) {
error(node, "#procedure may only be used within procedures");
o->type = t_untyped_string;
@@ -6142,7 +6207,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->type = t_untyped_string;
o->value = exact_value_string(c->proc_name);
}
} else if (bd->name == "caller_location") {
} else if (name == "caller_location") {
init_core_source_code_location(c->checker);
error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location;
@@ -6309,7 +6374,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (cl->type->ArrayType.tag != nullptr) {
Ast *tag = cl->type->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name;
String name = tag->BasicDirective.name.string;
if (name == "soa") {
error(node, "#soa arrays are not supported for compound literals");
return kind;
@@ -6321,7 +6386,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (cl->elems.count > 0) {
Ast *tag = cl->type->DynamicArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name;
String name = tag->BasicDirective.name.string;
if (name == "soa") {
error(node, "#soa arrays are not supported for compound literals");
return kind;
@@ -7536,47 +7601,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return Expr_Expr;
}
if (t->kind == Type_Struct) {
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
if (atom_op_table != nullptr) {
if (atom_op_table->op[TypeAtomOp_index_set]) {
if (c->assignment_lhs_hint == node) {
o->mode = Addressing_AtomOpAssign;
o->type = o->type;
o->expr = node;
return kind;
}
}
if (atom_op_table->op[TypeAtomOp_index_get]) {
Entity *e = atom_op_table->op[TypeAtomOp_index_get];
if (ie->index == nullptr) {
gbString str = expr_to_string(o->expr);
error(o->expr, "Missing index for '%s'", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
auto args = array_make<Ast *>(heap_allocator(), 2);
args[0] = ie->expr;
args[1] = ie->index;
GB_ASSERT(c->file != nullptr);
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {});
check_expr_base(c, o, fake_call, type_hint);
AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call};
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
o->expr = node;
return kind;
}
}
}
i64 max_count = -1;
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
@@ -7715,37 +7739,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (is_type_soa_struct(t)) {
valid = true;
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
} else {
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
Entity *e = atom_op_table->op[TypeAtomOp_slice];
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
Ast *expr = se->expr;
if (o->mode == Addressing_Variable) {
expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr);
} else if (is_type_pointer(o->type)) {
// Okay
} else {
gbString str = expr_to_string(node);
error(node, "Cannot slice '%s', value is not addressable", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
auto args = array_make<Ast *>(heap_allocator(), 1);
args[0] = expr;
GB_ASSERT(c->file != nullptr);
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {});
check_expr_base(c, o, fake_call, type_hint);
AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call};
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
valid = true;
}
}
break;
@@ -7774,10 +7767,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
o->mode = Addressing_Value;
if (se->low == nullptr && se->high != nullptr) {
// error(se->interval0, "1st index is required if a 2nd index is specified");
// It is okay to continue as it will assume the 1st index is zero
}
@@ -7812,6 +7802,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
}
if (max_count < 0) {
if (o->mode == Addressing_Constant) {
gbString s = expr_to_string(se->expr);
error(se->expr, "Cannot slice constant value '%s'", s);
gb_string_free(s);
}
}
o->mode = Addressing_Value;
if (is_type_string(t) && max_count >= 0) {
bool all_constant = true;
for (isize i = 0; i < gb_count_of(nodes); i++) {
@@ -8152,7 +8152,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
case_ast_node(bd, BasicDirective, node);
str = gb_string_append_rune(str, '#');
str = string_append_string(str, bd->name);
str = string_append_string(str, bd->name.string);
case_end;
case_ast_node(ud, Undef, node);
+70 -55
View File
@@ -7,7 +7,7 @@ bool is_diverging_stmt(Ast *stmt) {
return false;
}
if (expr->CallExpr.proc->kind == Ast_BasicDirective) {
String name = expr->CallExpr.proc->BasicDirective.name;
String name = expr->CallExpr.proc->BasicDirective.name.string;
return name == "panic";
}
Ast *proc = unparen_expr(expr->CallExpr.proc);
@@ -939,6 +939,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
case Token_Ellipsis: upper_op = Token_GtEq; break;
case Token_RangeFull: upper_op = Token_GtEq; break;
case Token_RangeHalf: upper_op = Token_Gt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -960,9 +961,44 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
add_constant_switch_case(ctx, &seen, lhs);
if (upper_op == Token_GtEq) {
add_constant_switch_case(ctx, &seen, rhs);
if (is_type_enum(x.type)) {
// TODO(bill): Fix this logic so it's fast!!!
i64 v0 = exact_value_to_i64(lhs.value);
i64 v1 = exact_value_to_i64(rhs.value);
Operand v = {};
v.mode = Addressing_Constant;
v.type = x.type;
v.expr = x.expr;
Type *bt = base_type(x.type);
GB_ASSERT(bt->kind == Type_Enum);
for (i64 vi = v0; vi <= v1; vi++) {
if (upper_op != Token_GtEq && vi == v1) {
break;
}
bool found = false;
for_array(j, bt->Enum.fields) {
Entity *f = bt->Enum.fields[j];
GB_ASSERT(f->kind == Entity_Constant);
i64 fv = exact_value_to_i64(f->Constant.value);
if (fv == vi) {
found = true;
break;
}
}
if (found) {
v.value = exact_value_i64(vi);
add_constant_switch_case(ctx, &seen, v);
}
}
} else {
add_constant_switch_case(ctx, &seen, lhs);
if (upper_op == Token_GtEq) {
add_constant_switch_case(ctx, &seen, rhs);
}
}
if (is_type_string(x.type)) {
@@ -1400,6 +1436,28 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
gbString expr_str = expr_to_string(operand.expr);
error(node, "Expression is not used: '%s'", expr_str);
gb_string_free(expr_str);
if (operand.expr->kind == Ast_BinaryExpr) {
ast_node(be, BinaryExpr, operand.expr);
if (be->op.kind != Token_CmpEq) {
break;
}
switch (be->left->tav.mode) {
case Addressing_Context:
case Addressing_Variable:
case Addressing_MapIndex:
case Addressing_SoaVariable:
{
gbString lhs = expr_to_string(be->left);
gbString rhs = expr_to_string(be->right);
error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs);
error_line("\t '%s = %s;'\n", lhs, rhs);
gb_string_free(rhs);
gb_string_free(lhs);
}
break;
}
}
break;
}
@@ -1454,53 +1512,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
isize max = gb_min(lhs_count, rhs_count);
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
// correctly for assignments
for (isize i = 0; i < max; i++) {
if (lhs_operands[i].mode == Addressing_AtomOpAssign) {
Operand lhs = lhs_operands[i];
Type *t = base_type(lhs.type);
GB_ASSERT(t->kind == Type_Struct);
ast_node(ie, IndexExpr, unparen_expr(lhs.expr));
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr);
Entity *e = atom_op_table->op[TypeAtomOp_index_set];
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
GB_ASSERT(ctx->file != nullptr);
TypeAndValue tv = type_and_value_of_expr(ie->expr);
Ast *expr = ie->expr;
if (is_type_pointer(tv.type)) {
// Okay
} else if (tv.mode == Addressing_Variable) {
// NOTE(bill): Hack it to take the address instead
expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr);
} else {
continue;
}
auto args = array_make<Ast *>(heap_allocator(), 3);
args[0] = expr;
args[1] = ie->index;
args[2] = rhs_operands[i].expr;
Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {});
Operand fake_operand = {};
fake_operand.expr = lhs.expr;
check_expr_base(ctx, &fake_operand, fake_call, nullptr);
AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call};
map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry);
lhs_to_ignore[i] = true;
}
}
for (isize i = 0; i < max; i++) {
if (lhs_to_ignore[i]) {
continue;
@@ -1526,8 +1537,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
Operand lhs = {Addressing_Invalid};
Operand rhs = {Addressing_Invalid};
Ast binary_expr = {Ast_BinaryExpr};
ast_node(be, BinaryExpr, &binary_expr);
Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
ast_node(be, BinaryExpr, binary_expr);
be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
// NOTE(bill): Only use the first one will be used
@@ -1535,7 +1546,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
be->right = as->rhs[0];
check_expr(ctx, &lhs, as->lhs[0]);
check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true);
check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
if (rhs.mode == Addressing_Invalid) {
return;
}
@@ -1632,7 +1643,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else {
for (isize i = 0; i < result_count; i++) {
Entity *e = pt->results->Tuple.variables[i];
check_assignment(ctx, &operands[i], e->type, str_lit("return statement"));
Operand *o = &operands[i];
check_assignment(ctx, o, e->type, str_lit("return statement"));
if (is_type_untyped(o->type)) {
update_expr_type(ctx, o->expr, e->type, true);
}
}
}
case_end;
+45 -43
View File
@@ -322,19 +322,6 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
array_add(&array, e);
map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
}
{
Type *dst_bt = base_type(named_type);
Type *src_bt = base_type(original_type);
if ((dst_bt != nullptr && src_bt != nullptr) &&
(dst_bt->kind == src_bt->kind)){
if (dst_bt->kind == Type_Struct) {
if (dst_bt->Struct.atom_op_table == nullptr) {
dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table;
}
}
}
}
}
Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
@@ -944,6 +931,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
if (upper - lower >= bits) {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
}
@@ -1203,10 +1191,15 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
if (allow_caller_location &&
expr->kind == Ast_BasicDirective &&
expr->BasicDirective.name == "caller_location") {
expr->BasicDirective.name.string == "caller_location") {
init_core_source_code_location(ctx->checker);
param_value.kind = ParameterValue_Location;
o.type = t_source_code_location;
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
} else {
if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type);
@@ -1214,6 +1207,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
check_expr(ctx, &o, expr);
}
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
if (is_operand_nil(o)) {
param_value.kind = ParameterValue_Nil;
} else if (o.mode != Addressing_Constant) {
@@ -1221,16 +1219,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant;
param_value.value = exact_value_procedure(expr);
} else {
Entity *e = nullptr;
// if (o.mode == Addressing_Value && is_type_proc(o.type)) {
if (o.mode == Addressing_Value || o.mode == Addressing_Variable) {
Operand x = {};
if (expr->kind == Ast_Ident) {
e = check_ident(ctx, &x, expr, nullptr, nullptr, false);
} else if (expr->kind == Ast_SelectorExpr) {
e = check_selector(ctx, &x, expr, nullptr);
}
}
Entity *e = entity_from_expr(o.expr);
if (e != nullptr) {
if (e->kind == Entity_Procedure) {
@@ -1253,8 +1242,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
} else if (allow_caller_location && o.mode == Addressing_Context) {
param_value.kind = ParameterValue_Value;
param_value.ast_value = expr;
} else if (o.value.kind != ExactValue_Invalid) {
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} else {
error(expr, "Default parameter must be a constant");
error(expr, "Default parameter must be a constant, %d", o.mode);
}
}
} else {
@@ -1267,12 +1259,14 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
}
}
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
if (out_type_) {
if (in_type != nullptr) {
*out_type_ = in_type;
} else {
*out_type_ = default_type(o.type);
}
}
if (out_type_) *out_type_ = default_type(o.type);
return param_value;
}
@@ -1389,6 +1383,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
}
}
if (type == nullptr) {
error(param, "Invalid parameter type");
type = t_invalid;
@@ -1408,6 +1405,21 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
type = t_invalid;
}
if (is_type_polymorphic(type)) {
switch (param_value.kind) {
case ParameterValue_Invalid:
case ParameterValue_Constant:
case ParameterValue_Nil:
break;
case ParameterValue_Location:
case ParameterValue_Value:
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
gb_string_free(str);
break;
}
}
if (p->flags&FieldFlag_c_vararg) {
if (p->type == nullptr ||
@@ -2517,16 +2529,6 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
return true;
}
}
// if (ctx->type_level == 0 && entity->state == EntityState_InProgress) {
// error(entity->token, "Illegal declaration cycle of `%.*s`", LIT(entity->token.string));
// for_array(j, *ctx->type_path) {
// Entity *k = (*ctx->type_path)[j];
// error(k->token, "\t%.*s refers to", LIT(k->token.string));
// }
// error(entity->token, "\t%.*s", LIT(entity->token.string));
// *type = t_invalid;
// }
return true;
}
@@ -2709,7 +2711,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
bool is_partial = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name;
String name = at->tag->BasicDirective.name.string;
if (name == "partial") {
is_partial = true;
} else {
@@ -2743,7 +2745,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name;
String name = at->tag->BasicDirective.name.string;
if (name == "soa") {
*type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
} else if (name == "simd") {
@@ -2768,7 +2770,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name;
String name = at->tag->BasicDirective.name.string;
if (name == "soa") {
*type = make_soa_struct_slice(ctx, e, at->elem, elem);
} else {
@@ -2788,7 +2790,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *elem = check_type(ctx, dat->elem);
if (dat->tag != nullptr) {
GB_ASSERT(dat->tag->kind == Ast_BasicDirective);
String name = dat->tag->BasicDirective.name;
String name = dat->tag->BasicDirective.name.string;
if (name == "soa") {
*type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem);
} else {
+10 -200
View File
@@ -2650,6 +2650,16 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "link_section") {
if (ev.kind == ExactValue_String) {
ac->link_section = ev.value_string;
if (!is_foreign_name_valid(ac->link_section)) {
error(elem, "Invalid link section: %.*s", LIT(ac->link_section));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
}
return false;
}
@@ -2666,206 +2676,6 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
} else if (name == "index_get") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.result_count == 0) {
error(value, "'%s' attribute must return something", LIT(name));
valid = false;
}
if (pt->Proc.param_count < 2) {
error(value, "'%s' attribute must allow for 2 parameters", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 2) {
error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name));
valid = false;
}
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
} else if (name == "index_set") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.param_count < 3) {
error(value, "'%s' attribute must allow for 3 parameters", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 3) {
error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name));
valid = false;
}
}
if (pt->Proc.variadic || pt->Proc.c_vararg) {
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
valid = false;
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
} else if (name == "slice") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.param_count < 1) {
error(value, "'%s' attribute must allow for 1 parameter", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 1) {
error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name));
valid = false;
}
{
Entity *param = pt->Proc.params->Tuple.variables[0];
Type *param_type = base_type(param->type);
if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) {
// okay
} else {
error(value, "'%s' attribute's first parameter must be a pointer", LIT(name));
valid = false;
}
}
}
if (pt->Proc.variadic || pt->Proc.c_vararg) {
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
valid = false;
}
if (pt->Proc.result_count != 1) {
error(value, "'%s' attribute must return 1 result", LIT(name));
valid = false;
} else {
Type *rt = pt->Proc.results->Tuple.variables[0]->type;
rt = base_type(rt);
if (!is_type_slice(rt)) {
error(value, "'%s' attribute must return a slice", LIT(name));
valid = false;
}
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_slice] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
}
return false;
}
+1 -1
View File
@@ -109,12 +109,12 @@ struct AttributeContext {
bool set_cold;
String link_name;
String link_prefix;
String link_section;
isize init_expr_list_count;
String thread_local_model;
String deprecated_message;
DeferredProcedure deferred_procedure;
u32 optimization_mode; // ProcedureOptimizationMode
struct TypeAtomOpTable *atom_op_table;
};
AttributeContext make_attribute_context(String link_prefix) {
+16
View File
@@ -56,6 +56,12 @@ enum BuiltinProcId {
BuiltinProc_overflow_sub,
BuiltinProc_overflow_mul,
BuiltinProc_sqrt,
BuiltinProc_mem_copy,
BuiltinProc_mem_copy_non_overlapping,
BuiltinProc_mem_zero,
BuiltinProc_volatile_store,
BuiltinProc_volatile_load,
@@ -197,6 +203,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_is_specialization_of,
BuiltinProc_type_is_variant_of,
BuiltinProc_type_struct_field_count,
BuiltinProc_type_proc_parameter_count,
@@ -276,6 +284,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_zero"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -415,6 +429,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+1
View File
@@ -158,6 +158,7 @@ struct Entity {
Ast * foreign_library_ident;
String link_name;
String link_prefix;
String link_section;
bool is_foreign;
bool is_export;
} Variable;
+77 -54
View File
@@ -10,21 +10,37 @@ struct lbArgType {
LLVMTypeRef cast_type; // Optional
LLVMTypeRef pad_type; // Optional
LLVMAttributeRef attribute; // Optional
LLVMAttributeRef align_attribute; // Optional
i64 byval_alignment;
bool is_byval;
};
i64 lb_sizeof(LLVMTypeRef type);
i64 lb_alignof(LLVMTypeRef type);
lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr};
return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, nullptr, 0, false};
}
lbArgType lb_arg_type_direct(LLVMTypeRef type) {
return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
}
lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr};
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr, 0, false};
}
lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) {
i64 alignment = lb_alignof(type);
alignment = gb_max(alignment, 8);
LLVMAttributeRef byval_attr = lb_create_enum_attribute_with_type(c, "byval", type);
LLVMAttributeRef align_attr = lb_create_enum_attribute(c, "align", alignment);
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, byval_attr, align_attr, alignment, true};
}
lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr};
return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr, 0, false};
}
struct lbFunctionType {
@@ -121,6 +137,9 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
if (arg->attribute) {
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute);
}
if (arg->align_attribute) {
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
}
arg_index++;
}
@@ -145,8 +164,6 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
}
i64 lb_sizeof(LLVMTypeRef type);
i64 lb_alignof(LLVMTypeRef type);
i64 lb_sizeof(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
@@ -328,7 +345,7 @@ namespace lbAbi386 {
if (sz == 0) {
args[i] = lb_arg_type_ignore(t);
} else {
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval"));
args[i] = lb_arg_type_indirect(t, nullptr);
}
} else {
args[i] = non_struct(c, t, false);
@@ -348,7 +365,7 @@ namespace lbAbi386 {
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
}
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret");
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
}
return non_struct(c, return_type, true);
@@ -419,8 +436,14 @@ namespace lbAbiAmd64SysV {
switch (reg_class) {
case RegClass_SSEFs:
case RegClass_SSEFv:
case RegClass_SSEDs:
case RegClass_SSEDv:
return true;
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
return true;
}
return false;
}
@@ -437,11 +460,10 @@ namespace lbAbiAmd64SysV {
Amd64TypeAttribute_StructRect,
};
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
void fixup(LLVMTypeRef t, Array<RegClass> *cls);
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind);
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
Array<RegClass> classify(LLVMTypeRef t);
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
@@ -452,11 +474,11 @@ namespace lbAbiAmd64SysV {
ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal);
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
}
if (return_is_defined) {
ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect);
ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect, calling_convention);
} else {
ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
}
@@ -493,7 +515,7 @@ namespace lbAbiAmd64SysV {
return false;
}
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) {
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
if (is_register(type)) {
LLVMAttributeRef attribute = nullptr;
if (type == LLVMInt1TypeInContext(c)) {
@@ -506,9 +528,12 @@ namespace lbAbiAmd64SysV {
if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) {
attribute = lb_create_enum_attribute(c, "byval");
if (!is_calling_convention_odin(calling_convention)) {
return lb_arg_type_indirect_byval(c, type);
}
attribute = nullptr;
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
attribute = lb_create_enum_attribute(c, "sret");
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
}
return lb_arg_type_indirect(type, attribute);
} else {
@@ -538,30 +563,48 @@ namespace lbAbiAmd64SysV {
return reg_classes;
}
void unify(Array<RegClass> *cls, i64 i, RegClass newv) {
RegClass &oldv = (*cls)[i];
void unify(Array<RegClass> *cls, i64 i, RegClass const newv) {
RegClass const oldv = (*cls)[i];
if (oldv == newv) {
return;
} else if (oldv == RegClass_NoClass) {
oldv = newv;
}
RegClass to_write = newv;
if (oldv == RegClass_NoClass) {
to_write = newv;
} else if (newv == RegClass_NoClass) {
return;
} else if (oldv == RegClass_Memory || newv == RegClass_Memory) {
return;
} else if (oldv == RegClass_Int || newv == RegClass_Int) {
return;
} else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 ||
newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
oldv = RegClass_Memory;
} else {
oldv = newv;
to_write = RegClass_Memory;
} else if (oldv == RegClass_Int || newv == RegClass_Int) {
to_write = RegClass_Int;
} else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87) {
to_write = RegClass_Memory;
} else if (newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
to_write = RegClass_Memory;
} else if (newv == RegClass_SSEUp) {
switch (oldv) {
case RegClass_SSEFv:
case RegClass_SSEFs:
case RegClass_SSEDv:
case RegClass_SSEDs:
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
return;
}
}
(*cls)[i] = to_write;
}
void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
i64 i = 0;
i64 e = cls->count;
if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) {
if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) ||
lb_is_type_kind(t, LLVMArrayTypeKind) ||
lb_is_type_kind(t, LLVMVectorTypeKind))) {
RegClass &oldv = (*cls)[i];
if (is_sse(oldv)) {
for (i++; i < e; i++) {
@@ -605,8 +648,8 @@ namespace lbAbiAmd64SysV {
unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
unsigned len = 1;
for (isize i = offset+1; i < reg_classes.count; i++) {
if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) {
for (isize i = offset; i < reg_classes.count; i++) {
if (reg_classes[i] != RegClass_SSEUp) {
break;
}
len++;
@@ -617,7 +660,7 @@ namespace lbAbiAmd64SysV {
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
for_array(i, reg_classes) {
for (isize i = 0; i < reg_classes.count; /**/) {
RegClass reg_class = reg_classes[i];
switch (reg_class) {
case RegClass_Int:
@@ -659,7 +702,7 @@ namespace lbAbiAmd64SysV {
break;
}
unsigned vec_len = llvec_len(reg_classes, i);
unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type);
i += vec_len;
@@ -675,9 +718,9 @@ namespace lbAbiAmd64SysV {
default:
GB_PANIC("Unhandled RegClass");
}
i += 1;
}
GB_ASSERT(types.count != 0);
if (types.count == 1) {
return types[0];
}
@@ -778,26 +821,6 @@ namespace lbAbiAmd64SysV {
}
}
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
LLVMTypeKind kind = LLVMGetTypeKind(t);
if (kind == LLVMStructTypeKind) {
i64 sz = lb_sizeof(t);
if (sz == 0) {
args[i] = lb_arg_type_ignore(t);
} else {
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval"));
}
} else {
args[i] = non_struct(c, t);
}
}
return args;
}
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
@@ -809,7 +832,7 @@ namespace lbAbiAmd64SysV {
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
}
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret");
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr);
} else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
@@ -959,7 +982,7 @@ namespace lbAbiArm64 {
}
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret");
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
return lb_arg_type_indirect(type, attr);
}
}
+864 -672
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -215,6 +215,12 @@ enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
};
struct lbCopyElisionHint {
lbValue ptr;
Ast * ast;
bool used;
};
struct lbProcedure {
u32 flags;
u16 state_flags;
@@ -260,9 +266,7 @@ struct lbProcedure {
LLVMMetadataRef debug_info;
lbValue return_ptr_hint_value;
Ast * return_ptr_hint_ast;
bool return_ptr_hint_used;
lbCopyElisionHint copy_elision_hint;
};
@@ -276,6 +280,7 @@ String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
@@ -412,6 +417,7 @@ lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type);
lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);
void lb_mem_zero_addr(lbProcedure *p, LLVMValueRef ptr, Type *type);
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
+127 -4
View File
@@ -1,7 +1,25 @@
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level);
void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level);
LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) {
lbModule *m = cast(lbModule *)user_data;
if (m == nullptr) {
return false;
}
if (value == nullptr) {
return false;
}
return LLVMIsAAllocaInst(value) != nullptr;
}
void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
if (false && optimization_level == 0 && m->debug_builder) {
// LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
}
}
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
LLVMAddPromoteMemoryToRegisterPass(fpm);
@@ -15,11 +33,13 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
LLVMAddCFGSimplificationPass(fpm);
}
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (ignore_memcpy_pass) {
lb_basic_populate_function_pass_manager(fpm);
return;
@@ -57,11 +77,13 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
#endif
}
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) {
void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm);
@@ -226,3 +248,104 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
LLVMAddCFGSimplificationPass(mpm);
}
void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
isize removal_count = 0;
isize pass_count = 0;
isize const max_pass_count = 10;
isize original_instruction_count = 0;
// Custom remove dead instruction pass
for (; pass_count < max_pass_count; pass_count++) {
bool was_dead_instructions = false;
// NOTE(bill): Iterate backwards
// reduces the number of passes as things later on will depend on things previously
for (LLVMBasicBlockRef block = LLVMGetLastBasicBlock(p->value);
block != nullptr;
block = LLVMGetPreviousBasicBlock(block)) {
// NOTE(bill): Iterate backwards
// reduces the number of passes as things later on will depend on things previously
for (LLVMValueRef instr = LLVMGetLastInstruction(block);
instr != nullptr;
/**/) {
if (pass_count == 0) {
original_instruction_count += 1;
}
LLVMValueRef curr_instr = instr;
instr = LLVMGetPreviousInstruction(instr);
LLVMUseRef first_use = LLVMGetFirstUse(curr_instr);
if (first_use != nullptr) {
continue;
}
if (LLVMTypeOf(curr_instr) == nullptr) {
continue;
}
// NOTE(bill): Explicit instructions are set here because some instructions could have side effects
switch (LLVMGetInstructionOpcode(curr_instr)) {
case LLVMFNeg:
case LLVMAdd:
case LLVMFAdd:
case LLVMSub:
case LLVMFSub:
case LLVMMul:
case LLVMFMul:
case LLVMUDiv:
case LLVMSDiv:
case LLVMFDiv:
case LLVMURem:
case LLVMSRem:
case LLVMFRem:
case LLVMShl:
case LLVMLShr:
case LLVMAShr:
case LLVMAnd:
case LLVMOr:
case LLVMXor:
case LLVMAlloca:
case LLVMLoad:
case LLVMGetElementPtr:
case LLVMTrunc:
case LLVMZExt:
case LLVMSExt:
case LLVMFPToUI:
case LLVMFPToSI:
case LLVMUIToFP:
case LLVMSIToFP:
case LLVMFPTrunc:
case LLVMFPExt:
case LLVMPtrToInt:
case LLVMIntToPtr:
case LLVMBitCast:
case LLVMAddrSpaceCast:
case LLVMICmp:
case LLVMFCmp:
case LLVMSelect:
case LLVMExtractElement:
case LLVMShuffleVector:
case LLVMExtractValue:
removal_count += 1;
LLVMInstructionEraseFromParent(curr_instr);
was_dead_instructions = true;
break;
}
}
}
if (!was_dead_instructions) {
break;
}
}
}
void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
LLVMRunFunctionPassManager(fpm, p->value);
// NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM
// which means we cannot rely upon it
// This is also useful for read the .ll for debug purposes because a lot of instructions
// are not removed
lb_run_remove_dead_instruction_pass(p);
}
+7 -1
View File
@@ -621,6 +621,7 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch,
@@ -741,6 +742,7 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
#if defined(GB_SYSTEM_WINDOWS)
add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
@@ -1320,6 +1322,10 @@ bool parse_build_flags(Array<String> args) {
}
break;
case BuildFlag_VerboseErrors:
build_context.show_error_line = true;
break;
#if defined(GB_SYSTEM_WINDOWS)
case BuildFlag_IgnoreVsSearch:
GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1719,7 +1725,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Disables automatic linking with the C Run Time");
print_usage_line(0, "");
print_usage_line(1, "-use-lld");
print_usage_line(1, "-lld");
print_usage_line(2, "Use the LLD linker rather than the default");
print_usage_line(0, "");
+126 -143
View File
@@ -1,109 +1,4 @@
Token ast_token(Ast *node) {
switch (node->kind) {
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.token;
case Ast_ProcLit: return ast_token(node->ProcLit.type);
case Ast_CompoundLit:
if (node->CompoundLit.type != nullptr) {
return ast_token(node->CompoundLit.type);
}
return node->CompoundLit.open;
case Ast_TagExpr: return node->TagExpr.token;
case Ast_BadExpr: return node->BadExpr.begin;
case Ast_UnaryExpr: return node->UnaryExpr.op;
case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left);
case Ast_ParenExpr: return node->ParenExpr.open;
case Ast_CallExpr: return ast_token(node->CallExpr.proc);
case Ast_SelectorExpr:
if (node->SelectorExpr.selector != nullptr) {
return ast_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case Ast_SelectorCallExpr:
if (node->SelectorCallExpr.expr != nullptr) {
return ast_token(node->SelectorCallExpr.expr);
}
return node->SelectorCallExpr.token;
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector != nullptr) {
return ast_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.open;
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
case Ast_TypeCast: return node->TypeCast.token;
case Ast_AutoCast: return node->AutoCast.token;
case Ast_InlineAsmExpr: return node->InlineAsmExpr.token;
case Ast_BadStmt: return node->BadStmt.begin;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_token(node->ExprStmt.expr);
case Ast_TagStmt: return node->TagStmt.token;
case Ast_AssignStmt: return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.open;
case Ast_IfStmt: return node->IfStmt.token;
case Ast_WhenStmt: return node->WhenStmt.token;
case Ast_ReturnStmt: return node->ReturnStmt.token;
case Ast_ForStmt: return node->ForStmt.token;
case Ast_RangeStmt: return node->RangeStmt.token;
case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token;
case Ast_CaseClause: return node->CaseClause.token;
case Ast_SwitchStmt: return node->SwitchStmt.token;
case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token;
case Ast_DeferStmt: return node->DeferStmt.token;
case Ast_BranchStmt: return node->BranchStmt.token;
case Ast_UsingStmt: return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.begin;
case Ast_Label: return node->Label.token;
case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]);
case Ast_PackageDecl: return node->PackageDecl.token;
case Ast_ImportDecl: return node->ImportDecl.token;
case Ast_ForeignImportDecl: return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token;
case Ast_Attribute:
return node->Attribute.token;
case Ast_Field:
if (node->Field.names.count > 0) {
return ast_token(node->Field.names[0]);
}
return ast_token(node->Field.type);
case Ast_FieldList:
return node->FieldList.token;
case Ast_TypeidType: return node->TypeidType.token;
case Ast_HelperType: return node->HelperType.token;
case Ast_DistinctType: return node->DistinctType.token;
case Ast_PolyType: return node->PolyType.token;
case Ast_ProcType: return node->ProcType.token;
case Ast_RelativeType: return ast_token(node->RelativeType.tag);
case Ast_PointerType: return node->PointerType.token;
case Ast_ArrayType: return node->ArrayType.token;
case Ast_DynamicArrayType: return node->DynamicArrayType.token;
case Ast_StructType: return node->StructType.token;
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
case Ast_MapType: return node->MapType.token;
}
return empty_token;
}
#include "parser_pos.cpp"
Token token_end_of_line(AstFile *f, Token tok) {
u8 const *start = f->tokenizer.start + tok.pos.offset;
@@ -115,6 +10,48 @@ Token token_end_of_line(AstFile *f, Token tok) {
return tok;
}
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) {
AstFile *file = get_ast_file_from_id(pos.file_id);
if (file == nullptr) {
return nullptr;
}
isize offset = pos.offset;
u8 *start = file->tokenizer.start;
u8 *end = file->tokenizer.end;
isize len = end-start;
if (len < offset) {
return nullptr;
}
u8 *pos_offset = start+offset;
u8 *line_start = pos_offset;
u8 *line_end = pos_offset;
while (line_start >= start) {
if (*line_start == '\n') {
line_start += 1;
break;
}
line_start -= 1;
}
while (line_end < end) {
if (*line_end == '\n') {
line_end -= 1;
break;
}
line_end += 1;
}
String the_line = make_string(line_start, line_end-line_start);
the_line = string_trim_whitespace(the_line);
if (offset_) *offset_ = cast(i32)(pos_offset - the_line.text);
return gb_string_make_length(heap_allocator(), the_line.text, the_line.len);
}
isize ast_node_size(AstKind kind) {
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
@@ -432,12 +369,15 @@ Ast *clone_ast(Ast *node) {
void error(Ast *node, char const *fmt, ...) {
Token token = {};
TokenPos end_pos = {};
if (node != nullptr) {
token = ast_token(node);
end_pos = ast_end_pos(node);
}
va_list va;
va_start(va, fmt);
error_va(token, fmt, va);
error_va(token.pos, end_pos, fmt, va);
va_end(va);
if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1;
@@ -451,7 +391,7 @@ void error_no_newline(Ast *node, char const *fmt, ...) {
}
va_list va;
va_start(va, fmt);
error_no_newline_va(token, fmt, va);
error_no_newline_va(token.pos, fmt, va);
va_end(va);
if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1;
@@ -459,16 +399,28 @@ void error_no_newline(Ast *node, char const *fmt, ...) {
}
void warning(Ast *node, char const *fmt, ...) {
Token token = {};
TokenPos end_pos = {};
if (node != nullptr) {
token = ast_token(node);
end_pos = ast_end_pos(node);
}
va_list va;
va_start(va, fmt);
warning_va(ast_token(node), fmt, va);
warning_va(token.pos, end_pos, fmt, va);
va_end(va);
}
void syntax_error(Ast *node, char const *fmt, ...) {
Token token = {};
TokenPos end_pos = {};
if (node != nullptr) {
token = ast_token(node);
end_pos = ast_end_pos(node);
}
va_list va;
va_start(va, fmt);
syntax_error_va(ast_token(node), fmt, va);
syntax_error_va(token.pos, end_pos, fmt, va);
va_end(va);
if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1;
@@ -640,7 +592,7 @@ Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
return result;
}
Ast *ast_basic_directive(AstFile *f, Token token, String name) {
Ast *ast_basic_directive(AstFile *f, Token token, Token name) {
Ast *result = alloc_ast_node(f, Ast_BasicDirective);
result->BasicDirective.token = token;
result->BasicDirective.name = name;
@@ -1344,6 +1296,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
bool is_token_range(TokenKind kind) {
switch (kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
return true;
}
@@ -1574,6 +1527,10 @@ void expect_semicolon(AstFile *f, Ast *s) {
return;
}
if (f->curr_token.kind == Token_EOF) {
return;
}
if (s != nullptr) {
bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0;
if (insert_semi) {
@@ -1994,35 +1951,28 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token name = expect_token(f, Token_Ident);
if (name.string == "type") {
return ast_helper_type(f, token, parse_type(f));
} /* else if (name.string == "no_deferred") {
operand = parse_expr(f, false);
if (unparen_expr(operand)->kind != Ast_CallExpr) {
syntax_error(operand, "#no_deferred can only be applied to procedure calls");
operand = ast_bad_expr(f, token, f->curr_token);
}
operand->state_flags |= StateFlag_no_deferred;
} */ else if (name.string == "file") {
return ast_basic_directive(f, token, name.string);
} else if (name.string == "line") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "file") {
return ast_basic_directive(f, token, name);
} else if (name.string == "line") { return ast_basic_directive(f, token, name);
} else if (name.string == "procedure") { return ast_basic_directive(f, token, name);
} else if (name.string == "caller_location") { return ast_basic_directive(f, token, name);
} else if (name.string == "location") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag);
} else if (name.string == "load") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag);
} else if (name.string == "assert") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag);
} else if (name.string == "defined") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag);
} else if (name.string == "config") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag);
} else if (name.string == "soa" || name.string == "simd") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
@@ -2034,7 +1984,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
}
return original_type;
} else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
@@ -2046,6 +1996,10 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return original_type;
} else if (name.string == "bounds_check") {
Ast *operand = parse_expr(f, lhs);
if (operand == nullptr) {
syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string));
return nullptr;
}
operand->state_flags |= StateFlag_bounds_check;
if ((operand->state_flags & StateFlag_no_bounds_check) != 0) {
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
@@ -2053,13 +2007,17 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return operand;
} else if (name.string == "no_bounds_check") {
Ast *operand = parse_expr(f, lhs);
if (operand == nullptr) {
syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string));
return nullptr;
}
operand->state_flags |= StateFlag_no_bounds_check;
if ((operand->state_flags & StateFlag_bounds_check) != 0) {
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
}
return operand;
} else if (name.string == "relative") {
Ast *tag = ast_basic_directive(f, token, name.string);
Ast *tag = ast_basic_directive(f, token, name);
tag = parse_call_expr(f, tag);
Ast *type = parse_type(f);
return ast_relative_type(f, tag, type);
@@ -2314,7 +2272,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
f->expr_level = prev_level;
}
skip_possible_newline_for_literal(f);
Token open = expect_token_after(f, Token_OpenBrace, "struct");
isize name_count = 0;
@@ -2674,6 +2632,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
// NOTE(bill): Do not err yet
case Token_Colon:
@@ -2685,6 +2644,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
syntax_error(f->curr_token, "Expected a colon, not a range");
/* fallthrough */
@@ -2723,6 +2683,16 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
}
break;
case Token_Increment:
case Token_Decrement:
if (!lhs) {
Token token = advance_token(f);
syntax_error(token, "Postfix '%.*s' operator is not supported", LIT(token.string));
} else {
loop = false;
}
break;
default:
loop = false;
break;
@@ -2753,16 +2723,26 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) {
return ast_auto_cast(f, token, expr);
}
case Token_Add:
case Token_Sub:
case Token_Not:
case Token_Xor:
case Token_And: {
case Token_And:
case Token_Not: {
Token token = advance_token(f);
Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr);
}
case Token_Increment:
case Token_Decrement: {
Token token = advance_token(f);
syntax_error(token, "Unary '%.*s' operator is not supported", LIT(token.string));
Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr);
}
case Token_Period: {
Token token = expect_token(f, Token_Period);
Ast *ident = parse_ident(f);
@@ -2791,6 +2771,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
case Token_when:
return 1;
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
if (!f->allow_range) {
return 0;
@@ -3152,6 +3133,13 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) {
return ast_bad_stmt(f, token, f->curr_token);
}
switch (token.kind) {
case Token_Increment:
case Token_Decrement:
advance_token(f);
syntax_error(token, "Postfix '%.*s' statement is not supported", LIT(token.string));
break;
}
#if 0
@@ -3898,12 +3886,6 @@ Ast *parse_return_stmt(AstFile *f) {
while (f->curr_token.kind != Token_Semicolon) {
Ast *arg = parse_expr(f, false);
// if (f->curr_token.kind == Token_Eq) {
// Token eq = expect_token(f, Token_Eq);
// Ast *value = parse_value(f);
// arg = ast_field_value(f, arg, value, eq);
// }
array_add(&results, arg);
if (f->curr_token.kind != Token_Comma ||
f->curr_token.kind == Token_EOF) {
@@ -4024,7 +4006,7 @@ Ast *parse_case_clause(AstFile *f, bool is_type) {
}
f->allow_range = prev_allow_range;
f->allow_in_expr = prev_allow_in_expr;
expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
expect_token(f, Token_Colon);
Array<Ast *> stmts = parse_stmt_list(f);
return ast_case_clause(f, token, list, stmts);
@@ -4482,10 +4464,10 @@ Ast *parse_stmt(AstFile *f) {
}
return s;
} else if (tag == "assert") {
Ast *t = ast_basic_directive(f, hash_token, tag);
Ast *t = ast_basic_directive(f, hash_token, name);
return ast_expr_stmt(f, parse_call_expr(f, t));
} else if (tag == "panic") {
Ast *t = ast_basic_directive(f, hash_token, tag);
Ast *t = ast_basic_directive(f, hash_token, name);
return ast_expr_stmt(f, parse_call_expr(f, t));
} else if (name.string == "force_inline" ||
name.string == "force_no_inline") {
@@ -4572,6 +4554,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
GB_ASSERT(f != nullptr);
f->fullpath = string_trim_whitespace(fullpath); // Just in case
set_file_path_string(f->id, fullpath);
set_ast_file_from_id(f->id, f);
if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
return ParseFile_WrongExtension;
}
+24 -19
View File
@@ -7,22 +7,21 @@ struct AstFile;
struct AstPackage;
enum AddressingMode {
Addressing_Invalid, // invalid addressing mode
Addressing_NoValue, // no value (void in C)
Addressing_Value, // computed value (rvalue)
Addressing_Context, // context value
Addressing_Variable, // addressable variable (lvalue)
Addressing_Constant, // constant
Addressing_Type, // type
Addressing_Builtin, // built-in procedure
Addressing_ProcGroup, // procedure group (overloaded procedure)
Addressing_MapIndex, // map index expression -
// lhs: acts like a Variable
// rhs: acts like OptionalOk
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check)
Addressing_SoaVariable, // Struct-Of-Arrays indexed variable
Addressing_Invalid = 0, // invalid addressing mode
Addressing_NoValue = 1, // no value (void in C)
Addressing_Value = 2, // computed value (rvalue)
Addressing_Context = 3, // context value
Addressing_Variable = 4, // addressable variable (lvalue)
Addressing_Constant = 5, // constant
Addressing_Type = 6, // type
Addressing_Builtin = 7, // built-in procedure
Addressing_ProcGroup = 8, // procedure group (overloaded procedure)
Addressing_MapIndex = 9, // map index expression -
// lhs: acts like a Variable
// rhs: acts like OptionalOk
Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check)
Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable
Addressing_AtomOpAssign, // Specialized for custom atom operations for assignments
};
struct TypeAndValue {
@@ -287,8 +286,8 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Token token; \
}) \
AST_KIND(BasicDirective, "basic directive", struct { \
Token token; \
String name; \
Token token; \
Token name; \
}) \
AST_KIND(Ellipsis, "ellipsis", struct { \
Token token; \
@@ -325,7 +324,7 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \
AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \
AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \
AST_KIND(DerefExpr, "dereference expression", struct { Ast *expr; Token op; }) \
AST_KIND(SliceExpr, "slice expression", struct { \
Ast *expr; \
Token open, close; \
@@ -345,7 +344,13 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; Type *type_hint; }) \
AST_KIND(TypeAssertion, "type assertion", struct { \
Ast *expr; \
Token dot; \
Ast *type; \
Type *type_hint; \
bool ignores[2]; \
}) \
AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \
AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
+340
View File
@@ -0,0 +1,340 @@
Token ast_token(Ast *node) {
switch (node->kind) {
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.token;
case Ast_ProcLit: return ast_token(node->ProcLit.type);
case Ast_CompoundLit:
if (node->CompoundLit.type != nullptr) {
return ast_token(node->CompoundLit.type);
}
return node->CompoundLit.open;
case Ast_TagExpr: return node->TagExpr.token;
case Ast_BadExpr: return node->BadExpr.begin;
case Ast_UnaryExpr: return node->UnaryExpr.op;
case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left);
case Ast_ParenExpr: return node->ParenExpr.open;
case Ast_CallExpr: return ast_token(node->CallExpr.proc);
case Ast_SelectorExpr:
if (node->SelectorExpr.selector != nullptr) {
return ast_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case Ast_SelectorCallExpr:
if (node->SelectorCallExpr.expr != nullptr) {
return ast_token(node->SelectorCallExpr.expr);
}
return node->SelectorCallExpr.token;
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector != nullptr) {
return ast_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.open;
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
case Ast_TypeCast: return node->TypeCast.token;
case Ast_AutoCast: return node->AutoCast.token;
case Ast_InlineAsmExpr: return node->InlineAsmExpr.token;
case Ast_BadStmt: return node->BadStmt.begin;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_token(node->ExprStmt.expr);
case Ast_TagStmt: return node->TagStmt.token;
case Ast_AssignStmt: return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.open;
case Ast_IfStmt: return node->IfStmt.token;
case Ast_WhenStmt: return node->WhenStmt.token;
case Ast_ReturnStmt: return node->ReturnStmt.token;
case Ast_ForStmt: return node->ForStmt.token;
case Ast_RangeStmt: return node->RangeStmt.token;
case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token;
case Ast_CaseClause: return node->CaseClause.token;
case Ast_SwitchStmt: return node->SwitchStmt.token;
case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token;
case Ast_DeferStmt: return node->DeferStmt.token;
case Ast_BranchStmt: return node->BranchStmt.token;
case Ast_UsingStmt: return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.begin;
case Ast_Label: return node->Label.token;
case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]);
case Ast_PackageDecl: return node->PackageDecl.token;
case Ast_ImportDecl: return node->ImportDecl.token;
case Ast_ForeignImportDecl: return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token;
case Ast_Attribute:
return node->Attribute.token;
case Ast_Field:
if (node->Field.names.count > 0) {
return ast_token(node->Field.names[0]);
}
return ast_token(node->Field.type);
case Ast_FieldList:
return node->FieldList.token;
case Ast_TypeidType: return node->TypeidType.token;
case Ast_HelperType: return node->HelperType.token;
case Ast_DistinctType: return node->DistinctType.token;
case Ast_PolyType: return node->PolyType.token;
case Ast_ProcType: return node->ProcType.token;
case Ast_RelativeType: return ast_token(node->RelativeType.tag);
case Ast_PointerType: return node->PointerType.token;
case Ast_ArrayType: return node->ArrayType.token;
case Ast_DynamicArrayType: return node->DynamicArrayType.token;
case Ast_StructType: return node->StructType.token;
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
case Ast_MapType: return node->MapType.token;
}
return empty_token;
}
TokenPos token_pos_end(Token const &token) {
TokenPos pos = token.pos;
pos.offset += cast(i32)token.string.len;
for (isize i = 0; i < token.string.len; i++) {
// TODO(bill): This assumes ASCII
char c = token.string[i];
if (c == '\n') {
pos.line += 1;
pos.column = 1;
} else {
pos.column += 1;
}
}
return pos;
}
Token ast_end_token(Ast *node) {
GB_ASSERT(node != nullptr);
switch (node->kind) {
case Ast_Invalid:
return empty_token;
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.close;
case Ast_ProcLit:
if (node->ProcLit.body) {
return ast_end_token(node->ProcLit.body);
}
return ast_end_token(node->ProcLit.type);
case Ast_CompoundLit:
return node->CompoundLit.close;
case Ast_BadExpr: return node->BadExpr.end;
case Ast_TagExpr:
if (node->TagExpr.expr) {
return ast_end_token(node->TagExpr.expr);
}
return node->TagExpr.name;
case Ast_UnaryExpr: return ast_end_token(node->UnaryExpr.expr);
case Ast_BinaryExpr: return ast_end_token(node->BinaryExpr.right);
case Ast_ParenExpr: return node->ParenExpr.close;
case Ast_CallExpr: return node->CallExpr.close;
case Ast_SelectorExpr:
return ast_end_token(node->SelectorExpr.selector);
case Ast_SelectorCallExpr:
return ast_end_token(node->SelectorCallExpr.call);
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector) {
return ast_end_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.close;
case Ast_SliceExpr: return node->SliceExpr.close;
case Ast_Ellipsis:
if (node->Ellipsis.expr) {
return ast_end_token(node->Ellipsis.expr);
}
return node->Ellipsis.token;
case Ast_FieldValue: return ast_end_token(node->FieldValue.value);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
case Ast_TypeAssertion: return ast_end_token(node->TypeAssertion.type);
case Ast_TypeCast: return ast_end_token(node->TypeCast.expr);
case Ast_AutoCast: return ast_end_token(node->AutoCast.expr);
case Ast_InlineAsmExpr: return node->InlineAsmExpr.close;
case Ast_BadStmt: return node->BadStmt.end;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_end_token(node->ExprStmt.expr);
case Ast_TagStmt: return ast_end_token(node->TagStmt.stmt);
case Ast_AssignStmt:
if (node->AssignStmt.rhs.count > 0) {
return ast_end_token(node->AssignStmt.rhs[node->AssignStmt.rhs.count-1]);
}
return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.close;
case Ast_IfStmt:
if (node->IfStmt.else_stmt) {
return ast_end_token(node->IfStmt.else_stmt);
}
return ast_end_token(node->IfStmt.body);
case Ast_WhenStmt:
if (node->WhenStmt.else_stmt) {
return ast_end_token(node->WhenStmt.else_stmt);
}
return ast_end_token(node->WhenStmt.body);
case Ast_ReturnStmt:
if (node->ReturnStmt.results.count > 0) {
return ast_end_token(node->ReturnStmt.results[node->ReturnStmt.results.count-1]);
}
return node->ReturnStmt.token;
case Ast_ForStmt: return ast_end_token(node->ForStmt.body);
case Ast_RangeStmt: return ast_end_token(node->RangeStmt.body);
case Ast_UnrollRangeStmt: return ast_end_token(node->UnrollRangeStmt.body);
case Ast_CaseClause:
if (node->CaseClause.stmts.count) {
return ast_end_token(node->CaseClause.stmts[node->CaseClause.stmts.count-1]);
} else if (node->CaseClause.list.count) {
return ast_end_token(node->CaseClause.list[node->CaseClause.list.count-1]);
}
return node->CaseClause.token;
case Ast_SwitchStmt: return ast_end_token(node->SwitchStmt.body);
case Ast_TypeSwitchStmt: return ast_end_token(node->TypeSwitchStmt.body);
case Ast_DeferStmt: return ast_end_token(node->DeferStmt.stmt);
case Ast_BranchStmt:
if (node->BranchStmt.label) {
return ast_end_token(node->BranchStmt.label);
}
return node->BranchStmt.token;
case Ast_UsingStmt:
if (node->UsingStmt.list.count > 0) {
return ast_end_token(node->UsingStmt.list[node->UsingStmt.list.count-1]);
}
return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.end;
case Ast_Label:
if (node->Label.name) {
return ast_end_token(node->Label.name);
}
return node->Label.token;
case Ast_ValueDecl:
if (node->ValueDecl.values.count > 0) {
return ast_end_token(node->ValueDecl.values[node->ValueDecl.values.count-1]);
}
if (node->ValueDecl.type) {
return ast_end_token(node->ValueDecl.type);
}
if (node->ValueDecl.names.count > 0) {
return ast_end_token(node->ValueDecl.names[node->ValueDecl.names.count-1]);
}
return {};
case Ast_PackageDecl: return node->PackageDecl.name;
case Ast_ImportDecl: return node->ImportDecl.relpath;
case Ast_ForeignImportDecl:
if (node->ForeignImportDecl.filepaths.count > 0) {
return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1];
}
if (node->ForeignImportDecl.library_name.kind != Token_Invalid) {
return node->ForeignImportDecl.library_name;
}
return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl:
return ast_end_token(node->ForeignBlockDecl.body);
case Ast_Attribute:
if (node->Attribute.close.kind != Token_Invalid) {
return node->Attribute.close;
}
return ast_end_token(node->Attribute.elems[node->Attribute.elems.count-1]);
case Ast_Field:
if (node->Field.tag.kind != Token_Invalid) {
return node->Field.tag;
}
if (node->Field.default_value) {
return ast_end_token(node->Field.default_value);
}
if (node->Field.type) {
return ast_end_token(node->Field.type);
}
return ast_end_token(node->Field.names[node->Field.names.count-1]);
case Ast_FieldList:
if (node->FieldList.list.count > 0) {
return ast_end_token(node->FieldList.list[node->FieldList.list.count-1]);
}
return node->FieldList.token;
case Ast_TypeidType:
if (node->TypeidType.specialization) {
return ast_end_token(node->TypeidType.specialization);
}
return node->TypeidType.token;
case Ast_HelperType: return ast_end_token(node->HelperType.type);
case Ast_DistinctType: return ast_end_token(node->DistinctType.type);
case Ast_PolyType:
if (node->PolyType.specialization) {
return ast_end_token(node->PolyType.specialization);
}
return ast_end_token(node->PolyType.type);
case Ast_ProcType:
if (node->ProcType.results) {
return ast_end_token(node->ProcType.results);
}
if (node->ProcType.params) {
return ast_end_token(node->ProcType.params);
}
return node->ProcType.token;
case Ast_RelativeType:
return ast_end_token(node->RelativeType.type);
case Ast_PointerType: return ast_end_token(node->PointerType.type);
case Ast_ArrayType: return ast_end_token(node->ArrayType.elem);
case Ast_DynamicArrayType: return ast_end_token(node->DynamicArrayType.elem);
case Ast_StructType:
if (node->StructType.fields.count > 0) {
return ast_end_token(node->StructType.fields[node->StructType.fields.count-1]);
}
return node->StructType.token;
case Ast_UnionType:
if (node->UnionType.variants.count > 0) {
return ast_end_token(node->UnionType.variants[node->UnionType.variants.count-1]);
}
return node->UnionType.token;
case Ast_EnumType:
if (node->EnumType.fields.count > 0) {
return ast_end_token(node->EnumType.fields[node->EnumType.fields.count-1]);
}
if (node->EnumType.base_type) {
return ast_end_token(node->EnumType.base_type);
}
return node->EnumType.token;
case Ast_BitSetType:
if (node->BitSetType.underlying) {
return ast_end_token(node->BitSetType.underlying);
}
return ast_end_token(node->BitSetType.elem);
case Ast_MapType: return ast_end_token(node->MapType.value);
}
return empty_token;
}
TokenPos ast_end_pos(Ast *node) {
return token_pos_end(ast_end_token(node));
}
+170 -51
View File
@@ -51,8 +51,10 @@ TOKEN_KIND(Token__AssignOpBegin, ""), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, ""), \
TOKEN_KIND(Token_ArrowRight, "->"), \
TOKEN_KIND(Token_Undef, "---"), \
TOKEN_KIND(Token_Increment, "++"), \
TOKEN_KIND(Token_Decrement, "--"), \
TOKEN_KIND(Token_ArrowRight,"->"), \
TOKEN_KIND(Token_Undef, "---"), \
\
TOKEN_KIND(Token__ComparisonBegin, ""), \
TOKEN_KIND(Token_CmpEq, "=="), \
@@ -74,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
TOKEN_KIND(Token_RangeFull, "..="), \
TOKEN_KIND(Token_RangeHalf, "..<"), \
TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, ""), \
@@ -185,9 +188,11 @@ void init_keyword_hash_table(void) {
GB_ASSERT(max_keyword_size < 16);
}
gb_global Array<String> global_file_path_strings; // index is file id
gb_global Array<String> global_file_path_strings; // index is file id
gb_global Array<struct AstFile *> global_files; // index is file id
String get_file_path_string(i32 index);
String get_file_path_string(i32 index);
struct AstFile *get_ast_file_from_id(i32 index);
struct TokenPos {
i32 file_id;
@@ -281,6 +286,7 @@ void init_global_error_collector(void) {
array_init(&global_error_collector.errors, heap_allocator());
array_init(&global_error_collector.error_buffer, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 4096);
array_init(&global_files, heap_allocator(), 4096);
}
@@ -302,6 +308,24 @@ bool set_file_path_string(i32 index, String const &path) {
return ok;
}
bool set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false;
GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex);
if (index >= global_files.count) {
array_resize(&global_files, index);
}
AstFile *prev = global_files[index];
if (prev == nullptr) {
global_files[index] = file;
ok = true;
}
gb_mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex);
@@ -315,6 +339,20 @@ String get_file_path_string(i32 index) {
return path;
}
AstFile *get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
gb_mutex_unlock(&global_error_collector.string_mutex);
return file;
}
void begin_error_block(void) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.in_block = true;
@@ -374,6 +412,8 @@ ErrorOutProc *error_out_va = default_error_out_va;
// NOTE: defined in build_settings.cpp
bool global_warnings_as_errors(void);
bool global_ignore_warnings(void);
bool show_error_line(void);
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
void error_out(char const *fmt, ...) {
va_list va;
@@ -383,17 +423,85 @@ void error_out(char const *fmt, ...) {
}
void error_va(Token token, char const *fmt, va_list va) {
bool show_error_on_line(TokenPos const &pos, TokenPos end) {
if (!show_error_line()) {
return false;
}
i32 offset = 0;
gbString the_line = get_file_line_as_string(pos, &offset);
defer (gb_string_free(the_line));
if (the_line != nullptr) {
String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
// TODO(bill): This assumes ASCII
enum {
MAX_LINE_LENGTH = 76,
MAX_TAB_WIDTH = 8,
ELLIPSIS_PADDING = 8
};
error_out("\n\t");
if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
i32 const half_width = MAX_LINE_LENGTH/2;
i32 left = cast(i32)(offset);
i32 right = cast(i32)(line.len - offset);
left = gb_min(left, half_width);
right = gb_min(right, half_width);
line.text += offset-left;
line.len -= offset+right-left;
line = string_trim_whitespace(line);
offset = left + ELLIPSIS_PADDING/2;
error_out("... %.*s ...", LIT(line));
} else {
error_out("%.*s", LIT(line));
}
error_out("\n\t");
for (i32 i = 0; i < offset; i++) {
error_out(" ");
}
error_out("^");
if (end.file_id == pos.file_id) {
if (end.line > pos.line) {
for (i32 i = offset; i < line.len; i++) {
error_out("~");
}
} else if (end.line == pos.line && end.column > pos.column) {
i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
for (i32 i = 1; i < length-1; i++) {
error_out("~");
}
if (length > 1) {
error_out("^");
}
}
}
error_out("\n\n");
return true;
}
return false;
}
void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) {
if (pos.line == 0) {
error_out("Error: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) {
global_error_collector.prev = token.pos;
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s\n",
token_pos_to_string(token.pos),
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
gb_mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
@@ -401,22 +509,23 @@ void error_va(Token token, char const *fmt, va_list va) {
}
}
void warning_va(Token token, char const *fmt, va_list va) {
void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
error_va(token, fmt, va);
error_va(pos, end, fmt, va);
return;
}
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++;
if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) {
if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) {
global_error_collector.prev = token.pos;
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Warning: %s\n",
token_pos_to_string(token.pos),
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
}
}
gb_mutex_unlock(&global_error_collector.mutex);
@@ -429,16 +538,16 @@ void error_line_va(char const *fmt, va_list va) {
gb_mutex_unlock(&global_error_collector.mutex);
}
void error_no_newline_va(Token token, char const *fmt, va_list va) {
void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) {
if (pos.line == 0) {
error_out("Error: %s", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) {
global_error_collector.prev = token.pos;
} else if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s %s",
token_pos_to_string(token.pos),
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
}
gb_mutex_unlock(&global_error_collector.mutex);
@@ -448,16 +557,17 @@ void error_no_newline_va(Token token, char const *fmt, va_list va) {
}
void syntax_error_va(Token token, char const *fmt, va_list va) {
void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != token.pos) {
global_error_collector.prev = token.pos;
if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Syntax Error: %s\n",
token_pos_to_string(token.pos),
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
} else if (token.pos.line == 0) {
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
}
@@ -467,21 +577,22 @@ void syntax_error_va(Token token, char const *fmt, va_list va) {
}
}
void syntax_warning_va(Token token, char const *fmt, va_list va) {
void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) {
syntax_error_va(token, fmt, va);
syntax_error_va(pos, end, fmt, va);
return;
}
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++;
if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != token.pos) {
global_error_collector.prev = token.pos;
if (global_error_collector.prev != pos) {
global_error_collector.prev = pos;
error_out("%s Syntax Warning: %s\n",
token_pos_to_string(token.pos),
token_pos_to_string(pos),
gb_bprintf_va(fmt, va));
} else if (token.pos.line == 0) {
show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
}
}
@@ -490,17 +601,17 @@ void syntax_warning_va(Token token, char const *fmt, va_list va) {
void warning(Token token, char const *fmt, ...) {
void warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
warning_va(token, fmt, va);
warning_va(token.pos, {}, fmt, va);
va_end(va);
}
void error(Token token, char const *fmt, ...) {
void error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
error_va(token, fmt, va);
error_va(token.pos, {}, fmt, va);
va_end(va);
}
@@ -509,7 +620,7 @@ void error(TokenPos pos, char const *fmt, ...) {
va_start(va, fmt);
Token token = {};
token.pos = pos;
error_va(token, fmt, va);
error_va(pos, {}, fmt, va);
va_end(va);
}
@@ -521,26 +632,24 @@ void error_line(char const *fmt, ...) {
}
void syntax_error(Token token, char const *fmt, ...) {
void syntax_error(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(token, fmt, va);
syntax_error_va(token.pos, {}, fmt, va);
va_end(va);
}
void syntax_error(TokenPos pos, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
Token token = {};
token.pos = pos;
syntax_error_va(token, fmt, va);
syntax_error_va(pos, {}, fmt, va);
va_end(va);
}
void syntax_warning(Token token, char const *fmt, ...) {
void syntax_warning(Token const &token, char const *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_warning_va(token, fmt, va);
syntax_warning_va(token.pos, {}, fmt, va);
va_end(va);
}
@@ -652,13 +761,14 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) {
if (column < 1) {
column = 1;
}
Token token = {};
token.pos.file_id = t->curr_file_id;
token.pos.line = t->line_count;
token.pos.column = cast(i32)column;
TokenPos pos = {};
pos.file_id = t->curr_file_id;
pos.line = t->line_count;
pos.column = cast(i32)column;
pos.offset = cast(i32)(t->read_curr - t->start);
va_start(va, msg);
syntax_error_va(token, msg, va);
syntax_error_va(pos, {}, msg, va);
va_end(va);
t->error_count++;
@@ -670,11 +780,9 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) {
if (column < 1) {
column = 1;
}
Token token = {};
token.pos = pos;
va_start(va, msg);
syntax_error_va(token, msg, va);
syntax_error_va(pos, {}, msg, va);
va_end(va);
t->error_count++;
@@ -1202,6 +1310,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '<') {
advance_to_next_rune(t);
token->kind = Token_RangeHalf;
} else if (t->curr_rune == '=') {
advance_to_next_rune(t);
token->kind = Token_RangeFull;
}
} else if ('0' <= t->curr_rune && t->curr_rune <= '9') {
scan_number_to_token(t, token, true);
@@ -1287,6 +1398,10 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '=') {
advance_to_next_rune(t);
token->kind = Token_AddEq;
} else if (t->curr_rune == '+') {
advance_to_next_rune(t);
token->kind = Token_Increment;
insert_semicolon = true;
}
break;
case '-':
@@ -1298,6 +1413,10 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
advance_to_next_rune(t);
advance_to_next_rune(t);
token->kind = Token_Undef;
} else if (t->curr_rune == '-') {
advance_to_next_rune(t);
token->kind = Token_Decrement;
insert_semicolon = true;
} else if (t->curr_rune == '>') {
advance_to_next_rune(t);
token->kind = Token_ArrowRight;
-19
View File
@@ -128,21 +128,6 @@ enum StructSoaKind {
StructSoa_Dynamic = 3,
};
enum TypeAtomOpKind {
TypeAtomOp_Invalid,
TypeAtomOp_index_get,
TypeAtomOp_index_set,
TypeAtomOp_slice,
TypeAtomOp_index_get_ptr,
TypeAtomOp_COUNT,
};
struct TypeAtomOpTable {
Entity *op[TypeAtomOp_COUNT];
};
struct TypeStruct {
Array<Entity *> fields;
Array<String> tags;
@@ -156,8 +141,6 @@ struct TypeStruct {
i64 custom_align;
Entity * names;
TypeAtomOpTable *atom_op_table;
Type * soa_elem;
i64 soa_count;
StructSoaKind soa_kind;
@@ -180,8 +163,6 @@ struct TypeUnion {
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
TypeAtomOpTable *atom_op_table;
bool no_nil;
bool maybe;
bool is_polymorphic;
+216
View File
@@ -0,0 +1,216 @@
package flag
import "core:runtime"
import "core:strings"
import "core:reflect"
import "core:fmt"
import "core:mem"
import "core:strconv"
Flag_Error :: enum {
None,
No_Base_Struct,
Arg_Error,
Arg_Unsupported_Field_Type,
Arg_Not_Defined,
Arg_Non_Optional,
Value_Parse_Error,
Tag_Error,
}
Flag :: struct {
optional: bool,
type: ^runtime.Type_Info,
data: rawptr,
tag_ptr: rawptr,
parsed: bool,
}
Flag_Context :: struct {
seen_flags: map[string]Flag,
}
parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error {
using runtime;
args := args;
for true {
if len(args) == 0 {
return .None;
}
arg := args[0];
if len(arg) < 2 || arg[0] != '-' {
return .Arg_Error;
}
minus_count := 1;
if arg[1] == '-' {
minus_count += 1;
if len(arg) == 2 {
return .Arg_Error;
}
}
name := arg[minus_count:];
if len(name) == 0 {
return .Arg_Error;
}
args = args[1:];
assign_index := strings.index(name, "=");
value := "";
if assign_index > 0 {
value = name[assign_index + 1:];
name = name[0:assign_index];
}
flag := &ctx.seen_flags[name];
if flag == nil {
return .Arg_Not_Defined;
}
if reflect.is_boolean(flag.type) {
tmp := true;
mem.copy(flag.data, &tmp, flag.type.size);
flag.parsed = true;
continue;
} else
//must be in the next argument
if value == "" {
if len(args) == 0 {
return .Arg_Error;
}
value = args[0];
args = args[1:];
}
#partial switch _ in flag.type.variant {
case Type_Info_Integer:
if v, ok := strconv.parse_int(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
case Type_Info_String:
raw_string := cast(^mem.Raw_String)flag.data;
raw_string.data = strings.ptr_from_string(value);
raw_string.len = len(value);
case Type_Info_Float:
switch flag.type.size {
case 32:
if v, ok := strconv.parse_f32(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
case 64:
if v, ok := strconv.parse_f64(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
}
}
flag.parsed = true;
}
return .None;
}
reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error {
using runtime;
if !reflect.is_struct(type_info_of(v.id)) {
return .No_Base_Struct;
}
names := reflect.struct_field_names(v.id);
types := reflect.struct_field_types(v.id);
offsets := reflect.struct_field_offsets(v.id);
tags := reflect.struct_field_tags(v.id);
for name, i in names {
flag: Flag;
type := types[i];
if named_type, ok := type.variant.(Type_Info_Named); ok {
if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 {
flag.optional = true;
flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i]));
type = union_type.variants[0];
} else {
return .Arg_Unsupported_Field_Type;
}
}
#partial switch _ in type.variant {
case Type_Info_Integer, Type_Info_String, Type_Info_Boolean, Type_Info_Float:
flag.type = type;
flag.data = rawptr(uintptr(v.data) + uintptr(offsets[i]));
case:
return .Arg_Unsupported_Field_Type;
}
flag_name: string;
if value, ok := reflect.struct_tag_lookup(tags[i], "flag"); ok {
flag_name = cast(string)value;
} else {
return .Tag_Error;
}
ctx.seen_flags[flag_name] = flag;
}
return .None;
}
parse :: proc(v: any, args: []string) -> Flag_Error {
if v == nil {
return .None;
}
ctx: Flag_Context;
if res := reflect_args_structure(&ctx, v); res != .None {
return res;
}
if res := parse_args(&ctx, args); res != .None {
return res;
}
//validate that the required flags were actually set
for k, v in ctx.seen_flags {
if v.optional && v.parsed {
tag_value: i32 = 1;
mem.copy(v.tag_ptr, &tag_value, 4); //4 constant is probably not portable, but it works for me currently
} else if !v.parsed && !v.optional {
return .Arg_Non_Optional;
}
}
return .None;
}
usage :: proc(v: any) -> string {
return "failed";
}
+125
View File
@@ -0,0 +1,125 @@
package odinfmt
import "core:os"
import "core:odin/format"
import "core:fmt"
import "core:strings"
import "core:path/filepath"
import "core:time"
import "core:mem"
import "flag"
Args :: struct {
write: Maybe(bool) `flag:"w" usage:"write the new format to file"`,
}
print_help :: proc(args: []string) {
if len(args) == 0 {
fmt.eprint("odinfmt ");
} else {
fmt.eprintf("%s ", args[0]);
}
fmt.eprintln();
}
print_arg_error :: proc(error: flag.Flag_Error) {
fmt.println(error);
}
format_file :: proc(filepath: string) -> (string, bool) {
if data, ok := os.read_entire_file(filepath); ok {
return format.format(string(data), format.default_style);
} else {
return "", false;
}
}
files: [dynamic]string;
walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool) {
if info.is_dir {
return 0, false;
}
if filepath.ext(info.name) != ".odin" {
return 0, false;
}
append(&files, strings.clone(info.fullpath));
return 0, false;
}
main :: proc() {
init_global_temporary_allocator(mem.megabytes(100));
args: Args;
if len(os.args) < 2 {
print_help(os.args);
os.exit(1);
}
if res := flag.parse(args, os.args[1:len(os.args) - 1]); res != .None {
print_arg_error(res);
os.exit(1);
}
path := os.args[len(os.args) - 1];
tick_time := time.tick_now();
if os.is_file(path) {
if _, ok := args.write.(bool); ok {
backup_path := strings.concatenate({path, "_bk"});
defer delete(backup_path);
if data, ok := format_file(path); ok {
os.rename(path, backup_path);
if os.write_entire_file(path, transmute([]byte)data) {
os.remove(backup_path);
}
} else {
fmt.eprintf("failed to write %v", path);
}
} else {
if data, ok := format_file(path); ok {
fmt.println(data);
}
}
} else if os.is_dir(path) {
filepath.walk(path, walk_files);
for file in files {
fmt.println(file);
backup_path := strings.concatenate({file, "_bk"});
defer delete(backup_path);
if data, ok := format_file(file); ok {
if _, ok := args.write.(bool); ok {
os.rename(file, backup_path);
if os.write_entire_file(file, transmute([]byte)data) {
os.remove(backup_path);
}
} else {
fmt.println(data);
}
} else {
fmt.eprintf("failed to format %v", file);
}
}
fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time)));
} else {
fmt.eprintf("%v is neither a directory nor a file \n", path);
os.exit(1);
}
os.exit(0);
}