/*
* Copyright (C) 2024 olang maintainers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "lexer.h"
#include "parser.h"
static bool
skip_expected_token(parser_t *parser, token_kind_t expected_kind);
static bool
expected_token(parser_t *parser, token_t *token, token_kind_t kind);
static bool
parser_parse_type(parser_t *parser, type_t *type);
static ast_node_t *
parser_parse_block(parser_t *parser);
static void
skip_line_feeds(lexer_t *lexer);
void
parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
{
assert(parser && "parser is required");
assert(lexer && "lexer is required");
assert(file_path && "file_path is required");
parser->lexer = lexer;
parser->arena = arena;
parser->file_path = file_path;
}
ast_node_t *
parser_parse_fn_definition(parser_t *parser)
{
if (!skip_expected_token(parser, TOKEN_FN)) {
return NULL;
}
skip_line_feeds(parser->lexer);
token_t fn_name_token;
if (!expected_token(parser, &fn_name_token, TOKEN_IDENTIFIER)) {
return NULL;
}
skip_line_feeds(parser->lexer);
if (!skip_expected_token(parser, TOKEN_OPAREN)) {
return NULL;
}
skip_line_feeds(parser->lexer);
if (!skip_expected_token(parser, TOKEN_CPAREN)) {
return NULL;
}
skip_line_feeds(parser->lexer);
if (!skip_expected_token(parser, TOKEN_COLON)) {
return NULL;
}
skip_line_feeds(parser->lexer);
type_t fn_return_type;
if (!parser_parse_type(parser, &fn_return_type)) {
return NULL;
}
skip_line_feeds(parser->lexer);
ast_node_t *block = parser_parse_block(parser);
if (block == NULL) {
return NULL;
}
return ast_new_node_fn_def(parser->arena, fn_name_token.value, fn_return_type, block);
}
static bool
parser_parse_type(parser_t *parser, type_t *type)
{
token_t token;
if (!expected_token(parser, &token, TOKEN_IDENTIFIER)) {
return false;
}
if (string_view_eq_to_cstr(token.value, "u32")) {
*type = TYPE_U32;
return true;
}
return false;
}
static ast_node_t *
parser_parse_block(parser_t *parser)
{
token_t number_token;
if (!skip_expected_token(parser, TOKEN_OCURLY)) {
return false;
}
skip_line_feeds(parser->lexer);
ast_node_t *node_block = ast_new_node_block(parser->arena);
if (!skip_expected_token(parser, TOKEN_RETURN)) {
return false;
}
ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena);
assert(node_return_stmt);
if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
return false;
}
ast_node_t *literal_node = ast_new_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
assert(literal_node);
node_return_stmt->data.as_return_stmt.data = literal_node;
list_append(node_block->data.as_block.nodes, node_return_stmt);
if (!skip_expected_token(parser, TOKEN_LF)) {
return false;
}
skip_line_feeds(parser->lexer);
if (!skip_expected_token(parser, TOKEN_CCURLY)) {
return false;
}
return node_block;
}
static bool
skip_expected_token(parser_t *parser, token_kind_t expected_kind)
{
token_t token;
return expected_token(parser, &token, expected_kind);
}
static bool
expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
{
lexer_next_token(parser->lexer, token);
if (token->kind != expected_kind) {
fprintf(stderr,
"%s:%lu:%lu: error: got '" SV_FMT "' token but expect <%s>\n",
parser->file_path,
token->location.row + 1,
(token->location.offset - token->location.bol) + 1,
SV_ARG(token->value),
token_kind_to_cstr(expected_kind));
string_view_t line = lexer_get_token_line(parser->lexer, token);
fprintf(stderr, "" SV_FMT "\n", SV_ARG(line));
fprintf(stderr, "%*s\n", (int)(token->location.offset - token->location.bol + 1), "^");
exit(EXIT_FAILURE);
}
return true;
}
static void
skip_line_feeds(lexer_t *lexer)
{
token_t token;
lexer_peek_next(lexer, &token);
while (token.kind == TOKEN_LF) {
lexer_next_token(lexer, &token);
lexer_peek_next(lexer, &token);
}
}