commit 7d545e715cb0bfeb7d535f5bdd1f0d385fd1fea2 Author: serr Date: Sun May 18 16:17:07 2025 +0300 comments diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adb36c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.exe \ No newline at end of file diff --git a/analyzers/test/test.l b/analyzers/test/test.l new file mode 100644 index 0000000..fd58bec --- /dev/null +++ b/analyzers/test/test.l @@ -0,0 +1,129 @@ +%{ +#include "test.tab.h" +#include +#include +#include + +extern int yylineno; + +void yyerror(const char *s) { + fprintf(stderr, "\033[1;91mError at line %i: %s\033[0m\n", yylineno, s); + exit(1); +} + +%} + +DIGIT [0-9] +LETTER [a-zA-Z_] +LETTER_OR_DIGIT [a-zA-Z0-9_] + +%% + +"string" { return STRING; } + +"uint" { return UINT; } +"uint8" { return UINT8; } +"uint16" { return UINT16; } +"uint32" { return UINT32; } +"uint64" { return UINT64; } +"int" { return INT; } +"int8" { return INT8; } +"int16" { return INT16; } +"int32" { return INT32; } +"int64" { return INT64; } +"complex64" { return COMPLEX64; } +"complex128" { return COMPLEX128; } +"byte" { return BYTE; } +"rune" { return RUNE; } +"float32" { return FLOAT32; } +"float64" { return FLOAT64; } +"uintptr" { return UINT_PTR; } +"true" { return BOOL_LITERAL; } +"false" { return BOOL_LITERAL; } +"if" { return IF; } +"else" { return ELSE; } +"<-" { return ARROW; } + +"==" { return EQ; } +"&&" { return AND; } +"||" { return OR; } +"!" { return NOT; } +"!=" { return NEQ; } +"<" { return LT; } +">" { return GT; } +"<=" { return LEQ; } +">=" { return GEQ; } + +"+=" { return PLUS_EQ; } +"-=" { return MINUS_EQ; } +"*=" { return MUL_EQ; } +"/=" { return DIV_EQ; } +"%=" { return MOD_EQ; } +"&=" { return AMPERSAND_EQ; } +"|=" { return PIPE_EQ; } +"^=" { return XOR_EQ; } +"<<=" { return LSHIFT_EQ; } +">>=" { return RSHIFT_EQ; } +"&^=" { return AND_NOT_EQ; } + +"++" { return INC; } +"--" { return DEC; } + +"for" { return FOR; } +"break" { return BREAK; } +"switch" { return SWITCH; } +"case" { return CASE; } +"chan" { return CHAN; } +"const" { return CONST; } +"continue" { return CONTINUE; } +"package" { return PACKAGE; } +"import" { return IMPORT; } +"var" { return VAR; } +"func" { return FUNC; } +"return" { return RETURN; } +":=" { return SHORT_DECLARATION; } +"=" { return ASSIGN; } +"+" { return PLUS; } +"-" { return MINUS; } +"*" { return MULT; } +"/" { return DIV; } +"%" { return MOD; } +"{" { return LBRACE; } +"}" { return RBRACE; } +"(" { return LPAREN; } +")" { return RPAREN; } +"," { return COMMA; } +";" { return SEMICOLON; } +"..." { return DOTS; } +":" { return COLON; } + +\"([^"\\]|\\.)*\" { // правило для строк с возможность экранирования через \спецсимвол + return STRING_LITERAL; +} + +{LETTER}{LETTER_OR_DIGIT}* { + yylval.str = strdup(yytext); + return IDENTIFIER; +} + +[0-9]+\.[0-9]+ { + return FLOAT_LITERAL; +} + +{DIGIT}+ { + return NUMBER; +} + +[ \t\r]+ ; // Пропускаем пробелы и табы +\n { yylineno++; } +. { + fprintf(stderr, "\033[91mUnexpected character at line %i: %c\033[0m\n", + yylineno, yytext[0]); + exit(1); +} + +%% + +int yywrap() { + return 1; +} \ No newline at end of file diff --git a/analyzers/test/test.y b/analyzers/test/test.y new file mode 100644 index 0000000..417cb3d --- /dev/null +++ b/analyzers/test/test.y @@ -0,0 +1,314 @@ +%{ +#include +#include +#include +#include + +extern int yylineno; +extern char *yytext; + +extern void yyerror(const char *s); +extern int yylex(); +extern FILE *yyin; + +void free_node(char *str) { + if (str) free(str); +} +%} + +%union { + char *str; + double num; +} + +%token SHORT_DECLARATION LBRACE RBRACE SEMICOLON ASSIGN LPAREN RPAREN COMMA COLON DOTS +%token VAR FUNC RETURN STRING_LITERAL FLOAT_LITERAL NUMBER PACKAGE IMPORT +%token INC DEC PLUS_EQ MINUS_EQ MUL_EQ DIV_EQ MOD_EQ +%token AMPERSAND_EQ PIPE_EQ XOR_EQ LSHIFT_EQ RSHIFT_EQ AND_NOT_EQ +%token FOR BREAK CONTINUE ARROW IF ELSE +%token CHAN CONST CASE SWITCH +%token PLUS MINUS MULT DIV MOD +%token STRING +%token UINT UINT8 UINT16 UINT32 UINT64 UINT_PTR +%token INT INT8 INT16 INT32 INT64 +%token RUNE BYTE BOOL_LITERAL +%token FLOAT32 FLOAT64 +%token COMPLEX64 COMPLEX128 +%token IDENTIFIER +%token AND OR NOT EQ NEQ LT GT LEQ GEQ + +%left PLUS_EQ MINUS_EQ MUL_EQ DIV_EQ MOD_EQ +%left AMPERSAND_EQ PIPE_EQ XOR_EQ +%left LSHIFT_EQ RSHIFT_EQ AND_NOT_EQ +%left PLUS MINUS +%left MULT DIV MOD +%left OR +%left AND +%right INC DEC +%right NOT +%nonassoc EQ NEQ LT GT LEQ GEQ +%left UMINUS + +%% + +// base +program: + package_declaration import_declaration + | program statement + ; + + +statement: + | var_declaration SEMICOLON + { printf("\033[1;33mSTATEMENT: variable declaration\033[0m\n"); } + | func_declaration + { printf("\033[1;33mSTATEMENT: function declaration\033[0m\n"); } + | cicle + { printf("\033[1;33mSTATEMENT: cicle\033[0m\n"); } + | condition + { printf("\033[1;33mSTATEMENT: condition\033[0m\n"); } + | IDENTIFIER COLON + { printf("\033[1;33mSTATEMENT: label definition '%s'\033[0m\n", $1); } + ; + +statements_list: + | statements_list statement + | statements_list block + | statements_list expr SEMICOLON + ; + +block: + LBRACE statements_list RBRACE + ; +// + +// condition +condition: + IF log_expr block else_part + +else_part: + { printf("\033[1;33mSTATEMENT: if condition with block\033[0m\n"); } + | ELSE IF log_expr block else_part { printf("\033[1;33mSTATEMENT: if condition with block else if block\033[0m\n"); } + | ELSE block { printf("\033[1;33mSTATEMENT: if condition with block else block\033[0m\n"); } + ; +// + +// cicle +cicle: + FOR loop_block + { printf("\033[1;34mLOOP: infinite for loop\033[0m\n"); } + | FOR init_loop_statement SEMICOLON log_expr SEMICOLON post_statement loop_block + { printf("\033[1;34mLOOP: full for loop with init, condition and post\033[0m\n"); } + | FOR log_expr loop_block + { printf("\033[1;34mLOOP: for loop with condition only\033[0m\n"); } + ; + +post_statement: + | IDENTIFIER ASSIGN math_expr { } + | IDENTIFIER INC { } + | IDENTIFIER DEC { } + | IDENTIFIER PLUS_EQ math_expr { } + | IDENTIFIER MINUS_EQ math_expr { } + | IDENTIFIER MUL_EQ math_expr { } + | IDENTIFIER DIV_EQ math_expr { } + | IDENTIFIER MOD_EQ math_expr { } + | IDENTIFIER AMPERSAND_EQ math_expr { } + | IDENTIFIER PIPE_EQ math_expr { } + | IDENTIFIER XOR_EQ math_expr { } + | IDENTIFIER LSHIFT_EQ math_expr { } + | IDENTIFIER RSHIFT_EQ math_expr { } + | IDENTIFIER AND_NOT_EQ math_expr { } + +loop_block: + LBRACE loop_statements RBRACE + ; + +init_loop_statement: + | IDENTIFIER SHORT_DECLARATION math_expr + | IDENTIFIER ASSIGN math_expr + +loop_statements: + | loop_statements statement + | loop_statements loop_block + | loop_statements expr SEMICOLON + | loop_statements break_statement + | loop_statements continue_statement + | loop_statements IF log_expr loop_block else_part_loop + ; + +else_part_loop: + { printf("\033[1;33mSTATEMENT: if condition with block\033[0m\n"); } + | ELSE IF log_expr loop_block else_part_loop { printf("\033[1;33mSTATEMENT: if condition with block else if block\033[0m\n"); } + | ELSE loop_block { printf("\033[1;33mSTATEMENT: if condition with block else block\033[0m\n"); } + ; + +break_statement: + BREAK SEMICOLON + { printf("\033[1;31mBREAK\033[0m\n"); } + | BREAK IDENTIFIER SEMICOLON + { printf("\033[1;31mBREAK TO LABEL: %s\033[0m\n", $2); } + ; + +continue_statement: + CONTINUE SEMICOLON + { printf("\033[1;31mCONTINUE\033[0m\n"); } + | CONTINUE IDENTIFIER SEMICOLON + { printf("\033[1;31mCONTINUE TO LABEL: %s\033[0m\n", $2); } + ; +// + + +// expressions +expr: + RETURN math_expr { printf("\033[1;35mRETURN math expr\033[0m\n") } + | RETURN literal { printf("\033[1;35mRETURN literal\033[0m\n") } + | IDENTIFIER ASSIGN math_expr { } + | math_expr { } + ; + +math_expr: + math_expr PLUS math_expr { printf("PLUS\n"); } + | math_expr MINUS math_expr { printf("MINUS\n"); } + | math_expr MULT math_expr { printf("MULT\n"); } + | math_expr DIV math_expr { printf("DIV\n"); } + | math_expr MOD math_expr { printf("MOD\n"); } + | MINUS math_expr %prec UMINUS { printf("UMINUS\n"); } + | LPAREN { printf("LPAREN\n"); } math_expr RPAREN { printf("RPAREN\n"); } + | NUMBER { printf("NUMBER\n"); } + | FLOAT_LITERAL { printf("FLOAT LITERAL\n"); } + | IDENTIFIER { printf("IDENTIFIER: %s\n", $1); } + | IDENTIFIER INC { printf("POST-INCREMENT: %s++\n", $1); } + | IDENTIFIER DEC { printf("POST-DECREMENT: %s--\n", $1); } + ; + +log_expr: + | log_expr AND log_expr { } + | log_expr OR log_expr { } + | NOT log_expr %prec UMINUS { } + | math_expr EQ math_expr { } + | math_expr NEQ math_expr { } + | math_expr LT math_expr { } + | math_expr LEQ math_expr { } + | math_expr GT math_expr { } + | math_expr GEQ math_expr { } + | LPAREN log_expr RPAREN { } + | BOOL_LITERAL + ; +// + +// types +int_types: + UINT { } + | UINT8 { } + | UINT16 { } + | UINT32 { } + | UINT64 { } + | INT { } + | INT8 { } + | INT16 { } + | INT32 {} + | INT64 { } + ; + +float_types: + FLOAT32 + | FLOAT64 + +complex_types: + COMPLEX64 + | COMPLEX128 + +string_types: + STRING { } + ; + +type: + int_types { } + | string_types { } + | float_types { } + | complex_types { } + ; + +// + + +// literals +literal: + STRING_LITERAL { } + | BOOL_LITERAL { } + | FLOAT_LITERAL { } + | NUMBER { } + ; +// + + +// Package & import blocks +package_declaration: + PACKAGE IDENTIFIER SEMICOLON + { printf("\033[1;34mPACKAGE IDENTIFIER: %s\n\033[0m", $2); } + ; + +import_declaration: + | IMPORT { printf("\033[1;36mHELLO, IMPORT BLOCK\n\033[0m"); } import { printf("\033[1;36mBY, IMPORT BLOCK\n\n\033[0m"); } + | IMPORT { printf("\033[1;36mHELLO, IMPORT BLOCK\n\033[0m"); } LPAREN import_list RPAREN { printf("\033[1;36mBY, IMPORT BLOCK\n\n\033[0m"); } + ; + +import: + IDENTIFIER STRING_LITERAL { printf("\033[1;36mIMPORTED PACKAGE\n\033[0m"); } SEMICOLON + | STRING_LITERAL { printf("\033[1;36mIMPORTED PACKAGE\n\033[0m"); } SEMICOLON + ; + +import_list: + import + | import_list import + ; +// + +// functions decl +arg_declaration: + IDENTIFIER type + { printf("\033[1;35mARG: %s\n\033[0m", $1); } + ; + +arg_list: + | arg_declaration + | arg_list COMMA arg_declaration + ; + +return_type: + | type { } + ; + +func_declaration: + FUNC IDENTIFIER + { printf("\033[1;35mHELLO, FUNC: %s\n\033[0m", $2); } + LPAREN arg_list RPAREN return_type block + { printf("\033[1;35mBY, FUNC: %s\n\n\033[0m", $2); } + ; +// + +// vars decl +var_declaration: + IDENTIFIER SHORT_DECLARATION math_expr { printf("\033[1;33mSHORT DECL with math expr: %s\n\033[0m", $1); } + | IDENTIFIER SHORT_DECLARATION literal { printf("\033[1;33mSHORT DECL with literal: %s\n\033[0m", $1); } + | VAR IDENTIFIER type { { printf("\033[1;33mVAR DECL without init value: %s\n\033[0m", $2); } } + | VAR IDENTIFIER type ASSIGN math_expr { { printf("\033[1;33mVAR DECL with math expr init value: %s\n\033[0m", $2); } } + | VAR IDENTIFIER type ASSIGN literal { { printf("\033[1;33mVAR DECL with literal init value: %s\n\033[0m", $2); } } + ; + +%% +// + +int main(int argc, char **argv) { + if (argc > 1) { + FILE *f = fopen(argv[1], "r"); + if (!f) { + perror("\033[1;91mFailed to open file\033[0m"); + return 1; + } + yyin = f; + } + yyparse(); + printf("\033[1;92mGOOD CODE\033[0m\n"); + return 0; +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..9aeb5f1 --- /dev/null +++ b/main.py @@ -0,0 +1,48 @@ +import os +import subprocess + +# ЭТИ ПУТИ НАДО ЗАДАТЬ ВРУЧНУЮ +# *.l и *.y файлы из директории ANALYZERS_DIR ДОЛЖНЫ НАЗЫВАТЬСЯ как basename этой директории!!! +ANALYZERS_DIR = r'C:\Users\user\Desktop\УЧЕБА\6_СЕМ\КОМПИЛЯТОРЫ\go-analyzer-ilya\analyzers\test' +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' + ] + + # Исполнение команд с выводом + for cmd in cmds: + print(f"\n\033[1mExecuting:\033[0m {cmd}") + try: + 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("\033[91mErrors:\033[0m") + print(e.stderr) + + # Очистка промежуточных файлов (только если все команды успешны) + for path in ['lex.yy.c', f'{analyzer_name}.tab.c', f'{analyzer_name}.tab.h']: + try: + os.remove(path) + print(f"\033[1mRemoved:\033[0m {path}") + except FileNotFoundError: + pass + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tests/main.go b/tests/main.go new file mode 100644 index 0000000..e1a8d25 --- /dev/null +++ b/tests/main.go @@ -0,0 +1,14 @@ +package main + +import "fmt" + +const ( + a = "123123" + b = "123123" +) + +func main() { + for i := 5 - 3*(6-7); i < 10; i = 5 + 3 { + fmt.Println(i) + } +} diff --git a/tests/test.txt b/tests/test.txt new file mode 100644 index 0000000..9566e6e --- /dev/null +++ b/tests/test.txt @@ -0,0 +1,24 @@ +package main; + +import ( + "fmt"; + "log"; +) + +func test() { + return "123"; +} + +func test(a int, b string) { + s := "123njda skjad"; + a := 2; + return a + 1; +} + +func main() { + + var a int; + a = 2+2*2-(1+10); + a = a + 1; + a = a; +} \ No newline at end of file diff --git a/tests/test_blocks.txt b/tests/test_blocks.txt new file mode 100644 index 0000000..c1fb84b --- /dev/null +++ b/tests/test_blocks.txt @@ -0,0 +1,90 @@ +package main; + +import ( + "fmt"; + "log"; +) + + +func test_cicle() { + for {} + +} + +func test_if() { + if a != a{ + + } + + if !(a != 1) { + + } + + if a != 1 && a > 1 { + + } +} + + + +func test_func(a string, b int, c uint16) int { + var x int = 42; + y := "hello"; +} + +func func_without_args() int { + return 1; +} + +func func_without_return_type(a string) { + return a; +} +func main() { + a := 229.162613; + if (a > 300) { + s := "test string"; + } + for i := 5; i < 10; i &^= 1 { + i++; + break; + } + for i := 0; i < 10 && j > 5; i++ { + } + for { + var d complex128; + a := 229.162613; + break gotocheck; + s := "test string"; + b := 2 - 1; + } + for i := 0; i < 10; i += 2 { } + for i := 0; i < 10; i = i * 2 { + break; + } + for i := 10; i > 0; i-- { } + a = 1; + a = s; + + s := "test string"; + + var a int; + var a int8; + var a int16; + var a int32; + var a int64; + var a uint8; + var a uint16; + var a uint32; + var a uint64; + var b float32; + var c float64; + var d complex128; + var e complex64; + var a string = "123123"; + { + + b := 2 - 1; + + s := "123"; + } +} \ No newline at end of file diff --git a/tests/test_cicle.txt b/tests/test_cicle.txt new file mode 100644 index 0000000..87bd634 --- /dev/null +++ b/tests/test_cicle.txt @@ -0,0 +1,69 @@ +package main; + +func main() { + + for i := 5 - 3*(6-7); i < 10; i++ { + } + + for { + break; + } + + i := 0; + for i < 5 { + i++; + } + + for j := 0; j < 10; j++ { + } + + for o := 0; o < 10; o++ { + if o%2 == 0 { + continue; + } + } + + for n := 0; ; n++ { + if n > 5 { + break; + } + } + +outer: + + + for p := 0; p < 3; p++ { + for q := 0; q < 3; q++ { + if p*q > 2 { + break outer; + } + } + } + +outer2: + for r := 0; r < 3; r++ { + for s := 0; s < 3; s++ { + if r*s > 1 { + continue outer2; + } + } + } + + for t := 10; t > 0; t-- { + } + + u := 0; + for u < 5 { + u++; + } + + for k, l := 0, 10; k < l; k, l = k+1, l-1 { + } + + for idx, val := range arr { + } + + for key, value := range m { + } + +} \ No newline at end of file diff --git a/tests/test_if.txt b/tests/test_if.txt new file mode 100644 index 0000000..2440dce --- /dev/null +++ b/tests/test_if.txt @@ -0,0 +1,57 @@ +package main; + +import "fmt"; + +func main() { + if true { + } + + if false { + } else { + } + + x := 10; + if x < 5 { + } else if x < 15 { + } else { + } + + + + + if true { + if false { + } else { + } + } + if (x > 5 || x < 15) && x != 10 { + } + + if z := x * 2; z > 15 && z < 25 { + } + + if false { + } + + + if isValid() { + } + + + + if shouldReturn() { + return; + } +} + +func isValid() bool { + return true +} + +func shouldReturn() bool { + return false +} +if a, b := 1, 2; a < b && b < 3 { +} +if y := 20; y > 15 { + } \ No newline at end of file