From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp0.migadu.com ([2001:41d0:303:5f26::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms5.migadu.com with LMTPS id eImhB44M8mX3rAAAbAwnHQ (envelope-from ) for ; Wed, 13 Mar 2024 21:29:02 +0100 Received: from aspmx1.migadu.com ([2001:41d0:303:e224::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp0.migadu.com with LMTPS id IE0PAY4M8mXSdgAAqHPOHw (envelope-from ) for ; Wed, 13 Mar 2024 21:29:02 +0100 X-Envelope-To: patches@johnnyrichard.com Authentication-Results: aspmx1.migadu.com; none Received: from mail-a.sr.ht (mail-a.sr.ht [46.23.81.152]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id E5DB930F40 for ; Wed, 13 Mar 2024 21:29:01 +0100 (CET) DKIM-Signature: a=rsa-sha256; bh=dPPj+roAoVJ4+55U8S0dOZ5fLPvFaiEyKnuNlLrAhIU=; c=simple/simple; d=lists.sr.ht; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Unsubscribe:List-Subscribe:List-Archive:List-Post:List-ID; q=dns/txt; s=20240113; t=1710361740; v=1; b=hX9Ldv/UtUQCMnPpR65JFJ20KWzKqgfH5bv1t0C+L8PLaDDzFv5GpmkWQxbr1s5KS7bErMc2 LrxAuhzFTaIF25KN6d6JWVrODn4tcVQXo9q/vq1MAd8IsFVEL53PG4t0Wx75L2uXlJYlNTbKNUz H2fN5xiw1pODHzwMIIOnMSBMewNwGrkfOV3XeLuM7AoQWpWFL9z5yliet58knIAd/FRPicvG9DG j9WFJi+l5LWc7GLh7s9bG/RCD03p40g51xT9T8ZA9MQ4E7jQPT4uoiLrgfSt50L9U4e2R8TxybU TO1h7nYpaeZspLz2x03xyz9s+tG2YEVoAimVBX/2Wlz/w== Received: from lists.sr.ht (unknown [46.23.81.154]) by mail-a.sr.ht (Postfix) with ESMTPSA id D7A4A2028F for ; Wed, 13 Mar 2024 20:29:00 +0000 (UTC) Received: from out-186.mta0.migadu.com (out-186.mta0.migadu.com [IPv6:2001:41d0:1004:224b::ba]) by mail-a.sr.ht (Postfix) with ESMTPS id 3A00120287 for <~johnnyrichard/olang-devel@lists.sr.ht>; Wed, 13 Mar 2024 20:29:00 +0000 (UTC) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=johnnyrichard.com; s=key1; t=1710361740; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=CxxJ75om54DPRDm9r8hxORh3/nVSFraDQTteii9t3zM=; b=n5D84Cj9iluUX7iUezWNGZ0yezzW2wrH5BYJ17W7x4bDzRS7aWzA2TDgsIk1ion7LU3wbl j1kgTF8yGc8meNn2wLpcT/6s1Rr6KT7bx6zf0gmvXiY9KEbhahB9J6Ux3DmesHUCNn6yZf ooYBDToWcd6gOYzR2L92DocAzfA7p/IDICSnETnvxwYihb9LCYWKuXG4ikDc1r+wkmY2k4 YrANe+l4OgbSWZPYRyKJmp0kZ/l/XPZjzNpcRoFvWHEPBjoOorZNu5+CGS+mR2NhzTXmwj IavmqdphDROoi8DkNJL6eQxyGtZLn5nfIiGLMu8f4K3Xr/jzX69dc1uORSrk+g== From: Johnny Richard To: ~johnnyrichard/olang-devel@lists.sr.ht Cc: Johnny Richard Subject: [PATCH olang v1 3/3] parser: add basic arithmetic expressions '+' '*' '/' '-' Date: Wed, 13 Mar 2024 22:21:52 +0100 Message-ID: <20240313212855.174554-4-johnny@johnnyrichard.com> In-Reply-To: <20240313212855.174554-1-johnny@johnnyrichard.com> References: <20240313212855.174554-1-johnny@johnnyrichard.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Sourcehut-Patchset-Status: PROPOSED List-Unsubscribe: List-Subscribe: List-Archive: Archived-At: List-Post: List-ID: ~johnnyrichard/olang-devel <~johnnyrichard/olang-devel.lists.sr.ht> Sender: ~johnnyrichard/olang-devel <~johnnyrichard/olang-devel@lists.sr.ht> X-Migadu-Country: NL X-Migadu-Flow: FLOW_IN X-Migadu-Scanner: mx12.migadu.com X-Migadu-Spam-Score: -4.00 X-Spam-Score: -4.00 X-Migadu-Queue-Id: E5DB930F40 X-TUID: bQHIPm9+iQyu This implementation uses a call chain to guarantee calling precedence. Whenever we try to add more expression types and it becomes complexer, we will design a solution based on priority. Now is pretty difficult to visualize the ast tree since we don't have a ast pretty printer. I have validated it by running **gdb**. Testing the ast with unit tests is quite annoying without a good "DSL". We can design such DSL in the near future. =^) Signed-off-by: Johnny Richard --- src/parser.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 13 deletions(-) diff --git a/src/parser.c b/src/parser.c index 5b7d39a..ab7051d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -39,6 +39,15 @@ parser_parse_block(parser_t *parser); ast_node_t * parser_parse_fn_definition(parser_t *parser); +static ast_node_t * +parser_parse_expr(parser_t *parser); + +static ast_node_t * +parser_parse_term(parser_t *parser); + +static ast_node_t * +parser_parse_factor(parser_t *parser); + static void skip_line_feeds(lexer_t *lexer); @@ -57,10 +66,117 @@ ast_node_t * parser_parse_program(parser_t *parser) { ast_node_t *fn = parser_parse_fn_definition(parser); + if (fn == NULL) { + return NULL; + } return ast_new_program(parser->arena, fn); } +static ast_binary_op_kind_t +token_kind_to_binary_op_kind(token_kind_t kind) +{ + switch (kind) { + case TOKEN_PLUS: + return AST_BINOP_ADDITION; + case TOKEN_DASH: + return AST_BINOP_SUBTRACTION; + case TOKEN_SLASH: + return AST_BINOP_DIVISION; + case TOKEN_STAR: + return AST_BINOP_MULTIPLICATION; + default: { + fprintf(stderr, "error: token kind (%s) not compatible with binary op kind\n", token_kind_to_cstr(kind)); + assert(false); + } + } +} + +static ast_node_t * +parser_parse_expr(parser_t *parser) +{ + ast_node_t *node = parser_parse_term(parser); + if (node == NULL) { + return NULL; + } + + token_t token; + lexer_peek_next(parser->lexer, &token); + + while (token.kind == TOKEN_PLUS || token.kind == TOKEN_DASH) { + lexer_next_token(parser->lexer, &token); + + ast_node_t *lhs = node; + ast_node_t *rhs = parser_parse_term(parser); + if (rhs == NULL) { + return NULL; + } + + node = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token.kind), lhs, rhs); + + lexer_peek_next(parser->lexer, &token); + } + + return node; +} + +static ast_node_t * +parser_parse_term(parser_t *parser) +{ + ast_node_t *node = parser_parse_factor(parser); + if (node == NULL) { + return NULL; + } + + token_t token; + lexer_peek_next(parser->lexer, &token); + + while (token.kind == TOKEN_STAR || token.kind == TOKEN_SLASH) { + lexer_next_token(parser->lexer, &token); + + ast_node_t *lhs = node; + ast_node_t *rhs = parser_parse_factor(parser); + if (rhs == NULL) { + return NULL; + } + + node = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token.kind), lhs, rhs); + + lexer_peek_next(parser->lexer, &token); + } + + return node; +} + +static ast_node_t * +parser_parse_factor(parser_t *parser) +{ + token_t token; + lexer_next_token(parser->lexer, &token); + + switch (token.kind) { + case TOKEN_NUMBER: + return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value)); + + case TOKEN_OPAREN: { + ast_node_t *expr = parser_parse_expr(parser); + if (expr == NULL) { + return NULL; + } + + if (!skip_expected_token(parser, TOKEN_CPAREN)) { + return NULL; + } + + return expr; + } + default: { + fprintf(stderr, "error: parse_factor: unsupported or invalid token (%s)\n", token_kind_to_cstr(token.kind)); + assert(false); + } + } +} + ast_node_t * parser_parse_fn_definition(parser_t *parser) { @@ -131,41 +247,39 @@ parser_parse_type(parser_t *parser, type_t *type) static ast_node_t * parser_parse_block(parser_t *parser) { - token_t number_token; if (!skip_expected_token(parser, TOKEN_OCURLY)) { - return false; + return NULL; } skip_line_feeds(parser->lexer); ast_node_t *node_block = ast_new_node_block(parser->arena); + if (node_block == NULL) { + return NULL; + } if (!skip_expected_token(parser, TOKEN_RETURN)) { - return false; + return NULL; } 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 *expr = parser_parse_expr(parser); + if (expr == NULL) { + return NULL; } - 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; + node_return_stmt->data.as_return_stmt.data = expr; list_append(node_block->data.as_block.nodes, node_return_stmt); - if (!skip_expected_token(parser, TOKEN_LF)) { - return false; + return NULL; } skip_line_feeds(parser->lexer); - if (!skip_expected_token(parser, TOKEN_CCURLY)) { - return false; + return NULL; } return node_block; -- 2.44.0