mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-31 18:32:12 +00:00
VarDecl and ConstDecl split; error, warning, et al. now global
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
main :: proc() {
|
||||
init :: proc(c: ^pn.Core) {
|
||||
|
||||
}
|
||||
|
||||
step :: proc(c: ^pn.Core) {
|
||||
@@ -11,5 +10,5 @@ main :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
// pn.run(init, step)
|
||||
pn.run(init, step)
|
||||
}
|
||||
|
||||
583
code/fmt.odin
Normal file
583
code/fmt.odin
Normal file
@@ -0,0 +1,583 @@
|
||||
#import "os.odin" as os
|
||||
|
||||
print_byte_buffer :: proc(buf: ^[]byte, b: []byte) {
|
||||
if buf.count < buf.capacity {
|
||||
n := min(buf.capacity-buf.count, b.count)
|
||||
if n > 0 {
|
||||
offset := ptr_offset(buf.data, buf.count)
|
||||
memory_copy(offset, ^b[0], n)
|
||||
buf.count += n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_string_to_buffer :: proc(buf: ^[]byte, s: string) {
|
||||
print_byte_buffer(buf, s as []byte)
|
||||
}
|
||||
|
||||
|
||||
byte_reverse :: proc(b: []byte) {
|
||||
n := b.count
|
||||
for i := 0; i < n/2; i++ {
|
||||
b[i], b[n-1-i] = b[n-1-i], b[i]
|
||||
}
|
||||
}
|
||||
|
||||
encode_rune :: proc(r: rune) -> ([4]byte, int) {
|
||||
buf: [4]byte
|
||||
i := r as u32
|
||||
mask: byte : 0x3f
|
||||
if i <= 1<<7-1 {
|
||||
buf[0] = r as byte
|
||||
return buf, 1
|
||||
}
|
||||
if i <= 1<<11-1 {
|
||||
buf[0] = 0xc0 | (r>>6) as byte
|
||||
buf[1] = 0x80 | (r) as byte & mask
|
||||
return buf, 2
|
||||
}
|
||||
|
||||
// Invalid or Surrogate range
|
||||
if i > 0x0010ffff ||
|
||||
(i >= 0xd800 && i <= 0xdfff) {
|
||||
r = 0xfffd
|
||||
}
|
||||
|
||||
if i <= 1<<16-1 {
|
||||
buf[0] = 0xe0 | (r>>12) as byte
|
||||
buf[1] = 0x80 | (r>>6) as byte & mask
|
||||
buf[2] = 0x80 | (r) as byte & mask
|
||||
return buf, 3
|
||||
}
|
||||
|
||||
buf[0] = 0xf0 | (r>>18) as byte
|
||||
buf[1] = 0x80 | (r>>12) as byte & mask
|
||||
buf[2] = 0x80 | (r>>6) as byte & mask
|
||||
buf[3] = 0x80 | (r) as byte & mask
|
||||
return buf, 4
|
||||
}
|
||||
|
||||
print_rune_to_buffer :: proc(buf: ^[]byte, r: rune) {
|
||||
b, n := encode_rune(r)
|
||||
print_string_to_buffer(buf, b[:n] as string)
|
||||
}
|
||||
|
||||
print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune " ") }
|
||||
print_nl_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\n") }
|
||||
|
||||
print_int_to_buffer :: proc(buf: ^[]byte, i: int) {
|
||||
print_int_base_to_buffer(buf, i, 10);
|
||||
}
|
||||
PRINT__NUM_TO_CHAR_TABLE :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
|
||||
print_int_base_to_buffer :: proc(buffer: ^[]byte, i, base: int) {
|
||||
|
||||
buf: [65]byte
|
||||
len := 0
|
||||
negative := false
|
||||
if i < 0 {
|
||||
negative = true
|
||||
i = -i
|
||||
}
|
||||
if i == 0 {
|
||||
buf[len] = #rune "0"
|
||||
len++
|
||||
}
|
||||
for i > 0 {
|
||||
buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
|
||||
len++
|
||||
i /= base
|
||||
}
|
||||
|
||||
if negative {
|
||||
buf[len] = #rune "-"
|
||||
len++
|
||||
}
|
||||
|
||||
byte_reverse(buf[:len])
|
||||
print_string_to_buffer(buffer, buf[:len] as string)
|
||||
}
|
||||
|
||||
print_uint_to_buffer :: proc(buffer: ^[]byte, i: uint) {
|
||||
print_uint_base_to_buffer(buffer, i, 10, 0, #rune " ")
|
||||
}
|
||||
print_uint_base_to_buffer :: proc(buffer: ^[]byte, i, base: uint, min_width: int, pad_char: byte) {
|
||||
buf: [65]byte
|
||||
len := 0
|
||||
if i == 0 {
|
||||
buf[len] = #rune "0"
|
||||
len++
|
||||
}
|
||||
for i > 0 {
|
||||
buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % base]
|
||||
len++
|
||||
i /= base
|
||||
}
|
||||
for len < min_width {
|
||||
buf[len] = pad_char
|
||||
len++
|
||||
}
|
||||
|
||||
byte_reverse(buf[:len])
|
||||
print_string_to_buffer(buffer, buf[:len] as string)
|
||||
}
|
||||
|
||||
print_bool_to_buffer :: proc(buffer: ^[]byte, b : bool) {
|
||||
if b { print_string_to_buffer(buffer, "true") }
|
||||
else { print_string_to_buffer(buffer, "false") }
|
||||
}
|
||||
|
||||
print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline { print_uint_base_to_buffer(buffer, p as uint, 16, 0, #rune " ") }
|
||||
|
||||
print_f32_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 7) }
|
||||
print_f64_to_buffer :: proc(buffer: ^[]byte, f: f64) #inline { print__f64(buffer, f, 10) }
|
||||
|
||||
print__f64 :: proc(buffer: ^[]byte, f: f64, decimal_places: int) {
|
||||
if f == 0 {
|
||||
print_rune_to_buffer(buffer, #rune "0")
|
||||
return
|
||||
}
|
||||
if f < 0 {
|
||||
print_rune_to_buffer(buffer, #rune "-")
|
||||
f = -f
|
||||
}
|
||||
|
||||
print_u64_to_buffer :: proc(buffer: ^[]byte, i: u64) {
|
||||
buf: [22]byte
|
||||
len := 0
|
||||
if i == 0 {
|
||||
buf[len] = #rune "0"
|
||||
len++
|
||||
}
|
||||
for i > 0 {
|
||||
buf[len] = PRINT__NUM_TO_CHAR_TABLE[i % 10]
|
||||
len++
|
||||
i /= 10
|
||||
}
|
||||
byte_reverse(buf[:len])
|
||||
print_string_to_buffer(buffer, buf[:len] as string)
|
||||
}
|
||||
|
||||
i := f as u64
|
||||
print_u64_to_buffer(buffer, i)
|
||||
f -= i as f64
|
||||
|
||||
print_rune_to_buffer(buffer, #rune ".")
|
||||
|
||||
mult := 10.0
|
||||
for decimal_places := 6; decimal_places >= 0; decimal_places-- {
|
||||
i = (f * mult) as u64
|
||||
print_u64_to_buffer(buffer, i as u64)
|
||||
f -= i as f64 / mult
|
||||
mult *= 10
|
||||
}
|
||||
}
|
||||
|
||||
print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) {
|
||||
if ti == null { return }
|
||||
|
||||
using Type_Info
|
||||
match type info : ti {
|
||||
case Named:
|
||||
print_string_to_buffer(buf, info.name)
|
||||
case Integer:
|
||||
match {
|
||||
case ti == type_info(int):
|
||||
print_string_to_buffer(buf, "int")
|
||||
case ti == type_info(uint):
|
||||
print_string_to_buffer(buf, "uint")
|
||||
default:
|
||||
if info.signed {
|
||||
print_string_to_buffer(buf, "i")
|
||||
} else {
|
||||
print_string_to_buffer(buf, "u")
|
||||
}
|
||||
print_int_to_buffer(buf, 8*info.size)
|
||||
}
|
||||
|
||||
case Float:
|
||||
match info.size {
|
||||
case 4: print_string_to_buffer(buf, "f32")
|
||||
case 8: print_string_to_buffer(buf, "f64")
|
||||
}
|
||||
case String: print_string_to_buffer(buf, "string")
|
||||
case Boolean: print_string_to_buffer(buf, "bool")
|
||||
case Pointer:
|
||||
print_string_to_buffer(buf, "^")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
case Procedure:
|
||||
print_string_to_buffer(buf, "proc")
|
||||
if info.params == null {
|
||||
print_string_to_buffer(buf, "()")
|
||||
} else {
|
||||
count := (info.params as ^Tuple).fields.count
|
||||
if count == 1 { print_string_to_buffer(buf, "(") }
|
||||
print_type_to_buffer(buf, info.params)
|
||||
if count == 1 { print_string_to_buffer(buf, ")") }
|
||||
}
|
||||
if info.results != null {
|
||||
print_string_to_buffer(buf, " -> ")
|
||||
print_type_to_buffer(buf, info.results)
|
||||
}
|
||||
case Tuple:
|
||||
count := info.fields.count
|
||||
if count != 1 { print_string_to_buffer(buf, "(") }
|
||||
for i := 0; i < count; i++ {
|
||||
if i > 0 { print_string_to_buffer(buf, ", ") }
|
||||
|
||||
f := info.fields[i]
|
||||
|
||||
if f.name.count > 0 {
|
||||
print_string_to_buffer(buf, f.name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
}
|
||||
print_type_to_buffer(buf, f.type_info)
|
||||
}
|
||||
if count != 1 { print_string_to_buffer(buf, ")") }
|
||||
|
||||
case Array:
|
||||
print_string_to_buffer(buf, "[")
|
||||
print_int_to_buffer(buf, info.count)
|
||||
print_string_to_buffer(buf, "]")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
case Slice:
|
||||
print_string_to_buffer(buf, "[")
|
||||
print_string_to_buffer(buf, "]")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
case Vector:
|
||||
print_string_to_buffer(buf, "{")
|
||||
print_int_to_buffer(buf, info.count)
|
||||
print_string_to_buffer(buf, "}")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
|
||||
case Struct:
|
||||
print_string_to_buffer(buf, "struct ")
|
||||
if info.packed { print_string_to_buffer(buf, "#packed ") }
|
||||
if info.ordered { print_string_to_buffer(buf, "#ordered ") }
|
||||
print_string_to_buffer(buf, "{")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
|
||||
case Union:
|
||||
print_string_to_buffer(buf, "union {")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
|
||||
case Raw_Union:
|
||||
print_string_to_buffer(buf, "raw_union {")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
|
||||
case Enum:
|
||||
print_string_to_buffer(buf, "enum ")
|
||||
print_type_to_buffer(buf, info.base)
|
||||
print_string_to_buffer(buf, "{}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
|
||||
using Type_Info
|
||||
match type info : arg.type_info {
|
||||
case Named:
|
||||
a: any
|
||||
a.type_info = info.base
|
||||
a.data = arg.data
|
||||
match type b : info.base {
|
||||
case Struct:
|
||||
print_string_to_buffer(buf, info.name)
|
||||
print_string_to_buffer(buf, "{")
|
||||
for i := 0; i < b.fields.count; i++ {
|
||||
f := b.fields[i];
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
print_any_to_buffer(buf, f.name)
|
||||
print_string_to_buffer(buf, " = ")
|
||||
v: any
|
||||
v.type_info = f.type_info
|
||||
v.data = ptr_offset(arg.data as ^byte, f.offset)
|
||||
print_any_to_buffer(buf, v)
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
|
||||
default:
|
||||
print_any_to_buffer(buf, a)
|
||||
}
|
||||
|
||||
case Integer:
|
||||
if info.signed {
|
||||
i: int = 0;
|
||||
if arg.data != null {
|
||||
match info.size {
|
||||
case 1: i = (arg.data as ^i8)^ as int
|
||||
case 2: i = (arg.data as ^i16)^ as int
|
||||
case 4: i = (arg.data as ^i32)^ as int
|
||||
case 8: i = (arg.data as ^i64)^ as int
|
||||
case 16: i = (arg.data as ^i128)^ as int
|
||||
}
|
||||
}
|
||||
print_int_to_buffer(buf, i)
|
||||
} else {
|
||||
i: uint = 0;
|
||||
if arg.data != null {
|
||||
match info.size {
|
||||
case 1: i = (arg.data as ^u8)^ as uint
|
||||
case 2: i = (arg.data as ^u16)^ as uint
|
||||
case 4: i = (arg.data as ^u32)^ as uint
|
||||
case 8: i = (arg.data as ^u64)^ as uint
|
||||
case 16: i = (arg.data as ^u128)^ as uint
|
||||
}
|
||||
}
|
||||
print_uint_to_buffer(buf, i)
|
||||
}
|
||||
|
||||
case Float:
|
||||
f: f64 = 0
|
||||
if arg.data != null {
|
||||
match info.size {
|
||||
case 4: f = (arg.data as ^f32)^ as f64
|
||||
case 8: f = (arg.data as ^f64)^ as f64
|
||||
}
|
||||
}
|
||||
print_f64_to_buffer(buf, f)
|
||||
|
||||
case String:
|
||||
s := ""
|
||||
if arg.data != null {
|
||||
s = (arg.data as ^string)^
|
||||
}
|
||||
print_string_to_buffer(buf, s)
|
||||
|
||||
case Boolean:
|
||||
v := false;
|
||||
if arg.data != null {
|
||||
v = (arg.data as ^bool)^
|
||||
}
|
||||
print_bool_to_buffer(buf, v)
|
||||
|
||||
case Pointer:
|
||||
v := null;
|
||||
if arg.data != null {
|
||||
v = (arg.data as ^rawptr)^
|
||||
}
|
||||
print_pointer_to_buffer(buf, v)
|
||||
|
||||
case Enum:
|
||||
v: any
|
||||
v.data = arg.data
|
||||
v.type_info = info.base
|
||||
print_any_to_buffer(buf, v)
|
||||
|
||||
|
||||
case Array:
|
||||
print_string_to_buffer(buf, "[")
|
||||
defer print_string_to_buffer(buf, "]")
|
||||
|
||||
for i := 0; i < info.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
|
||||
elem: any
|
||||
elem.data = (arg.data as int + i*info.elem_size) as rawptr
|
||||
elem.type_info = info.elem
|
||||
print_any_to_buffer(buf, elem)
|
||||
}
|
||||
|
||||
case Slice:
|
||||
slice := arg.data as ^[]byte
|
||||
print_string_to_buffer(buf, "[")
|
||||
defer print_string_to_buffer(buf, "]")
|
||||
|
||||
for i := 0; i < slice.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
|
||||
elem: any
|
||||
elem.data = ptr_offset(slice.data, i*info.elem_size)
|
||||
elem.type_info = info.elem
|
||||
print_any_to_buffer(buf, elem)
|
||||
}
|
||||
|
||||
case Vector:
|
||||
print_string_to_buffer(buf, "<")
|
||||
defer print_string_to_buffer(buf, ">")
|
||||
|
||||
for i := 0; i < info.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
|
||||
elem: any
|
||||
elem.data = ptr_offset(arg.data as ^byte, i*info.elem_size)
|
||||
elem.type_info = info.elem
|
||||
print_any_to_buffer(buf, elem)
|
||||
}
|
||||
|
||||
|
||||
case Struct:
|
||||
print_string_to_buffer(buf, "struct")
|
||||
print_string_to_buffer(buf, "{")
|
||||
defer print_string_to_buffer(buf, "}")
|
||||
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, " = ")
|
||||
a: any
|
||||
a.data = ptr_offset(arg.data as ^byte, info.fields[i].offset)
|
||||
a.type_info = info.fields[i].type_info
|
||||
print_any_to_buffer(buf, a)
|
||||
}
|
||||
|
||||
case Union:
|
||||
print_string_to_buffer(buf, "(union)")
|
||||
case Raw_Union:
|
||||
print_string_to_buffer(buf, "(raw_union)")
|
||||
case Procedure:
|
||||
print_type_to_buffer(buf, arg.type_info)
|
||||
print_string_to_buffer(buf, " @ 0x")
|
||||
print_pointer_to_buffer(buf, (arg.data as ^rawptr)^)
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
type_info_is_string :: proc(info: ^Type_Info) -> bool {
|
||||
using Type_Info
|
||||
if info == null {
|
||||
return false
|
||||
}
|
||||
|
||||
for {
|
||||
match type i : info {
|
||||
case Named:
|
||||
info = i.base
|
||||
continue
|
||||
case String:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
print_to_buffer :: proc(buf: ^[]byte, fmt: string, args: ..any) {
|
||||
is_digit :: proc(r: rune) -> bool #inline {
|
||||
return r >= #rune "0" && r <= #rune "9"
|
||||
}
|
||||
|
||||
parse_int :: proc(s: string, offset: int) -> (int, int) {
|
||||
result := 0
|
||||
|
||||
for ; offset < s.count; offset++ {
|
||||
c := s[offset] as rune
|
||||
if !is_digit(c) {
|
||||
break
|
||||
}
|
||||
|
||||
result *= 10
|
||||
result += (c - #rune "0") as int
|
||||
}
|
||||
|
||||
return result, offset
|
||||
}
|
||||
|
||||
prev := 0
|
||||
implicit_index := 0
|
||||
|
||||
for i := 0; i < fmt.count; i++ {
|
||||
r := fmt[i] as rune
|
||||
index := implicit_index
|
||||
|
||||
if r != #rune "%" {
|
||||
continue
|
||||
}
|
||||
|
||||
print_string_to_buffer(buf, fmt[prev:i])
|
||||
i++ // Skip %
|
||||
if i < fmt.count {
|
||||
next := fmt[i] as rune
|
||||
|
||||
if next == #rune "%" {
|
||||
print_string_to_buffer(buf, "%")
|
||||
i++
|
||||
prev = i
|
||||
continue
|
||||
}
|
||||
|
||||
if is_digit(next) {
|
||||
index, i = parse_int(fmt, i)
|
||||
}
|
||||
}
|
||||
|
||||
if 0 <= index && index < args.count {
|
||||
print_any_to_buffer(buf, args[index])
|
||||
implicit_index = index+1
|
||||
} else {
|
||||
// TODO(bill): Error check index out bounds
|
||||
print_string_to_buffer(buf, "<invalid>")
|
||||
}
|
||||
|
||||
prev = i
|
||||
}
|
||||
|
||||
print_string_to_buffer(buf, fmt[prev:])
|
||||
}
|
||||
|
||||
PRINT_BUF_SIZE :: 1<<12
|
||||
|
||||
print_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
print_to_buffer(^buf, fmt, ..args)
|
||||
os.write(f, buf)
|
||||
}
|
||||
|
||||
println_to_file :: proc(f: ^os.File, fmt: string, args: ..any) {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
print_to_buffer(^buf, fmt, ..args)
|
||||
print_nl_to_buffer(^buf)
|
||||
os.write(f, buf)
|
||||
}
|
||||
|
||||
|
||||
print :: proc(fmt: string, args: ..any) {
|
||||
print_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
|
||||
}
|
||||
print_err :: proc(fmt: string, args: ..any) {
|
||||
print_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
|
||||
}
|
||||
println :: proc(fmt: string, args: ..any) {
|
||||
println_to_file(os.get_standard_file(os.File_Standard.OUTPUT), fmt, ..args)
|
||||
}
|
||||
println_err :: proc(fmt: string, args: ..any) {
|
||||
println_to_file(os.get_standard_file(os.File_Standard.ERROR), fmt, ..args)
|
||||
}
|
||||
107
code/os.odin
Normal file
107
code/os.odin
Normal file
@@ -0,0 +1,107 @@
|
||||
#import "runtime.odin" as _ // TODO(bill): make the compile import this automatically
|
||||
#import "win32.odin" as win32
|
||||
|
||||
File :: type struct {
|
||||
Handle :: type win32.HANDLE
|
||||
handle: Handle
|
||||
}
|
||||
|
||||
open :: proc(name: string) -> (File, bool) {
|
||||
using win32
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
f := File{CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, null, OPEN_EXISTING, 0, null)}
|
||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
||||
|
||||
return f, success
|
||||
}
|
||||
|
||||
create :: proc(name: string) -> (File, bool) {
|
||||
using win32
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
f := File{
|
||||
handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, 0, null),
|
||||
}
|
||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
||||
return f, success
|
||||
}
|
||||
|
||||
|
||||
close :: proc(using f: ^File) {
|
||||
win32.CloseHandle(handle)
|
||||
}
|
||||
|
||||
write :: proc(using f: ^File, buf: []byte) -> bool {
|
||||
bytes_written: i32
|
||||
return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, null) != 0
|
||||
}
|
||||
|
||||
|
||||
File_Standard :: type enum {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
ERROR,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
__std_files := __set_file_standards();
|
||||
|
||||
__set_file_standards :: proc() -> [File_Standard.COUNT as int]File {
|
||||
return [File_Standard.COUNT as int]File{
|
||||
File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
|
||||
File{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},
|
||||
File{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)},
|
||||
}
|
||||
}
|
||||
|
||||
get_standard_file :: proc(std: File_Standard) -> ^File {
|
||||
return ^__std_files[std]
|
||||
}
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> (string, bool) {
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
|
||||
f, file_ok := open(name)
|
||||
if !file_ok {
|
||||
return "", false
|
||||
}
|
||||
defer close(^f)
|
||||
|
||||
length: i64
|
||||
file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0
|
||||
if !file_size_ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
data := new_slice(u8, length)
|
||||
if data.data == null {
|
||||
return "", false
|
||||
}
|
||||
|
||||
single_read_length: i32
|
||||
total_read: i64
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read
|
||||
to_read: u32
|
||||
MAX :: 1<<32-1
|
||||
if remaining <= MAX {
|
||||
to_read = remaining as u32
|
||||
} else {
|
||||
to_read = MAX
|
||||
}
|
||||
|
||||
win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, null)
|
||||
if single_read_length <= 0 {
|
||||
free(data.data)
|
||||
return "", false
|
||||
}
|
||||
|
||||
total_read += single_read_length as i64
|
||||
}
|
||||
|
||||
return data as string, true
|
||||
}
|
||||
484
code/punity.odin
Normal file
484
code/punity.odin
Normal file
@@ -0,0 +1,484 @@
|
||||
#import "win32.odin" as win32
|
||||
#import "fmt.odin" as _
|
||||
|
||||
CANVAS_WIDTH :: 128
|
||||
CANVAS_HEIGHT :: 128
|
||||
CANVAS_SCALE :: 3
|
||||
FRAME_TIME :: 1.0/30.0
|
||||
WINDOW_TITLE : string : "Punity\x00"
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0)
|
||||
|
||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE
|
||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE
|
||||
|
||||
|
||||
STACK_CAPACITY :: 1<<20
|
||||
STORAGE_CAPACITY :: 1<<20
|
||||
|
||||
DRAW_LIST_RESERVE :: 128
|
||||
|
||||
MAX_KEYS :: 256
|
||||
|
||||
Core :: struct {
|
||||
stack: ^Bank
|
||||
storage: ^Bank
|
||||
|
||||
running: bool
|
||||
key_modifiers: u32
|
||||
key_states: [MAX_KEYS]byte
|
||||
key_deltas: [MAX_KEYS]byte
|
||||
|
||||
perf_frame,
|
||||
perf_frame_inner,
|
||||
perf_step,
|
||||
perf_audio,
|
||||
perf_blit,
|
||||
perf_blit_cvt,
|
||||
perf_blit_gdi: Perf_Span
|
||||
|
||||
frame: i64
|
||||
|
||||
canvas: Canvas
|
||||
draw_list: ^Draw_List
|
||||
}
|
||||
|
||||
Perf_Span :: struct {
|
||||
stamp: f64
|
||||
delta: f32
|
||||
}
|
||||
|
||||
Bank :: struct {
|
||||
memory: []byte
|
||||
cursor: int
|
||||
}
|
||||
|
||||
Bank_State :: struct {
|
||||
state: Bank
|
||||
bank: ^Bank
|
||||
}
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{ a, b, g, r: byte }
|
||||
rgba: u32
|
||||
}
|
||||
|
||||
Palette :: struct {
|
||||
colors: [256]Color
|
||||
colors_count: byte
|
||||
}
|
||||
|
||||
|
||||
Rect :: raw_union {
|
||||
using minmax: struct {
|
||||
min_x, min_y, max_x, max_y: int
|
||||
}
|
||||
using pos: struct {
|
||||
left, top, right, bottom: int
|
||||
}
|
||||
e: [4]int
|
||||
}
|
||||
|
||||
Bitmap :: struct {
|
||||
pixels: []byte
|
||||
width: int
|
||||
height: int
|
||||
}
|
||||
|
||||
Font :: struct {
|
||||
using bitmap: Bitmap
|
||||
char_width: int
|
||||
char_height: int
|
||||
}
|
||||
|
||||
Canvas :: struct {
|
||||
using bitmap: ^Bitmap
|
||||
palette: Palette
|
||||
translate_x: int
|
||||
translate_y: int
|
||||
clip: Rect
|
||||
font: ^Font
|
||||
}
|
||||
|
||||
DrawFlag :: enum {
|
||||
NONE = 0,
|
||||
FLIP_H = 1<<0,
|
||||
FLIP_V = 1<<1,
|
||||
MASK = 1<<2,
|
||||
}
|
||||
|
||||
|
||||
Draw_List :: struct {
|
||||
Item :: struct {
|
||||
|
||||
}
|
||||
items: []Item
|
||||
}
|
||||
|
||||
Key :: enum {
|
||||
MOD_SHIFT = 0x0001,
|
||||
MOD_CONTROL = 0x0002,
|
||||
MOD_ALT = 0x0004,
|
||||
MOD_SUPER = 0x0008,
|
||||
|
||||
UNKNOWN =-1,
|
||||
INVALID =-2,
|
||||
|
||||
LBUTTON = 1,
|
||||
RBUTTON = 2,
|
||||
CANCEL = 3,
|
||||
MBUTTON = 4,
|
||||
|
||||
BACK = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
SHIFT = 16,
|
||||
CONTROL = 17,
|
||||
MENU = 18,
|
||||
PAUSE = 19,
|
||||
CAPITAL = 20,
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 32,
|
||||
PRIOR = 33,
|
||||
NEXT = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT = 37,
|
||||
UP = 38,
|
||||
RIGHT = 39,
|
||||
DOWN = 40,
|
||||
SELECT = 41,
|
||||
PRINT = 42,
|
||||
EXEC = 43,
|
||||
SNAPSHOT = 44,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
HELP = 47,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
|
||||
APOSTROPHE = 39, /* ' */
|
||||
COMMA = 44, /* , */
|
||||
MINUS = 45, /* - */
|
||||
PERIOD = 46, /* . */
|
||||
SLASH = 47, /* / */
|
||||
NUM0 = 48,
|
||||
NUM1 = 49,
|
||||
NUM2 = 50,
|
||||
NUM3 = 51,
|
||||
NUM4 = 52,
|
||||
NUM5 = 53,
|
||||
NUM6 = 54,
|
||||
NUM7 = 55,
|
||||
NUM8 = 56,
|
||||
NUM9 = 57,
|
||||
SEMICOLON = 59, /* ; */
|
||||
EQUAL = 61, /* = */
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LEFT_BRACKET = 91, /* [ */
|
||||
BACKSLASH = 92, /* \ */
|
||||
RIGHT_BRACKET = 93, /* ] */
|
||||
GRAVE_ACCENT = 96, /* ` */
|
||||
}
|
||||
|
||||
|
||||
key_down :: proc(k: Key) -> bool {
|
||||
return _core.key_states[k] != 0
|
||||
}
|
||||
|
||||
key_pressed :: proc(k: Key) -> bool {
|
||||
return (_core.key_deltas[k] != 0) && key_down(k)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency()
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0)
|
||||
|
||||
counter: i64
|
||||
win32.QueryPerformanceCounter(^counter)
|
||||
result := counter as f64 / win32_perf_count_freq as f64
|
||||
return result
|
||||
}
|
||||
|
||||
_core: Core
|
||||
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32
|
||||
|
||||
|
||||
_core.running = true
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall {
|
||||
win32_app_key_mods :: proc() -> u32 {
|
||||
mods: u32 = 0
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= Key.MOD_SHIFT as u32;
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= Key.MOD_CONTROL as u32;
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= Key.MOD_ALT as u32;
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= Key.MOD_SUPER as u32;
|
||||
}
|
||||
|
||||
return mods
|
||||
}
|
||||
|
||||
match msg {
|
||||
case WM_KEYDOWN:
|
||||
_core.key_modifiers = win32_app_key_mods()
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 1
|
||||
_core.key_deltas[wparam] = 1
|
||||
}
|
||||
return 0
|
||||
|
||||
case WM_KEYUP:
|
||||
_core.key_modifiers = win32_app_key_mods()
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 0
|
||||
_core.key_deltas[wparam] = 1
|
||||
}
|
||||
return 0
|
||||
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0)
|
||||
_core.running = false
|
||||
return 0
|
||||
}
|
||||
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
|
||||
window_class := WNDCLASSEXA{
|
||||
class_name = ("Punity\x00" as string).data, // C-style string
|
||||
size = size_of(WNDCLASSEXA) as u32,
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = GetModuleHandleA(null) as HINSTANCE,
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = GetStockObject(BLACK_BRUSH) as HBRUSH,
|
||||
}
|
||||
|
||||
if RegisterClassExA(^window_class) == 0 {
|
||||
/*fmt.*/println_err("RegisterClassExA failed")
|
||||
return
|
||||
}
|
||||
|
||||
screen_width := GetSystemMetrics(SM_CXSCREEN)
|
||||
screen_height := GetSystemMetrics(SM_CYSCREEN)
|
||||
|
||||
rc: RECT
|
||||
rc.left = (screen_width - WINDOW_WIDTH) / 2
|
||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2
|
||||
rc.right = rc.left + WINDOW_WIDTH
|
||||
rc.bottom = rc.top + WINDOW_HEIGHT
|
||||
|
||||
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
|
||||
assert(AdjustWindowRect(^rc, style, 0) != 0)
|
||||
|
||||
wt := WINDOW_TITLE
|
||||
|
||||
win32_window := CreateWindowExA(0,
|
||||
window_class.class_name,
|
||||
WINDOW_TITLE.data,
|
||||
// wt.data,
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
null, null, window_class.instance,
|
||||
null);
|
||||
|
||||
if win32_window == null {
|
||||
/*fmt.*/println_err("CreateWindowExA failed")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi.size = size_of(BITMAPINFO.HEADER) as u32
|
||||
window_bmi.width = CANVAS_WIDTH
|
||||
window_bmi.height = CANVAS_HEIGHT
|
||||
window_bmi.planes = 1
|
||||
window_bmi.bit_count = 32
|
||||
window_bmi.compression = BI_RGB
|
||||
|
||||
|
||||
user_init(^_core)
|
||||
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW)
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
assert(window_buffer.data != null)
|
||||
defer free(window_buffer.data)
|
||||
|
||||
for i := 0; i < window_buffer.count; i++ {
|
||||
window_buffer[i] = 0xff00ff
|
||||
}
|
||||
|
||||
|
||||
prev_time, curr_time,dt: f64
|
||||
prev_time = time_now()
|
||||
curr_time = time_now()
|
||||
total_time : f64 = 0
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
|
||||
message: MSG
|
||||
for _core.running {
|
||||
curr_time = time_now()
|
||||
dt = curr_time - prev_time
|
||||
prev_time = curr_time
|
||||
total_time += dt
|
||||
|
||||
offset_x += 1
|
||||
offset_y += 2
|
||||
|
||||
{
|
||||
data: [128]byte
|
||||
buf := data[:0]
|
||||
/*fmt.*/print_to_buffer(^buf, "Punity: % ms\x00", dt*1000)
|
||||
win32.SetWindowTextA(win32_window, buf.data)
|
||||
}
|
||||
|
||||
|
||||
for y := 0; y < CANVAS_HEIGHT; y++ {
|
||||
for x := 0; x < CANVAS_WIDTH; x++ {
|
||||
g := (x % 32) * 8
|
||||
b := (y % 32) * 8
|
||||
window_buffer[x + y*CANVAS_WIDTH] = (g << 8 | b) as u32
|
||||
}
|
||||
}
|
||||
|
||||
memory_zero(^_core.key_deltas[0], size_of(_core.key_deltas[0]))
|
||||
|
||||
|
||||
for PeekMessageA(^message, null, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false
|
||||
}
|
||||
TranslateMessage(^message)
|
||||
DispatchMessageA(^message)
|
||||
}
|
||||
|
||||
user_step(^_core)
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
window_buffer.data,
|
||||
^window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY)
|
||||
ReleaseDC(win32_window, dc)
|
||||
|
||||
|
||||
{
|
||||
delta := time_now() - prev_time
|
||||
ms := ((FRAME_TIME - delta) * 1000) as i32
|
||||
if ms > 0 {
|
||||
win32.Sleep(ms)
|
||||
}
|
||||
}
|
||||
|
||||
_core.frame++
|
||||
}
|
||||
}
|
||||
@@ -237,8 +237,6 @@ struct Checker {
|
||||
|
||||
gbArray(Type *) proc_stack;
|
||||
b32 in_defer; // TODO(bill): Actually handle correctly
|
||||
|
||||
ErrorCollector error_collector;
|
||||
};
|
||||
|
||||
gb_global Scope *universal_scope = NULL;
|
||||
@@ -635,18 +633,23 @@ b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
|
||||
if (insert_entity) {
|
||||
Entity *up = insert_entity->using_parent;
|
||||
if (up != NULL) {
|
||||
error(&c->error_collector, entity->token,
|
||||
error(entity->token,
|
||||
"Redeclararation of `%.*s` in this scope through `using`\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(entity->token.string),
|
||||
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
|
||||
return false;
|
||||
} else {
|
||||
error(&c->error_collector, entity->token,
|
||||
TokenPos pos = insert_entity->token.pos;
|
||||
if (token_pos_are_equal(pos, entity->token.pos)) {
|
||||
// NOTE(bill): Error should have been handled already
|
||||
return false;
|
||||
}
|
||||
error(entity->token,
|
||||
"Redeclararation of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(entity->token.string),
|
||||
LIT(entity->token.pos.file), entity->token.pos.line, entity->token.pos.column);
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -797,7 +800,7 @@ Type *const curr_procedure(Checker *c) {
|
||||
|
||||
void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
TokenPos zero_pos = {};
|
||||
c->error_collector.prev = zero_pos;
|
||||
global_error_collector.prev = zero_pos;
|
||||
c->curr_ast_file = file;
|
||||
}
|
||||
|
||||
@@ -924,64 +927,60 @@ void check_parsed_files(Checker *c) {
|
||||
// NOTE(bill): ignore
|
||||
case_end;
|
||||
|
||||
case_ast_node(cd, ConstDecl, decl);
|
||||
gb_for_array(i, cd->values) {
|
||||
AstNode *name = cd->names[i];
|
||||
AstNode *value = cd->values[i];
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
|
||||
DeclInfo *di = make_declaration_info(c->allocator, file_scope);
|
||||
di->type_expr = cd->type;
|
||||
di->init_expr = value;
|
||||
add_file_entity(c, file_scope, name, e, di);
|
||||
}
|
||||
|
||||
isize lhs_count = gb_array_count(cd->names);
|
||||
isize rhs_count = gb_array_count(cd->values);
|
||||
|
||||
if (rhs_count == 0 && cd->type == NULL) {
|
||||
error(ast_node_token(decl), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
error(ast_node_token(decl), "Extra initial expression");
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, VarDecl, decl);
|
||||
switch (vd->kind) {
|
||||
case Declaration_Immutable: {
|
||||
gb_for_array(i, vd->values) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = vd->values[i];
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
|
||||
DeclInfo *di = make_declaration_info(c->allocator, file_scope);
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = value;
|
||||
add_file_entity(c, file_scope, name, e, di);
|
||||
isize entity_count = gb_array_count(vd->names);
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (gb_array_count(vd->values) > 0) {
|
||||
di = make_declaration_info(gb_heap_allocator(), file_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values[0]; // TODO(bill): Is this correct?
|
||||
}
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < gb_array_count(vd->values)) {
|
||||
value = vd->values[i];
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(gb_heap_allocator(), file_scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
isize lhs_count = gb_array_count(vd->names);
|
||||
isize rhs_count = gb_array_count(vd->values);
|
||||
|
||||
if (rhs_count == 0 && vd->type == NULL) {
|
||||
error(&c->error_collector, ast_node_token(decl), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
error(&c->error_collector, ast_node_token(decl), "Extra initial expression");
|
||||
}
|
||||
} break;
|
||||
|
||||
case Declaration_Mutable: {
|
||||
isize entity_count = gb_array_count(vd->names);
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (gb_array_count(vd->values) > 0) {
|
||||
di = make_declaration_info(gb_heap_allocator(), file_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values[0]; // TODO(bill): Is this correct?
|
||||
}
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < gb_array_count(vd->values)) {
|
||||
value = vd->values[i];
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(gb_heap_allocator(), file_scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_file_entity(c, file_scope, name, e, d);
|
||||
}
|
||||
} break;
|
||||
add_file_entity(c, file_scope, name, e, d);
|
||||
}
|
||||
case_end;
|
||||
|
||||
@@ -1003,7 +1002,7 @@ void check_parsed_files(Checker *c) {
|
||||
case_end;
|
||||
|
||||
default:
|
||||
error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
error(ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1046,13 +1045,11 @@ void check_parsed_files(Checker *c) {
|
||||
continue;
|
||||
}
|
||||
// NOTE(bill): Do not add other imported entities
|
||||
if (e->kind != Entity_ImportName) {
|
||||
if (is_entity_exported(e)) {
|
||||
add_entity(c, file_scope, NULL, e);
|
||||
if (!id->is_load) {
|
||||
HashKey key = hash_string(e->token.string);
|
||||
map_set(&file_scope->implicit, key, e);
|
||||
}
|
||||
if (is_entity_exported(e)) {
|
||||
add_entity(c, file_scope, NULL, e);
|
||||
if (!id->is_load) { // `#import`ed entities don't get exported
|
||||
HashKey key = hash_string(e->token.string);
|
||||
map_set(&file_scope->implicit, key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ struct Entity {
|
||||
};
|
||||
|
||||
b32 is_entity_exported(Entity *e) {
|
||||
if (e->kind == Entity_ImportName) {
|
||||
return false;
|
||||
}
|
||||
// TODO(bill): Do I really want non-exported entities?
|
||||
// if (e->token.string.len >= 1 &&
|
||||
// e->token.string.text[0] == '_') {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -203,7 +203,7 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
|
||||
gbString str = expr_to_string(op_b.expr);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
|
||||
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
|
||||
} break;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
defer (gb_string_free(expr_str));
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
error(ast_node_token(operand->expr),
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
@@ -244,7 +244,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
Type *t = operand->type;
|
||||
if (is_type_untyped(t)) {
|
||||
if (t == t_invalid) {
|
||||
error(&c->error_collector, e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
error(e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return NULL;
|
||||
}
|
||||
@@ -285,6 +285,12 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
}
|
||||
|
||||
isize rhs_count = gb_array_count(operands);
|
||||
gb_for_array(i, operands) {
|
||||
if (operands[i].mode == Addressing_Invalid) {
|
||||
rhs_count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isize max = gb_min(lhs_count, rhs_count);
|
||||
for (isize i = 0; i < max; i++) {
|
||||
@@ -292,7 +298,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
}
|
||||
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(&c->error_collector, lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +313,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
error(ast_node_token(operand->expr),
|
||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
|
||||
if (e->type == NULL)
|
||||
e->type = t_invalid;
|
||||
@@ -343,7 +349,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(type_expr),
|
||||
error(ast_node_token(type_expr),
|
||||
"Invalid constant type `%s`", str);
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
@@ -411,13 +417,13 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(&c->error_collector, e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, e->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
error(e->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -429,7 +435,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
check_stmt_list(c, bs->stmts, 0);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure");
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
@@ -501,20 +507,20 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
|
||||
gbString str = type_to_string(proc_type);
|
||||
defer (gb_string_free(str));
|
||||
|
||||
error(&c->error_collector, e->token,
|
||||
error(e->token,
|
||||
"Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
error(&c->error_collector, ast_node_token(pd->type),
|
||||
error(ast_node_token(pd->type),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
error(&c->error_collector, ast_node_token(pd->body),
|
||||
error(ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
@@ -543,7 +549,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
|
||||
Type *this_type = get_base_type(e->type);
|
||||
Type *other_type = get_base_type(f->type);
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error(&c->error_collector, ast_node_token(d->proc_decl),
|
||||
error(ast_node_token(d->proc_decl),
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
@@ -662,118 +668,115 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
|
||||
|
||||
|
||||
|
||||
void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
void check_var_decl(Checker *c, AstNode *node) {
|
||||
ast_node(vd, VarDecl, node);
|
||||
isize entity_count = gb_array_count(vd->names);
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
switch (vd->kind) {
|
||||
case Declaration_Mutable: {
|
||||
Entity **new_entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
isize new_entity_count = 0;
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
Entity *entity = NULL;
|
||||
Token token = name->Ident;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
b32 can_be_ignored = are_strings_equal(str, make_string("_"));
|
||||
if (!can_be_ignored) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
if (!can_be_ignored) {
|
||||
new_entities[new_entity_count++] = entity;
|
||||
}
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(&c->error_collector, token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, token, "A variable declaration must be an identifier");
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
Entity *entity = NULL;
|
||||
Token token = name->Ident;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
b32 can_be_ignored = are_strings_equal(str, make_string("_"));
|
||||
if (!can_be_ignored) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (entity == NULL)
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vd->type) {
|
||||
init_type = check_type(c, vd->type, NULL);
|
||||
if (init_type == NULL)
|
||||
init_type = t_invalid;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->Variable.visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->Variable.visited = true;
|
||||
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
|
||||
check_init_variables(c, entities, entity_count, vd->values, make_string("variable declaration"));
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
add_entity(c, c->context.scope, vd->names[i], new_entities[i]);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case Declaration_Immutable: {
|
||||
gb_for_array(i, vd->values) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = vd->values[i];
|
||||
|
||||
GB_ASSERT(name->kind == AstNode_Ident);
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
String str = name->Ident.string;
|
||||
Entity *found = current_scope_lookup_entity(c->context.scope, str);
|
||||
if (found == NULL) {
|
||||
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
|
||||
entities[entity_index++] = e;
|
||||
check_const_decl(c, e, vd->type, value);
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
entities[entity_index++] = found;
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error(token, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL)
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
isize lhs_count = gb_array_count(vd->names);
|
||||
isize rhs_count = gb_array_count(vd->values);
|
||||
Type *init_type = NULL;
|
||||
if (vd->type) {
|
||||
init_type = check_type(c, vd->type, NULL);
|
||||
if (init_type == NULL)
|
||||
init_type = t_invalid;
|
||||
}
|
||||
|
||||
// TODO(bill): Better error messages or is this good enough?
|
||||
if (rhs_count == 0 && vd->type == NULL) {
|
||||
error(&c->error_collector, ast_node_token(node), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
error(&c->error_collector, ast_node_token(node), "Extra initial expression");
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->Variable.visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->Variable.visited = true;
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
|
||||
check_init_variables(c, entities, entity_count, vd->values, make_string("variable declaration"));
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
if (entities[i] != NULL) {
|
||||
add_entity(c, c->context.scope, vd->names[i], entities[i]);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
default:
|
||||
error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void check_const_decl(Checker *c, AstNode *node) {
|
||||
ast_node(vd, ConstDecl, node);
|
||||
isize entity_count = gb_array_count(vd->names);
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
|
||||
gb_for_array(i, vd->values) {
|
||||
AstNode *name = vd->names[i];
|
||||
AstNode *value = vd->values[i];
|
||||
|
||||
GB_ASSERT(name->kind == AstNode_Ident);
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
String str = name->Ident.string;
|
||||
Entity *found = current_scope_lookup_entity(c->context.scope, str);
|
||||
if (found == NULL) {
|
||||
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
|
||||
entities[entity_index++] = e;
|
||||
check_const_decl(c, e, vd->type, value);
|
||||
} else {
|
||||
entities[entity_index++] = found;
|
||||
}
|
||||
}
|
||||
|
||||
isize lhs_count = gb_array_count(vd->names);
|
||||
isize rhs_count = gb_array_count(vd->values);
|
||||
|
||||
// TODO(bill): Better error messages or is this good enough?
|
||||
if (rhs_count == 0 && vd->type == NULL) {
|
||||
error(ast_node_token(node), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
error(ast_node_token(node), "Extra initial expression");
|
||||
}
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
add_entity(c, c->context.scope, vd->names[i], entities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -786,7 +789,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
ExprKind kind = check_expr_base(c, &operand, es->expr);
|
||||
switch (operand.mode) {
|
||||
case Addressing_Type:
|
||||
error(&c->error_collector, ast_node_token(node), "Is not an expression");
|
||||
error(ast_node_token(node), "Is not an expression");
|
||||
break;
|
||||
case Addressing_NoValue:
|
||||
return;
|
||||
@@ -800,14 +803,14 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
error(&c->error_collector, ast_node_token(node), "Expression is not used: `%s`", expr_str);
|
||||
error(ast_node_token(node), "Expression is not used: `%s`", expr_str);
|
||||
} break;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ts, TagStmt, node);
|
||||
// TODO(bill): Tag Statements
|
||||
error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
|
||||
error(ast_node_token(node), "Tag statements are not supported yet");
|
||||
check_stmt(c, ts->stmt, flags);
|
||||
case_end;
|
||||
|
||||
@@ -823,7 +826,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
op.string.len = 1;
|
||||
break;
|
||||
default:
|
||||
error(&c->error_collector, ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
|
||||
error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -832,7 +835,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (operand.mode == Addressing_Invalid)
|
||||
return;
|
||||
if (!is_type_numeric(operand.type)) {
|
||||
error(&c->error_collector, ids->op, "Non numeric type");
|
||||
error(ids->op, "Non numeric type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -855,7 +858,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
case Token_Eq: {
|
||||
// a, b, c = 1, 2, 3; // Multisided
|
||||
if (gb_array_count(as->lhs) == 0) {
|
||||
error(&c->error_collector, as->op, "Missing lhs in assignment statement");
|
||||
error(as->op, "Missing lhs in assignment statement");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -888,7 +891,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_assignment_variable(c, &operands[i], lhs);
|
||||
}
|
||||
if (lhs_count != rhs_count) {
|
||||
error(&c->error_collector, ast_node_token(as->lhs[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
error(ast_node_token(as->lhs[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -896,11 +899,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
// a += 1; // Single-sided
|
||||
Token op = as->op;
|
||||
if (gb_array_count(as->lhs) != 1 || gb_array_count(as->rhs) != 1) {
|
||||
error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
|
||||
error(op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
|
||||
error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
// TODO(bill): Check if valid assignment operator
|
||||
@@ -938,7 +941,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_expr(c, &operand, is->cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
error(&c->error_collector, ast_node_token(is->cond),
|
||||
error(ast_node_token(is->cond),
|
||||
"Non-boolean condition in `if` statement");
|
||||
}
|
||||
|
||||
@@ -951,7 +954,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_stmt(c, is->else_stmt, mod_flags);
|
||||
break;
|
||||
default:
|
||||
error(&c->error_collector, ast_node_token(is->else_stmt),
|
||||
error(ast_node_token(is->else_stmt),
|
||||
"Invalid `else` statement in `if` statement");
|
||||
break;
|
||||
}
|
||||
@@ -962,23 +965,28 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
GB_ASSERT(gb_array_count(c->proc_stack) > 0);
|
||||
|
||||
if (c->in_defer) {
|
||||
error(&c->error_collector, rs->token, "You cannot `return` within a defer statement");
|
||||
error(rs->token, "You cannot `return` within a defer statement");
|
||||
// TODO(bill): Should I break here?
|
||||
break;
|
||||
}
|
||||
|
||||
Type *proc_type = c->proc_stack[gb_array_count(c->proc_stack)-1];
|
||||
isize result_count = 0;
|
||||
if (proc_type->Proc.results)
|
||||
if (proc_type->Proc.results) {
|
||||
result_count = proc_type->Proc.results->Tuple.variable_count;
|
||||
}
|
||||
if (result_count != gb_array_count(rs->results)) {
|
||||
error(&c->error_collector, rs->token, "Expected %td return %s, got %td",
|
||||
error(rs->token, "Expected %td return %s, got %td",
|
||||
result_count,
|
||||
(result_count != 1 ? "values" : "value"),
|
||||
gb_array_count(rs->results));
|
||||
} else if (result_count > 0) {
|
||||
auto *tuple = &proc_type->Proc.results->Tuple;
|
||||
check_init_variables(c, tuple->variables, tuple->variable_count,
|
||||
Entity **variables = NULL;
|
||||
if (proc_type->Proc.results != NULL) {
|
||||
auto *tuple = &proc_type->Proc.results->Tuple;
|
||||
variables = tuple->variables;
|
||||
}
|
||||
check_init_variables(c, variables, result_count,
|
||||
rs->results, make_string("return statement"));
|
||||
}
|
||||
case_end;
|
||||
@@ -995,7 +1003,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
check_expr(c, &operand, fs->cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
error(&c->error_collector, ast_node_token(fs->cond),
|
||||
error(ast_node_token(fs->cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
}
|
||||
}
|
||||
@@ -1040,13 +1048,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
error(ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != NULL) {
|
||||
if (first_default != NULL) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(&c->error_collector, ast_node_token(stmt),
|
||||
error(ast_node_token(stmt),
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
@@ -1114,8 +1122,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (are_types_identical(y.type, tap.type)) {
|
||||
TokenPos pos = tap.token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(&c->error_collector,
|
||||
ast_node_token(y.expr),
|
||||
error(ast_node_token(y.expr),
|
||||
"Duplicate case `%s`\n"
|
||||
"\tprevious case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
@@ -1158,7 +1165,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (!is_type_pointer(x.type) || !is_type_union(type_deref(x.type))) {
|
||||
gbString str = type_to_string(x.type);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(x.expr),
|
||||
error(ast_node_token(x.expr),
|
||||
"Expected a pointer to a union for this type match expression, got `%s`", str);
|
||||
break;
|
||||
}
|
||||
@@ -1177,13 +1184,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
error(ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != NULL) {
|
||||
if (first_default != NULL) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(&c->error_collector, ast_node_token(stmt),
|
||||
error(ast_node_token(stmt),
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
@@ -1226,7 +1233,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (!tag_type_found) {
|
||||
gbString type_str = type_to_string(y.type);
|
||||
defer (gb_string_free(type_str));
|
||||
error(&c->error_collector, ast_node_token(y.expr),
|
||||
error(ast_node_token(y.expr),
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
continue;
|
||||
}
|
||||
@@ -1237,8 +1244,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (found) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(&c->error_collector,
|
||||
ast_node_token(y.expr),
|
||||
error(ast_node_token(y.expr),
|
||||
"Duplicate type case `%s`\n"
|
||||
"\tprevious type case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
@@ -1266,7 +1272,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(ds, DeferStmt, node);
|
||||
if (is_ast_node_decl(ds->stmt)) {
|
||||
error(&c->error_collector, ds->token, "You cannot defer a declaration");
|
||||
error(ds->token, "You cannot defer a declaration");
|
||||
} else {
|
||||
b32 out_in_defer = c->in_defer;
|
||||
c->in_defer = true;
|
||||
@@ -1280,18 +1286,18 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
switch (token.kind) {
|
||||
case Token_break:
|
||||
if ((flags & Stmt_BreakAllowed) == 0)
|
||||
error(&c->error_collector, token, "`break` only allowed in `for` or `match` statements");
|
||||
error(token, "`break` only allowed in `for` or `match` statements");
|
||||
break;
|
||||
case Token_continue:
|
||||
if ((flags & Stmt_ContinueAllowed) == 0)
|
||||
error(&c->error_collector, token, "`continue` only allowed in `for` statements");
|
||||
error(token, "`continue` only allowed in `for` statements");
|
||||
break;
|
||||
case Token_fallthrough:
|
||||
if ((flags & Stmt_FallthroughAllowed) == 0)
|
||||
error(&c->error_collector, token, "`fallthrough` statement in illegal position");
|
||||
error(token, "`fallthrough` statement in illegal position");
|
||||
break;
|
||||
default:
|
||||
error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
|
||||
error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
|
||||
break;
|
||||
}
|
||||
case_end;
|
||||
@@ -1315,7 +1321,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
|
||||
if (e == NULL) {
|
||||
error(&c->error_collector, us->token, "`using` applied to an unknown entity");
|
||||
error(us->token, "`using` applied to an unknown entity");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1330,7 +1336,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *f = t->Record.other_fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
@@ -1340,7 +1346,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *f = t->Record.fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
@@ -1349,7 +1355,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *f = t->Record.other_fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
@@ -1363,7 +1369,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *decl = scope->elements.entries[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, decl);
|
||||
if (found != NULL) {
|
||||
error(&c->error_collector, us->token,
|
||||
error(us->token,
|
||||
"Namespace collision while `using` `%s` of: %.*s\n"
|
||||
"\tat %.*s(%td:%td)\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
@@ -1377,12 +1383,12 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
} break;
|
||||
|
||||
case Entity_Constant:
|
||||
error(&c->error_collector, us->token, "`using` cannot be applied to a constant");
|
||||
error(us->token, "`using` cannot be applied to a constant");
|
||||
break;
|
||||
|
||||
case Entity_Procedure:
|
||||
case Entity_Builtin:
|
||||
error(&c->error_collector, us->token, "`using` cannot be applied to a procedure");
|
||||
error(us->token, "`using` cannot be applied to a procedure");
|
||||
break;
|
||||
|
||||
case Entity_Variable: {
|
||||
@@ -1399,13 +1405,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
@@ -1417,9 +1423,9 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(vd, VarDecl, us->node);
|
||||
if (gb_array_count(vd->names) > 1 && vd->type != NULL) {
|
||||
error(&c->error_collector, us->token, "`using` can only be applied to one variable of the same type");
|
||||
error(us->token, "`using` can only be applied to one variable of the same type");
|
||||
}
|
||||
check_var_decl_node(c, us->node);
|
||||
check_var_decl(c, us->node);
|
||||
|
||||
gb_for_array(name_index, vd->names) {
|
||||
AstNode *item = vd->names[name_index];
|
||||
@@ -1436,13 +1442,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(&c->error_collector, us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1450,7 +1456,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
|
||||
default:
|
||||
error(&c->error_collector, us->token, "Invalid AST: Using Statement");
|
||||
error(us->token, "Invalid AST: Using Statement");
|
||||
break;
|
||||
}
|
||||
case_end;
|
||||
@@ -1461,7 +1467,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
|
||||
case_ast_node(vd, VarDecl, node);
|
||||
check_var_decl_node(c, node);
|
||||
check_var_decl(c, node);
|
||||
case_end;
|
||||
|
||||
case_ast_node(cd, ConstDecl, node);
|
||||
check_const_decl(c, node);
|
||||
case_end;
|
||||
|
||||
case_ast_node(pd, ProcDecl, node);
|
||||
|
||||
@@ -7,17 +7,9 @@ struct ssaGen {
|
||||
};
|
||||
|
||||
b32 ssa_gen_init(ssaGen *s, Checker *c) {
|
||||
if (c->error_collector.count != 0)
|
||||
if (global_error_collector.count != 0)
|
||||
return false;
|
||||
|
||||
gb_for_array(i, c->parser->files) {
|
||||
AstFile *f = &c->parser->files[i];
|
||||
if (f->error_collector.count != 0)
|
||||
return false;
|
||||
if (f->tokenizer.error_count != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
isize tc = c->parser->total_token_count;
|
||||
if (tc < 2) {
|
||||
return false;
|
||||
|
||||
@@ -3053,82 +3053,80 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, VarDecl, node);
|
||||
if (vd->kind == Declaration_Mutable) {
|
||||
if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
|
||||
gbArray(ssaAddr) lvals;
|
||||
gbArray(ssaValue *) inits;
|
||||
gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
defer (gb_array_free(lvals));
|
||||
defer (gb_array_free(inits));
|
||||
if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
|
||||
gbArray(ssaAddr) lvals;
|
||||
gbArray(ssaValue *) inits;
|
||||
gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
defer (gb_array_free(lvals));
|
||||
defer (gb_array_free(inits));
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
GB_ASSERT(lval.addr != NULL);
|
||||
}
|
||||
|
||||
gb_array_append(lvals, lval);
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
GB_ASSERT(lval.addr != NULL);
|
||||
}
|
||||
gb_for_array(i, vd->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vd->values[i]);
|
||||
|
||||
gb_array_append(lvals, lval);
|
||||
}
|
||||
gb_for_array(i, vd->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vd->values[i]);
|
||||
gb_array_append(inits, init);
|
||||
}
|
||||
|
||||
|
||||
gb_for_array(i, inits) {
|
||||
ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
|
||||
ssa_lvalue_store(proc, lvals[i], v);
|
||||
}
|
||||
|
||||
} else if (gb_array_count(vd->values) == 0) { // declared and zero-initialized
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, true);
|
||||
}
|
||||
}
|
||||
} else { // Tuple(s)
|
||||
gbArray(ssaAddr) lvals;
|
||||
gbArray(ssaValue *) inits;
|
||||
gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
defer (gb_array_free(lvals));
|
||||
defer (gb_array_free(inits));
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
}
|
||||
|
||||
gb_array_append(lvals, lval);
|
||||
}
|
||||
|
||||
gb_for_array(i, vd->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vd->values[i]);
|
||||
Type *t = ssa_type(init);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
ssaValue *v = ssa_emit_struct_ev(proc, init, i, e->type);
|
||||
gb_array_append(inits, v);
|
||||
}
|
||||
} else {
|
||||
gb_array_append(inits, init);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_for_array(i, inits) {
|
||||
ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
|
||||
ssa_lvalue_store(proc, lvals[i], v);
|
||||
}
|
||||
|
||||
} else if (gb_array_count(vd->values) == 0) { // declared and zero-initialized
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, true);
|
||||
}
|
||||
}
|
||||
} else { // Tuple(s)
|
||||
gbArray(ssaAddr) lvals;
|
||||
gbArray(ssaValue *) inits;
|
||||
gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
|
||||
defer (gb_array_free(lvals));
|
||||
defer (gb_array_free(inits));
|
||||
|
||||
gb_for_array(i, vd->names) {
|
||||
AstNode *name = vd->names[i];
|
||||
ssaAddr lval = ssa_make_addr(NULL, NULL);
|
||||
if (!ssa_is_blank_ident(name)) {
|
||||
ssa_add_local_for_identifier(proc, name, false);
|
||||
lval = ssa_build_addr(proc, name);
|
||||
}
|
||||
|
||||
gb_array_append(lvals, lval);
|
||||
}
|
||||
|
||||
gb_for_array(i, vd->values) {
|
||||
ssaValue *init = ssa_build_expr(proc, vd->values[i]);
|
||||
Type *t = ssa_type(init);
|
||||
if (t->kind == Type_Tuple) {
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
Entity *e = t->Tuple.variables[i];
|
||||
ssaValue *v = ssa_emit_struct_ev(proc, init, i, e->type);
|
||||
gb_array_append(inits, v);
|
||||
}
|
||||
} else {
|
||||
gb_array_append(inits, init);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_for_array(i, inits) {
|
||||
ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
|
||||
ssa_lvalue_store(proc, lvals[i], v);
|
||||
}
|
||||
gb_for_array(i, inits) {
|
||||
ssaValue *v = ssa_emit_conv(proc, inits[i], ssa_addr_type(lvals[i]));
|
||||
ssa_lvalue_store(proc, lvals[i], v);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
255
src/parser.cpp
255
src/parser.cpp
@@ -34,8 +34,6 @@ struct AstFile {
|
||||
isize scope_level;
|
||||
Scope * scope; // NOTE(bill): Created in checker
|
||||
|
||||
ErrorCollector error_collector;
|
||||
|
||||
// TODO(bill): Error recovery
|
||||
// NOTE(bill): Error recovery
|
||||
#define PARSER_MAX_FIX_COUNT 6
|
||||
@@ -59,13 +57,6 @@ struct Parser {
|
||||
isize total_token_count;
|
||||
};
|
||||
|
||||
enum DeclKind {
|
||||
Declaration_Invalid,
|
||||
Declaration_Mutable,
|
||||
Declaration_Immutable,
|
||||
Declaration_Count,
|
||||
};
|
||||
|
||||
enum ProcTag : u64 {
|
||||
ProcTag_bounds_check = GB_BIT(0),
|
||||
ProcTag_no_bounds_check = GB_BIT(1),
|
||||
@@ -227,20 +218,25 @@ AST_NODE_KIND(_StmtEnd, "", struct{}) \
|
||||
AST_NODE_KIND(_DeclBegin, "", struct{}) \
|
||||
AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(VarDecl, "variable declaration", struct { \
|
||||
DeclKind kind; \
|
||||
u64 tags; \
|
||||
b32 is_using; \
|
||||
AstNodeArray names; \
|
||||
AstNode *type; \
|
||||
AstNodeArray values; \
|
||||
}) \
|
||||
}) \
|
||||
AST_NODE_KIND(ConstDecl, "constant declaration", struct { \
|
||||
u64 tags; \
|
||||
AstNodeArray names; \
|
||||
AstNode *type; \
|
||||
AstNodeArray values; \
|
||||
}) \
|
||||
AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
|
||||
AstNode *name; \
|
||||
AstNode *type; \
|
||||
AstNode *body; \
|
||||
u64 tags; \
|
||||
String foreign_name; \
|
||||
}) \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeDecl, "type declaration", struct { Token token; AstNode *name, *type; }) \
|
||||
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
||||
Token token, relpath; \
|
||||
@@ -419,6 +415,8 @@ Token ast_node_token(AstNode *node) {
|
||||
return node->BadDecl.begin;
|
||||
case AstNode_VarDecl:
|
||||
return ast_node_token(node->VarDecl.names[0]);
|
||||
case AstNode_ConstDecl:
|
||||
return ast_node_token(node->ConstDecl.names[0]);
|
||||
case AstNode_ProcDecl:
|
||||
return node->ProcDecl.name->Ident;
|
||||
case AstNode_TypeDecl:
|
||||
@@ -458,26 +456,6 @@ HashKey hash_token(Token t) {
|
||||
return hash_string(t.string);
|
||||
}
|
||||
|
||||
#define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__)
|
||||
void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) {
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(file->error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
file->error_collector.prev = token.pos;
|
||||
|
||||
#if 0
|
||||
gb_printf_err("%s()\n", function);
|
||||
#endif
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Syntax error: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
file->error_collector.count++;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
|
||||
gb_inline AstNode *make_node(AstFile *f, AstNodeKind kind) {
|
||||
@@ -525,11 +503,11 @@ gb_inline AstNode *make_binary_expr(AstFile *f, Token op, AstNode *left, AstNode
|
||||
AstNode *result = make_node(f, AstNode_BinaryExpr);
|
||||
|
||||
if (left == NULL) {
|
||||
ast_file_err(f, op, "No lhs expression for binary expression `%.*s`", LIT(op.string));
|
||||
syntax_error(op, "No lhs expression for binary expression `%.*s`", LIT(op.string));
|
||||
left = make_bad_expr(f, op, op);
|
||||
}
|
||||
if (right == NULL) {
|
||||
ast_file_err(f, op, "No rhs expression for binary expression `%.*s`", LIT(op.string));
|
||||
syntax_error(op, "No rhs expression for binary expression `%.*s`", LIT(op.string));
|
||||
right = make_bad_expr(f, op, op);
|
||||
}
|
||||
|
||||
@@ -795,15 +773,22 @@ gb_inline AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_var_decl(AstFile *f, DeclKind kind, AstNodeArray names, AstNode *type, AstNodeArray values) {
|
||||
gb_inline AstNode *make_var_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
|
||||
AstNode *result = make_node(f, AstNode_VarDecl);
|
||||
result->VarDecl.kind = kind;
|
||||
result->VarDecl.names = names;
|
||||
result->VarDecl.type = type;
|
||||
result->VarDecl.values = values;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_const_decl(AstFile *f, AstNodeArray names, AstNode *type, AstNodeArray values) {
|
||||
AstNode *result = make_node(f, AstNode_ConstDecl);
|
||||
result->ConstDecl.names = names;
|
||||
result->ConstDecl.type = type;
|
||||
result->ConstDecl.values = values;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, b32 is_using) {
|
||||
AstNode *result = make_node(f, AstNode_Field);
|
||||
result->Field.names = names;
|
||||
@@ -918,7 +903,7 @@ gb_inline b32 next_token(AstFile *f) {
|
||||
f->cursor++;
|
||||
return true;
|
||||
} else {
|
||||
ast_file_err(f, f->cursor[0], "Token is EOF");
|
||||
syntax_error(f->cursor[0], "Token is EOF");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -926,7 +911,7 @@ gb_inline b32 next_token(AstFile *f) {
|
||||
gb_inline Token expect_token(AstFile *f, TokenKind kind) {
|
||||
Token prev = f->cursor[0];
|
||||
if (prev.kind != kind) {
|
||||
ast_file_err(f, f->cursor[0], "Expected `%.*s`, got `%.*s`",
|
||||
syntax_error(f->cursor[0], "Expected `%.*s`, got `%.*s`",
|
||||
LIT(token_strings[kind]),
|
||||
LIT(token_strings[prev.kind]));
|
||||
}
|
||||
@@ -937,7 +922,7 @@ gb_inline Token expect_token(AstFile *f, TokenKind kind) {
|
||||
gb_inline Token expect_operator(AstFile *f) {
|
||||
Token prev = f->cursor[0];
|
||||
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
|
||||
ast_file_err(f, f->cursor[0], "Expected an operator, got `%.*s`",
|
||||
syntax_error(f->cursor[0], "Expected an operator, got `%.*s`",
|
||||
LIT(token_strings[prev.kind]));
|
||||
}
|
||||
next_token(f);
|
||||
@@ -947,7 +932,7 @@ gb_inline Token expect_operator(AstFile *f) {
|
||||
gb_inline Token expect_keyword(AstFile *f) {
|
||||
Token prev = f->cursor[0];
|
||||
if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
|
||||
ast_file_err(f, f->cursor[0], "Expected a keyword, got `%.*s`",
|
||||
syntax_error(f->cursor[0], "Expected a keyword, got `%.*s`",
|
||||
LIT(token_strings[prev.kind]));
|
||||
}
|
||||
next_token(f);
|
||||
@@ -1027,7 +1012,7 @@ b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) {
|
||||
if (f->cursor[0].pos.line == f->cursor[-1].pos.line) {
|
||||
if (f->cursor[0].kind != Token_CloseBrace) {
|
||||
// CLEANUP(bill): Semicolon handling in parser
|
||||
ast_file_err(f, f->cursor[0],
|
||||
syntax_error(f->cursor[0],
|
||||
"Expected `;` after %.*s, got `%.*s`",
|
||||
LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind]));
|
||||
return false;
|
||||
@@ -1126,7 +1111,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags = 0);
|
||||
|
||||
void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
|
||||
if (*tags & tag) {
|
||||
ast_file_err(f, ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name));
|
||||
syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name));
|
||||
}
|
||||
*tags |= tag;
|
||||
}
|
||||
@@ -1200,7 +1185,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
|
||||
*foreign_name = f->cursor[0].string;
|
||||
// TODO(bill): Check if valid string
|
||||
if (!is_foreign_name_valid(*foreign_name)) {
|
||||
ast_file_err(f, ast_node_token(tag_expr), "Invalid alternative foreign procedure name");
|
||||
syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name");
|
||||
}
|
||||
|
||||
next_token(f);
|
||||
@@ -1216,22 +1201,22 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
|
||||
ELSE_IF_ADD_TAG(fastcall)
|
||||
// ELSE_IF_ADD_TAG(cdecl)
|
||||
else {
|
||||
ast_file_err(f, ast_node_token(tag_expr), "Unknown procedure tag");
|
||||
syntax_error(ast_node_token(tag_expr), "Unknown procedure tag");
|
||||
}
|
||||
|
||||
#undef ELSE_IF_ADD_TAG
|
||||
}
|
||||
|
||||
if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
|
||||
syntax_error(f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
|
||||
}
|
||||
|
||||
if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
|
||||
syntax_error(f->cursor[0], "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
|
||||
}
|
||||
|
||||
if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
|
||||
syntax_error(f->cursor[0], "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1272,7 +1257,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
|
||||
Token *s = &f->cursor[0];
|
||||
|
||||
if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) {
|
||||
ast_file_err(f, *s, "Invalid rune literal %.*s", LIT(s->string));
|
||||
syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string));
|
||||
}
|
||||
s->kind = Token_Rune; // NOTE(bill): Change it
|
||||
} else {
|
||||
@@ -1308,7 +1293,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
|
||||
String foreign_name = {};
|
||||
parse_proc_tags(f, &tags, &foreign_name);
|
||||
if (tags & ProcTag_foreign) {
|
||||
ast_file_err(f, f->cursor[0], "#foreign cannot be applied to procedure literals");
|
||||
syntax_error(f->cursor[0], "#foreign cannot be applied to procedure literals");
|
||||
}
|
||||
|
||||
if (f->cursor[0].kind != Token_OpenBrace) {
|
||||
@@ -1335,7 +1320,7 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
|
||||
}
|
||||
|
||||
Token begin = f->cursor[0];
|
||||
ast_file_err(f, begin, "Expected an operand");
|
||||
syntax_error(begin, "Expected an operand");
|
||||
fix_advance_to_next_stmt(f);
|
||||
return make_bad_expr(f, begin, f->cursor[0]);
|
||||
}
|
||||
@@ -1365,7 +1350,7 @@ AstNode *parse_call_expr(AstFile *f, AstNode *operand) {
|
||||
f->cursor[0].kind != Token_EOF &&
|
||||
ellipsis.pos.line == 0) {
|
||||
if (f->cursor[0].kind == Token_Comma)
|
||||
ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
|
||||
syntax_error(f->cursor[0], "Expected an expression not a ,");
|
||||
|
||||
if (f->cursor[0].kind == Token_Ellipsis) {
|
||||
ellipsis = f->cursor[0];
|
||||
@@ -1425,7 +1410,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
||||
operand = make_selector_expr(f, token, operand, parse_identifier(f));
|
||||
break;
|
||||
default: {
|
||||
ast_file_err(f, f->cursor[0], "Expected a selector");
|
||||
syntax_error(f->cursor[0], "Expected a selector");
|
||||
next_token(f);
|
||||
operand = make_selector_expr(f, f->cursor[0], operand, NULL);
|
||||
} break;
|
||||
@@ -1467,11 +1452,11 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
|
||||
if (colon_count == 2) {
|
||||
triple_indexed = true;
|
||||
if (indices[1] == NULL) {
|
||||
ast_file_err(f, colons[0], "Second index is required in a triple indexed slice");
|
||||
syntax_error(colons[0], "Second index is required in a triple indexed slice");
|
||||
indices[1] = make_bad_expr(f, colons[0], colons[1]);
|
||||
}
|
||||
if (indices[2] == NULL) {
|
||||
ast_file_err(f, colons[1], "Third index is required in a triple indexed slice");
|
||||
syntax_error(colons[1], "Third index is required in a triple indexed slice");
|
||||
indices[2] = make_bad_expr(f, colons[1], close);
|
||||
}
|
||||
}
|
||||
@@ -1558,7 +1543,7 @@ AstNode *parse_binary_expr(AstFile *f, b32 lhs, i32 prec_in) {
|
||||
default:
|
||||
right = parse_binary_expr(f, false, prec+1);
|
||||
if (!right) {
|
||||
ast_file_err(f, op, "Expected expression on the right hand side of the binary operator");
|
||||
syntax_error(op, "Expected expression on the right hand side of the binary operator");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1622,13 +1607,13 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
||||
case Token_CmpOrEq:
|
||||
{
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a simple statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
next_token(f);
|
||||
AstNodeArray rhs = parse_rhs_expr_list(f);
|
||||
if (gb_array_count(rhs) == 0) {
|
||||
ast_file_err(f, token, "No right-hand side in assignment statement.");
|
||||
syntax_error(token, "No right-hand side in assignment statement.");
|
||||
return make_bad_stmt(f, token, f->cursor[0]);
|
||||
}
|
||||
return make_assign_stmt(f, token, lhs, rhs);
|
||||
@@ -1639,7 +1624,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
||||
}
|
||||
|
||||
if (lhs_count > 1) {
|
||||
ast_file_err(f, token, "Expected 1 expression");
|
||||
syntax_error(token, "Expected 1 expression");
|
||||
return make_bad_stmt(f, token, f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -1648,7 +1633,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
||||
case Token_Increment:
|
||||
case Token_Decrement:
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a simple statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a simple statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
statement = make_inc_dec_stmt(f, token, lhs[0]);
|
||||
@@ -1663,7 +1648,7 @@ AstNode *parse_simple_stmt(AstFile *f) {
|
||||
|
||||
AstNode *parse_block_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a block statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a block statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
AstNode *block_stmt = parse_body(f);
|
||||
@@ -1677,7 +1662,7 @@ AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
|
||||
if (statement->kind == AstNode_ExprStmt)
|
||||
return statement->ExprStmt.expr;
|
||||
|
||||
ast_file_err(f, f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind));
|
||||
syntax_error(f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind));
|
||||
return make_bad_expr(f, f->cursor[0], f->cursor[1]);
|
||||
}
|
||||
|
||||
@@ -1710,7 +1695,7 @@ AstNode *parse_type(AstFile *f) {
|
||||
AstNode *type = parse_type_attempt(f);
|
||||
if (type == NULL) {
|
||||
Token token = f->cursor[0];
|
||||
ast_file_err(f, token, "Expected a type");
|
||||
syntax_error(token, "Expected a type");
|
||||
next_token(f);
|
||||
return make_bad_expr(f, token, f->cursor[0]);
|
||||
}
|
||||
@@ -1738,11 +1723,11 @@ AstNode *parse_field_decl(AstFile *f) {
|
||||
|
||||
AstNodeArray names = parse_lhs_expr_list(f);
|
||||
if (gb_array_count(names) == 0) {
|
||||
ast_file_err(f, f->cursor[0], "Empty field declaration");
|
||||
syntax_error(f->cursor[0], "Empty field declaration");
|
||||
}
|
||||
|
||||
if (gb_array_count(names) > 1 && is_using) {
|
||||
ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
||||
syntax_error(f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
||||
is_using = false;
|
||||
}
|
||||
|
||||
@@ -1755,11 +1740,11 @@ AstNode *parse_field_decl(AstFile *f) {
|
||||
next_token(f);
|
||||
type = parse_type_attempt(f);
|
||||
if (type == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "variadic parameter is missing a type after `..`");
|
||||
syntax_error(f->cursor[0], "variadic parameter is missing a type after `..`");
|
||||
type = make_bad_expr(f, ellipsis, f->cursor[0]);
|
||||
} else {
|
||||
if (gb_array_count(names) > 1) {
|
||||
ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only `..`");
|
||||
syntax_error(f->cursor[0], "mutliple variadic parameters, only `..`");
|
||||
} else {
|
||||
type = make_ellipsis(f, ellipsis, type);
|
||||
}
|
||||
@@ -1769,7 +1754,7 @@ AstNode *parse_field_decl(AstFile *f) {
|
||||
}
|
||||
|
||||
if (type == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
|
||||
syntax_error(f->cursor[0], "Expected a type for this field declaration");
|
||||
}
|
||||
|
||||
AstNode *field = make_field(f, names, type, is_using);
|
||||
@@ -1804,15 +1789,15 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
|
||||
}
|
||||
AstNodeArray names = parse_lhs_expr_list(f);
|
||||
if (gb_array_count(names) == 0) {
|
||||
ast_file_err(f, f->cursor[0], "Empty field declaration");
|
||||
syntax_error(f->cursor[0], "Empty field declaration");
|
||||
}
|
||||
|
||||
if (!using_allowed && is_using) {
|
||||
ast_file_err(f, f->cursor[0], "Cannot apply `using` to members of a union");
|
||||
syntax_error(f->cursor[0], "Cannot apply `using` to members of a union");
|
||||
is_using = false;
|
||||
}
|
||||
if (gb_array_count(names) > 1 && is_using) {
|
||||
ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
||||
syntax_error(f->cursor[0], "Cannot apply `using` to more than one of the same type");
|
||||
}
|
||||
|
||||
AstNode *decl = NULL;
|
||||
@@ -1821,11 +1806,11 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
|
||||
decl = parse_decl(f, names);
|
||||
|
||||
if (decl->kind == AstNode_ProcDecl) {
|
||||
ast_file_err(f, f->cursor[0], "Procedure declarations are not allowed within a structure");
|
||||
syntax_error(f->cursor[0], "Procedure declarations are not allowed within a structure");
|
||||
decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
|
||||
}
|
||||
} else {
|
||||
ast_file_err(f, f->cursor[0], "Illegal structure field");
|
||||
syntax_error(f->cursor[0], "Illegal structure field");
|
||||
decl = make_bad_decl(f, ast_node_token(names[0]), f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -1835,13 +1820,9 @@ AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, b32 using_allow
|
||||
gb_array_append(decls, decl);
|
||||
if (decl->kind == AstNode_VarDecl) {
|
||||
decl->VarDecl.is_using = is_using && using_allowed;
|
||||
|
||||
if (decl->VarDecl.kind == Declaration_Mutable) {
|
||||
if (gb_array_count(decl->VarDecl.values) > 0) {
|
||||
ast_file_err(f, f->cursor[0], "Default variable assignments within a structure will be ignored (at the moment)");
|
||||
}
|
||||
if (gb_array_count(decl->VarDecl.values) > 0) {
|
||||
syntax_error(f->cursor[0], "Default variable assignments within a structure will be ignored (at the moment)");
|
||||
}
|
||||
|
||||
} else {
|
||||
decl_count += 1;
|
||||
}
|
||||
@@ -1925,12 +1906,12 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
|
||||
} else if (are_strings_equal(tag.string, make_string("ordered"))) {
|
||||
is_ordered = true;
|
||||
} else {
|
||||
ast_file_err(f, tag, "Expected a `#packed` or `#ordered` tag");
|
||||
syntax_error(tag, "Expected a `#packed` or `#ordered` tag");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_packed && is_ordered) {
|
||||
ast_file_err(f, token, "`#ordered` is not needed with `#packed` which implies ordering");
|
||||
syntax_error(token, "`#ordered` is not needed with `#packed` which implies ordering");
|
||||
}
|
||||
|
||||
Token open = expect_token(f, Token_OpenBrace);
|
||||
@@ -2034,8 +2015,8 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
|
||||
break;
|
||||
// fallthrough
|
||||
default:
|
||||
ast_file_err(f, f->cursor[0],
|
||||
"Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
|
||||
syntax_error(f->cursor[0],
|
||||
"Expected a type or identifier after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2109,7 +2090,7 @@ AstNode *parse_proc_decl(AstFile *f, Token proc_token, AstNode *name) {
|
||||
|
||||
if (f->cursor[0].kind == Token_OpenBrace) {
|
||||
if ((tags & ProcTag_foreign) != 0) {
|
||||
ast_file_err(f, f->cursor[0], "A procedure tagged as `#foreign` cannot have a body");
|
||||
syntax_error(f->cursor[0], "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
body = parse_body(f);
|
||||
}
|
||||
@@ -2127,7 +2108,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
|
||||
String n = name->Ident.string;
|
||||
// NOTE(bill): Check for reserved identifiers
|
||||
if (are_strings_equal(n, make_string("context"))) {
|
||||
ast_file_err(f, ast_node_token(name), "`context` is a reserved identifier");
|
||||
syntax_error(ast_node_token(name), "`context` is a reserved identifier");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2138,15 +2119,16 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
|
||||
type = parse_identifier_or_type(f);
|
||||
}
|
||||
} else if (f->cursor[0].kind != Token_Eq && f->cursor[0].kind != Token_Semicolon) {
|
||||
ast_file_err(f, f->cursor[0], "Expected type separator `:` or `=`");
|
||||
syntax_error(f->cursor[0], "Expected type separator `:` or `=`");
|
||||
}
|
||||
|
||||
DeclKind declaration_kind = Declaration_Mutable;
|
||||
b32 is_mutable = true;
|
||||
|
||||
if (f->cursor[0].kind == Token_Eq ||
|
||||
f->cursor[0].kind == Token_Colon) {
|
||||
if (f->cursor[0].kind == Token_Colon)
|
||||
declaration_kind = Declaration_Immutable;
|
||||
if (f->cursor[0].kind == Token_Colon) {
|
||||
is_mutable = false;
|
||||
}
|
||||
next_token(f);
|
||||
|
||||
if (f->cursor[0].kind == Token_type ||
|
||||
@@ -2159,71 +2141,67 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
|
||||
next_token(f);
|
||||
}
|
||||
if (gb_array_count(names) != 1) {
|
||||
ast_file_err(f, ast_node_token(names[0]), "You can only declare one type at a time");
|
||||
syntax_error(ast_node_token(names[0]), "You can only declare one type at a time");
|
||||
return make_bad_decl(f, names[0]->Ident, token);
|
||||
}
|
||||
|
||||
if (type != NULL) {
|
||||
ast_file_err(f, f->cursor[-1], "Expected either `type` or nothing between : and :");
|
||||
syntax_error(f->cursor[-1], "Expected either `type` or nothing between : and :");
|
||||
// NOTE(bill): Do not fail though
|
||||
}
|
||||
|
||||
AstNode *type = parse_type(f);
|
||||
return make_type_decl(f, token, names[0], type);
|
||||
} else if (f->cursor[0].kind == Token_proc &&
|
||||
declaration_kind == Declaration_Immutable) {
|
||||
is_mutable == false) {
|
||||
// NOTE(bill): Procedure declarations
|
||||
Token proc_token = f->cursor[0];
|
||||
AstNode *name = names[0];
|
||||
if (gb_array_count(names) != 1) {
|
||||
ast_file_err(f, proc_token, "You can only declare one procedure at a time");
|
||||
syntax_error(proc_token, "You can only declare one procedure at a time");
|
||||
return make_bad_decl(f, name->Ident, proc_token);
|
||||
}
|
||||
|
||||
AstNode *proc_decl = parse_proc_decl(f, proc_token, name);
|
||||
return proc_decl;
|
||||
return parse_proc_decl(f, proc_token, name);
|
||||
|
||||
} else {
|
||||
values = parse_rhs_expr_list(f);
|
||||
if (gb_array_count(values) > gb_array_count(names)) {
|
||||
ast_file_err(f, f->cursor[0], "Too many values on the right hand side of the declaration");
|
||||
} else if (gb_array_count(values) < gb_array_count(names) &&
|
||||
declaration_kind == Declaration_Immutable) {
|
||||
ast_file_err(f, f->cursor[0], "All constant declarations must be defined");
|
||||
syntax_error(f->cursor[0], "Too many values on the right hand side of the declaration");
|
||||
} else if (gb_array_count(values) < gb_array_count(names) && !is_mutable) {
|
||||
syntax_error(f->cursor[0], "All constant declarations must be defined");
|
||||
} else if (gb_array_count(values) == 0) {
|
||||
ast_file_err(f, f->cursor[0], "Expected an expression for this declaration");
|
||||
syntax_error(f->cursor[0], "Expected an expression for this declaration");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration_kind == Declaration_Mutable) {
|
||||
if (is_mutable) {
|
||||
if (type == NULL && gb_array_count(values) == 0) {
|
||||
ast_file_err(f, f->cursor[0], "Missing variable type or initialization");
|
||||
return make_bad_decl(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
} else if (declaration_kind == Declaration_Immutable) {
|
||||
if (type == NULL && gb_array_count(values) == 0 && gb_array_count(names) > 0) {
|
||||
ast_file_err(f, f->cursor[0], "Missing constant value");
|
||||
syntax_error(f->cursor[0], "Missing variable type or initialization");
|
||||
return make_bad_decl(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
} else {
|
||||
Token begin = f->cursor[0];
|
||||
ast_file_err(f, begin, "Unknown type of variable declaration");
|
||||
fix_advance_to_next_stmt(f);
|
||||
return make_bad_decl(f, begin, f->cursor[0]);
|
||||
if (type == NULL && gb_array_count(values) == 0 && gb_array_count(names) > 0) {
|
||||
syntax_error(f->cursor[0], "Missing constant value");
|
||||
return make_bad_decl(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (values == NULL) {
|
||||
values = make_ast_node_array(f);
|
||||
}
|
||||
|
||||
return make_var_decl(f, declaration_kind, names, type, values);
|
||||
if (is_mutable) {
|
||||
return make_var_decl(f, names, type, values);
|
||||
}
|
||||
return make_const_decl(f, names, type, values);
|
||||
}
|
||||
|
||||
|
||||
AstNode *parse_if_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use an if statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use an if statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2252,7 +2230,7 @@ AstNode *parse_if_stmt(AstFile *f) {
|
||||
f->expr_level = prev_level;
|
||||
|
||||
if (cond == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "Expected condition for if statement");
|
||||
syntax_error(f->cursor[0], "Expected condition for if statement");
|
||||
}
|
||||
|
||||
body = parse_block_stmt(f);
|
||||
@@ -2266,7 +2244,7 @@ AstNode *parse_if_stmt(AstFile *f) {
|
||||
else_stmt = parse_block_stmt(f);
|
||||
break;
|
||||
default:
|
||||
ast_file_err(f, f->cursor[0], "Expected if statement block statement");
|
||||
syntax_error(f->cursor[0], "Expected if statement block statement");
|
||||
else_stmt = make_bad_stmt(f, f->cursor[0], f->cursor[1]);
|
||||
break;
|
||||
}
|
||||
@@ -2277,7 +2255,7 @@ AstNode *parse_if_stmt(AstFile *f) {
|
||||
|
||||
AstNode *parse_return_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a return statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a return statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2297,7 +2275,7 @@ AstNode *parse_return_stmt(AstFile *f) {
|
||||
|
||||
AstNode *parse_for_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a for statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a for statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2314,7 +2292,7 @@ AstNode *parse_for_stmt(AstFile *f) {
|
||||
if (f->cursor[0].kind != Token_Semicolon) {
|
||||
cond = parse_simple_stmt(f);
|
||||
if (is_ast_node_complex_stmt(cond)) {
|
||||
ast_file_err(f, f->cursor[0],
|
||||
syntax_error(f->cursor[0],
|
||||
"You are not allowed that type of statement in a for statement, it is too complex!");
|
||||
}
|
||||
}
|
||||
@@ -2371,7 +2349,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
|
||||
|
||||
AstNode *parse_match_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a match statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a match statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2444,7 +2422,7 @@ AstNode *parse_match_stmt(AstFile *f) {
|
||||
|
||||
AstNode *parse_defer_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
ast_file_err(f, f->cursor[0], "You cannot use a defer statement in the file scope");
|
||||
syntax_error(f->cursor[0], "You cannot use a defer statement in the file scope");
|
||||
return make_bad_stmt(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2452,13 +2430,13 @@ AstNode *parse_defer_stmt(AstFile *f) {
|
||||
AstNode *statement = parse_stmt(f);
|
||||
switch (statement->kind) {
|
||||
case AstNode_EmptyStmt:
|
||||
ast_file_err(f, token, "Empty statement after defer (e.g. `;`)");
|
||||
syntax_error(token, "Empty statement after defer (e.g. `;`)");
|
||||
break;
|
||||
case AstNode_DeferStmt:
|
||||
ast_file_err(f, token, "You cannot defer a defer statement");
|
||||
syntax_error(token, "You cannot defer a defer statement");
|
||||
break;
|
||||
case AstNode_ReturnStmt:
|
||||
ast_file_err(f, token, "You cannot a return statement");
|
||||
syntax_error(token, "You cannot a return statement");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2554,14 +2532,12 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
}
|
||||
} break;
|
||||
case AstNode_VarDecl:
|
||||
if (node->VarDecl.kind == Declaration_Mutable) {
|
||||
valid = true;
|
||||
}
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
ast_file_err(f, token, "Illegal use of `using` statement.");
|
||||
syntax_error(token, "Illegal use of `using` statement.");
|
||||
return make_bad_stmt(f, token, f->cursor[0]);
|
||||
}
|
||||
|
||||
@@ -2577,7 +2553,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
f->is_global_scope = true;
|
||||
return make_empty_stmt(f, f->cursor[0]);
|
||||
}
|
||||
ast_file_err(f, token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope.");
|
||||
syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope.");
|
||||
return make_bad_decl(f, token, f->cursor[0]);
|
||||
} else if (are_strings_equal(tag, make_string("import"))) {
|
||||
// TODO(bill): better error messages
|
||||
@@ -2589,7 +2565,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
return make_import_decl(f, s->TagStmt.token, file_path, import_name, false);
|
||||
}
|
||||
ast_file_err(f, token, "You cannot use #import within a procedure. This must be done at the file scope.");
|
||||
syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope.");
|
||||
return make_bad_decl(f, token, file_path);
|
||||
} else if (are_strings_equal(tag, make_string("load"))) {
|
||||
// TODO(bill): better error messages
|
||||
@@ -2600,24 +2576,23 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
if (f->curr_proc == NULL) {
|
||||
return make_import_decl(f, s->TagStmt.token, file_path, import_name, true);
|
||||
}
|
||||
ast_file_err(f, token, "You cannot use #load within a procedure. This must be done at the file scope.");
|
||||
syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope.");
|
||||
return make_bad_decl(f, token, file_path);
|
||||
} else if (are_strings_equal(tag, make_string("foreign_system_library"))) {
|
||||
Token file_path = expect_token(f, Token_String);
|
||||
if (f->curr_proc == NULL) {
|
||||
return make_foreign_system_library(f, s->TagStmt.token, file_path);
|
||||
}
|
||||
ast_file_err(f, token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope.");
|
||||
syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope.");
|
||||
return make_bad_decl(f, token, file_path);
|
||||
} else if (are_strings_equal(tag, make_string("thread_local"))) {
|
||||
AstNode *var_decl = parse_simple_stmt(f);
|
||||
if (var_decl->kind != AstNode_VarDecl ||
|
||||
var_decl->VarDecl.kind != Declaration_Mutable) {
|
||||
ast_file_err(f, token, "#thread_local may only be applied to variable declarations");
|
||||
if (var_decl->kind != AstNode_VarDecl) {
|
||||
syntax_error(token, "#thread_local may only be applied to variable declarations");
|
||||
return make_bad_decl(f, token, ast_node_token(var_decl));
|
||||
}
|
||||
if (f->curr_proc != NULL) {
|
||||
ast_file_err(f, token, "#thread_local is only allowed at the file scope.");
|
||||
syntax_error(token, "#thread_local is only allowed at the file scope.");
|
||||
return make_bad_decl(f, token, ast_node_token(var_decl));
|
||||
}
|
||||
var_decl->VarDecl.tags |= VarDeclTag_thread_local;
|
||||
@@ -2626,14 +2601,14 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
s = parse_stmt(f);
|
||||
s->stmt_state_flags |= StmtStateFlag_bounds_check;
|
||||
if ((s->stmt_state_flags & StmtStateFlag_no_bounds_check) != 0) {
|
||||
ast_file_err(f, token, "#bounds_check and #no_bounds_check cannot be applied together");
|
||||
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
|
||||
}
|
||||
return s;
|
||||
} else if (are_strings_equal(tag, make_string("no_bounds_check"))) {
|
||||
s = parse_stmt(f);
|
||||
s->stmt_state_flags |= StmtStateFlag_no_bounds_check;
|
||||
if ((s->stmt_state_flags & StmtStateFlag_bounds_check) != 0) {
|
||||
ast_file_err(f, token, "#bounds_check and #no_bounds_check cannot be applied together");
|
||||
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -2651,7 +2626,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
return s;
|
||||
}
|
||||
|
||||
ast_file_err(f, token,
|
||||
syntax_error(token,
|
||||
"Expected a statement, got `%.*s`",
|
||||
LIT(token_strings[token.kind]));
|
||||
fix_advance_to_next_stmt(f);
|
||||
@@ -2837,14 +2812,14 @@ void parse_file(Parser *p, AstFile *f) {
|
||||
node->kind != AstNode_BadStmt &&
|
||||
node->kind != AstNode_EmptyStmt) {
|
||||
// NOTE(bill): Sanity check
|
||||
ast_file_err(f, ast_node_token(node), "Only declarations are allowed at file scope");
|
||||
syntax_error(ast_node_token(node), "Only declarations are allowed at file scope");
|
||||
} else {
|
||||
if (node->kind == AstNode_ImportDecl) {
|
||||
auto *id = &node->ImportDecl;
|
||||
String file_str = id->relpath.string;
|
||||
|
||||
if (!is_import_path_valid(file_str)) {
|
||||
ast_file_err(f, ast_node_token(node), "Invalid `load` path");
|
||||
syntax_error(ast_node_token(node), "Invalid `load` path");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2868,7 +2843,7 @@ void parse_file(Parser *p, AstFile *f) {
|
||||
String file_str = id->filepath.string;
|
||||
|
||||
if (!is_import_path_valid(file_str)) {
|
||||
ast_file_err(f, ast_node_token(node), "Invalid `foreign_system_library` path");
|
||||
syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -142,11 +142,18 @@ void print_ast(AstNode *node, isize indent) {
|
||||
|
||||
case AstNode_VarDecl:
|
||||
print_indent(indent);
|
||||
if (node->VarDecl.kind == Declaration_Mutable) {
|
||||
gb_printf("(decl:var,mutable)\n");
|
||||
} else if (node->VarDecl.kind == Declaration_Immutable) {
|
||||
gb_printf("(decl:var,immutable)\n");
|
||||
gb_printf("(decl:var)\n");
|
||||
gb_for_array(i, node->VarDecl.names) {
|
||||
print_ast(node->VarDecl.names[i], indent+1);
|
||||
}
|
||||
print_ast(node->VarDecl.type, indent+1);
|
||||
gb_for_array(i, node->VarDecl.values) {
|
||||
print_ast(node->VarDecl.values[i], indent+1);
|
||||
}
|
||||
break;
|
||||
case AstNode_ConstDecl:
|
||||
print_indent(indent);
|
||||
gb_printf("(decl:const)\n");
|
||||
gb_for_array(i, node->VarDecl.names) {
|
||||
print_ast(node->VarDecl.names[i], indent+1);
|
||||
}
|
||||
|
||||
@@ -155,31 +155,58 @@ Token empty_token = {Token_Invalid};
|
||||
struct ErrorCollector {
|
||||
TokenPos prev;
|
||||
i64 count;
|
||||
i64 warning_count;
|
||||
};
|
||||
|
||||
gb_no_inline void error(ErrorCollector *ec, Token token, char *fmt, ...) {
|
||||
ec->count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(ec->prev, token.pos)) {
|
||||
ec->prev = token.pos;
|
||||
gb_global ErrorCollector global_error_collector;
|
||||
|
||||
|
||||
void warning(Token token, char *fmt, ...) {
|
||||
global_error_collector.warning_count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Warning: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
}
|
||||
|
||||
void error(Token token, char *fmt, ...) {
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
gb_no_inline void warning(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Warning: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
void syntax_error(Token token, char *fmt, ...) {
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user