Add syntax parser and integrate lexical analysis
This commit is contained in:
1 parent
0c47cb3792
commit
35dd18308e
18 files changed
+766
-29
No files matched your search
@@ -1 +1,3 @@
|
||||
.priv/
|
||||
build/
|
||||
programa.bl
|
||||
+2
-1
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 @@
|
||||
/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,4 +1,5 @@
|
||||
CMAKE_PROGRESS_1 = 1
|
||||
CMAKE_PROGRESS_2 = 2
|
||||
CMAKE_PROGRESS_3 = 3
|
||||
CMAKE_PROGRESS_4 = 4
|
||||
|
||||
Binary file not shown.
@@ -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 \
|
||||
|
||||
@@ -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 @@
|
||||
3
|
||||
4
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user