203 lines
4.8 KiB
Plaintext
203 lines
4.8 KiB
Plaintext
import "@std"
|
|
import "@std/mem"
|
|
import "@std/arraylist"
|
|
import "@std/debug"
|
|
import "@std/enums/enummap"
|
|
|
|
import "@source/lexer"
|
|
|
|
NodeId :: distinct u32
|
|
ExtraId :: distinct u32
|
|
|
|
NO_ID_NODE :: maxval!(NodeId)
|
|
NO_ID_EXTRA :: maxval!(ExtraId)
|
|
|
|
Diagnostic :: struct {
|
|
code ErrorCode
|
|
node NodeId
|
|
}
|
|
|
|
ParseError :: alias ErrorCode | mem.AllocError
|
|
|
|
ErrorCode :: enum {
|
|
unexpected_token,
|
|
}
|
|
|
|
ErrorDetails :: struct {
|
|
name []u8
|
|
message []u8
|
|
}
|
|
|
|
error_msg_map std.EnumMap(ErrorCode, ErrorDetails) :: enummap.init({
|
|
unexpected_token = ErrorDetails {
|
|
name = "P0",
|
|
message = "unexpected token",
|
|
},
|
|
})
|
|
|
|
hide Token :: alias lexer.Token
|
|
hide TokenId :: alias lexer.TokenId
|
|
hide TokenKind :: alias lexer.TokenKind
|
|
|
|
NodeData :: union {
|
|
token_id TokenId
|
|
node_id NodeId
|
|
extra_id ExtraId
|
|
}
|
|
|
|
Node :: struct {
|
|
kind NodeKind
|
|
main_token TokenId
|
|
|
|
data0 NodeData = NodeData{ node_id = NO_ID_NODE }
|
|
data1 NodeData = NodeData{ node_id = NO_ID_NODE }
|
|
}
|
|
|
|
NodeKind :: enum {
|
|
# LITERALS:
|
|
# * main_token: token literal
|
|
literal_int
|
|
literal_float
|
|
literal_string
|
|
|
|
# IDENTIFIER EXPR:
|
|
# * main_token: token literal
|
|
expr_identifier
|
|
|
|
expr_unary
|
|
expr_binary
|
|
|
|
# DECL:
|
|
# * main_token: symbol name (identifier)
|
|
# * data0: ExtraId
|
|
# + extra[data0]: NodeId - type identifier node
|
|
# + extra[data0 + 1]: NodeId - initializer expression
|
|
# * data1: TokenId of either `::` or `=` indicating mutability
|
|
stmt_decl
|
|
|
|
invalid
|
|
}
|
|
|
|
State :: struct {
|
|
nodes std.ArrayList(Node)
|
|
|
|
#! array of indexes into the nodes array.
|
|
extra std.ArrayList(NodeId)
|
|
|
|
tokens []Token = &[]
|
|
next_token TokenId = TokenId(0)
|
|
}
|
|
|
|
init proc(allocator mem.Allocator) State {
|
|
return State{
|
|
nodes = arraylist.init(allocator),
|
|
extra = arraylist.init(allocator),
|
|
}
|
|
}
|
|
|
|
deinit proc(state @mut State) void {
|
|
arraylist.deinit(&state.nodes)
|
|
arraylist.deinit(&state.extra)
|
|
}
|
|
|
|
parse proc(state @mut State, tokens []Token) void ! ParseError {
|
|
state.tokens = tokens
|
|
_ = try parse_decl(state)
|
|
}
|
|
|
|
parse_decl proc(state @mut State) NodeId ! ParseError {
|
|
# expect identifier
|
|
try expect(state, .ident)
|
|
ident_token :: state.next_token
|
|
state.next_token += 1
|
|
|
|
# check for type
|
|
# todo: make a parse_type proc for this
|
|
type_id :: if (state.tokens[state.next_token].kind == .ident)
|
|
try parse_primary(state)
|
|
else
|
|
NO_ID_NODE
|
|
|
|
# expect `::` (immutable assignment)
|
|
try expect(state, .double_colon)
|
|
mutability_tok_id :: state.next_token
|
|
state.next_token += 1
|
|
|
|
# expect expression
|
|
expr_id :: try parse_primary(state)
|
|
|
|
# expect statement terminator (newline)
|
|
try expect_either(state, &[.newline, .eof])
|
|
state.next_token += 1
|
|
|
|
extra_start :: try add_extra(&state.extra, type_id)
|
|
_ = try add_extra(&state.extra, expr_id)
|
|
|
|
decl :: try add_node(&state.nodes, Node{
|
|
kind = .stmt_decl,
|
|
main_token = ident_token,
|
|
data0 = NodeData{ extra_id = extra_start },
|
|
data1 = NodeData{ token_id = mutability_tok_id },
|
|
})
|
|
|
|
return decl
|
|
}
|
|
|
|
parse_primary proc(state @mut State) NodeId ! mem.AllocError {
|
|
start_token :: state.next_token
|
|
state.next_token += 1
|
|
return match state.tokens[start_token].kind {
|
|
.int: try add_node(&state.nodes, Node{
|
|
kind = .literal_int,
|
|
main_token = start_token,
|
|
})
|
|
.float: try add_node(&state.nodes, Node{
|
|
kind = .literal_float,
|
|
main_token = start_token,
|
|
})
|
|
.string: try add_node(&state.nodes, Node{
|
|
kind = .literal_string,
|
|
main_token = start_token,
|
|
})
|
|
.ident: try add_node(&state.nodes, Node{
|
|
kind = .expr_identifier,
|
|
main_token = start_token,
|
|
})
|
|
else: try add_node(&state.nodes, Node{
|
|
kind = .invalid,
|
|
main_token = start_token,
|
|
})
|
|
}
|
|
}
|
|
|
|
hide expect proc(state @mut State, token_kind TokenKind) void ! ErrorCode {
|
|
if (state.tokens[state.next_token].kind != token_kind) return .unexpected_token
|
|
}
|
|
|
|
hide expect_either proc(state @mut State, token_kinds []TokenKind) void ! ErrorCode {
|
|
for (token_kinds) |kind| if (state.tokens[state.next_token].kind == kind) return
|
|
return .unexpected_token
|
|
}
|
|
|
|
hide add_node proc(nodes @mut std.ArrayList(Node), node Node) NodeId ! mem.AllocError {
|
|
id :: node_id(nodes.items.len)
|
|
try arraylist.append(nodes, node)
|
|
return id
|
|
}
|
|
|
|
hide node_id proc(idx uint) NodeId {
|
|
debug.assert(u64(idx) < u64(NO_ID_NODE))
|
|
return NodeId(idx)
|
|
}
|
|
|
|
hide add_extra proc(extra @mut std.ArrayList(NodeId), id_node NodeId) ExtraId ! mem.AllocError {
|
|
id_extra :: extra_id(extra.items.len)
|
|
try arraylist.append(extra, id_node)
|
|
return id_extra
|
|
}
|
|
|
|
hide extra_id proc(idx uint) ExtraId {
|
|
debug.assert(u64(idx) < u64(NO_ID_EXTRA))
|
|
return ExtraId(idx)
|
|
}
|