From e0e6bba865440c5a3630ac7cb84ec5edd4fb0c34 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 11 Apr 2021 19:47:33 +0200 Subject: [PATCH 001/137] start new prototype --- core/odin/printer/printer.odin | 0 core/odin/printer/visit.odin | 1345 ++++++++++++++++++++++++++++++++ 2 files changed, 1345 insertions(+) create mode 100644 core/odin/printer/printer.odin create mode 100644 core/odin/printer/visit.odin diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin new file mode 100644 index 000000000..e69de29bb diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin new file mode 100644 index 000000000..2b02ae0a5 --- /dev/null +++ b/core/odin/printer/visit.odin @@ -0,0 +1,1345 @@ +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" + +@(private) +push_format_token :: proc(p: ^Printer, line: int, kind: tokenizer.Token_Kind, text: string, spaces_before: int) { + + if len(p.lines) <= line { + return; + } + + p.lines[line].used = true; + + format_token := Format_Token { + spaces_before = spaces_before, + kind = kind, + text = text, + }; + + append(&p.lines[line].format_tokens, format_token); +} + +set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { + p.source_position = pos; +} + +/* + + +print_expr :: proc(p: ^Printer, expr: ^ast.Expr) { + + using ast; + + if expr == nil { + return; + } + + set_source_position(p, expr.pos); + + switch v in expr.derived { + case Inline_Asm_Expr: + //TEMP + //this is probably not fully done, but need more examples + /* + Inline_Asm_Expr :: struct { + using node: Expr, + tok: tokenizer.Token, + param_types: []^Expr, + return_type: ^Expr, + has_side_effects: bool, + is_align_stack: bool, + dialect: Inline_Asm_Dialect, + open: tokenizer.Pos, + constraints_string: ^Expr, + asm_string: ^Expr, + close: tokenizer.Pos, + } + */ + + /* + cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) { + return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} { + "cpuid", + "={ax},={bx},={cx},={dx},{ax},{cx}", + }(ax, cx)); + } + */ + + print(p, v.tok, space, lparen); + print_exprs(p, v.param_types, ", "); + print(p, rparen, space); + + print(p, "->", space); + + print_expr(p, v.return_type); + + print(p, space); + + print(p, lbrace); + print_expr(p, v.asm_string); + print(p, ", "); + print_expr(p, v.constraints_string); + print(p, rbrace); + + case Undef: + print(p, "---"); + case Auto_Cast: + print(p, v.op, space); + print_expr(p, v.expr); + case Ternary_Expr: + print_expr(p, v.cond); + print(p, space, v.op1, space); + print_expr(p, v.x); + print(p, space, v.op2, space); + print_expr(p, v.y); + case Ternary_If_Expr: + print_expr(p, v.x); + print(p, space, v.op1, space); + print_expr(p, v.cond); + print(p, space, v.op2, space); + print_expr(p, v.y); + case Ternary_When_Expr: + print_expr(p, v.x); + print(p, space, v.op1, space); + print_expr(p, v.cond); + print(p, space, v.op2, space); + print_expr(p, v.y); + case Selector_Call_Expr: + print_expr(p, v.call.expr); + print(p, lparen); + print_exprs(p, v.call.args, ", "); + print(p, rparen); + case Ellipsis: + print(p, ".."); + print_expr(p, v.expr); + case Relative_Type: + print_expr(p, v.tag); + print(p, space); + print_expr(p, v.type); + case Slice_Expr: + print_expr(p, v.expr); + print(p, lbracket); + print_expr(p, v.low); + print(p, v.interval); + print_expr(p, v.high); + print(p, rbracket); + case Ident: + print(p, v); + case Deref_Expr: + print_expr(p, v.expr); + print(p, v.op); + case Type_Cast: + print(p, v.tok, lparen); + print_expr(p, v.type); + print(p, rparen); + print_expr(p, v.expr); + case Basic_Directive: + print(p, v.tok, v.name); + case Distinct_Type: + print(p, "distinct", space); + print_expr(p, v.type); + case Dynamic_Array_Type: + print_expr(p, v.tag); + print(p, lbracket, "dynamic", rbracket); + print_expr(p, v.elem); + case Bit_Set_Type: + print(p, "bit_set", lbracket); + print_expr(p, v.elem); + + if v.underlying != nil { + print(p, semicolon, space); + print_expr(p, v.underlying); + } + + print(p, rbracket); + case Union_Type: + print(p, "union"); + + if v.poly_params != nil { + print(p, lparen); + print_field_list(p, v.poly_params, ", "); + print(p, rparen); + } + + if v.is_maybe { + print(p, space, "#maybe"); + } + + if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { + print(p, space, lbrace); + set_source_position(p, v.variants[len(v.variants) - 1].pos); + print_exprs(p, v.variants, ", "); + print(p, rbrace); + } else { + print_begin_brace(p, v.pos, .Generic); + print(p, newline); + set_source_position(p, v.variants[len(v.variants) - 1].pos); + print_exprs(p, v.variants, ",", true); + print_end_brace(p, v.end); + } + case Enum_Type: + print(p, "enum"); + + if v.base_type != nil { + print(p, space); + print_expr(p, v.base_type); + } + + if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { + print(p, space, lbrace); + set_source_position(p, v.fields[len(v.fields) - 1].pos); + print_exprs(p, v.fields, ", "); + print(p, rbrace); + } else { + print_begin_brace(p, v.pos, .Generic); + print(p, newline); + set_source_position(p, v.fields[len(v.fields) - 1].pos); + print_enum_fields(p, v.fields, ","); + print_end_brace(p, v.end); + } + + set_source_position(p, v.end); + case Struct_Type: + print(p, "struct"); + + if v.is_packed { + print(p, space, "#packed"); + } + + if v.is_raw_union { + print(p, space, "#raw_union"); + } + + if v.align != nil { + print(p, space, "#align", space); + print_expr(p, v.align); + } + + if v.poly_params != nil { + print(p, lparen); + print_field_list(p, v.poly_params, ", "); + print(p, rparen); + } + + if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { + print(p, space, lbrace); + set_source_position(p, v.fields.pos); + print_field_list(p, v.fields, ", "); + print(p, rbrace); + } else { + print_begin_brace(p, v.pos, .Generic); + print(p, newline); + set_source_position(p, v.fields.pos); + print_struct_field_list(p, v.fields, ","); + print_end_brace(p, v.end); + } + + set_source_position(p, v.end); + case Proc_Lit: + + if v.inlining == .Inline { + print(p, "#force_inline", space); + } + + print_proc_type(p, v.type^); + + if v.where_clauses != nil { + print(p, space); + newline_until_pos(p, v.where_clauses[0].pos); + print(p, "where", space); + print_exprs(p, v.where_clauses, ", "); + } + + if v.body != nil { + set_source_position(p, v.body.pos); + print_stmt(p, v.body, .Proc); + } else { + print(p, space, "---"); + } + case Proc_Type: + print_proc_type(p, v); + case Basic_Lit: + print(p, v.tok); + case Binary_Expr: + print_binary_expr(p, v); + case Implicit_Selector_Expr: + print(p, dot, v.field^); + case Call_Expr: + print_expr(p, v.expr); + print(p, lparen); + + padding := get_length_of_names({v.expr}); + + print_call_exprs(p, v.args, ", ", v.ellipsis.kind == .Ellipsis, padding); + print(p, rparen); + case Typeid_Type: + print(p, "typeid"); + if v.specialization != nil { + print(p, "/"); + print_expr(p, v.specialization); + } + case Selector_Expr: + print_expr(p, v.expr); + print(p, v.op); + print_expr(p, v.field); + case Paren_Expr: + print(p, lparen); + print_expr(p, v.expr); + print(p, rparen); + case Index_Expr: + print_expr(p, v.expr); + print(p, lbracket); + print_expr(p, v.index); + print(p, rbracket); + case Proc_Group: + + print(p, v.tok); + + if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { + print_begin_brace(p, v.pos, .Generic); + print(p, newline); + set_source_position(p, v.args[len(v.args) - 1].pos); + print_exprs(p, v.args, ",", true); + print_end_brace(p, v.end); + } else { + print(p, space, lbrace); + print_exprs(p, v.args, ", "); + print(p, rbrace); + } + case Comp_Lit: + + if v.type != nil { + print_expr(p, v.type); + print(p, space); + } + + if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { + print_begin_brace(p, v.pos, .Comp_Lit); + print(p, newline); + set_source_position(p, v.elems[len(v.elems) - 1].pos); + print_exprs(p, v.elems, ",", true); + print_end_brace(p, v.end); + } else { + print(p, lbrace); + print_exprs(p, v.elems, ", "); + print(p, rbrace); + } + case Unary_Expr: + print(p, v.op); + print_expr(p, v.expr); + case Field_Value: + print_expr(p, v.field); + print(p, space, "=", space); + print_expr(p, v.value); + case Type_Assertion: + print_expr(p, v.expr); + + if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { + print(p, dot); + print_expr(p, v.type); + } else { + print(p, dot, lparen); + print_expr(p, v.type); + print(p, rparen); + } + + case Pointer_Type: + print(p, "^"); + print_expr(p, v.elem); + case Implicit: + print(p, v.tok); + case Poly_Type: + print(p, "$"); + print_expr(p, v.type); + + if v.specialization != nil { + print(p, "/"); + print_expr(p, v.specialization); + } + case Array_Type: + print_expr(p, v.tag); + print(p, lbracket); + print_expr(p, v.len); + print(p, rbracket); + print_expr(p, v.elem); + case Map_Type: + print(p, "map", lbracket); + print_expr(p, v.key); + print(p, rbracket); + print_expr(p, v.value); + case Helper_Type: + print_expr(p, v.type); + case: + panic(fmt.aprint(expr.derived)); + } +} + +print_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { + + print(p, "proc"); //TOOD(ast is missing proc token) + + if proc_type.calling_convention != .Odin { + print(p, space); + } + + switch proc_type.calling_convention { + case .Odin: + case .Contextless: + print(p, "\"contextless\"", space); + case .C_Decl: + print(p, "\"c\"", space); + case .Std_Call: + print(p, "\"std\"", space); + case .Fast_Call: + print(p, "\"fast\"", space); + case .None: + //nothing i guess + case .Invalid: + //nothing i guess + case .Foreign_Block_Default: + } + + print(p, lparen); + print_signature_list(p, proc_type.params, ", ", false); + print(p, rparen); + + if proc_type.results != nil { + print(p, space, "->", space); + + use_parens := false; + use_named := false; + + if len(proc_type.results.list) > 1 { + use_parens = true; + } else if len(proc_type.results.list) == 1 { + + for name in proc_type.results.list[0].names { + if ident, ok := name.derived.(ast.Ident); ok { + if ident.name != "_" { + use_parens = true; + } + } + } + } + + if use_parens { + print(p, lparen); + print_signature_list(p, proc_type.results, ", "); + print(p, rparen); + } else { + print_signature_list(p, proc_type.results, ", "); + } + } +} + +print_enum_fields :: proc(p: ^Printer, list: []^ast.Expr, sep := " ") { + + //print enum fields is like print_exprs, but it can contain fields that can be aligned. + + if len(list) == 0 { + return; + } + + if list[0].pos.line == list[len(list) - 1].pos.line { + //if everything is on one line, then it can be treated the same way as print_exprs + print_exprs(p, list, sep); + return; + } + + largest := 0; + last_field_value := 0; + + //first find all the field values and find the largest name + for expr, i in list { + + if field_value, ok := expr.derived.(ast.Field_Value); ok { + + if ident, ok := field_value.field.derived.(ast.Ident); ok { + largest = max(largest, strings.rune_count(ident.name)); + } + } + } + + for expr, i in list { + + newline_until_pos_limit(p, expr.pos, 1); + + if field_value, ok := expr.derived.(ast.Field_Value); ok && p.config.align_assignments { + + if ident, ok := field_value.field.derived.(ast.Ident); ok { + print_expr(p, field_value.field); + print_space_padding(p, largest - strings.rune_count(ident.name) + 1); + print(p, "=", space); + print_expr(p, field_value.value); + } else { + print_expr(p, expr); + } + } else { + print_expr(p, expr); + } + + if i != len(list) - 1 { + print(p, sep); + } else { + print(p, strings.trim_space(sep)); + } + } +} + +print_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, sep := " ", ellipsis := false, padding := 0) { + + if len(list) == 0 { + return; + } + + //all the expression are on the line + if list[0].pos.line == list[len(list) - 1].pos.line { + + for expr, i in list { + + if i == len(list) - 1 && ellipsis { + print(p, ".."); + } + + print_expr(p, expr); + + if i != len(list) - 1 { + print(p, sep); + } + } + } else { + + for expr, i in list { + + //we have to newline the expressions to respect the source + if newline_until_pos_limit(p, expr.pos, 1) { + print_space_padding(p, padding); + } + + if i == len(list) - 1 && ellipsis { + print(p, ".."); + } + + print_expr(p, expr); + + if i != len(list) - 1 { + print(p, sep); + } + } + } +} + +print_exprs :: proc(p: ^Printer, list: []^ast.Expr, sep := " ", trailing := false) { + + if len(list) == 0 { + return; + } + + //we have to newline the expressions to respect the source + for expr, i in list { + + newline_until_pos_limit(p, expr.pos, 1); + + print_expr(p, expr); + + if i != len(list) - 1 { + print(p, sep); + } else if trailing { + print(p, strings.trim_space(sep)); + } + } +} + +print_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { + + newline_until_pos(p, binary.left.pos); + + if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + print_binary_expr(p, v); + } else { + print_expr(p, binary.left); + } + + if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { + print(p, binary.op); + } else { + print(p, space, binary.op, space); + } + + newline_until_pos(p, binary.right.pos); + + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + print_binary_expr(p, v); + } else { + print_expr(p, binary.right); + } +} + +print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { + + if list.list == nil { + return; + } + + largest := 0; + using_size := len("using "); + + //NOTE(Daniel): Is there any other variables than using in structs? + + for field, i in list.list { + if .Using in field.flags { + largest = max(largest, get_length_of_names(field.names) + using_size); + } else { + largest = max(largest, get_length_of_names(field.names)); + } + } + + for field, i in list.list { + + newline_until_pos_limit(p, field.pos, 1); + + if .Using in field.flags { + print(p, "using", space); + } + + print_exprs(p, field.names, ", "); + + if len(field.names) != 0 { + print(p, ": "); + } + + if field.type == nil { + panic("struct field has to have types"); + } + + if .Using in field.flags { + print_space_padding(p, largest - get_length_of_names(field.names) - using_size); + } else { + print_space_padding(p, largest - get_length_of_names(field.names)); + } + + print_expr(p, field.type); + + if field.tag.text != "" { + print(p, space, field.tag); + } + + if i != len(list.list) - 1 { + print(p, sep); + } else { + print(p, strings.trim_space(sep)); + } + } +} + +print_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { + + if list.list == nil { + return; + } + + for field, i in list.list { + + newline_until_pos_limit(p, field.pos, 1); + + if .Using in field.flags { + print(p, "using", space); + } + + print_exprs(p, field.names, ", "); + + if len(field.names) != 0 { + print(p, ": "); + } + + if field.type != nil { + print_expr(p, field.type); + } else { + print(p, ":= "); + print_expr(p, field.default_value); + } + + if field.tag.text != "" { + print(p, space, field.tag); + } + + if i != len(list.list) - 1 { + print(p, sep); + } + } +} + +print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remove_blank := true) { + + if list.list == nil { + return; + } + + for field, i in list.list { + + newline_until_pos_limit(p, field.pos, 1); + + if .Using in field.flags { + print(p, "using", space); + } + + named := false; + + for name in field.names { + if ident, ok := name.derived.(ast.Ident); ok { + //for some reason the parser uses _ to mean empty + if ident.name != "_" || !remove_blank { + named = true; + } + } else { + //alternative is poly names + named = true; + } + } + + if named { + print_exprs(p, field.names, ", "); + + if len(field.names) != 0 && field.type != nil { + print(p, ": "); + } else { + print(p, space); + } + } + + if field.type != nil && field.default_value != nil { + print_expr(p, field.type); + print(p, space, "=", space); + print_expr(p, field.default_value); + } else if field.type != nil { + print_expr(p, field.type); + } else { + print(p, ":= "); + print_expr(p, field.default_value); + } + + if i != len(list.list) - 1 { + print(p, sep); + } + } +} + +print_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { + + using ast; + + if stmt == nil { + return; + } + + switch v in stmt.derived { + case Value_Decl: + print_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Import_Decl: + print_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Block_Decl: + print_decl(p, cast(^Decl)stmt, true); + return; + } + + switch v in stmt.derived { + case Using_Stmt: + newline_until_pos(p, v.pos); + print(p, "using", space); + print_exprs(p, v.list, ", "); + + if p.config.semicolons { + print(p, semicolon); + } + case Block_Stmt: + newline_until_pos(p, v.pos); + + if v.pos.line == v.end.line && len(v.stmts) > 1 && p.config.split_multiple_stmts { + + if !empty_block { + print_begin_brace(p, v.pos, block_type); + } + + set_source_position(p, v.pos); + + print_block_stmts(p, v.stmts, true); + + set_source_position(p, v.end); + + if !empty_block { + print_end_brace(p, v.end); + } + } else if v.pos.line == v.end.line { + if !empty_block { + print(p, lbrace); + } + + set_source_position(p, v.pos); + + print_block_stmts(p, v.stmts); + + set_source_position(p, v.end); + + if !empty_block { + print(p, rbrace); + } + } else { + if !empty_block { + print_begin_brace(p, v.pos, block_type); + } + + set_source_position(p, v.pos); + + print_block_stmts(p, v.stmts); + + set_source_position(p, v.end); + + if !empty_block { + print_end_brace(p, v.end); + } + } + case If_Stmt: + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + print(p, "if", space); + + if v.init != nil { + p.skip_semicolon = true; + print_stmt(p, v.init); + p.skip_semicolon = false; + print(p, semicolon, space); + } + + print_expr(p, v.cond); + + uses_do := false; + + if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + uses_do = true; + } + + if uses_do && !p.config.convert_do { + print(p, space, "do", space); + print_stmt(p, v.body, .If_Stmt, true); + } else { + if uses_do { + print(p, newline); + } + + print_stmt(p, v.body, .If_Stmt); + } + + if v.else_stmt != nil { + + if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { + print(p, newline); + } else { + print(p, space); + } + + print(p, "else"); + + if if_stmt, ok := v.else_stmt.derived.(ast.If_Stmt); ok { + print(p, space); + } + + set_source_position(p, v.else_stmt.pos); + + print_stmt(p, v.else_stmt); + } + case Switch_Stmt: + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + if v.partial { + print(p, "#partial", space); + } + + print(p, "switch"); + + if v.init != nil || v.cond != nil { + print(p, space); + } + + if v.init != nil { + p.skip_semicolon = true; + print_stmt(p, v.init); + p.skip_semicolon = false; + } + + if v.init != nil && v.cond != nil { + print(p, semicolon, space); + } + + print_expr(p, v.cond); + print_stmt(p, v.body); + case Case_Clause: + newline_until_pos(p, v.pos); + + if !p.config.indent_cases { + print(p, unindent); + } + + print(p, "case", indent); + + if v.list != nil { + print(p, space); + print_exprs(p, v.list, ","); + } + + print(p, v.terminator); + + print_block_stmts(p, v.body); + + print(p, unindent); + + if !p.config.indent_cases { + print(p, indent); + } + case Type_Switch_Stmt: + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + if v.partial { + print(p, "#partial", space); + } + + print(p, "switch", space); + + print_stmt(p, v.tag); + print_stmt(p, v.body); + case Assign_Stmt: + newline_until_pos(p, v.pos); + + /* + if len(v.lhs) == 1 { + + if ident, ok := v.lhs[0].derived.(Ident); ok && ident.name == "_" { + print(p, v.op, space); + print_exprs(p, v.rhs, ", "); + return; + } + + } + */ + + print_exprs(p, v.lhs, ", "); + + if p.config.align_assignments && p.align_info.assign_aligned_begin_line <= v.pos.line && v.pos.line <= p.align_info.assign_aligned_end_line { + print_space_padding(p, p.align_info.assign_aligned_padding - get_length_of_names(v.lhs)); + } + + print(p, space, v.op, space); + + print_exprs(p, v.rhs, ", "); + + if block_stmt && p.config.semicolons { + print(p, semicolon); + } + case Expr_Stmt: + newline_until_pos(p, v.pos); + print_expr(p, v.expr); + if block_stmt && p.config.semicolons { + print(p, semicolon); + } + case For_Stmt: + //this should be simplified + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + print(p, "for"); + + if v.init != nil || v.cond != nil || v.post != nil { + print(p, space); + } + + if v.init != nil { + p.skip_semicolon = true; + print_stmt(p, v.init); + p.skip_semicolon = false; + print(p, semicolon, space); + } else if v.post != nil { + print(p, semicolon, space); + } + + if v.cond != nil { + print_expr(p, v.cond); + } + + if v.post != nil { + print(p, semicolon); + print(p, space); + print_stmt(p, v.post); + } else if v.post == nil && v.cond != nil && v.init != nil { + print(p, semicolon); + } + + print_stmt(p, v.body); + case Inline_Range_Stmt: + + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + print(p, "#unroll", space); + + print(p, "for", space); + print_expr(p, v.val0); + + if v.val1 != nil { + print(p, ",", space); + print_expr(p, v.val1); + print(p, space); + } + + print(p, "in", space); + print_expr(p, v.expr); + + print_stmt(p, v.body); + case Range_Stmt: + + newline_until_pos(p, v.pos); + + if v.label != nil { + print_expr(p, v.label); + print(p, ":", space); + } + + print(p, "for", space); + + if len(v.vals) >= 1 { + print_expr(p, v.vals[0]); + } + + if len(v.vals) >= 2 { + print(p, ",", space); + print_expr(p, v.vals[1]); + print(p, space); + } else { + print(p, space); + } + + print(p, "in", space); + print_expr(p, v.expr); + + print_stmt(p, v.body); + case Return_Stmt: + newline_until_pos(p, v.pos); + print(p, "return"); + + if v.results != nil { + print(p, space); + print_exprs(p, v.results, ", "); + } + + if block_stmt && p.config.semicolons { + print(p, semicolon); + } + case Defer_Stmt: + newline_until_pos(p, v.pos); + print(p, "defer"); + + if block, ok := v.stmt.derived.(ast.Block_Stmt); !ok { + print(p, space); + } + + print_stmt(p, v.stmt); + + if p.config.semicolons { + print(p, semicolon); + } + case When_Stmt: + newline_until_pos(p, v.pos); + print(p, "when", space); + print_expr(p, v.cond); + + print_stmt(p, v.body); + + if v.else_stmt != nil { + + if p.config.brace_style == .Allman { + print(p, newline); + } else { + print(p, space); + } + + print(p, "else"); + + if when_stmt, ok := v.else_stmt.derived.(ast.When_Stmt); ok { + print(p, space); + } + + set_source_position(p, v.else_stmt.pos); + + print_stmt(p, v.else_stmt); + } + + case Branch_Stmt: + + newline_until_pos(p, v.pos); + + print(p, v.tok); + + if v.label != nil { + print(p, space); + print_expr(p, v.label); + } + + if p.config.semicolons { + print(p, semicolon); + } + case: + panic(fmt.aprint(stmt.derived)); + } + + set_source_position(p, stmt.end); +} + +print_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { + + using ast; + + if decl == nil { + return; + } + + switch v in decl.derived { + case Expr_Stmt: + newline_until_pos(p, decl.pos); + print_expr(p, v.expr); + if p.config.semicolons { + print(p, semicolon); + } + case When_Stmt: + print_stmt(p, cast(^Stmt)decl); + case Foreign_Import_Decl: + if len(v.attributes) > 0 { + newline_until_pos(p, v.attributes[0].pos); + } else { + newline_until_pos(p, decl.pos); + } + + print_attributes(p, v.attributes); + + if v.name != nil { + print(p, v.foreign_tok, space, v.import_tok, space, v.name^, space); + } else { + print(p, v.foreign_tok, space, v.import_tok, space); + } + + for path in v.fullpaths { + print(p, path); + } + case Foreign_Block_Decl: + + if len(v.attributes) > 0 { + newline_until_pos(p, v.attributes[0].pos); + } else { + newline_until_pos(p, decl.pos); + } + + print_attributes(p, v.attributes); + + print(p, newline, "foreign", space); + print_expr(p, v.foreign_library); + print_stmt(p, v.body); + case Import_Decl: + newline_until_pos(p, decl.pos); + + if v.name.text != "" { + print(p, v.import_tok, " ", v.name, " ", v.fullpath); + } else { + print(p, v.import_tok, " ", v.fullpath); + } + + case Value_Decl: + if len(v.attributes) > 0 { + newline_until_pos(p, v.attributes[0].pos); + print_attributes(p, v.attributes); + } + + newline_until_pos(p, decl.pos); + + if v.is_using { + print(p, "using", space); + } + + print_exprs(p, v.names, ", "); + + seperator := ":"; + + if !v.is_mutable && v.type == nil { + seperator = ":: "; + } else if !v.is_mutable && v.type != nil { + seperator = " :"; + } + + if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Colon_And_Equals { + print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); + } + + if v.type != nil { + print(p, seperator, space); + + if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals { + print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); + } else if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Colon_And_Equals { + print_space_padding(p, p.align_info.value_decl_aligned_type_padding - (v.type.end.column - v.type.pos.column)); + } + + print_expr(p, v.type); + + if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals && len(v.values) != 0 { + print_space_padding(p, p.align_info.value_decl_aligned_type_padding - (v.type.end.column - v.type.pos.column)); + } + } else { + if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals { + print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); + } + print(p, space, seperator); + } + + if v.is_mutable && v.type != nil && len(v.values) != 0 { + print(p, space, "=", space); + } else if v.is_mutable && v.type == nil && len(v.values) != 0 { + print(p, "=", space); + } else if !v.is_mutable && v.type != nil { + print(p, space, ":", space); + } + + print_exprs(p, v.values, ", "); + + add_semicolon := true; + + for value in v.values { + switch a in value.derived { + case Proc_Lit,Union_Type,Enum_Type,Struct_Type: + add_semicolon = false || called_in_stmt; + } + } + + if add_semicolon && p.config.semicolons && !p.skip_semicolon { + print(p, semicolon); + } + + case: + panic(fmt.aprint(decl.derived)); + } +} + +print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { + + if len(attributes) == 0 { + return; + } + + for attribute, i in attributes { + + print(p, "@", lparen); + print_exprs(p, attribute.elems, ", "); + print(p, rparen); + + if len(attributes) - 1 != i { + print(p, newline); + } + } +} + +print_file :: proc(p: ^Printer, file: ^ast.File) { + + p.comments = file.comments; + p.file = file; + + newline_until_pos(p, file.pkg_token.pos); + + print(p, file.pkg_token, space, file.pkg_name); + + for decl, i in file.decls { + + if value_decl, ok := decl.derived.(ast.Value_Decl); ok { + set_value_decl_alignment_padding(p, value_decl, file.decls[i + 1:]); + } + + print_decl(p, cast(^ast.Decl)decl); + } + + //todo(probably check if there already is a newline, but there really shouldn't be) + print(p, newline); //finish document with newline + write_whitespaces(p, p.current_whitespace); +} + +print_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { + + set_source_position(p, begin); + + newline_braced := p.config.brace_style == .Allman; + newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; + newline_braced &= p.config.brace_style != ._1TBS; + + if newline_braced { + print(p, newline); + print(p, lbrace); + print(p, indent); + } else { + + if type != .Comp_Lit && p.last_out_position.line == (p.out_position.line + get_current_newlines(p)) { + print(p, space); + } + print(p, lbrace); + print(p, indent); + } +} + +print_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { + set_source_position(p, end); + print(p, newline, unindent, rbrace); +} + +print_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false) { + for stmt, i in stmts { + + if newline_each { + print(p, newline); + } + + if value_decl, ok := stmt.derived.(ast.Value_Decl); ok { + set_value_decl_alignment_padding(p, value_decl, stmts[i + 1:]); + } else if assignment_stmt, ok := stmt.derived.(ast.Assign_Stmt); ok { + set_assign_alignment_padding(p, assignment_stmt, stmts[i + 1:]); + } + + print_stmt(p, stmt, .Generic, false, true); + } +} +*/ + + From 9139ca4673bb0de6acc95ef4ad87676d96887fc3 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 12 Apr 2021 17:01:43 +0200 Subject: [PATCH 002/137] more work on fmt --- core/odin/printer/printer.odin | 143 +++ core/odin/printer/visit.odin | 1898 ++++++++++++++++---------------- 2 files changed, 1117 insertions(+), 924 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index e69de29bb..d783bfb48 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -0,0 +1,143 @@ +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" + +Line :: struct { + format_tokens: [dynamic] Format_Token, + finalized: bool, + used: bool, + depth: int, +} + +Format_Token :: struct { + kind: tokenizer.Token_Kind, + text: string, + spaces_before: int, +} + +Printer :: struct { + string_builder: strings.Builder, + config: Config, + depth: int, //the identation depth + comments: [dynamic]^ast.Comment_Group, + allocator: mem.Allocator, + file: ^ast.File, + source_position: tokenizer.Pos, + lines: map [int]^Line, + skip_semicolon: bool, + current_line: ^Line, + current_line_index: int, + last_token: ^Format_Token, +} + +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, + brace_style: Brace_Style, + align_assignments: bool, + align_style: Alignment_Style, + indent_cases: bool, +} + +Brace_Style :: enum { + _1TBS, + Allman, + Stroustrup, + K_And_R, +} + +Block_Type :: enum { + None, + If_Stmt, + Proc, + Generic, + Comp_Lit, +} + +Alignment_Style :: enum { + Align_On_Colon_And_Equals, + Align_On_Type_And_Equals, +} + +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, +}; + +make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { + return { + config = config, + allocator = allocator, + }; +} + +print :: proc(p: ^Printer, file: ^ast.File) -> string { + + for decl in file.decls { + visit_stmt(p, decl); + } + + fix_lines(p); + + builder := strings.make_builder(p.allocator); + + last_line := 0; + + for key, value in p.lines { + diff_line := min(key - last_line, p.config.newline_limit); + + for i := 0; i < diff_line; i += 1 { + strings.write_byte(&builder, '\n'); + } + + for i := 0; i < value.depth * 4; i += 1 { + strings.write_byte(&builder, ' '); + } + + for format_token in value.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 = key; + } + + return strings.to_string(builder); +} + +fix_lines :: proc(p: ^Printer) { + + for key, value in p.lines { + + if len(value.format_tokens) <= 0 { + continue; + } + + + + } + + +} diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 2b02ae0a5..12f7da301 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -8,32 +8,639 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -@(private) -push_format_token :: proc(p: ^Printer, line: int, kind: tokenizer.Token_Kind, text: string, spaces_before: int) { - if len(p.lines) <= line { - return; - } - - p.lines[line].used = true; +@(private) +append_format_token :: proc(p: ^Printer, unwrapped_line: ^Line, format_token: Format_Token) -> ^Format_Token { + + format_token := format_token; + + if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || + p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || + p.last_token.kind == .Open_Brace) { + format_token.spaces_before = 0; + } + + if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { + format_token.spaces_before = 0; + } + + append(&unwrapped_line.format_tokens, format_token); + return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens)-1]; +} + +@(private) +push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { + + unwrapped_line := p.current_line; + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; format_token := Format_Token { spaces_before = spaces_before, kind = kind, + text = tokenizer.tokens[kind], + }; + + if value != "" { + format_token.text = value; + } + + p.last_token = append_format_token(p, unwrapped_line, format_token); +} + +@(private) +push_string_token :: proc(p: ^Printer, text: string, spaces_before: int) { + unwrapped_line := p.current_line; + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + + format_token := Format_Token { + spaces_before = spaces_before, + kind = .String, text = text, }; - append(&p.lines[line].format_tokens, format_token); + p.last_token = append_format_token(p, unwrapped_line, format_token); } +@(private) +push_ident_token :: proc(p: ^Printer, text: string, spaces_before: int) { + + unwrapped_line := p.current_line; + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + + format_token := Format_Token { + spaces_before = spaces_before, + kind = .Ident, + text = text, + }; + + p.last_token = append_format_token(p, unwrapped_line, format_token); +} + +@(private) set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { p.source_position = pos; } -/* +@(private) +move_line :: proc(p: ^Printer, pos: tokenizer.Pos) { + move_line_limit(p, pos, p.config.newline_limit); +} + +@(private) +move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> bool { + lines := min(pos.line - p.source_position.line, limit); + p.source_position = pos; + p.current_line_index += lines; + set_line(p, p.current_line_index); + return lines > 0; +} + +@(private) +set_line :: proc(p: ^Printer, line: int) -> ^Line { + + unwrapped_line: ^Line; + + if line not_in p.lines { + unwrapped_line = new(Line, p.allocator); + unwrapped_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); + p.lines[line] = unwrapped_line; + } else { + unwrapped_line = p.lines[line]; + } + + p.current_line = unwrapped_line; + + return unwrapped_line; +} + +@(private) +newline_source_position :: proc(p: ^Printer, count: int) { + p.current_line_index += count; + set_line(p, p.current_line_index); +} + +@(private) +indent :: proc(p: ^Printer) { + p.depth += 1; +} + +@(private) +unindent :: proc(p: ^Printer) { + p.depth -= 1; +} + +@(private) +visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { + + using ast; + + if decl == nil { + return; + } + + switch v in decl.derived { + case Expr_Stmt: + move_line(p, decl.pos); + visit_expr(p, v.expr); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case When_Stmt: + visit_stmt(p, cast(^Stmt)decl); + case Foreign_Import_Decl: + if len(v.attributes) > 0 { + move_line(p, v.attributes[0].pos); + } else { + move_line(p, decl.pos); + } + + visit_attributes(p, v.attributes); + + push_generic_token(p, v.foreign_tok.kind, 0); + push_generic_token(p, v.import_tok.kind, 1); + + if v.name != nil { + push_ident_token(p, v.name.name, 1); + } + + for path in v.fullpaths { + push_ident_token(p, path, 0); + } + case Foreign_Block_Decl: + + if len(v.attributes) > 0 { + move_line(p, v.attributes[0].pos); + } else { + move_line(p, decl.pos); + } + + visit_attributes(p, v.attributes); + + push_generic_token(p, .Foreign, 0); + + visit_expr(p, v.foreign_library); + visit_stmt(p, v.body); + case Import_Decl: + move_line(p, decl.pos); + + if v.name.text != "" { + push_generic_token(p, v.import_tok.kind, 1); + push_generic_token(p, v.name.kind, 1); + push_ident_token(p, v.fullpath, 0); + } else { + push_generic_token(p, v.import_tok.kind, 1); + push_ident_token(p, v.fullpath, 0); + } + + case Value_Decl: + if len(v.attributes) > 0 { + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } + + move_line(p, decl.pos); + + if v.is_using { + push_generic_token(p, .Using, 0); + } + + visit_exprs(p, v.names, true); + + if v.type != nil { + if !v.is_mutable && v.type != nil { + push_generic_token(p, .Colon, 1); + } else { + push_generic_token(p, .Colon, 1); + } + + visit_expr(p, v.type); + } else { + if !v.is_mutable && v.type == nil { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); + } else { + push_generic_token(p, .Colon, 1); + } + } + + if v.is_mutable && v.type != nil && len(v.values) != 0 { + push_generic_token(p, .Eq, 0); + } else if v.is_mutable && v.type == nil && len(v.values) != 0 { + push_generic_token(p, .Eq, 0); + } else if !v.is_mutable && v.type != nil { + push_generic_token(p, .Semicolon, 0); + } + + visit_exprs(p, v.values, true); + + add_semicolon := true; + + for value in v.values { + switch a in value.derived { + case Proc_Lit,Union_Type,Enum_Type,Struct_Type: + add_semicolon = false || called_in_stmt; + } + } + + if add_semicolon && p.config.semicolons && !p.skip_semicolon { + push_generic_token(p, .Semicolon, 0); + } + + case: + panic(fmt.aprint(decl.derived)); + } +} + +@(private) +visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false) { + + if len(list) == 0 { + return; + } + + //we have to newline the expressions to respect the source + for expr, i in list { + + move_line_limit(p, expr.pos, 1); + + visit_expr(p, expr); + + if i != len(list) - 1 && add_comma { + push_generic_token(p, .Comma, 0); + } else if trailing && add_comma { + //print(p, strings.trim_space(sep)); + } + } +} + +@(private) +visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { + +} + +@(private) +visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { + + using ast; + + if stmt == nil { + return; + } + + switch v in stmt.derived { + case Value_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Import_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Block_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + } + + switch v in stmt.derived { + case Using_Stmt: + move_line(p, v.pos); + + push_generic_token(p, .Using, 1); + + visit_exprs(p, v.list, true); + + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Block_Stmt: + move_line(p, v.pos); + + if v.pos.line == v.end.line && len(v.stmts) > 1 && p.config.split_multiple_stmts { + + if !empty_block { + visit_begin_brace(p, v.pos, block_type); + } + + set_source_position(p, v.pos); + + visit_block_stmts(p, v.stmts, true); + + set_source_position(p, v.end); + + if !empty_block { + visit_end_brace(p, v.end); + } + } else if v.pos.line == v.end.line { + if !empty_block { + push_generic_token(p, .Open_Brace, 0); + } + + set_source_position(p, v.pos); + + visit_block_stmts(p, v.stmts); + + set_source_position(p, v.end); + + if !empty_block { + push_generic_token(p, .Close_Brace, 0); + } + } else { + if !empty_block { + visit_begin_brace(p, v.pos, block_type); + } + + set_source_position(p, v.pos); + + visit_block_stmts(p, v.stmts); + + set_source_position(p, v.end); + + if !empty_block { + visit_end_brace(p, v.end); + } + } + case If_Stmt: + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } + + push_generic_token(p, .If, 1); + + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } + + visit_expr(p, v.cond); + + uses_do := false; + + if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + uses_do = true; + } + + if uses_do && !p.config.convert_do { + push_generic_token(p, .Do, 1); + visit_stmt(p, v.body, .If_Stmt, true); + } else { + if uses_do { + newline_source_position(p, 1); + } + + visit_stmt(p, v.body, .If_Stmt); + } + + if v.else_stmt != nil { + + if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { + newline_source_position(p, 1); + } + + push_generic_token(p, .Else, 1); + + set_source_position(p, v.else_stmt.pos); + + visit_stmt(p, v.else_stmt); + } + case Switch_Stmt: + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } + + if v.partial { + push_ident_token(p, "#partial", 0); + } + + push_generic_token(p, .Switch, 0); + + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + } + + if v.init != nil && v.cond != nil { + push_generic_token(p, .Semicolon, 0); + } + + visit_expr(p, v.cond); + visit_stmt(p, v.body); + case Case_Clause: + move_line(p, v.pos); + + if !p.config.indent_cases { + unindent(p); + } + + push_generic_token(p, .Case, 0); + + if v.list != nil { + visit_exprs(p, v.list, true); + } + + push_generic_token(p, v.terminator.kind, 0); + + indent(p); + + visit_block_stmts(p, v.body); + + unindent(p); + + if !p.config.indent_cases { + indent(p); + } + case Type_Switch_Stmt: + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } + + if v.partial { + push_ident_token(p, "partial", 0); + } + + push_generic_token(p, .Switch, 0); + + visit_stmt(p, v.tag); + visit_stmt(p, v.body); + case Assign_Stmt: + move_line(p, v.pos); + + visit_exprs(p, v.lhs, true); + + push_generic_token(p, v.op.kind, 1); + + visit_exprs(p, v.rhs, true); + + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Expr_Stmt: + move_line(p, v.pos); + visit_expr(p, v.expr); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case For_Stmt: + //this should be simplified + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 1); + } + + push_generic_token(p, .For, 0); + + //if v.init != nil || v.cond != nil || v.post != nil { + // print(p, space); + //} + + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } else if v.post != nil { + push_generic_token(p, .Semicolon, 0); + } + + if v.cond != nil { + visit_expr(p, v.cond); + } + + if v.post != nil { + push_generic_token(p, .Semicolon, 0); + //print(p, space); + visit_stmt(p, v.post); + } else if v.post == nil && v.cond != nil && v.init != nil { + push_generic_token(p, .Semicolon, 0); + } + + visit_stmt(p, v.body); + case Inline_Range_Stmt: + + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 1); + } + + push_ident_token(p, "#unroll", 0); + + push_generic_token(p, .For, 0); + visit_expr(p, v.val0); + + if v.val1 != nil { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.val1); + } + + push_generic_token(p, .In, 1); + + visit_expr(p, v.expr); + visit_stmt(p, v.body); + case Range_Stmt: + + move_line(p, v.pos); + + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 1); + } + + push_generic_token(p, .For, 0); + + if len(v.vals) >= 1 { + visit_expr(p, v.vals[0]); + } + + if len(v.vals) >= 2 { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.vals[1]); + } + + push_generic_token(p, .In, 1); + + visit_expr(p, v.expr); + + visit_stmt(p, v.body); + case Return_Stmt: + move_line(p, v.pos); + + push_generic_token(p, .Return, 0); + + if v.results != nil { + visit_exprs(p, v.results, true); + } + + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Defer_Stmt: + move_line(p, v.pos); + push_generic_token(p, .Defer, 0); + + visit_stmt(p, v.stmt); + + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case When_Stmt: + move_line(p, v.pos); + push_generic_token(p, .When, 0); + visit_expr(p, v.cond); + + visit_stmt(p, v.body); + + if v.else_stmt != nil { + + if p.config.brace_style == .Allman { + newline_source_position(p, 1); + } + + push_generic_token(p, .Else, 0); + + set_source_position(p, v.else_stmt.pos); + + visit_stmt(p, v.else_stmt); + } + + case Branch_Stmt: + + move_line(p, v.pos); + + push_generic_token(p, v.tok.kind, 0); + + if v.label != nil { + visit_expr(p, v.label); + } + + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case: + panic(fmt.aprint(stmt.derived)); + } + + set_source_position(p, stmt.end); +} -print_expr :: proc(p: ^Printer, expr: ^ast.Expr) { +@(private) +visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { using ast; @@ -45,360 +652,417 @@ print_expr :: proc(p: ^Printer, expr: ^ast.Expr) { switch v in expr.derived { case Inline_Asm_Expr: - //TEMP - //this is probably not fully done, but need more examples - /* - Inline_Asm_Expr :: struct { - using node: Expr, - tok: tokenizer.Token, - param_types: []^Expr, - return_type: ^Expr, - has_side_effects: bool, - is_align_stack: bool, - dialect: Inline_Asm_Dialect, - open: tokenizer.Pos, - constraints_string: ^Expr, - asm_string: ^Expr, - close: tokenizer.Pos, - } - */ - - /* - cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) { - return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} { - "cpuid", - "={ax},={bx},={cx},={dx},{ax},{cx}", - }(ax, cx)); - } - */ - + /* print(p, v.tok, space, lparen); - print_exprs(p, v.param_types, ", "); + visit_exprs(p, v.param_types, ", "); print(p, rparen, space); print(p, "->", space); - print_expr(p, v.return_type); + visit_expr(p, v.return_type); print(p, space); print(p, lbrace); - print_expr(p, v.asm_string); + visit_expr(p, v.asm_string); print(p, ", "); - print_expr(p, v.constraints_string); + visit_expr(p, v.constraints_string); print(p, rbrace); - + */ case Undef: - print(p, "---"); + push_generic_token(p, .Undef, 1); case Auto_Cast: - print(p, v.op, space); - print_expr(p, v.expr); + push_generic_token(p, v.op.kind, 1); + visit_expr(p, v.expr); case Ternary_Expr: - print_expr(p, v.cond); - print(p, space, v.op1, space); - print_expr(p, v.x); - print(p, space, v.op2, space); - print_expr(p, v.y); + visit_expr(p, v.cond); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.x); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); case Ternary_If_Expr: - print_expr(p, v.x); - print(p, space, v.op1, space); - print_expr(p, v.cond); - print(p, space, v.op2, space); - print_expr(p, v.y); + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); case Ternary_When_Expr: - print_expr(p, v.x); - print(p, space, v.op1, space); - print_expr(p, v.cond); - print(p, space, v.op2, space); - print_expr(p, v.y); + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); case Selector_Call_Expr: - print_expr(p, v.call.expr); - print(p, lparen); - print_exprs(p, v.call.args, ", "); - print(p, rparen); + visit_expr(p, v.call.expr); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.call.args, true); + push_generic_token(p, .Close_Paren, 0); case Ellipsis: - print(p, ".."); - print_expr(p, v.expr); + push_generic_token(p, .Ellipsis, 1); + visit_expr(p, v.expr); case Relative_Type: - print_expr(p, v.tag); - print(p, space); - print_expr(p, v.type); + visit_expr(p, v.tag); + visit_expr(p, v.type); case Slice_Expr: - print_expr(p, v.expr); - print(p, lbracket); - print_expr(p, v.low); - print(p, v.interval); - print_expr(p, v.high); - print(p, rbracket); + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 1); + visit_expr(p, v.low); + push_generic_token(p, v.interval.kind, 0); + visit_expr(p, v.high); + push_generic_token(p, .Close_Bracket, 0); case Ident: - print(p, v); + push_ident_token(p, v.name, 1); case Deref_Expr: - print_expr(p, v.expr); - print(p, v.op); + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); case Type_Cast: - print(p, v.tok, lparen); - print_expr(p, v.type); - print(p, rparen); - print_expr(p, v.expr); + push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, .Open_Paren, 1); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); + visit_expr(p, v.expr); case Basic_Directive: - print(p, v.tok, v.name); + push_generic_token(p, v.tok.kind, 0); + push_ident_token(p, v.name, 0); case Distinct_Type: - print(p, "distinct", space); - print_expr(p, v.type); + push_generic_token(p, .Distinct, 0); + visit_expr(p, v.type); case Dynamic_Array_Type: - print_expr(p, v.tag); - print(p, lbracket, "dynamic", rbracket); - print_expr(p, v.elem); + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + push_generic_token(p, .Dynamic, 0); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); case Bit_Set_Type: - print(p, "bit_set", lbracket); - print_expr(p, v.elem); + push_generic_token(p, .Bit_Set, 0); + push_generic_token(p, .Open_Bracket, 0); + + visit_expr(p, v.elem); if v.underlying != nil { - print(p, semicolon, space); - print_expr(p, v.underlying); + push_generic_token(p, .Semicolon, 0); + visit_expr(p, v.underlying); } - print(p, rbracket); + push_generic_token(p, .Close_Bracket, 0); case Union_Type: - print(p, "union"); + push_generic_token(p, .Union, 0); if v.poly_params != nil { - print(p, lparen); - print_field_list(p, v.poly_params, ", "); - print(p, rparen); + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, ", "); + push_generic_token(p, .Close_Paren, 0); } if v.is_maybe { - print(p, space, "#maybe"); + push_ident_token(p, "#maybe", 1); } if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { - print(p, space, lbrace); + push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.variants[len(v.variants) - 1].pos); - print_exprs(p, v.variants, ", "); - print(p, rbrace); + visit_exprs(p, v.variants, true); + push_generic_token(p, .Close_Brace, 0); } else { - print_begin_brace(p, v.pos, .Generic); - print(p, newline); + visit_begin_brace(p, v.pos, .Generic); + newline_source_position(p, 1); set_source_position(p, v.variants[len(v.variants) - 1].pos); - print_exprs(p, v.variants, ",", true); - print_end_brace(p, v.end); + visit_exprs(p, v.variants, true, true); + visit_end_brace(p, v.end); } case Enum_Type: - print(p, "enum"); + push_generic_token(p, .Enum, 0); if v.base_type != nil { - print(p, space); - print_expr(p, v.base_type); + visit_expr(p, v.base_type); } if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { - print(p, space, lbrace); + push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.fields[len(v.fields) - 1].pos); - print_exprs(p, v.fields, ", "); - print(p, rbrace); + visit_exprs(p, v.fields, true); + push_generic_token(p, .Close_Brace, 0); } else { - print_begin_brace(p, v.pos, .Generic); - print(p, newline); + visit_begin_brace(p, v.pos, .Generic); + newline_source_position(p, 1); set_source_position(p, v.fields[len(v.fields) - 1].pos); - print_enum_fields(p, v.fields, ","); - print_end_brace(p, v.end); + //visit_enum_fields(p, v.fields, ","); + visit_exprs(p, v.fields, true); + visit_end_brace(p, v.end); } set_source_position(p, v.end); case Struct_Type: - print(p, "struct"); + push_generic_token(p, .Struct, 0); if v.is_packed { - print(p, space, "#packed"); + push_ident_token(p, "#packed", 1); } if v.is_raw_union { - print(p, space, "#raw_union"); + push_ident_token(p, "#raw_union", 1); } if v.align != nil { - print(p, space, "#align", space); - print_expr(p, v.align); + push_ident_token(p, "#align", 1); + visit_expr(p, v.align); } if v.poly_params != nil { - print(p, lparen); - print_field_list(p, v.poly_params, ", "); - print(p, rparen); + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, ", "); + push_generic_token(p, .Close_Paren, 0); } if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { - print(p, space, lbrace); + push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.fields.pos); - print_field_list(p, v.fields, ", "); - print(p, rbrace); + visit_field_list(p, v.fields, ", "); + push_generic_token(p, .Close_Brace, 0); } else { - print_begin_brace(p, v.pos, .Generic); - print(p, newline); + visit_begin_brace(p, v.pos, .Generic); + newline_source_position(p, 1); set_source_position(p, v.fields.pos); - print_struct_field_list(p, v.fields, ","); - print_end_brace(p, v.end); + visit_field_list(p, v.fields, ", "); + //visit_struct_field_list(p, v.fields, ","); + visit_end_brace(p, v.end); } set_source_position(p, v.end); case Proc_Lit: if v.inlining == .Inline { - print(p, "#force_inline", space); + push_ident_token(p, "#force_inline", 0); } - print_proc_type(p, v.type^); + visit_proc_type(p, v.type^); if v.where_clauses != nil { - print(p, space); - newline_until_pos(p, v.where_clauses[0].pos); - print(p, "where", space); - print_exprs(p, v.where_clauses, ", "); + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); } if v.body != nil { set_source_position(p, v.body.pos); - print_stmt(p, v.body, .Proc); + visit_stmt(p, v.body, .Proc); } else { - print(p, space, "---"); + push_generic_token(p, .Ellipsis, 1); } case Proc_Type: - print_proc_type(p, v); + visit_proc_type(p, v); case Basic_Lit: - print(p, v.tok); + push_generic_token(p, v.tok.kind, 1, v.tok.text); case Binary_Expr: - print_binary_expr(p, v); + visit_binary_expr(p, v); case Implicit_Selector_Expr: - print(p, dot, v.field^); + push_generic_token(p, .Period, 0); + push_ident_token(p, v.field.name, 0); case Call_Expr: - print_expr(p, v.expr); - print(p, lparen); - - padding := get_length_of_names({v.expr}); - - print_call_exprs(p, v.args, ", ", v.ellipsis.kind == .Ellipsis, padding); - print(p, rparen); + visit_expr(p, v.expr); + push_generic_token(p, .Open_Paren, 0); + visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); + push_generic_token(p, .Close_Paren, 0); case Typeid_Type: - print(p, "typeid"); + push_generic_token(p, .Typeid, 0); + if v.specialization != nil { - print(p, "/"); - print_expr(p, v.specialization); + push_generic_token(p, .Quo, 0); + visit_expr(p, v.specialization); } case Selector_Expr: - print_expr(p, v.expr); - print(p, v.op); - print_expr(p, v.field); + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + visit_expr(p, v.field); case Paren_Expr: - print(p, lparen); - print_expr(p, v.expr); - print(p, rparen); + push_generic_token(p, .Open_Paren, 1); + visit_expr(p, v.expr); + push_generic_token(p, .Close_Paren, 0); case Index_Expr: - print_expr(p, v.expr); - print(p, lbracket); - print_expr(p, v.index); - print(p, rbracket); + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 1); + visit_expr(p, v.index); + push_generic_token(p, .Close_Bracket, 0); case Proc_Group: - - print(p, v.tok); + + push_generic_token(p, v.tok.kind, 0); if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { - print_begin_brace(p, v.pos, .Generic); - print(p, newline); + visit_begin_brace(p, v.pos, .Generic); + newline_source_position(p, 1); set_source_position(p, v.args[len(v.args) - 1].pos); - print_exprs(p, v.args, ",", true); - print_end_brace(p, v.end); + visit_exprs(p, v.args, true, true); + visit_end_brace(p, v.end); } else { - print(p, space, lbrace); - print_exprs(p, v.args, ", "); - print(p, rbrace); + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.args, true); + push_generic_token(p, .Close_Brace, 0); } + case Comp_Lit: - + if v.type != nil { - print_expr(p, v.type); - print(p, space); + visit_expr(p, v.type); } if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { - print_begin_brace(p, v.pos, .Comp_Lit); - print(p, newline); + visit_begin_brace(p, v.pos, .Comp_Lit); + newline_source_position(p, 1); set_source_position(p, v.elems[len(v.elems) - 1].pos); - print_exprs(p, v.elems, ",", true); - print_end_brace(p, v.end); + visit_exprs(p, v.elems, true, true); + visit_end_brace(p, v.end); } else { - print(p, lbrace); - print_exprs(p, v.elems, ", "); - print(p, rbrace); + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.elems, true); + push_generic_token(p, .Close_Brace, 0); } + case Unary_Expr: - print(p, v.op); - print_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + visit_expr(p, v.expr); case Field_Value: - print_expr(p, v.field); - print(p, space, "=", space); - print_expr(p, v.value); + visit_expr(p, v.field); + push_generic_token(p, .Eq, 1); + visit_expr(p, v.value); case Type_Assertion: - print_expr(p, v.expr); + visit_expr(p, v.expr); if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { - print(p, dot); - print_expr(p, v.type); + push_generic_token(p, .Period, 0); + visit_expr(p, v.type); } else { - print(p, dot, lparen); - print_expr(p, v.type); - print(p, rparen); + push_generic_token(p, .Period, 0); + push_generic_token(p, .Open_Paren, 0); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); } case Pointer_Type: - print(p, "^"); - print_expr(p, v.elem); + push_generic_token(p, .Pointer, 0); + visit_expr(p, v.elem); case Implicit: - print(p, v.tok); + push_generic_token(p, v.tok.kind, 0); case Poly_Type: - print(p, "$"); - print_expr(p, v.type); + push_generic_token(p, .Dollar, 0); + + visit_expr(p, v.type); if v.specialization != nil { - print(p, "/"); - print_expr(p, v.specialization); + push_generic_token(p, .Quo, 0); + visit_expr(p, v.specialization); } case Array_Type: - print_expr(p, v.tag); - print(p, lbracket); - print_expr(p, v.len); - print(p, rbracket); - print_expr(p, v.elem); + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + visit_expr(p, v.len); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); case Map_Type: - print(p, "map", lbracket); - print_expr(p, v.key); - print(p, rbracket); - print_expr(p, v.value); + push_generic_token(p, .Map, 0); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.key); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.value); case Helper_Type: - print_expr(p, v.type); + visit_expr(p, v.type); case: panic(fmt.aprint(expr.derived)); } } -print_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { - print(p, "proc"); //TOOD(ast is missing proc token) +visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { + + set_source_position(p, begin); + + newline_braced := p.config.brace_style == .Allman; + newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; + newline_braced &= p.config.brace_style != ._1TBS; + + if newline_braced { + newline_source_position(p, 1); + push_generic_token(p, .Open_Brace, 0); + indent(p); + } else { + push_generic_token(p, .Open_Brace, 1); + indent(p); + } +} + +visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { + set_source_position(p, end); + newline_source_position(p, 1); + unindent(p); + push_generic_token(p, .Close_Brace, 0); +} + +visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false) { + for stmt, i in stmts { + + if newline_each { + newline_source_position(p, 1); + } + + visit_stmt(p, stmt, .Generic, false, true); + } +} + +visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { + + if list.list == nil { + return; + } + + for field, i in list.list { + + move_line_limit(p, field.pos, 1); + + if .Using in field.flags { + push_generic_token(p, .Using, 0); + } + + visit_exprs(p, field.names, true); + + if len(field.names) != 0 { + push_generic_token(p, .Colon, 0); + } + + if field.type != nil { + visit_expr(p, field.type); + } else { + push_generic_token(p, .Colon, 0); + push_generic_token(p, .Eq, 0); + visit_expr(p, field.default_value); + } + + if field.tag.text != "" { + push_generic_token(p, field.tag.kind, 1); + } + + if i != len(list.list) - 1 { + //print(p, sep); + } + } +} + +visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { + + push_generic_token(p, .Proc, 1); if proc_type.calling_convention != .Odin { - print(p, space); + //print(p, space); } switch proc_type.calling_convention { case .Odin: case .Contextless: - print(p, "\"contextless\"", space); + push_string_token(p, "\"contextless\"", 0); case .C_Decl: - print(p, "\"c\"", space); + push_string_token(p, "\"c\"", 0); case .Std_Call: - print(p, "\"std\"", space); + push_string_token(p, "\"std\"", 0); case .Fast_Call: - print(p, "\"fast\"", space); + push_string_token(p, "\"fast\"", 0); case .None: //nothing i guess case .Invalid: @@ -406,10 +1070,13 @@ print_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { case .Foreign_Block_Default: } - print(p, lparen); - print_signature_list(p, proc_type.params, ", ", false); - print(p, rparen); + push_generic_token(p, .Open_Paren, 0); + //visit_signature_list(p, proc_type.params, ", ", false); + + push_generic_token(p, .Close_Paren, 0); + + /* if proc_type.results != nil { print(p, space, "->", space); @@ -437,19 +1104,88 @@ print_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { print_signature_list(p, proc_type.results, ", "); } } + */ } +visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { + + move_line(p, binary.left.pos); + + if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.left); + } + + if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { + push_generic_token(p, binary.op.kind, 0); + } else { + push_generic_token(p, binary.op.kind, 1); + } + + move_line(p, binary.right.pos); + + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.right); + } + +} + +visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { + + if len(list) == 0 { + return; + } + + //all the expression are on the line + if list[0].pos.line == list[len(list) - 1].pos.line { + for expr, i in list { + + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } + + visit_expr(p, expr); + + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } else { + for expr, i in list { + + //we have to newline the expressions to respect the source + move_line_limit(p, expr.pos, 1); + + + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } + + visit_expr(p, expr); + + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } +} + +/* + print_enum_fields :: proc(p: ^Printer, list: []^ast.Expr, sep := " ") { - //print enum fields is like print_exprs, but it can contain fields that can be aligned. + //print enum fields is like visit_exprs, but it can contain fields that can be aligned. if len(list) == 0 { return; } if list[0].pos.line == list[len(list) - 1].pos.line { - //if everything is on one line, then it can be treated the same way as print_exprs - print_exprs(p, list, sep); + //if everything is on one line, then it can be treated the same way as visit_exprs + visit_exprs(p, list, sep); return; } @@ -469,20 +1205,20 @@ print_enum_fields :: proc(p: ^Printer, list: []^ast.Expr, sep := " ") { for expr, i in list { - newline_until_pos_limit(p, expr.pos, 1); + move_line_limit(p, expr.pos, 1); if field_value, ok := expr.derived.(ast.Field_Value); ok && p.config.align_assignments { if ident, ok := field_value.field.derived.(ast.Ident); ok { - print_expr(p, field_value.field); + visit_expr(p, field_value.field); print_space_padding(p, largest - strings.rune_count(ident.name) + 1); print(p, "=", space); - print_expr(p, field_value.value); + visit_expr(p, field_value.value); } else { - print_expr(p, expr); + visit_expr(p, expr); } } else { - print_expr(p, expr); + visit_expr(p, expr); } if i != len(list) - 1 { @@ -493,94 +1229,11 @@ print_enum_fields :: proc(p: ^Printer, list: []^ast.Expr, sep := " ") { } } -print_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, sep := " ", ellipsis := false, padding := 0) { - if len(list) == 0 { - return; - } - //all the expression are on the line - if list[0].pos.line == list[len(list) - 1].pos.line { - for expr, i in list { - if i == len(list) - 1 && ellipsis { - print(p, ".."); - } - print_expr(p, expr); - - if i != len(list) - 1 { - print(p, sep); - } - } - } else { - - for expr, i in list { - - //we have to newline the expressions to respect the source - if newline_until_pos_limit(p, expr.pos, 1) { - print_space_padding(p, padding); - } - - if i == len(list) - 1 && ellipsis { - print(p, ".."); - } - - print_expr(p, expr); - - if i != len(list) - 1 { - print(p, sep); - } - } - } -} - -print_exprs :: proc(p: ^Printer, list: []^ast.Expr, sep := " ", trailing := false) { - - if len(list) == 0 { - return; - } - - //we have to newline the expressions to respect the source - for expr, i in list { - - newline_until_pos_limit(p, expr.pos, 1); - - print_expr(p, expr); - - if i != len(list) - 1 { - print(p, sep); - } else if trailing { - print(p, strings.trim_space(sep)); - } - } -} - -print_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { - - newline_until_pos(p, binary.left.pos); - - if v, ok := binary.left.derived.(ast.Binary_Expr); ok { - print_binary_expr(p, v); - } else { - print_expr(p, binary.left); - } - - if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { - print(p, binary.op); - } else { - print(p, space, binary.op, space); - } - - newline_until_pos(p, binary.right.pos); - - if v, ok := binary.right.derived.(ast.Binary_Expr); ok { - print_binary_expr(p, v); - } else { - print_expr(p, binary.right); - } -} print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { @@ -603,13 +1256,13 @@ print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { for field, i in list.list { - newline_until_pos_limit(p, field.pos, 1); + move_line_limit(p, field.pos, 1); if .Using in field.flags { print(p, "using", space); } - print_exprs(p, field.names, ", "); + visit_exprs(p, field.names, ", "); if len(field.names) != 0 { print(p, ": "); @@ -625,7 +1278,7 @@ print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { print_space_padding(p, largest - get_length_of_names(field.names)); } - print_expr(p, field.type); + visit_expr(p, field.type); if field.tag.text != "" { print(p, space, field.tag); @@ -639,43 +1292,6 @@ print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { } } -print_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { - - if list.list == nil { - return; - } - - for field, i in list.list { - - newline_until_pos_limit(p, field.pos, 1); - - if .Using in field.flags { - print(p, "using", space); - } - - print_exprs(p, field.names, ", "); - - if len(field.names) != 0 { - print(p, ": "); - } - - if field.type != nil { - print_expr(p, field.type); - } else { - print(p, ":= "); - print_expr(p, field.default_value); - } - - if field.tag.text != "" { - print(p, space, field.tag); - } - - if i != len(list.list) - 1 { - print(p, sep); - } - } -} - print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remove_blank := true) { if list.list == nil { @@ -684,7 +1300,7 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo for field, i in list.list { - newline_until_pos_limit(p, field.pos, 1); + move_line_limit(p, field.pos, 1); if .Using in field.flags { print(p, "using", space); @@ -705,7 +1321,7 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo } if named { - print_exprs(p, field.names, ", "); + visit_exprs(p, field.names, ", "); if len(field.names) != 0 && field.type != nil { print(p, ": "); @@ -715,14 +1331,14 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo } if field.type != nil && field.default_value != nil { - print_expr(p, field.type); + visit_expr(p, field.type); print(p, space, "=", space); - print_expr(p, field.default_value); + visit_expr(p, field.default_value); } else if field.type != nil { - print_expr(p, field.type); + visit_expr(p, field.type); } else { print(p, ":= "); - print_expr(p, field.default_value); + visit_expr(p, field.default_value); } if i != len(list.list) - 1 { @@ -731,530 +1347,7 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo } } -print_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { - using ast; - - if stmt == nil { - return; - } - - switch v in stmt.derived { - case Value_Decl: - print_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Import_Decl: - print_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Block_Decl: - print_decl(p, cast(^Decl)stmt, true); - return; - } - - switch v in stmt.derived { - case Using_Stmt: - newline_until_pos(p, v.pos); - print(p, "using", space); - print_exprs(p, v.list, ", "); - - if p.config.semicolons { - print(p, semicolon); - } - case Block_Stmt: - newline_until_pos(p, v.pos); - - if v.pos.line == v.end.line && len(v.stmts) > 1 && p.config.split_multiple_stmts { - - if !empty_block { - print_begin_brace(p, v.pos, block_type); - } - - set_source_position(p, v.pos); - - print_block_stmts(p, v.stmts, true); - - set_source_position(p, v.end); - - if !empty_block { - print_end_brace(p, v.end); - } - } else if v.pos.line == v.end.line { - if !empty_block { - print(p, lbrace); - } - - set_source_position(p, v.pos); - - print_block_stmts(p, v.stmts); - - set_source_position(p, v.end); - - if !empty_block { - print(p, rbrace); - } - } else { - if !empty_block { - print_begin_brace(p, v.pos, block_type); - } - - set_source_position(p, v.pos); - - print_block_stmts(p, v.stmts); - - set_source_position(p, v.end); - - if !empty_block { - print_end_brace(p, v.end); - } - } - case If_Stmt: - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - print(p, "if", space); - - if v.init != nil { - p.skip_semicolon = true; - print_stmt(p, v.init); - p.skip_semicolon = false; - print(p, semicolon, space); - } - - print_expr(p, v.cond); - - uses_do := false; - - if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { - uses_do = true; - } - - if uses_do && !p.config.convert_do { - print(p, space, "do", space); - print_stmt(p, v.body, .If_Stmt, true); - } else { - if uses_do { - print(p, newline); - } - - print_stmt(p, v.body, .If_Stmt); - } - - if v.else_stmt != nil { - - if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { - print(p, newline); - } else { - print(p, space); - } - - print(p, "else"); - - if if_stmt, ok := v.else_stmt.derived.(ast.If_Stmt); ok { - print(p, space); - } - - set_source_position(p, v.else_stmt.pos); - - print_stmt(p, v.else_stmt); - } - case Switch_Stmt: - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - if v.partial { - print(p, "#partial", space); - } - - print(p, "switch"); - - if v.init != nil || v.cond != nil { - print(p, space); - } - - if v.init != nil { - p.skip_semicolon = true; - print_stmt(p, v.init); - p.skip_semicolon = false; - } - - if v.init != nil && v.cond != nil { - print(p, semicolon, space); - } - - print_expr(p, v.cond); - print_stmt(p, v.body); - case Case_Clause: - newline_until_pos(p, v.pos); - - if !p.config.indent_cases { - print(p, unindent); - } - - print(p, "case", indent); - - if v.list != nil { - print(p, space); - print_exprs(p, v.list, ","); - } - - print(p, v.terminator); - - print_block_stmts(p, v.body); - - print(p, unindent); - - if !p.config.indent_cases { - print(p, indent); - } - case Type_Switch_Stmt: - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - if v.partial { - print(p, "#partial", space); - } - - print(p, "switch", space); - - print_stmt(p, v.tag); - print_stmt(p, v.body); - case Assign_Stmt: - newline_until_pos(p, v.pos); - - /* - if len(v.lhs) == 1 { - - if ident, ok := v.lhs[0].derived.(Ident); ok && ident.name == "_" { - print(p, v.op, space); - print_exprs(p, v.rhs, ", "); - return; - } - - } - */ - - print_exprs(p, v.lhs, ", "); - - if p.config.align_assignments && p.align_info.assign_aligned_begin_line <= v.pos.line && v.pos.line <= p.align_info.assign_aligned_end_line { - print_space_padding(p, p.align_info.assign_aligned_padding - get_length_of_names(v.lhs)); - } - - print(p, space, v.op, space); - - print_exprs(p, v.rhs, ", "); - - if block_stmt && p.config.semicolons { - print(p, semicolon); - } - case Expr_Stmt: - newline_until_pos(p, v.pos); - print_expr(p, v.expr); - if block_stmt && p.config.semicolons { - print(p, semicolon); - } - case For_Stmt: - //this should be simplified - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - print(p, "for"); - - if v.init != nil || v.cond != nil || v.post != nil { - print(p, space); - } - - if v.init != nil { - p.skip_semicolon = true; - print_stmt(p, v.init); - p.skip_semicolon = false; - print(p, semicolon, space); - } else if v.post != nil { - print(p, semicolon, space); - } - - if v.cond != nil { - print_expr(p, v.cond); - } - - if v.post != nil { - print(p, semicolon); - print(p, space); - print_stmt(p, v.post); - } else if v.post == nil && v.cond != nil && v.init != nil { - print(p, semicolon); - } - - print_stmt(p, v.body); - case Inline_Range_Stmt: - - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - print(p, "#unroll", space); - - print(p, "for", space); - print_expr(p, v.val0); - - if v.val1 != nil { - print(p, ",", space); - print_expr(p, v.val1); - print(p, space); - } - - print(p, "in", space); - print_expr(p, v.expr); - - print_stmt(p, v.body); - case Range_Stmt: - - newline_until_pos(p, v.pos); - - if v.label != nil { - print_expr(p, v.label); - print(p, ":", space); - } - - print(p, "for", space); - - if len(v.vals) >= 1 { - print_expr(p, v.vals[0]); - } - - if len(v.vals) >= 2 { - print(p, ",", space); - print_expr(p, v.vals[1]); - print(p, space); - } else { - print(p, space); - } - - print(p, "in", space); - print_expr(p, v.expr); - - print_stmt(p, v.body); - case Return_Stmt: - newline_until_pos(p, v.pos); - print(p, "return"); - - if v.results != nil { - print(p, space); - print_exprs(p, v.results, ", "); - } - - if block_stmt && p.config.semicolons { - print(p, semicolon); - } - case Defer_Stmt: - newline_until_pos(p, v.pos); - print(p, "defer"); - - if block, ok := v.stmt.derived.(ast.Block_Stmt); !ok { - print(p, space); - } - - print_stmt(p, v.stmt); - - if p.config.semicolons { - print(p, semicolon); - } - case When_Stmt: - newline_until_pos(p, v.pos); - print(p, "when", space); - print_expr(p, v.cond); - - print_stmt(p, v.body); - - if v.else_stmt != nil { - - if p.config.brace_style == .Allman { - print(p, newline); - } else { - print(p, space); - } - - print(p, "else"); - - if when_stmt, ok := v.else_stmt.derived.(ast.When_Stmt); ok { - print(p, space); - } - - set_source_position(p, v.else_stmt.pos); - - print_stmt(p, v.else_stmt); - } - - case Branch_Stmt: - - newline_until_pos(p, v.pos); - - print(p, v.tok); - - if v.label != nil { - print(p, space); - print_expr(p, v.label); - } - - if p.config.semicolons { - print(p, semicolon); - } - case: - panic(fmt.aprint(stmt.derived)); - } - - set_source_position(p, stmt.end); -} - -print_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { - - using ast; - - if decl == nil { - return; - } - - switch v in decl.derived { - case Expr_Stmt: - newline_until_pos(p, decl.pos); - print_expr(p, v.expr); - if p.config.semicolons { - print(p, semicolon); - } - case When_Stmt: - print_stmt(p, cast(^Stmt)decl); - case Foreign_Import_Decl: - if len(v.attributes) > 0 { - newline_until_pos(p, v.attributes[0].pos); - } else { - newline_until_pos(p, decl.pos); - } - - print_attributes(p, v.attributes); - - if v.name != nil { - print(p, v.foreign_tok, space, v.import_tok, space, v.name^, space); - } else { - print(p, v.foreign_tok, space, v.import_tok, space); - } - - for path in v.fullpaths { - print(p, path); - } - case Foreign_Block_Decl: - - if len(v.attributes) > 0 { - newline_until_pos(p, v.attributes[0].pos); - } else { - newline_until_pos(p, decl.pos); - } - - print_attributes(p, v.attributes); - - print(p, newline, "foreign", space); - print_expr(p, v.foreign_library); - print_stmt(p, v.body); - case Import_Decl: - newline_until_pos(p, decl.pos); - - if v.name.text != "" { - print(p, v.import_tok, " ", v.name, " ", v.fullpath); - } else { - print(p, v.import_tok, " ", v.fullpath); - } - - case Value_Decl: - if len(v.attributes) > 0 { - newline_until_pos(p, v.attributes[0].pos); - print_attributes(p, v.attributes); - } - - newline_until_pos(p, decl.pos); - - if v.is_using { - print(p, "using", space); - } - - print_exprs(p, v.names, ", "); - - seperator := ":"; - - if !v.is_mutable && v.type == nil { - seperator = ":: "; - } else if !v.is_mutable && v.type != nil { - seperator = " :"; - } - - if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Colon_And_Equals { - print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); - } - - if v.type != nil { - print(p, seperator, space); - - if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals { - print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); - } else if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Colon_And_Equals { - print_space_padding(p, p.align_info.value_decl_aligned_type_padding - (v.type.end.column - v.type.pos.column)); - } - - print_expr(p, v.type); - - if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals && len(v.values) != 0 { - print_space_padding(p, p.align_info.value_decl_aligned_type_padding - (v.type.end.column - v.type.pos.column)); - } - } else { - if in_value_decl_alignment(p, v) && p.config.align_style == .Align_On_Type_And_Equals { - print_space_padding(p, p.align_info.value_decl_aligned_padding - get_length_of_names(v.names)); - } - print(p, space, seperator); - } - - if v.is_mutable && v.type != nil && len(v.values) != 0 { - print(p, space, "=", space); - } else if v.is_mutable && v.type == nil && len(v.values) != 0 { - print(p, "=", space); - } else if !v.is_mutable && v.type != nil { - print(p, space, ":", space); - } - - print_exprs(p, v.values, ", "); - - add_semicolon := true; - - for value in v.values { - switch a in value.derived { - case Proc_Lit,Union_Type,Enum_Type,Struct_Type: - add_semicolon = false || called_in_stmt; - } - } - - if add_semicolon && p.config.semicolons && !p.skip_semicolon { - print(p, semicolon); - } - - case: - panic(fmt.aprint(decl.derived)); - } -} print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { @@ -1265,7 +1358,7 @@ print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { for attribute, i in attributes { print(p, "@", lparen); - print_exprs(p, attribute.elems, ", "); + visit_exprs(p, attribute.elems, ", "); print(p, rparen); if len(attributes) - 1 != i { @@ -1279,7 +1372,7 @@ print_file :: proc(p: ^Printer, file: ^ast.File) { p.comments = file.comments; p.file = file; - newline_until_pos(p, file.pkg_token.pos); + move_line(p, file.pkg_token.pos); print(p, file.pkg_token, space, file.pkg_name); @@ -1297,49 +1390,6 @@ print_file :: proc(p: ^Printer, file: ^ast.File) { write_whitespaces(p, p.current_whitespace); } -print_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { - - set_source_position(p, begin); - - newline_braced := p.config.brace_style == .Allman; - newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; - newline_braced &= p.config.brace_style != ._1TBS; - - if newline_braced { - print(p, newline); - print(p, lbrace); - print(p, indent); - } else { - - if type != .Comp_Lit && p.last_out_position.line == (p.out_position.line + get_current_newlines(p)) { - print(p, space); - } - print(p, lbrace); - print(p, indent); - } -} - -print_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { - set_source_position(p, end); - print(p, newline, unindent, rbrace); -} - -print_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false) { - for stmt, i in stmts { - - if newline_each { - print(p, newline); - } - - if value_decl, ok := stmt.derived.(ast.Value_Decl); ok { - set_value_decl_alignment_padding(p, value_decl, stmts[i + 1:]); - } else if assignment_stmt, ok := stmt.derived.(ast.Assign_Stmt); ok { - set_assign_alignment_padding(p, assignment_stmt, stmts[i + 1:]); - } - - print_stmt(p, stmt, .Generic, false, true); - } -} */ From 1d3458cadbda0d2d01edf5817e11b4664eeb708a Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 12 Apr 2021 22:35:26 +0200 Subject: [PATCH 003/137] single line comments work --- core/odin/printer/printer.odin | 16 +- core/odin/printer/visit.odin | 423 +++++++++++++++------------------ 2 files changed, 201 insertions(+), 238 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index d783bfb48..691369421 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -26,14 +26,19 @@ Printer :: struct { 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: map [int]^Line, skip_semicolon: bool, current_line: ^Line, current_line_index: int, + last_line_index: int, last_token: ^Format_Token, + merge_next_token: bool, + debug: bool, } Config :: struct { @@ -86,13 +91,16 @@ 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; + for decl in file.decls { - visit_stmt(p, decl); + visit_decl(p, cast(^ast.Decl)decl); } fix_lines(p); @@ -102,7 +110,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { last_line := 0; for key, value in p.lines { - diff_line := min(key - last_line, p.config.newline_limit); + diff_line := key - last_line; for i := 0; i < diff_line; i += 1 { strings.write_byte(&builder, '\n'); @@ -112,6 +120,10 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { strings.write_byte(&builder, ' '); } + if p.debug { + strings.write_string(&builder, fmt.tprintf("line %v: ", key)); + } + for format_token in value.format_tokens { for i := 0; i < format_token.spaces_before; i += 1 { diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 12f7da301..dcacef825 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -8,21 +8,104 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" - + @(private) -append_format_token :: proc(p: ^Printer, unwrapped_line: ^Line, format_token: Format_Token) -> ^Format_Token { +comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { + + if len(p.comments) <= p.latest_comment_index { + return false; + } + + comment := p.comments[p.latest_comment_index]; + + return comment.pos.offset < pos.offset; +} + +@(private) +next_comment_group :: proc(p: ^Printer) { + p.latest_comment_index += 1; +} + +@(private) +write_comment :: proc(p: ^Printer, comment: tokenizer.Token) { + + if len(comment.text) == 0 { + return; + } + + if comment.text[0] == '/' && comment.text[1] == '/' { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = comment.text, + }; + + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } + + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + } +} + +@(private) +write_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Token) { + + prev_comment: ^tokenizer.Token; + + for comment_before_position(p, pos) { + + comment_group := p.comments[p.latest_comment_index]; + lines := comment_group.pos.line - p.last_source_position.line; + + set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + + for comment, i in comment_group.list { + + if prev_comment != nil && p.last_source_position.line != comment.pos.line { + newline_position(p, comment.pos.line - prev_comment.pos.line); + } + + write_comment(p, comment); + + prev_comment = &comment_group.list[i]; + } + + next_comment_group(p); + } + + if prev_comment != nil { + newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); + } +} + +@(private) +append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_Token { format_token := format_token; - if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || + if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || - p.last_token.kind == .Open_Brace) { + p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { format_token.spaces_before = 0; + } else if p.merge_next_token { + format_token.spaces_before = 0; + p.merge_next_token = false; } + write_comments(p, p.source_position, format_token); + + unwrapped_line := p.current_line; + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { format_token.spaces_before = 0; } + + p.last_source_position = p.source_position; + p.last_line_index = p.current_line_index; append(&unwrapped_line.format_tokens, format_token); return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens)-1]; @@ -31,10 +114,6 @@ append_format_token :: proc(p: ^Printer, unwrapped_line: ^Line, format_token: Fo @(private) push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { - unwrapped_line := p.current_line; - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; - format_token := Format_Token { spaces_before = spaces_before, kind = kind, @@ -45,38 +124,31 @@ push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_befor format_token.text = value; } - p.last_token = append_format_token(p, unwrapped_line, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_string_token :: proc(p: ^Printer, text: string, spaces_before: int) { - unwrapped_line := p.current_line; - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; - + format_token := Format_Token { spaces_before = spaces_before, kind = .String, text = text, }; - p.last_token = append_format_token(p, unwrapped_line, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_ident_token :: proc(p: ^Printer, text: string, spaces_before: int) { - unwrapped_line := p.current_line; - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; - format_token := Format_Token { spaces_before = spaces_before, kind = .Ident, text = text, }; - p.last_token = append_format_token(p, unwrapped_line, format_token); + p.last_token = append_format_token(p, format_token); } @(private) @@ -112,12 +184,13 @@ set_line :: proc(p: ^Printer, line: int) -> ^Line { } p.current_line = unwrapped_line; + p.current_line_index = line; return unwrapped_line; } @(private) -newline_source_position :: proc(p: ^Printer, count: int) { +newline_position :: proc(p: ^Printer, count: int) { p.current_line_index += count; set_line(p, p.current_line_index); } @@ -132,6 +205,10 @@ unindent :: proc(p: ^Printer) { p.depth -= 1; } +merge_next_token :: proc(p: ^Printer) { + p.merge_next_token = true; +} + @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { @@ -231,7 +308,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { } else if v.is_mutable && v.type == nil && len(v.values) != 0 { push_generic_token(p, .Eq, 0); } else if !v.is_mutable && v.type != nil { - push_generic_token(p, .Semicolon, 0); + push_generic_token(p, .Colon, 0); } visit_exprs(p, v.values, true); @@ -240,7 +317,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { for value in v.values { switch a in value.derived { - case Proc_Lit,Union_Type,Enum_Type,Struct_Type: + case Proc_Lit, Union_Type, Enum_Type, Struct_Type: add_semicolon = false || called_in_stmt; } } @@ -268,17 +345,32 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing visit_expr(p, expr); - if i != len(list) - 1 && add_comma { + if (i != len(list) - 1 || trailing) && add_comma { push_generic_token(p, .Comma, 0); - } else if trailing && add_comma { - //print(p, strings.trim_space(sep)); - } + } } } @(private) visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { + if len(attributes) == 0 { + return; + } + + for attribute, i in attributes { + + push_generic_token(p, .At, 0); + push_generic_token(p, .Open_Paren, 0); + + visit_exprs(p, attribute.elems, true); + + push_generic_token(p, .Close_Paren, 0); + + if len(attributes) - 1 != i { + newline_position(p, 1); + } + } } @(private) @@ -390,7 +482,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_stmt(p, v.body, .If_Stmt, true); } else { if uses_do { - newline_source_position(p, 1); + newline_position(p, 1); } visit_stmt(p, v.body, .If_Stmt); @@ -399,7 +491,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.else_stmt != nil { if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { - newline_source_position(p, 1); + newline_position(p, 1); } push_generic_token(p, .Else, 1); @@ -498,14 +590,10 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.label != nil { visit_expr(p, v.label); - push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); } - push_generic_token(p, .For, 0); - - //if v.init != nil || v.cond != nil || v.post != nil { - // print(p, space); - //} + push_generic_token(p, .For, 1); if v.init != nil { p.skip_semicolon = true; @@ -522,7 +610,6 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.post != nil { push_generic_token(p, .Semicolon, 0); - //print(p, space); visit_stmt(p, v.post); } else if v.post == nil && v.cond != nil && v.init != nil { push_generic_token(p, .Semicolon, 0); @@ -540,7 +627,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_ident_token(p, "#unroll", 0); - push_generic_token(p, .For, 0); + push_generic_token(p, .For, 1); visit_expr(p, v.val0); if v.val1 != nil { @@ -561,7 +648,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Colon, 1); } - push_generic_token(p, .For, 0); + push_generic_token(p, .For, 1); if len(v.vals) >= 1 { visit_expr(p, v.vals[0]); @@ -580,7 +667,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener case Return_Stmt: move_line(p, v.pos); - push_generic_token(p, .Return, 0); + push_generic_token(p, .Return, 1); if v.results != nil { visit_exprs(p, v.results, true); @@ -600,7 +687,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } case When_Stmt: move_line(p, v.pos); - push_generic_token(p, .When, 0); + push_generic_token(p, .When, 1); visit_expr(p, v.cond); visit_stmt(p, v.body); @@ -608,10 +695,10 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.else_stmt != nil { if p.config.brace_style == .Allman { - newline_source_position(p, 1); + newline_position(p, 1); } - push_generic_token(p, .Else, 0); + push_generic_token(p, .Else, 1); set_source_position(p, v.else_stmt.pos); @@ -734,7 +821,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Bracket, 0); visit_expr(p, v.elem); case Bit_Set_Type: - push_generic_token(p, .Bit_Set, 0); + push_generic_token(p, .Bit_Set, 1); push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.elem); @@ -750,7 +837,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.poly_params != nil { push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, ", "); + visit_field_list(p, v.poly_params, true, true); push_generic_token(p, .Close_Paren, 0); } @@ -760,18 +847,17 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); - set_source_position(p, v.variants[len(v.variants) - 1].pos); visit_exprs(p, v.variants, true); push_generic_token(p, .Close_Brace, 0); } else { visit_begin_brace(p, v.pos, .Generic); - newline_source_position(p, 1); - set_source_position(p, v.variants[len(v.variants) - 1].pos); + newline_position(p, 1); + set_source_position(p, v.variants[0].pos); visit_exprs(p, v.variants, true, true); visit_end_brace(p, v.end); } case Enum_Type: - push_generic_token(p, .Enum, 0); + push_generic_token(p, .Enum, 1); if v.base_type != nil { visit_expr(p, v.base_type); @@ -779,21 +865,19 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); - set_source_position(p, v.fields[len(v.fields) - 1].pos); visit_exprs(p, v.fields, true); push_generic_token(p, .Close_Brace, 0); } else { visit_begin_brace(p, v.pos, .Generic); - newline_source_position(p, 1); - set_source_position(p, v.fields[len(v.fields) - 1].pos); - //visit_enum_fields(p, v.fields, ","); - visit_exprs(p, v.fields, true); + newline_position(p, 1); + set_source_position(p, v.fields[0].pos); + visit_exprs(p, v.fields, true, true); visit_end_brace(p, v.end); } set_source_position(p, v.end); case Struct_Type: - push_generic_token(p, .Struct, 0); + push_generic_token(p, .Struct, 1); if v.is_packed { push_ident_token(p, "#packed", 1); @@ -810,21 +894,20 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.poly_params != nil { push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, ", "); + visit_field_list(p, v.poly_params, true, true); push_generic_token(p, .Close_Paren, 0); } if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, ", "); + visit_field_list(p, v.fields, true); push_generic_token(p, .Close_Brace, 0); } else { visit_begin_brace(p, v.pos, .Generic); - newline_source_position(p, 1); + newline_position(p, 1); set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, ", "); - //visit_struct_field_list(p, v.fields, ","); + visit_field_list(p, v.fields, true, true); visit_end_brace(p, v.end); } @@ -880,7 +963,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Paren, 0); case Index_Expr: visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 1); + push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.index); push_generic_token(p, .Close_Bracket, 0); case Proc_Group: @@ -889,8 +972,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { visit_begin_brace(p, v.pos, .Generic); - newline_source_position(p, 1); - set_source_position(p, v.args[len(v.args) - 1].pos); + newline_position(p, 1); + set_source_position(p, v.args[0].pos); visit_exprs(p, v.args, true, true); visit_end_brace(p, v.end); } else { @@ -907,8 +990,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { visit_begin_brace(p, v.pos, .Comp_Lit); - newline_source_position(p, 1); - set_source_position(p, v.elems[len(v.elems) - 1].pos); + newline_position(p, 1); + set_source_position(p, v.elems[0].pos); visit_exprs(p, v.elems, true, true); visit_end_brace(p, v.end); } else { @@ -919,6 +1002,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Unary_Expr: push_generic_token(p, v.op.kind, 0); + merge_next_token(p); visit_expr(p, v.expr); case Field_Value: visit_expr(p, v.field); @@ -939,6 +1023,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Pointer_Type: push_generic_token(p, .Pointer, 0); + merge_next_token(p); visit_expr(p, v.elem); case Implicit: push_generic_token(p, v.tok.kind, 0); @@ -958,7 +1043,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Bracket, 0); visit_expr(p, v.elem); case Map_Type: - push_generic_token(p, .Map, 0); + push_generic_token(p, .Map, 1); push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.key); push_generic_token(p, .Close_Bracket, 0); @@ -980,7 +1065,7 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { newline_braced &= p.config.brace_style != ._1TBS; if newline_braced { - newline_source_position(p, 1); + newline_position(p, 1); push_generic_token(p, .Open_Brace, 0); indent(p); } else { @@ -991,7 +1076,7 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { set_source_position(p, end); - newline_source_position(p, 1); + newline_position(p, 1); unindent(p); push_generic_token(p, .Close_Brace, 0); } @@ -1000,14 +1085,14 @@ visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false for stmt, i in stmts { if newline_each { - newline_source_position(p, 1); + newline_position(p, 1); } visit_stmt(p, stmt, .Generic, false, true); } } -visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { +visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false) { if list.list == nil { return; @@ -1039,8 +1124,8 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { push_generic_token(p, field.tag.kind, 1); } - if i != len(list.list) - 1 { - //print(p, sep); + if (i != len(list.list) - 1 || trailing) && add_comma { + push_generic_token(p, .Comma, 0); } } } @@ -1049,20 +1134,22 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { push_generic_token(p, .Proc, 1); - if proc_type.calling_convention != .Odin { - //print(p, space); - } + explicit_calling := false; switch proc_type.calling_convention { case .Odin: case .Contextless: - push_string_token(p, "\"contextless\"", 0); + push_string_token(p, "\"contextless\"", 1); + explicit_calling = true; case .C_Decl: - push_string_token(p, "\"c\"", 0); + push_string_token(p, "\"c\"", 1); + explicit_calling = true; case .Std_Call: - push_string_token(p, "\"std\"", 0); + push_string_token(p, "\"std\"", 1); + explicit_calling = true; case .Fast_Call: - push_string_token(p, "\"fast\"", 0); + push_string_token(p, "\"fast\"", 1); + explicit_calling = true; case .None: //nothing i guess case .Invalid: @@ -1070,15 +1157,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { case .Foreign_Block_Default: } - push_generic_token(p, .Open_Paren, 0); + if explicit_calling { + push_generic_token(p, .Open_Paren, 1); + } else { + push_generic_token(p, .Open_Paren, 0); + } - //visit_signature_list(p, proc_type.params, ", ", false); + visit_signature_list(p, proc_type.params, false); push_generic_token(p, .Close_Paren, 0); - /* if proc_type.results != nil { - print(p, space, "->", space); + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); use_parens := false; use_named := false; @@ -1097,14 +1188,14 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { } if use_parens { - print(p, lparen); - print_signature_list(p, proc_type.results, ", "); - print(p, rparen); + push_generic_token(p, .Open_Paren, 1); + visit_signature_list(p, proc_type.results); + push_generic_token(p, .Close_Paren, 0); } else { - print_signature_list(p, proc_type.results, ", "); + visit_signature_list(p, proc_type.results); } } - */ + } visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { @@ -1173,126 +1264,8 @@ visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { } } -/* -print_enum_fields :: proc(p: ^Printer, list: []^ast.Expr, sep := " ") { - - //print enum fields is like visit_exprs, but it can contain fields that can be aligned. - - if len(list) == 0 { - return; - } - - if list[0].pos.line == list[len(list) - 1].pos.line { - //if everything is on one line, then it can be treated the same way as visit_exprs - visit_exprs(p, list, sep); - return; - } - - largest := 0; - last_field_value := 0; - - //first find all the field values and find the largest name - for expr, i in list { - - if field_value, ok := expr.derived.(ast.Field_Value); ok { - - if ident, ok := field_value.field.derived.(ast.Ident); ok { - largest = max(largest, strings.rune_count(ident.name)); - } - } - } - - for expr, i in list { - - move_line_limit(p, expr.pos, 1); - - if field_value, ok := expr.derived.(ast.Field_Value); ok && p.config.align_assignments { - - if ident, ok := field_value.field.derived.(ast.Ident); ok { - visit_expr(p, field_value.field); - print_space_padding(p, largest - strings.rune_count(ident.name) + 1); - print(p, "=", space); - visit_expr(p, field_value.value); - } else { - visit_expr(p, expr); - } - } else { - visit_expr(p, expr); - } - - if i != len(list) - 1 { - print(p, sep); - } else { - print(p, strings.trim_space(sep)); - } - } -} - - - - - - - -print_struct_field_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "") { - - if list.list == nil { - return; - } - - largest := 0; - using_size := len("using "); - - //NOTE(Daniel): Is there any other variables than using in structs? - - for field, i in list.list { - if .Using in field.flags { - largest = max(largest, get_length_of_names(field.names) + using_size); - } else { - largest = max(largest, get_length_of_names(field.names)); - } - } - - for field, i in list.list { - - move_line_limit(p, field.pos, 1); - - if .Using in field.flags { - print(p, "using", space); - } - - visit_exprs(p, field.names, ", "); - - if len(field.names) != 0 { - print(p, ": "); - } - - if field.type == nil { - panic("struct field has to have types"); - } - - if .Using in field.flags { - print_space_padding(p, largest - get_length_of_names(field.names) - using_size); - } else { - print_space_padding(p, largest - get_length_of_names(field.names)); - } - - visit_expr(p, field.type); - - if field.tag.text != "" { - print(p, space, field.tag); - } - - if i != len(list.list) - 1 { - print(p, sep); - } else { - print(p, strings.trim_space(sep)); - } - } -} - -print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remove_blank := true) { +visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := true) { if list.list == nil { return; @@ -1303,7 +1276,7 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo move_line_limit(p, field.pos, 1); if .Using in field.flags { - print(p, "using", space); + push_generic_token(p, .Using, 0); } named := false; @@ -1321,34 +1294,33 @@ print_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, sep := "", remo } if named { - visit_exprs(p, field.names, ", "); + visit_exprs(p, field.names, true); if len(field.names) != 0 && field.type != nil { - print(p, ": "); - } else { - print(p, space); - } + push_generic_token(p, .Colon, 0); + } } if field.type != nil && field.default_value != nil { visit_expr(p, field.type); - print(p, space, "=", space); + push_generic_token(p, .Eq, 0); visit_expr(p, field.default_value); } else if field.type != nil { visit_expr(p, field.type); } else { - print(p, ":= "); + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Eq, 0); visit_expr(p, field.default_value); } if i != len(list.list) - 1 { - print(p, sep); + push_generic_token(p, .Comma, 0); } } } - +/* print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { if len(attributes) == 0 { @@ -1367,28 +1339,7 @@ print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { } } -print_file :: proc(p: ^Printer, file: ^ast.File) { - p.comments = file.comments; - p.file = file; - - move_line(p, file.pkg_token.pos); - - print(p, file.pkg_token, space, file.pkg_name); - - for decl, i in file.decls { - - if value_decl, ok := decl.derived.(ast.Value_Decl); ok { - set_value_decl_alignment_padding(p, value_decl, file.decls[i + 1:]); - } - - print_decl(p, cast(^ast.Decl)decl); - } - - //todo(probably check if there already is a newline, but there really shouldn't be) - print(p, newline); //finish document with newline - write_whitespaces(p, p.current_whitespace); -} */ From 2e8da35851af9b60499b47c68a54c387a984be09 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 13 Apr 2021 15:27:04 +0200 Subject: [PATCH 004/137] multiline comments work --- core/odin/printer/visit.odin | 153 ++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index dcacef825..71c125e93 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -27,10 +27,10 @@ next_comment_group :: proc(p: ^Printer) { } @(private) -write_comment :: proc(p: ^Printer, comment: tokenizer.Token) { +write_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { if len(comment.text) == 0 { - return; + return 0; } if comment.text[0] == '/' && comment.text[1] == '/' { @@ -44,8 +44,90 @@ write_comment :: proc(p: ^Printer, comment: tokenizer.Token) { format_token.spaces_before = 0; } + p.current_line.used = true; + p.current_line.depth = p.depth; + append(&p.current_line.format_tokens, format_token); p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + + return 0; + } else { + + builder := strings.make_builder(context.temp_allocator); + + c_len := len(comment.text); + trim_space := true; + + multilines: [dynamic] string; + + for i := 0; i < len(comment.text); i += 1 { + + c := comment.text[i]; + + if c != ' ' && c != '\t' { + trim_space = false; + } + + if (c == ' ' || c == '\t' || c == '\n') && trim_space { + continue; + } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + i += 1; + } else if c == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { + strings.write_string(&builder, "/*"); + trim_space = true; + p.depth += 1; + i += 1; + } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { + p.depth -= 1; + trim_space = true; + strings.write_string(&builder, "*/"); + i += 1; + } else { + strings.write_byte(&builder, c); + } + + } + + if strings.builder_len(builder) > 0 { + append(&multilines, strings.to_string(builder)); + } + + for line in multilines { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = line, + }; + + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } + + if strings.contains(line, "*/") { + unindent(p); + } + + p.current_line.used = true; + p.current_line.depth = p.depth; + + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + + if strings.contains(line, "/*") { + indent(p); + } + + newline_position(p, 1); + } + + return len(multilines); } } @@ -53,22 +135,24 @@ write_comment :: proc(p: ^Printer, comment: tokenizer.Token) { write_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Token) { prev_comment: ^tokenizer.Token; + prev_comment_lines: int; for comment_before_position(p, pos) { comment_group := p.comments[p.latest_comment_index]; - lines := comment_group.pos.line - p.last_source_position.line; - set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + if prev_comment == nil { + lines := comment_group.pos.line - p.last_source_position.line; + set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + } for comment, i in comment_group.list { - + if prev_comment != nil && p.last_source_position.line != comment.pos.line { - newline_position(p, comment.pos.line - prev_comment.pos.line); + newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); } - write_comment(p, comment); - + prev_comment_lines = write_comment(p, comment); prev_comment = &comment_group.list[i]; } @@ -792,7 +876,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_expr(p, v.type); case Slice_Expr: visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 1); + push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.low); push_generic_token(p, v.interval.kind, 0); visit_expr(p, v.high); @@ -803,7 +887,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_expr(p, v.expr); push_generic_token(p, v.op.kind, 0); case Type_Cast: - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 1); push_generic_token(p, .Open_Paren, 1); visit_expr(p, v.type); push_generic_token(p, .Close_Paren, 0); @@ -812,7 +896,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, v.tok.kind, 0); push_ident_token(p, v.name, 0); case Distinct_Type: - push_generic_token(p, .Distinct, 0); + push_generic_token(p, .Distinct, 1); visit_expr(p, v.type); case Dynamic_Array_Type: visit_expr(p, v.tag); @@ -833,11 +917,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Bracket, 0); case Union_Type: - push_generic_token(p, .Union, 0); + push_generic_token(p, .Union, 1); if v.poly_params != nil { push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, true); + visit_field_list(p, v.poly_params, true, false); push_generic_token(p, .Close_Paren, 0); } @@ -894,7 +978,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.poly_params != nil { push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, true); + visit_field_list(p, v.poly_params, true, false); push_generic_token(p, .Close_Paren, 0); } @@ -947,7 +1031,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); push_generic_token(p, .Close_Paren, 0); case Typeid_Type: - push_generic_token(p, .Typeid, 0); + push_generic_token(p, .Typeid, 1); if v.specialization != nil { push_generic_token(p, .Quo, 0); @@ -1001,7 +1085,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } case Unary_Expr: - push_generic_token(p, v.op.kind, 0); + push_generic_token(p, v.op.kind, 1); merge_next_token(p); visit_expr(p, v.expr); case Field_Value: @@ -1022,18 +1106,19 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } case Pointer_Type: - push_generic_token(p, .Pointer, 0); + push_generic_token(p, .Pointer, 1); merge_next_token(p); visit_expr(p, v.elem); case Implicit: - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 1); case Poly_Type: - push_generic_token(p, .Dollar, 0); - + push_generic_token(p, .Dollar, 1); + merge_next_token(p); visit_expr(p, v.type); if v.specialization != nil { push_generic_token(p, .Quo, 0); + merge_next_token(p); visit_expr(p, v.specialization); } case Array_Type: @@ -1077,8 +1162,9 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { set_source_position(p, end); newline_position(p, 1); - unindent(p); push_generic_token(p, .Close_Brace, 0); + unindent(p); + p.current_line.depth = p.depth; } visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false) { @@ -1319,28 +1405,3 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := } } - -/* -print_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { - - if len(attributes) == 0 { - return; - } - - for attribute, i in attributes { - - print(p, "@", lparen); - visit_exprs(p, attribute.elems, ", "); - print(p, rparen); - - if len(attributes) - 1 != i { - print(p, newline); - } - } -} - - - -*/ - - From d046c9c07202f0e493d79eb7132cf5f077a833cc Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 13 Apr 2021 20:18:50 +0200 Subject: [PATCH 005/137] at the same point as the previous formatter now --- core/odin/printer/printer.odin | 1 + core/odin/printer/visit.odin | 51 +++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 691369421..7786de2b4 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -38,6 +38,7 @@ Printer :: struct { last_line_index: int, last_token: ^Format_Token, merge_next_token: bool, + space_next_token: bool, debug: bool, } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 71c125e93..ebd2a98ce 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -158,7 +158,7 @@ write_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Tok next_comment_group(p); } - + if prev_comment != nil { newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); } @@ -176,6 +176,9 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ } else if p.merge_next_token { format_token.spaces_before = 0; p.merge_next_token = false; + } else if p.space_next_token { + format_token.spaces_before = 1; + p.space_next_token = false; } write_comments(p, p.source_position, format_token); @@ -289,10 +292,15 @@ unindent :: proc(p: ^Printer) { p.depth -= 1; } +@(private) merge_next_token :: proc(p: ^Printer) { p.merge_next_token = true; } +space_next_token :: proc(p: ^Printer) { + p.space_next_token = true; +} + @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { @@ -374,7 +382,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { if !v.is_mutable && v.type != nil { push_generic_token(p, .Colon, 1); } else { - push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); } visit_expr(p, v.type); @@ -388,7 +396,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { } if v.is_mutable && v.type != nil && len(v.values) != 0 { - push_generic_token(p, .Eq, 0); + push_generic_token(p, .Eq, 1); } else if v.is_mutable && v.type == nil && len(v.values) != 0 { push_generic_token(p, .Eq, 0); } else if !v.is_mutable && v.type != nil { @@ -823,23 +831,22 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { switch v in expr.derived { case Inline_Asm_Expr: - /* - print(p, v.tok, space, lparen); - visit_exprs(p, v.param_types, ", "); - print(p, rparen, space); + push_generic_token(p, v.tok.kind, 1, v.tok.text); - print(p, "->", space); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.param_types, true, false); + push_generic_token(p, .Close_Paren, 0); + + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); visit_expr(p, v.return_type); - print(p, space); - - print(p, lbrace); + push_generic_token(p, .Open_Brace, 1); visit_expr(p, v.asm_string); - print(p, ", "); + push_generic_token(p, .Comma, 0); visit_expr(p, v.constraints_string); - print(p, rbrace); - */ + push_generic_token(p, .Close_Brace, 0); case Undef: push_generic_token(p, .Undef, 1); case Auto_Cast: @@ -893,7 +900,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Paren, 0); visit_expr(p, v.expr); case Basic_Directive: - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 1); push_ident_token(p, v.name, 0); case Distinct_Type: push_generic_token(p, .Distinct, 1); @@ -924,11 +931,17 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_field_list(p, v.poly_params, true, false); push_generic_token(p, .Close_Paren, 0); } - + if v.is_maybe { push_ident_token(p, "#maybe", 1); } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } + if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); visit_exprs(p, v.variants, true); @@ -982,6 +995,12 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Paren, 0); } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } + if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.fields.pos); From b09e53d7fe2256e976c38d089312606ea3b5a76f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 13 Apr 2021 23:42:58 +0200 Subject: [PATCH 006/137] now aligning comments --- core/odin/printer/printer.odin | 118 +++++++++++++++++++++++++++++---- core/odin/printer/visit.odin | 29 +++++--- 2 files changed, 125 insertions(+), 22 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 7786de2b4..67b494f2c 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,11 +8,16 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" +Line_Type_Enum :: enum{Line_Comment, Value_Decl}; + +Line_Type :: bit_set[Line_Type_Enum]; + Line :: struct { format_tokens: [dynamic] Format_Token, finalized: bool, used: bool, depth: int, + types: Line_Type, } Format_Token :: struct { @@ -31,7 +36,7 @@ Printer :: struct { file: ^ast.File, source_position: tokenizer.Pos, last_source_position: tokenizer.Pos, - lines: map [int]^Line, + 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, @@ -80,7 +85,7 @@ default_style := Config { newline_limit = 2, convert_do = false, semicolons = true, - tabs = true, + tabs = false, brace_style = ._1TBS, split_multiple_stmts = true, align_assignments = true, @@ -99,6 +104,15 @@ make_printer :: proc(config: Config, allocator := context.allocator) -> Printer 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_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); @@ -110,22 +124,28 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { last_line := 0; - for key, value in p.lines { - diff_line := key - last_line; - + for line, line_index in p.lines { + diff_line := line_index - last_line; + for i := 0; i < diff_line; i += 1 { strings.write_byte(&builder, '\n'); } - for i := 0; i < value.depth * 4; i += 1 { - strings.write_byte(&builder, ' '); + 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: ", key)); + strings.write_string(&builder, fmt.tprintf("line %v: ", line_index)); } - for format_token in value.format_tokens { + for format_token in line.format_tokens { for i := 0; i < format_token.spaces_before; i += 1 { strings.write_byte(&builder, ' '); @@ -134,23 +154,95 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { strings.write_string(&builder, format_token.text); } - last_line = key; + last_line = line_index; } return strings.to_string(builder); } fix_lines :: proc(p: ^Printer) { + align_comments(p); + align_var_decls(p); +} - for key, value in p.lines { +align_var_decls :: proc(p: ^Printer) { - if len(value.format_tokens) <= 0 { +} + +align_comments :: proc(p: ^Printer) { + + Comment_Align_Info :: struct { + length: int, + begin: int, + end: 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 { + + 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.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 { + + 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; + } + } + + length += format_token.spaces_before + len(format_token.text); + } + + } + + } + +} \ No newline at end of file diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index ebd2a98ce..f9a2b59d6 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -27,7 +27,7 @@ next_comment_group :: proc(p: ^Printer) { } @(private) -write_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { +push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { if len(comment.text) == 0 { return 0; @@ -50,6 +50,8 @@ write_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { append(&p.current_line.format_tokens, format_token); p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + hint_current_line(p, {.Line_Comment}); + return 0; } else { @@ -132,7 +134,7 @@ write_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { } @(private) -write_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Token) { +push_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Token) { prev_comment: ^tokenizer.Token; prev_comment_lines: int; @@ -152,7 +154,7 @@ write_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Tok newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); } - prev_comment_lines = write_comment(p, comment); + prev_comment_lines = push_comment(p, comment); prev_comment = &comment_group.list[i]; } @@ -181,7 +183,7 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ p.space_next_token = false; } - write_comments(p, p.source_position, format_token); + push_comments(p, p.source_position, format_token); unwrapped_line := p.current_line; unwrapped_line.used = true; @@ -262,12 +264,15 @@ set_line :: proc(p: ^Printer, line: int) -> ^Line { unwrapped_line: ^Line; - if line not_in p.lines { - unwrapped_line = new(Line, p.allocator); - unwrapped_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); - p.lines[line] = unwrapped_line; + if line >= len(p.lines) { + for i := len(p.lines); i <= line; i += 1 { + new_line: Line; + new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); + append(&p.lines, new_line); + } + unwrapped_line = &p.lines[line]; } else { - unwrapped_line = p.lines[line]; + unwrapped_line = &p.lines[line]; } p.current_line = unwrapped_line; @@ -297,10 +302,16 @@ merge_next_token :: proc(p: ^Printer) { p.merge_next_token = true; } +@(private) space_next_token :: proc(p: ^Printer) { p.space_next_token = true; } +@(private) +hint_current_line :: proc(p: ^Printer, hint: Line_Type) { + p.current_line.types |= hint; +} + @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { From 2001384ae64039dbab3d9704cdd94843ac8919d3 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 13 Apr 2021 23:52:23 +0200 Subject: [PATCH 007/137] make sure to print comments at the end of the file --- core/odin/printer/printer.odin | 15 +++++++++++++++ core/odin/printer/visit.odin | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 67b494f2c..fd62f96f0 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -118,6 +118,12 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { 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(p.allocator); @@ -163,12 +169,17 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { fix_lines :: proc(p: ^Printer) { align_comments(p); align_var_decls(p); + align_blocks(p); } align_var_decls :: proc(p: ^Printer) { } +align_blocks :: proc(p: ^Printer) { + +} + align_comments :: proc(p: ^Printer) { Comment_Align_Info :: struct { @@ -222,6 +233,10 @@ align_comments :: proc(p: ^Printer) { for info in comment_infos { + if info.begin == info.end { + continue; + } + for i := info.begin; i <= info.end; i += 1 { l := p.lines[i]; diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index f9a2b59d6..419cc09c5 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -134,7 +134,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { } @(private) -push_comments :: proc(p: ^Printer, pos: tokenizer.Pos, format_token: Format_Token) { +push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { prev_comment: ^tokenizer.Token; prev_comment_lines: int; @@ -183,7 +183,7 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ p.space_next_token = false; } - push_comments(p, p.source_position, format_token); + push_comments(p, p.source_position); unwrapped_line := p.current_line; unwrapped_line.used = true; From 3157467e4becbdf0225df4f5d0004e2ddf98ea75 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Tue, 13 Apr 2021 23:59:40 +0200 Subject: [PATCH 008/137] bring over the odinfmt code --- core/odin/format/format.odin | 35 ++++++ tools/odinfmt/flag/flag.odin | 232 +++++++++++++++++++++++++++++++++++ tools/odinfmt/main.odin | 140 +++++++++++++++++++++ 3 files changed, 407 insertions(+) create mode 100644 core/odin/format/format.odin create mode 100644 tools/odinfmt/flag/flag.odin create mode 100644 tools/odinfmt/main.odin diff --git a/core/odin/format/format.odin b/core/odin/format/format.odin new file mode 100644 index 000000000..d86808669 --- /dev/null +++ b/core/odin/format/format.odin @@ -0,0 +1,35 @@ +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(source: [] u8, config: printer.Config, allocator := context.allocator) -> ([] u8, bool) { + + pkg := ast.Package { + kind = .Normal, + }; + + file := ast.File { + pkg = &pkg, + src = source, + }; + + p := parser.default_parser(); + + ok := parser.parse_file(&p, &file); + + if !ok || file.syntax_error_count > 0 { + return {}, false; + } + + prnt := printer.make_printer(config, allocator); + + return transmute([]u8) printer.print(&prnt, &file), true; +} \ No newline at end of file diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin new file mode 100644 index 000000000..543438678 --- /dev/null +++ b/tools/odinfmt/flag/flag.odin @@ -0,0 +1,232 @@ +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; + } + + //must be in the next argument + else 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"; +} \ No newline at end of file diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin new file mode 100644 index 000000000..78624bd94 --- /dev/null +++ b/tools/odinfmt/main.odin @@ -0,0 +1,140 @@ +package odinfmt + +import "core:os" +import "core:odin/format" +import "core:fmt" +import "core:strings" +import "core:path/filepath" + +import "flag" + +Args :: struct { + write: Maybe(bool) `flag:"w" usage:"write the new format to file"`, +} + +print_help :: proc() { + +} + +print_arg_error :: proc(error: flag.Flag_Error) { + fmt.println(error); +} + +format_file :: proc(filepath: string) -> ([] u8, bool) { + + if data, ok := os.read_entire_file(filepath); ok { + return format.format(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() { + + args: Args; + + if len(os.args) < 2 { + print_help(); + 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]; + + if os.is_file(path) { + + if _, ok := args.write.(bool); ok { + + backup_path := strings.concatenate({path, "_bk"}, context.temp_allocator); + + if data, ok := format_file(path); ok { + + os.rename(path, backup_path); + + if os.write_entire_file(path, data) { + os.remove(backup_path); + } + + } + + else { + fmt.eprintf("failed to write %v", path); + } + + } + + else { + + if data, ok := format_file(path); ok { + fmt.println(transmute(string)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"}, context.temp_allocator); + + if data, ok := format_file(file); ok { + + if _, ok := args.write.(bool); ok { + os.rename(file, backup_path); + + if os.write_entire_file(file, data) { + os.remove(backup_path); + } + } + + else { + fmt.println(transmute(string)data); + } + + + } + + free_all(context.temp_allocator); + } + + fmt.printf("formatted %v files", len(files)); + + } + + else{ + fmt.eprintf("%v is neither a directory nor a file \n", path); + os.exit(1); + } + + os.exit(0); +} \ No newline at end of file From c99afd04ada0ac1eba0ee279cc0f38918bb12f4e Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 00:08:09 +0200 Subject: [PATCH 009/137] work --- core/odin/printer/visit.odin | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 419cc09c5..b470b7fe7 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -612,10 +612,10 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } if v.partial { - push_ident_token(p, "#partial", 0); + push_ident_token(p, "#partial", 1); } - push_generic_token(p, .Switch, 0); + push_generic_token(p, .Switch, 1); if v.init != nil { p.skip_semicolon = true; @@ -662,10 +662,10 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } if v.partial { - push_ident_token(p, "partial", 0); + push_ident_token(p, "#partial", 1); } - push_generic_token(p, .Switch, 0); + push_generic_token(p, .Switch, 1); visit_stmt(p, v.tag); visit_stmt(p, v.body); @@ -897,7 +897,10 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.low); push_generic_token(p, v.interval.kind, 0); - visit_expr(p, v.high); + if v.high != nil { + merge_next_token(p); + visit_expr(p, v.high); + } push_generic_token(p, .Close_Bracket, 0); case Ident: push_ident_token(p, v.name, 1); From 1cb3a31f326708627ef6e885e914b37246544068 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 00:53:34 +0200 Subject: [PATCH 010/137] have to sort attributes because they are not ordered by position(maybe bug on parser) --- core/odin/printer/visit.odin | 61 +++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index b470b7fe7..9794635ef 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -7,7 +7,26 @@ import "core:runtime" import "core:fmt" import "core:unicode/utf8" import "core:mem" +import "core:sort" +@(private) +sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { + return sort.Interface { + collection = rawptr(s), + len = proc(it: sort.Interface) -> int { + s := (^[dynamic]^ast.Attribute)(it.collection); + return len(s^); + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + s := (^[dynamic]^ast.Attribute)(it.collection); + return s[i].pos.offset < s[j].pos.offset; + }, + swap = proc(it: sort.Interface, i, j: int) { + s := (^[dynamic]^ast.Attribute)(it.collection); + s[i], s[j] = s[j], s[i]; + }, + }; +} @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { @@ -253,6 +272,11 @@ move_line :: proc(p: ^Printer, pos: tokenizer.Pos) { @(private) move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> bool { lines := min(pos.line - p.source_position.line, limit); + + if lines < 0 { + return false; + } + p.source_position = pos; p.current_line_index += lines; set_line(p, p.current_line_index); @@ -321,7 +345,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { return; } - switch v in decl.derived { + switch v in &decl.derived { case Expr_Stmt: move_line(p, decl.pos); visit_expr(p, v.expr); @@ -332,13 +356,13 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { visit_stmt(p, cast(^Stmt)decl); case Foreign_Import_Decl: if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); move_line(p, v.attributes[0].pos); - } else { - move_line(p, decl.pos); - } - - visit_attributes(p, v.attributes); + visit_attributes(p, v.attributes); + } + move_line(p, decl.pos); + push_generic_token(p, v.foreign_tok.kind, 0); push_generic_token(p, v.import_tok.kind, 1); @@ -352,13 +376,13 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { case Foreign_Block_Decl: if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); move_line(p, v.attributes[0].pos); - } else { - move_line(p, decl.pos); - } - - visit_attributes(p, v.attributes); - + visit_attributes(p, v.attributes); + } + + move_line(p, decl.pos); + push_generic_token(p, .Foreign, 0); visit_expr(p, v.foreign_library); @@ -377,6 +401,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { case Value_Decl: if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); move_line(p, v.attributes[0].pos); visit_attributes(p, v.attributes); } @@ -463,16 +488,14 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { for attribute, i in attributes { + move_line_limit(p, attribute.pos, 1); + push_generic_token(p, .At, 0); push_generic_token(p, .Open_Paren, 0); visit_exprs(p, attribute.elems, true); - push_generic_token(p, .Close_Paren, 0); - - if len(attributes) - 1 != i { - newline_position(p, 1); - } + push_generic_token(p, .Close_Paren, 0); } } @@ -1047,7 +1070,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { set_source_position(p, v.body.pos); visit_stmt(p, v.body, .Proc); } else { - push_generic_token(p, .Ellipsis, 1); + push_generic_token(p, .Undef, 1); } case Proc_Type: visit_proc_type(p, v); @@ -1240,7 +1263,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, } if field.tag.text != "" { - push_generic_token(p, field.tag.kind, 1); + push_generic_token(p, field.tag.kind, 1, field.tag.text); } if (i != len(list.list) - 1 || trailing) && add_comma { From 411beaa3bf981ae7a8baab348a417c05c55d29cd Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 01:15:46 +0200 Subject: [PATCH 011/137] work on comments --- core/odin/printer/printer.odin | 2 +- core/odin/printer/visit.odin | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index fd62f96f0..55a67ca70 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -233,7 +233,7 @@ align_comments :: proc(p: ^Printer) { for info in comment_infos { - if info.begin == info.end { + if info.begin == info.end || info.length == 0 { continue; } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 9794635ef..8899b8e5f 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -63,8 +63,10 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { format_token.spaces_before = 0; } - p.current_line.used = true; - p.current_line.depth = p.depth; + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } append(&p.current_line.format_tokens, format_token); p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; @@ -135,8 +137,10 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { unindent(p); } - p.current_line.used = true; - p.current_line.depth = p.depth; + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } append(&p.current_line.format_tokens, format_token); p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; @@ -416,7 +420,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { if v.type != nil { if !v.is_mutable && v.type != nil { - push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); } else { push_generic_token(p, .Colon, 0); } From cb4b7efd3effdf2d315b95828284223c9aa06769 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 01:57:42 +0200 Subject: [PATCH 012/137] only align comments in same indentation blocks --- core/odin/printer/printer.odin | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 55a67ca70..dc4cb1c1a 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -186,6 +186,7 @@ align_comments :: proc(p: ^Printer) { length: int, begin: int, end: int, + depth: int, }; comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator); @@ -200,7 +201,7 @@ align_comments :: proc(p: ^Printer) { if .Line_Comment in line.types { - if current_info.end + 1 != line_index { + if current_info.end + 1 != line_index || current_info.depth != line.depth { if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 { append(&comment_infos, current_info); @@ -208,6 +209,7 @@ align_comments :: proc(p: ^Printer) { current_info.begin = line_index; current_info.end = line_index; + current_info.depth = line.depth; current_info.length = 0; } From c46317c00b04cefe83101be6bb4231e3edcd2fff Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 02:20:05 +0200 Subject: [PATCH 013/137] fix os error --- core/odin/printer/printer.odin | 3 ++- core/os/file_windows.odin | 4 ++-- tools/odinfmt/main.odin | 10 +++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index dc4cb1c1a..bc2a0a913 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -201,7 +201,8 @@ align_comments :: proc(p: ^Printer) { if .Line_Comment in line.types { - if current_info.end + 1 != line_index || current_info.depth != line.depth { + 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); diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin index 4bb4c689f..8b99ee9ee 100644 --- a/core/os/file_windows.odin +++ b/core/os/file_windows.odin @@ -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; } diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index 78624bd94..4040fe4c2 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -5,6 +5,8 @@ import "core:odin/format" import "core:fmt" import "core:strings" import "core:path/filepath" +import "core:time" +import "core:mem" import "flag" @@ -51,6 +53,8 @@ walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip main :: proc() { + init_global_temporary_allocator(mem.megabytes(100)); + args: Args; if len(os.args) < 2 { @@ -65,6 +69,8 @@ main :: proc() { path := os.args[len(os.args)-1]; + tick_time := time.tick_now(); + if os.is_file(path) { if _, ok := args.write.(bool); ok { @@ -122,12 +128,14 @@ main :: proc() { } + } else { + fmt.eprintf("failed to format %v", file); } free_all(context.temp_allocator); } - fmt.printf("formatted %v files", len(files)); + fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time))); } From 1f563f2810e55fd2fefff6b29b64be9d2bf7d4e4 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 15:34:50 +0200 Subject: [PATCH 014/137] work on switch alignment --- core/odin/printer/printer.odin | 131 +++++++++++++++++++++++++++++++-- core/odin/printer/visit.odin | 32 ++++++-- 2 files changed, 149 insertions(+), 14 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index bc2a0a913..f9610682f 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,7 +8,7 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -Line_Type_Enum :: enum{Line_Comment, Value_Decl}; +Line_Type_Enum :: enum{Line_Comment, Value_Decl, Switch_Stmt}; Line_Type :: bit_set[Line_Type_Enum]; @@ -24,6 +24,7 @@ Format_Token :: struct { kind: tokenizer.Token_Kind, text: string, spaces_before: int, + parameter_count: int, } Printer :: struct { @@ -58,6 +59,7 @@ Config :: struct { align_assignments: bool, align_style: Alignment_Style, indent_cases: bool, + newline_style: Newline_Style, } Brace_Style :: enum { @@ -73,6 +75,7 @@ Block_Type :: enum { Proc, Generic, Comp_Lit, + Switch_Stmt, } Alignment_Style :: enum { @@ -80,6 +83,11 @@ Alignment_Style :: enum { Align_On_Type_And_Equals, } +Newline_Style :: enum { + CRLF, + LF, +} + default_style := Config { spaces = 4, newline_limit = 2, @@ -126,17 +134,25 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { fix_lines(p); - builder := strings.make_builder(p.allocator); + 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_byte(&builder, '\n'); - } - + 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'); @@ -167,17 +183,118 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { } fix_lines :: proc(p: ^Printer) { - align_comments(p); align_var_decls(p); align_blocks(p); + align_comments(p); //align them last since they rely on the other alignments } align_var_decls :: proc(p: ^Printer) { } +align_switch_smt :: 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; + 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; + + //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 in line.format_tokens { + + if format_token.kind == .Comment { + continue; + } + + //this will only happen if the case is one lined + if case_found && colon_found { + largest = max(length, largest); + break; + } + + if format_token.kind == .Case { + case_found = true; + } else if format_token.kind == .Colon { + colon_found = true; + } + + length += len(format_token.text) + format_token.spaces_before; + } + } + + 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 { + continue; + } + + //this will only happen if the case is one lined + if case_found && colon_found { + line.format_tokens[i].spaces_before += (largest - length); + break; + } + + if format_token.kind == .Case { + case_found = true; + } else if format_token.kind == .Colon { + colon_found = true; + } + + length += len(format_token.text) + format_token.spaces_before; + } + } + +} + align_blocks :: proc(p: ^Printer) { + for line, line_index in p.lines { + + if len(line.format_tokens) <= 0 { + continue; + } + + if .Switch_Stmt in line.types { + align_switch_smt(p, line_index); + } + + } + } align_comments :: proc(p: ^Printer) { diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 8899b8e5f..6afc04a61 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -9,6 +9,7 @@ import "core:unicode/utf8" import "core:mem" import "core:sort" +//right the attribute order is not linearly parsed(bug?) @(private) sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { return sort.Interface { @@ -209,8 +210,11 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ push_comments(p, p.source_position); unwrapped_line := p.current_line; - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; + + if !unwrapped_line.used { + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + } if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { format_token.spaces_before = 0; @@ -223,6 +227,11 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens)-1]; } +@(private) +push_format_token :: proc(p: ^Printer, format_token: Format_Token) { + p.last_token = append_format_token(p, format_token); +} + @(private) push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { @@ -541,7 +550,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.pos.line == v.end.line && len(v.stmts) > 1 && p.config.split_multiple_stmts { if !empty_block { - visit_begin_brace(p, v.pos, block_type); + visit_begin_brace(p, v.pos, block_type, len(v.stmts)); } set_source_position(p, v.pos); @@ -569,7 +578,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } } else { if !empty_block { - visit_begin_brace(p, v.pos, block_type); + visit_begin_brace(p, v.pos, block_type, len(v.stmts)); } set_source_position(p, v.pos); @@ -644,6 +653,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Switch, 1); + hint_current_line(p, {.Switch_Stmt}); + if v.init != nil { p.skip_semicolon = true; visit_stmt(p, v.init); @@ -1201,7 +1212,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } -visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { +visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0) { set_source_position(p, begin); @@ -1209,12 +1220,19 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type) { newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; newline_braced &= p.config.brace_style != ._1TBS; + format_token := Format_Token { + kind = .Open_Brace, + parameter_count = count, + text = "{", + }; + if newline_braced { newline_position(p, 1); - push_generic_token(p, .Open_Brace, 0); + push_format_token(p, format_token); indent(p); } else { - push_generic_token(p, .Open_Brace, 1); + format_token.spaces_before = 1; + push_format_token(p, format_token); indent(p); } } From 7e90ece84a24d956d9bd65cbe14614ef5dcc904e Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 16:03:36 +0200 Subject: [PATCH 015/137] more switch alignment work --- core/odin/printer/printer.odin | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index f9610682f..c47227d42 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,7 +8,7 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -Line_Type_Enum :: enum{Line_Comment, Value_Decl, Switch_Stmt}; +Line_Type_Enum :: enum{Line_Comment, Value_Decl, Switch_Stmt, Struct}; Line_Type :: bit_set[Line_Type_Enum]; @@ -17,7 +17,7 @@ Line :: struct { finalized: bool, used: bool, depth: int, - types: Line_Type, + 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 } Format_Token :: struct { @@ -55,8 +55,10 @@ Config :: struct { 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, indent_cases: bool, newline_style: Newline_Style, @@ -99,6 +101,8 @@ default_style := Config { align_assignments = true, align_style = .Align_On_Type_And_Equals, indent_cases = false, + align_switch = true, + align_structs = true, }; make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { @@ -221,6 +225,7 @@ align_switch_smt :: proc(p: ^Printer, index: int) { } largest := 0; + case_count := 0; //find all the switch cases that are one lined for line, line_index in p.lines[brace_line+1:] { @@ -243,14 +248,22 @@ align_switch_smt :: proc(p: ^Printer, index: int) { 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; + } + } + case_count = 0; + for line, line_index in p.lines[brace_line+1:] { case_found := false; @@ -271,16 +284,26 @@ align_switch_smt :: proc(p: ^Printer, index: int) { 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; + } + } } } +align_struct :: proc(p: ^Printer, index: int) { + +} + align_blocks :: proc(p: ^Printer) { for line, line_index in p.lines { @@ -289,8 +312,10 @@ align_blocks :: proc(p: ^Printer) { continue; } - if .Switch_Stmt in line.types { + if .Switch_Stmt in line.types && p.config.align_switch { align_switch_smt(p, line_index); + } else if .Struct in line.types && p.config.align_structs { + align_struct(p, line_index); } } From b0721f1e0c51f3f738459db9dc29534161553d4c Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 16:31:31 +0200 Subject: [PATCH 016/137] aligning structs now work --- core/odin/printer/printer.odin | 90 ++++++++++++++++++++++++++++++++-- core/odin/printer/visit.odin | 15 +++--- 2 files changed, 96 insertions(+), 9 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index c47227d42..6d591a6ae 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -208,7 +208,7 @@ align_switch_smt :: proc(p: ^Printer, index: int) { if format_token.kind == .Open_Brace && switch_found { brace_token = format_token; - brace_line = line_index; + brace_line = line_index+index; break found_switch_brace; } else if format_token.kind == .Open_Brace { break; @@ -294,7 +294,7 @@ align_switch_smt :: proc(p: ^Printer, index: int) { if case_count > brace_token.parameter_count { break; } - + } } @@ -302,6 +302,88 @@ align_switch_smt :: proc(p: ^Printer, index: int) { align_struct :: proc(p: ^Printer, index: 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; + } + + largest := 0; + colon_count := 0; + + + + for line, line_index in p.lines[brace_line+1:] { + + length := 0; + + for format_token in line.format_tokens { + + if format_token.kind == .Comment { + continue; + } + + if format_token.kind == .Colon { + colon_count += 1; + largest = max(length, largest); + break; + } + + length += len(format_token.text) + format_token.spaces_before; + } + + if colon_count > brace_token.parameter_count { + break; + } + } + + colon_count = 0; + + 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 { + continue; + } + + if format_token.kind == .Colon { + colon_count += 1; + line.format_tokens[i+1].spaces_before += (largest - length) - 1; + break; + } + + length += len(format_token.text) + format_token.spaces_before; + } + + if colon_count > brace_token.parameter_count { + break; + } + } + + } align_blocks :: proc(p: ^Printer) { @@ -314,7 +396,9 @@ align_blocks :: proc(p: ^Printer) { if .Switch_Stmt in line.types && p.config.align_switch { align_switch_smt(p, line_index); - } else if .Struct in line.types && p.config.align_structs { + } + + if .Struct in line.types && p.config.align_structs { align_struct(p, line_index); } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 6afc04a61..adbe68fb5 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -1028,6 +1028,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Struct_Type: push_generic_token(p, .Struct, 1); + hint_current_line(p, {.Struct}); + if v.is_packed { push_ident_token(p, "#packed", 1); } @@ -1058,11 +1060,10 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { set_source_position(p, v.fields.pos); visit_field_list(p, v.fields, true); push_generic_token(p, .Close_Brace, 0); - } else { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); + } else if v.fields != nil { + visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true, true); + visit_field_list(p, v.fields, true, true, true); visit_end_brace(p, v.end); } @@ -1256,7 +1257,7 @@ visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false } } -visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false) { +visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false, enforce_newline := false) { if list.list == nil { return; @@ -1264,7 +1265,9 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, for field, i in list.list { - move_line_limit(p, field.pos, 1); + if !move_line_limit(p, field.pos, 1) && enforce_newline { + newline_position(p, 1); + } if .Using in field.flags { push_generic_token(p, .Using, 0); From aded272b330cc6f751a6101bed0004845548e658 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 18:04:48 +0200 Subject: [PATCH 017/137] split stmts --- core/odin/printer/visit.odin | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index adbe68fb5..5e54368bb 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -547,29 +547,14 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener case Block_Stmt: move_line(p, v.pos); - if v.pos.line == v.end.line && len(v.stmts) > 1 && p.config.split_multiple_stmts { - - if !empty_block { - visit_begin_brace(p, v.pos, block_type, len(v.stmts)); - } - - set_source_position(p, v.pos); - - visit_block_stmts(p, v.stmts, true); - - set_source_position(p, v.end); - - if !empty_block { - visit_end_brace(p, v.end); - } - } else if v.pos.line == v.end.line { + if v.pos.line == v.end.line { if !empty_block { push_generic_token(p, .Open_Brace, 0); } set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); set_source_position(p, v.end); @@ -583,7 +568,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); set_source_position(p, v.end); @@ -1246,14 +1231,13 @@ visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { p.current_line.depth = p.depth; } -visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, newline_each := false) { +visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) { for stmt, i in stmts { + visit_stmt(p, stmt, .Generic, false, true); - if newline_each { + if split && i != len(stmts)-1 && stmt.pos.line == stmts[i+1].pos.line { newline_position(p, 1); } - - visit_stmt(p, stmt, .Generic, false, true); } } From 1de928df7844e2244774cab2b42c70bddfa8698f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 18:43:04 +0200 Subject: [PATCH 018/137] fixed bug in parser on when --- core/odin/parser/parser.odin | 2 ++ core/odin/printer/printer.odin | 19 +++++++++---------- core/odin/printer/visit.odin | 9 ++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 2b657f76f..35fd91465 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2226,6 +2226,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 { @@ -3152,6 +3153,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; } } diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 6d591a6ae..21765f6f0 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -256,12 +256,14 @@ align_switch_smt :: proc(p: ^Printer, index: int) { length += len(format_token.text) + format_token.spaces_before; } - if case_count > brace_token.parameter_count { + if case_count >= brace_token.parameter_count { break; } } + fmt.println(case_count, brace_token); + case_count = 0; for line, line_index in p.lines[brace_line+1:] { @@ -291,10 +293,10 @@ align_switch_smt :: proc(p: ^Printer, index: int) { length += len(format_token.text) + format_token.spaces_before; - if case_count > brace_token.parameter_count { - break; - } + } + if case_count >= brace_token.parameter_count { + break; } } @@ -331,8 +333,6 @@ align_struct :: proc(p: ^Printer, index: int) { largest := 0; colon_count := 0; - - for line, line_index in p.lines[brace_line+1:] { length := 0; @@ -352,7 +352,7 @@ align_struct :: proc(p: ^Printer, index: int) { length += len(format_token.text) + format_token.spaces_before; } - if colon_count > brace_token.parameter_count { + if colon_count >= brace_token.parameter_count { break; } } @@ -371,19 +371,18 @@ align_struct :: proc(p: ^Printer, index: int) { if format_token.kind == .Colon { colon_count += 1; - line.format_tokens[i+1].spaces_before += (largest - length) - 1; + line.format_tokens[i+1].spaces_before = largest - length + 1; break; } length += len(format_token.text) + format_token.spaces_before; } - if colon_count > brace_token.parameter_count { + if colon_count >= brace_token.parameter_count { break; } } - } align_blocks :: proc(p: ^Printer) { diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 5e54368bb..a00c8ec7c 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -1259,14 +1259,13 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, visit_exprs(p, field.names, true); - if len(field.names) != 0 { - push_generic_token(p, .Colon, 0); - } - if field.type != nil { + if len(field.names) != 0 { + push_generic_token(p, .Colon, 0); + } visit_expr(p, field.type); } else { - push_generic_token(p, .Colon, 0); + push_generic_token(p, .Colon, 1); push_generic_token(p, .Eq, 0); visit_expr(p, field.default_value); } From bab4e5531aa13c3e3e76f22d59ce3253456276d3 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 14 Apr 2021 21:53:11 +0200 Subject: [PATCH 019/137] remove print --- core/odin/printer/printer.odin | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 21765f6f0..b194ead26 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -103,6 +103,7 @@ default_style := Config { indent_cases = false, align_switch = true, align_structs = true, + newline_style = .LF, }; make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { @@ -262,8 +263,6 @@ align_switch_smt :: proc(p: ^Printer, index: int) { } - fmt.println(case_count, brace_token); - case_count = 0; for line, line_index in p.lines[brace_line+1:] { From a09300fb0ef3c5c1fe6fa085961b7fb9f3646379 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 15 Apr 2021 00:11:10 +0200 Subject: [PATCH 020/137] more fixes --- core/odin/printer/visit.odin | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index a00c8ec7c..f08eae7e7 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -405,11 +405,11 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { if v.name.text != "" { push_generic_token(p, v.import_tok.kind, 1); - push_generic_token(p, v.name.kind, 1); - push_ident_token(p, v.fullpath, 0); + push_generic_token(p, v.name.kind, 1, v.name.text); + push_ident_token(p, v.fullpath, 1); } else { push_generic_token(p, v.import_tok.kind, 1); - push_ident_token(p, v.fullpath, 0); + push_ident_token(p, v.fullpath, 1); } case Value_Decl: @@ -748,7 +748,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.label != nil { visit_expr(p, v.label); - push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); } push_ident_token(p, "#unroll", 0); @@ -771,7 +771,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.label != nil { visit_expr(p, v.label); - push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); } push_generic_token(p, .For, 1); @@ -932,9 +932,10 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, v.op.kind, 0); case Type_Cast: push_generic_token(p, v.tok.kind, 1); - push_generic_token(p, .Open_Paren, 1); + push_generic_token(p, .Open_Paren, 0); visit_expr(p, v.type); push_generic_token(p, .Close_Paren, 0); + merge_next_token(p); visit_expr(p, v.expr); case Basic_Directive: push_generic_token(p, v.tok.kind, 1); @@ -1080,7 +1081,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Binary_Expr: visit_binary_expr(p, v); case Implicit_Selector_Expr: - push_generic_token(p, .Period, 0); + push_generic_token(p, .Period, 1); push_ident_token(p, v.field.name, 0); case Call_Expr: visit_expr(p, v.expr); From 22daa50374478d1aa98a24ed985e9ce0c9691a27 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 15 Apr 2021 00:19:13 +0200 Subject: [PATCH 021/137] ran the odinfmt - looks good, except for multi line binary operations --- core/odin/printer/printer.odin | 638 +++++----- core/odin/printer/visit.odin | 2078 ++++++++++++++++---------------- 2 files changed, 1346 insertions(+), 1370 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index b194ead26..4d8539dfa 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,483 +8,467 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -Line_Type_Enum :: enum{Line_Comment, Value_Decl, Switch_Stmt, Struct}; +Line_Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct} Line_Type :: bit_set[Line_Type_Enum]; 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 + 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 } Format_Token :: struct { - kind: tokenizer.Token_Kind, - text: string, - spaces_before: int, - parameter_count: int, + kind: tokenizer.Token_Kind, + text: string, + 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, + 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, + 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, + 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, - indent_cases: bool, - newline_style: Newline_Style, + 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, + indent_cases: bool, + newline_style: Newline_Style, } Brace_Style :: enum { - _1TBS, - Allman, - Stroustrup, - K_And_R, + _1TBS, + Allman, + Stroustrup, + K_And_R, } Block_Type :: enum { - None, - If_Stmt, - Proc, - Generic, - Comp_Lit, - Switch_Stmt, + None, + If_Stmt, + Proc, + Generic, + Comp_Lit, + Switch_Stmt, } Alignment_Style :: enum { - Align_On_Colon_And_Equals, - Align_On_Type_And_Equals, + Align_On_Colon_And_Equals, + Align_On_Type_And_Equals, } Newline_Style :: enum { - CRLF, - LF, + CRLF, + LF, } default_style := Config { - spaces = 4, - newline_limit = 2, - convert_do = false, - semicolons = true, - tabs = false, - 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, - newline_style = .LF, + spaces = 4, + newline_limit = 2, + convert_do = false, + semicolons = true, + tabs = false, + 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, + newline_style = .CRLF, }; make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { - return { - config = config, - allocator = allocator, - debug = false, - }; + 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); - } + p.comments = file.comments; - set_line(p, 0); + 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); + } - push_generic_token(p, .Package, 0); - push_ident_token(p, file.pkg_name, 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); - } + if len(p.comments) > 0 { + infinite := p.comments[len(p.comments) - 1].end; + infinite.offset = 9999999; + push_comments(p, infinite); + } - fix_lines(p); + fix_lines(p); builder := strings.make_builder(0, mem.megabytes(5), p.allocator); last_line := 0; - newline: string; + newline: string; - if p.config.newline_style == .LF { - newline = "\n"; - } else { - newline = "\r\n"; - } + 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, ' '); - } - } + for i := 0; i < diff_line; i += 1 { + strings.write_string(&builder, newline); + } - if p.debug { - strings.write_string(&builder, fmt.tprintf("line %v: ", line_index)); - } + 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, ' '); + } + } - for format_token in line.format_tokens { + if p.debug { + strings.write_string(&builder, fmt.tprintf("line %v: ", line_index)); + } - for i := 0; i < format_token.spaces_before; i += 1 { - strings.write_byte(&builder, ' '); - } + for format_token in line.format_tokens { - strings.write_string(&builder, format_token.text); - } - - last_line = line_index; + 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; } return strings.to_string(builder); } fix_lines :: proc(p: ^Printer) { - align_var_decls(p); - align_blocks(p); - align_comments(p); //align them last since they rely on the other alignments + align_var_decls(p); + align_blocks(p); + align_comments(p); //align them last since they rely on the other alignments } align_var_decls :: proc(p: ^Printer) { - } align_switch_smt :: proc(p: ^Printer, index: int) { - switch_found := false; - brace_token: Format_Token; - brace_line: int; + switch_found := false; + brace_token: Format_Token; + brace_line: int; - found_switch_brace: for line, line_index in p.lines[index:] { + found_switch_brace: for line, line_index in p.lines[index:] { - for format_token in line.format_tokens { + 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 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; - if !switch_found { - return; - } + //find all the switch cases that are one lined + for line, line_index in p.lines[brace_line + 1:] { - largest := 0; - case_count := 0; + case_found := false; + colon_found := false; + length := 0; - //find all the switch cases that are one lined - for line, line_index in p.lines[brace_line+1:] { + for format_token in line.format_tokens { - case_found := false; - colon_found := false; - length := 0; + if format_token.kind == .Comment { + continue; + } - for format_token in line.format_tokens { + //this will only happen if the case is one lined + if case_found && colon_found { + largest = max(length, largest); + break; + } - if format_token.kind == .Comment { - continue; - } + if format_token.kind == .Case { + case_found = true; + case_count += 1; + } else if format_token.kind == .Colon { + colon_found = true; + } - //this will only happen if the case is one lined - if case_found && colon_found { - largest = max(length, largest); - break; - } + length += len(format_token.text) + format_token.spaces_before; + } - if format_token.kind == .Case { - case_found = true; - case_count += 1; - } else if format_token.kind == .Colon { - colon_found = true; - } + if case_count >= brace_token.parameter_count { + break; + } + } - length += len(format_token.text) + format_token.spaces_before; - } + case_count = 0; - if case_count >= brace_token.parameter_count { - break; - } + for line, line_index in p.lines[brace_line + 1:] { - } + case_found := false; + colon_found := false; + length := 0; - case_count = 0; + for format_token, i in line.format_tokens { - for line, line_index in p.lines[brace_line+1:] { + if format_token.kind == .Comment { + continue; + } - case_found := false; - colon_found := false; - length := 0; + //this will only happen if the case is one lined + if case_found && colon_found { + line.format_tokens[i].spaces_before += (largest - length); + break; + } - for format_token, i in line.format_tokens { + if format_token.kind == .Case { + case_found = true; + case_count += 1; + } else if format_token.kind == .Colon { + colon_found = true; + } - if format_token.kind == .Comment { - continue; - } - - //this will only happen if the case is one lined - if case_found && colon_found { - line.format_tokens[i].spaces_before += (largest - length); - 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; - } - } + length += len(format_token.text) + format_token.spaces_before; + } + if case_count >= brace_token.parameter_count { + break; + } + } } align_struct :: proc(p: ^Printer, index: int) { - struct_found := false; - brace_token: Format_Token; - brace_line: int; + struct_found := false; + brace_token: Format_Token; + brace_line: int; - found_struct_brace: for line, line_index in p.lines[index:] { + found_struct_brace: for line, line_index in p.lines[index:] { - for format_token in line.format_tokens { + 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 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; + } - } + largest := 0; + colon_count := 0; - if !struct_found { - return; - } + for line, line_index in p.lines[brace_line + 1:] { - largest := 0; - colon_count := 0; + length := 0; - for line, line_index in p.lines[brace_line+1:] { + for format_token in line.format_tokens { - length := 0; + if format_token.kind == .Comment { + continue; + } - for format_token in line.format_tokens { + if format_token.kind == .Colon { + colon_count += 1; + largest = max(length, largest); + break; + } - if format_token.kind == .Comment { - continue; - } + length += len(format_token.text) + format_token.spaces_before; + } - if format_token.kind == .Colon { - colon_count += 1; - largest = max(length, largest); - break; - } + if colon_count >= brace_token.parameter_count { + break; + } + } - length += len(format_token.text) + format_token.spaces_before; - } + colon_count = 0; - if colon_count >= brace_token.parameter_count { - break; - } - } + for line, line_index in p.lines[brace_line + 1:] { - colon_count = 0; + length := 0; - for line, line_index in p.lines[brace_line+1:] { + for format_token, i in line.format_tokens { - length := 0; + if format_token.kind == .Comment { + continue; + } - for format_token, i in line.format_tokens { + if format_token.kind == .Colon { + colon_count += 1; + line.format_tokens[i + 1].spaces_before = largest - length + 1; + break; + } - if format_token.kind == .Comment { - continue; - } - - if format_token.kind == .Colon { - colon_count += 1; - line.format_tokens[i+1].spaces_before = largest - length + 1; - break; - } - - length += len(format_token.text) + format_token.spaces_before; - } - - if colon_count >= brace_token.parameter_count { - break; - } - } + length += len(format_token.text) + format_token.spaces_before; + } + if colon_count >= brace_token.parameter_count { + break; + } + } } align_blocks :: proc(p: ^Printer) { - for line, line_index in p.lines { + for line, line_index in p.lines { - if len(line.format_tokens) <= 0 { - continue; - } + if len(line.format_tokens) <= 0 { + continue; + } - if .Switch_Stmt in line.types && p.config.align_switch { - align_switch_smt(p, line_index); - } - - if .Struct in line.types && p.config.align_structs { - align_struct(p, line_index); - } - - } + if .Switch_Stmt in line.types && p.config.align_switch { + align_switch_smt(p, line_index); + } + if .Struct in line.types && p.config.align_structs { + align_struct(p, line_index); + } + } } 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); + Comment_Align_Info :: struct { + length: int, + begin: int, + end: int, + depth: int, + }; - current_info: Comment_Align_Info; + comment_infos := make([dynamic] Comment_Align_Info, 0, context.temp_allocator); - for line, line_index in p.lines { + current_info: Comment_Align_Info; - if len(line.format_tokens) <= 0 { - continue; - } + for line, line_index in p.lines { - if .Line_Comment in line.types { + if len(line.format_tokens) <= 0 { + continue; + } - if current_info.end + 1 != line_index || current_info.depth != line.depth || - (current_info.begin == current_info.end && current_info.length == 0) { + if .Line_Comment in line.types { - if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 { - append(&comment_infos, current_info); - } + if current_info.end + 1 != line_index || current_info.depth != line.depth || + (current_info.begin == current_info.end && current_info.length == 0) { - current_info.begin = line_index; - current_info.end = line_index; - current_info.depth = line.depth; - current_info.length = 0; - } + if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 { + append(&comment_infos, current_info); + } - length := 0; + current_info.begin = line_index; + current_info.end = line_index; + current_info.depth = line.depth; + current_info.length = 0; + } - for format_token, i in line.format_tokens { + length := 0; - if format_token.kind == .Comment { - current_info.length = max(current_info.length, length); - current_info.end = line_index; - } + for format_token, i in line.format_tokens { - length += format_token.spaces_before + len(format_token.text); - } + 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); + } - if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 { - append(&comment_infos, current_info); - } + for info in comment_infos { - for info in comment_infos { + if info.begin == info.end || info.length == 0 { + continue; + } - if info.begin == info.end || info.length == 0 { - continue; - } + for i := info.begin; i <= info.end; i += 1 { - for i := info.begin; i <= info.end; i += 1 { + l := p.lines[i]; - l := p.lines[i]; + length := 0; - length := 0; + for format_token, i in l.format_tokens { - 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; - } - } - - length += format_token.spaces_before + len(format_token.text); - } - - } - - } + 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; + } + } + length += format_token.spaces_before + len(format_token.text); + } + } + } } \ No newline at end of file diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index f08eae7e7..7cc856e3b 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -11,239 +11,238 @@ import "core:sort" //right the attribute order is not linearly parsed(bug?) @(private) -sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { - return sort.Interface { - collection = rawptr(s), - len = proc(it: sort.Interface) -> int { - s := (^[dynamic]^ast.Attribute)(it.collection); - return len(s^); - }, - less = proc(it: sort.Interface, i, j: int) -> bool { - s := (^[dynamic]^ast.Attribute)(it.collection); - return s[i].pos.offset < s[j].pos.offset; - }, - swap = proc(it: sort.Interface, i, j: int) { - s := (^[dynamic]^ast.Attribute)(it.collection); - s[i], s[j] = s[j], s[i]; - }, - }; +sort_attribute :: proc(s: ^[dynamic] ^ast.Attribute) -> sort.Interface { + return sort.Interface { + collection = rawptr(s), + len = proc(it: sort.Interface) -> int { + s := (^[dynamic] ^ast.Attribute)(it.collection); + return len(s^); + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + s := (^[dynamic] ^ast.Attribute)(it.collection); + return s[i].pos.offset < s[j].pos.offset; + }, + swap = proc(it: sort.Interface, i, j: int) { + s := (^[dynamic] ^ast.Attribute)(it.collection); + s[i], s[j] = s[j], s[i]; + }, + }; } @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { - if len(p.comments) <= p.latest_comment_index { - return false; - } + if len(p.comments) <= p.latest_comment_index { + return false; + } - comment := p.comments[p.latest_comment_index]; + comment := p.comments[p.latest_comment_index]; - return comment.pos.offset < pos.offset; + return comment.pos.offset < pos.offset; } @(private) next_comment_group :: proc(p: ^Printer) { - p.latest_comment_index += 1; + p.latest_comment_index += 1; } - -@(private) + +@(private) push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { - if len(comment.text) == 0 { - return 0; - } + if len(comment.text) == 0 { + return 0; + } - if comment.text[0] == '/' && comment.text[1] == '/' { - format_token := Format_Token { - spaces_before = 1, - kind = .Comment, - text = comment.text, - }; + if comment.text[0] == '/' && comment.text[1] == '/' { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = comment.text, + }; - if len(p.current_line.format_tokens) == 0 { - format_token.spaces_before = 0; - } + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } - if !p.current_line.used { - p.current_line.used = true; - p.current_line.depth = p.depth; - } + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } - append(&p.current_line.format_tokens, format_token); - p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; - hint_current_line(p, {.Line_Comment}); + hint_current_line(p,{.Line_Comment}); - return 0; - } else { + return 0; + } else { - builder := strings.make_builder(context.temp_allocator); + builder := strings.make_builder(context.temp_allocator); - c_len := len(comment.text); - trim_space := true; + c_len := len(comment.text); + trim_space := true; - multilines: [dynamic] string; + multilines: [dynamic] string; - for i := 0; i < len(comment.text); i += 1 { + for i := 0; i < len(comment.text); i += 1 { - c := comment.text[i]; + c := comment.text[i]; - if c != ' ' && c != '\t' { - trim_space = false; - } + if c != ' ' && c != '\t' { + trim_space = false; + } - if (c == ' ' || c == '\t' || c == '\n') && trim_space { - continue; - } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { - append(&multilines, strings.to_string(builder)); - builder = strings.make_builder(context.temp_allocator); - trim_space = true; - i += 1; - } else if c == 10 { - append(&multilines, strings.to_string(builder)); - builder = strings.make_builder(context.temp_allocator); - trim_space = true; - } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { - strings.write_string(&builder, "/*"); - trim_space = true; - p.depth += 1; - i += 1; - } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { - p.depth -= 1; - trim_space = true; - strings.write_string(&builder, "*/"); - i += 1; - } else { - strings.write_byte(&builder, c); - } + if (c == ' ' || c == '\t' || c == '\n') && trim_space { + continue; + } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + i += 1; + } else if c == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { + strings.write_string(&builder, "/*"); + trim_space = true; + p.depth += 1; + i += 1; + } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { + p.depth -= 1; + trim_space = true; + strings.write_string(&builder, "*/"); + i += 1; + } else { + strings.write_byte(&builder, c); + } + } - } + if strings.builder_len(builder) > 0 { + append(&multilines, strings.to_string(builder)); + } - if strings.builder_len(builder) > 0 { - append(&multilines, strings.to_string(builder)); - } + for line in multilines { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = line, + }; - for line in multilines { - format_token := Format_Token { - spaces_before = 1, - kind = .Comment, - text = line, - }; + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } - if len(p.current_line.format_tokens) == 0 { - format_token.spaces_before = 0; - } + if strings.contains(line, "*/") { + unindent(p); + } - if strings.contains(line, "*/") { - unindent(p); - } + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } - if !p.current_line.used { - p.current_line.used = true; - p.current_line.depth = p.depth; - } + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; - append(&p.current_line.format_tokens, format_token); - p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens)-1]; + if strings.contains(line, "/*") { + indent(p); + } - if strings.contains(line, "/*") { - indent(p); - } + newline_position(p, 1); + } - newline_position(p, 1); - } - - return len(multilines); - } + return len(multilines); + } } @(private) push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { - prev_comment: ^tokenizer.Token; - prev_comment_lines: int; + prev_comment: ^tokenizer.Token; + prev_comment_lines: int; - for comment_before_position(p, pos) { + for comment_before_position(p, pos) { - comment_group := p.comments[p.latest_comment_index]; + comment_group := p.comments[p.latest_comment_index]; - if prev_comment == nil { - lines := comment_group.pos.line - p.last_source_position.line; - set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); - } + if prev_comment == nil { + lines := comment_group.pos.line - p.last_source_position.line; + set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + } - for comment, i in comment_group.list { + for comment, i in comment_group.list { - if prev_comment != nil && p.last_source_position.line != comment.pos.line { - newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); - } + if prev_comment != nil && p.last_source_position.line != comment.pos.line { + newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); + } - prev_comment_lines = push_comment(p, comment); - prev_comment = &comment_group.list[i]; - } + prev_comment_lines = push_comment(p, comment); + prev_comment = &comment_group.list[i]; + } - next_comment_group(p); - } - - if prev_comment != nil { - newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); - } + next_comment_group(p); + } + + if prev_comment != nil { + newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); + } } @(private) append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_Token { - format_token := format_token; + format_token := format_token; - if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || - p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || - p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { - format_token.spaces_before = 0; - } else if p.merge_next_token { - format_token.spaces_before = 0; - p.merge_next_token = false; - } else if p.space_next_token { - format_token.spaces_before = 1; - p.space_next_token = false; - } + if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || + p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || + p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { + format_token.spaces_before = 0; + } else if p.merge_next_token { + format_token.spaces_before = 0; + p.merge_next_token = false; + } else if p.space_next_token { + format_token.spaces_before = 1; + p.space_next_token = false; + } - push_comments(p, p.source_position); + push_comments(p, p.source_position); - unwrapped_line := p.current_line; + unwrapped_line := p.current_line; - if !unwrapped_line.used { - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; - } + if !unwrapped_line.used { + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + } - if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { - format_token.spaces_before = 0; - } - - p.last_source_position = p.source_position; - p.last_line_index = p.current_line_index; + if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { + format_token.spaces_before = 0; + } - append(&unwrapped_line.format_tokens, format_token); - return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens)-1]; + p.last_source_position = p.source_position; + p.last_line_index = p.current_line_index; + + append(&unwrapped_line.format_tokens, format_token); + return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens) - 1]; } @(private) push_format_token :: proc(p: ^Printer, format_token: Format_Token) { - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { - + format_token := Format_Token { spaces_before = spaces_before, kind = kind, text = tokenizer.tokens[kind], }; - if value != "" { - format_token.text = value; - } + if value != "" { + format_token.text = value; + } p.last_token = append_format_token(p, format_token); } @@ -269,1204 +268,1197 @@ push_ident_token :: proc(p: ^Printer, text: string, spaces_before: int) { text = text, }; - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { - p.source_position = pos; + p.source_position = pos; } @(private) move_line :: proc(p: ^Printer, pos: tokenizer.Pos) { - move_line_limit(p, pos, p.config.newline_limit); + move_line_limit(p, pos, p.config.newline_limit); } @(private) move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> bool { - lines := min(pos.line - p.source_position.line, limit); + lines := min(pos.line - p.source_position.line, limit); - if lines < 0 { - return false; - } + if lines < 0 { + return false; + } - p.source_position = pos; - p.current_line_index += lines; - set_line(p, p.current_line_index); - return lines > 0; + p.source_position = pos; + p.current_line_index += lines; + set_line(p, p.current_line_index); + return lines > 0; } @(private) set_line :: proc(p: ^Printer, line: int) -> ^Line { - unwrapped_line: ^Line; + unwrapped_line: ^Line; - if line >= len(p.lines) { - for i := len(p.lines); i <= line; i += 1 { - new_line: Line; - new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); - append(&p.lines, new_line); - } - unwrapped_line = &p.lines[line]; + if line >= len(p.lines) { + for i := len(p.lines); i <= line; i += 1 { + new_line: Line; + new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); + append(&p.lines, new_line); + } + unwrapped_line = &p.lines[line]; } else { unwrapped_line = &p.lines[line]; } - p.current_line = unwrapped_line; - p.current_line_index = line; + p.current_line = unwrapped_line; + p.current_line_index = line; - return unwrapped_line; + return unwrapped_line; } @(private) newline_position :: proc(p: ^Printer, count: int) { - p.current_line_index += count; - set_line(p, p.current_line_index); + p.current_line_index += count; + set_line(p, p.current_line_index); } @(private) indent :: proc(p: ^Printer) { - p.depth += 1; + p.depth += 1; } @(private) unindent :: proc(p: ^Printer) { - p.depth -= 1; + p.depth -= 1; } @(private) merge_next_token :: proc(p: ^Printer) { - p.merge_next_token = true; + p.merge_next_token = true; } @(private) space_next_token :: proc(p: ^Printer) { - p.space_next_token = true; + p.space_next_token = true; } @(private) hint_current_line :: proc(p: ^Printer, hint: Line_Type) { - p.current_line.types |= hint; + p.current_line.types |= hint; } @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { - using ast; + using ast; - if decl == nil { - return; - } + if decl == nil { + return; + } - switch v in &decl.derived { - case Expr_Stmt: - move_line(p, decl.pos); - visit_expr(p, v.expr); - if p.config.semicolons { + switch v in &decl.derived { + case Expr_Stmt: + move_line(p, decl.pos); + visit_expr(p, v.expr); + if p.config.semicolons { push_generic_token(p, .Semicolon, 0); - } - case When_Stmt: - visit_stmt(p, cast(^Stmt)decl); - case Foreign_Import_Decl: - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } + } + case When_Stmt: + visit_stmt(p, cast(^Stmt)decl); + case Foreign_Import_Decl: + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } + + move_line(p, decl.pos); - move_line(p, decl.pos); - push_generic_token(p, v.foreign_tok.kind, 0); push_generic_token(p, v.import_tok.kind, 1); - if v.name != nil { + if v.name != nil { push_ident_token(p, v.name.name, 1); - } + } - for path in v.fullpaths { + for path in v.fullpaths { push_ident_token(p, path, 0); - } - case Foreign_Block_Decl: + } + case Foreign_Block_Decl: + + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } + + move_line(p, decl.pos); - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } - - move_line(p, decl.pos); - push_generic_token(p, .Foreign, 0); - visit_expr(p, v.foreign_library); - visit_stmt(p, v.body); - case Import_Decl: - move_line(p, decl.pos); + visit_expr(p, v.foreign_library); + visit_stmt(p, v.body); + case Import_Decl: + move_line(p, decl.pos); - if v.name.text != "" { + if v.name.text != "" { push_generic_token(p, v.import_tok.kind, 1); push_generic_token(p, v.name.kind, 1, v.name.text); push_ident_token(p, v.fullpath, 1); - } else { + } else { push_generic_token(p, v.import_tok.kind, 1); push_ident_token(p, v.fullpath, 1); - } + } - case Value_Decl: - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } + case Value_Decl: + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } - move_line(p, decl.pos); + move_line(p, decl.pos); - if v.is_using { + if v.is_using { push_generic_token(p, .Using, 0); - } + } - visit_exprs(p, v.names, true); + visit_exprs(p, v.names, true); - if v.type != nil { + if v.type != nil { if !v.is_mutable && v.type != nil { push_generic_token(p, .Colon, 0); - } else { + } else { push_generic_token(p, .Colon, 0); } - visit_expr(p, v.type); - } else { + visit_expr(p, v.type); + } else { if !v.is_mutable && v.type == nil { push_generic_token(p, .Colon, 1); - push_generic_token(p, .Colon, 0); + push_generic_token(p, .Colon, 0); } else { push_generic_token(p, .Colon, 1); } - } + } - if v.is_mutable && v.type != nil && len(v.values) != 0 { + if v.is_mutable && v.type != nil && len(v.values) != 0 { push_generic_token(p, .Eq, 1); - } else if v.is_mutable && v.type == nil && len(v.values) != 0 { - push_generic_token(p, .Eq, 0); - } else if !v.is_mutable && v.type != nil { + } else if v.is_mutable && v.type == nil && len(v.values) != 0 { + push_generic_token(p, .Eq, 0); + } else if !v.is_mutable && v.type != nil { push_generic_token(p, .Colon, 0); - } + } - visit_exprs(p, v.values, true); + visit_exprs(p, v.values, true); - add_semicolon := true; + add_semicolon := true; - for value in v.values { - switch a in value.derived { - case Proc_Lit, Union_Type, Enum_Type, Struct_Type: - add_semicolon = false || called_in_stmt; - } - } + for value in v.values { + switch a in value.derived { + case Proc_Lit, Union_Type, Enum_Type, Struct_Type: + add_semicolon = false || called_in_stmt; + } + } - if add_semicolon && p.config.semicolons && !p.skip_semicolon { + if add_semicolon && p.config.semicolons && !p.skip_semicolon { push_generic_token(p, .Semicolon, 0); - } + } - case: - panic(fmt.aprint(decl.derived)); - } + case: + panic(fmt.aprint(decl.derived)); + } } @(private) -visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false) { +visit_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, add_comma := false, trailing := false) { - if len(list) == 0 { - return; - } + if len(list) == 0 { + return; + } - //we have to newline the expressions to respect the source - for expr, i in list { + //we have to newline the expressions to respect the source + for expr, i in list { - move_line_limit(p, expr.pos, 1); + move_line_limit(p, expr.pos, 1); - visit_expr(p, expr); + visit_expr(p, expr); - if (i != len(list) - 1 || trailing) && add_comma { - push_generic_token(p, .Comma, 0); - } - } + if (i != len(list) - 1 || trailing) && add_comma { + push_generic_token(p, .Comma, 0); + } + } } @(private) -visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { +visit_attributes :: proc(p: ^Printer, attributes: [dynamic] ^ast.Attribute) { - if len(attributes) == 0 { - return; - } + if len(attributes) == 0 { + return; + } - for attribute, i in attributes { + for attribute, i in attributes { - move_line_limit(p, attribute.pos, 1); + move_line_limit(p, attribute.pos, 1); - push_generic_token(p, .At, 0); - push_generic_token(p, .Open_Paren, 0); + push_generic_token(p, .At, 0); + push_generic_token(p, .Open_Paren, 0); - visit_exprs(p, attribute.elems, true); + visit_exprs(p, attribute.elems, true); - push_generic_token(p, .Close_Paren, 0); - } + push_generic_token(p, .Close_Paren, 0); + } } @(private) visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { - using ast; + using ast; - if stmt == nil { - return; - } + if stmt == nil { + return; + } - switch v in stmt.derived { - case Value_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Import_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Block_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - } + switch v in stmt.derived { + case Value_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Import_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Block_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + } - switch v in stmt.derived { - case Using_Stmt: - move_line(p, v.pos); + switch v in stmt.derived { + case Using_Stmt: + move_line(p, v.pos); - push_generic_token(p, .Using, 1); + push_generic_token(p, .Using, 1); - visit_exprs(p, v.list, true); + visit_exprs(p, v.list, true); - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Block_Stmt: - move_line(p, v.pos); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Block_Stmt: + move_line(p, v.pos); - if v.pos.line == v.end.line { - if !empty_block { - push_generic_token(p, .Open_Brace, 0); - } + if v.pos.line == v.end.line { + if !empty_block { + push_generic_token(p, .Open_Brace, 0); + } - set_source_position(p, v.pos); + set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); - set_source_position(p, v.end); + set_source_position(p, v.end); - if !empty_block { - push_generic_token(p, .Close_Brace, 0); - } - } else { - if !empty_block { - visit_begin_brace(p, v.pos, block_type, len(v.stmts)); - } + if !empty_block { + push_generic_token(p, .Close_Brace, 0); + } + } else { + if !empty_block { + visit_begin_brace(p, v.pos, block_type, len(v.stmts)); + } - set_source_position(p, v.pos); + set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); - set_source_position(p, v.end); + set_source_position(p, v.end); - if !empty_block { - visit_end_brace(p, v.end); - } - } - case If_Stmt: - move_line(p, v.pos); + if !empty_block { + visit_end_brace(p, v.end); + } + } + case If_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .If, 1); + push_generic_token(p, .If, 1); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } - visit_expr(p, v.cond); + visit_expr(p, v.cond); - uses_do := false; + uses_do := false; - if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { - uses_do = true; - } + if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + uses_do = true; + } - if uses_do && !p.config.convert_do { - push_generic_token(p, .Do, 1); - visit_stmt(p, v.body, .If_Stmt, true); - } else { - if uses_do { - newline_position(p, 1); - } + if uses_do && !p.config.convert_do { + push_generic_token(p, .Do, 1); + visit_stmt(p, v.body, .If_Stmt, true); + } else { + if uses_do { + newline_position(p, 1); + } - visit_stmt(p, v.body, .If_Stmt); - } + visit_stmt(p, v.body, .If_Stmt); + } - if v.else_stmt != nil { + if v.else_stmt != nil { - if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { - newline_position(p, 1); - } + if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { + newline_position(p, 1); + } - push_generic_token(p, .Else, 1); + push_generic_token(p, .Else, 1); - set_source_position(p, v.else_stmt.pos); + set_source_position(p, v.else_stmt.pos); - visit_stmt(p, v.else_stmt); - } - case Switch_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.else_stmt); + } + case Switch_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - if v.partial { - push_ident_token(p, "#partial", 1); - } + if v.partial { + push_ident_token(p, "#partial", 1); + } - push_generic_token(p, .Switch, 1); + push_generic_token(p, .Switch, 1); - hint_current_line(p, {.Switch_Stmt}); + hint_current_line(p,{.Switch_Stmt}); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + } - if v.init != nil && v.cond != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil && v.cond != nil { + push_generic_token(p, .Semicolon, 0); + } - visit_expr(p, v.cond); - visit_stmt(p, v.body); - case Case_Clause: - move_line(p, v.pos); + visit_expr(p, v.cond); + visit_stmt(p, v.body); + case Case_Clause: + move_line(p, v.pos); - if !p.config.indent_cases { - unindent(p); - } + if !p.config.indent_cases { + unindent(p); + } - push_generic_token(p, .Case, 0); + push_generic_token(p, .Case, 0); - if v.list != nil { - visit_exprs(p, v.list, true); - } + if v.list != nil { + visit_exprs(p, v.list, true); + } - push_generic_token(p, v.terminator.kind, 0); + push_generic_token(p, v.terminator.kind, 0); - indent(p); + indent(p); - visit_block_stmts(p, v.body); + visit_block_stmts(p, v.body); - unindent(p); + unindent(p); - if !p.config.indent_cases { - indent(p); - } - case Type_Switch_Stmt: - move_line(p, v.pos); + if !p.config.indent_cases { + indent(p); + } + case Type_Switch_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - if v.partial { - push_ident_token(p, "#partial", 1); - } + if v.partial { + push_ident_token(p, "#partial", 1); + } - push_generic_token(p, .Switch, 1); + push_generic_token(p, .Switch, 1); - visit_stmt(p, v.tag); - visit_stmt(p, v.body); - case Assign_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.tag); + visit_stmt(p, v.body); + case Assign_Stmt: + move_line(p, v.pos); - visit_exprs(p, v.lhs, true); + visit_exprs(p, v.lhs, true); - push_generic_token(p, v.op.kind, 1); + push_generic_token(p, v.op.kind, 1); - visit_exprs(p, v.rhs, true); + visit_exprs(p, v.rhs, true); - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Expr_Stmt: - move_line(p, v.pos); - visit_expr(p, v.expr); - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case For_Stmt: - //this should be simplified - move_line(p, v.pos); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Expr_Stmt: + move_line(p, v.pos); + visit_expr(p, v.expr); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case For_Stmt: + //this should be simplified + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .For, 1); + push_generic_token(p, .For, 1); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - push_generic_token(p, .Semicolon, 0); - } else if v.post != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } else if v.post != nil { + push_generic_token(p, .Semicolon, 0); + } - if v.cond != nil { - visit_expr(p, v.cond); - } + if v.cond != nil { + visit_expr(p, v.cond); + } - if v.post != nil { - push_generic_token(p, .Semicolon, 0); - visit_stmt(p, v.post); - } else if v.post == nil && v.cond != nil && v.init != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.post != nil { + push_generic_token(p, .Semicolon, 0); + visit_stmt(p, v.post); + } else if v.post == nil && v.cond != nil && v.init != nil { + push_generic_token(p, .Semicolon, 0); + } - visit_stmt(p, v.body); - case Inline_Range_Stmt: + visit_stmt(p, v.body); + case Inline_Range_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_ident_token(p, "#unroll", 0); + push_ident_token(p, "#unroll", 0); - push_generic_token(p, .For, 1); - visit_expr(p, v.val0); + push_generic_token(p, .For, 1); + visit_expr(p, v.val0); - if v.val1 != nil { - push_generic_token(p, .Comma, 0); - visit_expr(p, v.val1); - } + if v.val1 != nil { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.val1); + } - push_generic_token(p, .In, 1); + push_generic_token(p, .In, 1); - visit_expr(p, v.expr); - visit_stmt(p, v.body); - case Range_Stmt: + visit_expr(p, v.expr); + visit_stmt(p, v.body); + case Range_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .For, 1); + push_generic_token(p, .For, 1); - if len(v.vals) >= 1 { - visit_expr(p, v.vals[0]); - } + if len(v.vals) >= 1 { + visit_expr(p, v.vals[0]); + } - if len(v.vals) >= 2 { - push_generic_token(p, .Comma, 0); - visit_expr(p, v.vals[1]); - } + if len(v.vals) >= 2 { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.vals[1]); + } - push_generic_token(p, .In, 1); + push_generic_token(p, .In, 1); - visit_expr(p, v.expr); + visit_expr(p, v.expr); - visit_stmt(p, v.body); - case Return_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.body); + case Return_Stmt: + move_line(p, v.pos); - push_generic_token(p, .Return, 1); + push_generic_token(p, .Return, 1); - if v.results != nil { - visit_exprs(p, v.results, true); - } + if v.results != nil { + visit_exprs(p, v.results, true); + } - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Defer_Stmt: - move_line(p, v.pos); - push_generic_token(p, .Defer, 0); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Defer_Stmt: + move_line(p, v.pos); + push_generic_token(p, .Defer, 0); - visit_stmt(p, v.stmt); + visit_stmt(p, v.stmt); - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case When_Stmt: - move_line(p, v.pos); - push_generic_token(p, .When, 1); - visit_expr(p, v.cond); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case When_Stmt: + move_line(p, v.pos); + push_generic_token(p, .When, 1); + visit_expr(p, v.cond); - visit_stmt(p, v.body); + visit_stmt(p, v.body); - if v.else_stmt != nil { + if v.else_stmt != nil { - if p.config.brace_style == .Allman { - newline_position(p, 1); - } + if p.config.brace_style == .Allman { + newline_position(p, 1); + } - push_generic_token(p, .Else, 1); + push_generic_token(p, .Else, 1); - set_source_position(p, v.else_stmt.pos); + set_source_position(p, v.else_stmt.pos); - visit_stmt(p, v.else_stmt); - } + visit_stmt(p, v.else_stmt); + } - case Branch_Stmt: + case Branch_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 0); - if v.label != nil { - visit_expr(p, v.label); - } + if v.label != nil { + visit_expr(p, v.label); + } - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case: - panic(fmt.aprint(stmt.derived)); - } + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case: + panic(fmt.aprint(stmt.derived)); + } - set_source_position(p, stmt.end); + set_source_position(p, stmt.end); } - @(private) visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { - using ast; + using ast; - if expr == nil { - return; - } + if expr == nil { + return; + } - set_source_position(p, expr.pos); + set_source_position(p, expr.pos); - switch v in expr.derived { - case Inline_Asm_Expr: - push_generic_token(p, v.tok.kind, 1, v.tok.text); + switch v in expr.derived { + case Inline_Asm_Expr: + push_generic_token(p, v.tok.kind, 1, v.tok.text); - push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.param_types, true, false); - push_generic_token(p, .Close_Paren, 0); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.param_types, true, false); + push_generic_token(p, .Close_Paren, 0); - push_generic_token(p, .Sub, 1); - push_generic_token(p, .Gt, 0); + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); - visit_expr(p, v.return_type); + visit_expr(p, v.return_type); - push_generic_token(p, .Open_Brace, 1); - visit_expr(p, v.asm_string); - push_generic_token(p, .Comma, 0); - visit_expr(p, v.constraints_string); - push_generic_token(p, .Close_Brace, 0); - case Undef: - push_generic_token(p, .Undef, 1); - case Auto_Cast: - push_generic_token(p, v.op.kind, 1); - visit_expr(p, v.expr); - case Ternary_Expr: - visit_expr(p, v.cond); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.x); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Ternary_If_Expr: - visit_expr(p, v.x); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.cond); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Ternary_When_Expr: - visit_expr(p, v.x); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.cond); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Selector_Call_Expr: - visit_expr(p, v.call.expr); - push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.call.args, true); - push_generic_token(p, .Close_Paren, 0); - case Ellipsis: - push_generic_token(p, .Ellipsis, 1); - visit_expr(p, v.expr); - case Relative_Type: - visit_expr(p, v.tag); - visit_expr(p, v.type); - case Slice_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.low); - push_generic_token(p, v.interval.kind, 0); - if v.high != nil { - merge_next_token(p); - visit_expr(p, v.high); - } - push_generic_token(p, .Close_Bracket, 0); - case Ident: - push_ident_token(p, v.name, 1); - case Deref_Expr: - visit_expr(p, v.expr); - push_generic_token(p, v.op.kind, 0); - case Type_Cast: - push_generic_token(p, v.tok.kind, 1); - push_generic_token(p, .Open_Paren, 0); - visit_expr(p, v.type); - push_generic_token(p, .Close_Paren, 0); - merge_next_token(p); - visit_expr(p, v.expr); - case Basic_Directive: - push_generic_token(p, v.tok.kind, 1); - push_ident_token(p, v.name, 0); - case Distinct_Type: - push_generic_token(p, .Distinct, 1); - visit_expr(p, v.type); - case Dynamic_Array_Type: - visit_expr(p, v.tag); - push_generic_token(p, .Open_Bracket, 1); - push_generic_token(p, .Dynamic, 0); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.elem); - case Bit_Set_Type: - push_generic_token(p, .Bit_Set, 1); - push_generic_token(p, .Open_Bracket, 0); + push_generic_token(p, .Open_Brace, 1); + visit_expr(p, v.asm_string); + push_generic_token(p, .Comma, 0); + visit_expr(p, v.constraints_string); + push_generic_token(p, .Close_Brace, 0); + case Undef: + push_generic_token(p, .Undef, 1); + case Auto_Cast: + push_generic_token(p, v.op.kind, 1); + visit_expr(p, v.expr); + case Ternary_Expr: + visit_expr(p, v.cond); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.x); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Ternary_If_Expr: + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Ternary_When_Expr: + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Selector_Call_Expr: + visit_expr(p, v.call.expr); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.call.args, true); + push_generic_token(p, .Close_Paren, 0); + case Ellipsis: + push_generic_token(p, .Ellipsis, 1); + visit_expr(p, v.expr); + case Relative_Type: + visit_expr(p, v.tag); + visit_expr(p, v.type); + case Slice_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.low); + push_generic_token(p, v.interval.kind, 0); + if v.high != nil { + merge_next_token(p); + visit_expr(p, v.high); + } + push_generic_token(p, .Close_Bracket, 0); + case Ident: + push_ident_token(p, v.name, 1); + case Deref_Expr: + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + case Type_Cast: + push_generic_token(p, v.tok.kind, 1); + push_generic_token(p, .Open_Paren, 0); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); + merge_next_token(p); + visit_expr(p, v.expr); + case Basic_Directive: + push_generic_token(p, v.tok.kind, 1); + push_ident_token(p, v.name, 0); + case Distinct_Type: + push_generic_token(p, .Distinct, 1); + visit_expr(p, v.type); + case Dynamic_Array_Type: + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + push_generic_token(p, .Dynamic, 0); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); + case Bit_Set_Type: + push_generic_token(p, .Bit_Set, 1); + push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.elem); + visit_expr(p, v.elem); - if v.underlying != nil { - push_generic_token(p, .Semicolon, 0); - visit_expr(p, v.underlying); - } + if v.underlying != nil { + push_generic_token(p, .Semicolon, 0); + visit_expr(p, v.underlying); + } - push_generic_token(p, .Close_Bracket, 0); - case Union_Type: - push_generic_token(p, .Union, 1); + push_generic_token(p, .Close_Bracket, 0); + case Union_Type: + push_generic_token(p, .Union, 1); - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } - - if v.is_maybe { - push_ident_token(p, "#maybe", 1); - } + if v.poly_params != nil { + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, true, false); + push_generic_token(p, .Close_Paren, 0); + } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + if v.is_maybe { + push_ident_token(p, "#maybe", 1); + } - if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.variants, true); - push_generic_token(p, .Close_Brace, 0); - } else { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.variants[0].pos); - visit_exprs(p, v.variants, true, true); - visit_end_brace(p, v.end); - } - case Enum_Type: - push_generic_token(p, .Enum, 1); + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - if v.base_type != nil { - visit_expr(p, v.base_type); - } + if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + visit_exprs(p, v.variants, true); + push_generic_token(p, .Close_Brace, 0); + } else { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.variants[0].pos); + visit_exprs(p, v.variants, true, true); + visit_end_brace(p, v.end); + } + case Enum_Type: + push_generic_token(p, .Enum, 1); - if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.fields, true); - push_generic_token(p, .Close_Brace, 0); - } else { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.fields[0].pos); - visit_exprs(p, v.fields, true, true); - visit_end_brace(p, v.end); - } + if v.base_type != nil { + visit_expr(p, v.base_type); + } - set_source_position(p, v.end); - case Struct_Type: - push_generic_token(p, .Struct, 1); + if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + visit_exprs(p, v.fields, true); + push_generic_token(p, .Close_Brace, 0); + } else { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.fields[0].pos); + visit_exprs(p, v.fields, true, true); + visit_end_brace(p, v.end); + } - hint_current_line(p, {.Struct}); + set_source_position(p, v.end); + case Struct_Type: + push_generic_token(p, .Struct, 1); - if v.is_packed { - push_ident_token(p, "#packed", 1); - } + hint_current_line(p,{.Struct}); - if v.is_raw_union { - push_ident_token(p, "#raw_union", 1); - } + if v.is_packed { + push_ident_token(p, "#packed", 1); + } - if v.align != nil { - push_ident_token(p, "#align", 1); - visit_expr(p, v.align); - } + if v.is_raw_union { + push_ident_token(p, "#raw_union", 1); + } - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } + if v.align != nil { + push_ident_token(p, "#align", 1); + visit_expr(p, v.align); + } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + if v.poly_params != nil { + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, true, false); + push_generic_token(p, .Close_Paren, 0); + } - if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true); - push_generic_token(p, .Close_Brace, 0); - } else if v.fields != nil { - visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); - set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true, true, true); - visit_end_brace(p, v.end); - } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - set_source_position(p, v.end); - case Proc_Lit: + if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + set_source_position(p, v.fields.pos); + visit_field_list(p, v.fields, true); + push_generic_token(p, .Close_Brace, 0); + } else if v.fields != nil { + visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); + set_source_position(p, v.fields.pos); + visit_field_list(p, v.fields, true, true, true); + visit_end_brace(p, v.end); + } - if v.inlining == .Inline { - push_ident_token(p, "#force_inline", 0); - } + set_source_position(p, v.end); + case Proc_Lit: - visit_proc_type(p, v.type^); + if v.inlining == .Inline { + push_ident_token(p, "#force_inline", 0); + } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + visit_proc_type(p, v.type^); - if v.body != nil { - set_source_position(p, v.body.pos); - visit_stmt(p, v.body, .Proc); - } else { - push_generic_token(p, .Undef, 1); - } - case Proc_Type: - visit_proc_type(p, v); - case Basic_Lit: - push_generic_token(p, v.tok.kind, 1, v.tok.text); - case Binary_Expr: - visit_binary_expr(p, v); - case Implicit_Selector_Expr: - push_generic_token(p, .Period, 1); - push_ident_token(p, v.field.name, 0); - case Call_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Paren, 0); - visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); - push_generic_token(p, .Close_Paren, 0); - case Typeid_Type: - push_generic_token(p, .Typeid, 1); + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - if v.specialization != nil { - push_generic_token(p, .Quo, 0); - visit_expr(p, v.specialization); - } - case Selector_Expr: - visit_expr(p, v.expr); - push_generic_token(p, v.op.kind, 0); - visit_expr(p, v.field); - case Paren_Expr: - push_generic_token(p, .Open_Paren, 1); - visit_expr(p, v.expr); - push_generic_token(p, .Close_Paren, 0); - case Index_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.index); - push_generic_token(p, .Close_Bracket, 0); - case Proc_Group: - - push_generic_token(p, v.tok.kind, 0); + if v.body != nil { + set_source_position(p, v.body.pos); + visit_stmt(p, v.body, .Proc); + } else { + push_generic_token(p, .Undef, 1); + } + case Proc_Type: + visit_proc_type(p, v); + case Basic_Lit: + push_generic_token(p, v.tok.kind, 1, v.tok.text); + case Binary_Expr: + visit_binary_expr(p, v); + case Implicit_Selector_Expr: + push_generic_token(p, .Period, 1); + push_ident_token(p, v.field.name, 0); + case Call_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Paren, 0); + visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); + push_generic_token(p, .Close_Paren, 0); + case Typeid_Type: + push_generic_token(p, .Typeid, 1); - if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.args[0].pos); - visit_exprs(p, v.args, true, true); - visit_end_brace(p, v.end); - } else { - push_generic_token(p, .Open_Brace, 0); - visit_exprs(p, v.args, true); - push_generic_token(p, .Close_Brace, 0); - } - - case Comp_Lit: - - if v.type != nil { - visit_expr(p, v.type); - } + if v.specialization != nil { + push_generic_token(p, .Quo, 0); + visit_expr(p, v.specialization); + } + case Selector_Expr: + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + visit_expr(p, v.field); + case Paren_Expr: + push_generic_token(p, .Open_Paren, 1); + visit_expr(p, v.expr); + push_generic_token(p, .Close_Paren, 0); + case Index_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.index); + push_generic_token(p, .Close_Bracket, 0); + case Proc_Group: - if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { - visit_begin_brace(p, v.pos, .Comp_Lit); - newline_position(p, 1); - set_source_position(p, v.elems[0].pos); - visit_exprs(p, v.elems, true, true); - visit_end_brace(p, v.end); - } else { - push_generic_token(p, .Open_Brace, 0); - visit_exprs(p, v.elems, true); - push_generic_token(p, .Close_Brace, 0); - } - - case Unary_Expr: - push_generic_token(p, v.op.kind, 1); - merge_next_token(p); - visit_expr(p, v.expr); - case Field_Value: - visit_expr(p, v.field); - push_generic_token(p, .Eq, 1); - visit_expr(p, v.value); - case Type_Assertion: - visit_expr(p, v.expr); + push_generic_token(p, v.tok.kind, 0); - if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { - push_generic_token(p, .Period, 0); - visit_expr(p, v.type); - } else { - push_generic_token(p, .Period, 0); - push_generic_token(p, .Open_Paren, 0); - visit_expr(p, v.type); - push_generic_token(p, .Close_Paren, 0); - } + if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.args[0].pos); + visit_exprs(p, v.args, true, true); + visit_end_brace(p, v.end); + } else { + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.args, true); + push_generic_token(p, .Close_Brace, 0); + } - case Pointer_Type: - push_generic_token(p, .Pointer, 1); - merge_next_token(p); - visit_expr(p, v.elem); - case Implicit: - push_generic_token(p, v.tok.kind, 1); - case Poly_Type: - push_generic_token(p, .Dollar, 1); - merge_next_token(p); - visit_expr(p, v.type); + case Comp_Lit: - if v.specialization != nil { - push_generic_token(p, .Quo, 0); - merge_next_token(p); - visit_expr(p, v.specialization); - } - case Array_Type: - visit_expr(p, v.tag); - push_generic_token(p, .Open_Bracket, 1); - visit_expr(p, v.len); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.elem); - case Map_Type: - push_generic_token(p, .Map, 1); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.key); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.value); - case Helper_Type: - visit_expr(p, v.type); - case: - panic(fmt.aprint(expr.derived)); - } + if v.type != nil { + visit_expr(p, v.type); + } + + if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { + visit_begin_brace(p, v.pos, .Comp_Lit); + newline_position(p, 1); + set_source_position(p, v.elems[0].pos); + visit_exprs(p, v.elems, true, true); + visit_end_brace(p, v.end); + } else { + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.elems, true); + push_generic_token(p, .Close_Brace, 0); + } + + case Unary_Expr: + push_generic_token(p, v.op.kind, 1); + merge_next_token(p); + visit_expr(p, v.expr); + case Field_Value: + visit_expr(p, v.field); + push_generic_token(p, .Eq, 1); + visit_expr(p, v.value); + case Type_Assertion: + visit_expr(p, v.expr); + + if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { + push_generic_token(p, .Period, 0); + visit_expr(p, v.type); + } else { + push_generic_token(p, .Period, 0); + push_generic_token(p, .Open_Paren, 0); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); + } + + case Pointer_Type: + push_generic_token(p, .Pointer, 1); + merge_next_token(p); + visit_expr(p, v.elem); + case Implicit: + push_generic_token(p, v.tok.kind, 1); + case Poly_Type: + push_generic_token(p, .Dollar, 1); + merge_next_token(p); + visit_expr(p, v.type); + + if v.specialization != nil { + push_generic_token(p, .Quo, 0); + merge_next_token(p); + visit_expr(p, v.specialization); + } + case Array_Type: + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + visit_expr(p, v.len); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); + case Map_Type: + push_generic_token(p, .Map, 1); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.key); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.value); + case Helper_Type: + visit_expr(p, v.type); + case: + panic(fmt.aprint(expr.derived)); + } } - visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0) { - set_source_position(p, begin); + set_source_position(p, begin); - newline_braced := p.config.brace_style == .Allman; - newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; - newline_braced &= p.config.brace_style != ._1TBS; + newline_braced := p.config.brace_style == .Allman; + newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; + newline_braced &= p.config.brace_style != ._1TBS; - format_token := Format_Token { - kind = .Open_Brace, - parameter_count = count, - text = "{", - }; + format_token := Format_Token { + kind = .Open_Brace, + parameter_count = count, + text = "{", + }; - if newline_braced { - newline_position(p, 1); - push_format_token(p, format_token); - indent(p); - } else { - format_token.spaces_before = 1; - push_format_token(p, format_token); - indent(p); - } + if newline_braced { + newline_position(p, 1); + push_format_token(p, format_token); + indent(p); + } else { + format_token.spaces_before = 1; + push_format_token(p, format_token); + indent(p); + } } visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { - set_source_position(p, end); - newline_position(p, 1); - push_generic_token(p, .Close_Brace, 0); - unindent(p); - p.current_line.depth = p.depth; + set_source_position(p, end); + newline_position(p, 1); + push_generic_token(p, .Close_Brace, 0); + unindent(p); + p.current_line.depth = p.depth; } -visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) { - for stmt, i in stmts { - visit_stmt(p, stmt, .Generic, false, true); +visit_block_stmts :: proc(p: ^Printer, stmts: [] ^ast.Stmt, split := false) { + for stmt, i in stmts { + visit_stmt(p, stmt, .Generic, false, true); - if split && i != len(stmts)-1 && stmt.pos.line == stmts[i+1].pos.line { - newline_position(p, 1); - } - } + if split && i != len(stmts) - 1 && stmt.pos.line == stmts[i + 1].pos.line { + newline_position(p, 1); + } + } } visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false, enforce_newline := false) { - if list.list == nil { - return; - } + if list.list == nil { + return; + } - for field, i in list.list { + for field, i in list.list { - if !move_line_limit(p, field.pos, 1) && enforce_newline { - newline_position(p, 1); - } + if !move_line_limit(p, field.pos, 1) && enforce_newline { + newline_position(p, 1); + } - if .Using in field.flags { - push_generic_token(p, .Using, 0); - } + if .Using in field.flags { + push_generic_token(p, .Using, 0); + } - visit_exprs(p, field.names, true); + visit_exprs(p, field.names, true); - if field.type != nil { - if len(field.names) != 0 { - push_generic_token(p, .Colon, 0); - } - visit_expr(p, field.type); - } else { - push_generic_token(p, .Colon, 1); - push_generic_token(p, .Eq, 0); - visit_expr(p, field.default_value); - } + if field.type != nil { + if len(field.names) != 0 { + push_generic_token(p, .Colon, 0); + } + visit_expr(p, field.type); + } else { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Eq, 0); + visit_expr(p, field.default_value); + } - if field.tag.text != "" { - push_generic_token(p, field.tag.kind, 1, field.tag.text); - } + if field.tag.text != "" { + push_generic_token(p, field.tag.kind, 1, field.tag.text); + } - if (i != len(list.list) - 1 || trailing) && add_comma { - push_generic_token(p, .Comma, 0); - } - } + if (i != len(list.list) - 1 || trailing) && add_comma { + push_generic_token(p, .Comma, 0); + } + } } visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { - push_generic_token(p, .Proc, 1); + push_generic_token(p, .Proc, 1); - explicit_calling := false; + explicit_calling := false; - switch proc_type.calling_convention { - case .Odin: - case .Contextless: - push_string_token(p, "\"contextless\"", 1); - explicit_calling = true; - case .C_Decl: - push_string_token(p, "\"c\"", 1); - explicit_calling = true; - case .Std_Call: - push_string_token(p, "\"std\"", 1); - explicit_calling = true; - case .Fast_Call: - push_string_token(p, "\"fast\"", 1); - explicit_calling = true; - case .None: - //nothing i guess - case .Invalid: - //nothing i guess - case .Foreign_Block_Default: - } + switch proc_type.calling_convention { + case .Odin: + case .Contextless: + push_string_token(p, "\"contextless\"", 1); + explicit_calling = true; + case .C_Decl: + push_string_token(p, "\"c\"", 1); + explicit_calling = true; + case .Std_Call: + push_string_token(p, "\"std\"", 1); + explicit_calling = true; + case .Fast_Call: + push_string_token(p, "\"fast\"", 1); + explicit_calling = true; + case .None: + //nothing i guess + case .Invalid: + //nothing i guess + case .Foreign_Block_Default: + } - if explicit_calling { - push_generic_token(p, .Open_Paren, 1); - } else { - push_generic_token(p, .Open_Paren, 0); - } + if explicit_calling { + push_generic_token(p, .Open_Paren, 1); + } else { + push_generic_token(p, .Open_Paren, 0); + } - visit_signature_list(p, proc_type.params, false); + visit_signature_list(p, proc_type.params, false); - push_generic_token(p, .Close_Paren, 0); + push_generic_token(p, .Close_Paren, 0); - if proc_type.results != nil { - push_generic_token(p, .Sub, 1); - push_generic_token(p, .Gt, 0); + if proc_type.results != nil { + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); - use_parens := false; - use_named := false; + use_parens := false; + use_named := false; - if len(proc_type.results.list) > 1 { - use_parens = true; - } else if len(proc_type.results.list) == 1 { + if len(proc_type.results.list) > 1 { + use_parens = true; + } else if len(proc_type.results.list) == 1 { - for name in proc_type.results.list[0].names { - if ident, ok := name.derived.(ast.Ident); ok { - if ident.name != "_" { - use_parens = true; - } - } - } - } + for name in proc_type.results.list[0].names { + if ident, ok := name.derived.(ast.Ident); ok { + if ident.name != "_" { + use_parens = true; + } + } + } + } - if use_parens { - push_generic_token(p, .Open_Paren, 1); - visit_signature_list(p, proc_type.results); - push_generic_token(p, .Close_Paren, 0); - } else { - visit_signature_list(p, proc_type.results); - } - } - + if use_parens { + push_generic_token(p, .Open_Paren, 1); + visit_signature_list(p, proc_type.results); + push_generic_token(p, .Close_Paren, 0); + } else { + visit_signature_list(p, proc_type.results); + } + } } visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { - move_line(p, binary.left.pos); + move_line(p, binary.left.pos); - if v, ok := binary.left.derived.(ast.Binary_Expr); ok { - visit_binary_expr(p, v); - } else { - visit_expr(p, binary.left); - } + if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.left); + } - if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { - push_generic_token(p, binary.op.kind, 0); - } else { - push_generic_token(p, binary.op.kind, 1); - } + if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { + push_generic_token(p, binary.op.kind, 0); + } else { + push_generic_token(p, binary.op.kind, 1); + } - move_line(p, binary.right.pos); - - if v, ok := binary.right.derived.(ast.Binary_Expr); ok { - visit_binary_expr(p, v); - } else { - visit_expr(p, binary.right); - } + move_line(p, binary.right.pos); + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.right); + } } -visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { +visit_call_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, ellipsis := false) { - if len(list) == 0 { - return; - } + if len(list) == 0 { + return; + } - //all the expression are on the line - if list[0].pos.line == list[len(list) - 1].pos.line { - for expr, i in list { + //all the expression are on the line + if list[0].pos.line == list[len(list) - 1].pos.line { + for expr, i in list { - if i == len(list) - 1 && ellipsis { - push_generic_token(p, .Ellipsis, 0); - } + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } - visit_expr(p, expr); + visit_expr(p, expr); - if i != len(list) - 1 { - push_generic_token(p, .Comma, 0); - } - } - } else { - for expr, i in list { + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } else { + for expr, i in list { - //we have to newline the expressions to respect the source - move_line_limit(p, expr.pos, 1); - + //we have to newline the expressions to respect the source + move_line_limit(p, expr.pos, 1); - if i == len(list) - 1 && ellipsis { - push_generic_token(p, .Ellipsis, 0); - } + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } - visit_expr(p, expr); + visit_expr(p, expr); - if i != len(list) - 1 { - push_generic_token(p, .Comma, 0); - } - } - } + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } } - visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := true) { - if list.list == nil { - return; - } + if list.list == nil { + return; + } - for field, i in list.list { + for field, i in list.list { - move_line_limit(p, field.pos, 1); + move_line_limit(p, field.pos, 1); - if .Using in field.flags { - push_generic_token(p, .Using, 0); - } + if .Using in field.flags { + push_generic_token(p, .Using, 0); + } - named := false; + named := false; - for name in field.names { - if ident, ok := name.derived.(ast.Ident); ok { - //for some reason the parser uses _ to mean empty - if ident.name != "_" || !remove_blank { - named = true; - } - } else { - //alternative is poly names - named = true; - } - } + for name in field.names { + if ident, ok := name.derived.(ast.Ident); ok { + //for some reason the parser uses _ to mean empty + if ident.name != "_" || !remove_blank { + named = true; + } + } else { + //alternative is poly names + named = true; + } + } - if named { - visit_exprs(p, field.names, true); + if named { + visit_exprs(p, field.names, true); - if len(field.names) != 0 && field.type != nil { - push_generic_token(p, .Colon, 0); - } - } + if len(field.names) != 0 && field.type != nil { + push_generic_token(p, .Colon, 0); + } + } - if field.type != nil && field.default_value != nil { - visit_expr(p, field.type); - push_generic_token(p, .Eq, 0); - visit_expr(p, field.default_value); - } else if field.type != nil { - visit_expr(p, field.type); - } else { - push_generic_token(p, .Colon, 1); - push_generic_token(p, .Eq, 0); - visit_expr(p, field.default_value); - } - - if i != len(list.list) - 1 { - push_generic_token(p, .Comma, 0); - } - } -} + if field.type != nil && field.default_value != nil { + visit_expr(p, field.type); + push_generic_token(p, .Eq, 1); + visit_expr(p, field.default_value); + } else if field.type != nil { + visit_expr(p, field.type); + } else { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Eq, 0); + visit_expr(p, field.default_value); + } + if i != len(list.list) - 1 { + push_generic_token(p, .Comma, 0); + } + } +} \ No newline at end of file From a12db382e05cbd3ea05df05c06f8dd15de8b8b65 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 15 Apr 2021 00:24:00 +0200 Subject: [PATCH 022/137] damn, ran the odinfmt with spaces instead of tabs... --- core/odin/printer/printer.odin | 640 +++++----- core/odin/printer/visit.odin | 2120 ++++++++++++++++---------------- 2 files changed, 1380 insertions(+), 1380 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 4d8539dfa..3db48662c 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -13,184 +13,184 @@ Line_Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct} Line_Type :: bit_set[Line_Type_Enum]; 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 + 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 } Format_Token :: struct { - kind: tokenizer.Token_Kind, - text: string, - spaces_before: int, - parameter_count: int, + kind: tokenizer.Token_Kind, + text: string, + 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, + 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, - indent_cases: bool, - newline_style: Newline_Style, + 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, + indent_cases: bool, + newline_style: Newline_Style, } Brace_Style :: enum { - _1TBS, - Allman, - Stroustrup, - K_And_R, + _1TBS, + Allman, + Stroustrup, + K_And_R, } Block_Type :: enum { - None, - If_Stmt, - Proc, - Generic, - Comp_Lit, - Switch_Stmt, + None, + If_Stmt, + Proc, + Generic, + Comp_Lit, + Switch_Stmt, } Alignment_Style :: enum { - Align_On_Colon_And_Equals, - Align_On_Type_And_Equals, + Align_On_Colon_And_Equals, + Align_On_Type_And_Equals, } Newline_Style :: enum { - CRLF, - LF, + CRLF, + LF, } default_style := Config { - spaces = 4, - newline_limit = 2, - convert_do = false, - semicolons = true, - tabs = false, - 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, - newline_style = .CRLF, + 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, + newline_style = .CRLF, }; make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { - return { - config = config, - allocator = allocator, - debug = false, - }; + return { + config = config, + allocator = allocator, + debug = false, + }; } print :: proc(p: ^Printer, file: ^ast.File) -> string { - p.comments = file.comments; + 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); - } + 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_line(p, 0); + set_line(p, 0); - push_generic_token(p, .Package, 0); - push_ident_token(p, file.pkg_name, 1); + 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); - } + 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); - } + if len(p.comments) > 0 { + infinite := p.comments[len(p.comments) - 1].end; + infinite.offset = 9999999; + push_comments(p, infinite); + } - fix_lines(p); + fix_lines(p); - builder := strings.make_builder(0, mem.megabytes(5), p.allocator); + builder := strings.make_builder(0, mem.megabytes(5), p.allocator); - last_line := 0; + last_line := 0; - newline: string; + newline: string; - if p.config.newline_style == .LF { - newline = "\n"; - } else { - newline = "\r\n"; - } + 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 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); - } + 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.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)); - } + if p.debug { + strings.write_string(&builder, fmt.tprintf("line %v: ", line_index)); + } - for format_token in line.format_tokens { + for format_token in line.format_tokens { - for i := 0; i < format_token.spaces_before; i += 1 { - strings.write_byte(&builder, ' '); - } + for i := 0; i < format_token.spaces_before; i += 1 { + strings.write_byte(&builder, ' '); + } - strings.write_string(&builder, format_token.text); - } + strings.write_string(&builder, format_token.text); + } - last_line = line_index; - } + last_line = line_index; + } - return strings.to_string(builder); + return strings.to_string(builder); } fix_lines :: proc(p: ^Printer) { - align_var_decls(p); - align_blocks(p); - align_comments(p); //align them last since they rely on the other alignments + align_var_decls(p); + align_blocks(p); + align_comments(p); //align them last since they rely on the other alignments } align_var_decls :: proc(p: ^Printer) { @@ -198,277 +198,277 @@ align_var_decls :: proc(p: ^Printer) { align_switch_smt :: proc(p: ^Printer, index: int) { - switch_found := false; - brace_token: Format_Token; - brace_line: int; + switch_found := false; + brace_token: Format_Token; + brace_line: int; - found_switch_brace: for line, line_index in p.lines[index:] { + found_switch_brace: for line, line_index in p.lines[index:] { - for format_token in line.format_tokens { + 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 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; - } + if !switch_found { + return; + } - largest := 0; - case_count := 0; + largest := 0; + case_count := 0; - //find all the switch cases that are one lined - for line, line_index in p.lines[brace_line + 1:] { + //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; + case_found := false; + colon_found := false; + length := 0; - for format_token in line.format_tokens { + for format_token in line.format_tokens { - if format_token.kind == .Comment { - continue; - } + if format_token.kind == .Comment { + continue; + } - //this will only happen if the case is one lined - if case_found && colon_found { - largest = max(length, largest); - break; - } + //this will only happen if the case is one lined + if case_found && colon_found { + 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; - } + 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; - } + length += len(format_token.text) + format_token.spaces_before; + } - if case_count >= brace_token.parameter_count { - break; - } - } + if case_count >= brace_token.parameter_count { + break; + } + } - case_count = 0; + case_count = 0; - for line, line_index in p.lines[brace_line + 1:] { + for line, line_index in p.lines[brace_line + 1:] { - case_found := false; - colon_found := false; - length := 0; + case_found := false; + colon_found := false; + length := 0; - for format_token, i in line.format_tokens { + for format_token, i in line.format_tokens { - if format_token.kind == .Comment { - continue; - } + if format_token.kind == .Comment { + continue; + } - //this will only happen if the case is one lined - if case_found && colon_found { - line.format_tokens[i].spaces_before += (largest - length); - break; - } + //this will only happen if the case is one lined + if case_found && colon_found { + line.format_tokens[i].spaces_before += (largest - length); + break; + } - if format_token.kind == .Case { - case_found = true; - case_count += 1; - } else if format_token.kind == .Colon { - colon_found = true; - } + 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; - } + length += len(format_token.text) + format_token.spaces_before; + } - if case_count >= brace_token.parameter_count { - break; - } - } + if case_count >= brace_token.parameter_count { + break; + } + } } align_struct :: proc(p: ^Printer, index: int) { - struct_found := false; - brace_token: Format_Token; - brace_line: int; + struct_found := false; + brace_token: Format_Token; + brace_line: int; - found_struct_brace: for line, line_index in p.lines[index:] { + found_struct_brace: for line, line_index in p.lines[index:] { - for format_token in line.format_tokens { + 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 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; - } + if !struct_found { + return; + } - largest := 0; - colon_count := 0; + largest := 0; + colon_count := 0; - for line, line_index in p.lines[brace_line + 1:] { + for line, line_index in p.lines[brace_line + 1:] { - length := 0; + length := 0; - for format_token in line.format_tokens { + for format_token in line.format_tokens { - if format_token.kind == .Comment { - continue; - } + if format_token.kind == .Comment { + continue; + } - if format_token.kind == .Colon { - colon_count += 1; - largest = max(length, largest); - break; - } + if format_token.kind == .Colon { + colon_count += 1; + largest = max(length, largest); + break; + } - length += len(format_token.text) + format_token.spaces_before; - } + length += len(format_token.text) + format_token.spaces_before; + } - if colon_count >= brace_token.parameter_count { - break; - } - } + if colon_count >= brace_token.parameter_count { + break; + } + } - colon_count = 0; + colon_count = 0; - for line, line_index in p.lines[brace_line + 1:] { + for line, line_index in p.lines[brace_line + 1:] { - length := 0; + length := 0; - for format_token, i in line.format_tokens { + for format_token, i in line.format_tokens { - if format_token.kind == .Comment { - continue; - } + if format_token.kind == .Comment { + continue; + } - if format_token.kind == .Colon { - colon_count += 1; - line.format_tokens[i + 1].spaces_before = largest - length + 1; - break; - } + if format_token.kind == .Colon { + colon_count += 1; + line.format_tokens[i + 1].spaces_before = largest - length + 1; + break; + } - length += len(format_token.text) + format_token.spaces_before; - } + length += len(format_token.text) + format_token.spaces_before; + } - if colon_count >= brace_token.parameter_count { - break; - } - } + if colon_count >= brace_token.parameter_count { + break; + } + } } align_blocks :: proc(p: ^Printer) { - for line, line_index in p.lines { + for line, line_index in p.lines { - if len(line.format_tokens) <= 0 { - continue; - } + if len(line.format_tokens) <= 0 { + continue; + } - if .Switch_Stmt in line.types && p.config.align_switch { - align_switch_smt(p, line_index); - } + if .Switch_Stmt in line.types && p.config.align_switch { + align_switch_smt(p, line_index); + } - if .Struct in line.types && p.config.align_structs { - align_struct(p, line_index); - } - } + if .Struct in line.types && p.config.align_structs { + align_struct(p, line_index); + } + } } align_comments :: proc(p: ^Printer) { - Comment_Align_Info :: struct { - length: int, - begin: int, - end: int, - depth: int, - }; + Comment_Align_Info :: struct { + length: int, + begin: int, + end: int, + depth: int, + }; - comment_infos := make([dynamic] Comment_Align_Info, 0, context.temp_allocator); + comment_infos := make([dynamic] Comment_Align_Info, 0, context.temp_allocator); - current_info: Comment_Align_Info; + current_info: Comment_Align_Info; - for line, line_index in p.lines { + for line, line_index in p.lines { - if len(line.format_tokens) <= 0 { - continue; - } + if len(line.format_tokens) <= 0 { + continue; + } - if .Line_Comment in line.types { + 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.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); - } + 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; - } + current_info.begin = line_index; + current_info.end = line_index; + current_info.depth = line.depth; + current_info.length = 0; + } - length := 0; + length := 0; - for format_token, i in line.format_tokens { + 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; - } + 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); - } - } - } + 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); - } + if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 { + append(&comment_infos, current_info); + } - for info in comment_infos { + for info in comment_infos { - if info.begin == info.end || info.length == 0 { - continue; - } + if info.begin == info.end || info.length == 0 { + continue; + } - for i := info.begin; i <= info.end; i += 1 { + for i := info.begin; i <= info.end; i += 1 { - l := p.lines[i]; + l := p.lines[i]; - length := 0; + length := 0; - for format_token, i in l.format_tokens { + 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; - } - } + 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; + } + } - length += format_token.spaces_before + len(format_token.text); - } - } - } + length += format_token.spaces_before + len(format_token.text); + } + } + } } \ No newline at end of file diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 7cc856e3b..ae3097f03 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -12,1453 +12,1453 @@ import "core:sort" //right the attribute order is not linearly parsed(bug?) @(private) sort_attribute :: proc(s: ^[dynamic] ^ast.Attribute) -> sort.Interface { - return sort.Interface { - collection = rawptr(s), - len = proc(it: sort.Interface) -> int { - s := (^[dynamic] ^ast.Attribute)(it.collection); - return len(s^); - }, - less = proc(it: sort.Interface, i, j: int) -> bool { - s := (^[dynamic] ^ast.Attribute)(it.collection); - return s[i].pos.offset < s[j].pos.offset; - }, - swap = proc(it: sort.Interface, i, j: int) { - s := (^[dynamic] ^ast.Attribute)(it.collection); - s[i], s[j] = s[j], s[i]; - }, - }; + return sort.Interface { + collection = rawptr(s), + len = proc(it: sort.Interface) -> int { + s := (^[dynamic] ^ast.Attribute)(it.collection); + return len(s^); + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + s := (^[dynamic] ^ast.Attribute)(it.collection); + return s[i].pos.offset < s[j].pos.offset; + }, + swap = proc(it: sort.Interface, i, j: int) { + s := (^[dynamic] ^ast.Attribute)(it.collection); + s[i], s[j] = s[j], s[i]; + }, + }; } @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { - if len(p.comments) <= p.latest_comment_index { - return false; - } + if len(p.comments) <= p.latest_comment_index { + return false; + } - comment := p.comments[p.latest_comment_index]; + comment := p.comments[p.latest_comment_index]; - return comment.pos.offset < pos.offset; + return comment.pos.offset < pos.offset; } @(private) next_comment_group :: proc(p: ^Printer) { - p.latest_comment_index += 1; + p.latest_comment_index += 1; } @(private) push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { - if len(comment.text) == 0 { - return 0; - } + if len(comment.text) == 0 { + return 0; + } - if comment.text[0] == '/' && comment.text[1] == '/' { - format_token := Format_Token { - spaces_before = 1, - kind = .Comment, - text = comment.text, - }; + if comment.text[0] == '/' && comment.text[1] == '/' { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = comment.text, + }; - if len(p.current_line.format_tokens) == 0 { - format_token.spaces_before = 0; - } + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } - if !p.current_line.used { - p.current_line.used = true; - p.current_line.depth = p.depth; - } + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } - append(&p.current_line.format_tokens, format_token); - p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; - hint_current_line(p,{.Line_Comment}); + hint_current_line(p,{.Line_Comment}); - return 0; - } else { + return 0; + } else { - builder := strings.make_builder(context.temp_allocator); + builder := strings.make_builder(context.temp_allocator); - c_len := len(comment.text); - trim_space := true; + c_len := len(comment.text); + trim_space := true; - multilines: [dynamic] string; + multilines: [dynamic] string; - for i := 0; i < len(comment.text); i += 1 { + for i := 0; i < len(comment.text); i += 1 { - c := comment.text[i]; + c := comment.text[i]; - if c != ' ' && c != '\t' { - trim_space = false; - } + if c != ' ' && c != '\t' { + trim_space = false; + } - if (c == ' ' || c == '\t' || c == '\n') && trim_space { - continue; - } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { - append(&multilines, strings.to_string(builder)); - builder = strings.make_builder(context.temp_allocator); - trim_space = true; - i += 1; - } else if c == 10 { - append(&multilines, strings.to_string(builder)); - builder = strings.make_builder(context.temp_allocator); - trim_space = true; - } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { - strings.write_string(&builder, "/*"); - trim_space = true; - p.depth += 1; - i += 1; - } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { - p.depth -= 1; - trim_space = true; - strings.write_string(&builder, "*/"); - i += 1; - } else { - strings.write_byte(&builder, c); - } - } + if (c == ' ' || c == '\t' || c == '\n') && trim_space { + continue; + } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + i += 1; + } else if c == 10 { + append(&multilines, strings.to_string(builder)); + builder = strings.make_builder(context.temp_allocator); + trim_space = true; + } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { + strings.write_string(&builder, "/*"); + trim_space = true; + p.depth += 1; + i += 1; + } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { + p.depth -= 1; + trim_space = true; + strings.write_string(&builder, "*/"); + i += 1; + } else { + strings.write_byte(&builder, c); + } + } - if strings.builder_len(builder) > 0 { - append(&multilines, strings.to_string(builder)); - } + if strings.builder_len(builder) > 0 { + append(&multilines, strings.to_string(builder)); + } - for line in multilines { - format_token := Format_Token { - spaces_before = 1, - kind = .Comment, - text = line, - }; + for line in multilines { + format_token := Format_Token { + spaces_before = 1, + kind = .Comment, + text = line, + }; - if len(p.current_line.format_tokens) == 0 { - format_token.spaces_before = 0; - } + if len(p.current_line.format_tokens) == 0 { + format_token.spaces_before = 0; + } - if strings.contains(line, "*/") { - unindent(p); - } + if strings.contains(line, "*/") { + unindent(p); + } - if !p.current_line.used { - p.current_line.used = true; - p.current_line.depth = p.depth; - } + if !p.current_line.used { + p.current_line.used = true; + p.current_line.depth = p.depth; + } - append(&p.current_line.format_tokens, format_token); - p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; + append(&p.current_line.format_tokens, format_token); + p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; - if strings.contains(line, "/*") { - indent(p); - } + if strings.contains(line, "/*") { + indent(p); + } - newline_position(p, 1); - } + newline_position(p, 1); + } - return len(multilines); - } + return len(multilines); + } } @(private) push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { - prev_comment: ^tokenizer.Token; - prev_comment_lines: int; + prev_comment: ^tokenizer.Token; + prev_comment_lines: int; - for comment_before_position(p, pos) { + for comment_before_position(p, pos) { - comment_group := p.comments[p.latest_comment_index]; + comment_group := p.comments[p.latest_comment_index]; - if prev_comment == nil { - lines := comment_group.pos.line - p.last_source_position.line; - set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); - } + if prev_comment == nil { + lines := comment_group.pos.line - p.last_source_position.line; + set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + } - for comment, i in comment_group.list { + for comment, i in comment_group.list { - if prev_comment != nil && p.last_source_position.line != comment.pos.line { - newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); - } + if prev_comment != nil && p.last_source_position.line != comment.pos.line { + newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); + } - prev_comment_lines = push_comment(p, comment); - prev_comment = &comment_group.list[i]; - } + prev_comment_lines = push_comment(p, comment); + prev_comment = &comment_group.list[i]; + } - next_comment_group(p); - } + next_comment_group(p); + } - if prev_comment != nil { - newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); - } + if prev_comment != nil { + newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); + } } @(private) append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_Token { - format_token := format_token; + format_token := format_token; - if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || - p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || - p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { - format_token.spaces_before = 0; - } else if p.merge_next_token { - format_token.spaces_before = 0; - p.merge_next_token = false; - } else if p.space_next_token { - format_token.spaces_before = 1; - p.space_next_token = false; - } + if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || + p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || + p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { + format_token.spaces_before = 0; + } else if p.merge_next_token { + format_token.spaces_before = 0; + p.merge_next_token = false; + } else if p.space_next_token { + format_token.spaces_before = 1; + p.space_next_token = false; + } - push_comments(p, p.source_position); + push_comments(p, p.source_position); - unwrapped_line := p.current_line; + unwrapped_line := p.current_line; - if !unwrapped_line.used { - unwrapped_line.used = true; - unwrapped_line.depth = p.depth; - } + if !unwrapped_line.used { + unwrapped_line.used = true; + unwrapped_line.depth = p.depth; + } - if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { - format_token.spaces_before = 0; - } + if len(unwrapped_line.format_tokens) == 0 && format_token.spaces_before == 1 { + format_token.spaces_before = 0; + } - p.last_source_position = p.source_position; - p.last_line_index = p.current_line_index; + p.last_source_position = p.source_position; + p.last_line_index = p.current_line_index; - append(&unwrapped_line.format_tokens, format_token); - return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens) - 1]; + append(&unwrapped_line.format_tokens, format_token); + return &unwrapped_line.format_tokens[len(unwrapped_line.format_tokens) - 1]; } @(private) push_format_token :: proc(p: ^Printer, format_token: Format_Token) { - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { - format_token := Format_Token { - spaces_before = spaces_before, - kind = kind, - text = tokenizer.tokens[kind], - }; + format_token := Format_Token { + spaces_before = spaces_before, + kind = kind, + text = tokenizer.tokens[kind], + }; - if value != "" { - format_token.text = value; - } + if value != "" { + format_token.text = value; + } - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_string_token :: proc(p: ^Printer, text: string, spaces_before: int) { - format_token := Format_Token { - spaces_before = spaces_before, - kind = .String, - text = text, - }; + format_token := Format_Token { + spaces_before = spaces_before, + kind = .String, + text = text, + }; - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) push_ident_token :: proc(p: ^Printer, text: string, spaces_before: int) { - format_token := Format_Token { - spaces_before = spaces_before, - kind = .Ident, - text = text, - }; + format_token := Format_Token { + spaces_before = spaces_before, + kind = .Ident, + text = text, + }; - p.last_token = append_format_token(p, format_token); + p.last_token = append_format_token(p, format_token); } @(private) set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { - p.source_position = pos; + p.source_position = pos; } @(private) move_line :: proc(p: ^Printer, pos: tokenizer.Pos) { - move_line_limit(p, pos, p.config.newline_limit); + move_line_limit(p, pos, p.config.newline_limit); } @(private) move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> bool { - lines := min(pos.line - p.source_position.line, limit); + lines := min(pos.line - p.source_position.line, limit); - if lines < 0 { - return false; - } + if lines < 0 { + return false; + } - p.source_position = pos; - p.current_line_index += lines; - set_line(p, p.current_line_index); - return lines > 0; + p.source_position = pos; + p.current_line_index += lines; + set_line(p, p.current_line_index); + return lines > 0; } @(private) set_line :: proc(p: ^Printer, line: int) -> ^Line { - unwrapped_line: ^Line; + unwrapped_line: ^Line; - if line >= len(p.lines) { - for i := len(p.lines); i <= line; i += 1 { - new_line: Line; - new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); - append(&p.lines, new_line); - } - unwrapped_line = &p.lines[line]; - } else { - unwrapped_line = &p.lines[line]; - } + if line >= len(p.lines) { + for i := len(p.lines); i <= line; i += 1 { + new_line: Line; + new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); + append(&p.lines, new_line); + } + unwrapped_line = &p.lines[line]; + } else { + unwrapped_line = &p.lines[line]; + } - p.current_line = unwrapped_line; - p.current_line_index = line; + p.current_line = unwrapped_line; + p.current_line_index = line; - return unwrapped_line; + return unwrapped_line; } @(private) newline_position :: proc(p: ^Printer, count: int) { - p.current_line_index += count; - set_line(p, p.current_line_index); + p.current_line_index += count; + set_line(p, p.current_line_index); } @(private) indent :: proc(p: ^Printer) { - p.depth += 1; + p.depth += 1; } @(private) unindent :: proc(p: ^Printer) { - p.depth -= 1; + p.depth -= 1; } @(private) merge_next_token :: proc(p: ^Printer) { - p.merge_next_token = true; + p.merge_next_token = true; } @(private) space_next_token :: proc(p: ^Printer) { - p.space_next_token = true; + p.space_next_token = true; } @(private) hint_current_line :: proc(p: ^Printer, hint: Line_Type) { - p.current_line.types |= hint; + p.current_line.types |= hint; } @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { - using ast; + using ast; - if decl == nil { - return; - } + if decl == nil { + return; + } - switch v in &decl.derived { - case Expr_Stmt: - move_line(p, decl.pos); - visit_expr(p, v.expr); - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case When_Stmt: - visit_stmt(p, cast(^Stmt)decl); - case Foreign_Import_Decl: - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } + switch v in &decl.derived { + case Expr_Stmt: + move_line(p, decl.pos); + visit_expr(p, v.expr); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case When_Stmt: + visit_stmt(p, cast(^Stmt)decl); + case Foreign_Import_Decl: + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } - move_line(p, decl.pos); + move_line(p, decl.pos); - push_generic_token(p, v.foreign_tok.kind, 0); - push_generic_token(p, v.import_tok.kind, 1); + push_generic_token(p, v.foreign_tok.kind, 0); + push_generic_token(p, v.import_tok.kind, 1); - if v.name != nil { - push_ident_token(p, v.name.name, 1); - } + if v.name != nil { + push_ident_token(p, v.name.name, 1); + } - for path in v.fullpaths { - push_ident_token(p, path, 0); - } - case Foreign_Block_Decl: + for path in v.fullpaths { + push_ident_token(p, path, 0); + } + case Foreign_Block_Decl: - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } - move_line(p, decl.pos); + move_line(p, decl.pos); - push_generic_token(p, .Foreign, 0); + push_generic_token(p, .Foreign, 0); - visit_expr(p, v.foreign_library); - visit_stmt(p, v.body); - case Import_Decl: - move_line(p, decl.pos); + visit_expr(p, v.foreign_library); + visit_stmt(p, v.body); + case Import_Decl: + move_line(p, decl.pos); - if v.name.text != "" { - push_generic_token(p, v.import_tok.kind, 1); - push_generic_token(p, v.name.kind, 1, v.name.text); - push_ident_token(p, v.fullpath, 1); - } else { - push_generic_token(p, v.import_tok.kind, 1); - push_ident_token(p, v.fullpath, 1); - } + if v.name.text != "" { + push_generic_token(p, v.import_tok.kind, 1); + push_generic_token(p, v.name.kind, 1, v.name.text); + push_ident_token(p, v.fullpath, 1); + } else { + push_generic_token(p, v.import_tok.kind, 1); + push_ident_token(p, v.fullpath, 1); + } - case Value_Decl: - if len(v.attributes) > 0 { - sort.sort(sort_attribute(&v.attributes)); - move_line(p, v.attributes[0].pos); - visit_attributes(p, v.attributes); - } + case Value_Decl: + if len(v.attributes) > 0 { + sort.sort(sort_attribute(&v.attributes)); + move_line(p, v.attributes[0].pos); + visit_attributes(p, v.attributes); + } - move_line(p, decl.pos); + move_line(p, decl.pos); - if v.is_using { - push_generic_token(p, .Using, 0); - } + if v.is_using { + push_generic_token(p, .Using, 0); + } - visit_exprs(p, v.names, true); + visit_exprs(p, v.names, true); - if v.type != nil { - if !v.is_mutable && v.type != nil { - push_generic_token(p, .Colon, 0); - } else { - push_generic_token(p, .Colon, 0); - } + if v.type != nil { + if !v.is_mutable && v.type != nil { + push_generic_token(p, .Colon, 0); + } else { + push_generic_token(p, .Colon, 0); + } - visit_expr(p, v.type); - } else { - if !v.is_mutable && v.type == nil { - push_generic_token(p, .Colon, 1); - push_generic_token(p, .Colon, 0); - } else { - push_generic_token(p, .Colon, 1); - } - } + visit_expr(p, v.type); + } else { + if !v.is_mutable && v.type == nil { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Colon, 0); + } else { + push_generic_token(p, .Colon, 1); + } + } - if v.is_mutable && v.type != nil && len(v.values) != 0 { - push_generic_token(p, .Eq, 1); - } else if v.is_mutable && v.type == nil && len(v.values) != 0 { - push_generic_token(p, .Eq, 0); - } else if !v.is_mutable && v.type != nil { - push_generic_token(p, .Colon, 0); - } + if v.is_mutable && v.type != nil && len(v.values) != 0 { + push_generic_token(p, .Eq, 1); + } else if v.is_mutable && v.type == nil && len(v.values) != 0 { + push_generic_token(p, .Eq, 0); + } else if !v.is_mutable && v.type != nil { + push_generic_token(p, .Colon, 0); + } - visit_exprs(p, v.values, true); + visit_exprs(p, v.values, true); - add_semicolon := true; + add_semicolon := true; - for value in v.values { - switch a in value.derived { - case Proc_Lit, Union_Type, Enum_Type, Struct_Type: - add_semicolon = false || called_in_stmt; - } - } + for value in v.values { + switch a in value.derived { + case Proc_Lit, Union_Type, Enum_Type, Struct_Type: + add_semicolon = false || called_in_stmt; + } + } - if add_semicolon && p.config.semicolons && !p.skip_semicolon { - push_generic_token(p, .Semicolon, 0); - } + if add_semicolon && p.config.semicolons && !p.skip_semicolon { + push_generic_token(p, .Semicolon, 0); + } - case: - panic(fmt.aprint(decl.derived)); - } + case: + panic(fmt.aprint(decl.derived)); + } } @(private) visit_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, add_comma := false, trailing := false) { - if len(list) == 0 { - return; - } + if len(list) == 0 { + return; + } - //we have to newline the expressions to respect the source - for expr, i in list { + //we have to newline the expressions to respect the source + for expr, i in list { - move_line_limit(p, expr.pos, 1); + move_line_limit(p, expr.pos, 1); - visit_expr(p, expr); + visit_expr(p, expr); - if (i != len(list) - 1 || trailing) && add_comma { - push_generic_token(p, .Comma, 0); - } - } + if (i != len(list) - 1 || trailing) && add_comma { + push_generic_token(p, .Comma, 0); + } + } } @(private) visit_attributes :: proc(p: ^Printer, attributes: [dynamic] ^ast.Attribute) { - if len(attributes) == 0 { - return; - } + if len(attributes) == 0 { + return; + } - for attribute, i in attributes { + for attribute, i in attributes { - move_line_limit(p, attribute.pos, 1); + move_line_limit(p, attribute.pos, 1); - push_generic_token(p, .At, 0); - push_generic_token(p, .Open_Paren, 0); + push_generic_token(p, .At, 0); + push_generic_token(p, .Open_Paren, 0); - visit_exprs(p, attribute.elems, true); + visit_exprs(p, attribute.elems, true); - push_generic_token(p, .Close_Paren, 0); - } + push_generic_token(p, .Close_Paren, 0); + } } @(private) visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { - using ast; + using ast; - if stmt == nil { - return; - } + if stmt == nil { + return; + } - switch v in stmt.derived { - case Value_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Import_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - case Foreign_Block_Decl: - visit_decl(p, cast(^Decl)stmt, true); - return; - } + switch v in stmt.derived { + case Value_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Import_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + case Foreign_Block_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; + } - switch v in stmt.derived { - case Using_Stmt: - move_line(p, v.pos); + switch v in stmt.derived { + case Using_Stmt: + move_line(p, v.pos); - push_generic_token(p, .Using, 1); + push_generic_token(p, .Using, 1); - visit_exprs(p, v.list, true); + visit_exprs(p, v.list, true); - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Block_Stmt: - move_line(p, v.pos); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Block_Stmt: + move_line(p, v.pos); - if v.pos.line == v.end.line { - if !empty_block { - push_generic_token(p, .Open_Brace, 0); - } + if v.pos.line == v.end.line { + if !empty_block { + push_generic_token(p, .Open_Brace, 0); + } - set_source_position(p, v.pos); + set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); - set_source_position(p, v.end); + set_source_position(p, v.end); - if !empty_block { - push_generic_token(p, .Close_Brace, 0); - } - } else { - if !empty_block { - visit_begin_brace(p, v.pos, block_type, len(v.stmts)); - } + if !empty_block { + push_generic_token(p, .Close_Brace, 0); + } + } else { + if !empty_block { + visit_begin_brace(p, v.pos, block_type, len(v.stmts)); + } - set_source_position(p, v.pos); + set_source_position(p, v.pos); - visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); + visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); - set_source_position(p, v.end); + set_source_position(p, v.end); - if !empty_block { - visit_end_brace(p, v.end); - } - } - case If_Stmt: - move_line(p, v.pos); + if !empty_block { + visit_end_brace(p, v.end); + } + } + case If_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .If, 1); + push_generic_token(p, .If, 1); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } - visit_expr(p, v.cond); + visit_expr(p, v.cond); - uses_do := false; + uses_do := false; - if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { - uses_do = true; - } + if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do { + uses_do = true; + } - if uses_do && !p.config.convert_do { - push_generic_token(p, .Do, 1); - visit_stmt(p, v.body, .If_Stmt, true); - } else { - if uses_do { - newline_position(p, 1); - } + if uses_do && !p.config.convert_do { + push_generic_token(p, .Do, 1); + visit_stmt(p, v.body, .If_Stmt, true); + } else { + if uses_do { + newline_position(p, 1); + } - visit_stmt(p, v.body, .If_Stmt); - } + visit_stmt(p, v.body, .If_Stmt); + } - if v.else_stmt != nil { + if v.else_stmt != nil { - if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { - newline_position(p, 1); - } + if p.config.brace_style == .Allman || p.config.brace_style == .Stroustrup { + newline_position(p, 1); + } - push_generic_token(p, .Else, 1); + push_generic_token(p, .Else, 1); - set_source_position(p, v.else_stmt.pos); + set_source_position(p, v.else_stmt.pos); - visit_stmt(p, v.else_stmt); - } - case Switch_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.else_stmt); + } + case Switch_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - if v.partial { - push_ident_token(p, "#partial", 1); - } + if v.partial { + push_ident_token(p, "#partial", 1); + } - push_generic_token(p, .Switch, 1); + push_generic_token(p, .Switch, 1); - hint_current_line(p,{.Switch_Stmt}); + hint_current_line(p,{.Switch_Stmt}); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + } - if v.init != nil && v.cond != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil && v.cond != nil { + push_generic_token(p, .Semicolon, 0); + } - visit_expr(p, v.cond); - visit_stmt(p, v.body); - case Case_Clause: - move_line(p, v.pos); + visit_expr(p, v.cond); + visit_stmt(p, v.body); + case Case_Clause: + move_line(p, v.pos); - if !p.config.indent_cases { - unindent(p); - } + if !p.config.indent_cases { + unindent(p); + } - push_generic_token(p, .Case, 0); + push_generic_token(p, .Case, 0); - if v.list != nil { - visit_exprs(p, v.list, true); - } + if v.list != nil { + visit_exprs(p, v.list, true); + } - push_generic_token(p, v.terminator.kind, 0); + push_generic_token(p, v.terminator.kind, 0); - indent(p); + indent(p); - visit_block_stmts(p, v.body); + visit_block_stmts(p, v.body); - unindent(p); + unindent(p); - if !p.config.indent_cases { - indent(p); - } - case Type_Switch_Stmt: - move_line(p, v.pos); + if !p.config.indent_cases { + indent(p); + } + case Type_Switch_Stmt: + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - if v.partial { - push_ident_token(p, "#partial", 1); - } + if v.partial { + push_ident_token(p, "#partial", 1); + } - push_generic_token(p, .Switch, 1); + push_generic_token(p, .Switch, 1); - visit_stmt(p, v.tag); - visit_stmt(p, v.body); - case Assign_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.tag); + visit_stmt(p, v.body); + case Assign_Stmt: + move_line(p, v.pos); - visit_exprs(p, v.lhs, true); + visit_exprs(p, v.lhs, true); - push_generic_token(p, v.op.kind, 1); + push_generic_token(p, v.op.kind, 1); - visit_exprs(p, v.rhs, true); + visit_exprs(p, v.rhs, true); - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Expr_Stmt: - move_line(p, v.pos); - visit_expr(p, v.expr); - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case For_Stmt: - //this should be simplified - move_line(p, v.pos); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Expr_Stmt: + move_line(p, v.pos); + visit_expr(p, v.expr); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case For_Stmt: + //this should be simplified + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .For, 1); + push_generic_token(p, .For, 1); - if v.init != nil { - p.skip_semicolon = true; - visit_stmt(p, v.init); - p.skip_semicolon = false; - push_generic_token(p, .Semicolon, 0); - } else if v.post != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.init != nil { + p.skip_semicolon = true; + visit_stmt(p, v.init); + p.skip_semicolon = false; + push_generic_token(p, .Semicolon, 0); + } else if v.post != nil { + push_generic_token(p, .Semicolon, 0); + } - if v.cond != nil { - visit_expr(p, v.cond); - } + if v.cond != nil { + visit_expr(p, v.cond); + } - if v.post != nil { - push_generic_token(p, .Semicolon, 0); - visit_stmt(p, v.post); - } else if v.post == nil && v.cond != nil && v.init != nil { - push_generic_token(p, .Semicolon, 0); - } + if v.post != nil { + push_generic_token(p, .Semicolon, 0); + visit_stmt(p, v.post); + } else if v.post == nil && v.cond != nil && v.init != nil { + push_generic_token(p, .Semicolon, 0); + } - visit_stmt(p, v.body); - case Inline_Range_Stmt: + visit_stmt(p, v.body); + case Inline_Range_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_ident_token(p, "#unroll", 0); + push_ident_token(p, "#unroll", 0); - push_generic_token(p, .For, 1); - visit_expr(p, v.val0); + push_generic_token(p, .For, 1); + visit_expr(p, v.val0); - if v.val1 != nil { - push_generic_token(p, .Comma, 0); - visit_expr(p, v.val1); - } + if v.val1 != nil { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.val1); + } - push_generic_token(p, .In, 1); + push_generic_token(p, .In, 1); - visit_expr(p, v.expr); - visit_stmt(p, v.body); - case Range_Stmt: + visit_expr(p, v.expr); + visit_stmt(p, v.body); + case Range_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - if v.label != nil { - visit_expr(p, v.label); - push_generic_token(p, .Colon, 0); - } + if v.label != nil { + visit_expr(p, v.label); + push_generic_token(p, .Colon, 0); + } - push_generic_token(p, .For, 1); + push_generic_token(p, .For, 1); - if len(v.vals) >= 1 { - visit_expr(p, v.vals[0]); - } + if len(v.vals) >= 1 { + visit_expr(p, v.vals[0]); + } - if len(v.vals) >= 2 { - push_generic_token(p, .Comma, 0); - visit_expr(p, v.vals[1]); - } + if len(v.vals) >= 2 { + push_generic_token(p, .Comma, 0); + visit_expr(p, v.vals[1]); + } - push_generic_token(p, .In, 1); + push_generic_token(p, .In, 1); - visit_expr(p, v.expr); + visit_expr(p, v.expr); - visit_stmt(p, v.body); - case Return_Stmt: - move_line(p, v.pos); + visit_stmt(p, v.body); + case Return_Stmt: + move_line(p, v.pos); - push_generic_token(p, .Return, 1); + push_generic_token(p, .Return, 1); - if v.results != nil { - visit_exprs(p, v.results, true); - } + if v.results != nil { + visit_exprs(p, v.results, true); + } - if block_stmt && p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case Defer_Stmt: - move_line(p, v.pos); - push_generic_token(p, .Defer, 0); + if block_stmt && p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case Defer_Stmt: + move_line(p, v.pos); + push_generic_token(p, .Defer, 0); - visit_stmt(p, v.stmt); + visit_stmt(p, v.stmt); - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case When_Stmt: - move_line(p, v.pos); - push_generic_token(p, .When, 1); - visit_expr(p, v.cond); + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case When_Stmt: + move_line(p, v.pos); + push_generic_token(p, .When, 1); + visit_expr(p, v.cond); - visit_stmt(p, v.body); + visit_stmt(p, v.body); - if v.else_stmt != nil { + if v.else_stmt != nil { - if p.config.brace_style == .Allman { - newline_position(p, 1); - } + if p.config.brace_style == .Allman { + newline_position(p, 1); + } - push_generic_token(p, .Else, 1); + push_generic_token(p, .Else, 1); - set_source_position(p, v.else_stmt.pos); + set_source_position(p, v.else_stmt.pos); - visit_stmt(p, v.else_stmt); - } + visit_stmt(p, v.else_stmt); + } - case Branch_Stmt: + case Branch_Stmt: - move_line(p, v.pos); + move_line(p, v.pos); - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 0); - if v.label != nil { - visit_expr(p, v.label); - } + if v.label != nil { + visit_expr(p, v.label); + } - if p.config.semicolons { - push_generic_token(p, .Semicolon, 0); - } - case: - panic(fmt.aprint(stmt.derived)); - } + if p.config.semicolons { + push_generic_token(p, .Semicolon, 0); + } + case: + panic(fmt.aprint(stmt.derived)); + } - set_source_position(p, stmt.end); + set_source_position(p, stmt.end); } @(private) visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { - using ast; + using ast; - if expr == nil { - return; - } + if expr == nil { + return; + } - set_source_position(p, expr.pos); + set_source_position(p, expr.pos); - switch v in expr.derived { - case Inline_Asm_Expr: - push_generic_token(p, v.tok.kind, 1, v.tok.text); + switch v in expr.derived { + case Inline_Asm_Expr: + push_generic_token(p, v.tok.kind, 1, v.tok.text); - push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.param_types, true, false); - push_generic_token(p, .Close_Paren, 0); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.param_types, true, false); + push_generic_token(p, .Close_Paren, 0); - push_generic_token(p, .Sub, 1); - push_generic_token(p, .Gt, 0); + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); - visit_expr(p, v.return_type); + visit_expr(p, v.return_type); - push_generic_token(p, .Open_Brace, 1); - visit_expr(p, v.asm_string); - push_generic_token(p, .Comma, 0); - visit_expr(p, v.constraints_string); - push_generic_token(p, .Close_Brace, 0); - case Undef: - push_generic_token(p, .Undef, 1); - case Auto_Cast: - push_generic_token(p, v.op.kind, 1); - visit_expr(p, v.expr); - case Ternary_Expr: - visit_expr(p, v.cond); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.x); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Ternary_If_Expr: - visit_expr(p, v.x); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.cond); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Ternary_When_Expr: - visit_expr(p, v.x); - push_generic_token(p, v.op1.kind, 1); - visit_expr(p, v.cond); - push_generic_token(p, v.op2.kind, 1); - visit_expr(p, v.y); - case Selector_Call_Expr: - visit_expr(p, v.call.expr); - push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.call.args, true); - push_generic_token(p, .Close_Paren, 0); - case Ellipsis: - push_generic_token(p, .Ellipsis, 1); - visit_expr(p, v.expr); - case Relative_Type: - visit_expr(p, v.tag); - visit_expr(p, v.type); - case Slice_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.low); - push_generic_token(p, v.interval.kind, 0); - if v.high != nil { - merge_next_token(p); - visit_expr(p, v.high); - } - push_generic_token(p, .Close_Bracket, 0); - case Ident: - push_ident_token(p, v.name, 1); - case Deref_Expr: - visit_expr(p, v.expr); - push_generic_token(p, v.op.kind, 0); - case Type_Cast: - push_generic_token(p, v.tok.kind, 1); - push_generic_token(p, .Open_Paren, 0); - visit_expr(p, v.type); - push_generic_token(p, .Close_Paren, 0); - merge_next_token(p); - visit_expr(p, v.expr); - case Basic_Directive: - push_generic_token(p, v.tok.kind, 1); - push_ident_token(p, v.name, 0); - case Distinct_Type: - push_generic_token(p, .Distinct, 1); - visit_expr(p, v.type); - case Dynamic_Array_Type: - visit_expr(p, v.tag); - push_generic_token(p, .Open_Bracket, 1); - push_generic_token(p, .Dynamic, 0); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.elem); - case Bit_Set_Type: - push_generic_token(p, .Bit_Set, 1); - push_generic_token(p, .Open_Bracket, 0); + push_generic_token(p, .Open_Brace, 1); + visit_expr(p, v.asm_string); + push_generic_token(p, .Comma, 0); + visit_expr(p, v.constraints_string); + push_generic_token(p, .Close_Brace, 0); + case Undef: + push_generic_token(p, .Undef, 1); + case Auto_Cast: + push_generic_token(p, v.op.kind, 1); + visit_expr(p, v.expr); + case Ternary_Expr: + visit_expr(p, v.cond); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.x); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Ternary_If_Expr: + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Ternary_When_Expr: + visit_expr(p, v.x); + push_generic_token(p, v.op1.kind, 1); + visit_expr(p, v.cond); + push_generic_token(p, v.op2.kind, 1); + visit_expr(p, v.y); + case Selector_Call_Expr: + visit_expr(p, v.call.expr); + push_generic_token(p, .Open_Paren, 1); + visit_exprs(p, v.call.args, true); + push_generic_token(p, .Close_Paren, 0); + case Ellipsis: + push_generic_token(p, .Ellipsis, 1); + visit_expr(p, v.expr); + case Relative_Type: + visit_expr(p, v.tag); + visit_expr(p, v.type); + case Slice_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.low); + push_generic_token(p, v.interval.kind, 0); + if v.high != nil { + merge_next_token(p); + visit_expr(p, v.high); + } + push_generic_token(p, .Close_Bracket, 0); + case Ident: + push_ident_token(p, v.name, 1); + case Deref_Expr: + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + case Type_Cast: + push_generic_token(p, v.tok.kind, 1); + push_generic_token(p, .Open_Paren, 0); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); + merge_next_token(p); + visit_expr(p, v.expr); + case Basic_Directive: + push_generic_token(p, v.tok.kind, 1); + push_ident_token(p, v.name, 0); + case Distinct_Type: + push_generic_token(p, .Distinct, 1); + visit_expr(p, v.type); + case Dynamic_Array_Type: + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + push_generic_token(p, .Dynamic, 0); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); + case Bit_Set_Type: + push_generic_token(p, .Bit_Set, 1); + push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.elem); + visit_expr(p, v.elem); - if v.underlying != nil { - push_generic_token(p, .Semicolon, 0); - visit_expr(p, v.underlying); - } + if v.underlying != nil { + push_generic_token(p, .Semicolon, 0); + visit_expr(p, v.underlying); + } - push_generic_token(p, .Close_Bracket, 0); - case Union_Type: - push_generic_token(p, .Union, 1); + push_generic_token(p, .Close_Bracket, 0); + case Union_Type: + push_generic_token(p, .Union, 1); - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } + if v.poly_params != nil { + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, true, false); + push_generic_token(p, .Close_Paren, 0); + } - if v.is_maybe { - push_ident_token(p, "#maybe", 1); - } + if v.is_maybe { + push_ident_token(p, "#maybe", 1); + } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.variants, true); - push_generic_token(p, .Close_Brace, 0); - } else { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.variants[0].pos); - visit_exprs(p, v.variants, true, true); - visit_end_brace(p, v.end); - } - case Enum_Type: - push_generic_token(p, .Enum, 1); + if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + visit_exprs(p, v.variants, true); + push_generic_token(p, .Close_Brace, 0); + } else { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.variants[0].pos); + visit_exprs(p, v.variants, true, true); + visit_end_brace(p, v.end); + } + case Enum_Type: + push_generic_token(p, .Enum, 1); - if v.base_type != nil { - visit_expr(p, v.base_type); - } + if v.base_type != nil { + visit_expr(p, v.base_type); + } - if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.fields, true); - push_generic_token(p, .Close_Brace, 0); - } else { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.fields[0].pos); - visit_exprs(p, v.fields, true, true); - visit_end_brace(p, v.end); - } + if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + visit_exprs(p, v.fields, true); + push_generic_token(p, .Close_Brace, 0); + } else { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.fields[0].pos); + visit_exprs(p, v.fields, true, true); + visit_end_brace(p, v.end); + } - set_source_position(p, v.end); - case Struct_Type: - push_generic_token(p, .Struct, 1); + set_source_position(p, v.end); + case Struct_Type: + push_generic_token(p, .Struct, 1); - hint_current_line(p,{.Struct}); + hint_current_line(p,{.Struct}); - if v.is_packed { - push_ident_token(p, "#packed", 1); - } + if v.is_packed { + push_ident_token(p, "#packed", 1); + } - if v.is_raw_union { - push_ident_token(p, "#raw_union", 1); - } + if v.is_raw_union { + push_ident_token(p, "#raw_union", 1); + } - if v.align != nil { - push_ident_token(p, "#align", 1); - visit_expr(p, v.align); - } + if v.align != nil { + push_ident_token(p, "#align", 1); + visit_expr(p, v.align); + } - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } + if v.poly_params != nil { + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, v.poly_params, true, false); + push_generic_token(p, .Close_Paren, 0); + } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { - push_generic_token(p, .Open_Brace, 1); - set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true); - push_generic_token(p, .Close_Brace, 0); - } else if v.fields != nil { - visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); - set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true, true, true); - visit_end_brace(p, v.end); - } + if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { + push_generic_token(p, .Open_Brace, 1); + set_source_position(p, v.fields.pos); + visit_field_list(p, v.fields, true); + push_generic_token(p, .Close_Brace, 0); + } else if v.fields != nil { + visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); + set_source_position(p, v.fields.pos); + visit_field_list(p, v.fields, true, true, true); + visit_end_brace(p, v.end); + } - set_source_position(p, v.end); - case Proc_Lit: + set_source_position(p, v.end); + case Proc_Lit: - if v.inlining == .Inline { - push_ident_token(p, "#force_inline", 0); - } + if v.inlining == .Inline { + push_ident_token(p, "#force_inline", 0); + } - visit_proc_type(p, v.type^); + visit_proc_type(p, v.type^); - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + if v.where_clauses != nil { + move_line(p, v.where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, v.where_clauses, true); + } - if v.body != nil { - set_source_position(p, v.body.pos); - visit_stmt(p, v.body, .Proc); - } else { - push_generic_token(p, .Undef, 1); - } - case Proc_Type: - visit_proc_type(p, v); - case Basic_Lit: - push_generic_token(p, v.tok.kind, 1, v.tok.text); - case Binary_Expr: - visit_binary_expr(p, v); - case Implicit_Selector_Expr: - push_generic_token(p, .Period, 1); - push_ident_token(p, v.field.name, 0); - case Call_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Paren, 0); - visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); - push_generic_token(p, .Close_Paren, 0); - case Typeid_Type: - push_generic_token(p, .Typeid, 1); + if v.body != nil { + set_source_position(p, v.body.pos); + visit_stmt(p, v.body, .Proc); + } else { + push_generic_token(p, .Undef, 1); + } + case Proc_Type: + visit_proc_type(p, v); + case Basic_Lit: + push_generic_token(p, v.tok.kind, 1, v.tok.text); + case Binary_Expr: + visit_binary_expr(p, v); + case Implicit_Selector_Expr: + push_generic_token(p, .Period, 1); + push_ident_token(p, v.field.name, 0); + case Call_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Paren, 0); + visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); + push_generic_token(p, .Close_Paren, 0); + case Typeid_Type: + push_generic_token(p, .Typeid, 1); - if v.specialization != nil { - push_generic_token(p, .Quo, 0); - visit_expr(p, v.specialization); - } - case Selector_Expr: - visit_expr(p, v.expr); - push_generic_token(p, v.op.kind, 0); - visit_expr(p, v.field); - case Paren_Expr: - push_generic_token(p, .Open_Paren, 1); - visit_expr(p, v.expr); - push_generic_token(p, .Close_Paren, 0); - case Index_Expr: - visit_expr(p, v.expr); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.index); - push_generic_token(p, .Close_Bracket, 0); - case Proc_Group: + if v.specialization != nil { + push_generic_token(p, .Quo, 0); + visit_expr(p, v.specialization); + } + case Selector_Expr: + visit_expr(p, v.expr); + push_generic_token(p, v.op.kind, 0); + visit_expr(p, v.field); + case Paren_Expr: + push_generic_token(p, .Open_Paren, 1); + visit_expr(p, v.expr); + push_generic_token(p, .Close_Paren, 0); + case Index_Expr: + visit_expr(p, v.expr); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.index); + push_generic_token(p, .Close_Bracket, 0); + case Proc_Group: - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 0); - if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { - visit_begin_brace(p, v.pos, .Generic); - newline_position(p, 1); - set_source_position(p, v.args[0].pos); - visit_exprs(p, v.args, true, true); - visit_end_brace(p, v.end); - } else { - push_generic_token(p, .Open_Brace, 0); - visit_exprs(p, v.args, true); - push_generic_token(p, .Close_Brace, 0); - } + if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { + visit_begin_brace(p, v.pos, .Generic); + newline_position(p, 1); + set_source_position(p, v.args[0].pos); + visit_exprs(p, v.args, true, true); + visit_end_brace(p, v.end); + } else { + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.args, true); + push_generic_token(p, .Close_Brace, 0); + } - case Comp_Lit: + case Comp_Lit: - if v.type != nil { - visit_expr(p, v.type); - } + if v.type != nil { + visit_expr(p, v.type); + } - if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { - visit_begin_brace(p, v.pos, .Comp_Lit); - newline_position(p, 1); - set_source_position(p, v.elems[0].pos); - visit_exprs(p, v.elems, true, true); - visit_end_brace(p, v.end); - } else { - push_generic_token(p, .Open_Brace, 0); - visit_exprs(p, v.elems, true); - push_generic_token(p, .Close_Brace, 0); - } + if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { + visit_begin_brace(p, v.pos, .Comp_Lit); + newline_position(p, 1); + set_source_position(p, v.elems[0].pos); + visit_exprs(p, v.elems, true, true); + visit_end_brace(p, v.end); + } else { + push_generic_token(p, .Open_Brace, 0); + visit_exprs(p, v.elems, true); + push_generic_token(p, .Close_Brace, 0); + } - case Unary_Expr: - push_generic_token(p, v.op.kind, 1); - merge_next_token(p); - visit_expr(p, v.expr); - case Field_Value: - visit_expr(p, v.field); - push_generic_token(p, .Eq, 1); - visit_expr(p, v.value); - case Type_Assertion: - visit_expr(p, v.expr); + case Unary_Expr: + push_generic_token(p, v.op.kind, 1); + merge_next_token(p); + visit_expr(p, v.expr); + case Field_Value: + visit_expr(p, v.field); + push_generic_token(p, .Eq, 1); + visit_expr(p, v.value); + case Type_Assertion: + visit_expr(p, v.expr); - if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { - push_generic_token(p, .Period, 0); - visit_expr(p, v.type); - } else { - push_generic_token(p, .Period, 0); - push_generic_token(p, .Open_Paren, 0); - visit_expr(p, v.type); - push_generic_token(p, .Close_Paren, 0); - } + if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" { + push_generic_token(p, .Period, 0); + visit_expr(p, v.type); + } else { + push_generic_token(p, .Period, 0); + push_generic_token(p, .Open_Paren, 0); + visit_expr(p, v.type); + push_generic_token(p, .Close_Paren, 0); + } - case Pointer_Type: - push_generic_token(p, .Pointer, 1); - merge_next_token(p); - visit_expr(p, v.elem); - case Implicit: - push_generic_token(p, v.tok.kind, 1); - case Poly_Type: - push_generic_token(p, .Dollar, 1); - merge_next_token(p); - visit_expr(p, v.type); + case Pointer_Type: + push_generic_token(p, .Pointer, 1); + merge_next_token(p); + visit_expr(p, v.elem); + case Implicit: + push_generic_token(p, v.tok.kind, 1); + case Poly_Type: + push_generic_token(p, .Dollar, 1); + merge_next_token(p); + visit_expr(p, v.type); - if v.specialization != nil { - push_generic_token(p, .Quo, 0); - merge_next_token(p); - visit_expr(p, v.specialization); - } - case Array_Type: - visit_expr(p, v.tag); - push_generic_token(p, .Open_Bracket, 1); - visit_expr(p, v.len); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.elem); - case Map_Type: - push_generic_token(p, .Map, 1); - push_generic_token(p, .Open_Bracket, 0); - visit_expr(p, v.key); - push_generic_token(p, .Close_Bracket, 0); - visit_expr(p, v.value); - case Helper_Type: - visit_expr(p, v.type); - case: - panic(fmt.aprint(expr.derived)); - } + if v.specialization != nil { + push_generic_token(p, .Quo, 0); + merge_next_token(p); + visit_expr(p, v.specialization); + } + case Array_Type: + visit_expr(p, v.tag); + push_generic_token(p, .Open_Bracket, 1); + visit_expr(p, v.len); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.elem); + case Map_Type: + push_generic_token(p, .Map, 1); + push_generic_token(p, .Open_Bracket, 0); + visit_expr(p, v.key); + push_generic_token(p, .Close_Bracket, 0); + visit_expr(p, v.value); + case Helper_Type: + visit_expr(p, v.type); + case: + panic(fmt.aprint(expr.derived)); + } } visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0) { - set_source_position(p, begin); + set_source_position(p, begin); - newline_braced := p.config.brace_style == .Allman; - newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; - newline_braced &= p.config.brace_style != ._1TBS; + newline_braced := p.config.brace_style == .Allman; + newline_braced |= p.config.brace_style == .K_And_R && type == .Proc; + newline_braced &= p.config.brace_style != ._1TBS; - format_token := Format_Token { - kind = .Open_Brace, - parameter_count = count, - text = "{", - }; + format_token := Format_Token { + kind = .Open_Brace, + parameter_count = count, + text = "{", + }; - if newline_braced { - newline_position(p, 1); - push_format_token(p, format_token); - indent(p); - } else { - format_token.spaces_before = 1; - push_format_token(p, format_token); - indent(p); - } + if newline_braced { + newline_position(p, 1); + push_format_token(p, format_token); + indent(p); + } else { + format_token.spaces_before = 1; + push_format_token(p, format_token); + indent(p); + } } visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { - set_source_position(p, end); - newline_position(p, 1); - push_generic_token(p, .Close_Brace, 0); - unindent(p); - p.current_line.depth = p.depth; + set_source_position(p, end); + newline_position(p, 1); + push_generic_token(p, .Close_Brace, 0); + unindent(p); + p.current_line.depth = p.depth; } visit_block_stmts :: proc(p: ^Printer, stmts: [] ^ast.Stmt, split := false) { - for stmt, i in stmts { - visit_stmt(p, stmt, .Generic, false, true); + for stmt, i in stmts { + visit_stmt(p, stmt, .Generic, false, true); - if split && i != len(stmts) - 1 && stmt.pos.line == stmts[i + 1].pos.line { - newline_position(p, 1); - } - } + if split && i != len(stmts) - 1 && stmt.pos.line == stmts[i + 1].pos.line { + newline_position(p, 1); + } + } } visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false, enforce_newline := false) { - if list.list == nil { - return; - } + if list.list == nil { + return; + } - for field, i in list.list { + for field, i in list.list { - if !move_line_limit(p, field.pos, 1) && enforce_newline { - newline_position(p, 1); - } + if !move_line_limit(p, field.pos, 1) && enforce_newline { + newline_position(p, 1); + } - if .Using in field.flags { - push_generic_token(p, .Using, 0); - } + if .Using in field.flags { + push_generic_token(p, .Using, 0); + } - visit_exprs(p, field.names, true); + visit_exprs(p, field.names, true); - if field.type != nil { - if len(field.names) != 0 { - push_generic_token(p, .Colon, 0); - } - visit_expr(p, field.type); - } else { - push_generic_token(p, .Colon, 1); - push_generic_token(p, .Eq, 0); - visit_expr(p, field.default_value); - } + if field.type != nil { + if len(field.names) != 0 { + push_generic_token(p, .Colon, 0); + } + visit_expr(p, field.type); + } else { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Eq, 0); + visit_expr(p, field.default_value); + } - if field.tag.text != "" { - push_generic_token(p, field.tag.kind, 1, field.tag.text); - } + if field.tag.text != "" { + push_generic_token(p, field.tag.kind, 1, field.tag.text); + } - if (i != len(list.list) - 1 || trailing) && add_comma { - push_generic_token(p, .Comma, 0); - } - } + if (i != len(list.list) - 1 || trailing) && add_comma { + push_generic_token(p, .Comma, 0); + } + } } visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { - push_generic_token(p, .Proc, 1); + push_generic_token(p, .Proc, 1); - explicit_calling := false; + explicit_calling := false; - switch proc_type.calling_convention { - case .Odin: - case .Contextless: - push_string_token(p, "\"contextless\"", 1); - explicit_calling = true; - case .C_Decl: - push_string_token(p, "\"c\"", 1); - explicit_calling = true; - case .Std_Call: - push_string_token(p, "\"std\"", 1); - explicit_calling = true; - case .Fast_Call: - push_string_token(p, "\"fast\"", 1); - explicit_calling = true; - case .None: - //nothing i guess - case .Invalid: - //nothing i guess - case .Foreign_Block_Default: - } + switch proc_type.calling_convention { + case .Odin: + case .Contextless: + push_string_token(p, "\"contextless\"", 1); + explicit_calling = true; + case .C_Decl: + push_string_token(p, "\"c\"", 1); + explicit_calling = true; + case .Std_Call: + push_string_token(p, "\"std\"", 1); + explicit_calling = true; + case .Fast_Call: + push_string_token(p, "\"fast\"", 1); + explicit_calling = true; + case .None: + //nothing i guess + case .Invalid: + //nothing i guess + case .Foreign_Block_Default: + } - if explicit_calling { - push_generic_token(p, .Open_Paren, 1); - } else { - push_generic_token(p, .Open_Paren, 0); - } + if explicit_calling { + push_generic_token(p, .Open_Paren, 1); + } else { + push_generic_token(p, .Open_Paren, 0); + } - visit_signature_list(p, proc_type.params, false); + visit_signature_list(p, proc_type.params, false); - push_generic_token(p, .Close_Paren, 0); + push_generic_token(p, .Close_Paren, 0); - if proc_type.results != nil { - push_generic_token(p, .Sub, 1); - push_generic_token(p, .Gt, 0); + if proc_type.results != nil { + push_generic_token(p, .Sub, 1); + push_generic_token(p, .Gt, 0); - use_parens := false; - use_named := false; + use_parens := false; + use_named := false; - if len(proc_type.results.list) > 1 { - use_parens = true; - } else if len(proc_type.results.list) == 1 { + if len(proc_type.results.list) > 1 { + use_parens = true; + } else if len(proc_type.results.list) == 1 { - for name in proc_type.results.list[0].names { - if ident, ok := name.derived.(ast.Ident); ok { - if ident.name != "_" { - use_parens = true; - } - } - } - } + for name in proc_type.results.list[0].names { + if ident, ok := name.derived.(ast.Ident); ok { + if ident.name != "_" { + use_parens = true; + } + } + } + } - if use_parens { - push_generic_token(p, .Open_Paren, 1); - visit_signature_list(p, proc_type.results); - push_generic_token(p, .Close_Paren, 0); - } else { - visit_signature_list(p, proc_type.results); - } - } + if use_parens { + push_generic_token(p, .Open_Paren, 1); + visit_signature_list(p, proc_type.results); + push_generic_token(p, .Close_Paren, 0); + } else { + visit_signature_list(p, proc_type.results); + } + } } visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { - move_line(p, binary.left.pos); + move_line(p, binary.left.pos); - if v, ok := binary.left.derived.(ast.Binary_Expr); ok { - visit_binary_expr(p, v); - } else { - visit_expr(p, binary.left); - } + if v, ok := binary.left.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.left); + } - if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { - push_generic_token(p, binary.op.kind, 0); - } else { - push_generic_token(p, binary.op.kind, 1); - } + if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { + push_generic_token(p, binary.op.kind, 0); + } else { + push_generic_token(p, binary.op.kind, 1); + } - move_line(p, binary.right.pos); + move_line(p, binary.right.pos); - if v, ok := binary.right.derived.(ast.Binary_Expr); ok { - visit_binary_expr(p, v); - } else { - visit_expr(p, binary.right); - } + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { + visit_binary_expr(p, v); + } else { + visit_expr(p, binary.right); + } } visit_call_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, ellipsis := false) { - if len(list) == 0 { - return; - } + if len(list) == 0 { + return; + } - //all the expression are on the line - if list[0].pos.line == list[len(list) - 1].pos.line { - for expr, i in list { + //all the expression are on the line + if list[0].pos.line == list[len(list) - 1].pos.line { + for expr, i in list { - if i == len(list) - 1 && ellipsis { - push_generic_token(p, .Ellipsis, 0); - } + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } - visit_expr(p, expr); + visit_expr(p, expr); - if i != len(list) - 1 { - push_generic_token(p, .Comma, 0); - } - } - } else { - for expr, i in list { + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } else { + for expr, i in list { - //we have to newline the expressions to respect the source - move_line_limit(p, expr.pos, 1); + //we have to newline the expressions to respect the source + move_line_limit(p, expr.pos, 1); - if i == len(list) - 1 && ellipsis { - push_generic_token(p, .Ellipsis, 0); - } + if i == len(list) - 1 && ellipsis { + push_generic_token(p, .Ellipsis, 0); + } - visit_expr(p, expr); + visit_expr(p, expr); - if i != len(list) - 1 { - push_generic_token(p, .Comma, 0); - } - } - } + if i != len(list) - 1 { + push_generic_token(p, .Comma, 0); + } + } + } } visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := true) { - if list.list == nil { - return; - } + if list.list == nil { + return; + } - for field, i in list.list { + for field, i in list.list { - move_line_limit(p, field.pos, 1); + move_line_limit(p, field.pos, 1); - if .Using in field.flags { - push_generic_token(p, .Using, 0); - } + if .Using in field.flags { + push_generic_token(p, .Using, 0); + } - named := false; + named := false; - for name in field.names { - if ident, ok := name.derived.(ast.Ident); ok { - //for some reason the parser uses _ to mean empty - if ident.name != "_" || !remove_blank { - named = true; - } - } else { - //alternative is poly names - named = true; - } - } + for name in field.names { + if ident, ok := name.derived.(ast.Ident); ok { + //for some reason the parser uses _ to mean empty + if ident.name != "_" || !remove_blank { + named = true; + } + } else { + //alternative is poly names + named = true; + } + } - if named { - visit_exprs(p, field.names, true); + if named { + visit_exprs(p, field.names, true); - if len(field.names) != 0 && field.type != nil { - push_generic_token(p, .Colon, 0); - } - } + if len(field.names) != 0 && field.type != nil { + push_generic_token(p, .Colon, 0); + } + } - if field.type != nil && field.default_value != nil { - visit_expr(p, field.type); - push_generic_token(p, .Eq, 1); - visit_expr(p, field.default_value); - } else if field.type != nil { - visit_expr(p, field.type); - } else { - push_generic_token(p, .Colon, 1); - push_generic_token(p, .Eq, 0); - visit_expr(p, field.default_value); - } + if field.type != nil && field.default_value != nil { + visit_expr(p, field.type); + push_generic_token(p, .Eq, 1); + visit_expr(p, field.default_value); + } else if field.type != nil { + visit_expr(p, field.type); + } else { + push_generic_token(p, .Colon, 1); + push_generic_token(p, .Eq, 0); + visit_expr(p, field.default_value); + } - if i != len(list.list) - 1 { - push_generic_token(p, .Comma, 0); - } - } + if i != len(list.list) - 1 { + push_generic_token(p, .Comma, 0); + } + } } \ No newline at end of file From f17fc05ff2431962572bd0fc2ebdc1207251c261 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 16 Apr 2021 00:44:16 +0200 Subject: [PATCH 023/137] format call and binary expression --- core/odin/printer/printer.odin | 208 ++++++++++++++++++++++++++++----- core/odin/printer/visit.odin | 53 ++++++--- 2 files changed, 214 insertions(+), 47 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 3db48662c..98a7e9ade 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,12 +8,12 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -Line_Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct} +Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum} -Line_Type :: bit_set[Line_Type_Enum]; +Line_Type :: bit_set[Type_Enum]; Line :: struct { - format_tokens: [dynamic] Format_Token, + format_tokens: [dynamic]Format_Token, finalized: bool, used: bool, depth: int, @@ -23,6 +23,7 @@ Line :: struct { Format_Token :: struct { kind: tokenizer.Token_Kind, text: string, + type: Type_Enum, spaces_before: int, parameter_count: int, } @@ -31,13 +32,13 @@ Printer :: struct { string_builder: strings.Builder, config: Config, depth: int, //the identation depth - comments: [dynamic] ^ast.Comment_Group, + 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 + 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, @@ -119,7 +120,7 @@ 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); + p.lines = make([dynamic]Line, 0, (file.decls[len(file.decls) - 1].end.line - file.decls[0].pos.line) * 2, context.temp_allocator); } set_line(p, 0); @@ -188,15 +189,182 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { } fix_lines :: proc(p: ^Printer) { - align_var_decls(p); - align_blocks(p); + align_var_decls_and_assignments(p); + format_generic(p); align_comments(p); //align them last since they rely on the other alignments } -align_var_decls :: proc(p: ^Printer) { +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; + } + + } + } -align_switch_smt :: proc(p: ^Printer, index: int) { +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, index: int) { + + paren_found := false; + paren_token: Format_Token; + paren_line: int; + largest := 0; + + found_paren: 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 == .Open_Paren && format_token.type == .Call { + paren_token = format_token; + paren_line = line_index + index; + paren_found = true; + break found_paren; + } else if format_token.kind == .Open_Paren { + return; + } + } + } + + if !paren_found { + return; + } + + paren_count := 1; + done := false; + + for line, line_index in p.lines[paren_line+1:] { + + if len(line.format_tokens) == 0 { + continue; + } + + for format_token, i in line.format_tokens { + + 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; + } + + } + + line.format_tokens[0].spaces_before += largest; + + if done { + return; + } + + + } + + + +} + +format_generic :: proc(p: ^Printer) { + + for line, line_index in p.lines { + + if len(line.format_tokens) <= 0 { + continue; + } + + if .Switch_Stmt in line.types && p.config.align_switch { + align_switch_stmt(p, line_index); + } + + if .Struct in line.types && p.config.align_structs { + 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); + } + + if .Call in line.types { + format_call(p, line_index); + } + } +} + +align_var_decls_and_assignments :: proc(p: ^Printer) { + + + + +} + +align_switch_stmt :: proc(p: ^Printer, index: int) { switch_found := false; brace_token: Format_Token; @@ -375,24 +543,6 @@ align_struct :: proc(p: ^Printer, index: int) { } } -align_blocks :: proc(p: ^Printer) { - - for line, line_index in p.lines { - - if len(line.format_tokens) <= 0 { - continue; - } - - if .Switch_Stmt in line.types && p.config.align_switch { - align_switch_smt(p, line_index); - } - - if .Struct in line.types && p.config.align_structs { - align_struct(p, line_index); - } - } -} - align_comments :: proc(p: ^Printer) { Comment_Align_Info :: struct { @@ -402,7 +552,7 @@ align_comments :: proc(p: ^Printer) { depth: int, }; - comment_infos := make([dynamic] Comment_Align_Info, 0, context.temp_allocator); + comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator); current_info: Comment_Align_Info; diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index ae3097f03..d8ec98e17 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -11,19 +11,19 @@ import "core:sort" //right the attribute order is not linearly parsed(bug?) @(private) -sort_attribute :: proc(s: ^[dynamic] ^ast.Attribute) -> sort.Interface { +sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { return sort.Interface { collection = rawptr(s), len = proc(it: sort.Interface) -> int { - s := (^[dynamic] ^ast.Attribute)(it.collection); + s := (^[dynamic]^ast.Attribute)(it.collection); return len(s^); }, less = proc(it: sort.Interface, i, j: int) -> bool { - s := (^[dynamic] ^ast.Attribute)(it.collection); + s := (^[dynamic]^ast.Attribute)(it.collection); return s[i].pos.offset < s[j].pos.offset; }, swap = proc(it: sort.Interface, i, j: int) { - s := (^[dynamic] ^ast.Attribute)(it.collection); + s := (^[dynamic]^ast.Attribute)(it.collection); s[i], s[j] = s[j], s[i]; }, }; @@ -72,7 +72,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { append(&p.current_line.format_tokens, format_token); p.last_token = &p.current_line.format_tokens[len(p.current_line.format_tokens) - 1]; - hint_current_line(p,{.Line_Comment}); + hint_current_line(p, {.Line_Comment}); return 0; } else { @@ -82,7 +82,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { c_len := len(comment.text); trim_space := true; - multilines: [dynamic] string; + multilines: [dynamic]string; for i := 0; i < len(comment.text); i += 1 { @@ -185,7 +185,7 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { } if prev_comment != nil { - newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line)); + newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line - prev_comment_lines)); } } @@ -303,7 +303,7 @@ set_line :: proc(p: ^Printer, line: int) -> ^Line { if line >= len(p.lines) { for i := len(p.lines); i <= line; i += 1 { new_line: Line; - new_line.format_tokens = make([dynamic] Format_Token, 0, 50, p.allocator); + new_line.format_tokens = make([dynamic]Format_Token, 0, 50, p.allocator); append(&p.lines, new_line); } unwrapped_line = &p.lines[line]; @@ -427,18 +427,20 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { visit_exprs(p, v.names, true); if v.type != nil { - if !v.is_mutable && v.type != nil { + if !v.is_mutable { push_generic_token(p, .Colon, 0); } else { + hint_current_line(p, {.Value_Decl}); push_generic_token(p, .Colon, 0); } visit_expr(p, v.type); } else { - if !v.is_mutable && v.type == nil { + if !v.is_mutable { push_generic_token(p, .Colon, 1); push_generic_token(p, .Colon, 0); } else { + hint_current_line(p, {.Value_Decl}); push_generic_token(p, .Colon, 1); } } @@ -472,7 +474,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { } @(private) -visit_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, add_comma := false, trailing := false) { +visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false) { if len(list) == 0 { return; @@ -492,7 +494,7 @@ visit_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, add_comma := false, trailin } @(private) -visit_attributes :: proc(p: ^Printer, attributes: [dynamic] ^ast.Attribute) { +visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { if len(attributes) == 0 { return; @@ -637,7 +639,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Switch, 1); - hint_current_line(p,{.Switch_Stmt}); + hint_current_line(p, {.Switch_Stmt}); if v.init != nil { p.skip_semicolon = true; @@ -694,6 +696,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener case Assign_Stmt: move_line(p, v.pos); + hint_current_line(p, {.Assign}); + visit_exprs(p, v.lhs, true); push_generic_token(p, v.op.kind, 1); @@ -730,11 +734,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } if v.cond != nil { + move_line(p, v.cond.pos); visit_expr(p, v.cond); } if v.post != nil { push_generic_token(p, .Semicolon, 0); + move_line(p, v.post.pos); visit_stmt(p, v.post); } else if v.post == nil && v.cond != nil && v.init != nil { push_generic_token(p, .Semicolon, 0); @@ -946,6 +952,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Open_Bracket, 1); push_generic_token(p, .Dynamic, 0); push_generic_token(p, .Close_Bracket, 0); + merge_next_token(p); visit_expr(p, v.elem); case Bit_Set_Type: push_generic_token(p, .Bit_Set, 1); @@ -1012,7 +1019,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Struct_Type: push_generic_token(p, .Struct, 1); - hint_current_line(p,{.Struct}); + hint_current_line(p, {.Struct}); if v.is_packed { push_ident_token(p, "#packed", 1); @@ -1083,7 +1090,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_ident_token(p, v.field.name, 0); case Call_Expr: visit_expr(p, v.expr); - push_generic_token(p, .Open_Paren, 0); + + push_format_token(p, Format_Token { + kind = .Open_Paren, + type = .Call, + text = "(", + }); + + hint_current_line(p, {.Call}); + visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis); push_generic_token(p, .Close_Paren, 0); case Typeid_Type: @@ -1135,7 +1150,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_exprs(p, v.elems, true, true); visit_end_brace(p, v.end); } else { - push_generic_token(p, .Open_Brace, 0); + push_generic_token(p, .Open_Brace, 1); visit_exprs(p, v.elems, true); push_generic_token(p, .Close_Brace, 0); } @@ -1182,12 +1197,14 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Open_Bracket, 1); visit_expr(p, v.len); push_generic_token(p, .Close_Bracket, 0); + merge_next_token(p); visit_expr(p, v.elem); case Map_Type: push_generic_token(p, .Map, 1); push_generic_token(p, .Open_Bracket, 0); visit_expr(p, v.key); push_generic_token(p, .Close_Bracket, 0); + merge_next_token(p); visit_expr(p, v.value); case Helper_Type: visit_expr(p, v.type); @@ -1229,7 +1246,7 @@ visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { p.current_line.depth = p.depth; } -visit_block_stmts :: proc(p: ^Printer, stmts: [] ^ast.Stmt, split := false) { +visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) { for stmt, i in stmts { visit_stmt(p, stmt, .Generic, false, true); @@ -1370,7 +1387,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { } } -visit_call_exprs :: proc(p: ^Printer, list: [] ^ast.Expr, ellipsis := false) { +visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { if len(list) == 0 { return; From 5a8c7b4f90a3f48ed2bbd574cac0b38862e4742d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 16 Apr 2021 02:15:29 +0200 Subject: [PATCH 024/137] support multiline for and if --- core/odin/printer/printer.odin | 131 ++++++++++++++++++++++++++++++++- core/odin/printer/visit.odin | 9 +++ 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 98a7e9ade..700d9ef63 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,7 +8,7 @@ import "core:fmt" import "core:unicode/utf8" import "core:mem" -Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum} +Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For} Line_Type :: bit_set[Type_Enum]; @@ -320,13 +320,130 @@ format_call :: proc(p: ^Printer, index: int) { return; } + } +} + +format_for :: proc(p: ^Printer, index: int) { + + for_found := false; + for_token: Format_Token; + for_line: int; + largest := 0; + + found_for: 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 == .For { + for_token = format_token; + for_line = line_index + index; + for_found = true; + break found_for; + } + } + } + + if !for_found { + return; + } + + brace_count := 0; + done := false; + + for line, line_index in p.lines[for_line:] { + + if len(line.format_tokens) == 0 { + continue; + } + + for format_token, i in line.format_tokens { + + 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_if :: proc(p: ^Printer, index: int) { + + if_found := false; + if_token: Format_Token; + if_line: int; + largest := 0; + + found_if: 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 == .If { + if_token = format_token; + if_line = line_index + index; + if_found = true; + break found_if; + } + } + } + + if !if_found { + return; + } + + brace_count := 0; + done := false; + + for line, line_index in p.lines[if_line:] { + + if len(line.format_tokens) == 0 { + continue; + } + + for format_token, i in line.format_tokens { + + 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 { + break; + } } - - } + format_generic :: proc(p: ^Printer) { for line, line_index in p.lines { @@ -354,6 +471,14 @@ format_generic :: proc(p: ^Printer) { if .Call in line.types { format_call(p, line_index); } + + if .If in line.types { + format_if(p, line_index); + } + + if .For in line.types { + format_for(p, line_index); + } } } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index d8ec98e17..36c8c42d9 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -587,6 +587,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .If, 1); + hint_current_line(p, {.If}); + if v.init != nil { p.skip_semicolon = true; visit_stmt(p, v.init); @@ -724,6 +726,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .For, 1); + hint_current_line(p, {.For}); + if v.init != nil { p.skip_semicolon = true; visit_stmt(p, v.init); @@ -759,6 +763,9 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_ident_token(p, "#unroll", 0); push_generic_token(p, .For, 1); + + hint_current_line(p, {.For}); + visit_expr(p, v.val0); if v.val1 != nil { @@ -781,6 +788,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .For, 1); + hint_current_line(p, {.For}); + if len(v.vals) >= 1 { visit_expr(p, v.vals[0]); } From a72180233709e47197bc6c737f18e9464e661d5d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 18 Apr 2021 21:07:58 +0200 Subject: [PATCH 025/137] fixing calls in calls --- core/odin/printer/printer.odin | 170 ++++++++++++--------------------- 1 file changed, 59 insertions(+), 111 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 700d9ef63..ad17a63c4 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -229,7 +229,7 @@ format_value_decl :: proc(p: ^Printer, index: int) { } if align_next { - line.format_tokens[0].spaces_before += largest + 1; + line.format_tokens[0].spaces_before = largest + 1; align_next = false; } @@ -264,37 +264,41 @@ format_assignment :: proc(p: ^Printer, index: int) { } -format_call :: 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, line_index in p.lines[index:] { - for format_token in line.format_tokens { + 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 + index; + paren_line = line_index + i; paren_found = true; + paren_token_index = j; break found_paren; - } else if format_token.kind == .Open_Paren { - return; - } + } } } if !paren_found { - return; + panic("Should not be possible");; } paren_count := 1; done := false; - for line, line_index in p.lines[paren_line+1:] { + for line, line_index in p.lines[paren_line:] { if len(line.format_tokens) == 0 { continue; @@ -302,6 +306,10 @@ format_call :: proc(p: ^Printer, index: int) { for format_token, i in line.format_tokens { + 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 { @@ -314,44 +322,45 @@ format_call :: proc(p: ^Printer, index: int) { } - line.format_tokens[0].spaces_before += largest; + if line_index != 0 { + line.format_tokens[0].spaces_before = largest; + } if done { return; } - } } -format_for :: proc(p: ^Printer, index: int) { +format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword: tokenizer.Token_Kind) { - for_found := false; - for_token: Format_Token; - for_line: int; + keyword_found := false; + keyword_token: Format_Token; + keyword_line: int; largest := 0; - found_for: 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 == .For { - for_token = format_token; - for_line = line_index + index; - for_found = true; - break found_for; - } - } - } - - if !for_found { - return; - } - brace_count := 0; done := false; - for line, line_index in p.lines[for_line:] { + 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; @@ -372,7 +381,7 @@ format_for :: proc(p: ^Printer, index: int) { } if line_index != 0 { - line.format_tokens[0].spaces_before += largest + 1; + line.format_tokens[0].spaces_before = largest + 1; } if done { @@ -383,67 +392,6 @@ format_for :: proc(p: ^Printer, index: int) { } -format_if :: proc(p: ^Printer, index: int) { - - if_found := false; - if_token: Format_Token; - if_line: int; - largest := 0; - - found_if: 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 == .If { - if_token = format_token; - if_line = line_index + index; - if_found = true; - break found_if; - } - } - } - - if !if_found { - return; - } - - brace_count := 0; - done := false; - - for line, line_index in p.lines[if_line:] { - - if len(line.format_tokens) == 0 { - continue; - } - - for format_token, i in line.format_tokens { - - 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 { - break; - } - - } - -} - - format_generic :: proc(p: ^Printer) { for line, line_index in p.lines { @@ -452,6 +400,17 @@ format_generic :: proc(p: ^Printer) { continue; } + for format_token, token_index in line.format_tokens { + + if format_token.kind == .For || format_token.kind == .If + || format_token.kind == .When || format_token.kind == .Switch { + format_keyword_to_brace(p, line_index, token_index, format_token.kind); + } else 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); } @@ -468,17 +427,6 @@ format_generic :: proc(p: ^Printer) { format_assignment(p, line_index); } - if .Call in line.types { - format_call(p, line_index); - } - - if .If in line.types { - format_if(p, line_index); - } - - if .For in line.types { - format_for(p, line_index); - } } } @@ -568,7 +516,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { //this will only happen if the case is one lined if case_found && colon_found { - line.format_tokens[i].spaces_before += (largest - length); + line.format_tokens[i].spaces_before = (largest - length); break; } @@ -736,9 +684,9 @@ align_comments :: proc(p: ^Printer) { if format_token.kind == .Comment { if len(l.format_tokens) == 1 { - l.format_tokens[i].spaces_before += info.length + 1; + l.format_tokens[i].spaces_before = info.length + 1; } else { - l.format_tokens[i].spaces_before += info.length - length; + l.format_tokens[i].spaces_before = info.length - length + 1; } } From 11bd518f36fecf24d69443b257e369bd986645b7 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 18 Apr 2021 21:52:00 +0200 Subject: [PATCH 026/137] fix bugs --- core/odin/printer/printer.odin | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index ad17a63c4..bd77e9b7c 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -306,6 +306,10 @@ format_call :: proc(p: ^Printer, line_index: int, format_index: int) { for format_token, i in line.format_tokens { + if format_token.kind == .Comment { + continue; + } + if line_index == 0 && i <= paren_token_index { continue; } @@ -368,6 +372,14 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, for format_token, i in line.format_tokens { + if format_token.kind == .Comment { + continue; + } + + if line_index == 0 && i <= format_index { + continue; + } + if format_token.kind == .Open_Brace { brace_count += 1; } else if format_token.kind == .Close_Brace { From 2cbb3443d3051bea25dff8b1028c5ec6f71f3c0f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 18 Apr 2021 21:53:52 +0200 Subject: [PATCH 027/137] ran odinfmt --- core/odin/printer/printer.odin | 38 +++++++++++----------------------- core/odin/printer/visit.odin | 12 +++++------ 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index bd77e9b7c..928c0e07d 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -211,7 +211,7 @@ format_value_decl :: proc(p: ^Printer, index: int) { eq_line = line_index + index; eq_found = true; break found_eq; - } + } } } @@ -233,7 +233,7 @@ format_value_decl :: proc(p: ^Printer, index: int) { align_next = false; } - kind := find_last_token(line.format_tokens).kind; + 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; @@ -242,26 +242,22 @@ format_value_decl :: proc(p: ^Printer, index: int) { 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 { + 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) { @@ -287,12 +283,12 @@ format_call :: proc(p: ^Printer, line_index: int, format_index: int) { paren_found = true; paren_token_index = j; break found_paren; - } + } } } if !paren_found { - panic("Should not be possible");; + panic("Should not be possible"); } paren_count := 1; @@ -305,7 +301,7 @@ format_call :: proc(p: ^Printer, line_index: int, format_index: int) { } for format_token, i in line.format_tokens { - + if format_token.kind == .Comment { continue; } @@ -323,7 +319,6 @@ format_call :: proc(p: ^Printer, line_index: int, format_index: int) { if paren_count == 0 { done = true; } - } if line_index != 0 { @@ -356,7 +351,7 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword_line = line_index + i; keyword_found = true; break found_keyword; - } + } } } @@ -371,7 +366,7 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, } for format_token, i in line.format_tokens { - + if format_token.kind == .Comment { continue; } @@ -389,7 +384,6 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, if brace_count == 1 { done = true; } - } if line_index != 0 { @@ -399,9 +393,7 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, if done { return; } - } - } format_generic :: proc(p: ^Printer) { @@ -414,13 +406,12 @@ format_generic :: proc(p: ^Printer) { for format_token, token_index in line.format_tokens { - if format_token.kind == .For || format_token.kind == .If - || format_token.kind == .When || format_token.kind == .Switch { + if format_token.kind == .For || format_token.kind == .If || + format_token.kind == .When || format_token.kind == .Switch { format_keyword_to_brace(p, line_index, token_index, format_token.kind); } else if format_token.type == .Call { format_call(p, line_index, token_index); } - } if .Switch_Stmt in line.types && p.config.align_switch { @@ -438,15 +429,10 @@ format_generic :: proc(p: ^Printer) { if .Assign in line.types { format_assignment(p, line_index); } - } } - + align_var_decls_and_assignments :: proc(p: ^Printer) { - - - - } align_switch_stmt :: proc(p: ^Printer, index: int) { @@ -650,7 +636,7 @@ align_comments :: proc(p: ^Printer) { 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) { + (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); diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 36c8c42d9..b1b1d2693 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -195,8 +195,8 @@ append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_ format_token := format_token; if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || - p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || - p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { + p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || + p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { format_token.spaces_before = 0; } else if p.merge_next_token { format_token.spaces_before = 0; @@ -1101,10 +1101,10 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_expr(p, v.expr); push_format_token(p, Format_Token { - kind = .Open_Paren, - type = .Call, - text = "(", - }); + kind = .Open_Paren, + type = .Call, + text = "(", + }); hint_current_line(p, {.Call}); From f7b8b3a3400a28c4f35a6e10885bb0f0142441c8 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sun, 18 Apr 2021 22:06:32 +0200 Subject: [PATCH 028/137] ensure that the comments gets pushed if it's in the beginning before package --- core/odin/printer/printer.odin | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 928c0e07d..600f0bfe3 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -123,6 +123,8 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { 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); + set_line(p, 0); push_generic_token(p, .Package, 0); @@ -514,7 +516,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { //this will only happen if the case is one lined if case_found && colon_found { - line.format_tokens[i].spaces_before = (largest - length); + line.format_tokens[i].spaces_before = (largest - length) + 1; break; } From f1dc7c0b27f95df8999b6b12c5284476dc0bc64d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 19 Apr 2021 19:38:08 +0200 Subject: [PATCH 029/137] more work --- core/odin/printer/printer.odin | 4 ++++ core/odin/printer/visit.odin | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 600f0bfe3..c972be019 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -124,6 +124,8 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { } set_source_position(p, file.pkg_token.pos); + + p.last_source_position.line = 1; set_line(p, 0); @@ -187,6 +189,8 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { last_line = line_index; } + strings.write_string(&builder, newline); + return strings.to_string(builder); } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index b1b1d2693..953e0951f 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -106,10 +106,8 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { strings.write_string(&builder, "/*"); trim_space = true; - p.depth += 1; i += 1; } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { - p.depth -= 1; trim_space = true; strings.write_string(&builder, "*/"); i += 1; @@ -453,7 +451,11 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { push_generic_token(p, .Colon, 0); } - visit_exprs(p, v.values, true); + if len(v.values) == 1 { + visit_expr(p, v.values[0]); //this is too ensure that one value are never newlined(procs, structs, etc.) + } else { + visit_exprs(p, v.values, true); + } add_semicolon := true; From 3464784e5eb5186b9eb2f3145f127095d0830c2f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Mon, 19 Apr 2021 19:41:53 +0200 Subject: [PATCH 030/137] add proc to format multiline --- core/odin/printer/printer.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index c972be019..b7c87f111 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -413,7 +413,8 @@ format_generic :: proc(p: ^Printer) { for format_token, token_index in line.format_tokens { if format_token.kind == .For || format_token.kind == .If || - format_token.kind == .When || format_token.kind == .Switch { + format_token.kind == .When || format_token.kind == .Switch || + format_token.kind == .Proc { format_keyword_to_brace(p, line_index, token_index, format_token.kind); } else if format_token.type == .Call { format_call(p, line_index, token_index); From 25c3b6dc95847d530b8856478a2437ec17db698f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 21 Apr 2021 23:21:45 +0200 Subject: [PATCH 031/137] align value decls --- core/odin/printer/printer.odin | 132 ++++++++++++++++++++++++++++++++- core/odin/printer/visit.odin | 13 +++- 2 files changed, 139 insertions(+), 6 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index b7c87f111..0da6a9b38 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -124,7 +124,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { } set_source_position(p, file.pkg_token.pos); - + p.last_source_position.line = 1; set_line(p, 0); @@ -195,7 +195,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string { } fix_lines :: proc(p: ^Printer) { - align_var_decls_and_assignments(p); + align_var_decls(p); format_generic(p); align_comments(p); //align them last since they rely on the other alignments } @@ -439,7 +439,131 @@ format_generic :: proc(p: ^Printer) { } } -align_var_decls_and_assignments :: proc(p: ^Printer) { +align_var_decls :: proc(p: ^Printer) { + + current_line: int; + current_typed: 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; + + for i := 0; i < len(line.format_tokens) - 1; i += 1 { + if line.format_tokens[i].kind == .Colon && line.format_tokens[i + 1].kind == .Eq { + typed = false; + break; + } + } + + if line_index != current_line + 1 || typed != current_typed { + + if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed { + 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_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; + + 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}); + break; + } + } + + largest_lhs = max(largest_lhs, lhs_length); + largest_rhs = max(largest_rhs, rhs_length); + } + + fmt.println(current_typed); + + //repeating myself, move to sub procedure + if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed { + 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) { @@ -699,4 +823,4 @@ align_comments :: proc(p: ^Printer) { } } } -} \ No newline at end of file +} diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 953e0951f..67d0fdc50 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -485,7 +485,10 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing //we have to newline the expressions to respect the source for expr, i in list { - move_line_limit(p, expr.pos, 1); + //Don't move the first expression, it looks bad + if i != 0 { + move_line_limit(p, expr.pos, 1); + } visit_expr(p, expr); @@ -614,7 +617,11 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener newline_position(p, 1); } + set_source_position(p, v.body.pos); + visit_stmt(p, v.body, .If_Stmt); + + set_source_position(p, v.body.end); } if v.else_stmt != nil { @@ -1445,7 +1452,9 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := for field, i in list.list { - move_line_limit(p, field.pos, 1); + if i != 0 { + move_line_limit(p, field.pos, 1); + } if .Using in field.flags { push_generic_token(p, .Using, 0); From 6eb64f2bdc539786a1210f5048c346c75534e504 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 21 Apr 2021 23:40:08 +0200 Subject: [PATCH 032/137] align enum --- core/odin/printer/printer.odin | 87 +++++++++++++++++++++++++++++++++- core/odin/printer/visit.odin | 4 +- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 0da6a9b38..b81ef6481 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -61,6 +61,7 @@ Config :: struct { align_assignments: bool, align_structs: bool, align_style: Alignment_Style, + align_enums: bool, indent_cases: bool, newline_style: Newline_Style, } @@ -104,6 +105,7 @@ default_style := Config { indent_cases = false, align_switch = true, align_structs = true, + align_enums = true, newline_style = .CRLF, }; @@ -425,6 +427,10 @@ format_generic :: proc(p: ^Printer) { 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 { align_struct(p, line_index); } @@ -542,8 +548,6 @@ align_var_decls :: proc(p: ^Printer) { largest_rhs = max(largest_rhs, rhs_length); } - fmt.println(current_typed); - //repeating myself, move to sub procedure if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed { for colon_token in colon_tokens { @@ -665,6 +669,85 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { } } +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; + eq_count := 0; + + for line, line_index in p.lines[brace_line + 1:] { + + length := 0; + + for format_token in line.format_tokens { + + if format_token.kind == .Comment { + continue; + } + + if format_token.kind == .Eq { + eq_count += 1; + largest = max(length, largest); + break; + } + + length += len(format_token.text) + format_token.spaces_before; + } + + if eq_count >= brace_token.parameter_count { + break; + } + } + + eq_count = 0; + + 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 { + continue; + } + + if format_token.kind == .Eq { + eq_count += 1; + line.format_tokens[i].spaces_before = largest - length + 1; + break; + } + + length += len(format_token.text) + format_token.spaces_before; + } + + if eq_count >= brace_token.parameter_count { + break; + } + } +} + align_struct :: proc(p: ^Printer, index: int) { struct_found := false; diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 67d0fdc50..01272c60e 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -1017,6 +1017,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Enum_Type: push_generic_token(p, .Enum, 1); + hint_current_line(p, {.Enum}); + if v.base_type != nil { visit_expr(p, v.base_type); } @@ -1026,7 +1028,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_exprs(p, v.fields, true); push_generic_token(p, .Close_Brace, 0); } else { - visit_begin_brace(p, v.pos, .Generic); + visit_begin_brace(p, v.pos, .Generic, len(v.fields)); newline_position(p, 1); set_source_position(p, v.fields[0].pos); visit_exprs(p, v.fields, true, true); From ca112c0b6d1ef0f7450922e354218017641dc8c2 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:04:01 +0200 Subject: [PATCH 033/137] better placing of the end brace --- core/odin/printer/printer.odin | 1 + core/odin/printer/visit.odin | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index b81ef6481..14b67507f 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -907,3 +907,4 @@ align_comments :: proc(p: ^Printer) { } } } + \ No newline at end of file diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 01272c60e..84c4ebfd5 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -9,7 +9,7 @@ import "core:unicode/utf8" import "core:mem" import "core:sort" -//right the attribute order is not linearly parsed(bug?) +//right now the attribute order is not linearly parsed(bug?) @(private) sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { return sort.Interface { @@ -576,8 +576,6 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_block_stmts(p, v.stmts, len(v.stmts) > 1 && p.config.split_multiple_stmts); - set_source_position(p, v.end); - if !empty_block { visit_end_brace(p, v.end); } @@ -1259,8 +1257,7 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, c } visit_end_brace :: proc(p: ^Printer, end: tokenizer.Pos) { - set_source_position(p, end); - newline_position(p, 1); + move_line(p, end); push_generic_token(p, .Close_Brace, 0); unindent(p); p.current_line.depth = p.depth; From b18e8898d81a7358865d8601c28a3fb80ca9bcf2 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:07:07 +0200 Subject: [PATCH 034/137] ran odinfmt on source --- tools/odinfmt/flag/flag.odin | 451 +++++++++++++++++------------------ tools/odinfmt/main.odin | 275 ++++++++++----------- 2 files changed, 346 insertions(+), 380 deletions(-) diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin index 543438678..041b69df0 100644 --- a/tools/odinfmt/flag/flag.odin +++ b/tools/odinfmt/flag/flag.odin @@ -1,232 +1,219 @@ -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; - } - - //must be in the next argument - else 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"; -} \ No newline at end of file +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"; +} diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index 4040fe4c2..29c1f6c1b 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -1,148 +1,127 @@ -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() { - -} - -print_arg_error :: proc(error: flag.Flag_Error) { - fmt.println(error); -} - -format_file :: proc(filepath: string) -> ([] u8, bool) { - - if data, ok := os.read_entire_file(filepath); ok { - return format.format(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.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"}, context.temp_allocator); - - if data, ok := format_file(path); ok { - - os.rename(path, backup_path); - - if os.write_entire_file(path, data) { - os.remove(backup_path); - } - - } - - else { - fmt.eprintf("failed to write %v", path); - } - - } - - else { - - if data, ok := format_file(path); ok { - fmt.println(transmute(string)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"}, context.temp_allocator); - - if data, ok := format_file(file); ok { - - if _, ok := args.write.(bool); ok { - os.rename(file, backup_path); - - if os.write_entire_file(file, data) { - os.remove(backup_path); - } - } - - else { - fmt.println(transmute(string)data); - } - - - } else { - fmt.eprintf("failed to format %v", file); - } - - free_all(context.temp_allocator); - } - - 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); -} \ No newline at end of file +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() { +} + +print_arg_error :: proc(error: flag.Flag_Error) { + fmt.println(error); +} + +format_file :: proc(filepath: string) -> ([]u8, bool) { + + if data, ok := os.read_entire_file(filepath); ok { + return format.format(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.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"}, context.temp_allocator); + + if data, ok := format_file(path); ok { + + os.rename(path, backup_path); + + if os.write_entire_file(path, data) { + os.remove(backup_path); + } + } else { + fmt.eprintf("failed to write %v", path); + } + } else { + + if data, ok := format_file(path); ok { + fmt.println(transmute(string)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"}, context.temp_allocator); + + if data, ok := format_file(file); ok { + + if _, ok := args.write.(bool); ok { + os.rename(file, backup_path); + + if os.write_entire_file(file, data) { + os.remove(backup_path); + } + } else { + fmt.println(transmute(string)data); + } + } else { + fmt.eprintf("failed to format %v", file); + } + + free_all(context.temp_allocator); + } + + 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); +} From 9b8563dfc0ac54a270b0667b77047e0a4cba9e8d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:08:03 +0200 Subject: [PATCH 035/137] style --- tools/odinfmt/flag/flag.odin | 3 --- tools/odinfmt/main.odin | 5 ----- 2 files changed, 8 deletions(-) diff --git a/tools/odinfmt/flag/flag.odin b/tools/odinfmt/flag/flag.odin index 041b69df0..a943a5a1c 100644 --- a/tools/odinfmt/flag/flag.odin +++ b/tools/odinfmt/flag/flag.odin @@ -146,13 +146,11 @@ reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error { 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])); @@ -202,7 +200,6 @@ parse :: proc(v: any, args: []string) -> Flag_Error { //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 diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index 29c1f6c1b..a651aaff0 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -68,9 +68,7 @@ main :: proc() { tick_time := time.tick_now(); if os.is_file(path) { - if _, ok := args.write.(bool); ok { - backup_path := strings.concatenate({path, "_bk"}, context.temp_allocator); if data, ok := format_file(path); ok { @@ -84,17 +82,14 @@ main :: proc() { fmt.eprintf("failed to write %v", path); } } else { - if data, ok := format_file(path); ok { fmt.println(transmute(string)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"}, context.temp_allocator); From de1838c0cb71d3734699a610eb1a0848c45e225d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:23:17 +0200 Subject: [PATCH 036/137] align not mutable correctly --- core/odin/printer/printer.odin | 24 ++++++++++++++++++++---- core/odin/printer/visit.odin | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 14b67507f..36b0052c3 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -449,6 +449,7 @@ align_var_decls :: proc(p: ^Printer) { current_line: int; current_typed: bool; + current_not_mutable: bool; largest_lhs := 0; largest_rhs := 0; @@ -470,17 +471,31 @@ align_var_decls :: proc(p: ^Printer) { } typed := true; + not_mutable := false; + continue_flag := false; for i := 0; i < len(line.format_tokens) - 1; i += 1 { if line.format_tokens[i].kind == .Colon && line.format_tokens[i + 1].kind == .Eq { typed = false; - break; } + + if line.format_tokens[i].kind == .Colon && line.format_tokens[i + 1].kind == .Colon { + not_mutable = true; + } + + if tokenizer.Token_Kind.B_Keyword_Begin <= line.format_tokens[i].kind && line.format_tokens[i].kind <= tokenizer.Token_Kind.B_Keyword_End { + continue_flag = true; + } + } - if line_index != current_line + 1 || typed != current_typed { + if continue_flag { + continue; + } - if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed { + 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; } @@ -507,6 +522,7 @@ align_var_decls :: proc(p: ^Printer) { largest_rhs = 0; largest_lhs = 0; current_typed = typed; + current_not_mutable = not_mutable; } current_line = line_index; @@ -549,7 +565,7 @@ align_var_decls :: proc(p: ^Printer) { } //repeating myself, move to sub procedure - if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed { + 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; } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 84c4ebfd5..b07a75e06 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -424,11 +424,12 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { visit_exprs(p, v.names, true); + hint_current_line(p, {.Value_Decl}); + if v.type != nil { if !v.is_mutable { push_generic_token(p, .Colon, 0); } else { - hint_current_line(p, {.Value_Decl}); push_generic_token(p, .Colon, 0); } @@ -438,7 +439,6 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { push_generic_token(p, .Colon, 1); push_generic_token(p, .Colon, 0); } else { - hint_current_line(p, {.Value_Decl}); push_generic_token(p, .Colon, 1); } } From 43589a56b7300c83bc206bff8ff8e35ed2365499 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:32:36 +0200 Subject: [PATCH 037/137] odinfmt on printer --- core/odin/printer/printer.odin | 66 ++++++++++++---------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 36b0052c3..ba031f2b1 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -206,7 +206,7 @@ format_value_decl :: proc(p: ^Printer, index: int) { eq_found := false; eq_token: Format_Token; - eq_line: int; + eq_line: int; largest := 0; found_eq: for line, line_index in p.lines[index:] { @@ -271,8 +271,8 @@ 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: Format_Token; + paren_line: int; paren_token_index: int; largest := 0; @@ -300,7 +300,7 @@ format_call :: proc(p: ^Printer, line_index: int, format_index: int) { } paren_count := 1; - done := false; + done := false; for line, line_index in p.lines[paren_line:] { @@ -343,11 +343,11 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword_found := false; keyword_token: Format_Token; - keyword_line: int; + keyword_line: int; largest := 0; brace_count := 0; - done := false; + done := false; found_keyword: for line, i in p.lines[line_index:] { for format_token in line.format_tokens { @@ -447,8 +447,8 @@ format_generic :: proc(p: ^Printer) { align_var_decls :: proc(p: ^Printer) { - current_line: int; - current_typed: bool; + current_line: int; + current_typed: bool; current_not_mutable: bool; largest_lhs := 0; @@ -470,8 +470,8 @@ align_var_decls :: proc(p: ^Printer) { continue; } - typed := true; - not_mutable := false; + typed := true; + not_mutable := false; continue_flag := false; for i := 0; i < len(line.format_tokens) - 1; i += 1 { @@ -528,8 +528,8 @@ align_var_decls :: proc(p: ^Printer) { current_line = line_index; current_token_index := 0; - lhs_length := 0; - rhs_length := 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 { @@ -590,12 +590,10 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { switch_found := false; brace_token: Format_Token; - brace_line: int; + 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; @@ -612,15 +610,15 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { return; } - largest := 0; + largest := 0; case_count := 0; //find all the switch cases that are one lined for line, line_index in p.lines[brace_line + 1:] { - case_found := false; + case_found := false; colon_found := false; - length := 0; + length := 0; for format_token in line.format_tokens { @@ -652,13 +650,11 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { case_count = 0; for line, line_index in p.lines[brace_line + 1:] { - - case_found := false; + case_found := false; colon_found := false; - length := 0; + length := 0; for format_token, i in line.format_tokens { - if format_token.kind == .Comment { continue; } @@ -688,12 +684,10 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { align_enum :: proc(p: ^Printer, index: int) { enum_found := false; brace_token: Format_Token; - brace_line: int; + 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; @@ -710,15 +704,13 @@ align_enum :: proc(p: ^Printer, index: int) { return; } - largest := 0; + largest := 0; eq_count := 0; for line, line_index in p.lines[brace_line + 1:] { - length := 0; for format_token in line.format_tokens { - if format_token.kind == .Comment { continue; } @@ -740,11 +732,9 @@ align_enum :: proc(p: ^Printer, index: int) { eq_count = 0; 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 { continue; } @@ -768,12 +758,10 @@ align_struct :: proc(p: ^Printer, index: int) { struct_found := false; brace_token: Format_Token; - brace_line: int; + 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; @@ -790,15 +778,13 @@ align_struct :: proc(p: ^Printer, index: int) { return; } - largest := 0; + largest := 0; colon_count := 0; for line, line_index in p.lines[brace_line + 1:] { - length := 0; for format_token in line.format_tokens { - if format_token.kind == .Comment { continue; } @@ -820,11 +806,9 @@ align_struct :: proc(p: ^Printer, index: int) { colon_count = 0; 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 { continue; } @@ -858,13 +842,11 @@ align_comments :: proc(p: ^Printer) { 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) { @@ -881,7 +863,6 @@ align_comments :: proc(p: ^Printer) { 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; @@ -903,13 +884,11 @@ align_comments :: proc(p: ^Printer) { } 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; @@ -923,4 +902,3 @@ align_comments :: proc(p: ^Printer) { } } } - \ No newline at end of file From ab53900c95cb68dd4579dc1dca3926eb50340f9d Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:55:25 +0200 Subject: [PATCH 038/137] check comma count in enum instead --- core/odin/printer/printer.odin | 18 +++++++++++------- core/odin/printer/visit.odin | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index ba031f2b1..1515ae4e2 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -486,13 +486,15 @@ align_var_decls :: proc(p: ^Printer) { if tokenizer.Token_Kind.B_Keyword_Begin <= line.format_tokens[i].kind && line.format_tokens[i].kind <= tokenizer.Token_Kind.B_Keyword_End { continue_flag = true; } - } if continue_flag { continue; } + fmt.println(line); + fmt.println(largest_rhs); + 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 { @@ -705,7 +707,7 @@ align_enum :: proc(p: ^Printer, index: int) { } largest := 0; - eq_count := 0; + comma_count := 0; for line, line_index in p.lines[brace_line + 1:] { length := 0; @@ -716,20 +718,21 @@ align_enum :: proc(p: ^Printer, index: int) { } if format_token.kind == .Eq { - eq_count += 1; largest = max(length, largest); break; + } else if format_token.kind == .Comma { + comma_count += 1; } length += len(format_token.text) + format_token.spaces_before; } - if eq_count >= brace_token.parameter_count { + if comma_count >= brace_token.parameter_count { break; } } - eq_count = 0; + comma_count = 0; for line, line_index in p.lines[brace_line + 1:] { length := 0; @@ -740,15 +743,16 @@ align_enum :: proc(p: ^Printer, index: int) { } if format_token.kind == .Eq { - eq_count += 1; line.format_tokens[i].spaces_before = largest - length + 1; break; + } else if format_token.kind == .Comma { + comma_count += 1; } length += len(format_token.text) + format_token.spaces_before; } - if eq_count >= brace_token.parameter_count { + if comma_count >= brace_token.parameter_count { break; } } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index b07a75e06..4fcd4d76e 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -31,7 +31,7 @@ sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { - + if len(p.comments) <= p.latest_comment_index { return false; } From 40ed7e48d0e4a1f000efbd03d19a4eebe9b8e2f6 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 22 Apr 2021 00:56:31 +0200 Subject: [PATCH 039/137] remove prints --- core/odin/printer/printer.odin | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 1515ae4e2..c0d39f4ba 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -492,9 +492,6 @@ align_var_decls :: proc(p: ^Printer) { continue; } - fmt.println(line); - fmt.println(largest_rhs); - 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 { From 3f9ad6ba09176e022af756ae2b1856e2b2775f8c Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 16:33:11 +0200 Subject: [PATCH 040/137] fix proc group, struct align with internal structs --- core/odin/printer/printer.odin | 51 +++++++++++++++++----------------- core/odin/printer/visit.odin | 14 +++++----- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index c0d39f4ba..21d94a3e9 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -483,7 +483,10 @@ align_var_decls :: proc(p: ^Printer) { not_mutable = true; } - if tokenizer.Token_Kind.B_Keyword_Begin <= line.format_tokens[i].kind && line.format_tokens[i].kind <= tokenizer.Token_Kind.B_Keyword_End { + if line.format_tokens[i].kind == tokenizer.Token_Kind.Proc || + line.format_tokens[i].kind == tokenizer.Token_Kind.Union || + line.format_tokens[i].kind == tokenizer.Token_Kind.Enum || + line.format_tokens[i].kind == tokenizer.Token_Kind.Struct { continue_flag = true; } } @@ -496,6 +499,7 @@ align_var_decls :: proc(p: ^Printer) { if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable { for colon_token in colon_tokens { + fmt.println(colon_token); colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1; } } else if p.config.align_style == .Align_On_Type_And_Equals { @@ -703,7 +707,7 @@ align_enum :: proc(p: ^Printer, index: int) { return; } - largest := 0; + largest := 0; comma_count := 0; for line, line_index in p.lines[brace_line + 1:] { @@ -781,30 +785,14 @@ align_struct :: proc(p: ^Printer, index: int) { largest := 0; colon_count := 0; + seen_brace := false; - for line, line_index in p.lines[brace_line + 1:] { - length := 0; + TokenAndLength :: struct { + format_token: ^Format_Token, + length: int, + }; - for format_token in line.format_tokens { - if format_token.kind == .Comment { - continue; - } - - if format_token.kind == .Colon { - colon_count += 1; - largest = max(length, largest); - break; - } - - length += len(format_token.text) + format_token.spaces_before; - } - - if colon_count >= brace_token.parameter_count { - break; - } - } - - colon_count = 0; + format_tokens := make([] TokenAndLength, brace_token.parameter_count, context.temp_allocator); for line, line_index in p.lines[brace_line + 1:] { length := 0; @@ -812,12 +800,18 @@ align_struct :: proc(p: ^Printer, index: int) { for format_token, i in line.format_tokens { if format_token.kind == .Comment { continue; + } 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 }; colon_count += 1; - line.format_tokens[i + 1].spaces_before = largest - length + 1; - break; + largest = max(length, largest); } length += len(format_token.text) + format_token.spaces_before; @@ -827,6 +821,11 @@ align_struct :: proc(p: ^Printer, index: int) { break; } } + + for token in format_tokens { + token.format_token.spaces_before = largest - token.length + 1; + } + } align_comments :: proc(p: ^Printer) { diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 4fcd4d76e..9bfcc3427 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -31,7 +31,7 @@ sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { - + if len(p.comments) <= p.latest_comment_index { return false; } @@ -42,7 +42,7 @@ comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { } @(private) -next_comment_group :: proc(p: ^Printer) { +next_comment_group :: proc(p: ^Printer) { p.latest_comment_index += 1; } @@ -79,7 +79,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { builder := strings.make_builder(context.temp_allocator); - c_len := len(comment.text); + c_len := len(comment.text); trim_space := true; multilines: [dynamic]string; @@ -157,7 +157,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { @(private) push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { - prev_comment: ^tokenizer.Token; + prev_comment: ^tokenizer.Token; prev_comment_lines: int; for comment_before_position(p, pos) { @@ -1141,7 +1141,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Bracket, 0); case Proc_Group: - push_generic_token(p, v.tok.kind, 0); + push_generic_token(p, v.tok.kind, 1); if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { visit_begin_brace(p, v.pos, .Generic); @@ -1354,7 +1354,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { push_generic_token(p, .Gt, 0); use_parens := false; - use_named := false; + use_named := false; if len(proc_type.results.list) > 1 { use_parens = true; @@ -1497,4 +1497,4 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := push_generic_token(p, .Comma, 0); } } -} \ No newline at end of file +} From 3b5b845ea6be2eb2fe4efd884124259eaeb62e3a Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 16:45:55 +0200 Subject: [PATCH 041/137] refractor --- core/odin/printer/printer.odin | 80 ++++++++++------------------------ 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 21d94a3e9..bf0fe207b 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -616,6 +616,13 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { 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:] { @@ -623,7 +630,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { colon_found := false; length := 0; - for format_token in line.format_tokens { + for format_token, i in line.format_tokens { if format_token.kind == .Comment { continue; @@ -631,6 +638,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { //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; } @@ -650,38 +658,10 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { } } - case_count = 0; - - 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 { - continue; - } - - //this will only happen if the case is one lined - if case_found && colon_found { - line.format_tokens[i].spaces_before = (largest - length) + 1; - 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) { @@ -710,15 +690,23 @@ align_enum :: proc(p: ^Printer, index: int) { 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 in line.format_tokens { + for format_token, i in line.format_tokens { if format_token.kind == .Comment { continue; } 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 { @@ -733,30 +721,10 @@ align_enum :: proc(p: ^Printer, index: int) { } } - comma_count = 0; - - 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 { - continue; - } - - if format_token.kind == .Eq { - line.format_tokens[i].spaces_before = largest - length + 1; - 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) { From 951e940470223c9cd2485f8b75f474182b7d2d4c Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 21:52:27 +0200 Subject: [PATCH 042/137] handle comments in stupid places --- core/odin/printer/printer.odin | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index bf0fe207b..2b4a94eb6 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -376,7 +376,7 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, for format_token, i in line.format_tokens { if format_token.kind == .Comment { - continue; + break; } if line_index == 0 && i <= format_index { @@ -483,10 +483,14 @@ align_var_decls :: proc(p: ^Printer) { not_mutable = true; } - if line.format_tokens[i].kind == tokenizer.Token_Kind.Proc || - line.format_tokens[i].kind == tokenizer.Token_Kind.Union || - line.format_tokens[i].kind == tokenizer.Token_Kind.Enum || - line.format_tokens[i].kind == tokenizer.Token_Kind.Struct { + if line.format_tokens[i].kind == .Proc || + line.format_tokens[i].kind == .Union || + line.format_tokens[i].kind == .Enum || + line.format_tokens[i].kind == .Struct { + continue_flag = true; + } + + if line.format_tokens[i].kind == .Comment { continue_flag = true; } } @@ -633,7 +637,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { for format_token, i in line.format_tokens { if format_token.kind == .Comment { - continue; + break; } //this will only happen if the case is one lined @@ -702,7 +706,7 @@ align_enum :: proc(p: ^Printer, index: int) { for format_token, i in line.format_tokens { if format_token.kind == .Comment { - continue; + break; } if format_token.kind == .Eq { @@ -767,7 +771,7 @@ align_struct :: proc(p: ^Printer, index: int) { for format_token, i in line.format_tokens { if format_token.kind == .Comment { - continue; + break; } else if format_token.kind == .Open_Brace { seen_brace = true; } else if format_token.kind == .Close_Brace { From aafbf5bac7b6aec6a1a11a6ac48d064319432891 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 22:55:59 +0200 Subject: [PATCH 043/137] odinfmt --- core/odin/printer/printer.odin | 20 ++++++++++---------- core/odin/printer/visit.odin | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 2b4a94eb6..1ee7d7a62 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -460,7 +460,7 @@ align_var_decls :: proc(p: ^Printer) { }; colon_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator); - type_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 { @@ -483,10 +483,11 @@ align_var_decls :: proc(p: ^Printer) { not_mutable = true; } - if line.format_tokens[i].kind == .Proc || + if line.format_tokens[i].kind == .Proc || line.format_tokens[i].kind == .Union || line.format_tokens[i].kind == .Enum || - line.format_tokens[i].kind == .Struct { + line.format_tokens[i].kind == .Struct || + line.format_tokens[i].kind == .For { continue_flag = true; } @@ -503,7 +504,6 @@ align_var_decls :: proc(p: ^Printer) { if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable { for colon_token in colon_tokens { - fmt.println(colon_token); colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1; } } else if p.config.align_style == .Align_On_Type_And_Equals { @@ -625,7 +625,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { length: int, }; - format_tokens := make([dynamic] TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator); + 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:] { @@ -642,7 +642,7 @@ align_switch_stmt :: proc(p: ^Printer, index: int) { //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 }); + append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length}); largest = max(length, largest); break; } @@ -699,7 +699,7 @@ align_enum :: proc(p: ^Printer, index: int) { length: int, }; - format_tokens := make([dynamic] TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator); + 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; @@ -710,7 +710,7 @@ align_enum :: proc(p: ^Printer, index: int) { } if format_token.kind == .Eq { - append(&format_tokens, TokenAndLength { format_token = &line.format_tokens[i], length = length }); + append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length}); largest = max(length, largest); break; } else if format_token.kind == .Comma { @@ -764,7 +764,7 @@ align_struct :: proc(p: ^Printer, index: int) { length: int, }; - format_tokens := make([] TokenAndLength, brace_token.parameter_count, context.temp_allocator); + format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator); for line, line_index in p.lines[brace_line + 1:] { length := 0; @@ -781,7 +781,7 @@ align_struct :: proc(p: ^Printer, index: int) { } if format_token.kind == .Colon { - format_tokens[colon_count] = { format_token = &line.format_tokens[i + 1], length = length }; + format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length}; colon_count += 1; largest = max(length, largest); } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 9bfcc3427..3fdb77615 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -42,7 +42,7 @@ comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { } @(private) -next_comment_group :: proc(p: ^Printer) { +next_comment_group :: proc(p: ^Printer) { p.latest_comment_index += 1; } From 5fba548aa032ada33fce0ba0dc6ca0876e0fceb1 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 23:22:48 +0200 Subject: [PATCH 044/137] more fixes with wierd comment placements --- core/odin/printer/printer.odin | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 1ee7d7a62..13c8dbf6b 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -12,6 +12,9 @@ Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Line_Type :: bit_set[Type_Enum]; +/* + Represents an unwrapped line +*/ Line :: struct { format_tokens: [dynamic]Format_Token, finalized: bool, @@ -20,6 +23,9 @@ Line :: struct { 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 an singular token in a unwrapped line +*/ Format_Token :: struct { kind: tokenizer.Token_Kind, text: string, @@ -474,12 +480,12 @@ align_var_decls :: proc(p: ^Printer) { not_mutable := false; continue_flag := false; - for i := 0; i < len(line.format_tokens) - 1; i += 1 { - if line.format_tokens[i].kind == .Colon && line.format_tokens[i + 1].kind == .Eq { + 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[i + 1].kind == .Colon { + if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Colon { not_mutable = true; } From 87bfd3166482eaa3d89387bde03852f23c6f0629 Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Fri, 23 Apr 2021 23:42:53 +0200 Subject: [PATCH 045/137] force all enums newlined if there is more than one line --- core/odin/printer/printer.odin | 2 +- core/odin/printer/visit.odin | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 13c8dbf6b..e1d9f5b7d 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -24,7 +24,7 @@ Line :: struct { } /* - Represents an singular token in a unwrapped line + Represents a singular token in a unwrapped line */ Format_Token :: struct { kind: tokenizer.Token_Kind, diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 3fdb77615..a6b6759de 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -476,7 +476,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { } @(private) -visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false) { +visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false, force_newline := false) { if len(list) == 0 { return; @@ -484,9 +484,12 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing //we have to newline the expressions to respect the source for expr, i in list { - //Don't move the first expression, it looks bad - if i != 0 { + if i != 0 && force_newline { + newline_position(p, 1); + } + + else if i != 0 { move_line_limit(p, expr.pos, 1); } @@ -496,6 +499,10 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing push_generic_token(p, .Comma, 0); } } + + if len(list) > 1 && force_newline { + newline_position(p, 1); + } } @(private) @@ -506,7 +513,6 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { } for attribute, i in attributes { - move_line_limit(p, attribute.pos, 1); push_generic_token(p, .At, 0); @@ -1029,7 +1035,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_begin_brace(p, v.pos, .Generic, len(v.fields)); newline_position(p, 1); set_source_position(p, v.fields[0].pos); - visit_exprs(p, v.fields, true, true); + visit_exprs(p, v.fields, true, true, true); + set_source_position(p, v.end); visit_end_brace(p, v.end); } From 088f4b503994feb7c5abe64b8956d827ef9ae36f Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Wed, 28 Apr 2021 12:53:04 +0200 Subject: [PATCH 046/137] fix out of bounds with empty struct --- core/odin/printer/printer.odin | 7 ++++++- core/odin/printer/visit.odin | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index e1d9f5b7d..63c0c5fc7 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -493,7 +493,8 @@ align_var_decls :: proc(p: ^Printer) { 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 == .For || + line.format_tokens[i].kind == .If { continue_flag = true; } @@ -772,6 +773,10 @@ align_struct :: proc(p: ^Printer, index: int) { format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator); + if brace_token.parameter_count == 0 { + return; + } + for line, line_index in p.lines[brace_line + 1:] { length := 0; diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index a6b6759de..003bb8332 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -487,9 +487,7 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing //Don't move the first expression, it looks bad if i != 0 && force_newline { newline_position(p, 1); - } - - else if i != 0 { + } else if i != 0 { move_line_limit(p, expr.pos, 1); } From 9c6ab059810624043b0ebab9cc1b87a110457e7a Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 29 Apr 2021 00:51:24 +0200 Subject: [PATCH 047/137] fix tokenizer for ~= and better struct aligning --- core/odin/printer/printer.odin | 19 ++++++++++++------- core/odin/printer/visit.odin | 9 ++++++++- core/odin/tokenizer/tokenizer.odin | 2 +- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 63c0c5fc7..407e5a415 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -350,8 +350,8 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword_found := false; keyword_token: Format_Token; keyword_line: int; - largest := 0; + largest := 0; brace_count := 0; done := false; @@ -385,6 +385,10 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, break; } + if format_token.kind == .Undef || format_token.kind == .Comma { + return; + } + if line_index == 0 && i <= format_index { continue; } @@ -489,18 +493,20 @@ align_var_decls :: proc(p: ^Printer) { not_mutable = true; } - if line.format_tokens[i].kind == .Proc || - line.format_tokens[i].kind == .Union || + 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 == .If || + line.format_tokens[i].kind == .Comment { continue_flag = true; } - if line.format_tokens[i].kind == .Comment { + //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 { @@ -601,7 +607,6 @@ align_var_decls :: proc(p: ^Printer) { } align_switch_stmt :: proc(p: ^Printer, index: int) { - switch_found := false; brace_token: Format_Token; brace_line: int; @@ -739,7 +744,6 @@ align_enum :: proc(p: ^Printer, index: int) { } align_struct :: proc(p: ^Printer, index: int) { - struct_found := false; brace_token: Format_Token; brace_line: int; @@ -795,6 +799,7 @@ align_struct :: proc(p: ^Printer, index: int) { format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length}; colon_count += 1; largest = max(length, largest); + break; } length += len(format_token.text) + format_token.spaces_before; diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 003bb8332..a8f7068c8 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -461,8 +461,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { for value in v.values { switch a in value.derived { - case Proc_Lit, Union_Type, Enum_Type, Struct_Type: + case Union_Type, Enum_Type, Struct_Type: add_semicolon = false || called_in_stmt; + case Proc_Lit: + add_semicolon = false; } } @@ -532,6 +534,9 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } switch v in stmt.derived { + case Import_Decl: + visit_decl(p, cast(^Decl)stmt, true); + return; case Value_Decl: visit_decl(p, cast(^Decl)stmt, true); return; @@ -693,6 +698,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener case Type_Switch_Stmt: move_line(p, v.pos); + hint_current_line(p, {.Switch_Stmt}); + if v.label != nil { visit_expr(p, v.label); push_generic_token(p, .Colon, 0); diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 58f546191..b1b446192 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -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); From 87a18338628fc42ba49c24d095b37e351e779ede Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Sat, 1 May 2021 21:26:40 +0200 Subject: [PATCH 048/137] fix weird behavior of nesting proc types in structs --- core/odin/printer/printer.odin | 56 ++++++++++++++++++++++++++-------- core/odin/printer/visit.odin | 19 ++++++++++-- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 407e5a415..bef1235e9 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,7 +8,7 @@ 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} +Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit} Line_Type :: bit_set[Type_Enum]; @@ -68,6 +68,7 @@ Config :: struct { align_structs: bool, align_style: Alignment_Style, align_enums: bool, + align_length_break: int, indent_cases: bool, newline_style: Newline_Style, } @@ -113,6 +114,7 @@ default_style := Config { align_structs = true, align_enums = true, newline_style = .CRLF, + align_length_break = 9, }; make_printer :: proc(config: Config, allocator := context.allocator) -> Printer { @@ -383,11 +385,9 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, if format_token.kind == .Comment { break; - } - - if format_token.kind == .Undef || format_token.kind == .Comma { + } else if format_token.kind == .Undef { return; - } + } if line_index == 0 && i <= format_index { continue; @@ -416,6 +416,8 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, format_generic :: proc(p: ^Printer) { + next_struct_line := 0; + for line, line_index in p.lines { if len(line.format_tokens) <= 0 { @@ -423,10 +425,9 @@ format_generic :: proc(p: ^Printer) { } for format_token, token_index in line.format_tokens { - if format_token.kind == .For || format_token.kind == .If || format_token.kind == .When || format_token.kind == .Switch || - format_token.kind == .Proc { + (format_token.kind == .Proc && format_token.type == .Proc_Lit) { format_keyword_to_brace(p, line_index, token_index, format_token.kind); } else if format_token.type == .Call { format_call(p, line_index, token_index); @@ -441,8 +442,8 @@ format_generic :: proc(p: ^Printer) { align_enum(p, line_index); } - if .Struct in line.types && p.config.align_structs { - align_struct(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 { @@ -452,6 +453,8 @@ format_generic :: proc(p: ^Printer) { if .Assign in line.types { format_assignment(p, line_index); } + + next_struct_line -= 1; } } @@ -743,7 +746,7 @@ align_enum :: proc(p: ^Printer, index: int) { } -align_struct :: proc(p: ^Printer, index: int) { +align_struct :: proc(p: ^Printer, index: int) -> int { struct_found := false; brace_token: Format_Token; brace_line: int; @@ -763,11 +766,12 @@ align_struct :: proc(p: ^Printer, index: int) { } if !struct_found { - return; + return 0; } largest := 0; colon_count := 0; + nested := false; seen_brace := false; TokenAndLength :: struct { @@ -778,15 +782,21 @@ align_struct :: proc(p: ^Printer, index: int) { format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator); if brace_token.parameter_count == 0 { - return; + 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 { @@ -797,23 +807,43 @@ align_struct :: proc(p: ^Printer, index: int) { 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); - break; } 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) { diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index a8f7068c8..c2bd8eaf8 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -1095,7 +1095,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_ident_token(p, "#force_inline", 0); } - visit_proc_type(p, v.type^); + visit_proc_type(p, v.type^, true); if v.where_clauses != nil { move_line(p, v.where_clauses[0].pos); @@ -1324,9 +1324,22 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, } } -visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type) { +visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) { - push_generic_token(p, .Proc, 1); + if is_proc_lit { + push_format_token(p, Format_Token { + kind = .Proc, + type = .Proc_Lit, + text = "proc", + spaces_before = 1, + }); + } else { + push_format_token(p, Format_Token { + kind = .Proc, + text = "proc", + spaces_before = 1, + }); + } explicit_calling := false; From ff6fdc7812bce3edfbf102dddc97c5821da6e840 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 May 2021 21:16:26 +0100 Subject: [PATCH 049/137] Correct SysV ABI for `-> (f32, bool)` --- src/llvm_abi.cpp | 5 +++++ src/llvm_backend.cpp | 27 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 739d43d60..6635423e7 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -549,6 +549,11 @@ namespace lbAbiAmd64SysV { } else if (oldv == RegClass_Memory || newv == RegClass_Memory) { return; } else if (oldv == RegClass_Int || newv == RegClass_Int) { + if (oldv == RegClass_SSEFv || oldv == RegClass_SSEFs) { + oldv = RegClass_Int; + } else if (newv == RegClass_SSEFv || newv == RegClass_SSEFs) { + oldv = RegClass_Int; + } return; } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9a6595b24..78ffb36e5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5794,9 +5794,15 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { if (USE_SEPARTE_MODULES) { lbModule *other_module = lb_pkg_module(m->gen, e->pkg); + + // TODO(bill): correct this logic bool is_external = other_module != m; if (!is_external) { - other_module = e->code_gen_module; + if (e->code_gen_module != nullptr) { + other_module = e->code_gen_module; + } else { + other_module = nullptr; + } is_external = other_module != m; } @@ -5806,8 +5812,20 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { lbValue g = {}; g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name)); g.type = alloc_type_pointer(e->type); + lb_add_entity(m, e, g); + lb_add_member(m, name, g); + LLVMSetLinkage(g.value, LLVMExternalLinkage); + // if (other_module != nullptr) { + // lbValue *other_found = string_map_get(&other_module->members, name); + // if (other_found) { + // lbValue other_g = *other_found; + // } + // } + + // LLVMSetLinkage(other_g.value, LLVMExternalLinkage); + if (e->Variable.thread_local_model != "") { LLVMSetThreadLocal(g.value, true); @@ -5827,8 +5845,7 @@ lbValue lb_find_value_from_entity(lbModule *m, Entity *e) { LLVMSetThreadLocalMode(g.value, mode); } - lb_add_entity(m, e, g); - lb_add_member(m, name, g); + return g; } } @@ -14713,9 +14730,7 @@ void lb_generate_code(lbGenerator *gen) { if (is_export) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); - } - - if (e->flags & EntityFlag_Static) { + } else { // LLVMSetLinkage(g.value, LLVMInternalLinkage); } From 9a4d942b0bad10c4543a3818ced1475bd14bed00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 May 2021 21:29:25 +0100 Subject: [PATCH 050/137] Fix debug information for array types by setting the `DISubrange` --- src/llvm_backend.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 78ffb36e5..c581ea46d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1886,16 +1886,32 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Pointer: return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0); - case Type_Array: + case Type_Array: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->Array.count + ); + return LLVMDIBuilderCreateArrayType(m->debug_builder, - type->Array.count, + 8*cast(uint64_t)type_size_of(type), 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->Array.elem), - nullptr, 0); + subscripts, gb_count_of(subscripts)); + } case Type_EnumeratedArray: { + LLVMMetadataRef subscripts[1] = {}; + subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, + 0ll, + type->EnumeratedArray.count + ); + LLVMMetadataRef array_type = LLVMDIBuilderCreateArrayType(m->debug_builder, - type->EnumeratedArray.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->EnumeratedArray.elem), nullptr, 0); + 8*cast(uint64_t)type_size_of(type), + 8*cast(unsigned)type_align_of(type), + lb_debug_type(m, type->EnumeratedArray.elem), + subscripts, gb_count_of(subscripts)); gbString name = type_to_string(type, temporary_allocator()); return LLVMDIBuilderCreateTypedef(m->debug_builder, array_type, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); } From 073bd3f6c9a07298e7381ba2c504b26546a65454 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 10:55:55 +0100 Subject: [PATCH 051/137] Update LLVM-C.lib (since it was missing many required definitions missing from the official one that LLVM supplied) --- bin/llvm/windows/LLVM-C.lib | Bin 274018 -> 287196 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/llvm/windows/LLVM-C.lib b/bin/llvm/windows/LLVM-C.lib index 2353c6a39e4b0149ce2c9166eb52ed706efb0ac0..a55bb863cca93ef6b8fa8cbd2de1452744c8cfed 100644 GIT binary patch delta 49317 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 N%slivIk#0b|35K9B>w;a delta 41661 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 Xkq>QVb5Mi>{{@isSvFJnNSglx Date: Tue, 11 May 2021 12:00:27 +0100 Subject: [PATCH 052/137] Improve SysV ABI --- src/llvm_abi.cpp | 62 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 6635423e7..c2298bcd0 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -419,8 +419,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; } @@ -538,35 +544,48 @@ namespace lbAbiAmd64SysV { return reg_classes; } - void unify(Array *cls, i64 i, RegClass newv) { - RegClass &oldv = (*cls)[i]; + void unify(Array *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) { - if (oldv == RegClass_SSEFv || oldv == RegClass_SSEFs) { - oldv = RegClass_Int; - } else if (newv == RegClass_SSEFv || newv == RegClass_SSEFs) { - oldv = RegClass_Int; + 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; } - 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; } + + (*cls)[i] = to_write; } void fixup(LLVMTypeRef t, Array *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++) { @@ -610,8 +629,8 @@ namespace lbAbiAmd64SysV { unsigned llvec_len(Array const ®_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++; @@ -622,7 +641,7 @@ namespace lbAbiAmd64SysV { LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) { auto types = array_make(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: @@ -664,7 +683,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; @@ -680,6 +699,7 @@ namespace lbAbiAmd64SysV { default: GB_PANIC("Unhandled RegClass"); } + i += 1; } GB_ASSERT(types.count != 0); From 8ff80dec5824a710eac384ed83608f5a88a57cb7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:00:55 +0100 Subject: [PATCH 053/137] Minor change (in preparation for something else) to opt passes --- src/llvm_backend.cpp | 16 ++++++++-------- src/llvm_backend_opt.cpp | 32 +++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c581ea46d..d784e36b6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14019,7 +14019,7 @@ struct lbGlobalVariable { lbProcedure *lb_create_startup_type_info(lbModule *m) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14050,7 +14050,7 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14140,7 +14140,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager); Type *params = alloc_type_tuple(); @@ -14351,10 +14351,10 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { LLVMInitializeFunctionPassManager(function_pass_manager_size); LLVMInitializeFunctionPassManager(function_pass_manager_speed); - lb_populate_function_pass_manager(default_function_pass_manager, false, build_context.optimization_level); - lb_populate_function_pass_manager_specific(function_pass_manager_minimal, 0); - lb_populate_function_pass_manager_specific(function_pass_manager_size, 1); - lb_populate_function_pass_manager_specific(function_pass_manager_speed, 2); + lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level); + lb_populate_function_pass_manager_specific(m, function_pass_manager_minimal, 0); + lb_populate_function_pass_manager_specific(m, function_pass_manager_size, 1); + lb_populate_function_pass_manager_specific(m, function_pass_manager_speed, 2); LLVMFinalizeFunctionPassManager(default_function_pass_manager); LLVMFinalizeFunctionPassManager(function_pass_manager_minimal); @@ -14364,7 +14364,7 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { LLVMPassManagerRef default_function_pass_manager_without_memcpy = LLVMCreateFunctionPassManagerForModule(m->mod); LLVMInitializeFunctionPassManager(default_function_pass_manager_without_memcpy); - lb_populate_function_pass_manager(default_function_pass_manager_without_memcpy, true, build_context.optimization_level); + lb_populate_function_pass_manager(m, default_function_pass_manager_without_memcpy, true, build_context.optimization_level); LLVMFinalizeFunctionPassManager(default_function_pass_manager_without_memcpy); diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 3b268dffa..fa1c87bf5 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -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); @@ -194,7 +216,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa LLVMAddGlobalDCEPass(mpm); LLVMAddGlobalOptimizerPass(mpm); - // LLVMAddLowerConstantIntrinsicsPass(mpm); + LLVMAddLowerConstantIntrinsicsPass(mpm); LLVMAddLoopRotatePass(mpm); From 8bb6651dda3e239be9ae85eeadf2d189bd478f73 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:08:09 +0100 Subject: [PATCH 054/137] Remove test call for LLVM --- src/llvm_backend_opt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index fa1c87bf5..b40998c5f 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -16,7 +16,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data 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); + // LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback); } } From 20f7e61363db27bc55d652d9d26fb704b8a71237 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:11:59 +0100 Subject: [PATCH 055/137] Revert LLVM-C.lib --- bin/llvm/windows/LLVM-C.lib | Bin 287196 -> 274018 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bin/llvm/windows/LLVM-C.lib b/bin/llvm/windows/LLVM-C.lib index a55bb863cca93ef6b8fa8cbd2de1452744c8cfed..2353c6a39e4b0149ce2c9166eb52ed706efb0ac0 100644 GIT binary patch delta 41661 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 N%slivIk#0b|35K9B>w;a From eac61fb5361a595363684df4737c3d918e9e7b88 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:17:48 +0100 Subject: [PATCH 056/137] Remove `LLVMAddLowerConstantIntrinsicsPass` --- src/llvm_backend_opt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index b40998c5f..6b7ca507a 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -216,7 +216,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa LLVMAddGlobalDCEPass(mpm); LLVMAddGlobalOptimizerPass(mpm); - LLVMAddLowerConstantIntrinsicsPass(mpm); + // LLVMAddLowerConstantIntrinsicsPass(mpm); LLVMAddLoopRotatePass(mpm); From e1c2528d87b182b19311ffad71a060acf218a7a4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:29:27 +0100 Subject: [PATCH 057/137] Remove warning on \*nix --- src/llvm_backend.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d784e36b6..d92df5ae2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -13061,13 +13061,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { void lb_init_module(lbModule *m, Checker *c) { m->info = &c->info; - gbString module_name = gb_string_make(heap_allocator(), "odin_package-"); + gbString module_name = gb_string_make(heap_allocator(), "odin_package"); if (m->pkg) { + module_name = gb_string_appendc(module_name, "-"); module_name = gb_string_append_length(module_name, m->pkg->name.text, m->pkg->name.len); } else if (USE_SEPARTE_MODULES) { - module_name = gb_string_appendc(module_name, "builtin"); - } else { - module_name = "odin_package"; + module_name = gb_string_appendc(module_name, "-builtin"); } m->ctx = LLVMContextCreate(); From d0f7cf74e94741f2820a61ecba60ed5be460a921 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 12:31:56 +0100 Subject: [PATCH 058/137] Add llvm-config version 11 check for Darwin in Makefile --- Makefile | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 6633438bc..5cdf1e467 100644 --- a/Makefile +++ b/Makefile @@ -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 From 8144e82c6c1ffa3406ed8bcd87c15096d6ab4538 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 13:13:34 +0100 Subject: [PATCH 059/137] Fix docs for `-lld` --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 8f13b28f6..0dd1e0c27 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1719,7 +1719,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, ""); From d353f97f91c18fd910c4c16604dcaf3e3afbc5f2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 13:30:27 +0100 Subject: [PATCH 060/137] Add `byval` with `align`, `sret` attributes for SysV --- src/llvm_abi.cpp | 39 +++++++++++++++++++++++++++------------ src/llvm_backend.cpp | 29 +++++++++++++++++++++++++---- src/llvm_backend.hpp | 1 + 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index c2298bcd0..d8f16b451 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -10,21 +10,35 @@ struct lbArgType { LLVMTypeRef cast_type; // Optional LLVMTypeRef pad_type; // Optional LLVMAttributeRef attribute; // Optional + LLVMAttributeRef align_attribute; // Optional }; + +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}; } 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}; +} + +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}; } lbArgType lb_arg_type_ignore(LLVMTypeRef type) { - return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr}; + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr}; } struct lbFunctionType { @@ -121,6 +135,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 +162,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 +343,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_byval(c, t); } } else { args[i] = non_struct(c, t, false); @@ -348,7 +363,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); @@ -512,9 +527,9 @@ namespace lbAbiAmd64SysV { if (is_mem_cls(cls, attribute_kind)) { LLVMAttributeRef attribute = nullptr; if (attribute_kind == Amd64TypeAttribute_ByVal) { - attribute = lb_create_enum_attribute(c, "byval"); + return lb_arg_type_indirect_byval(c, type); } 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 { @@ -814,7 +829,7 @@ namespace lbAbiAmd64SysV { 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, lb_create_enum_attribute_with_type(c, "byval", t)); } } else { args[i] = non_struct(c, t); @@ -834,7 +849,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); @@ -984,7 +999,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); } } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index d92df5ae2..28c37be7d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2545,6 +2545,27 @@ lbValue lb_emit_string(lbProcedure *p, lbValue str_elem, lbValue str_len) { } } +LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type) { + String s = make_string_c(name); + + // NOTE(2021-02-25, bill); All this attributes require a type associated with them + // and the current LLVM C API does not expose this functionality yet. + // It is better to ignore the attributes for the time being + if (s == "byval") { + // return nullptr; + } else if (s == "byref") { + return nullptr; + } else if (s == "preallocated") { + return nullptr; + } else if (s == "sret") { + // return nullptr; + } + + unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); + GB_ASSERT_MSG(kind != 0, "unknown attribute: %s", name); + return LLVMCreateEnumAttribute(ctx, kind, 0); +} + LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value) { String s = make_string_c(name); @@ -2552,13 +2573,13 @@ LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, // and the current LLVM C API does not expose this functionality yet. // It is better to ignore the attributes for the time being if (s == "byval") { - return nullptr; + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } else if (s == "byref") { - return nullptr; + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } else if (s == "preallocated") { - return nullptr; + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } else if (s == "sret") { - return nullptr; + GB_PANIC("lb_create_enum_attribute_with_type should be used for %s", name); } unsigned kind = LLVMGetEnumAttributeKindForName(name, s.len); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f212d9fc0..b35c042ee 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -276,6 +276,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); From e08f39ec28eaf462ddee6c418721ce4c7a03450e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 11 May 2021 16:20:50 +0100 Subject: [PATCH 061/137] Remove unneeded assert --- src/llvm_abi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index d8f16b451..dcbd07118 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -717,7 +717,6 @@ namespace lbAbiAmd64SysV { i += 1; } - GB_ASSERT(types.count != 0); if (types.count == 1) { return types[0]; } From d2fcbf0e1d5488ee3d64f94e488122d7800214eb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 15:00:19 +0100 Subject: [PATCH 062/137] Fix #948 --- src/llvm_backend.cpp | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 28c37be7d..81ccf1659 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10773,42 +10773,22 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri Type *lt = left.type; Type *rt = right.type; - // if (is_type_bit_set(lt) && is_type_bit_set(rt)) { - // Type *blt = base_type(lt); - // Type *brt = base_type(rt); - // i64 bits = gb_max(blt->BitSet.bits, brt->BitSet.bits); - // i64 bytes = bits / 8; - // switch (bytes) { - // case 1: - // left = lb_emit_conv(p, left, t_u8); - // right = lb_emit_conv(p, right, t_u8); - // break; - // case 2: - // left = lb_emit_conv(p, left, t_u16); - // right = lb_emit_conv(p, right, t_u16); - // break; - // case 4: - // left = lb_emit_conv(p, left, t_u32); - // right = lb_emit_conv(p, right, t_u32); - // break; - // case 8: - // left = lb_emit_conv(p, left, t_u64); - // right = lb_emit_conv(p, right, t_u64); - // break; - // default: GB_PANIC("Unknown integer size"); break; - // } - // } - lt = left.type; rt = right.type; i64 ls = type_size_of(lt); i64 rs = type_size_of(rt); + + // NOTE(bill): Quick heuristic, larger types are usually the target type if (ls < rs) { left = lb_emit_conv(p, left, rt); } else if (ls > rs) { right = lb_emit_conv(p, right, lt); } else { - right = lb_emit_conv(p, right, lt); + if (is_type_union(rt)) { + left = lb_emit_conv(p, left, rt); + } else { + right = lb_emit_conv(p, right, lt); + } } } From dfb81431496f68bce56b9a92681a841b072300be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 22:23:43 +0100 Subject: [PATCH 063/137] Fix #651 --- src/check_expr.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 60f07a068..a64acf744 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2380,9 +2380,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 +2398,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; From d962cfdc6bd1865c7df302d08af237027533437f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 22:38:37 +0100 Subject: [PATCH 064/137] Fix #713 --- src/check_expr.cpp | 10 ++++++++-- src/check_type.cpp | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a64acf744..26c7983de 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5572,9 +5572,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; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index b90732e00..89f5ca34c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1271,7 +1271,13 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * check_assignment(ctx, &o, in_type, str_lit("parameter value")); } - if (out_type_) *out_type_ = default_type(o.type); + if (out_type_) { + if (in_type != nullptr) { + *out_type_ = in_type; + } else { + *out_type_ = default_type(o.type); + } + } return param_value; } @@ -1389,6 +1395,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 +1417,12 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } + if (param_value.kind != ParameterValue_Invalid && is_type_polymorphic(type)) { + 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); + } + if (p->flags&FieldFlag_c_vararg) { if (p->type == nullptr || From da9cabc3344900059d0837729e54e09e58089ae9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:02:57 +0100 Subject: [PATCH 065/137] Fix #682 --- src/check_decl.cpp | 13 +++++++++++++ src/check_type.cpp | 10 ---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f43248593..baabe4184 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -376,6 +376,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 +410,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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 89f5ca34c..e433faf7f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2532,16 +2532,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; } From fdd0c726bc1b0ff3978ead267001e81bda736673 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:12:20 +0100 Subject: [PATCH 066/137] Improve error message to "fix" #640 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 26c7983de..ddf2a3aed 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5730,7 +5730,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: From c81f7b31c6253d70ff08f3c4ca346676f4bfbda0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:22:43 +0100 Subject: [PATCH 067/137] Add explicit numbers to `enum AddressingMode` --- src/parser.hpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/parser.hpp b/src/parser.hpp index 2a0b0fa27..982588c0f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -7,22 +7,22 @@ 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 + Addressing_AtomOpAssign = 12, // Specialized for custom atom operations for assignments }; struct TypeAndValue { From 5420cc083d08a4c0faffbcf3ec1deecf05b6265d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:26:21 +0100 Subject: [PATCH 068/137] Implement #807 --- core/fmt/fmt.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index f9c386ffb..3b3716a15 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -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 { From c6c5af527b907036361e9de96d7e1dcdd72ee945 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 12 May 2021 23:43:16 +0100 Subject: [PATCH 069/137] Fix #746 --- src/parser.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 376ac58dc..b24bb8aed 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1994,14 +1994,7 @@ 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") { + } 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); @@ -2046,6 +2039,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,6 +2050,10 @@ 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"); From 083e9e20536fa5438886d8a9756b2a87448a21d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Thu, 13 May 2021 01:25:37 +0200 Subject: [PATCH 070/137] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index df81be2be..263161b46 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,6 +7,8 @@ assignees: '' --- +# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" + **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 [...] From d5c3f996550b75f0da383bfcf7b30dba482cf081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20Hjortsh=C3=B8j?= Date: Thu, 13 May 2021 01:26:15 +0200 Subject: [PATCH 071/137] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 263161b46..33d7e7a71 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,7 +7,7 @@ assignees: '' --- -# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" +# 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 [...] From 2e5f57d8a18352c02da72f2f2385116a099d7942 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 00:44:33 +0100 Subject: [PATCH 072/137] Fix #741 --- src/check_expr.cpp | 4 +++- src/check_type.cpp | 25 +++++++++++-------------- src/llvm_backend.cpp | 5 +++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ddf2a3aed..5784744d9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -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; } } diff --git a/src/check_type.cpp b/src/check_type.cpp index e433faf7f..ef0e20948 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1207,6 +1207,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type * 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 +1219,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 +1231,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) { @@ -1267,10 +1268,6 @@ 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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 81ccf1659..8a9e5d7e2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -6023,7 +6023,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc res.value = llvm_const_array(et, elems, cast(unsigned)count); return res; } - GB_PANIC("HERE!\n"); + GB_PANIC("This should not have happened!\n"); LLVMValueRef data = LLVMConstStringInContext(ctx, cast(char const *)value.value_string.text, @@ -9715,7 +9715,8 @@ lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterVal switch (param_value.kind) { case ParameterValue_Constant: if (is_type_constant_type(parameter_type)) { - return lb_const_value(p->module, parameter_type, param_value.value); + auto res = lb_const_value(p->module, parameter_type, param_value.value); + return res; } else { ExactValue ev = param_value.value; lbValue arg = {}; From be12f12c3c2872fd89021af16c2ab838277c4c4a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 00:57:10 +0100 Subject: [PATCH 073/137] Fix #814 --- src/check_stmt.cpp | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a3c9a529c..9ca53c4fc 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -960,9 +960,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)) { From d4ee1a9e1924778e69214dd24164ef11f3953ac9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 10:07:28 +0100 Subject: [PATCH 074/137] Correct default procedure parameter logic --- src/check_type.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index ef0e20948..0e77792b7 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1254,8 +1254,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 { @@ -1414,10 +1417,19 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is type = t_invalid; } - if (param_value.kind != ParameterValue_Invalid && is_type_polymorphic(type)) { - 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); + 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; + } } From b37d344eb214336036c597250206f36043c75975 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:04:51 +0100 Subject: [PATCH 075/137] Add intrinsics.type_is_variant_of --- core/intrinsics/intrinsics.odin | 1 + src/check_builtin.cpp | 40 +++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ 3 files changed, 45 insertions(+) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index d767f5e47..60b595aab 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -159,6 +159,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 --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 6b299a68a..8932ac914 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2429,6 +2429,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) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 98ef5180b..5b1aecd68 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -197,6 +197,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, @@ -415,6 +417,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}, From 465b6139d55441fbe6da8d2699865c414cd87d3a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:05:23 +0100 Subject: [PATCH 076/137] Temporarily fix syscall in Linux and Freebsd (eventually to be replaced with a proper implementation) --- core/os/os_freebsd.odin | 2 +- core/os/os_linux.odin | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 137c6f864..2afa8bd14 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -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); diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index dd0914f40..7569909d7 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -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 { From b8a35c658cf30a3f4f46ec40b38cdf478224a371 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:06:35 +0100 Subject: [PATCH 077/137] Remove incl/excl usage from demo --- examples/demo/demo.odin | 10 +++++----- examples/demo_insert_semicolon/demo.odin | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 6817b15a4..76e8aa9fc 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -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); } { diff --git a/examples/demo_insert_semicolon/demo.odin b/examples/demo_insert_semicolon/demo.odin index ae677e5a9..3fbae274e 100644 --- a/examples/demo_insert_semicolon/demo.odin +++ b/examples/demo_insert_semicolon/demo.odin @@ -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) } { From 63b54ce7c695fb1e204fe77db029cdebf7206b28 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 13 May 2021 12:48:12 +0100 Subject: [PATCH 078/137] Add minor ignoring hint on type assertions to get better code generation with no optimizations enabled --- src/check_expr.cpp | 20 ++++++++++++++++ src/llvm_backend.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++-- src/parser.hpp | 8 ++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5784744d9..51dad8f79 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3949,6 +3949,16 @@ bool check_assignment_arguments(CheckerContext *ctx, Array 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; @@ -4063,6 +4073,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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8a9e5d7e2..dc36100a4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11119,7 +11119,54 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A return value; } -lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos, bool do_conversion_check=true) { +lbValue lb_emit_union_cast_only_ok_check(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { + GB_ASSERT(is_type_tuple(type)); + lbModule *m = p->module; + + Type *src_type = value.type; + bool is_ptr = is_type_pointer(src_type); + + + // IMPORTANT NOTE(bill): This assumes that the value is completely ignored + // so when it does an assignment, it complete ignores the value. + // Just make it two booleans and ignore the first one + // + // _, ok := x.(T); + // + Type *ok_type = type->Tuple.variables[1]->type; + Type *gen_tuple_types[2] = {}; + gen_tuple_types[0] = ok_type; + gen_tuple_types[1] = ok_type; + + Type *gen_tuple = alloc_type_tuple_from_field_types(gen_tuple_types, gb_count_of(gen_tuple_types), false, true); + + lbAddr v = lb_add_local_generated(p, gen_tuple, false); + + if (is_ptr) { + value = lb_emit_load(p, value); + } + Type *src = base_type(type_deref(src_type)); + GB_ASSERT_MSG(is_type_union(src), "%s", type_to_string(src_type)); + Type *dst = type->Tuple.variables[0]->type; + + lbValue cond = {}; + + if (is_type_union_maybe_pointer(src)) { + lbValue data = lb_emit_transmute(p, value, dst); + cond = lb_emit_comp_against_nil(p, Token_NotEq, data); + } else { + lbValue tag = lb_emit_union_tag_value(p, value); + lbValue dst_tag = lb_const_union_tag(m, src, dst); + cond = lb_emit_comp(p, Token_CmpEq, tag, dst_tag); + } + + lbValue gep1 = lb_emit_struct_ep(p, v.addr, 1); + lb_emit_store(p, gep1, cond); + + return lb_addr_load(p, v); +} + +lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos pos) { lbModule *m = p->module; Type *src_type = value.type; @@ -11183,7 +11230,7 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - if (do_conversion_check) { + { // NOTE(bill): Panic on invalid conversion Type *dst_type = tuple->Tuple.variables[0]->type; @@ -11487,6 +11534,10 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbValue e = lb_build_expr(p, ta->expr); Type *t = type_deref(e.type); if (is_type_union(t)) { + if (ta->ignores[0]) { + // NOTE(bill): This is not needed for optimization levels other than 0 + return lb_emit_union_cast_only_ok_check(p, e, type, pos); + } return lb_emit_union_cast(p, e, type, pos); } else if (is_type_any(t)) { return lb_emit_any_cast(p, e, type, pos); diff --git a/src/parser.hpp b/src/parser.hpp index 982588c0f..b6bf42f63 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -345,7 +345,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 { \ From b01c2e101712cf58e60bfc41d85fc33b60fcf349 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 16:40:40 +0100 Subject: [PATCH 079/137] Disallow slicing of constant values --- src/check_expr.cpp | 13 ++++++++++--- src/llvm_backend.cpp | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 51dad8f79..18f210b63 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7811,10 +7811,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 } @@ -7849,6 +7846,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++) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index dc36100a4..66c45793b 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11360,6 +11360,15 @@ lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { return {}; } +bool lb_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; +} + lbValue lb_build_expr(lbProcedure *p, Ast *expr) { lbModule *m = p->module; @@ -11683,6 +11692,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + if (is_type_slice(type_of_expr(se->expr))) { + // NOTE(bill): Quick optimization + if (se->high == nullptr && + (se->low == nullptr || lb_is_expr_constant_zero(se->low))) { + return lb_build_expr(p, se->expr); + } + } return lb_addr_load(p, lb_build_addr(p, expr)); case_end; @@ -12303,6 +12319,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(se, SliceExpr, expr); + lbValue low = lb_const_int(p->module, t_int, 0); lbValue high = {}; @@ -13268,7 +13285,7 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) { g.type = alloc_type_pointer(type); g.value = LLVMAddGlobal(m->mod, lb_type(m, type), cast(char const *)str); if (value.value != nullptr) { - GB_ASSERT(LLVMIsConstant(value.value)); + GB_ASSERT_MSG(LLVMIsConstant(value.value), LLVMPrintValueToString(value.value)); LLVMSetInitializer(g.value, value.value); } else { LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, type))); From f7b1290fe930099a1250dbde64cc1c081fbd4828 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:02:06 +0100 Subject: [PATCH 080/137] Change `for i in x..y {}` behaviour Adds an extra check before incrementation to prevent the possibility of overflowing of `y` is at the limit maximum size of the integer e.g. `for i in u8(0)..255 {}` (assuming `255` is not known at compile time) --- src/llvm_backend.cpp | 198 ++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 108 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 66c45793b..8d19f6ba7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2971,72 +2971,6 @@ Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { } -lbValue lb_add_param(lbProcedure *p, Entity *e, Ast *expr, Type *abi_type, i32 index) { - lbParamPasskind kind = lbParamPass_Value; - lbValue v = lb_value_param(p, e, abi_type, index, &kind); - array_add(&p->params, v); - - lbValue res = {}; - - switch (kind) { - case lbParamPass_Value: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = v; - if (abi_type == t_llvm_bool) { - x = lb_emit_conv(p, x, t_bool); - } - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Pointer: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_Integer: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue iptr = lb_emit_conv(p, l.addr, alloc_type_pointer(abi_type)); - lb_emit_store(p, iptr, v); - return lb_addr_load(p, l); - } - - case lbParamPass_ConstRef: - lb_add_entity(p->module, e, v); - return lb_emit_load(p, v); - - case lbParamPass_BitCast: { - lbAddr l = lb_add_local(p, e->type, e, false, index); - lbValue x = lb_emit_transmute(p, v, e->type); - lb_addr_store(p, l, x); - return x; - } - case lbParamPass_Tuple: { - lbAddr l = lb_add_local(p, e->type, e, true, index); - Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); - lbValue ptr = lb_emit_transmute(p, l.addr, alloc_type_pointer(st)); - if (abi_type->Tuple.variables.count > 0) { - array_pop(&p->params); - } - for_array(i, abi_type->Tuple.variables) { - Type *t = abi_type->Tuple.variables[i]->type; - GB_ASSERT(!is_type_tuple(t)); - - lbParamPasskind elem_kind = lbParamPass_Value; - lbValue elem = lb_value_param(p, nullptr, t, index+cast(i32)i, &elem_kind); - array_add(&p->params, elem); - - lbValue dst = lb_emit_struct_ep(p, ptr, cast(i32)i); - lb_emit_store(p, dst, elem); - } - return lb_addr_load(p, l); - } - - } - - - GB_PANIC("Unreachable"); - return {}; -} - void lb_start_block(lbProcedure *p, lbBlock *b) { GB_ASSERT(b != nullptr); if (!b->appended) { @@ -4072,38 +4006,31 @@ void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type, } -void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, Type *val_type, - lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { +void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, + AstRangeStmt *rs, Scope *scope) { + bool ADD_EXTRA_WRAPPING_CHECK = true; + lbModule *m = p->module; - // TODO(bill): How should the behaviour work for lower and upper bounds checking for iteration? - // If 'lower' is changed, should 'val' do so or is that not typical behaviour? + lb_open_scope(p, scope); - lbValue lower = lb_build_expr(p, node->left); - lbValue upper = {}; - - lbValue val = {}; - lbValue idx = {}; - lbBlock *loop = nullptr; - lbBlock *done = nullptr; - lbBlock *body = nullptr; - - if (val_type == nullptr) { - val_type = lower.type; + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) { + val0_type = type_of_expr(rs->vals[0]); + } + if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) { + val1_type = type_of_expr(rs->vals[1]); } - lbAddr value = lb_add_local_generated(p, val_type, false); - lb_addr_store(p, value, lower); - - lbAddr index = lb_add_local_generated(p, t_int, false); - lb_addr_store(p, index, lb_const_int(m, t_int, 0)); - - loop = lb_create_block(p, "for.interval.loop"); - lb_emit_jump(p, loop); - lb_start_block(p, loop); - - body = lb_create_block(p, "for.interval.body"); - done = lb_create_block(p, "for.interval.done"); + if (val0_type != nullptr) { + Entity *e = entity_of_node(rs->vals[0]); + lb_add_local(p, e->type, e, true); + } + if (val1_type != nullptr) { + Entity *e = entity_of_node(rs->vals[1]); + lb_add_local(p, e->type, e, true); + } TokenKind op = Token_Lt; switch (node->op.kind) { @@ -4112,23 +4039,71 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, Type *val_type default: GB_PANIC("Invalid interval operator"); break; } - upper = lb_build_expr(p, node->right); + lbValue lower = lb_build_expr(p, node->left); + lbValue upper = {}; // initialized each time in the loop + lbAddr value = lb_add_local_generated(p, val0_type ? val0_type : lower.type, false); + lb_addr_store(p, value, lower); + + lbAddr index = lb_add_local_generated(p, t_int, false); + lb_addr_store(p, index, lb_const_int(m, t_int, 0)); + + lbBlock *loop = lb_create_block(p, "for.interval.loop"); + lbBlock *body = lb_create_block(p, "for.interval.body"); + lbBlock *done = lb_create_block(p, "for.interval.done"); + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + upper = lb_build_expr(p, node->right); lbValue curr_value = lb_addr_load(p, value); lbValue cond = lb_emit_comp(p, op, curr_value, upper); lb_emit_if(p, cond, body, done); lb_start_block(p, body); - val = lb_addr_load(p, value); - idx = lb_addr_load(p, index); + lbValue val = lb_addr_load(p, value); + lbValue idx = lb_addr_load(p, index); + if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val); + if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], idx); - lb_emit_increment(p, value.addr); - lb_emit_increment(p, index.addr); + { + // NOTE: this check block will most likely be optimized out, and is here + // to make this code easier to read + lbBlock *check = nullptr; + lbBlock *post = lb_create_block(p, "for.interval.post"); - if (val_) *val_ = val; - if (idx_) *idx_ = idx; - if (loop_) *loop_ = loop; - if (done_) *done_ = done; + lbBlock *continue_block = post; + + if (ADD_EXTRA_WRAPPING_CHECK && + op == Token_LtEq) { + check = lb_create_block(p, "for.interval.check"); + continue_block = check; + } + + lb_push_target_list(p, rs->label, done, continue_block, nullptr); + + lb_build_stmt(p, rs->body); + + lb_close_scope(p, lbDeferExit_Default, nullptr); + lb_pop_target_list(p); + + if (check != nullptr) { + lb_emit_jump(p, check); + lb_start_block(p, check); + + lbValue check_cond = lb_emit_comp(p, Token_NotEq, curr_value, upper); + lb_emit_if(p, check_cond, post, done); + } else { + lb_emit_jump(p, post); + } + + lb_start_block(p, post); + lb_emit_increment(p, value.addr); + lb_emit_increment(p, index.addr); + lb_emit_jump(p, loop); + } + + lb_start_block(p, done); } void lb_build_range_enum(lbProcedure *p, Type *enum_type, Type *val_type, lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) { @@ -4283,6 +4258,11 @@ void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs, Scope *sco void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { Ast *expr = unparen_expr(rs->expr); + if (is_ast_range(expr)) { + lb_build_range_interval(p, &expr->BinaryExpr, rs, scope); + return; + } + Type *expr_type = type_of_expr(expr); if (expr_type != nullptr) { Type *et = base_type(type_deref(expr_type)); @@ -4319,10 +4299,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { bool is_map = false; TypeAndValue tav = type_and_value_of_expr(expr); - - if (is_ast_range(expr)) { - lb_build_range_interval(p, &expr->BinaryExpr, val0_type, &val, &key, &loop, &done); - } else if (tav.mode == Addressing_Type) { + if (tav.mode == Addressing_Type) { lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done); } else { Type *expr_type = type_of_expr(expr); @@ -7824,10 +7801,13 @@ lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p GB_ASSERT(pt->kind == Type_Proc); GB_ASSERT(pt->Proc.calling_convention == ProcCC_Odin); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); + String name = str_lit("__.context_ptr"); + + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; LLVMValueRef context_ptr = LLVMGetParam(p->value, LLVMCountParams(p->value)-1); + LLVMSetValueName2(context_ptr, cast(char const *)name.text, name.len); context_ptr = LLVMBuildPointerCast(p->builder, context_ptr, lb_type(p->module, e->type), ""); lbValue param = {context_ptr, e->type}; @@ -14241,8 +14221,10 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) { // initialize `runtime.args__` lbValue argc = {LLVMGetParam(p->value, 0), t_i32}; - argc = lb_emit_conv(p, argc, t_int); lbValue argv = {LLVMGetParam(p->value, 1), t_ptr_cstring}; + LLVMSetValueName2(argc.value, "argc", 4); + LLVMSetValueName2(argv.value, "argv", 4); + argc = lb_emit_conv(p, argc, t_int); lbAddr args = lb_addr(lb_find_runtime_value(p->module, str_lit("args__"))); lb_fill_slice(p, args, argv, argc); } From 0507b9ebb78e1239501a3153315bd4fd195a88a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:40:06 +0100 Subject: [PATCH 081/137] Fix #987 --- src/check_expr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 18f210b63..8cc5aa894 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1132,7 +1132,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)); } From 5ae564cc8c6934675c25cb251e753103e4549c52 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:53:52 +0100 Subject: [PATCH 082/137] Add name to aggregate result pointer to procedures --- src/llvm_backend.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8d19f6ba7..ce633b127 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3184,11 +3184,15 @@ void lb_begin_procedure_body(lbProcedure *p) { lbValue return_ptr_value = {}; if (ft->ret.kind == lbArg_Indirect) { // NOTE(bill): this must be parameter 0 + + String name = str_lit("agg.result"); + Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results)); - Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); + Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; return_ptr_value.value = LLVMGetParam(p->value, 0); + LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len); return_ptr_value.type = ptr_type; p->return_ptr = lb_addr(return_ptr_value); From 7b7081d60733caa996a89be2651482a2aeed8bbd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 18:59:54 +0100 Subject: [PATCH 083/137] Remove old dead code --- src/check_decl.cpp | 11 --- src/check_expr.cpp | 72 ---------------- src/check_stmt.cpp | 47 ---------- src/check_type.cpp | 13 --- src/checker.cpp | 200 ------------------------------------------- src/checker.hpp | 1 - src/llvm_backend.cpp | 51 ----------- src/parser.hpp | 1 - src/types.cpp | 19 ---- 9 files changed, 415 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index baabe4184..5e8e79791 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -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; - } - } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8cc5aa894..fb3a51415 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7573,47 +7573,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(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); @@ -7752,37 +7711,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(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; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 9ca53c4fc..a954b44b6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1489,53 +1489,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { auto lhs_to_ignore = array_make(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(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; diff --git a/src/check_type.cpp b/src/check_type.cpp index 0e77792b7..cdce6dae8 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -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, diff --git a/src/checker.cpp b/src/checker.cpp index 8016f1020..21ca4c398 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2666,206 +2666,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; } diff --git a/src/checker.hpp b/src/checker.hpp index 41b6e8c42..38628ed51 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -114,7 +114,6 @@ struct AttributeContext { String deprecated_message; DeferredProcedure deferred_procedure; u32 optimization_mode; // ProcedureOptimizationMode - struct TypeAtomOpTable *atom_op_table; }; AttributeContext make_attribute_context(String link_prefix) { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ce633b127..240e33c80 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12147,27 +12147,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { return lb_addr(val); } - if (!is_type_indexable(t)) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found != nullptr) { - if (found->kind == TypeAtomOp_index_get) { - return lb_build_addr(p, found->node); - } else if (found->kind == TypeAtomOp_index_get_ptr) { - return lb_addr(lb_build_expr(p, found->node)); - } else if (found->kind == TypeAtomOp_index_set) { - lbValue ptr = lb_build_addr_ptr(p, ie->expr); - if (deref) { - ptr = lb_emit_load(p, ptr); - } - - lbAddr addr = {lbAddr_AtomOp_index_set}; - addr.addr = ptr; - addr.index_set.index = lb_build_expr(p, ie->index); - addr.index_set.node = found->node; - return addr; - } - } - } GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr)); if (is_type_map(t)) { @@ -12312,36 +12291,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { bool no_indices = se->low == nullptr && se->high == nullptr; - { - Type *type = base_type(type_of_expr(se->expr)); - if (type->kind == Type_Struct && !is_type_soa_struct(type)) { - TypeAtomOpTable *atom_op_table = type->Struct.atom_op_table; - if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) { - AtomOpMapEntry *found = map_get(&p->module->info->atom_op_map, hash_pointer(expr)); - if (found) { - lbValue base = lb_build_expr(p, found->node); - - Type *slice_type = base.type; - lbValue len = lb_slice_len(p, base); - if (high.value == nullptr) high = len; - - if (!no_indices) { - lb_emit_slice_bounds_check(p, se->open, low, high, len, se->low != nullptr); - } - - - lbValue elem = lb_emit_ptr_offset(p, lb_slice_elem(p, base), low); - lbValue new_len = lb_emit_arith(p, Token_Sub, high, low, t_int); - - lbAddr slice = lb_add_local_generated(p, slice_type, false); - lb_fill_slice(p, slice, elem, new_len); - return slice; - } - } - } - } - - lbAddr addr = lb_build_addr(p, se->expr); lbValue base = lb_addr_load(p, addr); Type *type = base_type(base.type); diff --git a/src/parser.hpp b/src/parser.hpp index b6bf42f63..77d5f1b02 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -22,7 +22,6 @@ enum AddressingMode { 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 = 12, // Specialized for custom atom operations for assignments }; struct TypeAndValue { diff --git a/src/types.cpp b/src/types.cpp index ffdb90c03..8a78e08d1 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -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 fields; Array 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; From 5e31c04a01e5fa1d11c5a72b684263005451980a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:13:34 +0100 Subject: [PATCH 084/137] Disallow duplicate unary operators for `+`, `-`, and `~` --- src/parser.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index b24bb8aed..f11735af6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2754,16 +2754,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_Add: + case Token_Sub: + case Token_Xor: { + Token token = advance_token(f); + Ast *expr = parse_unary_expr(f, lhs); + if (expr != nullptr && expr->kind == Ast_UnaryExpr) { + if (expr->UnaryExpr.op.kind == token.kind) { + syntax_error(expr, "Duplicate unary operator '%.*s' will produce a redundant no-op", LIT(token.string)); + } + } + return ast_unary_expr(f, token, expr); + } + case Token_Period: { Token token = expect_token(f, Token_Period); Ast *ident = parse_ident(f); From 5d03bc61b870d525536290627044e9638c3495b9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:34:46 +0100 Subject: [PATCH 085/137] Tokenize `++` and `--` as tokens but disallow them in the parser, and give better error messages for they are used as operators/statements --- src/parser.cpp | 33 +++++++++++++++++++++++++-------- src/tokenizer.cpp | 14 +++++++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index f11735af6..0dae732e5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2724,6 +2724,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; @@ -2754,6 +2764,10 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_auto_cast(f, token, expr); } + + case Token_Add: + case Token_Sub: + case Token_Xor: case Token_And: case Token_Not: { Token token = advance_token(f); @@ -2761,19 +2775,15 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) { return ast_unary_expr(f, token, expr); } - case Token_Add: - case Token_Sub: - case Token_Xor: { + 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); - if (expr != nullptr && expr->kind == Ast_UnaryExpr) { - if (expr->UnaryExpr.op.kind == token.kind) { - syntax_error(expr, "Duplicate unary operator '%.*s' will produce a redundant no-op", LIT(token.string)); - } - } return ast_unary_expr(f, token, expr); } + case Token_Period: { Token token = expect_token(f, Token_Period); Ast *ident = parse_ident(f); @@ -3163,6 +3173,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 diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 081ef6443..3410517bc 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -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, "=="), \ @@ -1287,6 +1289,9 @@ 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; } break; case '-': @@ -1298,7 +1303,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 == '>') { + } else if (t->curr_rune == '-') { + advance_to_next_rune(t); + token->kind = Token_Decrement; + }else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight; } From 0ad599675e4be36a4548b61f5458edef65aeeef3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:47:00 +0100 Subject: [PATCH 086/137] Improve semicolon insertion rule for dummy tokens `++` and `--` --- src/tokenizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3410517bc..b3d3f660e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1292,6 +1292,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { } else if (t->curr_rune == '+') { advance_to_next_rune(t); token->kind = Token_Increment; + insert_semicolon = true; } break; case '-': @@ -1306,6 +1307,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { } 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; From 7886798156ff67a4ab20349a7c789efcc981c4ef Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 19:49:48 +0100 Subject: [PATCH 087/137] Add space --- src/tokenizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index b3d3f660e..5936ac3fe 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1308,7 +1308,7 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { advance_to_next_rune(t); token->kind = Token_Decrement; insert_semicolon = true; - }else if (t->curr_rune == '>') { + } else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight; } From 1cf6b6679dd7dfd4e00cb55891970ce68f59c67d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 21:10:06 +0100 Subject: [PATCH 088/137] Add custom basic dead instruction elimination pass --- src/llvm_backend.cpp | 28 ++++++------ src/llvm_backend_opt.cpp | 93 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 240e33c80..7bdc8b04a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3318,9 +3318,11 @@ void lb_end_procedure_body(lbProcedure *p) { LLVMBuildBr(p->builder, p->entry_block->block); LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block); + LLVMValueRef instr = nullptr; + // Make sure there is a "ret void" at the end of a procedure with no return type if (p->type->Proc.result_count == 0) { - LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + instr = LLVMGetLastInstruction(p->curr_block->block); if (!lb_is_instr_terminating(instr)) { lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); LLVMBuildRetVoid(p->builder); @@ -3332,7 +3334,7 @@ void lb_end_procedure_body(lbProcedure *p) { // Make sure every block terminates, and if not, make it unreachable for (block = first_block; block != nullptr; block = LLVMGetNextBasicBlock(block)) { - LLVMValueRef instr = LLVMGetLastInstruction(block); + instr = LLVMGetLastInstruction(block); if (instr == nullptr || !lb_is_instr_terminating(instr)) { LLVMPositionBuilderAtEnd(p->builder, block); LLVMBuildUnreachable(p->builder); @@ -14045,7 +14047,7 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) { LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14134,7 +14136,7 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14251,7 +14253,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) LLVMVerifyFunction(p->value, LLVMAbortProcessAction); } - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); return p; } @@ -14376,26 +14378,26 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { lbProcedure *p = m->procedures_to_generate[i]; if (p->body != nullptr) { // Build Procedure if (p->flags & lbProcedureFlag_WithoutMemcpyPass) { - LLVMRunFunctionPassManager(default_function_pass_manager_without_memcpy, p->value); + lb_run_function_pass_manager(default_function_pass_manager_without_memcpy, p); } else { if (p->entity && p->entity->kind == Entity_Procedure) { switch (p->entity->Procedure.optimization_mode) { case ProcedureOptimizationMode_None: case ProcedureOptimizationMode_Minimal: - LLVMRunFunctionPassManager(function_pass_manager_minimal, p->value); + lb_run_function_pass_manager(function_pass_manager_minimal, p); break; case ProcedureOptimizationMode_Size: - LLVMRunFunctionPassManager(function_pass_manager_size, p->value); + lb_run_function_pass_manager(function_pass_manager_size, p); break; case ProcedureOptimizationMode_Speed: - LLVMRunFunctionPassManager(function_pass_manager_speed, p->value); + lb_run_function_pass_manager(function_pass_manager_speed, p); break; default: - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); break; } } else { - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } } } @@ -14403,11 +14405,11 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) { for_array(i, m->equal_procs.entries) { lbProcedure *p = m->equal_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } for_array(i, m->hasher_procs.entries) { lbProcedure *p = m->hasher_procs.entries[i].value; - LLVMRunFunctionPassManager(default_function_pass_manager, p->value); + lb_run_function_pass_manager(default_function_pass_manager, p); } return 0; diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index 6b7ca507a..fa9d3b456 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -248,3 +248,96 @@ 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; + // 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; + /**/) { + 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 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: + 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); +} From fffb83282b4e725152476ceb54a19d770e93a83e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 21:24:17 +0100 Subject: [PATCH 089/137] Add missing instructions to pass --- src/llvm_backend_opt.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index fa9d3b456..c4eecdb5d 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -318,6 +318,8 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { case LLVMFCmp: case LLVMSelect: case LLVMExtractElement: + case LLVMShuffleVector: + case LLVMExtractValue: removal_count += 1; LLVMInstructionEraseFromParent(curr_instr); was_dead_instructions = true; From b1cfeb6c95066e0f50d43bcd44f846e1f28f61ce Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 May 2021 22:14:20 +0100 Subject: [PATCH 090/137] Add missing instruction to pass --- src/llvm_backend_opt.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp index c4eecdb5d..213005f8f 100644 --- a/src/llvm_backend_opt.cpp +++ b/src/llvm_backend_opt.cpp @@ -253,6 +253,7 @@ 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; @@ -267,6 +268,10 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { for (LLVMValueRef instr = LLVMGetLastInstruction(block); instr != nullptr; /**/) { + if (pass_count == 0) { + original_instruction_count += 1; + } + LLVMValueRef curr_instr = instr; instr = LLVMGetPreviousInstruction(instr); @@ -280,6 +285,7 @@ void lb_run_remove_dead_instruction_pass(lbProcedure *p) { // 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: From 85e5be03d1d8660bbd4025c278bb59897cb153c9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 00:25:47 +0100 Subject: [PATCH 091/137] Redesign `os2.Error` to work with the new extended `union` behaviour --- core/os/os2/errors.odin | 65 +++++++++--------------------- core/os/os2/file_stream.odin | 20 ++------- core/os/os2/file_util.odin | 1 + core/os/os2/file_windows.odin | 20 ++++----- core/os/os2/pipe_windows.odin | 2 +- core/os/os2/stat_windows.odin | 6 +-- core/os/os2/temp_file_windows.odin | 4 +- 7 files changed, 39 insertions(+), 79 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 00cd600a8..2fc49deed 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -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< (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"; diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 6877faea4..208b2323b 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -10,23 +10,11 @@ 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; + err, ok := ferr.(io.Error); + if !ok { + err = .Unknown; } - return .Unknown; + return err; } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 435eba3ab..db6842cf8 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -1,6 +1,7 @@ package os2 import "core:mem" +import "core:io" import "core:strconv" import "core:unicode/utf8" diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 97fe6b3d9..5e87d80a4 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -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; } diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index 68adb6c3b..04750bf88 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -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; } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index ed739b894..48811340a 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -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); diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 19dca1b04..dd050ab48 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -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 { From 24c89b3eeed9d36fd6adfdc2f4a412b39fc59c6b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 00:29:22 +0100 Subject: [PATCH 092/137] Minor change --- core/os/os2/file_stream.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 208b2323b..52f5b30e9 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -10,6 +10,9 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) { @(private) error_to_io_error :: proc(ferr: Error) -> io.Error { + if ferr == nil { + return .None; + } err, ok := ferr.(io.Error); if !ok { err = .Unknown; From ce08e832f7dcdeeae37cf0e432648efa2f27f2a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:34:35 +0100 Subject: [PATCH 093/137] Allow `..=` alongside `..` as a "full range" operator; Update `core:odin/parser` etc --- core/odin/parser/parser.odin | 66 +++++++++++++++++++++++++++--- core/odin/tokenizer/token.odin | 2 + core/odin/tokenizer/tokenizer.odin | 3 ++ src/check_expr.cpp | 7 ++-- src/check_stmt.cpp | 1 + src/check_type.cpp | 1 + src/llvm_backend.cpp | 6 ++- src/parser.cpp | 18 ++++---- src/tokenizer.cpp | 4 ++ 9 files changed, 89 insertions(+), 19 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 160eb3a5c..661c46751 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -12,6 +12,10 @@ 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 + optional_semicolons: bool, + warn: Warning_Handler, err: Error_Handler, @@ -128,6 +132,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.line_comment = nil; } + if p.optional_semicolons { + 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 +408,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { if node == nil { return false; } + + if p.optional_semicolons { + return true; + } + switch n in node.derived { case ast.Empty_Stmt, ast.Block_Stmt: return true; @@ -439,14 +452,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 !p.optional_semicolons && .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 +648,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 +701,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 +761,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 +865,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 +876,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 +1004,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 +1334,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; } @@ -2233,6 +2280,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 { @@ -2405,6 +2454,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); @@ -2473,6 +2523,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 +2554,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 +2654,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 +2865,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 +2873,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: diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 1b37bae23..88908d7f8 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -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 { ",", "..", "..<", + "..=", "\\", "", diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 58f546191..297f42e3d 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -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; } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index fb3a51415..61cfd7d6e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5940,8 +5940,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); @@ -5952,7 +5953,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); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a954b44b6..2c9d8e00e 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -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; } diff --git a/src/check_type.cpp b/src/check_type.cpp index cdce6dae8..24a3e0a59 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -931,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)); } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7bdc8b04a..41b1e8fc9 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4041,6 +4041,7 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node, TokenKind op = Token_Lt; switch (node->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } @@ -4454,7 +4455,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s ExactValue start = start_expr->tav.value; ExactValue end = end_expr->tav.value; - if (op == Token_Ellipsis) { // .. [start, end] + if (op != Token_RangeHalf) { // .. [start, end] (or ..=) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_LtEq, val, end); @@ -4465,7 +4466,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s lb_build_stmt(p, rs->body); } - } else if (op == Token_RangeHalf) { // ..< [start, end) + } else { // ..< [start, end) ExactValue index = exact_value_i64(0); for (ExactValue val = start; compare_exact_values(Token_Lt, val, end); @@ -4632,6 +4633,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) { TokenKind op = Token_Invalid; switch (ie->op.kind) { case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeFull: op = Token_LtEq; break; case Token_RangeHalf: op = Token_Lt; break; default: GB_PANIC("Invalid interval operator"); break; } diff --git a/src/parser.cpp b/src/parser.cpp index 0dae732e5..2e27b8698 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1344,6 +1344,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 +1575,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) { @@ -2315,7 +2320,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; @@ -2675,6 +2680,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: @@ -2686,6 +2692,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 */ @@ -2812,6 +2819,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; @@ -3926,12 +3934,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) { @@ -4052,7 +4054,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 stmts = parse_stmt_list(f); return ast_case_clause(f, token, list, stmts); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 5936ac3fe..d1310b56e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -76,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, ""), \ @@ -1204,6 +1205,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); From df3512b1120dc17a01f8cfebd1f2f9042acdb9b5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:38:27 +0100 Subject: [PATCH 094/137] Make `core:odin` use a `string` for the source rather than `[]byte` --- core/odin/ast/ast.odin | 2 +- core/odin/parser/parse_files.odin | 2 +- core/odin/tokenizer/tokenizer.odin | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 0d015f9bb..cf2cdeacc 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -69,7 +69,7 @@ File :: struct { pkg: ^Package, fullpath: string, - src: []byte, + src: string, docs: ^Comment_Group, diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin index 99275777c..f622c9781 100644 --- a/core/odin/parser/parse_files.odin +++ b/core/odin/parser/parse_files.odin @@ -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; } diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 297f42e3d..f13dd5b7a 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -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 { From 9ccdc40f65542702d5a4603f34a468435d97c352 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:43:35 +0100 Subject: [PATCH 095/137] Make `.Optional_Semicolons` a flag for the parser --- core/odin/parser/parser.odin | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 661c46751..f8373eabe 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -8,13 +8,20 @@ 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 + // If .Optional_Semicolons is true, semicolons are completely as statement terminators // different to .Insert_Semicolon in tok.flags - optional_semicolons: bool, + flags: Flags, warn: Warning_Handler, err: Error_Handler, @@ -104,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, }; @@ -132,7 +140,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.line_comment = nil; } - if p.optional_semicolons { + if .Optional_Semicolons in p.flags { p.tok.flags += {.Insert_Semicolon}; } @@ -409,7 +417,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { return false; } - if p.optional_semicolons { + if .Optional_Semicolons in p.flags { return true; } @@ -453,7 +461,7 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { } expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) { - if !p.optional_semicolons && .Insert_Semicolon in p.tok.flags && token.text == "\n" { + 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: From 385385364b3d19c6bac2ee6184da1f0db38e9099 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:46:25 +0100 Subject: [PATCH 096/137] Fix #988 --- src/check_expr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 61cfd7d6e..ecffe7510 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1115,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; } From 50369cf19ca6aceb342e4688aee1d1903d7a1bb8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 12:48:36 +0100 Subject: [PATCH 097/137] Update LICENSE year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e9e75e569..8ee9b17d6 100644 --- a/LICENSE +++ b/LICENSE @@ -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: From 2e633f57a065b09c9b0c7ad16f393762475a308a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 13:00:16 +0100 Subject: [PATCH 098/137] Add concrete type information for untyped values as procedure arguments --- src/check_expr.cpp | 6 ++++++ src/llvm_backend.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ecffe7510..4a37b2903 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4282,6 +4282,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; @@ -4335,6 +4337,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)) { @@ -4557,6 +4561,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; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 41b1e8fc9..c46c4fd85 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -11386,6 +11386,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { if (tv.value.kind != ExactValue_Invalid) { // NOTE(bill): Short on constant values + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s", type_to_string(tv.type), token_pos_to_string(expr_pos)); return lb_const_value(p->module, tv.type, tv.value); } From 6ef96d33003d2fbdedd283ea432e70afc2f1d7ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 16 May 2021 14:44:02 +0100 Subject: [PATCH 099/137] Improve untyped to typed logic for aiding the backend --- src/check_expr.cpp | 23 +++++++++++++++-- src/check_stmt.cpp | 6 ++++- src/llvm_backend.cpp | 60 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4a37b2903..68f05084e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2216,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; } @@ -2886,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); @@ -3200,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) { @@ -4108,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)); @@ -4299,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) { @@ -4347,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); } } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c9d8e00e..ee60a4acd 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1621,7 +1621,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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c46c4fd85..a15b6a172 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3409,6 +3409,20 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals LLVMBuildCondBr(p->builder, cv, true_block->block, false_block->block); } +bool lb_is_expr_untyped_const(Ast *expr) { + auto const &tv = type_and_value_of_expr(expr); + if (is_type_untyped(tv.type)) { + return tv.value.kind != ExactValue_Invalid; + } + return false; +} + +lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) { + GB_ASSERT(is_type_typed(t)); + auto const &tv = type_and_value_of_expr(expr); + return lb_const_value(m, t, tv.value); +} + lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *false_block) { GB_ASSERT(cond != nullptr); GB_ASSERT(true_block != nullptr); @@ -3440,8 +3454,13 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f case_end; } - lbValue v = lb_build_expr(p, cond); - // v = lb_emit_conv(p, v, t_bool); + lbValue v = {}; + if (lb_is_expr_untyped_const(cond)) { + v = lb_expr_untyped_const_to_typed(p->module, cond, t_llvm_bool); + } else { + v = lb_build_expr(p, cond); + } + v = lb_emit_conv(p, v, t_llvm_bool); lb_emit_if(p, v, true_block, false_block); @@ -4872,6 +4891,9 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast if (done->preds.count == 0) { lb_start_block(p, rhs); + if (lb_is_expr_untyped_const(right)) { + return lb_expr_untyped_const_to_typed(m, right, type); + } return lb_build_expr(p, right); } @@ -4886,7 +4908,12 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast } lb_start_block(p, rhs); - lbValue edge = lb_build_expr(p, right); + lbValue edge = {}; + if (lb_is_expr_untyped_const(right)) { + edge = lb_expr_untyped_const_to_typed(m, right, type); + } else { + edge = lb_build_expr(p, right); + } incoming_values[done->preds.count] = edge.value; incoming_blocks[done->preds.count] = p->curr_block->block; @@ -7010,15 +7037,29 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) { case Token_And: case Token_Or: case Token_Xor: - case Token_AndNot: - case Token_Shl: - case Token_Shr: { + case Token_AndNot: { Type *type = default_type(tv.type); lbValue left = lb_build_expr(p, be->left); lbValue right = lb_build_expr(p, be->right); return lb_emit_arith(p, be->op.kind, left, right, type); } + case Token_Shl: + case Token_Shr: { + lbValue left, right; + Type *type = default_type(tv.type); + left = lb_build_expr(p, be->left); + + if (lb_is_expr_untyped_const(be->right)) { + // NOTE(bill): RHS shift operands can still be untyped + // Just bypass the standard lb_build_expr + right = lb_expr_untyped_const_to_typed(p->module, be->right, type); + } else { + right = lb_build_expr(p, be->right); + } + return lb_emit_arith(p, be->op.kind, left, right, type); + } + case Token_CmpEq: case Token_NotEq: case Token_Lt: @@ -11385,8 +11426,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { GB_ASSERT_MSG(tv.mode != Addressing_Invalid, "invalid expression '%s' (tv.mode = %d, tv.type = %s) @ %s\n Current Proc: %.*s : %s", expr_to_string(expr), tv.mode, type_to_string(tv.type), token_pos_to_string(expr_pos), LIT(p->name), type_to_string(p->type)); if (tv.value.kind != ExactValue_Invalid) { + // NOTE(bill): The commented out code below is just for debug purposes only + // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s\n%s", type_to_string(tv.type), token_pos_to_string(expr_pos), expr_to_string(expr)); + // if (is_type_untyped(tv.type)) { + // gb_printf_err("%s %s\n", token_pos_to_string(expr_pos), expr_to_string(expr)); + // } + // NOTE(bill): Short on constant values - // GB_ASSERT_MSG(!is_type_untyped(tv.type), "%s @ %s", type_to_string(tv.type), token_pos_to_string(expr_pos)); return lb_const_value(p->module, tv.type, tv.value); } From 8d044fd442f2f8fef678c2aef4b4de733b817f1c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 17 May 2021 17:08:25 +0100 Subject: [PATCH 100/137] Minor ABI change and cleanup --- src/llvm_abi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index dcbd07118..aba85ae83 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -343,7 +343,7 @@ namespace lbAbi386 { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect_byval(c, t); + args[i] = lb_arg_type_indirect(t, nullptr); } } else { args[i] = non_struct(c, t, false); @@ -828,7 +828,7 @@ namespace lbAbiAmd64SysV { if (sz == 0) { args[i] = lb_arg_type_ignore(t); } else { - args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute_with_type(c, "byval", t)); + args[i] = lb_arg_type_indirect_byval(c, t); } } else { args[i] = non_struct(c, t); From e0225c3579147557ad4fe2241d8fbe61851a6389 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:32:41 +0100 Subject: [PATCH 101/137] Add `intrinsics.sqrt` for floating-point values --- core/runtime/internal.odin | 17 ++++++----------- src/check_builtin.cpp | 28 ++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend.cpp | 22 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 0e128567a..b95f3e64d 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -409,11 +409,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 +440,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); } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 8932ac914..54cd21582 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2026,6 +2026,34 @@ 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_atomic_fence: case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_rel: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5b1aecd68..cb864f37d 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -56,6 +56,8 @@ enum BuiltinProcId { BuiltinProc_overflow_sub, BuiltinProc_overflow_mul, + BuiltinProc_sqrt, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -278,6 +280,8 @@ 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("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a15b6a172..9dfa123a2 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9429,6 +9429,28 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_sqrt: + { + Type *type = tv.type; + + lbValue x = lb_build_expr(p, ce->args[0]); + x = lb_emit_conv(p, x, type); + + char const *name = "llvm.sqrt"; + LLVMTypeRef types[1] = {lb_type(p->module, type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[1] = {}; + args[0] = x.value; + + lbValue res = {}; + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + res.type = type; + return res; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); From e82e4398b65de36bf68e9f61d634165642b03af1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:50:02 +0100 Subject: [PATCH 102/137] Add `intrinsics.mem_copy` and `intrinsics.mem_copy_non_overlapping` --- core/runtime/internal.odin | 24 +++------------- src/check_builtin.cpp | 53 +++++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 6 ++++ src/llvm_backend.cpp | 38 +++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 20 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index b95f3e64d..8a7b22ca4 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -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; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 54cd21582..c9b911f92 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2053,6 +2053,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } 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_atomic_fence: case BuiltinProc_atomic_fence_acq: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index cb864f37d..b69bacd30 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -58,6 +58,9 @@ enum BuiltinProcId { BuiltinProc_sqrt, + BuiltinProc_mem_copy, + BuiltinProc_mem_copy_non_overlapping, + BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -282,6 +285,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9dfa123a2..b6d1990c3 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9451,6 +9451,44 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return res; } + case BuiltinProc_mem_copy: + case BuiltinProc_mem_copy_non_overlapping: + { + + + lbValue dst = lb_build_expr(p, ce->args[0]); + lbValue src = lb_build_expr(p, ce->args[1]); + lbValue len = lb_build_expr(p, ce->args[2]); + dst = lb_emit_conv(p, dst, t_rawptr); + src = lb_emit_conv(p, src, t_rawptr); + len = lb_emit_conv(p, len, t_int); + + char const *name = nullptr; + switch (id) { + case BuiltinProc_mem_copy: name = "llvm.memmove"; break; + case BuiltinProc_mem_copy_non_overlapping: name = "llvm.memcpy"; break; + } + + LLVMTypeRef types[3] = { + lb_type(p->module, t_rawptr), + lb_type(p->module, t_rawptr), + lb_type(p->module, t_int) + }; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]), LLVMPrintTypeToString(types[2])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[4] = {}; + args[0] = dst.value; + args[1] = src.value; + args[2] = len.value; + args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, false); // is_volatile parameter + + LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + + return {}; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); From a580cdbe7b61e9b2d9d7cb7e31dcb83cce88ae1e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:50:27 +0100 Subject: [PATCH 103/137] Delete core:sync/sync2/channel* stuff (for the time being) --- core/sync/sync2/channel.odin | 886 --------------------------- core/sync/sync2/channel_unix.odin | 17 - core/sync/sync2/channel_windows.odin | 34 - 3 files changed, 937 deletions(-) delete mode 100644 core/sync/sync2/channel.odin delete mode 100644 core/sync/sync2/channel_unix.odin delete mode 100644 core/sync/sync2/channel_windows.odin diff --git a/core/sync/sync2/channel.odin b/core/sync/sync2/channel.odin deleted file mode 100644 index fc30d8280..000000000 --- a/core/sync/sync2/channel.odin +++ /dev/null @@ -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; -} - diff --git a/core/sync/sync2/channel_unix.odin b/core/sync/sync2/channel_unix.odin deleted file mode 100644 index 7429b67db..000000000 --- a/core/sync/sync2/channel_unix.odin +++ /dev/null @@ -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 -} diff --git a/core/sync/sync2/channel_windows.odin b/core/sync/sync2/channel_windows.odin deleted file mode 100644 index e365506c8..000000000 --- a/core/sync/sync2/channel_windows.odin +++ /dev/null @@ -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); - } -} From 10b798456cd5bf7c13b4c9dd49a4d07533f78ac6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 10:55:56 +0100 Subject: [PATCH 104/137] Add `soa_zip` and `soa_unzip` to demo.odin --- examples/demo/demo.odin | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 76e8aa9fc..8cf3cfd9a 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -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); // 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() { From 28561ef5f5fba8bd4fd79ac3dcd7bd891248ab13 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:26:05 +0100 Subject: [PATCH 105/137] Minor change to internal linkage stuff --- src/llvm_backend.cpp | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b6d1990c3..5c43b1f1a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5607,7 +5607,9 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(global_data, LLVMInternalLinkage); + } LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -5649,7 +5651,9 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - LLVMSetLinkage(global_data, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(global_data, LLVMInternalLinkage); + } LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -14689,7 +14693,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info, max_type_info_count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), LB_TYPE_INFO_DATA_NAME); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lbValue value = {}; value.value = g; @@ -14729,7 +14735,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info_ptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); } @@ -14738,7 +14746,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); } { @@ -14746,7 +14756,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_uintptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); } @@ -14755,7 +14767,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_bool, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); } @@ -14764,7 +14778,9 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - // LLVMSetLinkage(g, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g, LLVMInternalLinkage); + } lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); } } @@ -14862,7 +14878,9 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); } else { - // LLVMSetLinkage(g.value, LLVMInternalLinkage); + if (!USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMInternalLinkage); + } } lbGlobalVariable var = {}; From b34e4a9fd1fad2548c90624504ff8ce420cfeccc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:46:27 +0100 Subject: [PATCH 106/137] More minor linkage changes --- src/llvm_backend.cpp | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 5c43b1f1a..a0f22be44 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5607,9 +5607,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) { LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(global_data, LLVMInternalLinkage); - } + LLVMSetLinkage(global_data, LLVMInternalLinkage); LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2); string_map_set(&m->const_strings, key, ptr); @@ -5651,9 +5649,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str) } LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name); LLVMSetInitializer(global_data, data); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(global_data, LLVMInternalLinkage); - } + LLVMSetLinkage(global_data, LLVMInternalLinkage); LLVMValueRef ptr = nullptr; if (str.len != 0) { @@ -13373,26 +13369,31 @@ lbValue lb_get_type_info_ptr(lbModule *m, Type *type) { lbValue lb_type_info_member_types_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_types.addr, lb_global_type_info_member_types_index); lb_global_type_info_member_types_index += cast(i32)count; return offset; } lbValue lb_type_info_member_names_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_names.addr, lb_global_type_info_member_names_index); lb_global_type_info_member_names_index += cast(i32)count; return offset; } lbValue lb_type_info_member_offsets_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_offsets.addr, lb_global_type_info_member_offsets_index); lb_global_type_info_member_offsets_index += cast(i32)count; return offset; } lbValue lb_type_info_member_usings_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_usings.addr, lb_global_type_info_member_usings_index); lb_global_type_info_member_usings_index += cast(i32)count; return offset; } lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) { + GB_ASSERT(p->module == &p->module->gen->default_module); lbValue offset = lb_emit_array_epi(p, lb_global_type_info_member_tags.addr, lb_global_type_info_member_tags_index); lb_global_type_info_member_tags_index += cast(i32)count; return offset; @@ -14735,9 +14736,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_type_info_ptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_types = lb_addr({g, alloc_type_pointer(t)}); } @@ -14746,9 +14745,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_names = lb_addr({g, alloc_type_pointer(t)}); } { @@ -14756,9 +14753,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_uintptr, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_offsets = lb_addr({g, alloc_type_pointer(t)}); } @@ -14767,9 +14762,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_bool, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_usings = lb_addr({g, alloc_type_pointer(t)}); } @@ -14778,9 +14771,7 @@ void lb_generate_code(lbGenerator *gen) { Type *t = alloc_type_array(t_string, count); LLVMValueRef g = LLVMAddGlobal(m->mod, lb_type(m, t), name); LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t))); - if (!USE_SEPARTE_MODULES) { - LLVMSetLinkage(g, LLVMInternalLinkage); - } + LLVMSetLinkage(g, LLVMInternalLinkage); lb_global_type_info_member_tags = lb_addr({g, alloc_type_pointer(t)}); } } @@ -14878,7 +14869,9 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMDLLExportLinkage); LLVMSetDLLStorageClass(g.value, LLVMDLLExportStorageClass); } else { - if (!USE_SEPARTE_MODULES) { + if (USE_SEPARTE_MODULES) { + LLVMSetLinkage(g.value, LLVMExternalLinkage); + } else { LLVMSetLinkage(g.value, LLVMInternalLinkage); } } From 26ce40c188c31539cbf7f97cf2ad1bb81000bc55 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:51:48 +0100 Subject: [PATCH 107/137] Remove @(static) for global variables --- core/math/rand/rand.odin | 4 ++-- core/strings/builder.odin | 2 +- core/unicode/tables.odin | 10 ---------- src/check_decl.cpp | 5 ++--- src/llvm_backend.cpp | 2 +- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 4f6e7474f..f5558bb8c 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -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) { diff --git a/core/strings/builder.odin b/core/strings/builder.odin index dd7fd4f1e..843f79381 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -221,7 +221,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { } -@(private, static) +@(private) DIGITS_LOWER := "0123456789abcdefx"; write_quoted_string :: proc{ diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index bb858fd04..ff4793402 100644 --- a/core/unicode/tables.odin +++ b/core/unicode/tables.odin @@ -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, diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5e8e79791..0aef40546 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -899,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); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a0f22be44..8bd5aaa37 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14910,7 +14910,7 @@ void lb_generate_code(lbGenerator *gen) { LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, e->file); LLVMMetadataRef llvm_scope = llvm_file; - LLVMBool local_to_unit = e->flags & EntityFlag_Static; + LLVMBool local_to_unit = LLVMGetLinkage(g.value) == LLVMInternalLinkage; LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0); LLVMMetadataRef llvm_decl = nullptr; From 3ac934dd15b59be670627213643e80c6d902fe4d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 11:58:02 +0100 Subject: [PATCH 108/137] Add suggestion for unused expression on `x == y` Expression is not used: 'x == 123' Suggestion: Did you mean to do an assignment? 'x = 123;' --- src/check_stmt.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee60a4acd..a62b55ab2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1436,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; } From 86dbcb1b20e7a5575013f2acbf0f48d194595d27 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 12:57:30 +0100 Subject: [PATCH 109/137] Add `-verbose-errors` which shows the error in the line of code --- src/build_settings.cpp | 5 +++ src/main.cpp | 6 +++ src/parser.cpp | 43 ++++++++++++++++++++ src/tokenizer.cpp | 92 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 09d860cfc..0207221bc 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -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) { diff --git a/src/main.cpp b/src/main.cpp index 0dd1e0c27..d16a110e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 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 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); diff --git a/src/parser.cpp b/src/parser.cpp index 2e27b8698..77ce7ea70 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -115,6 +115,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 *)); @@ -4602,6 +4644,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; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index d1310b56e..84d6ac9e2 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -188,9 +188,11 @@ void init_keyword_hash_table(void) { GB_ASSERT(max_keyword_size < 16); } -gb_global Array global_file_path_strings; // index is file id +gb_global Array global_file_path_strings; // index is file id +gb_global Array 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; @@ -284,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); } @@ -305,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); @@ -318,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; @@ -377,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; @@ -386,6 +423,53 @@ void error_out(char const *fmt, ...) { } +bool show_error_on_line(TokenPos const &pos) { + 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)); + + 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("^\n"); + error_out("\n"); + return true; + } + return false; +} + void error_va(Token token, char const *fmt, va_list va) { gb_mutex_lock(&global_error_collector.mutex); global_error_collector.count++; @@ -397,6 +481,7 @@ void error_va(Token token, char const *fmt, va_list va) { error_out("%s %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -420,6 +505,7 @@ void warning_va(Token token, char const *fmt, va_list va) { error_out("%s Warning: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -460,6 +546,7 @@ void syntax_error_va(Token token, char const *fmt, va_list va) { error_out("%s Syntax Error: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } else if (token.pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -484,6 +571,7 @@ void syntax_warning_va(Token token, char const *fmt, va_list va) { error_out("%s Syntax Warning: %s\n", token_pos_to_string(token.pos), gb_bprintf_va(fmt, va)); + show_error_on_line(token.pos); } else if (token.pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } From 5108ebf015e460e9c226a2067f6109785a20a143 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 13:02:44 +0100 Subject: [PATCH 110/137] Replace `error` calls with `Token` to use `TokenPos` --- src/parser.cpp | 8 ++-- src/tokenizer.cpp | 99 +++++++++++++++++++++++------------------------ 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 77ce7ea70..b8d53e724 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -479,7 +479,7 @@ void error(Ast *node, char const *fmt, ...) { } va_list va; va_start(va, fmt); - error_va(token, fmt, va); + error_va(token.pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -493,7 +493,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; @@ -503,14 +503,14 @@ void error_no_newline(Ast *node, char const *fmt, ...) { void warning(Ast *node, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(ast_token(node), fmt, va); + warning_va(ast_token(node).pos, fmt, va); va_end(va); } void syntax_error(Ast *node, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(ast_token(node), fmt, va); + syntax_error_va(ast_token(node).pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 84d6ac9e2..3c44aaad4 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -470,18 +470,18 @@ bool show_error_on_line(TokenPos const &pos) { return false; } -void error_va(Token token, char const *fmt, va_list va) { +void error_va(TokenPos 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\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(token.pos); + show_error_on_line(pos); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -489,23 +489,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, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - error_va(token, fmt, va); + error_va(pos, 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(token.pos); + show_error_on_line(pos); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -518,16 +518,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); @@ -537,17 +537,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, 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)); - show_error_on_line(token.pos); - } else if (token.pos.line == 0) { + show_error_on_line(pos); + } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -557,22 +557,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, char const *fmt, va_list va) { if (global_warnings_as_errors()) { - syntax_error_va(token, fmt, va); + syntax_error_va(pos, 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)); - show_error_on_line(token.pos); - } else if (token.pos.line == 0) { + show_error_on_line(pos); + } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } } @@ -581,17 +581,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); } @@ -600,7 +600,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); } @@ -612,26 +612,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); } @@ -743,13 +741,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++; @@ -761,11 +760,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++; From 9c54ed57924bf8ff241b0bffebaeabfa541db24c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 14:15:57 +0100 Subject: [PATCH 111/137] Add range-based error messages to `-verbose-errors` Example: Cannot convert '(1 + 2)' to 'untyped bool' from 'untyped integer' x := (1 + 2) * true; ^~~~~~^ --- src/check_builtin.cpp | 4 +- src/check_expr.cpp | 17 ++- src/check_stmt.cpp | 2 +- src/check_type.cpp | 10 +- src/llvm_backend.cpp | 4 +- src/parser.cpp | 158 +++++--------------- src/parser.hpp | 6 +- src/parser_pos.cpp | 331 ++++++++++++++++++++++++++++++++++++++++++ src/tokenizer.cpp | 64 +++++--- 9 files changed, 429 insertions(+), 167 deletions(-) create mode 100644 src/parser_pos.cpp diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index c9b911f92..1acb9732f 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -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); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 68f05084e..81fe3baa9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5516,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; @@ -6191,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; @@ -6206,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; @@ -6373,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; @@ -6385,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; @@ -8151,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); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a62b55ab2..64d17a8c8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -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); diff --git a/src/check_type.cpp b/src/check_type.cpp index 24a3e0a59..419904876 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1191,7 +1191,7 @@ 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; @@ -2711,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 { @@ -2745,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") { @@ -2770,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 { @@ -2790,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 { diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8bd5aaa37..08c9445bd 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8887,7 +8887,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, switch (id) { case BuiltinProc_DIRECTIVE: { ast_node(bd, BasicDirective, ce->proc); - String name = bd->name; + String name = bd->name.string; GB_ASSERT(name == "location"); String procedure = p->entity->token.string; TokenPos pos = ast_token(ce->proc).pos; @@ -11515,7 +11515,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(bd, BasicDirective, expr); TokenPos pos = bd->token.pos; - GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name)); + GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(bd->name.string)); case_end; case_ast_node(i, Implicit, expr); diff --git a/src/parser.cpp b/src/parser.cpp index b8d53e724..a5180b4dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -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; @@ -474,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.pos, fmt, va); + error_va(token.pos, end_pos, fmt, va); va_end(va); if (node != nullptr && node->file != nullptr) { node->file->error_count += 1; @@ -501,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).pos, 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).pos, 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; @@ -682,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; @@ -2042,27 +1952,27 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); } 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); + 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) { @@ -2074,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) { @@ -2107,7 +2017,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { } 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); @@ -4554,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") { diff --git a/src/parser.hpp b/src/parser.hpp index 77d5f1b02..89f714aaa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -286,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; \ @@ -324,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; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp new file mode 100644 index 000000000..c5ad89604 --- /dev/null +++ b/src/parser_pos.cpp @@ -0,0 +1,331 @@ +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_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: return ast_end_token(node->TagExpr.expr); + 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: + return ast_end_token(node->SelectorExpr.selector); + 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)); +} diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3c44aaad4..826fccc04 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -423,7 +423,7 @@ void error_out(char const *fmt, ...) { } -bool show_error_on_line(TokenPos const &pos) { +bool show_error_on_line(TokenPos const &pos, TokenPos end) { if (!show_error_line()) { return false; } @@ -435,6 +435,8 @@ bool show_error_on_line(TokenPos const &pos) { 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, @@ -462,15 +464,33 @@ bool show_error_on_line(TokenPos const &pos) { } error_out("\n\t"); - for (i32 i = 0; i < offset; i++) error_out(" "); - error_out("^\n"); - error_out("\n"); + 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 pos, char const *fmt, va_list va) { +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 @@ -481,7 +501,7 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { error_out("%s %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } gb_mutex_unlock(&global_error_collector.mutex); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { @@ -489,9 +509,9 @@ void error_va(TokenPos pos, char const *fmt, va_list va) { } } -void warning_va(TokenPos const &pos, 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(pos, fmt, va); + error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -505,7 +525,7 @@ void warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } } gb_mutex_unlock(&global_error_collector.mutex); @@ -537,7 +557,7 @@ void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { } -void syntax_error_va(TokenPos const &pos, 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 @@ -546,7 +566,7 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Error: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); } @@ -557,9 +577,9 @@ void syntax_error_va(TokenPos const &pos, char const *fmt, va_list va) { } } -void syntax_warning_va(TokenPos const &pos, 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(pos, fmt, va); + syntax_error_va(pos, end, fmt, va); return; } gb_mutex_lock(&global_error_collector.mutex); @@ -571,7 +591,7 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { error_out("%s Syntax Warning: %s\n", token_pos_to_string(pos), gb_bprintf_va(fmt, va)); - show_error_on_line(pos); + show_error_on_line(pos, end); } else if (pos.line == 0) { error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); } @@ -584,14 +604,14 @@ void syntax_warning_va(TokenPos const &pos, char const *fmt, va_list va) { void warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - warning_va(token.pos, fmt, va); + warning_va(token.pos, {}, fmt, va); va_end(va); } void error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - error_va(token.pos, fmt, va); + error_va(token.pos, {}, fmt, va); va_end(va); } @@ -600,7 +620,7 @@ void error(TokenPos pos, char const *fmt, ...) { va_start(va, fmt); Token token = {}; token.pos = pos; - error_va(pos, fmt, va); + error_va(pos, {}, fmt, va); va_end(va); } @@ -615,21 +635,21 @@ void error_line(char const *fmt, ...) { void syntax_error(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_error_va(token.pos, 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); - syntax_error_va(pos, fmt, va); + syntax_error_va(pos, {}, fmt, va); va_end(va); } void syntax_warning(Token const &token, char const *fmt, ...) { va_list va; va_start(va, fmt); - syntax_warning_va(token.pos, fmt, va); + syntax_warning_va(token.pos, {}, fmt, va); va_end(va); } @@ -748,7 +768,7 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) { pos.offset = cast(i32)(t->read_curr - t->start); va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; @@ -762,7 +782,7 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) { } va_start(va, msg); - syntax_error_va(pos, msg, va); + syntax_error_va(pos, {}, msg, va); va_end(va); t->error_count++; From 4d580ed693edab796498acf5eba5a1c4d88c9b2b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 14:26:20 +0100 Subject: [PATCH 112/137] Keep -vet happy --- examples/demo/demo.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 8cf3cfd9a..af67df3e9 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1868,7 +1868,7 @@ soa_struct_layout :: proc() { // iterate over the #soa slice for v, i in s { - fmt.println(v); // exactly the same as s[i] + 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: From 92abddddc5ca4be622e93856c7246159b594e9e9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 19 May 2021 15:02:36 +0100 Subject: [PATCH 113/137] Be a little more correct with the temporary Ast node --- src/check_stmt.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 64d17a8c8..7772b5c97 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1537,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 @@ -1546,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; } From 50035f257eb33769211ca49a30c51f9a20440a0e Mon Sep 17 00:00:00 2001 From: Daniel Gavin Date: Thu, 20 May 2021 12:13:23 +0200 Subject: [PATCH 114/137] don't factor in the rhs length for lines that don't have Equal tokens. --- core/odin/printer/printer.odin | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index bef1235e9..2a0be1c3c 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -567,7 +567,7 @@ align_var_decls :: proc(p: ^Printer) { } current_token_index += 1; - + largest_lhs = max(largest_lhs, lhs_length); break; } } @@ -579,12 +579,11 @@ align_var_decls :: proc(p: ^Printer) { 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; } } - - largest_lhs = max(largest_lhs, lhs_length); - largest_rhs = max(largest_rhs, rhs_length); + } //repeating myself, move to sub procedure From fe74b479c69fde6d7651e5dff6429736a170ec5d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 20 May 2021 21:02:05 +0100 Subject: [PATCH 115/137] Begin changes to sync2 --- core/sync/sync2/atomic.odin | 2 + core/sync/sync2/primitives.odin | 4 +- core/sync/sync2/primitives_atomic.odin | 454 ++++++++++++++--------- core/sync/sync2/primitives_pthreads.odin | 2 +- 4 files changed, 274 insertions(+), 188 deletions(-) diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin index fa86ec352..efefc8025 100644 --- a/core/sync/sync2/atomic.odin +++ b/core/sync/sync2/atomic.odin @@ -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; diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index 1ed83f706..e524586ec 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -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 diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/sync2/primitives_atomic.odin index 7043f8c84..aed01eb1f 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/sync2/primitives_atomic.odin @@ -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.. 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.. 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)< 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 diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin index 5fd43d871..7e45e0565 100644 --- a/core/sync/sync2/primitives_pthreads.odin +++ b/core/sync/sync2/primitives_pthreads.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd +//+build linux, freebsd //+private package sync2 From 8758afdf4e5a917a996e36919071947d59af3833 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 10:21:23 +0100 Subject: [PATCH 116/137] General fixes for `odinfmt` --- core/odin/format/format.odin | 33 ++++++++--------- core/odin/printer/printer.odin | 43 +++++++++++---------- core/odin/printer/visit.odin | 68 ++++++++++------------------------ tools/odinfmt/main.odin | 37 +++++++++--------- 4 files changed, 76 insertions(+), 105 deletions(-) diff --git a/core/odin/format/format.odin b/core/odin/format/format.odin index d86808669..10e198958 100644 --- a/core/odin/format/format.odin +++ b/core/odin/format/format.odin @@ -10,26 +10,25 @@ simplify :: proc(file: ^ast.File) { } -format :: proc(source: [] u8, config: printer.Config, allocator := context.allocator) -> ([] u8, bool) { +format :: proc(source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) { + pkg := ast.Package { + kind = .Normal, + }; - pkg := ast.Package { - kind = .Normal, - }; + file := ast.File { + pkg = &pkg, + src = source, + }; - file := ast.File { - pkg = &pkg, - src = source, - }; + p := parser.default_parser(parser_flags); - p := parser.default_parser(); + ok := parser.parse_file(&p, &file); - ok := parser.parse_file(&p, &file); + if !ok || file.syntax_error_count > 0 { + return {}, false; + } - if !ok || file.syntax_error_count > 0 { - return {}, false; - } + prnt := printer.make_printer(config, allocator); - prnt := printer.make_printer(config, allocator); - - return transmute([]u8) printer.print(&prnt, &file), true; -} \ No newline at end of file + return printer.print(&prnt, &file), true; +} diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 2a0be1c3c..0eba91ca2 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -8,7 +8,7 @@ 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} +Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit}; Line_Type :: bit_set[Type_Enum]; @@ -100,21 +100,21 @@ Newline_Style :: enum { } default_style := Config { - spaces = 4, - newline_limit = 2, - convert_do = false, - semicolons = true, - tabs = true, - brace_style = ._1TBS, + 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, + 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 { @@ -126,7 +126,6 @@ make_printer :: proc(config: Config, allocator := context.allocator) -> Printer } print :: proc(p: ^Printer, file: ^ast.File) -> string { - p.comments = file.comments; if len(file.decls) > 0 { @@ -387,7 +386,7 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, break; } else if format_token.kind == .Undef { return; - } + } if line_index == 0 && i <= format_index { continue; @@ -500,7 +499,7 @@ align_var_decls :: proc(p: ^Printer) { 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 == .If || line.format_tokens[i].kind == .Comment { continue_flag = true; } @@ -583,7 +582,7 @@ align_var_decls :: proc(p: ^Printer) { break; } } - + } //repeating myself, move to sub procedure @@ -790,7 +789,7 @@ align_struct :: proc(p: ^Printer, index: int) -> int { length := 0; for format_token, i in line.format_tokens { - + //give up on nested structs if format_token.kind == .Comment { break; @@ -825,7 +824,7 @@ align_struct :: proc(p: ^Printer, index: int) -> int { if colon_count >= brace_token.parameter_count { break; } - } + } //give up aligning nested, it never looks good if nested { @@ -833,7 +832,7 @@ align_struct :: proc(p: ^Printer, index: int) -> int { for format_token in line.format_tokens { if format_token.kind == .Close_Brace { return end_line_index + line_index - index; - } + } } } } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index c2bd8eaf8..bc1222dd2 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -31,7 +31,6 @@ sort_attribute :: proc(s: ^[dynamic]^ast.Attribute) -> sort.Interface { @(private) comment_before_position :: proc(p: ^Printer, pos: tokenizer.Pos) -> bool { - if len(p.comments) <= p.latest_comment_index { return false; } @@ -48,7 +47,6 @@ next_comment_group :: proc(p: ^Printer) { @(private) push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { - if len(comment.text) == 0 { return 0; } @@ -76,7 +74,6 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { return 0; } else { - builder := strings.make_builder(context.temp_allocator); c_len := len(comment.text); @@ -156,12 +153,10 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { @(private) push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { - prev_comment: ^tokenizer.Token; prev_comment_lines: int; for comment_before_position(p, pos) { - comment_group := p.comments[p.latest_comment_index]; if prev_comment == nil { @@ -170,7 +165,6 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { } for comment, i in comment_group.list { - if prev_comment != nil && p.last_source_position.line != comment.pos.line { newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); } @@ -189,7 +183,6 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { @(private) append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_Token { - format_token := format_token; if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || @@ -231,7 +224,6 @@ push_format_token :: proc(p: ^Printer, format_token: Format_Token) { @(private) push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_before: int, value := "") { - format_token := Format_Token { spaces_before = spaces_before, kind = kind, @@ -247,7 +239,6 @@ push_generic_token :: proc(p: ^Printer, kind: tokenizer.Token_Kind, spaces_befor @(private) push_string_token :: proc(p: ^Printer, text: string, spaces_before: int) { - format_token := Format_Token { spaces_before = spaces_before, kind = .String, @@ -259,7 +250,6 @@ push_string_token :: proc(p: ^Printer, text: string, spaces_before: int) { @(private) push_ident_token :: proc(p: ^Printer, text: string, spaces_before: int) { - format_token := Format_Token { spaces_before = spaces_before, kind = .Ident, @@ -295,7 +285,6 @@ move_line_limit :: proc(p: ^Printer, pos: tokenizer.Pos, limit: int) -> bool { @(private) set_line :: proc(p: ^Printer, line: int) -> ^Line { - unwrapped_line: ^Line; if line >= len(p.lines) { @@ -348,7 +337,6 @@ hint_current_line :: proc(p: ^Printer, hint: Line_Type) { @(private) visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { - using ast; if decl == nil { @@ -384,7 +372,6 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { push_ident_token(p, path, 0); } case Foreign_Block_Decl: - if len(v.attributes) > 0 { sort.sort(sort_attribute(&v.attributes)); move_line(p, v.attributes[0].pos); @@ -479,14 +466,13 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { @(private) visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false, force_newline := false) { - if len(list) == 0 { return; } - //we have to newline the expressions to respect the source + // we have to newline the expressions to respect the source for expr, i in list { - //Don't move the first expression, it looks bad + // Don't move the first expression, it looks bad if i != 0 && force_newline { newline_position(p, 1); } else if i != 0 { @@ -507,7 +493,6 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing @(private) visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { - if len(attributes) == 0 { return; } @@ -526,7 +511,6 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { @(private) visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { - using ast; if stmt == nil { @@ -734,7 +718,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Semicolon, 0); } case For_Stmt: - //this should be simplified + // this should be simplified move_line(p, v.pos); if v.label != nil { @@ -796,7 +780,6 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_expr(p, v.expr); visit_stmt(p, v.body); case Range_Stmt: - move_line(p, v.pos); if v.label != nil { @@ -864,7 +847,6 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } case Branch_Stmt: - move_line(p, v.pos); push_generic_token(p, v.tok.kind, 0); @@ -885,7 +867,6 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener @(private) visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { - using ast; if expr == nil { @@ -1090,9 +1071,12 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { set_source_position(p, v.end); case Proc_Lit: - - if v.inlining == .Inline { + switch v.inlining { + case .None: + case .Inline: push_ident_token(p, "#force_inline", 0); + case .No_Inline: + push_ident_token(p, "#force_no_inline", 0); } visit_proc_type(p, v.type^, true); @@ -1121,11 +1105,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Call_Expr: visit_expr(p, v.expr); - push_format_token(p, Format_Token { - kind = .Open_Paren, - type = .Call, - text = "(", - }); + push_format_token(p, + Format_Token { + kind = .Open_Paren, + type = .Call, + text = "(", + }, + ); hint_current_line(p, {.Call}); @@ -1152,7 +1138,6 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_expr(p, v.index); push_generic_token(p, .Close_Bracket, 0); case Proc_Group: - push_generic_token(p, v.tok.kind, 1); if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { @@ -1168,7 +1153,6 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } case Comp_Lit: - if v.type != nil { visit_expr(p, v.type); } @@ -1244,7 +1228,6 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0) { - set_source_position(p, begin); newline_braced := p.config.brace_style == .Allman; @@ -1262,7 +1245,6 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, c push_format_token(p, format_token); indent(p); } else { - format_token.spaces_before = 1; push_format_token(p, format_token); indent(p); } @@ -1286,13 +1268,11 @@ visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) { } visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false, enforce_newline := false) { - if list.list == nil { return; } for field, i in list.list { - if !move_line_limit(p, field.pos, 1) && enforce_newline { newline_position(p, 1); } @@ -1325,7 +1305,6 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, } visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) { - if is_proc_lit { push_format_token(p, Format_Token { kind = .Proc, @@ -1357,11 +1336,8 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa case .Fast_Call: push_string_token(p, "\"fast\"", 1); explicit_calling = true; - case .None: - //nothing i guess - case .Invalid: - //nothing i guess - case .Foreign_Block_Default: + case .None, .Invalid, .Foreign_Block_Default: + // nothing } if explicit_calling { @@ -1405,7 +1381,6 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa } visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { - move_line(p, binary.left.pos); if v, ok := binary.left.derived.(ast.Binary_Expr); ok { @@ -1430,15 +1405,13 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { } visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { - if len(list) == 0 { return; } - //all the expression are on the line + // all the expression are on the line if list[0].pos.line == list[len(list) - 1].pos.line { for expr, i in list { - if i == len(list) - 1 && ellipsis { push_generic_token(p, .Ellipsis, 0); } @@ -1451,8 +1424,7 @@ visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { } } else { for expr, i in list { - - //we have to newline the expressions to respect the source + // we have to newline the expressions to respect the source move_line_limit(p, expr.pos, 1); if i == len(list) - 1 && ellipsis { @@ -1469,13 +1441,11 @@ visit_call_exprs :: proc(p: ^Printer, list: []^ast.Expr, ellipsis := false) { } visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := true) { - if list.list == nil { return; } for field, i in list.list { - if i != 0 { move_line_limit(p, field.pos, 1); } diff --git a/tools/odinfmt/main.odin b/tools/odinfmt/main.odin index a651aaff0..63e02269f 100644 --- a/tools/odinfmt/main.odin +++ b/tools/odinfmt/main.odin @@ -14,26 +14,31 @@ Args :: struct { write: Maybe(bool) `flag:"w" usage:"write the new format to file"`, } -print_help :: proc() { +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) -> ([]u8, bool) { + +format_file :: proc(filepath: string) -> (string, bool) { if data, ok := os.read_entire_file(filepath); ok { - return format.format(data, format.default_style); + return format.format(string(data), format.default_style); } else { - return {}, false; + 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; } @@ -48,13 +53,12 @@ walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip } main :: proc() { - init_global_temporary_allocator(mem.megabytes(100)); args: Args; if len(os.args) < 2 { - print_help(); + print_help(os.args); os.exit(1); } @@ -69,13 +73,13 @@ main :: proc() { if os.is_file(path) { if _, ok := args.write.(bool); ok { - backup_path := strings.concatenate({path, "_bk"}, context.temp_allocator); + 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, data) { + if os.write_entire_file(path, transmute([]byte)data) { os.remove(backup_path); } } else { @@ -83,7 +87,7 @@ main :: proc() { } } else { if data, ok := format_file(path); ok { - fmt.println(transmute(string)data); + fmt.println(data); } } } else if os.is_dir(path) { @@ -92,24 +96,23 @@ main :: proc() { for file in files { fmt.println(file); - backup_path := strings.concatenate({file, "_bk"}, context.temp_allocator); + 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, data) { + if os.write_entire_file(file, transmute([]byte)data) { os.remove(backup_path); } } else { - fmt.println(transmute(string)data); + fmt.println(data); } } else { fmt.eprintf("failed to format %v", file); } - - free_all(context.temp_allocator); } fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time))); From 247f4f3293c2b5a5021062dc432f559602e12282 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 10:51:19 +0100 Subject: [PATCH 117/137] Fix `..=` logic in the backend --- src/check_expr.cpp | 2 +- src/llvm_backend.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 81fe3baa9..d714e90a8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3392,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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 08c9445bd..3b4e5f3f6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -6231,7 +6231,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } if (lo == i) { @@ -6315,7 +6315,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } if (lo == i) { @@ -12704,7 +12704,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } @@ -12803,7 +12803,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } @@ -12912,7 +12912,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } @@ -13016,7 +13016,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { TokenKind op = ie->op.kind; i64 lo = exact_value_to_i64(lo_tav.value); i64 hi = exact_value_to_i64(hi_tav.value); - if (op == Token_Ellipsis) { + if (op != Token_RangeHalf) { hi += 1; } From d7e85725e101cd2b57a0b5ea4f08c3ec1d2ec7c2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 11:16:07 +0100 Subject: [PATCH 118/137] General improves to formatting for the visitor --- core/odin/printer/visit.odin | 80 +++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index bc1222dd2..f5c9d5663 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -51,7 +51,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { return 0; } - if comment.text[0] == '/' && comment.text[1] == '/' { + if comment.text[:2] != "/*" { format_token := Format_Token { spaces_before = 1, kind = .Comment, @@ -82,33 +82,33 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int { multilines: [dynamic]string; for i := 0; i < len(comment.text); i += 1 { - c := comment.text[i]; if c != ' ' && c != '\t' { trim_space = false; } - if (c == ' ' || c == '\t' || c == '\n') && trim_space { + switch { + case (c == ' ' || c == '\t' || c == '\n') && trim_space: continue; - } else if c == 13 && comment.text[min(c_len - 1, i + 1)] == 10 { + case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n': append(&multilines, strings.to_string(builder)); builder = strings.make_builder(context.temp_allocator); trim_space = true; i += 1; - } else if c == 10 { + case c == '\n': append(&multilines, strings.to_string(builder)); builder = strings.make_builder(context.temp_allocator); trim_space = true; - } else if c == '/' && comment.text[min(c_len - 1, i + 1)] == '*' { + case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*': strings.write_string(&builder, "/*"); trim_space = true; i += 1; - } else if c == '*' && comment.text[min(c_len - 1, i + 1)] == '/' { + case c == '*' && comment.text[min(c_len - 1, i + 1)] == '/': trim_space = true; strings.write_string(&builder, "*/"); i += 1; - } else { + case: strings.write_byte(&builder, c); } } @@ -185,7 +185,9 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { append_format_token :: proc(p: ^Printer, format_token: Format_Token) -> ^Format_Token { format_token := format_token; - if p.last_token != nil && (p.last_token.kind == .Ellipsis || p.last_token.kind == .Range_Half || + if p.last_token != nil && ( + p.last_token.kind == .Ellipsis || + p.last_token.kind == .Range_Half || p.last_token.kind == .Range_Full || p.last_token.kind == .Open_Paren || p.last_token.kind == .Period || p.last_token.kind == .Open_Brace || p.last_token.kind == .Open_Bracket) { format_token.spaces_before = 0; @@ -548,7 +550,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener if v.pos.line == v.end.line { if !empty_block { - push_generic_token(p, .Open_Brace, 0); + push_generic_token(p, .Open_Brace, 1); } set_source_position(p, v.pos); @@ -753,8 +755,8 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } visit_stmt(p, v.body); - case Inline_Range_Stmt: + case Inline_Range_Stmt: move_line(p, v.pos); if v.label != nil { @@ -779,6 +781,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener visit_expr(p, v.expr); visit_stmt(p, v.body); + case Range_Stmt: move_line(p, v.pos); @@ -865,6 +868,17 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener set_source_position(p, stmt.end); } +@(private) +push_where_clauses :: proc(p: ^Printer, where_clauses: []^ast.Expr) { + if where_clauses == nil { + return; + } + move_line(p, where_clauses[0].pos); + push_generic_token(p, .Where, 1); + visit_exprs(p, where_clauses, true); +} + + @(private) visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { using ast; @@ -987,11 +1001,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_ident_token(p, "#maybe", 1); } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + push_where_clauses(p, v.where_clauses); if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); @@ -1051,11 +1061,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, .Close_Paren, 0); } - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + push_where_clauses(p, v.where_clauses); if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); @@ -1081,11 +1087,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_proc_type(p, v.type^, true); - if v.where_clauses != nil { - move_line(p, v.where_clauses[0].pos); - push_generic_token(p, .Where, 1); - visit_exprs(p, v.where_clauses, true); - } + push_where_clauses(p, v.where_clauses); if v.body != nil { set_source_position(p, v.body.pos); @@ -1158,13 +1160,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } if len(v.elems) != 0 && v.pos.line != v.elems[len(v.elems) - 1].pos.line { - visit_begin_brace(p, v.pos, .Comp_Lit); + visit_begin_brace(p, v.pos, .Comp_Lit, 0); newline_position(p, 1); set_source_position(p, v.elems[0].pos); visit_exprs(p, v.elems, true, true); visit_end_brace(p, v.end); } else { - push_generic_token(p, .Open_Brace, 1); + push_generic_token(p, .Open_Brace, 0 if v.type != nil else 1); visit_exprs(p, v.elems, true); push_generic_token(p, .Close_Brace, 0); } @@ -1227,7 +1229,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } } -visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0) { +visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, count := 0, same_line_spaces_before := 1) { set_source_position(p, begin); newline_braced := p.config.brace_style == .Allman; @@ -1245,6 +1247,7 @@ visit_begin_brace :: proc(p: ^Printer, begin: tokenizer.Pos, type: Block_Type, c push_format_token(p, format_token); indent(p); } else { + format_token.spaces_before = same_line_spaces_before; push_format_token(p, format_token); indent(p); } @@ -1278,7 +1281,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, } if .Using in field.flags { - push_generic_token(p, .Using, 0); + push_generic_token(p, .Using, 1); } visit_exprs(p, field.names, true); @@ -1389,14 +1392,25 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { visit_expr(p, binary.left); } - if binary.op.kind == .Ellipsis || binary.op.kind == .Range_Half { - push_generic_token(p, binary.op.kind, 0); - } else { + either_implicit_selector := false; + if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok { + either_implicit_selector = true; + } else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok { + either_implicit_selector = true; + } + + #partial switch binary.op.kind { + case .Ellipsis: + push_generic_token(p, binary.op.kind, 1 if either_implicit_selector else 0, tokenizer.tokens[tokenizer.Token_Kind.Range_Full]); + case .Range_Half, .Range_Full: + push_generic_token(p, binary.op.kind, 1 if either_implicit_selector else 0); + case: push_generic_token(p, binary.op.kind, 1); } move_line(p, binary.right.pos); + if v, ok := binary.right.derived.(ast.Binary_Expr); ok { visit_binary_expr(p, v); } else { From 159daba7590873ba76acef19beac88265eafb949 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 12:44:45 +0100 Subject: [PATCH 119/137] Fix `ast_end_token` --- src/parser_pos.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index c5ad89604..895818873 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -125,6 +125,8 @@ 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; @@ -140,7 +142,11 @@ Token ast_end_token(Ast *node) { return node->CompoundLit.close; case Ast_BadExpr: return node->BadExpr.end; - case Ast_TagExpr: return ast_end_token(node->TagExpr.expr); + 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; @@ -150,7 +156,10 @@ Token ast_end_token(Ast *node) { case Ast_SelectorCallExpr: return ast_end_token(node->SelectorCallExpr.call); case Ast_ImplicitSelectorExpr: - return ast_end_token(node->SelectorExpr.selector); + 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: From cc1d3a7b19775b3151108f7fafc997ec69ec40d0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 13:07:00 +0100 Subject: [PATCH 120/137] `parser.check_poly_params_for_type` --- core/odin/parser/parser.odin | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 890ebe86d..efdee41f4 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2031,23 +2031,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); @@ -2416,7 +2399,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; @@ -2493,7 +2475,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; From f0c9f82e1b4c2bc4548a77059ded3c53c91837f0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 13:42:29 +0100 Subject: [PATCH 121/137] Minor changes to printer to use a bit set rather than booleans for list options --- core/odin/format/format.odin | 3 +- core/odin/printer/printer.odin | 18 +++-- core/odin/printer/visit.odin | 140 ++++++++++++++++++++++----------- 3 files changed, 105 insertions(+), 56 deletions(-) diff --git a/core/odin/format/format.odin b/core/odin/format/format.odin index 10e198958..c231e0afd 100644 --- a/core/odin/format/format.odin +++ b/core/odin/format/format.odin @@ -10,7 +10,7 @@ simplify :: proc(file: ^ast.File) { } -format :: proc(source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) { +format :: proc(filepath: string, source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) { pkg := ast.Package { kind = .Normal, }; @@ -18,6 +18,7 @@ format :: proc(source: string, config: printer.Config, parser_flags := parser.Fl file := ast.File { pkg = &pkg, src = source, + fullpath = filepath, }; p := parser.default_parser(parser_flags); diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin index 0eba91ca2..f99bd2b56 100644 --- a/core/odin/printer/printer.odin +++ b/core/odin/printer/printer.odin @@ -90,8 +90,8 @@ Block_Type :: enum { } Alignment_Style :: enum { - Align_On_Colon_And_Equals, Align_On_Type_And_Equals, + Align_On_Colon_And_Equals, } Newline_Style :: enum { @@ -414,7 +414,6 @@ format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, } format_generic :: proc(p: ^Printer) { - next_struct_line := 0; for line, line_index in p.lines { @@ -424,12 +423,17 @@ format_generic :: proc(p: ^Printer) { } for format_token, token_index in line.format_tokens { - if format_token.kind == .For || format_token.kind == .If || - format_token.kind == .When || format_token.kind == .Switch || - (format_token.kind == .Proc && format_token.type == .Proc_Lit) { + #partial switch format_token.kind { + case .For, .If, .When, .Switch: format_keyword_to_brace(p, line_index, token_index, format_token.kind); - } else if format_token.type == .Call { - format_call(p, line_index, token_index); + 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); + } } } diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index f5c9d5663..0e492af70 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -411,7 +411,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { push_generic_token(p, .Using, 0); } - visit_exprs(p, v.names, true); + visit_exprs(p, v.names, {.Add_Comma}); hint_current_line(p, {.Value_Decl}); @@ -443,7 +443,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { if len(v.values) == 1 { visit_expr(p, v.values[0]); //this is too ensure that one value are never newlined(procs, structs, etc.) } else { - visit_exprs(p, v.values, true); + visit_exprs(p, v.values, {.Add_Comma}); } add_semicolon := true; @@ -467,7 +467,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { } @(private) -visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing := false, force_newline := false) { +visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, options := List_Options{}) { if len(list) == 0 { return; } @@ -475,20 +475,20 @@ visit_exprs :: proc(p: ^Printer, list: []^ast.Expr, add_comma := false, trailing // we have to newline the expressions to respect the source for expr, i in list { // Don't move the first expression, it looks bad - if i != 0 && force_newline { + if i != 0 && .Enforce_Newline in options { newline_position(p, 1); } else if i != 0 { move_line_limit(p, expr.pos, 1); } - visit_expr(p, expr); + visit_expr(p, expr, options); - if (i != len(list) - 1 || trailing) && add_comma { + if (i != len(list) - 1 || .Trailing in options) && .Add_Comma in options { push_generic_token(p, .Comma, 0); } } - if len(list) > 1 && force_newline { + if len(list) > 1 && .Enforce_Newline in options { newline_position(p, 1); } } @@ -505,7 +505,7 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) { push_generic_token(p, .At, 0); push_generic_token(p, .Open_Paren, 0); - visit_exprs(p, attribute.elems, true); + visit_exprs(p, attribute.elems, {.Add_Comma}); push_generic_token(p, .Close_Paren, 0); } @@ -540,7 +540,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Using, 1); - visit_exprs(p, v.list, true); + visit_exprs(p, v.list, {.Add_Comma}); if p.config.semicolons { push_generic_token(p, .Semicolon, 0); @@ -667,7 +667,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Case, 0); if v.list != nil { - visit_exprs(p, v.list, true); + visit_exprs(p, v.list, {.Add_Comma}); } push_generic_token(p, v.terminator.kind, 0); @@ -704,11 +704,11 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener hint_current_line(p, {.Assign}); - visit_exprs(p, v.lhs, true); + visit_exprs(p, v.lhs, {.Add_Comma}); push_generic_token(p, v.op.kind, 1); - visit_exprs(p, v.rhs, true); + visit_exprs(p, v.rhs, {.Add_Comma}); if block_stmt && p.config.semicolons { push_generic_token(p, .Semicolon, 0); @@ -814,7 +814,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener push_generic_token(p, .Return, 1); if v.results != nil { - visit_exprs(p, v.results, true); + visit_exprs(p, v.results, {.Add_Comma}); } if block_stmt && p.config.semicolons { @@ -869,18 +869,50 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener } @(private) -push_where_clauses :: proc(p: ^Printer, where_clauses: []^ast.Expr) { - if where_clauses == nil { +push_where_clauses :: proc(p: ^Printer, clauses: []^ast.Expr) { + if len(clauses) == 0 { return; } - move_line(p, where_clauses[0].pos); + + // TODO(bill): This is not outputting correctly at all + + move_line(p, clauses[0].pos); push_generic_token(p, .Where, 1); - visit_exprs(p, where_clauses, true); + + force_newline := false; + + for expr, i in clauses { + // Don't move the first expression, it looks bad + if i != 0 && i != len(clauses)-1 && force_newline { + newline_position(p, 1); + } else if i != 0 { + move_line_limit(p, expr.pos, 1); + } + + visit_expr(p, expr); + + if i != len(clauses) - 1 { + push_generic_token(p, .Comma, 0); + } + } + + if len(clauses) > 1 && force_newline { + newline_position(p, 1); + } +} + +@(private) +push_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) { + if poly_params != nil { + push_generic_token(p, .Open_Paren, 0); + visit_field_list(p, poly_params, {.Add_Comma, .Enforce_Poly_Names}); + push_generic_token(p, .Close_Paren, 0); + } } @(private) -visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { +visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { using ast; if expr == nil { @@ -894,7 +926,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { push_generic_token(p, v.tok.kind, 1, v.tok.text); push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.param_types, true, false); + visit_exprs(p, v.param_types, {.Add_Comma}); push_generic_token(p, .Close_Paren, 0); push_generic_token(p, .Sub, 1); @@ -933,7 +965,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Selector_Call_Expr: visit_expr(p, v.call.expr); push_generic_token(p, .Open_Paren, 1); - visit_exprs(p, v.call.args, true); + visit_exprs(p, v.call.args, {.Add_Comma}); push_generic_token(p, .Close_Paren, 0); case Ellipsis: push_generic_token(p, .Ellipsis, 1); @@ -952,7 +984,12 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { } push_generic_token(p, .Close_Bracket, 0); case Ident: - push_ident_token(p, v.name, 1); + if .Enforce_Poly_Names in options { + push_generic_token(p, .Dollar, 1); + push_ident_token(p, v.name, 0); + } else { + push_ident_token(p, v.name, 1); + } case Deref_Expr: visit_expr(p, v.expr); push_generic_token(p, v.op.kind, 0); @@ -991,11 +1028,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { case Union_Type: push_generic_token(p, .Union, 1); - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } + push_poly_params(p, v.poly_params); if v.is_maybe { push_ident_token(p, "#maybe", 1); @@ -1005,13 +1038,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.variants != nil && (len(v.variants) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.variants, true); + visit_exprs(p, v.variants, {.Add_Comma}); push_generic_token(p, .Close_Brace, 0); } else { visit_begin_brace(p, v.pos, .Generic); newline_position(p, 1); set_source_position(p, v.variants[0].pos); - visit_exprs(p, v.variants, true, true); + visit_exprs(p, v.variants, {.Add_Comma, .Trailing}); visit_end_brace(p, v.end); } case Enum_Type: @@ -1025,13 +1058,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { if v.fields != nil && (len(v.fields) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); - visit_exprs(p, v.fields, true); + visit_exprs(p, v.fields, {.Add_Comma}); push_generic_token(p, .Close_Brace, 0); } else { visit_begin_brace(p, v.pos, .Generic, len(v.fields)); newline_position(p, 1); set_source_position(p, v.fields[0].pos); - visit_exprs(p, v.fields, true, true, true); + visit_exprs(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline}); set_source_position(p, v.end); visit_end_brace(p, v.end); } @@ -1042,6 +1075,8 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { hint_current_line(p, {.Struct}); + push_poly_params(p, v.poly_params); + if v.is_packed { push_ident_token(p, "#packed", 1); } @@ -1055,23 +1090,17 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_expr(p, v.align); } - if v.poly_params != nil { - push_generic_token(p, .Open_Paren, 0); - visit_field_list(p, v.poly_params, true, false); - push_generic_token(p, .Close_Paren, 0); - } - push_where_clauses(p, v.where_clauses); if v.fields != nil && (len(v.fields.list) == 0 || v.pos.line == v.end.line) { push_generic_token(p, .Open_Brace, 1); set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true); + visit_field_list(p, v.fields, {.Add_Comma}); push_generic_token(p, .Close_Brace, 0); } else if v.fields != nil { visit_begin_brace(p, v.pos, .Generic, len(v.fields.list)); set_source_position(p, v.fields.pos); - visit_field_list(p, v.fields, true, true, true); + visit_field_list(p, v.fields, {.Add_Comma, .Trailing, .Enforce_Newline}); visit_end_brace(p, v.end); } @@ -1146,11 +1175,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_begin_brace(p, v.pos, .Generic); newline_position(p, 1); set_source_position(p, v.args[0].pos); - visit_exprs(p, v.args, true, true); + visit_exprs(p, v.args, {.Add_Comma, .Trailing}); visit_end_brace(p, v.end); } else { push_generic_token(p, .Open_Brace, 0); - visit_exprs(p, v.args, true); + visit_exprs(p, v.args, {.Add_Comma}); push_generic_token(p, .Close_Brace, 0); } @@ -1163,11 +1192,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr) { visit_begin_brace(p, v.pos, .Comp_Lit, 0); newline_position(p, 1); set_source_position(p, v.elems[0].pos); - visit_exprs(p, v.elems, true, true); + visit_exprs(p, v.elems, {.Add_Comma, .Trailing}); visit_end_brace(p, v.end); } else { push_generic_token(p, .Open_Brace, 0 if v.type != nil else 1); - visit_exprs(p, v.elems, true); + visit_exprs(p, v.elems, {.Add_Comma}); push_generic_token(p, .Close_Brace, 0); } @@ -1270,13 +1299,22 @@ visit_block_stmts :: proc(p: ^Printer, stmts: []^ast.Stmt, split := false) { } } -visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, trailing := false, enforce_newline := false) { +List_Option :: enum u8 { + Add_Comma, + Trailing, + Enforce_Newline, + Enforce_Poly_Names, +} + +List_Options :: distinct bit_set[List_Option]; + +visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Options{}) { if list.list == nil { return; } for field, i in list.list { - if !move_line_limit(p, field.pos, 1) && enforce_newline { + if !move_line_limit(p, field.pos, 1) && .Enforce_Newline in options { newline_position(p, 1); } @@ -1284,7 +1322,12 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, push_generic_token(p, .Using, 1); } - visit_exprs(p, field.names, true); + name_options := List_Options{.Add_Comma}; + if .Enforce_Poly_Names in options { + name_options += {.Enforce_Poly_Names}; + } + + visit_exprs(p, field.names, name_options); if field.type != nil { if len(field.names) != 0 { @@ -1301,7 +1344,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, add_comma := false, push_generic_token(p, field.tag.kind, 1, field.tag.text); } - if (i != len(list.list) - 1 || trailing) && add_comma { + if (i != len(list.list) - 1 || .Trailing in options) && .Add_Comma in options { push_generic_token(p, .Comma, 0); } } @@ -1401,7 +1444,8 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) { #partial switch binary.op.kind { case .Ellipsis: - push_generic_token(p, binary.op.kind, 1 if either_implicit_selector else 0, tokenizer.tokens[tokenizer.Token_Kind.Range_Full]); + push_generic_token(p, binary.op.kind, 1 if either_implicit_selector else 0, + tokenizer.tokens[tokenizer.Token_Kind.Range_Full]); case .Range_Half, .Range_Full: push_generic_token(p, binary.op.kind, 1 if either_implicit_selector else 0); case: @@ -1483,7 +1527,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank := } if named { - visit_exprs(p, field.names, true); + visit_exprs(p, field.names, {.Add_Comma}); if len(field.names) != 0 && field.type != nil { push_generic_token(p, .Colon, 0); From 9e2eb717fe2a146ccd15e66a45d48d48f867e097 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 21 May 2021 15:22:35 +0100 Subject: [PATCH 122/137] Correct `newline_limit` logic --- core/odin/ast/ast.odin | 16 ++++++---------- core/odin/format/format.odin | 6 ++++++ core/odin/parser/parser.odin | 32 ++++++++------------------------ core/odin/printer/visit.odin | 25 ++++++------------------- 4 files changed, 26 insertions(+), 53 deletions(-) diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index cf2cdeacc..b213c4b74 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -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 { diff --git a/core/odin/format/format.odin b/core/odin/format/format.odin index c231e0afd..325881db8 100644 --- a/core/odin/format/format.odin +++ b/core/odin/format/format.odin @@ -11,6 +11,8 @@ 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, }; @@ -21,6 +23,10 @@ format :: proc(filepath: string, source: string, config: printer.Config, parser_ 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); diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index efdee41f4..7eee8f5b5 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1939,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) { @@ -1981,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); diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin index 0e492af70..3eab3b4b3 100644 --- a/core/odin/printer/visit.odin +++ b/core/odin/printer/visit.odin @@ -161,12 +161,12 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { if prev_comment == nil { lines := comment_group.pos.line - p.last_source_position.line; - set_line(p, p.last_line_index + min(p.config.newline_limit, lines)); + set_line(p, p.last_line_index + min(p.config.newline_limit+1, lines)); } for comment, i in comment_group.list { if prev_comment != nil && p.last_source_position.line != comment.pos.line { - newline_position(p, min(p.config.newline_limit, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); + newline_position(p, min(p.config.newline_limit+1, comment.pos.line - prev_comment.pos.line - prev_comment_lines)); } prev_comment_lines = push_comment(p, comment); @@ -177,7 +177,7 @@ push_comments :: proc(p: ^Printer, pos: tokenizer.Pos) { } if prev_comment != nil { - newline_position(p, min(p.config.newline_limit, p.source_position.line - prev_comment.pos.line - prev_comment_lines)); + newline_position(p, min(p.config.newline_limit+1, p.source_position.line - prev_comment.pos.line - prev_comment_lines)); } } @@ -268,7 +268,7 @@ set_source_position :: proc(p: ^Printer, pos: tokenizer.Pos) { @(private) move_line :: proc(p: ^Printer, pos: tokenizer.Pos) { - move_line_limit(p, pos, p.config.newline_limit); + move_line_limit(p, pos, p.config.newline_limit+1); } @(private) @@ -1368,22 +1368,9 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa explicit_calling := false; - switch proc_type.calling_convention { - case .Odin: - case .Contextless: - push_string_token(p, "\"contextless\"", 1); + if v, ok := proc_type.calling_convention.(string); ok { explicit_calling = true; - case .C_Decl: - push_string_token(p, "\"c\"", 1); - explicit_calling = true; - case .Std_Call: - push_string_token(p, "\"std\"", 1); - explicit_calling = true; - case .Fast_Call: - push_string_token(p, "\"fast\"", 1); - explicit_calling = true; - case .None, .Invalid, .Foreign_Block_Default: - // nothing + push_string_token(p, v, 1); } if explicit_calling { From b8f8d4c3a12ea85285511112b33425923418556a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 22 May 2021 11:33:08 +0100 Subject: [PATCH 123/137] Modify ABI for the Odin calling conventions on SysV slightly --- src/llvm_abi.cpp | 44 ++++++++++++++------------------------------ src/llvm_backend.cpp | 11 +++++------ 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index aba85ae83..1347a1cdd 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -11,6 +11,8 @@ struct lbArgType { LLVMTypeRef pad_type; // Optional LLVMAttributeRef attribute; // Optional LLVMAttributeRef align_attribute; // Optional + i64 byval_alignment; + bool is_byval; }; @@ -18,14 +20,14 @@ 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, nullptr}; + 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, nullptr}; + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr, 0, false}; } lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) { @@ -34,11 +36,11 @@ lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) { 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}; + 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, nullptr}; + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr, 0, false}; } struct lbFunctionType { @@ -458,11 +460,10 @@ namespace lbAbiAmd64SysV { Amd64TypeAttribute_StructRect, }; - Array 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 *cls, i64 ix, i64 off); void fixup(LLVMTypeRef t, Array *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 classify(LLVMTypeRef t); LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes); @@ -473,11 +474,11 @@ namespace lbAbiAmd64SysV { ft->args = array_make(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)); } @@ -514,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)) { @@ -527,7 +528,10 @@ namespace lbAbiAmd64SysV { if (is_mem_cls(cls, attribute_kind)) { LLVMAttributeRef attribute = nullptr; if (attribute_kind == Amd64TypeAttribute_ByVal) { - return lb_arg_type_indirect_byval(c, type); + 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_with_type(c, "sret", type); } @@ -817,26 +821,6 @@ namespace lbAbiAmd64SysV { } } - Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) { - auto args = array_make(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_byval(c, t); - } - } 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)); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 3b4e5f3f6..ab2c97557 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3712,11 +3712,8 @@ void lb_build_constant_value_decl(lbProcedure *p, AstValueDecl *vd) { value.type = nested_proc->type; array_add(&p->module->procedures_to_generate, nested_proc); - if (p != nullptr) { - array_add(&p->children, nested_proc); - } else { - string_map_set(&p->module->members, name, value); - } + array_add(&p->children, nested_proc); + string_map_set(&p->module->members, name, value); } } } @@ -8507,7 +8504,9 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, } else if (arg->kind == lbArg_Indirect) { lbValue ptr = {}; - if (is_calling_convention_odin(pt->Proc.calling_convention)) { + if (arg->is_byval) { + ptr = lb_copy_value_to_ptr(p, x, original_type, arg->byval_alignment); + } else if (is_calling_convention_odin(pt->Proc.calling_convention)) { // NOTE(bill): Odin parameters are immutable so the original value can be passed if possible // i.e. `T const &` in C++ ptr = lb_address_from_load_or_generate_local(p, x); From e82f8214e81e87f337d75c76ae0606a07e51afa0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 May 2021 11:46:43 +0100 Subject: [PATCH 124/137] Add `bytes.remove`, `bytes.remove_all`, `strings.remove`, `strings.remove_all` --- core/bytes/bytes.odin | 8 ++++++++ core/strings/strings.odin | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin index 639e36721..b3d332c67 100644 --- a/core/bytes/bytes.odin +++ b/core/bytes/bytes.odin @@ -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}; diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 2aa2ac71d..5537822a8 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -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}; From 71cfa0c9fe3ae50e228aaeee28b163cbcfe10b96 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 May 2021 12:13:13 +0100 Subject: [PATCH 125/137] Clean up organization of `package runtime` --- core/runtime/default_allocators.odin | 41 ++++++- core/runtime/os_specific.odin | 8 ++ core/runtime/os_specific_any.odin | 4 +- core/runtime/os_specific_freestanding.odin | 4 +- core/runtime/os_specific_windows.odin | 126 ++++++++------------- core/runtime/procs_essence.odin | 14 ++- 6 files changed, 109 insertions(+), 88 deletions(-) diff --git a/core/runtime/default_allocators.odin b/core/runtime/default_allocators.odin index b47e60659..7d13f2a32 100644 --- a/core/runtime/default_allocators.odin +++ b/core/runtime/default_allocators.odin @@ -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" diff --git a/core/runtime/os_specific.odin b/core/runtime/os_specific.odin index 0ba11d0e6..03acf4279 100644 --- a/core/runtime/os_specific.odin +++ b/core/runtime/os_specific.odin @@ -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(); +} diff --git a/core/runtime/os_specific_any.odin b/core/runtime/os_specific_any.odin index 2e52490ec..9c2686661 100644 --- a/core/runtime/os_specific_any.odin +++ b/core/runtime/os_specific_any.odin @@ -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(); } diff --git a/core/runtime/os_specific_freestanding.odin b/core/runtime/os_specific_freestanding.odin index 2c715ccb7..cae41daae 100644 --- a/core/runtime/os_specific_freestanding.odin +++ b/core/runtime/os_specific_freestanding.odin @@ -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; } diff --git a/core/runtime/os_specific_windows.odin b/core/runtime/os_specific_windows.odin index 0cf9d28f4..f5a9d744d 100644 --- a/core/runtime/os_specific_windows.odin +++ b/core/runtime/os_specific_windows.odin @@ -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); } diff --git a/core/runtime/procs_essence.odin b/core/runtime/procs_essence.odin index a2cd26df6..6e6020b83 100644 --- a/core/runtime/procs_essence.odin +++ b/core/runtime/procs_essence.odin @@ -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); } From 39eccdf6b947ee57e6e2c0921e7f733bd78dd8a8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 23 May 2021 15:17:58 +0100 Subject: [PATCH 126/137] Make `js` default to nil allocator for the time being --- core/runtime/default_allocators.odin | 2 +- core/runtime/internal.odin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/runtime/default_allocators.odin b/core/runtime/default_allocators.odin index 7d13f2a32..86fce47ae 100644 --- a/core/runtime/default_allocators.odin +++ b/core/runtime/default_allocators.odin @@ -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, diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 8a7b22ca4..be1a69316 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -623,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); From 79f115d6a7eed3000a7ca8d40c4a8db2b79dd595 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 14:46:03 +0100 Subject: [PATCH 127/137] Handle #c_vararg correctly --- src/llvm_backend.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ab2c97557..c009e846a 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8519,6 +8519,12 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, param_index += 1; } + if (is_c_vararg) { + for (isize i = processed_args.count; i < args.count; i++) { + array_add(&processed_args, args[i]); + } + } + if (inlining == ProcInlining_none) { inlining = p->inlining; } From c440296ae8b0f171fc0f7df311831954c9992162 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 15:41:22 +0100 Subject: [PATCH 128/137] Add `@(link_section=)` for global variables --- src/check_decl.cpp | 3 +++ src/checker.cpp | 10 ++++++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend.cpp | 3 +++ 5 files changed, 18 insertions(+) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0aef40546..323de6d43 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -934,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; diff --git a/src/checker.cpp b/src/checker.cpp index 21ca4c398..8f426f116 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -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; } diff --git a/src/checker.hpp b/src/checker.hpp index 38628ed51..f0f116a02 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -109,6 +109,7 @@ 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; diff --git a/src/entity.cpp b/src/entity.cpp index 460f4ec6d..173a3fcd0 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -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; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c009e846a..a0294aee0 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -14880,6 +14880,9 @@ void lb_generate_code(lbGenerator *gen) { LLVMSetLinkage(g.value, LLVMInternalLinkage); } } + if (e->Variable.link_section.len > 0) { + LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section)); + } lbGlobalVariable var = {}; var.var = g; From d35a9e65b68e9d38f470c3cf0e54909e08d7bde8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 20:57:44 +0100 Subject: [PATCH 129/137] Heavily improve the copy elision logic in the backend --- src/llvm_backend.cpp | 140 +++++++++++++++++++++++++++++-------------- src/llvm_backend.hpp | 11 +++- 2 files changed, 104 insertions(+), 47 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a0294aee0..bc519f70d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4927,6 +4927,23 @@ lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast return res; } +lbCopyElisionHint lb_set_copy_elision_hint(lbProcedure *p, lbAddr const &addr, Ast *ast) { + lbCopyElisionHint prev = p->copy_elision_hint; + p->copy_elision_hint.used = false; + p->copy_elision_hint.ptr = {}; + p->copy_elision_hint.ast = nullptr; + if (addr.kind == lbAddr_Default && addr.addr.value != nullptr) { + p->copy_elision_hint.ptr = lb_addr_get_ptr(p, addr); + p->copy_elision_hint.ast = unparen_expr(ast); + } + return prev; +} + +void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint) { + p->copy_elision_hint = prev_hint; +} + + void lb_build_stmt(lbProcedure *p, Ast *node) { Ast *prev_stmt = p->curr_stmt; defer (p->curr_stmt = prev_stmt); @@ -5084,6 +5101,47 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { lb_add_local(p, e->type, e, true); } } + } else if (vd->names.count == vd->values.count) { + auto lvals = array_make(permanent_allocator(), 0, vd->names.count); + auto inits = array_make(permanent_allocator(), 0, vd->names.count); + + for_array(i, vd->names) { + Ast *name = vd->names[i]; + lbAddr lval = {}; + if (!is_blank_ident(name)) { + Entity *e = entity_of_node(name); + bool zero_init = true; + if (vd->names.count == vd->values.count) { + // Possibly uses copy elision + // Make the caller mem zero + zero_init = true; + } + lval = lb_add_local(p, e->type, e, zero_init); + } + array_add(&lvals, lval); + } + + for_array(i, vd->values) { + Ast *rhs = unparen_expr(vd->values[i]); + + auto prev_hint = lb_set_copy_elision_hint(p, lvals[i], rhs); + + lbValue init = lb_build_expr(p, rhs); + Type *t = init.type; + GB_ASSERT(t->kind != Type_Tuple); + array_add(&inits, init); + + if (p->copy_elision_hint.used) { + lvals[i] = {}; // zero lval + } + lb_reset_copy_elision_hint(p, prev_hint); + } + + for_array(i, inits) { + lbAddr lval = lvals[i]; + lbValue init = inits[i]; + lb_addr_store(p, lval, init); + } } else { // Tuple(s) auto lvals = array_make(permanent_allocator(), 0, vd->names.count); auto inits = array_make(permanent_allocator(), 0, vd->names.count); @@ -5093,13 +5151,15 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { lbAddr lval = {}; if (!is_blank_ident(name)) { Entity *e = entity_of_node(name); - lval = lb_add_local(p, e->type, e, false); + bool zero_init = false; + lval = lb_add_local(p, e->type, e, zero_init); } array_add(&lvals, lval); } for_array(i, vd->values) { - lbValue init = lb_build_expr(p, vd->values[i]); + Ast *rhs = unparen_expr(vd->values[i]); + lbValue init = lb_build_expr(p, rhs); Type *t = init.type; if (t->kind == Type_Tuple) { for_array(i, t->Tuple.variables) { @@ -5112,7 +5172,6 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } } - for_array(i, inits) { lbAddr lval = lvals[i]; lbValue init = inits[i]; @@ -5135,24 +5194,26 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } if (as->lhs.count == as->rhs.count) { - if (as->lhs.count == 1) { - lbAddr lval = lvals[0]; - Ast *rhs = as->rhs[0]; + auto inits = array_make(permanent_allocator(), 0, lvals.count); + + for_array(i, as->rhs) { + Ast *rhs = unparen_expr(as->rhs[i]); + + auto prev_hint = lb_set_copy_elision_hint(p, lvals[i], rhs); + lbValue init = lb_build_expr(p, rhs); - lb_addr_store(p, lvals[0], init); - } else { - auto inits = array_make(permanent_allocator(), 0, lvals.count); + array_add(&inits, init); - for_array(i, as->rhs) { - lbValue init = lb_build_expr(p, as->rhs[i]); - array_add(&inits, init); + if (p->copy_elision_hint.used) { + lvals[i] = {}; // zero lval } + lb_reset_copy_elision_hint(p, prev_hint); + } - for_array(i, inits) { - lbAddr lval = lvals[i]; - lbValue init = inits[i]; - lb_addr_store(p, lval, init); - } + for_array(i, inits) { + lbAddr lval = lvals[i]; + lbValue init = inits[i]; + lb_addr_store(p, lval, init); } } else { auto inits = array_make(permanent_allocator(), 0, lvals.count); @@ -8426,7 +8487,7 @@ lbValue lb_emit_runtime_call(lbProcedure *p, char const *c_name, Array return lb_emit_call(p, proc, args); } -lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, bool use_return_ptr_hint) { +lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, ProcInlining inlining, bool use_copy_elision_hint) { lbModule *m = p->module; Type *pt = base_type(value.type); @@ -8532,10 +8593,13 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, Type *rt = reduce_tuple_to_single_type(results); if (return_by_pointer) { lbValue return_ptr = {}; - if (use_return_ptr_hint && p->return_ptr_hint_value.value != nullptr) { - if (are_types_identical(type_deref(p->return_ptr_hint_value.type), rt)) { - return_ptr = p->return_ptr_hint_value; - p->return_ptr_hint_used = true; + if (use_copy_elision_hint && p->copy_elision_hint.ptr.value != nullptr) { + if (are_types_identical(type_deref(p->copy_elision_hint.ptr.type), rt)) { + return_ptr = p->copy_elision_hint.ptr; + p->copy_elision_hint.used = true; + // consume it + p->copy_elision_hint.ptr = {}; + p->copy_elision_hint.ast = nullptr; } } if (return_ptr.value == nullptr) { @@ -9958,7 +10022,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { } } - return lb_emit_call(p, value, args, ce->inlining, p->return_ptr_hint_ast == expr); + return lb_emit_call(p, value, args, ce->inlining, p->copy_elision_hint.ast == expr); } isize arg_index = 0; @@ -10140,7 +10204,7 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { } auto call_args = array_slice(args, 0, final_count); - return lb_emit_call(p, value, call_args, ce->inlining, p->return_ptr_hint_ast == expr); + return lb_emit_call(p, value, call_args, ce->inlining, p->copy_elision_hint.ast == expr); } bool lb_is_const(lbValue value) { @@ -12751,18 +12815,10 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { } for_array(i, temp_data) { - auto return_ptr_hint_ast = p->return_ptr_hint_ast; - auto return_ptr_hint_value = p->return_ptr_hint_value; - auto return_ptr_hint_used = p->return_ptr_hint_used; - defer (p->return_ptr_hint_ast = return_ptr_hint_ast); - defer (p->return_ptr_hint_value = return_ptr_hint_value); - defer (p->return_ptr_hint_used = return_ptr_hint_used); - lbValue field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - p->return_ptr_hint_value = temp_data[i].gep; - p->return_ptr_hint_ast = unparen_expr(expr); + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); if (field_expr.value == nullptr) { field_expr = lb_build_expr(p, expr); @@ -12771,9 +12827,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { GB_ASSERT(t->kind != Type_Tuple); lbValue ev = lb_emit_conv(p, field_expr, et); - if (!p->return_ptr_hint_used) { + if (!p->copy_elision_hint.used) { temp_data[i].value = ev; } + + lb_reset_copy_elision_hint(p, prev_hint); } for_array(i, temp_data) { @@ -12854,18 +12912,10 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { } for_array(i, temp_data) { - auto return_ptr_hint_ast = p->return_ptr_hint_ast; - auto return_ptr_hint_value = p->return_ptr_hint_value; - auto return_ptr_hint_used = p->return_ptr_hint_used; - defer (p->return_ptr_hint_ast = return_ptr_hint_ast); - defer (p->return_ptr_hint_value = return_ptr_hint_value); - defer (p->return_ptr_hint_used = return_ptr_hint_used); - lbValue field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - p->return_ptr_hint_value = temp_data[i].gep; - p->return_ptr_hint_ast = unparen_expr(expr); + auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr); if (field_expr.value == nullptr) { field_expr = lb_build_expr(p, expr); @@ -12874,9 +12924,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { GB_ASSERT(t->kind != Type_Tuple); lbValue ev = lb_emit_conv(p, field_expr, et); - if (!p->return_ptr_hint_used) { + if (!p->copy_elision_hint.used) { temp_data[i].value = ev; } + + lb_reset_copy_elision_hint(p, prev_hint); } for_array(i, temp_data) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index b35c042ee..8f50650a8 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -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; }; @@ -413,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" From 3f156bcb4b4d428c9d8547d63de145f01080d043 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 22:09:21 +0100 Subject: [PATCH 130/137] Refactor backend code for assignments; Refactor some statements into separate procedures --- src/llvm_backend.cpp | 736 ++++++++++++++++++++----------------------- 1 file changed, 336 insertions(+), 400 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index bc519f70d..9121bc083 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -4425,7 +4425,7 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) { lb_start_block(p, done); } -void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *scope) { +void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *scope) { lbModule *m = p->module; lb_open_scope(p, scope); // Open scope here @@ -4943,6 +4943,321 @@ void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint) { p->copy_elision_hint = prev_hint; } +void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { + for_array(i, vd->names) { + lbValue value = {}; + if (vd->values.count > 0) { + GB_ASSERT(vd->names.count == vd->values.count); + Ast *ast_value = vd->values[i]; + GB_ASSERT(ast_value->tav.mode == Addressing_Constant || + ast_value->tav.mode == Addressing_Invalid); + + bool allow_local = false; + value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, allow_local); + } + + Ast *ident = vd->names[i]; + GB_ASSERT(!is_blank_ident(ident)); + Entity *e = entity_of_node(ident); + GB_ASSERT(e->flags & EntityFlag_Static); + String name = e->token.string; + + String mangled_name = {}; + { + gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len); + str = gb_string_appendc(str, "-"); + str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id); + mangled_name.text = cast(u8 *)str; + mangled_name.len = gb_string_length(str); + } + + char *c_name = alloc_cstring(permanent_allocator(), mangled_name); + + LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name); + LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); + if (value.value != nullptr) { + LLVMSetInitializer(global, value.value); + } else { + } + if (e->Variable.thread_local_model != "") { + LLVMSetThreadLocal(global, true); + + String m = e->Variable.thread_local_model; + LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; + if (m == "default") { + mode = LLVMGeneralDynamicTLSModel; + } else if (m == "localdynamic") { + mode = LLVMLocalDynamicTLSModel; + } else if (m == "initialexec") { + mode = LLVMInitialExecTLSModel; + } else if (m == "localexec") { + mode = LLVMLocalExecTLSModel; + } else { + GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); + } + LLVMSetThreadLocalMode(global, mode); + } else { + LLVMSetLinkage(global, LLVMInternalLinkage); + } + + + lbValue global_val = {global, alloc_type_pointer(e->type)}; + lb_add_entity(p->module, e, global_val); + lb_add_member(p->module, mangled_name, global_val); + } +} + + +void lb_build_assignment(lbProcedure *p, Array &lvals, Slice const &values) { + if (values.count == 0) { + return; + } + + auto inits = array_make(permanent_allocator(), 0, lvals.count); + + for_array(i, values) { + Ast *rhs = values[i]; + if (is_type_tuple(type_of_expr(rhs))) { + lbValue init = lb_build_expr(p, rhs); + Type *t = init.type; + GB_ASSERT(t->kind == Type_Tuple); + for_array(i, t->Tuple.variables) { + Entity *e = t->Tuple.variables[i]; + lbValue v = lb_emit_struct_ev(p, init, cast(i32)i); + array_add(&inits, v); + } + } else { + auto prev_hint = lb_set_copy_elision_hint(p, lvals[inits.count], rhs); + lbValue init = lb_build_expr(p, rhs); + if (p->copy_elision_hint.used) { + lvals[inits.count] = {}; // zero lval + } + lb_reset_copy_elision_hint(p, prev_hint); + array_add(&inits, init); + } + } + + GB_ASSERT(lvals.count == inits.count); + for_array(i, inits) { + lbAddr lval = lvals[i]; + lbValue init = inits[i]; + lb_addr_store(p, lval, init); + } +} + +void lb_build_return_stmt(lbProcedure *p, AstReturnStmt *rs) { + lbValue res = {}; + + TypeTuple *tuple = &p->type->Proc.results->Tuple; + isize return_count = p->type->Proc.result_count; + isize res_count = rs->results.count; + + if (return_count == 0) { + // No return values + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + + LLVMBuildRetVoid(p->builder); + return; + } else if (return_count == 1) { + Entity *e = tuple->variables[0]; + if (res_count == 0) { + lbValue *found = map_get(&p->module->values, hash_entity(e)); + GB_ASSERT(found); + res = lb_emit_load(p, *found); + } else { + res = lb_build_expr(p, rs->results[0]); + res = lb_emit_conv(p, res, e->type); + } + if (p->type->Proc.has_named_results) { + // NOTE(bill): store the named values before returning + if (e->token.string != "") { + lbValue *found = map_get(&p->module->values, hash_entity(e)); + GB_ASSERT(found != nullptr); + lb_emit_store(p, *found, lb_emit_conv(p, res, e->type)); + } + } + + } else { + auto results = array_make(permanent_allocator(), 0, return_count); + + if (res_count != 0) { + for (isize res_index = 0; res_index < res_count; res_index++) { + lbValue res = lb_build_expr(p, rs->results[res_index]); + Type *t = res.type; + if (t->kind == Type_Tuple) { + for_array(i, t->Tuple.variables) { + Entity *e = t->Tuple.variables[i]; + lbValue v = lb_emit_struct_ev(p, res, cast(i32)i); + array_add(&results, v); + } + } else { + array_add(&results, res); + } + } + } else { + for (isize res_index = 0; res_index < return_count; res_index++) { + Entity *e = tuple->variables[res_index]; + lbValue *found = map_get(&p->module->values, hash_entity(e)); + GB_ASSERT(found); + lbValue res = lb_emit_load(p, *found); + array_add(&results, res); + } + } + + GB_ASSERT(results.count == return_count); + + if (p->type->Proc.has_named_results) { + // NOTE(bill): store the named values before returning + for_array(i, p->type->Proc.results->Tuple.variables) { + Entity *e = p->type->Proc.results->Tuple.variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + + if (e->token.string == "") { + continue; + } + lbValue *found = map_get(&p->module->values, hash_entity(e)); + GB_ASSERT(found != nullptr); + lb_emit_store(p, *found, lb_emit_conv(p, results[i], e->type)); + } + } + + Type *ret_type = p->type->Proc.results; + // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops + res = lb_add_local_generated(p, ret_type, false).addr; + for_array(i, results) { + Entity *e = tuple->variables[i]; + lbValue field = lb_emit_struct_ep(p, res, cast(i32)i); + lbValue val = lb_emit_conv(p, results[i], e->type); + lb_emit_store(p, field, val); + } + + res = lb_emit_load(p, res); + } + + + lb_ensure_abi_function_type(p->module, p); + if (p->abi_function_type->ret.kind == lbArg_Indirect) { + if (res.value != nullptr) { + LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value); + } else { + LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value); + } + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + + LLVMBuildRetVoid(p->builder); + } else { + LLVMValueRef ret_val = res.value; + ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type); + if (p->abi_function_type->ret.cast_type != nullptr) { + ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type); + } + + lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); + LLVMBuildRet(p->builder, ret_val); + } +} + +void lb_build_if_stmt(lbProcedure *p, Ast *node) { + ast_node(is, IfStmt, node); + lb_open_scope(p, node->scope); // Scope #1 + + if (is->init != nullptr) { + // TODO(bill): Should this have a separate block to begin with? + #if 1 + lbBlock *init = lb_create_block(p, "if.init"); + lb_emit_jump(p, init); + lb_start_block(p, init); + #endif + lb_build_stmt(p, is->init); + } + lbBlock *then = lb_create_block(p, "if.then"); + lbBlock *done = lb_create_block(p, "if.done"); + lbBlock *else_ = done; + if (is->else_stmt != nullptr) { + else_ = lb_create_block(p, "if.else"); + } + + lb_build_cond(p, is->cond, then, else_); + lb_start_block(p, then); + + if (is->label != nullptr) { + lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr); + tl->is_block = true; + } + + lb_build_stmt(p, is->body); + + lb_emit_jump(p, done); + + if (is->else_stmt != nullptr) { + lb_start_block(p, else_); + + lb_open_scope(p, is->else_stmt->scope); + lb_build_stmt(p, is->else_stmt); + lb_close_scope(p, lbDeferExit_Default, nullptr); + + lb_emit_jump(p, done); + } + + lb_start_block(p, done); + lb_close_scope(p, lbDeferExit_Default, nullptr); +} + +void lb_build_for_stmt(lbProcedure *p, Ast *node) { + ast_node(fs, ForStmt, node); + + lb_open_scope(p, node->scope); // Open Scope here + + if (fs->init != nullptr) { + #if 1 + lbBlock *init = lb_create_block(p, "for.init"); + lb_emit_jump(p, init); + lb_start_block(p, init); + #endif + lb_build_stmt(p, fs->init); + } + lbBlock *body = lb_create_block(p, "for.body"); + lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later + lbBlock *loop = body; + if (fs->cond != nullptr) { + loop = lb_create_block(p, "for.loop"); + } + lbBlock *post = loop; + if (fs->post != nullptr) { + post = lb_create_block(p, "for.post"); + } + + + lb_emit_jump(p, loop); + lb_start_block(p, loop); + + if (loop != body) { + lb_build_cond(p, fs->cond, body, done); + lb_start_block(p, body); + } + + lb_push_target_list(p, fs->label, done, post, nullptr); + + lb_build_stmt(p, fs->body); + lb_close_scope(p, lbDeferExit_Default, nullptr); + + lb_pop_target_list(p); + + lb_emit_jump(p, post); + + if (fs->post != nullptr) { + lb_start_block(p, post); + lb_build_stmt(p, fs->post); + lb_emit_jump(p, loop); + } + + lb_start_block(p, done); +} + void lb_build_stmt(lbProcedure *p, Ast *node) { Ast *prev_stmt = p->curr_stmt; @@ -5028,156 +5343,24 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } if (is_static) { - for_array(i, vd->names) { - lbValue value = {}; - if (vd->values.count > 0) { - GB_ASSERT(vd->names.count == vd->values.count); - Ast *ast_value = vd->values[i]; - GB_ASSERT(ast_value->tav.mode == Addressing_Constant || - ast_value->tav.mode == Addressing_Invalid); - - bool allow_local = false; - value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, allow_local); - } - - Ast *ident = vd->names[i]; - GB_ASSERT(!is_blank_ident(ident)); - Entity *e = entity_of_node(ident); - GB_ASSERT(e->flags & EntityFlag_Static); - String name = e->token.string; - - String mangled_name = {}; - { - gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len); - str = gb_string_appendc(str, "-"); - str = gb_string_append_fmt(str, ".%.*s-%llu", LIT(name), cast(long long)e->id); - mangled_name.text = cast(u8 *)str; - mangled_name.len = gb_string_length(str); - } - - char *c_name = alloc_cstring(permanent_allocator(), mangled_name); - - LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name); - LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); - if (value.value != nullptr) { - LLVMSetInitializer(global, value.value); - } else { - } - if (e->Variable.thread_local_model != "") { - LLVMSetThreadLocal(global, true); - - String m = e->Variable.thread_local_model; - LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel; - if (m == "default") { - mode = LLVMGeneralDynamicTLSModel; - } else if (m == "localdynamic") { - mode = LLVMLocalDynamicTLSModel; - } else if (m == "initialexec") { - mode = LLVMInitialExecTLSModel; - } else if (m == "localexec") { - mode = LLVMLocalExecTLSModel; - } else { - GB_PANIC("Unhandled thread local mode %.*s", LIT(m)); - } - LLVMSetThreadLocalMode(global, mode); - } else { - LLVMSetLinkage(global, LLVMInternalLinkage); - } - - - lbValue global_val = {global, alloc_type_pointer(e->type)}; - lb_add_entity(p->module, e, global_val); - lb_add_member(p->module, mangled_name, global_val); - } + lb_build_static_variables(p, vd); return; } - if (vd->values.count == 0) { // declared and zero-initialized - for_array(i, vd->names) { - Ast *name = vd->names[i]; - if (!is_blank_ident(name)) { - Entity *e = entity_of_node(name); - lb_add_local(p, e->type, e, true); - } - } - } else if (vd->names.count == vd->values.count) { - auto lvals = array_make(permanent_allocator(), 0, vd->names.count); - auto inits = array_make(permanent_allocator(), 0, vd->names.count); - - for_array(i, vd->names) { - Ast *name = vd->names[i]; - lbAddr lval = {}; - if (!is_blank_ident(name)) { - Entity *e = entity_of_node(name); - bool zero_init = true; - if (vd->names.count == vd->values.count) { - // Possibly uses copy elision - // Make the caller mem zero - zero_init = true; - } - lval = lb_add_local(p, e->type, e, zero_init); - } - array_add(&lvals, lval); - } - - for_array(i, vd->values) { - Ast *rhs = unparen_expr(vd->values[i]); - - auto prev_hint = lb_set_copy_elision_hint(p, lvals[i], rhs); - - lbValue init = lb_build_expr(p, rhs); - Type *t = init.type; - GB_ASSERT(t->kind != Type_Tuple); - array_add(&inits, init); - - if (p->copy_elision_hint.used) { - lvals[i] = {}; // zero lval - } - lb_reset_copy_elision_hint(p, prev_hint); - } - - for_array(i, inits) { - lbAddr lval = lvals[i]; - lbValue init = inits[i]; - lb_addr_store(p, lval, init); - } - } else { // Tuple(s) - auto lvals = array_make(permanent_allocator(), 0, vd->names.count); - auto inits = array_make(permanent_allocator(), 0, vd->names.count); - - for_array(i, vd->names) { - Ast *name = vd->names[i]; - lbAddr lval = {}; - if (!is_blank_ident(name)) { - Entity *e = entity_of_node(name); - bool zero_init = false; - lval = lb_add_local(p, e->type, e, zero_init); - } - array_add(&lvals, lval); - } - - for_array(i, vd->values) { - Ast *rhs = unparen_expr(vd->values[i]); - lbValue init = lb_build_expr(p, rhs); - Type *t = init.type; - if (t->kind == Type_Tuple) { - for_array(i, t->Tuple.variables) { - Entity *e = t->Tuple.variables[i]; - lbValue v = lb_emit_struct_ev(p, init, cast(i32)i); - array_add(&inits, v); - } - } else { - array_add(&inits, init); - } - } - - for_array(i, inits) { - lbAddr lval = lvals[i]; - lbValue init = inits[i]; - lb_addr_store(p, lval, init); + auto lvals = array_make(permanent_allocator(), 0, vd->names.count); + + for_array(i, vd->names) { + Ast *name = vd->names[i]; + lbAddr lval = {}; + if (!is_blank_ident(name)) { + Entity *e = entity_of_node(name); + bool zero_init = true; // Zero always and optimize out later + lval = lb_add_local(p, e->type, e, zero_init); } + array_add(&lvals, lval); } + lb_build_assignment(p, lvals, vd->values); case_end; case_ast_node(as, AssignStmt, node); @@ -5192,54 +5375,10 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { } array_add(&lvals, lval); } - - if (as->lhs.count == as->rhs.count) { - auto inits = array_make(permanent_allocator(), 0, lvals.count); - - for_array(i, as->rhs) { - Ast *rhs = unparen_expr(as->rhs[i]); - - auto prev_hint = lb_set_copy_elision_hint(p, lvals[i], rhs); - - lbValue init = lb_build_expr(p, rhs); - array_add(&inits, init); - - if (p->copy_elision_hint.used) { - lvals[i] = {}; // zero lval - } - lb_reset_copy_elision_hint(p, prev_hint); - } - - for_array(i, inits) { - lbAddr lval = lvals[i]; - lbValue init = inits[i]; - lb_addr_store(p, lval, init); - } - } else { - auto inits = array_make(permanent_allocator(), 0, lvals.count); - - for_array(i, as->rhs) { - lbValue init = lb_build_expr(p, as->rhs[i]); - Type *t = init.type; - // TODO(bill): refactor for code reuse as this is repeated a bit - if (t->kind == Type_Tuple) { - for_array(i, t->Tuple.variables) { - Entity *e = t->Tuple.variables[i]; - lbValue v = lb_emit_struct_ev(p, init, cast(i32)i); - array_add(&inits, v); - } - } else { - array_add(&inits, init); - } - } - - for_array(i, inits) { - lbAddr lval = lvals[i]; - lbValue init = inits[i]; - lb_addr_store(p, lval, init); - } - } + lb_build_assignment(p, lvals, as->rhs); } else { + GB_ASSERT(as->lhs.count == 1); + GB_ASSERT(as->rhs.count == 1); // NOTE(bill): Only 1 += 1 is allowed, no tuples // +=, -=, etc i32 op = cast(i32)as->op.kind; @@ -5270,222 +5409,19 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { case_end; case_ast_node(ds, DeferStmt, node); - isize scope_index = p->scope_index; - lb_add_defer_node(p, scope_index, ds->stmt); + lb_add_defer_node(p, p->scope_index, ds->stmt); case_end; case_ast_node(rs, ReturnStmt, node); - lbValue res = {}; - - TypeTuple *tuple = &p->type->Proc.results->Tuple; - isize return_count = p->type->Proc.result_count; - isize res_count = rs->results.count; - - if (return_count == 0) { - // No return values - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRetVoid(p->builder); - return; - } else if (return_count == 1) { - Entity *e = tuple->variables[0]; - if (res_count == 0) { - lbValue *found = map_get(&p->module->values, hash_entity(e)); - GB_ASSERT(found); - res = lb_emit_load(p, *found); - } else { - res = lb_build_expr(p, rs->results[0]); - res = lb_emit_conv(p, res, e->type); - } - if (p->type->Proc.has_named_results) { - // NOTE(bill): store the named values before returning - if (e->token.string != "") { - lbValue *found = map_get(&p->module->values, hash_entity(e)); - GB_ASSERT(found != nullptr); - lb_emit_store(p, *found, lb_emit_conv(p, res, e->type)); - } - } - - } else { - auto results = array_make(permanent_allocator(), 0, return_count); - - if (res_count != 0) { - for (isize res_index = 0; res_index < res_count; res_index++) { - lbValue res = lb_build_expr(p, rs->results[res_index]); - Type *t = res.type; - if (t->kind == Type_Tuple) { - for_array(i, t->Tuple.variables) { - Entity *e = t->Tuple.variables[i]; - lbValue v = lb_emit_struct_ev(p, res, cast(i32)i); - array_add(&results, v); - } - } else { - array_add(&results, res); - } - } - } else { - for (isize res_index = 0; res_index < return_count; res_index++) { - Entity *e = tuple->variables[res_index]; - lbValue *found = map_get(&p->module->values, hash_entity(e)); - GB_ASSERT(found); - lbValue res = lb_emit_load(p, *found); - array_add(&results, res); - } - } - - GB_ASSERT(results.count == return_count); - - if (p->type->Proc.has_named_results) { - // NOTE(bill): store the named values before returning - for_array(i, p->type->Proc.results->Tuple.variables) { - Entity *e = p->type->Proc.results->Tuple.variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - - if (e->token.string == "") { - continue; - } - lbValue *found = map_get(&p->module->values, hash_entity(e)); - GB_ASSERT(found != nullptr); - lb_emit_store(p, *found, lb_emit_conv(p, results[i], e->type)); - } - } - - Type *ret_type = p->type->Proc.results; - // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops - res = lb_add_local_generated(p, ret_type, false).addr; - for_array(i, results) { - Entity *e = tuple->variables[i]; - lbValue field = lb_emit_struct_ep(p, res, cast(i32)i); - lbValue val = lb_emit_conv(p, results[i], e->type); - lb_emit_store(p, field, val); - } - - res = lb_emit_load(p, res); - } - - - lb_ensure_abi_function_type(p->module, p); - if (p->abi_function_type->ret.kind == lbArg_Indirect) { - if (res.value != nullptr) { - LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value); - } else { - LLVMBuildStore(p->builder, LLVMConstNull(p->abi_function_type->ret.type), p->return_ptr.addr.value); - } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - - LLVMBuildRetVoid(p->builder); - } else { - LLVMValueRef ret_val = res.value; - ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.type); - if (p->abi_function_type->ret.cast_type != nullptr) { - ret_val = OdinLLVMBuildTransmute(p, ret_val, p->abi_function_type->ret.cast_type); - } - - lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr); - LLVMBuildRet(p->builder, ret_val); - } - - - + lb_build_return_stmt(p, rs); case_end; case_ast_node(is, IfStmt, node); - lb_open_scope(p, node->scope); // Scope #1 - - if (is->init != nullptr) { - // TODO(bill): Should this have a separate block to begin with? - #if 1 - lbBlock *init = lb_create_block(p, "if.init"); - lb_emit_jump(p, init); - lb_start_block(p, init); - #endif - lb_build_stmt(p, is->init); - } - lbBlock *then = lb_create_block(p, "if.then"); - lbBlock *done = lb_create_block(p, "if.done"); - lbBlock *else_ = done; - if (is->else_stmt != nullptr) { - else_ = lb_create_block(p, "if.else"); - } - - lb_build_cond(p, is->cond, then, else_); - lb_start_block(p, then); - - if (is->label != nullptr) { - lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr); - tl->is_block = true; - } - - lb_build_stmt(p, is->body); - - lb_emit_jump(p, done); - - if (is->else_stmt != nullptr) { - lb_start_block(p, else_); - - lb_open_scope(p, is->else_stmt->scope); - lb_build_stmt(p, is->else_stmt); - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_emit_jump(p, done); - } - - - lb_start_block(p, done); - lb_close_scope(p, lbDeferExit_Default, nullptr); + lb_build_if_stmt(p, node); case_end; case_ast_node(fs, ForStmt, node); - lb_open_scope(p, node->scope); // Open Scope here - - if (fs->init != nullptr) { - #if 1 - lbBlock *init = lb_create_block(p, "for.init"); - lb_emit_jump(p, init); - lb_start_block(p, init); - #endif - lb_build_stmt(p, fs->init); - } - lbBlock *body = lb_create_block(p, "for.body"); - lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later - lbBlock *loop = body; - if (fs->cond != nullptr) { - loop = lb_create_block(p, "for.loop"); - } - lbBlock *post = loop; - if (fs->post != nullptr) { - post = lb_create_block(p, "for.post"); - } - - - lb_emit_jump(p, loop); - lb_start_block(p, loop); - - if (loop != body) { - lb_build_cond(p, fs->cond, body, done); - lb_start_block(p, body); - } - - lb_push_target_list(p, fs->label, done, post, nullptr); - - lb_build_stmt(p, fs->body); - lb_close_scope(p, lbDeferExit_Default, nullptr); - - lb_pop_target_list(p); - - lb_emit_jump(p, post); - - if (fs->post != nullptr) { - lb_start_block(p, post); - lb_build_stmt(p, fs->post); - lb_emit_jump(p, loop); - } - - lb_start_block(p, done); + lb_build_for_stmt(p, node); case_end; case_ast_node(rs, RangeStmt, node); @@ -5493,7 +5429,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { case_end; case_ast_node(rs, UnrollRangeStmt, node); - lb_build_inline_range_stmt(p, rs, node->scope); + lb_build_unroll_range_stmt(p, rs, node->scope); case_end; case_ast_node(ss, SwitchStmt, node); From 284a2631fd809b520e9dbde36c3ee3d270e6422c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 22:16:22 +0100 Subject: [PATCH 131/137] Refactoring of lbFunctionType retrieval --- src/llvm_backend.cpp | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 9121bc083..38500fe1d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1584,6 +1584,18 @@ LLVMTypeRef lb_type(lbModule *m, Type *type) { return llvm_type; } +lbFunctionType *lb_get_function_type(lbModule *m, lbProcedure *p, Type *pt) { + lbFunctionType **ft_found = nullptr; + ft_found = map_get(&m->function_type_map, hash_type(pt)); + if (!ft_found) { + LLVMTypeRef llvm_proc_type = lb_type(p->module, pt); + ft_found = map_get(&m->function_type_map, hash_type(pt)); + } + GB_ASSERT(ft_found != nullptr); + + return *ft_found; +} + LLVMMetadataRef lb_get_llvm_metadata(lbModule *m, void *key) { if (key == nullptr) { @@ -4943,6 +4955,14 @@ void lb_reset_copy_elision_hint(lbProcedure *p, lbCopyElisionHint prev_hint) { p->copy_elision_hint = prev_hint; } +lbValue lb_consume_copy_elision_hint(lbProcedure *p) { + lbValue return_ptr = p->copy_elision_hint.ptr; + p->copy_elision_hint.used = true; + p->copy_elision_hint.ptr = {}; + p->copy_elision_hint.ast = nullptr; + return return_ptr; +} + void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { for_array(i, vd->names) { lbValue value = {}; @@ -5052,6 +5072,9 @@ void lb_build_return_stmt(lbProcedure *p, AstReturnStmt *rs) { isize return_count = p->type->Proc.result_count; isize res_count = rs->results.count; + lbFunctionType *ft = lb_get_function_type(p->module, p, p->type); + bool return_by_pointer = ft->ret.kind == lbArg_Indirect; + if (return_count == 0) { // No return values @@ -5139,7 +5162,7 @@ void lb_build_return_stmt(lbProcedure *p, AstReturnStmt *rs) { lb_ensure_abi_function_type(p->module, p); - if (p->abi_function_type->ret.kind == lbArg_Indirect) { + if (return_by_pointer) { if (res.value != nullptr) { LLVMBuildStore(p->builder, res.value, p->return_ptr.addr.value); } else { @@ -8459,15 +8482,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, auto processed_args = array_make(permanent_allocator(), 0, args.count); { - lbFunctionType **ft_found = nullptr; - ft_found = map_get(&m->function_type_map, hash_type(pt)); - if (!ft_found) { - LLVMTypeRef llvm_proc_type = lb_type(p->module, pt); - ft_found = map_get(&m->function_type_map, hash_type(pt)); - } - GB_ASSERT(ft_found != nullptr); - - lbFunctionType *ft = *ft_found; + lbFunctionType *ft = lb_get_function_type(m, p, pt); bool return_by_pointer = ft->ret.kind == lbArg_Indirect; unsigned param_index = 0; @@ -8531,11 +8546,7 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args, lbValue return_ptr = {}; if (use_copy_elision_hint && p->copy_elision_hint.ptr.value != nullptr) { if (are_types_identical(type_deref(p->copy_elision_hint.ptr.type), rt)) { - return_ptr = p->copy_elision_hint.ptr; - p->copy_elision_hint.used = true; - // consume it - p->copy_elision_hint.ptr = {}; - p->copy_elision_hint.ast = nullptr; + return_ptr = lb_consume_copy_elision_hint(p); } } if (return_ptr.value == nullptr) { From 44b6e7c45db3bb3cc6ff30bca08d0b761b975c30 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 22:32:38 +0100 Subject: [PATCH 132/137] Move the mem zero into a separate procedure for reuse --- src/llvm_backend.cpp | 46 ++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 38500fe1d..b622d1729 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -57,6 +57,9 @@ void lb_loop_end(lbProcedure *p, lbLoopData const &data); LLVMValueRef llvm_zero(lbModule *m) { return LLVMConstInt(lb_type(m, t_int), 0, false); } +LLVMValueRef llvm_zero32(lbModule *m) { + return LLVMConstInt(lb_type(m, t_i32), 0, false); +} LLVMValueRef llvm_one(lbModule *m) { return LLVMConstInt(lb_type(m, t_i32), 1, false); } @@ -3480,6 +3483,28 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f return v; } +void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) { + LLVMTypeRef llvm_type = lb_type(p->module, type); + + LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); + + switch (kind) { + case LLVMStructTypeKind: + case LLVMArrayTypeKind: + { + // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too + LLVMTypeRef type_i8 = LLVMInt8TypeInContext(p->module->ctx); + LLVMTypeRef type_i32 = LLVMInt32TypeInContext(p->module->ctx); + i32 sz = cast(i32)type_size_of(type); + LLVMBuildMemSet(p->builder, ptr, LLVMConstNull(type_i8), LLVMConstInt(type_i32, sz, false), alignment); + } + break; + default: + LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr); + break; + } +} + lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 param_index) { GB_ASSERT(p->decl_block != p->curr_block); LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block); @@ -3513,23 +3538,7 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p } if (zero_init) { - LLVMTypeKind kind = LLVMGetTypeKind(llvm_type); - - switch (kind) { - case LLVMStructTypeKind: - case LLVMArrayTypeKind: - { - // NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too - LLVMTypeRef type_i8 = LLVMInt8TypeInContext(p->module->ctx); - LLVMTypeRef type_i32 = LLVMInt32TypeInContext(p->module->ctx); - i32 sz = cast(i32)type_size_of(type); - LLVMBuildMemSet(p->builder, ptr, LLVMConstNull(type_i8), LLVMConstInt(type_i32, sz, false), alignment); - } - break; - default: - LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr); - break; - } + lb_mem_zero_ptr(p, ptr, type, alignment); } lbValue val = {}; @@ -5370,7 +5379,6 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { return; } - auto lvals = array_make(permanent_allocator(), 0, vd->names.count); for_array(i, vd->names) { @@ -5378,7 +5386,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { lbAddr lval = {}; if (!is_blank_ident(name)) { Entity *e = entity_of_node(name); - bool zero_init = true; // Zero always and optimize out later + bool zero_init = true; // Always do it lval = lb_add_local(p, e->type, e, zero_init); } array_add(&lvals, lval); From 0c46d06e6308281be257b0529874b7c8cc110ea3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 22:39:27 +0100 Subject: [PATCH 133/137] Add `intrinsics.mem_zero` --- core/intrinsics/intrinsics.odin | 7 ++++++ src/check_builtin.cpp | 41 +++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 2 ++ src/llvm_backend.cpp | 16 +++++++++++-- 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index 60b595aab..c1d8b0d28 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -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) --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1acb9732f..1630b5b7d 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2106,6 +2106,47 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } 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: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index b69bacd30..4fd97f804 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -60,6 +60,7 @@ enum BuiltinProcId { BuiltinProc_mem_copy, BuiltinProc_mem_copy_non_overlapping, + BuiltinProc_mem_zero, BuiltinProc_volatile_store, BuiltinProc_volatile_load, @@ -287,6 +288,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {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}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b622d1729..74c6cfc11 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -9478,8 +9478,6 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, case BuiltinProc_mem_copy: case BuiltinProc_mem_copy_non_overlapping: { - - lbValue dst = lb_build_expr(p, ce->args[0]); lbValue src = lb_build_expr(p, ce->args[1]); lbValue len = lb_build_expr(p, ce->args[2]); @@ -9513,6 +9511,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, return {}; } + case BuiltinProc_mem_zero: + { + lbValue ptr = lb_build_expr(p, ce->args[0]); + lbValue len = lb_build_expr(p, ce->args[1]); + ptr = lb_emit_conv(p, ptr, t_rawptr); + len = lb_emit_conv(p, len, t_int); + + LLVMTypeRef type_i8 = LLVMInt8TypeInContext(p->module->ctx); + unsigned alignment = 1; + LLVMBuildMemSet(p->builder, ptr.value, LLVMConstNull(type_i8), len.value, alignment); + + return {}; + } + case BuiltinProc_atomic_fence: LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, ""); From e948fcd2f1526de905220b2ff8a7891b1f52005e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 22:41:18 +0100 Subject: [PATCH 134/137] Use `intrinsics.mem_zero` in `runtime.mem_zero` --- core/runtime/internal.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index be1a69316..4e35da47f 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -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; } From c21c754b6f649b8523edd9e03079df9c053f6c65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 May 2021 23:51:01 +0100 Subject: [PATCH 135/137] Minimize copying on getting the address of a call if required --- src/llvm_backend.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 74c6cfc11..8439dd244 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -12599,7 +12599,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { if (is_type_relative_pointer(type_of_expr(de->expr))) { lbAddr addr = lb_build_addr(p, de->expr); addr.relative.deref = true; - return addr; + return addr;\ } lbValue addr = lb_build_expr(p, de->expr); return lb_addr(addr); @@ -12608,9 +12608,13 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { case_ast_node(ce, CallExpr, expr); // NOTE(bill): This is make sure you never need to have an 'array_ev' lbValue e = lb_build_expr(p, expr); + #if 1 + return lb_addr(lb_address_from_load_or_generate_local(p, e)); + #else lbAddr v = lb_add_local_generated(p, e.type, false); lb_addr_store(p, v, e); return v; + #endif case_end; case_ast_node(cl, CompoundLit, expr); From 4a886a1bc5c21b0f06a84c7d063fb7ed8eb2683d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 25 May 2021 15:43:34 +0100 Subject: [PATCH 136/137] Disable copy elision on assignments for the time being --- src/llvm_backend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8439dd244..6ff57aca7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -5056,12 +5056,12 @@ void lb_build_assignment(lbProcedure *p, Array &lvals, Slice cons array_add(&inits, v); } } else { - auto prev_hint = lb_set_copy_elision_hint(p, lvals[inits.count], rhs); + // auto prev_hint = lb_set_copy_elision_hint(p, lvals[inits.count], rhs); lbValue init = lb_build_expr(p, rhs); if (p->copy_elision_hint.used) { lvals[inits.count] = {}; // zero lval } - lb_reset_copy_elision_hint(p, prev_hint); + // lb_reset_copy_elision_hint(p, prev_hint); array_add(&inits, init); } } From bb7bd94b0ab671513ca2a4e3f9b9973bed87daa6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 27 May 2021 09:52:50 +0100 Subject: [PATCH 137/137] Fix comparison bug of enumerated arrays --- src/llvm_backend.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 6ff57aca7..fb7e6441c 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -10918,7 +10918,7 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } } - if (is_type_array(a)) { + if (is_type_array(a) || is_type_enumerated_array(a)) { Type *tl = base_type(a); lbValue lhs = lb_address_from_load_or_generate_local(p, left); lbValue rhs = lb_address_from_load_or_generate_local(p, right); @@ -10935,7 +10935,11 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri } bool inline_array_arith = type_size_of(tl) <= build_context.max_align; - i32 count = cast(i32)tl->Array.count; + i32 count = 0; + switch (tl->kind) { + case Type_Array: count = cast(i32)tl->Array.count; break; + case Type_EnumeratedArray: count = cast(i32)tl->EnumeratedArray.count; break; + } if (inline_array_arith) { // inline