Add syntax parser and integrate lexical analysis

This commit is contained in:
Clinv committed 2026-10-09 12:12:16 -03:00
1 parent 0c47cb3792
commit 35dd18308e
18 files changed
+766 -29

No files matched your search

+2
View File
@@ -1 +1,3 @@
.priv/
build/
programa.bl
+2 -1
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.20)
project(BLCeti VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 17)
@@ -9,6 +9,7 @@ set(CMAKE_C_EXTENSIONS OFF)
add_executable(BL
src/main.c
src/lexer.c
src/parser.c
)
target_include_directories(BL PRIVATE include)
BIN
View File
Binary file not shown.
+1
View File
@@ -10,6 +10,7 @@ set(CMAKE_DEPENDS_LANGUAGES
set(CMAKE_DEPENDS_DEPENDENCY_FILES
"/home/benicio/Escritorio/BL-Ceti/src/lexer.c" "CMakeFiles/BL.dir/src/lexer.c.o" "gcc" "CMakeFiles/BL.dir/src/lexer.c.o.d"
"/home/benicio/Escritorio/BL-Ceti/src/main.c" "CMakeFiles/BL.dir/src/main.c.o" "gcc" "CMakeFiles/BL.dir/src/main.c.o.d"
"/home/benicio/Escritorio/BL-Ceti/src/parser.c" "CMakeFiles/BL.dir/src/parser.c.o" "gcc" "CMakeFiles/BL.dir/src/parser.c.o.d"
"" "BL" "gcc" "CMakeFiles/BL.dir/link.d"
)
+18 -2
View File
@@ -100,20 +100,36 @@ CMakeFiles/BL.dir/src/lexer.c.s: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/BL.dir/src/lexer.c.s"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/benicio/Escritorio/BL-Ceti/src/lexer.c -o CMakeFiles/BL.dir/src/lexer.c.s
CMakeFiles/BL.dir/src/parser.c.o: CMakeFiles/BL.dir/flags.make
CMakeFiles/BL.dir/src/parser.c.o: /home/benicio/Escritorio/BL-Ceti/src/parser.c
CMakeFiles/BL.dir/src/parser.c.o: CMakeFiles/BL.dir/compiler_depend.ts
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building C object CMakeFiles/BL.dir/src/parser.c.o"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -MD -MT CMakeFiles/BL.dir/src/parser.c.o -MF CMakeFiles/BL.dir/src/parser.c.o.d -o CMakeFiles/BL.dir/src/parser.c.o -c /home/benicio/Escritorio/BL-Ceti/src/parser.c
CMakeFiles/BL.dir/src/parser.c.i: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing C source to CMakeFiles/BL.dir/src/parser.c.i"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -E /home/benicio/Escritorio/BL-Ceti/src/parser.c > CMakeFiles/BL.dir/src/parser.c.i
CMakeFiles/BL.dir/src/parser.c.s: cmake_force
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling C source to assembly CMakeFiles/BL.dir/src/parser.c.s"
/usr/bin/cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -S /home/benicio/Escritorio/BL-Ceti/src/parser.c -o CMakeFiles/BL.dir/src/parser.c.s
# Object files for target BL
BL_OBJECTS = \
"CMakeFiles/BL.dir/src/main.c.o" \
"CMakeFiles/BL.dir/src/lexer.c.o"
"CMakeFiles/BL.dir/src/lexer.c.o" \
"CMakeFiles/BL.dir/src/parser.c.o"
# External object files for target BL
BL_EXTERNAL_OBJECTS =
BL: CMakeFiles/BL.dir/src/main.c.o
BL: CMakeFiles/BL.dir/src/lexer.c.o
BL: CMakeFiles/BL.dir/src/parser.c.o
BL: CMakeFiles/BL.dir/build.make
BL: CMakeFiles/BL.dir/compiler_depend.ts
BL: CMakeFiles/BL.dir/link.txt
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Linking C executable BL"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Linking C executable BL"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/BL.dir/link.txt --verbose=$(VERBOSE)
# Rule to build all files generated by this target.
@@ -6,6 +6,8 @@ file(REMOVE_RECURSE
"CMakeFiles/BL.dir/src/lexer.c.o.d"
"CMakeFiles/BL.dir/src/main.c.o"
"CMakeFiles/BL.dir/src/main.c.o.d"
"CMakeFiles/BL.dir/src/parser.c.o"
"CMakeFiles/BL.dir/src/parser.c.o.d"
)
# Per-language clean rules from dependency scanning.
+3
View File
@@ -4,6 +4,7 @@ BL: \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/crtbeginS.o \
CMakeFiles/BL.dir/src/main.c.o \
CMakeFiles/BL.dir/src/lexer.c.o \
CMakeFiles/BL.dir/src/parser.c.o \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc.a \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc_s_asneeded.so \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc_s_asneeded.so \
@@ -46,6 +47,8 @@ CMakeFiles/BL.dir/src/main.c.o:
CMakeFiles/BL.dir/src/lexer.c.o:
CMakeFiles/BL.dir/src/parser.c.o:
/usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc.a:
/usr/lib/gcc/x86_64-pc-linux-gnu/16/libgcc_s_asneeded.so:
+1 -1
View File
@@ -1 +1 @@
/usr/bin/cc -Wl,--dependency-file=CMakeFiles/BL.dir/link.d CMakeFiles/BL.dir/src/main.c.o CMakeFiles/BL.dir/src/lexer.c.o -o BL
/usr/bin/cc -Wl,--dependency-file=CMakeFiles/BL.dir/link.d CMakeFiles/BL.dir/src/main.c.o CMakeFiles/BL.dir/src/lexer.c.o CMakeFiles/BL.dir/src/parser.c.o -o BL
+1
View File
@@ -1,4 +1,5 @@
CMAKE_PROGRESS_1 = 1
CMAKE_PROGRESS_2 = 2
CMAKE_PROGRESS_3 = 3
CMAKE_PROGRESS_4 = 4
Binary file not shown.
+7 -5
View File
@@ -3,11 +3,13 @@ CMakeFiles/BL.dir/src/main.c.o: \
/home/benicio/Escritorio/BL-Ceti/include/lexer.h \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/include/stdbool.h \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/include/stddef.h \
/usr/include/stdio.h /usr/include/bits/libc-header-start.h \
/usr/include/features.h /usr/include/features-time64.h \
/usr/include/bits/wordsize.h /usr/include/bits/timesize.h \
/usr/include/sys/cdefs.h /usr/include/bits/long-double.h \
/usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \
/home/benicio/Escritorio/BL-Ceti/include/parser.h \
/home/benicio/Escritorio/BL-Ceti/include/lexer.h /usr/include/stdio.h \
/usr/include/bits/libc-header-start.h /usr/include/features.h \
/usr/include/features-time64.h /usr/include/bits/wordsize.h \
/usr/include/bits/timesize.h /usr/include/sys/cdefs.h \
/usr/include/bits/long-double.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-pc-linux-gnu/16/include/stdarg.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/bits/time64.h /usr/include/bits/types/__fpos_t.h \
+3 -3
View File
@@ -85,12 +85,12 @@ clean: CMakeFiles/BL.dir/clean
CMakeFiles/BL.dir/all:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/depend
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/build
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=1,2,3 "Built target BL"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=1,2,3,4 "Built target BL"
.PHONY : CMakeFiles/BL.dir/all
# Build rule for subdir invocation for target.
CMakeFiles/BL.dir/rule: cmake_check_build_system
$(CMAKE_COMMAND) -E cmake_progress_start /home/benicio/Escritorio/BL-Ceti/build/CMakeFiles 3
$(CMAKE_COMMAND) -E cmake_progress_start /home/benicio/Escritorio/BL-Ceti/build/CMakeFiles 4
$(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 CMakeFiles/BL.dir/all
$(CMAKE_COMMAND) -E cmake_progress_start /home/benicio/Escritorio/BL-Ceti/build/CMakeFiles 0
.PHONY : CMakeFiles/BL.dir/rule
@@ -102,7 +102,7 @@ BL: CMakeFiles/BL.dir/rule
# codegen rule for target.
CMakeFiles/BL.dir/codegen:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/codegen
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=1,2,3 "Finished codegen for target BL"
@$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --progress-dir=/home/benicio/Escritorio/BL-Ceti/build/CMakeFiles --progress-num=1,2,3,4 "Finished codegen for target BL"
.PHONY : CMakeFiles/BL.dir/codegen
# clean rule for target.
+1 -1
View File
@@ -1 +1 @@
3
4
+27
View File
@@ -222,6 +222,30 @@ src/main.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/src/main.c.s
.PHONY : src/main.c.s
src/parser.o: src/parser.c.o
.PHONY : src/parser.o
# target to build an object file
src/parser.c.o:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/src/parser.c.o
.PHONY : src/parser.c.o
src/parser.i: src/parser.c.i
.PHONY : src/parser.i
# target to preprocess a source file
src/parser.c.i:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/src/parser.c.i
.PHONY : src/parser.c.i
src/parser.s: src/parser.c.s
.PHONY : src/parser.s
# target to generate assembly for a file
src/parser.c.s:
$(MAKE) $(MAKESILENT) -f CMakeFiles/BL.dir/build.make CMakeFiles/BL.dir/src/parser.c.s
.PHONY : src/parser.c.s
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@@ -241,6 +265,9 @@ help:
@echo "... src/main.o"
@echo "... src/main.i"
@echo "... src/main.s"
@echo "... src/parser.o"
@echo "... src/parser.i"
@echo "... src/parser.s"
.PHONY : help
+18
View File
@@ -0,0 +1,18 @@
#ifndef BL_CETI_PARSER_H
#define BL_CETI_PARSER_H
#include "lexer.h"
#include <stdbool.h>
#include <stddef.h>
typedef struct {
char code[6];
char message[160];
size_t line;
size_t column;
} BlParseError;
bool bl_parse(const BlToken *tokens, size_t count, BlParseError *error);
#endif
+5 -2
View File
@@ -1,4 +1,7 @@
cat > /tmp/bl-error.bl <<'EOF'
fn main() {
let mensaje: String = "Hola desde BL-Ceti";
print(mensaje);
let numero: Int = ;
}
EOF
./build/BL .C /tmp/bl-error.bl
+36 -14
View File
@@ -1,5 +1,5 @@
#include "lexer.h"
#include "parser.h"
#include <stdio.h>
#include <stdlib.h>
@@ -16,6 +16,7 @@ static char *read_file(const char *path, size_t *length)
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
fprintf(stderr, "BL: no se pudo leer el archivo.\n");
return NULL;
}
@@ -23,6 +24,7 @@ static char *read_file(const char *path, size_t *length)
if (size < 0 || fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
fprintf(stderr, "BL: no se pudo determinar el tamaño del archivo.\n");
return NULL;
}
@@ -46,6 +48,7 @@ static char *read_file(const char *path, size_t *length)
buffer[read_count] = '\0';
*length = read_count;
fclose(file);
return buffer;
}
@@ -54,29 +57,48 @@ static int analyze_file(const char *path)
size_t length = 0;
char *source = read_file(path, &length);
if (source == NULL) return 1;
if (source == NULL) {
return 1;
}
BlLexResult result = {0};
bool success = bl_lex(source, length, &result);
BlLexResult lex_result = {0};
if (!success) {
if (!bl_lex(source, length, &lex_result)) {
fprintf(stderr, "%s:%zu:%zu: error %s: %s\n",
path,
result.error.line,
result.error.column,
result.error.code,
result.error.message);
bl_lex_result_destroy(&result);
lex_result.error.line,
lex_result.error.column,
lex_result.error.code,
lex_result.error.message);
bl_lex_result_destroy(&lex_result);
free(source);
return 1;
}
printf("BL-Ceti: analisis lexico correcto: %s\n", path);
printf("Tokens reconocidos: %zu\n",
result.count > 0 ? result.count - 1 : 0);
BlParseError parse_error = {0};
bl_lex_result_destroy(&result);
if (!bl_parse(lex_result.tokens, lex_result.count, &parse_error)) {
fprintf(stderr, "%s:%zu:%zu: error %s: %s\n",
path,
parse_error.line,
parse_error.column,
parse_error.code,
parse_error.message);
bl_lex_result_destroy(&lex_result);
free(source);
return 1;
}
printf("BL-Ceti 0.1.0: analisis correcto: %s\n", path);
printf("Tokens reconocidos: %zu\n",
lex_result.count > 0 ? lex_result.count - 1 : 0);
printf("Sintaxis: correcta\n");
bl_lex_result_destroy(&lex_result);
free(source);
return 0;
}
+639
View File
@@ -0,0 +1,639 @@
#include "parser.h"
#include <stdio.h>
#include <string.h>
typedef struct {
const BlToken *tokens;
size_t count;
size_t position;
BlParseError *error;
bool failed;
} Parser;
typedef struct {
bool identifier;
bool call;
} ExprInfo;
static const BlToken *current(Parser *parser)
{
if (parser->position >= parser->count) {
return &parser->tokens[parser->count - 1];
}
return &parser->tokens[parser->position];
}
static bool check(Parser *parser, BlTokenType type)
{
return current(parser)->type == type;
}
static const BlToken *advance(Parser *parser)
{
const BlToken *token = current(parser);
if (token->type != BL_EOF) {
parser->position++;
}
return token;
}
static bool fail_at(Parser *parser, const BlToken *token,
const char *code, const char *message)
{
if (!parser->failed) {
parser->failed = true;
snprintf(parser->error->code, sizeof parser->error->code,
"%s", code);
snprintf(parser->error->message, sizeof parser->error->message,
"%s", message);
parser->error->line = token->line;
parser->error->column = token->column;
}
return false;
}
static bool expect(Parser *parser, BlTokenType type,
const char *message)
{
if (check(parser, type)) {
advance(parser);
return true;
}
return fail_at(parser, current(parser), "E1001", message);
}
static bool parse_expression(Parser *parser, ExprInfo *info);
static bool parse_statement(Parser *parser);
static bool parse_block(Parser *parser);
static bool parse_type(Parser *parser, bool allow_void)
{
BlTokenType type = current(parser)->type;
if (type == BL_INT_TYPE ||
type == BL_FLOAT_TYPE ||
type == BL_BOOL_TYPE ||
type == BL_STRING_TYPE) {
advance(parser);
return true;
}
if (allow_void && type == BL_VOID_TYPE) {
advance(parser);
return true;
}
return fail_at(parser, current(parser), "E1001",
"Se esperaba un tipo valido.");
}
static bool parse_primary(Parser *parser, ExprInfo *info)
{
const BlToken *token = current(parser);
info->identifier = false;
info->call = false;
switch (token->type) {
case BL_INTEGER:
case BL_FLOAT:
case BL_STRING:
case BL_TRUE:
case BL_FALSE:
advance(parser);
return true;
case BL_IDENTIFIER:
advance(parser);
if (check(parser, BL_LPAREN)) {
advance(parser);
if (!check(parser, BL_RPAREN)) {
ExprInfo argument;
if (!parse_expression(parser, &argument)) {
return false;
}
while (check(parser, BL_COMMA)) {
advance(parser);
if (check(parser, BL_RPAREN)) {
break;
}
if (!parse_expression(parser, &argument)) {
return false;
}
}
}
if (!expect(parser, BL_RPAREN,
"Se esperaba ')' al cerrar la llamada.")) {
return false;
}
info->call = true;
return true;
}
info->identifier = true;
return true;
case BL_LPAREN: {
advance(parser);
ExprInfo inner;
if (!parse_expression(parser, &inner)) {
return false;
}
if (!expect(parser, BL_RPAREN,
"Se esperaba ')' en la expresion.")) {
return false;
}
return true;
}
default:
return fail_at(parser, token, "E1001",
"Se esperaba una expresion.");
}
}
static bool parse_unary(Parser *parser, ExprInfo *info)
{
if (check(parser, BL_MINUS) || check(parser, BL_NOT)) {
advance(parser);
if (!parse_unary(parser, info)) {
return false;
}
info->identifier = false;
info->call = false;
return true;
}
return parse_primary(parser, info);
}
static bool parse_cast(Parser *parser, ExprInfo *info)
{
if (!parse_unary(parser, info)) {
return false;
}
while (check(parser, BL_AS)) {
advance(parser);
if (!parse_type(parser, false)) {
return false;
}
info->identifier = false;
info->call = false;
}
return true;
}
static bool parse_multiplication(Parser *parser, ExprInfo *info)
{
if (!parse_cast(parser, info)) {
return false;
}
while (check(parser, BL_STAR) ||
check(parser, BL_SLASH) ||
check(parser, BL_PERCENT)) {
advance(parser);
ExprInfo right;
if (!parse_cast(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
}
return true;
}
static bool parse_addition(Parser *parser, ExprInfo *info)
{
if (!parse_multiplication(parser, info)) {
return false;
}
while (check(parser, BL_PLUS) || check(parser, BL_MINUS)) {
advance(parser);
ExprInfo right;
if (!parse_multiplication(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
}
return true;
}
static bool is_relational(BlTokenType type)
{
return type == BL_LESS ||
type == BL_LESS_EQUAL ||
type == BL_GREATER ||
type == BL_GREATER_EQUAL;
}
static bool parse_relation(Parser *parser, ExprInfo *info)
{
if (!parse_addition(parser, info)) {
return false;
}
if (is_relational(current(parser)->type)) {
advance(parser);
ExprInfo right;
if (!parse_addition(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
if (is_relational(current(parser)->type)) {
return fail_at(parser, current(parser), "E1005",
"Las comparaciones relacionales no se pueden encadenar.");
}
}
return true;
}
static bool parse_equality(Parser *parser, ExprInfo *info)
{
if (!parse_relation(parser, info)) {
return false;
}
if (check(parser, BL_EQUAL) || check(parser, BL_NOT_EQUAL)) {
advance(parser);
ExprInfo right;
if (!parse_relation(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
if (check(parser, BL_EQUAL) || check(parser, BL_NOT_EQUAL)) {
return fail_at(parser, current(parser), "E1005",
"Las comparaciones de igualdad no se pueden encadenar.");
}
}
return true;
}
static bool parse_and(Parser *parser, ExprInfo *info)
{
if (!parse_equality(parser, info)) {
return false;
}
while (check(parser, BL_AND)) {
advance(parser);
ExprInfo right;
if (!parse_equality(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
}
return true;
}
static bool parse_expression(Parser *parser, ExprInfo *info)
{
if (!parse_and(parser, info)) {
return false;
}
while (check(parser, BL_OR)) {
advance(parser);
ExprInfo right;
if (!parse_and(parser, &right)) {
return false;
}
info->identifier = false;
info->call = false;
}
return true;
}
static bool is_assignment_operator(BlTokenType type)
{
return type == BL_ASSIGN ||
type == BL_PLUS_ASSIGN ||
type == BL_MINUS_ASSIGN ||
type == BL_STAR_ASSIGN ||
type == BL_SLASH_ASSIGN ||
type == BL_PERCENT_ASSIGN;
}
static bool parse_variable(Parser *parser)
{
advance(parser); /* let o var */
if (!expect(parser, BL_IDENTIFIER,
"Se esperaba un nombre de variable.")) {
return false;
}
if (check(parser, BL_COLON)) {
advance(parser);
if (!parse_type(parser, false)) {
return false;
}
}
if (!expect(parser, BL_ASSIGN,
"Se esperaba '=' para inicializar la variable.")) {
return false;
}
ExprInfo value;
if (!parse_expression(parser, &value)) {
return false;
}
return expect(parser, BL_SEMICOLON,
"Se esperaba ';' al final de la declaracion.");
}
static bool parse_if(Parser *parser)
{
advance(parser); /* if */
ExprInfo condition;
if (!parse_expression(parser, &condition)) {
return false;
}
if (!parse_block(parser)) {
return false;
}
if (check(parser, BL_ELSE)) {
advance(parser);
if (check(parser, BL_IF)) {
return parse_if(parser);
}
return parse_block(parser);
}
return true;
}
static bool parse_while(Parser *parser)
{
advance(parser); /* while */
ExprInfo condition;
if (!parse_expression(parser, &condition)) {
return false;
}
return parse_block(parser);
}
static bool parse_return(Parser *parser)
{
advance(parser); /* return */
if (!check(parser, BL_SEMICOLON)) {
ExprInfo value;
if (!parse_expression(parser, &value)) {
return false;
}
}
return expect(parser, BL_SEMICOLON,
"Se esperaba ';' despues de return.");
}
static bool parse_simple_statement(Parser *parser)
{
ExprInfo expression;
if (!parse_expression(parser, &expression)) {
return false;
}
if (is_assignment_operator(current(parser)->type)) {
if (!expression.identifier) {
return fail_at(parser, current(parser), "E1006",
"El lado izquierdo de una asignacion debe ser una variable.");
}
advance(parser);
ExprInfo value;
if (!parse_expression(parser, &value)) {
return false;
}
} else if (!expression.call) {
return fail_at(parser, current(parser), "E1007",
"Una sentencia de expresion debe ser una llamada a funcion.");
}
return expect(parser, BL_SEMICOLON,
"Se esperaba ';' al final de la sentencia.");
}
static bool parse_statement(Parser *parser)
{
switch (current(parser)->type) {
case BL_LET:
case BL_VAR:
return parse_variable(parser);
case BL_IF:
return parse_if(parser);
case BL_WHILE:
return parse_while(parser);
case BL_BREAK:
case BL_CONTINUE:
advance(parser);
return expect(parser, BL_SEMICOLON,
"Se esperaba ';' despues de la sentencia.");
case BL_RETURN:
return parse_return(parser);
case BL_LBRACE:
return parse_block(parser);
case BL_IDENTIFIER:
case BL_INTEGER:
case BL_FLOAT:
case BL_STRING:
case BL_TRUE:
case BL_FALSE:
case BL_LPAREN:
case BL_MINUS:
case BL_NOT:
return parse_simple_statement(parser);
default:
return fail_at(parser, current(parser), "E1001",
"Sentencia no valida.");
}
}
static bool parse_block(Parser *parser)
{
if (!expect(parser, BL_LBRACE,
"Se esperaba '{' para comenzar el bloque.")) {
return false;
}
while (!check(parser, BL_RBRACE) &&
!check(parser, BL_EOF) &&
!parser->failed) {
if (!parse_statement(parser)) {
return false;
}
}
return expect(parser, BL_RBRACE,
"Se esperaba '}' para cerrar el bloque.");
}
static bool parse_function(Parser *parser)
{
advance(parser); /* fn */
if (!expect(parser, BL_IDENTIFIER,
"Se esperaba el nombre de la funcion.")) {
return false;
}
if (!expect(parser, BL_LPAREN,
"Se esperaba '(' despues del nombre de la funcion.")) {
return false;
}
if (!check(parser, BL_RPAREN)) {
for (;;) {
if (!expect(parser, BL_IDENTIFIER,
"Se esperaba el nombre del parametro.")) {
return false;
}
if (!expect(parser, BL_COLON,
"Se esperaba ':' despues del nombre del parametro.")) {
return false;
}
if (!parse_type(parser, false)) {
return false;
}
if (!check(parser, BL_COMMA)) {
break;
}
advance(parser);
if (check(parser, BL_RPAREN)) {
break;
}
}
}
if (!expect(parser, BL_RPAREN,
"Se esperaba ')' despues de los parametros.")) {
return false;
}
if (check(parser, BL_ARROW)) {
advance(parser);
if (!parse_type(parser, true)) {
return false;
}
}
return parse_block(parser);
}
bool bl_parse(const BlToken *tokens, size_t count, BlParseError *error)
{
if (tokens == NULL || count == 0 || error == NULL) {
return false;
}
memset(error, 0, sizeof *error);
Parser parser = {
.tokens = tokens,
.count = count,
.position = 0,
.error = error,
.failed = false
};
while (!check(&parser, BL_EOF) && !parser.failed) {
if (!check(&parser, BL_FN)) {
return fail_at(&parser, current(&parser), "E1001",
"En el nivel superior solo se permiten funciones.");
}
if (!parse_function(&parser)) {
return false;
}
}
return !parser.failed && check(&parser, BL_EOF);
}