From 7dbb3f0125b03afdaa19a87eb09c143715e178a0 Mon Sep 17 00:00:00 2001 From: serr Date: Sun, 30 Mar 2025 19:38:08 +0300 Subject: [PATCH] c-like lang analizer start coding --- .gitignore | 3 +- analyzers/cpl/cpl.l | 51 +++++++++++++++++++++++ analyzers/cpl/cpl.y | 99 +++++++++++++++++++++++++++++++++++++++++++++ main.py | 47 +++++++++++---------- 4 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 analyzers/cpl/cpl.l create mode 100644 analyzers/cpl/cpl.y diff --git a/.gitignore b/.gitignore index adb36c8..2a57182 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*.exe \ No newline at end of file +*.exe +*test* \ No newline at end of file diff --git a/analyzers/cpl/cpl.l b/analyzers/cpl/cpl.l new file mode 100644 index 0000000..115679c --- /dev/null +++ b/analyzers/cpl/cpl.l @@ -0,0 +1,51 @@ +%{ +#include "cpl.tab.h" +#include +#include +#include + +extern int yylineno; +void yyerror(const char *s); +%} + +%option noyywrap + +DIGIT [0-9] +ID [a-zA-Z][a-zA-Z0-9_]* +WS [ \t]+ + +%% + +"if" { return IF; } +"else" { return ELSE; } +"while" { return WHILE; } +"return" { return RETURN; } +"print" { return PRINT; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +">=" { return GE; } +"<=" { return LE; } +"==" { return EQ; } +"!=" { return NE; } +">" { return '>'; } +"<" { return '<'; } +"+" { return '+'; } +"-" { return '-'; } +"*" { return '*'; } +"/" { return '/'; } +"%" { return '%'; } +"(" { return '('; } +")" { return ')'; } +";" { return ';'; } +"=" { return '='; } +"{" { return '{'; } +"}" { return '}'; } + +{DIGIT}+ { yylval.num = atoi(yytext); return NUMBER; } +{ID} { yylval.str = strdup(yytext); return IDENTIFIER; } +{WS} /* skip whitespace */ +\n { yylineno++; } +. { yyerror("Invalid character"); } + +%% \ No newline at end of file diff --git a/analyzers/cpl/cpl.y b/analyzers/cpl/cpl.y new file mode 100644 index 0000000..6b3f1ca --- /dev/null +++ b/analyzers/cpl/cpl.y @@ -0,0 +1,99 @@ +%{ +#include +#include +#include + +extern int yylineno; +void yyerror(const char *s); +int yylex(); + +extern FILE *yyin; +%} + +%union { + int num; + char *str; +} + +%token NUMBER +%token IDENTIFIER +%token IF ELSE WHILE RETURN PRINT +%token AND OR NOT GE LE EQ NE + +%nonassoc '>' '<' GE LE EQ NE +%left OR +%left AND +%left '=' '+' '-' +%left '*' '/' '%' +%right NOT UMINUS + +%% + +program: + | program statement + ; + +statement: + expr ';' semicolons + | IF '(' expr ')' statement ELSE statement + | IF '(' expr ')' statement + | WHILE '(' expr ')' statement + | RETURN expr ';' semicolons + | PRINT expr ';' semicolons + | '{' program '}' + ; + +semicolons: + /* empty */ + | semicolons ';' + ; + +expr: + NUMBER + | IDENTIFIER + | IDENTIFIER '=' expr + | expr '+' expr + | expr '-' expr + | expr '*' expr + | expr '/' expr + | expr '%' expr + | expr AND expr + | expr OR expr + | NOT expr + | '-' expr %prec UMINUS + | '+' expr + | expr '>' expr + | expr '<' expr + | expr GE expr + | expr LE expr + | expr EQ expr + | expr NE expr + | '(' expr ')' + ; + +%% + +void yyerror(const char *s) { + fprintf(stderr, "Error at line %d: %s\n", yylineno, s); + exit(1); +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + FILE *input = fopen(argv[1], "r"); + if (!input) { + perror("Error opening file"); + return 1; + } + + yyin = input; + yyparse(); + fclose(input); + + printf("Parsing completed successfully!\n"); + return 0; +} \ No newline at end of file diff --git a/main.py b/main.py index 47b9b8f..92ceb6a 100644 --- a/main.py +++ b/main.py @@ -2,42 +2,47 @@ import os import subprocess # ЭТИ ПУТИ НАДО ЗАДАТЬ ВРУЧНУЮ -# АНАЛИЗАТОРЫ .l, .y ДОЛЖНЫ ИМЕТЬ ОДИНАКОВЫЙ basename -# И ЛЕЖАТЬ В ПАПАКЕ С ИМЕНЕМ basename -ANALYZERS_DIR = r'C:\Users\user\Desktop\УЧЕБА\6_СЕМ\КОМПИЛЯТОРЫ\flex_bison_test\analyzers\calc' +ANALYZERS_DIR = r'C:\Users\user\Desktop\УЧЕБА\6_СЕМ\КОМПИЛЯТОРЫ\flex_bison_test\analyzers\cpl' FLEX_EXE_PATH = r"C:\tools\win_flex_bison\win_flex.exe" BISON_EXE_PATH = r"C:\tools\win_flex_bison\win_bison.exe" -# def main(): - # Подготовка путей analyzer_name = os.path.basename(ANALYZERS_DIR) lexical_analyzer_path = fr"{ANALYZERS_DIR}\{analyzer_name}.l" syntaxic_analyzer_path = fr"{ANALYZERS_DIR}\{analyzer_name}.y" - # # Подготовка списка команд - cmds = [f'{FLEX_EXE_PATH} {lexical_analyzer_path}', - f'{BISON_EXE_PATH} -d {syntaxic_analyzer_path}', - f'gcc lex.yy.c {analyzer_name}.tab.c -o {analyzer_name}.exe'] - # + cmds = [ + f'{FLEX_EXE_PATH} {lexical_analyzer_path}', + f'{BISON_EXE_PATH} -d {syntaxic_analyzer_path}', + f'gcc lex.yy.c {analyzer_name}.tab.c -o {analyzer_name}.exe' + ] - # Исполнение команд + # Исполнение команд с выводом for cmd in cmds: + print(f"\n\033[1mExecuting: {cmd}\033[0m") try: - subprocess.run(cmd, capture_output=True, check=True) - print(f'\033[92mSuccess execute \033[0m{cmd}!') + result = subprocess.run( + cmd, + shell=True, + check=True, + text=True, + stderr=subprocess.PIPE + ) + print(f'\033[92mSuccessfully executed!\033[0m') except subprocess.CalledProcessError as e: - print(f"\033[91mError: {e}\033[0m") - # + print("\033[91mErrors:\033[0m") + print(e.stderr) + return - # Очистка промежуточных файлов - for path in ['lex.yy.c', - f'{analyzer_name}.tab.c', - f'{analyzer_name}.tab.h']: - os.remove(path) - # + # Очистка промежуточных файлов (только если все команды успешны) + for path in ['lex.yy.c', f'{analyzer_name}.tab.c', f'{analyzer_name}.tab.h']: + try: + os.remove(path) + print(f"Removed: {path}") + except FileNotFoundError: + pass if __name__ == "__main__": main() \ No newline at end of file