Improve messages, implement* field access

Compiler messages are now printed in color with their respective code,
field access support has been added in the type checker. However, field
access nodes that contain other field access nodes are not yet
supported.

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
Slendi 2024-04-08 11:42:39 +03:00
parent 0f5db0972c
commit a966ff45a3
11 changed files with 362 additions and 139 deletions

View File

@ -49,14 +49,14 @@ NodeKind :: enum {
Node :: struct {
kind: NodeKind,
range: TextRange,
range: SourceLocation,
children: [dynamic]^Node,
value: TokenValue,
value_token_kind: TokenKind,
return_type: ^Type,
}
node_create_value :: proc(kind: NodeKind, range: TextRange, value: TokenValue) -> (ret: ^Node) {
node_create_value :: proc(kind: NodeKind, range: SourceLocation, value: TokenValue) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = kind,
@ -66,7 +66,7 @@ node_create_value :: proc(kind: NodeKind, range: TextRange, value: TokenValue) -
return
}
node_create_pointer :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
node_create_pointer :: proc(range: SourceLocation, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Pointer,
@ -76,7 +76,7 @@ node_create_pointer :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
return
}
node_create_array :: proc(range: TextRange, size: u64, value: ^Node) -> (ret: ^Node) {
node_create_array :: proc(range: SourceLocation, size: u64, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Array,
@ -87,7 +87,7 @@ node_create_array :: proc(range: TextRange, size: u64, value: ^Node) -> (ret: ^N
return
}
node_create_block :: proc(range: TextRange, children: [dynamic]^Node) -> (ret: ^Node) {
node_create_block :: proc(range: SourceLocation, children: [dynamic]^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Block,
@ -97,7 +97,7 @@ node_create_block :: proc(range: TextRange, children: [dynamic]^Node) -> (ret: ^
return
}
node_create_binary :: proc(kind: TokenKind, range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
node_create_binary :: proc(kind: TokenKind, range: SourceLocation, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .BinaryExpression,
@ -108,7 +108,7 @@ node_create_binary :: proc(kind: TokenKind, range: TextRange, left: ^Node, right
return
}
node_create_unary :: proc(kind: TokenKind, range: TextRange, operand: ^Node) -> (ret: ^Node) {
node_create_unary :: proc(kind: TokenKind, range: SourceLocation, operand: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .UnaryExpression,
@ -119,7 +119,7 @@ node_create_unary :: proc(kind: TokenKind, range: TextRange, operand: ^Node) ->
return
}
node_create_field_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
node_create_field_access :: proc(range: SourceLocation, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .FieldAccess,
@ -129,7 +129,7 @@ node_create_field_access :: proc(range: TextRange, left: ^Node, right: ^Node) ->
return
}
node_create_index_access :: proc(range: TextRange, left: ^Node, right: ^Node) -> (ret: ^Node) {
node_create_index_access :: proc(range: SourceLocation, left: ^Node, right: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .IndexAccess,
@ -139,7 +139,7 @@ node_create_index_access :: proc(range: TextRange, left: ^Node, right: ^Node) ->
return
}
node_create_function_call :: proc(range: TextRange, name: ^Node, args: [dynamic]^Node) -> (ret: ^Node) {
node_create_function_call :: proc(range: SourceLocation, name: ^Node, args: [dynamic]^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .FunctionCall,
@ -153,7 +153,7 @@ node_create_function_call :: proc(range: TextRange, name: ^Node, args: [dynamic]
}
node_create_extern_function :: proc(
range: TextRange,
range: SourceLocation,
name: [dynamic]u8,
return_type: ^Node,
args: [dynamic]^Node,
@ -174,7 +174,7 @@ node_create_extern_function :: proc(
}
node_create_function :: proc(
range: TextRange,
range: SourceLocation,
name: [dynamic]u8,
return_type, body: ^Node,
args: [dynamic]^Node,
@ -264,7 +264,7 @@ parse_use_path2 :: proc(path: [dynamic]u8) -> (ret: [dynamic]u8) {
return
}
node_create_use :: proc(range: TextRange, path, alias: [dynamic]u8) -> (ret: ^Node) {
node_create_use :: proc(range: SourceLocation, path, alias: [dynamic]u8) -> (ret: ^Node) {
path_ := path
// Check if the path ends with ".cat", if not, append it.
path_ = parse_use_path(path)
@ -288,7 +288,7 @@ node_create_use :: proc(range: TextRange, path, alias: [dynamic]u8) -> (ret: ^No
return
}
node_create_if :: proc(range: TextRange, condition, then, else_: ^Node) -> (ret: ^Node) {
node_create_if :: proc(range: SourceLocation, condition, then, else_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .If,
@ -305,7 +305,7 @@ node_create_if :: proc(range: TextRange, condition, then, else_: ^Node) -> (ret:
return
}
node_create_for :: proc(range: TextRange, init, condition, step, body: ^Node) -> (ret: ^Node) {
node_create_for :: proc(range: SourceLocation, init, condition, step, body: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .For,
@ -315,7 +315,7 @@ node_create_for :: proc(range: TextRange, init, condition, step, body: ^Node) ->
return
}
node_create_variable :: proc(range: TextRange, name, type_, value: ^Node, is_const: bool) -> (ret: ^Node) {
node_create_variable :: proc(range: SourceLocation, name, type_, value: ^Node, is_const: bool) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .VariableDeclaration,
@ -326,7 +326,7 @@ node_create_variable :: proc(range: TextRange, name, type_, value: ^Node, is_con
return
}
node_create_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret: ^Node) {
node_create_cast :: proc(range: SourceLocation, value, type_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Cast,
@ -336,7 +336,7 @@ node_create_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret: ^Node)
return
}
node_create_bitwise_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret: ^Node) {
node_create_bitwise_cast :: proc(range: SourceLocation, value, type_: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .BitwiseCast,
@ -346,7 +346,7 @@ node_create_bitwise_cast :: proc(range: TextRange, value, type_: ^Node) -> (ret:
return
}
node_create_ret :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
node_create_ret :: proc(range: SourceLocation, value: ^Node) -> (ret: ^Node) {
ret = new(Node)
ret^ = {
kind = .Ret,
@ -356,7 +356,13 @@ node_create_ret :: proc(range: TextRange, value: ^Node) -> (ret: ^Node) {
return
}
node_create_struct_initializer :: proc(range: TextRange, name: [dynamic]u8, fields: [dynamic]^Node) -> (ret: ^Node) {
node_create_struct_initializer :: proc(
range: SourceLocation,
name: [dynamic]u8,
fields: [dynamic]^Node,
) -> (
ret: ^Node,
) {
ret = new(Node)
if ret == nil {
panic("Failed to allocate node")
@ -374,7 +380,7 @@ node_create_struct_initializer :: proc(range: TextRange, name: [dynamic]u8, fiel
}
node_create_struct_enum_or_union :: proc(
range: TextRange,
range: SourceLocation,
kind: NodeKind,
name: [dynamic]u8,
fields: [dynamic]^Node,

View File

@ -1,24 +1,93 @@
package main
import "core:fmt"
MessageLevel :: enum {
FIXME,
Warning,
Error,
Fatal,
}
// FIXME: Add file
Message :: struct {
level: MessageLevel,
message: string,
range: TextRange,
level: MessageLevel,
message: string,
source_location: SourceLocation,
}
g_message_list : [dynamic]Message
g_message_list: [dynamic]Message
message_create :: proc(level: MessageLevel, message: string, range: TextRange) -> Message {
return Message {
level = level,
message = message,
range = range,
message_create :: proc(level: MessageLevel, message: string, source_location: SourceLocation) -> Message {
return Message{level = level, message = message, source_location = source_location}
}
source_print :: proc(data: ^[]u8, range: TextRange) {
pos := TextPosition{1, 1}
should_print := false
additional_spaces := 0
for ch in data {
should_print := pos.line >= range.start.line && pos.line <= range.end.line
if should_print {
if pos.column == 1 {
fmt.printf("\x1B[90m % 4d | \x1B[0m", pos.line)
}
if range.start.line == pos.line && range.start.column - 1 == pos.column {
fmt.printf("\x1B[95m")
}
if ch == '\t' {
additional_spaces += 1
fmt.printf(" ")
} else {
fmt.printf("%c", ch)
}
if pos == range.end {
fmt.printf("\x1B[0m")
}
}
pos.column += 1
if ch == '\n' {
pos.column = 1
pos.line += 1
}
}
fmt.printf(" ")
for _ in 0 ..< range.start.column + u64(additional_spaces) {
fmt.printf(" ")
}
fmt.printf("\x1B[95m")
for _ in 0 ..= range.end.column - range.start.column {
fmt.printf("^")
}
fmt.println(" Here \x1B[0m")
}
message_print :: proc(message: ^Message, source_code: ^[]u8) {
fmt.printf("\x1b[1m")
if message.level == .FIXME {
fmt.printf("\x1B[94mFIXME\x1B[0m \x1b[37mat ")
} else if message.level == .Warning {
fmt.printf("\x1B[93mWarning\x1B[0m \x1b[37mat ")
} else if message.level == .Error {
fmt.printf("\x1B[91mError\x1B[0m \x1b[37mat ")
} else if message.level == .Fatal {
fmt.printf("\x1B[91;1mError\x1B[0m \x1b[37mat ")
}
fmt.printf(
"\x1B[32m%s\x1b[90m:\x1B[36m{}:{}\x1B[0m->\x1B[36m{}:{}\x1B[0m: ",
message.source_location.file,
message.source_location.range.start.line,
message.source_location.range.start.column,
message.source_location.range.end.line,
message.source_location.range.end.column,
)
fmt.println(message.message)
source_print(source_code, message.source_location.range)
}

View File

@ -5,6 +5,7 @@ import "core:fmt"
import "core:math"
Lexer :: struct {
file_name: string,
data: ^[dynamic]u8,
read_position: u64,
position: TextPosition,
@ -13,9 +14,10 @@ Lexer :: struct {
should_return_semicolon: bool,
}
lexer_create :: proc(data: ^[dynamic]u8) -> ^Lexer {
lexer_create :: proc(data: ^[dynamic]u8, file_name: string) -> ^Lexer {
lexer := new(Lexer)
lexer^ = {
file_name = file_name,
data = data,
read_position = 0,
position = TextPosition{line = 1, column = 1},
@ -100,15 +102,15 @@ lexer_next :: proc(lexer: ^Lexer) -> (ret: Token) {
if lexer.should_return_semicolon {
lexer.should_return_semicolon = false
return token_create(.Semicolon, TextRange{start = lexer.position, end = lexer.position})
return token_create(
.Semicolon,
SourceLocation{TextRange{start = lexer.position, end = lexer.position}, lexer.file_name},
)
}
defer lexer.last_token_kind = ret.kind
crange := TextRange {
start = lexer.position,
end = lexer.position,
}
crange := SourceLocation{TextRange{start = lexer.position, end = lexer.position}, lexer.file_name}
ret = token_create(.Invalid, crange)
should_advance := true
@ -118,18 +120,18 @@ lexer_next :: proc(lexer: ^Lexer) -> (ret: Token) {
ret = token_create(.Add, crange)
if lexer.next == '+' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.Increment, crange)
}
case '-':
ret = token_create(.Subtract, crange)
if lexer.next == '-' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.Decrement, crange)
} else if lexer.next == '>' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.Arrow, crange)
}
case '*':
@ -144,36 +146,36 @@ lexer_next :: proc(lexer: ^Lexer) -> (ret: Token) {
ret = token_create(.Assign, crange)
if lexer.next == '=' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.Equals, crange)
}
case '!':
ret = token_create(.Not, crange)
if lexer.next == '=' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.NotEquals, crange)
}
case '<':
ret = token_create(.LessThan, crange)
if lexer.next == '=' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.LessThanOrEqual, crange)
} else if lexer.next == '<' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.BitwiseLeftShift, crange)
}
case '>':
ret = token_create(.GreaterThan, crange)
if lexer.next == '=' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.GreaterThanOrEqual, crange)
} else if lexer.next == '>' {
lexer_advance(lexer)
crange.end = lexer.position
crange.range.end = lexer.position
ret = token_create(.BitwiseRightShift, crange)
}
case '&':
@ -236,10 +238,7 @@ lexer_next :: proc(lexer: ^Lexer) -> (ret: Token) {
@(private = "file")
lexer_read_string :: proc(lexer: ^Lexer, kind: TokenKind, outer: u8) -> Token {
crange := TextRange {
start = lexer.position,
end = lexer.position,
}
crange := SourceLocation{TextRange{start = lexer.position, end = lexer.position}, lexer.file_name}
lexer_advance(lexer)
@ -268,7 +267,7 @@ lexer_read_string :: proc(lexer: ^Lexer, kind: TokenKind, outer: u8) -> Token {
message_create(
.Warning,
fmt.aprintf("Invalid string/character escape: %c at %s", lexer.char, "TODO LOCATION"),
range,
SourceLocation{range, lexer.file_name},
),
)
}
@ -279,21 +278,19 @@ lexer_read_string :: proc(lexer: ^Lexer, kind: TokenKind, outer: u8) -> Token {
lexer_advance(lexer)
}
crange.end = lexer.position
crange.range.end = lexer.position
return token_create_u8(kind, str, crange)
}
@(private = "file")
lexer_read_identifier :: proc(lexer: ^Lexer) -> Token {
crange := TextRange {
start = lexer.position,
}
crange := SourceLocation{TextRange{start = lexer.position}, lexer.file_name}
str: [dynamic]u8
for libc.isalnum(i32(lexer.char)) != 0 || lexer.char == '_' {
append(&str, lexer.char)
crange.end = lexer.position
crange.range.end = lexer.position
lexer_advance(lexer)
}
@ -421,7 +418,11 @@ lexer_read_number :: proc(lexer: ^Lexer) -> Token {
} else if read_mode == .Hex {
append(
&g_message_list,
message_create(.Error, "Hexadecimal floating point numbers are not supported yet", crange),
message_create(
.Error,
"Hexadecimal floating point numbers are not supported yet",
SourceLocation{crange, lexer.file_name},
),
)
lowered := libc.tolower(i32(lexer.char))
for libc.isxdigit(lowered) != 0 && lexer.char > 0 {
@ -437,7 +438,11 @@ lexer_read_number :: proc(lexer: ^Lexer) -> Token {
} else if read_mode == .Binary {
append(
&g_message_list,
message_create(.Error, "Binary floating point numbers are not supported yet", crange),
message_create(
.Error,
"Binary floating point numbers are not supported yet",
SourceLocation{crange, lexer.file_name},
),
)
for lexer.char == '0' || lexer.char == '1' {
fractional_part = (fractional_part << 1) + u64(lexer.char) - '0'
@ -456,8 +461,8 @@ lexer_read_number :: proc(lexer: ^Lexer) -> Token {
floating: f64 = 0
floating = f64(fractional_part) / math.pow_f64(10, f64(count)) + f64(whole_part)
return token_create_f64(.Float, floating, crange)
return token_create_f64(.Float, floating, SourceLocation{crange, lexer.file_name})
}
return token_create_u64(.Integer, whole_part, crange)
return token_create_u64(.Integer, whole_part, SourceLocation{crange, lexer.file_name})
}

View File

@ -5,13 +5,15 @@ import "core:os"
main :: proc() {
handle: os.Handle
file_name := "<stdin>"
if len(os.args) >= 2 {
errno: os.Errno
handle, errno = os.open(os.args[1])
if errno != 0 {
fmt.printf("Error opening file\n", errno)
return
return
}
file_name = os.args[1]
} else {
handle = os.stdin
}
@ -23,37 +25,51 @@ main :: proc() {
return
}
u8_arr : [dynamic]u8
u8_arr: [dynamic]u8
for ch in data {
append(&u8_arr, u8(ch))
}
lexer := lexer_create(&u8_arr)
lexer := lexer_create(&u8_arr, file_name)
parser := parser_create(lexer)
ast := parser_parse(&parser)
if len(g_message_list) > 0 {
for msg in g_message_list {
fmt.printf("%s\n", msg)
contains_errors := false
for &msg in g_message_list {
message_print(&msg, &data)
if msg.level == .Error || msg.level == .Fatal {
contains_errors = true
}
//fmt.printf("%s\n", msg)
}
if contains_errors {
return
}
return
}
fmt.println("After parse:")
node_print(ast)
//node_print(ast)
clear(&g_message_list)
type_check(ast, nil)
fmt.println("After type check:")
node_print(ast)
//node_print(ast)
if len(g_message_list) > 0 {
for msg in g_message_list {
fmt.printf("%s\n", msg)
contains_errors := false
for &msg in g_message_list {
message_print(&msg, &data)
if msg.level == .Error || msg.level == .Fatal {
contains_errors = true
}
//fmt.printf("%s\n", msg)
}
if contains_errors {
return
}
return
}
node_print(ast)
//node_print(ast)
name : string
name: string
if handle == os.stdin {
name = "stdin"
} else {

View File

@ -112,7 +112,7 @@ parser_parse_statement :: proc(parser: ^Parser) -> (ret: ^Node) {
expect(parser, .Semicolon)
}
if ret != nil {
ret.range.start = range_beg.start
ret.range.range.start = range_beg.range.start
}
return
}
@ -395,7 +395,7 @@ parser_parse_binary_expression :: proc(
parser.can_be_function = false
rhs := next(parser)
lhs = node_create_binary(kind, lhs.range, lhs, rhs)
lhs^.range.end = rhs.range.end
lhs^.range.range.end = rhs.range.range.end
parser.can_be_function = prev_can_be_function
}
i += 1
@ -481,23 +481,23 @@ parser_parse_prefix_2 :: proc(parser: ^Parser) -> ^Node {
range := parser.tok.range
if accept(parser, .Not) {
rhs := parser_parse_suffix(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
return node_create_unary(.Not, range, rhs)
} else if accept(parser, .BitwiseNot) {
rhs := parser_parse_suffix(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
return node_create_unary(.BitwiseNot, range, rhs)
} else if accept(parser, .Increment) {
rhs := parser_parse_suffix(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
return node_create_unary(.Increment, range, rhs)
} else if accept(parser, .Decrement) {
rhs := parser_parse_suffix(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
return node_create_unary(.Decrement, range, rhs)
} else if accept(parser, .BitwiseXOR) {
rhs := parser_parse_suffix(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
return node_create_unary(.BitwiseXOR, range, rhs)
}
return parser_parse_suffix(parser)
@ -508,10 +508,10 @@ parser_parse_suffix :: proc(parser: ^Parser) -> ^Node {
range := parser.tok.range
lhs := parser_parse_prefix(parser)
range_op := parser.tok.range
range.end = range_op.end
range.range.end = range_op.range.end
if accept(parser, .OpenBracket) {
rhs := parser_parse_expression(parser)
range.end = rhs.range.end
range.range.end = rhs.range.range.end
expect(parser, .CloseBracket)
return node_create_index_access(range, lhs, rhs)
} else if accept(parser, .Increment) {
@ -520,11 +520,11 @@ parser_parse_suffix :: proc(parser: ^Parser) -> ^Node {
return node_create_unary(.Decrement, range, lhs)
} else if accept(parser, .As) {
type := parser_parse_type(parser)
range.end = type.range.end
range.range.end = type.range.range.end
return node_create_cast(range, lhs, type)
} else if accept(parser, .BitwiseAs) {
type := parser_parse_type(parser)
range.end = type.range.end
range.range.end = type.range.range.end
return node_create_bitwise_cast(range, lhs, type)
}
return lhs
@ -574,7 +574,7 @@ parser_parse_factor :: proc(parser: ^Parser) -> (ret: ^Node) {
expect(parser, .OpenBrace)
args := parser_parse_expression_list(parser)
range.end = parser.tok.range.end
range.range.end = parser.tok.range.range.end
expect(parser, .CloseBrace)
ret = node_create_struct_initializer(range, name, args)
} else if parser.tok.kind == .Integer {
@ -595,7 +595,11 @@ parser_parse_factor :: proc(parser: ^Parser) -> (ret: ^Node) {
prev := parser.can_be_function
parser.can_be_function = false
if accept(parser, .Dot) {
ret = node_create_field_access({ret.range.start, parser.tok.range.start}, ret, parser_parse_factor(parser))
ret = node_create_field_access(
SourceLocation{{ret.range.range.start, parser.tok.range.range.start}, ret.range.file},
ret,
parser_parse_factor(parser),
)
}
parser.can_be_function = prev
if parser.can_be_function &&

View File

@ -88,13 +88,18 @@ TokenKind :: enum {
}
TextPosition :: struct {
line: u64,
line: u64,
column: u64,
}
TextRange :: struct {
start: TextPosition,
end: TextPosition,
end: TextPosition,
}
SourceLocation :: struct {
range: TextRange,
file: string,
}
TokenValue :: union {
@ -106,42 +111,23 @@ TokenValue :: union {
}
Token :: struct {
kind: TokenKind,
kind: TokenKind,
value: TokenValue,
range: TextRange,
range: SourceLocation,
}
token_create :: proc(kind: TokenKind, range: TextRange) -> Token {
return {
kind = kind,
value = nil,
range = range,
};
token_create :: proc(kind: TokenKind, range: SourceLocation) -> Token {
return {kind = kind, value = nil, range = range}
}
token_create_u8 :: proc(kind: TokenKind, text: [dynamic]u8, range: TextRange) -> Token {
return {
kind = kind,
value = text,
range = range,
};
token_create_u8 :: proc(kind: TokenKind, text: [dynamic]u8, range: SourceLocation) -> Token {
return {kind = kind, value = text, range = range}
}
token_create_u64 :: proc(kind: TokenKind, value: u64, range: TextRange) -> Token {
return {
kind = kind,
value = value,
range = range,
};
token_create_u64 :: proc(kind: TokenKind, value: u64, range: SourceLocation) -> Token {
return {kind = kind, value = value, range = range}
}
token_create_f64 :: proc(kind: TokenKind, value: f64, range: TextRange) -> Token {
return {
kind = kind,
value = value,
range = range,
};
token_create_f64 :: proc(kind: TokenKind, value: f64, range: SourceLocation) -> Token {
return {kind = kind, value = value, range = range}
}

View File

@ -1,5 +1,7 @@
package main
import "core:fmt"
TypeKind :: enum {
Integer,
Float,
@ -22,6 +24,25 @@ Type :: struct {
struct_type: ^StructType,
}
type_to_string :: proc(type: ^Type) -> string {
if type.kind == .Integer {
if type.is_signed {
return fmt.aprintf("i{}", type.bit_size)
} else {
return fmt.aprintf("u{}", type.bit_size)
}
} else if type.kind == .Float {
return fmt.aprintf("f{}", type.bit_size)
} else if type.kind == .Pointer {
return fmt.aprintf("^{}", type_to_string(type.pointer_to))
} else if type.kind == .Array {
return fmt.aprintf("[{}]{}", type.array_size, type_to_string(type.array_of))
} else if type.kind == .Struct {
return fmt.aprintf("Struct`%s`", type.struct_type.name)
}
return "???"
}
FunctionType :: struct {
name: [dynamic]u8,
return_type: ^Type,
@ -31,6 +52,11 @@ FunctionType :: struct {
compare_types :: proc(a: ^Type, b: ^Type) -> (ret: bool, cast_required: bool) {
cast_required = false
if (a == nil && b != nil) || (a != nil && b == nil) {
ret = false
return
}
if (a.kind == .Integer || a.kind == .Float) && (a.bit_size > b.bit_size) {
ret = true
cast_required = true

View File

@ -232,20 +232,93 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
type_check(child, ast)
}
scope_leave()
case .FunctionCall:
if ast.children[0].kind == .FieldAccess {
type_check(ast.children[0], ast)
if ast.children[0].return_type == nil {
append(
&g_message_list,
message_create(.Error, fmt.aprintf("Field access return type is nil"), ast.children[0].range),
)
case .FieldAccess:
lhs := ast.children[0]
rhs := ast.children[1]
// FIXME: Add support for nesting
if lhs.kind != .Identifier {
append(
&g_message_list,
message_create(.Error, fmt.aprintf("Field access lhs is not an identifier"), lhs.range),
)
break
}
if rhs.kind != .Identifier {
append(
&g_message_list,
message_create(.Error, fmt.aprintf("Field access rhs is not an identifier"), rhs.range),
)
break
}
struct_var := scope_variable_lookup(lhs.value.([dynamic]u8))
if struct_var == nil {
append(
&g_message_list,
message_create(
.Error,
fmt.aprintf("Cannot find struct of name: `%s`", lhs.value.([dynamic]u8)),
rhs.range,
),
)
break
}
struct_ := scope_struct_lookup(struct_var.struct_type.name)
if struct_ == nil {
append(
&g_message_list,
message_create(
.Error,
fmt.aprintf("Cannot find struct of type name: `%s`", lhs.value.([dynamic]u8)),
rhs.range,
),
)
break
}
found_field := false
for &field in struct_.fields {
if compare_dyn_arrs(&field.name, &rhs.value.([dynamic]u8)) {
ast.return_type = field.type
found_field = true
break
}
}
lhs := ast.children[0].children[0]
rhs := ast.children[0].children[1]
if !found_field {
append(
&g_message_list,
message_create(
.Error,
fmt.aprintf("Cannot find field of name: `%s`", rhs.value.([dynamic]u8)),
rhs.range,
),
)
break
}
case .FunctionCall:
if ast.children[0].kind == .FieldAccess {
// FIXME: This is some temporary shitfuckery, check if a function is part
// of a struct or namespace first, then do this shit
type_check(ast.children[0], ast)
child := ast.children[0]^
free(ast.children[0])
clear(&ast.children)
ast^ = child
return
//if ast.children[0].return_type == nil {
// append(
// &g_message_list,
// message_create(.Error, fmt.aprintf("Field access return type is nil"), ast.children[0].range),
// )
// break
//}
//lhs := ast.children[0].children[0]
//rhs := ast.children[0].children[1]
}
type := scope_variable_lookup(ast.children[0].value.([dynamic]u8))
@ -336,7 +409,11 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
&g_message_list,
message_create(
.Error,
fmt.aprintf("Type mismatch: {} and {}", ast.children[0].return_type, ast.children[1].return_type),
fmt.aprintf(
"Type mismatch: {} and {}",
type_to_string(ast.children[0].return_type),
type_to_string(ast.children[1].return_type),
),
ast.range,
),
)
@ -365,7 +442,6 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
ast.value_token_kind == .LessThanOrEqual {
ast.return_type = type_create_integer(1, true)
}
// FIXME: Verify that the operation is possible
case .UnaryExpression:
// FIXME: Verify that the operation is possible
@ -390,7 +466,11 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
&g_message_list,
message_create(
.Error,
fmt.aprintf("Type mismatch: {} and {}", function_return_type, ast.children[0].return_type),
fmt.aprintf(
"Type mismatch: {} and {}",
type_to_string(function_return_type),
type_to_string(ast.children[0].return_type),
),
ast.range,
),
)
@ -399,19 +479,27 @@ type_check :: proc(ast: ^Node, parent_ast: ^Node) {
case .Cast:
type_check(ast.children[0], ast)
type_to := ast_to_type(ast.children[1])
append(
&g_message_list,
message_create(
.Warning,
fmt.aprintf("Cast to type not checked: {}", ast.children[1].value.([dynamic]u8)),
ast.children[1].range,
),
)
// FIXME: Check if compatible
if ast.children[0].return_type.kind == .Struct || type_to.kind == .Struct {
append(&g_message_list, message_create(.Error, "Cannot cast to/from Struct type.", ast.range))
} else {
// FIXME: Check if compatible
append(
&g_message_list,
message_create(
.FIXME,
fmt.aprintf("Cast to type not checked: %s.", ast.children[1].value.([dynamic]u8)),
ast.range,
),
)
}
ast.return_type = type_to
case .BitwiseCast:
type_check(ast.children[0], ast)
// FIXME: Check if they are both the same bit size
append(
&g_message_list,
message_create(.FIXME, fmt.aprintf("BitwiseCast bit size check not implemented."), ast.range),
)
ast.return_type = ast_to_type(ast.children[1])
case .VariableDeclaration:
if ast.children[2] != nil {

View File

@ -31,3 +31,18 @@ get_character_sum_of_dyn_arr :: proc(a: ^[dynamic]u8) -> int {
}
return sum
}
digit_count := proc(a: u64) -> (r: u64) {
a_ := a
if a == 0 {
return 1
}
r = 0
for a_ != 0 {
a_ /= 10
r += 1
}
return
}

View File

@ -59,7 +59,7 @@ fn WindowShouldClose i32
1 << 3 | 2
fn ColorToRaylib(c: Color) u32 {
ret c.a as u32 << 24 | c.b as u32 << 16 | c.g as u32 << 8 | c.r
ret c.a as u32 << 24 as u32 | c.b as u32 << 16 as u32 | c.g as u32 << 8 as u32 | c.r
}
fn ClearBackgroundWrap(c: Color) {

8
testmin.cat Normal file
View File

@ -0,0 +1,8 @@
struct A {
a b: i32,
}
let inst := .A{ 1 2 }
inst.a as u32