├── .gitignore ├── CMakeLists.txt ├── Makefile ├── README.md ├── analysis.c ├── analysis.h ├── code_generator.c ├── code_generator.h ├── doc ├── .gitignore ├── Makefile ├── czechiso.bst ├── dokumentace.bib ├── dokumentace.pdf ├── dokumentace.tex └── inc │ ├── FA_graph.pdf │ ├── FA_graph.vsdx │ ├── FIT_logo.pdf │ ├── LL_table.pdf │ └── prec_table.pdf ├── dynamic_string.c ├── dynamic_string.h ├── error.h ├── expression.c ├── expression.h ├── ifj17_compiler.c ├── ifj2017.pdf ├── prezentace ├── IFJ_prezentace.pdf ├── IFJ_prezentace.tex └── img │ ├── FIT_barevne_CMYK_CZ-eps-converted-to.pdf │ ├── FIT_barevne_CMYK_CZ.eps │ ├── Generovani_kodu.pdf │ ├── Generovani_kodu.vsdx │ ├── Main.pdf │ ├── Main.vsdx │ ├── Parser.pdf │ ├── Parser.vsdx │ ├── Scanner.pdf │ ├── Scanner.vsdx │ ├── Vyrazy.pdf │ ├── Vyrazy.vsdx │ ├── Zdrojovy_kod.pdf │ └── Zdrojovy_kod.vsdx ├── rozdeleni ├── scanner.c ├── scanner.h ├── symstack.c ├── symstack.h ├── symtable.c ├── symtable.h └── tests ├── ic17int_linux64_2017-10-30 ├── README └── ic17int ├── ic17int_win64_2017-10-30 ├── .gitignore ├── README ├── cyggcc_s-seh-1.dll ├── cygstdc++-6.dll ├── cygwin1.dll └── ic17int.exe ├── ifj17.bas └── is_it_ok.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.gitignore 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Object files 8 | *.o 9 | *.ko 10 | *.obj 11 | *.elf 12 | 13 | # Linker output 14 | *.ilk 15 | *.map 16 | *.exp 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | # Debug files 43 | *.dSYM/ 44 | *.su 45 | *.idb 46 | *.pdb 47 | 48 | # Kernel Module Compile Results 49 | *.mod* 50 | *.cmd 51 | .tmp_versions/ 52 | modules.order 53 | Module.symvers 54 | Mkfile.old 55 | dkms.conf 56 | 57 | # CMake 58 | /cmake-*/ 59 | 60 | # Project executable 61 | ifj17_compiler 62 | 63 | # is_it_ok test dir 64 | /is_it_ok_test 65 | 66 | # doc 67 | /dokumentace.pdf 68 | 69 | # pack 70 | /xharmi00.tgz 71 | 72 | # VisualStudio project files 73 | /*.vcxproj* 74 | /*.VC* 75 | /*.sln 76 | /CMakeFiles/* 77 | 78 | # Test source file 79 | /test.bas 80 | 81 | # Logs from IFJcode17-toolkit tests 82 | /log/ 83 | /Debug 84 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Author: Dominik Harmim 2 | 3 | CMAKE_MINIMUM_REQUIRED(VERSION 3.7) 4 | 5 | IF(UNIX) 6 | SET(CMAKE_C_COMPILER gcc) 7 | SET(CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -Werror -g") 8 | ENDIF() 9 | 10 | PROJECT(ifj17_compiler) 11 | 12 | FILE(GLOB SOURCES "*.c") 13 | FILE(GLOB HEADERS "*.h") 14 | 15 | ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES} ${HEADERS}) 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Author: Dominik Harmim 2 | 3 | PACK = xharmi00 4 | 5 | IS_IT_OK_DIR = is_it_ok_test 6 | IS_IT_OK_SCRIPT = ./tests/is_it_ok.sh 7 | 8 | DOC_DIR = doc 9 | DOC = dokumentace.pdf 10 | 11 | CC = gcc 12 | CFLAGS = -std=c99 -Wall -Wextra -Werror 13 | 14 | EXECUTABLE = ifj17_compiler 15 | SRCS = *.c 16 | OBJS = $(shell $(CC) $(CFLAGS) -MM $(SRCS) | grep ':' | cut -d ':' -f1 | tr '\n' ' ') 17 | 18 | 19 | .PHONY: all 20 | all: $(EXECUTABLE) clean_depdir 21 | 22 | 23 | $(EXECUTABLE): $(OBJS) 24 | 25 | 26 | .PHONY: pack 27 | pack: clean $(PACK).tgz 28 | 29 | 30 | $(PACK).tgz: $(DOC) 31 | tar -czf $@ *.h *.c Makefile rozdeleni $^ 32 | 33 | 34 | .PHONY: clean_pack 35 | clean_pack: 36 | rm -f $(PACK).tgz 37 | 38 | 39 | $(DOC): 40 | cd $(DOC_DIR) && make 41 | cp $(DOC_DIR)/$(DOC) $(DOC) 42 | 43 | 44 | .PHONY: clean_doc 45 | clean_doc: 46 | rm -f $(DOC) 47 | cd $(DOC_DIR) && make clean 48 | 49 | 50 | .PHONY: is_it_ok 51 | is_it_ok: $(PACK).tgz $(IS_IT_OK_SCRIPT) clean_is_it_ok 52 | chmod +x $(IS_IT_OK_SCRIPT) 53 | $(IS_IT_OK_SCRIPT) $(PACK).tgz $(IS_IT_OK_DIR) 54 | 55 | 56 | .PHONY: clean_is_it_ok 57 | clean_is_it_ok: 58 | rm -rf $(IS_IT_OK_DIR) 59 | 60 | 61 | .PHONY: clean 62 | clean: clean_pack clean_is_it_ok clean_depdir clean_doc 63 | rm -rf $(EXECUTABLE) *.o *.out *.dSYM/ log/ 64 | 65 | 66 | # #################### Auto-Dependency Generation ##################### 67 | DEPDIR := .d 68 | $(shell mkdir -p $(DEPDIR) > /dev/null) 69 | DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td 70 | 71 | .PHONY: clean_depdir 72 | clean_depdir: 73 | rm -rf $(DEPDIR) 74 | 75 | COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c 76 | POSTCOMPILE = @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d && touch $@ 77 | 78 | %.o: %.c 79 | %.o: %.c $(DEPDIR)/%.d 80 | $(COMPILE.c) $(OUTPUT_OPTION) $< 81 | $(POSTCOMPILE) 82 | 83 | $(DEPDIR)/%.d: ; 84 | .PRECIOUS: $(DEPDIR)/%.d 85 | 86 | include $(wildcard $(patsubst %, $(DEPDIR)/%.d, $(basename $(SRCS)))) 87 | # ##################################################################### 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Formální jazyky a překladače - Projekt 2 | ## Implementace překladače imperativního jazyka IFJ17. 3 | 4 | #### Autoři: 5 | - Dominik Harmim 6 | - Vojtěch Hertl 7 | - Timotej Halás 8 | - Matej Karas 9 | -------------------------------------------------------------------------------- /analysis.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Syntactical and semantical analysis imeplementation. 5 | * 6 | * @author Timotej Halás 7 | * @author Matej Karas 8 | * @author Dominik Harmim 9 | */ 10 | 11 | 12 | #include "dynamic_string.h" 13 | #include "error.h" 14 | #include "symtable.h" 15 | #include "analysis.h" 16 | #include "code_generator.h" 17 | 18 | 19 | #define IS_VALUE(token) \ 20 | (token).type == TOKEN_TYPE_DOUBLE_NUMBER \ 21 | || (token).type == TOKEN_TYPE_INT_NUMBER \ 22 | || (token).type == TOKEN_TYPE_STRING \ 23 | || (token).type == TOKEN_TYPE_IDENTIFIER 24 | 25 | #define GET_TOKEN() \ 26 | if ((result = get_next_token(&data->token)) != SCANNER_TOKEN_OK)\ 27 | return result 28 | 29 | #define CHECK_TYPE(_type) \ 30 | if (data->token.type != (_type)) return SYNTAX_ERR 31 | 32 | #define CHECK_RULE(rule) \ 33 | if ((result = rule(data))) return result 34 | 35 | #define CHECK_KEYWORD(_keyword) \ 36 | if ( \ 37 | data->token.type != TOKEN_TYPE_KEYWORD \ 38 | || data->token.attribute.keyword != (_keyword) \ 39 | ) return SYNTAX_ERR 40 | 41 | #define GET_TOKEN_AND_CHECK_TYPE(_type) \ 42 | do { \ 43 | GET_TOKEN(); \ 44 | CHECK_TYPE(_type); \ 45 | } while(0) 46 | 47 | #define GET_TOKEN_AND_CHECK_RULE(rule) \ 48 | do { \ 49 | GET_TOKEN(); \ 50 | CHECK_RULE(rule); \ 51 | } while(0) 52 | 53 | #define GET_TOKEN_AND_CHECK_KEYWORD(_keyword) \ 54 | do { \ 55 | GET_TOKEN(); \ 56 | CHECK_KEYWORD(_keyword); \ 57 | } while(0) 58 | 59 | 60 | // forward declarations 61 | static int scope(PData* data); 62 | static int end(PData* data); 63 | static int params(PData* data); 64 | static int param_n(PData* data); 65 | static int statement(PData* data); 66 | static int def_var(PData* data); 67 | static int def_value(PData* data); 68 | static int arg(PData* data); 69 | static int arg_n(PData* data); 70 | static int value(PData* data); 71 | static int print(PData* data); 72 | 73 | 74 | /** 75 | * Implementation of rule. 76 | * 77 | * @return Given exit code. 78 | */ 79 | static int prog(PData* data) 80 | { 81 | int result; 82 | 83 | // -> DECLARE FUNCTION ID ( ) AS TYPE 84 | if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_DECLARE) 85 | { 86 | data->in_declaration = true; 87 | data->non_declared_function = true; 88 | 89 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_FUNCTION); 90 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_IDENTIFIER); 91 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_LEFT_BRACKET); 92 | 93 | // add id to the global symbol table 94 | bool internal_error; 95 | data->current_id = sym_table_add_symbol(&data->global_table, data->token.attribute.string->str, &internal_error); 96 | if (!data->current_id) 97 | { 98 | if (internal_error) return ERROR_INTERNAL; 99 | else return SEM_ERR_UNDEFINED_VAR; 100 | } 101 | 102 | GET_TOKEN_AND_CHECK_RULE(params); 103 | CHECK_TYPE(TOKEN_TYPE_RIGHT_BRACKET); 104 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_AS); 105 | 106 | // get next token 107 | GET_TOKEN(); 108 | 109 | // check for rule 110 | if (data->token.type == TOKEN_TYPE_KEYWORD) 111 | { 112 | switch (data->token.attribute.keyword) 113 | { 114 | case KEYWORD_INTEGER: 115 | data->current_id->type = TYPE_INT; 116 | break; 117 | 118 | case KEYWORD_DOUBLE: 119 | data->current_id->type = TYPE_DOUBLE; 120 | break; 121 | 122 | case KEYWORD_STRING: 123 | data->current_id->type = TYPE_STRING; 124 | break; 125 | 126 | default: 127 | return SYNTAX_ERR; 128 | } 129 | } 130 | else 131 | return SYNTAX_ERR; 132 | 133 | // get next token and execute rule 134 | GET_TOKEN(); 135 | return prog(data); 136 | } 137 | 138 | // -> FUNCTION ID ( ) AS TYPE EOL END FUNCTION 139 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_FUNCTION) 140 | { 141 | data->in_function = true; 142 | data->in_declaration = false; 143 | data->label_index = 0; 144 | 145 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_IDENTIFIER); 146 | 147 | // if function wasn't declared add it to the global symbol table 148 | bool internal_error; 149 | data->current_id = sym_table_add_symbol(&data->global_table, data->token.attribute.string->str, &internal_error); 150 | if (data->current_id) 151 | data->non_declared_function = true; 152 | else 153 | { 154 | if (internal_error) return ERROR_INTERNAL; 155 | data->non_declared_function = false; 156 | data->current_id = sym_table_search(&data->global_table, data->token.attribute.string->str); 157 | if (data->current_id->defined) 158 | return SEM_ERR_UNDEFINED_VAR; 159 | } 160 | 161 | GENERATE_CODE(generate_function_start, data->current_id->identifier); 162 | 163 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_LEFT_BRACKET); 164 | GET_TOKEN_AND_CHECK_RULE(params); 165 | CHECK_TYPE(TOKEN_TYPE_RIGHT_BRACKET); 166 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_AS); 167 | 168 | // get next token 169 | GET_TOKEN(); 170 | 171 | // check for rule 172 | if (data->token.type == TOKEN_TYPE_KEYWORD) 173 | { 174 | switch (data->token.attribute.keyword) 175 | { 176 | case KEYWORD_INTEGER: 177 | if (!data->non_declared_function && data->current_id->type != TYPE_INT) 178 | return SEM_ERR_UNDEFINED_VAR; 179 | 180 | data->current_id->type = TYPE_INT; 181 | break; 182 | 183 | case KEYWORD_DOUBLE: 184 | if (!data->non_declared_function && data->current_id->type != TYPE_DOUBLE) 185 | return SEM_ERR_UNDEFINED_VAR; 186 | 187 | data->current_id->type = TYPE_DOUBLE; 188 | break; 189 | 190 | case KEYWORD_STRING: 191 | if (!data->non_declared_function && data->current_id->type != TYPE_STRING) 192 | return SEM_ERR_UNDEFINED_VAR; 193 | 194 | data->current_id->type = TYPE_STRING; 195 | break; 196 | 197 | default: 198 | return SYNTAX_ERR; 199 | } 200 | } 201 | else 202 | return SYNTAX_ERR; 203 | 204 | GENERATE_CODE(generate_function_retval, data->current_id->type); 205 | 206 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 207 | GET_TOKEN_AND_CHECK_RULE(statement); 208 | CHECK_KEYWORD(KEYWORD_END); 209 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_FUNCTION); 210 | 211 | GENERATE_CODE(generate_function_end, data->current_id->identifier); 212 | 213 | // Current function is defined 214 | data->current_id->defined = true; 215 | 216 | // clear local symtable 217 | sym_table_free(&data->local_table); 218 | 219 | // get next token and execute rule 220 | GET_TOKEN(); 221 | return prog(data); 222 | } 223 | 224 | // -> EOL 225 | else if (data->token.type == TOKEN_TYPE_EOL) 226 | { 227 | GET_TOKEN(); 228 | return prog(data); 229 | } 230 | 231 | // -> 232 | else 233 | { 234 | return scope(data); 235 | } 236 | 237 | return SYNTAX_ERR; 238 | } 239 | 240 | 241 | /** 242 | * Implementation of rule. 243 | * 244 | * @return Given exit code. 245 | */ 246 | static int scope(PData* data) 247 | { 248 | int result; 249 | 250 | // -> SCOPE EOL END SCOPE 251 | if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_SCOPE) 252 | { 253 | // check if all functions are defined 254 | for (int i = 0; i < MAX_SYMTABLE_SIZE; i++) 255 | for (Sym_table_item* it = data->global_table[i]; it != NULL; it = it->next) 256 | if (!it->data.defined) return SEM_ERR_UNDEFINED_VAR; 257 | 258 | // we are in scope 259 | data->in_function = false; 260 | data->current_id = NULL; 261 | data->label_index = 0; 262 | 263 | GENERATE_CODE(generate_main_scope_start); 264 | 265 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 266 | GET_TOKEN_AND_CHECK_RULE(statement); 267 | CHECK_KEYWORD(KEYWORD_END); 268 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_SCOPE); 269 | 270 | GENERATE_CODE(generate_main_scope_end); 271 | 272 | // clear local symbol table 273 | sym_table_free(&data->local_table); 274 | 275 | // get next token and execute rule 276 | GET_TOKEN(); 277 | return end(data); 278 | } 279 | 280 | return SYNTAX_ERR; 281 | } 282 | 283 | 284 | /** 285 | * Implementation of rule. 286 | * 287 | * @return Given exit code. 288 | */ 289 | static int end(PData* data) 290 | { 291 | int result; 292 | 293 | // -> EOL 294 | if (data->token.type == TOKEN_TYPE_EOL) 295 | { 296 | GET_TOKEN(); 297 | return end(data); 298 | } 299 | 300 | // -> EOF 301 | if (data->token.type == TOKEN_TYPE_EOF) 302 | { 303 | return SYNTAX_OK; 304 | } 305 | 306 | return SYNTAX_ERR; 307 | } 308 | 309 | 310 | /** 311 | * Implementation of rule. Only for variables. 312 | * 313 | * @return Given exit code. 314 | */ 315 | static int type(PData* data) 316 | { 317 | if (data->token.type == TOKEN_TYPE_KEYWORD) 318 | { 319 | switch (data->token.attribute.keyword) 320 | { 321 | case KEYWORD_INTEGER: 322 | if (data->non_declared_function) 323 | { 324 | if (!sym_table_add_param(data->current_id, TYPE_INT)) return ERROR_INTERNAL; 325 | } 326 | else if (data->current_id->params->str[data->param_index] != 'i') return SEM_ERR_UNDEFINED_VAR; 327 | 328 | if (!data->in_declaration) data->rhs_id->type = TYPE_INT; 329 | break; 330 | 331 | case KEYWORD_DOUBLE: 332 | if (data->non_declared_function) 333 | { 334 | if (!sym_table_add_param(data->current_id, TYPE_DOUBLE)) return ERROR_INTERNAL; 335 | } 336 | else if (data->current_id->params->str[data->param_index] != 'd') return SEM_ERR_UNDEFINED_VAR; 337 | 338 | if (!data->in_declaration) data->rhs_id->type = TYPE_DOUBLE; 339 | break; 340 | 341 | case KEYWORD_STRING: 342 | if (data->non_declared_function) 343 | { 344 | if (!sym_table_add_param(data->current_id, TYPE_STRING)) return ERROR_INTERNAL; 345 | } 346 | else if (data->current_id->params->str[data->param_index] != 's') return SEM_ERR_UNDEFINED_VAR; 347 | 348 | if (!data->in_declaration) data->rhs_id->type = TYPE_STRING; 349 | break; 350 | 351 | default: 352 | return SYNTAX_ERR; 353 | } 354 | } 355 | else 356 | return SYNTAX_ERR; 357 | 358 | return SYNTAX_OK; 359 | } 360 | 361 | 362 | /** 363 | * Implementation of rule. 364 | * 365 | * @return Given exit code. 366 | */ 367 | static int params(PData* data) 368 | { 369 | int result; 370 | data->param_index = 0; 371 | 372 | // -> ID AS 373 | if (data->token.type == TOKEN_TYPE_IDENTIFIER) 374 | { 375 | // if there is function named as parameter 376 | if (sym_table_search(&data->global_table, data->token.attribute.string->str)) 377 | return SEM_ERR_UNDEFINED_VAR; 378 | 379 | // if we are in defintion, we need to add parameters to the local symbol table 380 | if (!data->in_declaration) 381 | { 382 | bool internal_error; 383 | if (!(data->rhs_id = sym_table_add_symbol(&data->local_table, data->token.attribute.string->str, &internal_error))) 384 | { 385 | if (internal_error) return ERROR_INTERNAL; 386 | else return SEM_ERR_UNDEFINED_VAR; 387 | } 388 | 389 | GENERATE_CODE(generate_function_param_declare, data->rhs_id->identifier, data->param_index); 390 | } 391 | 392 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_AS); 393 | GET_TOKEN_AND_CHECK_RULE(type); 394 | GET_TOKEN_AND_CHECK_RULE(param_n); 395 | 396 | if (data->param_index + 1 != data->current_id->params->length) 397 | return SEM_ERR_UNDEFINED_VAR; 398 | } 399 | else if (!data->in_declaration && data->current_id->params->length) 400 | return SEM_ERR_UNDEFINED_VAR; 401 | 402 | // -> ε 403 | 404 | return SYNTAX_OK; 405 | } 406 | 407 | 408 | /** 409 | * Implementation of rule. 410 | * 411 | * @return Given exit code. 412 | */ 413 | static int param_n(PData* data) 414 | { 415 | int result; 416 | 417 | // -> , ID AS 418 | if (data->token.type == TOKEN_TYPE_COMMA) 419 | { 420 | data->param_index++; 421 | 422 | if (!data->in_declaration && !data->non_declared_function && data->param_index == data->current_id->params->length) 423 | return SEM_ERR_UNDEFINED_VAR; 424 | 425 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_IDENTIFIER); 426 | 427 | // if we are in defintion, we need to add ID to the local symbol table 428 | if (!data->in_declaration) 429 | { 430 | bool internal_error; 431 | if (!(data->rhs_id = sym_table_add_symbol(&data->local_table, data->token.attribute.string->str, &internal_error))) 432 | { 433 | if (internal_error) return ERROR_INTERNAL; 434 | else return SEM_ERR_UNDEFINED_VAR; 435 | } 436 | 437 | GENERATE_CODE(generate_function_param_declare, data->rhs_id->identifier, data->param_index); 438 | } 439 | 440 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_AS); 441 | GET_TOKEN_AND_CHECK_RULE(type); 442 | 443 | // get next token and execute rule 444 | GET_TOKEN(); 445 | return param_n(data); 446 | } 447 | 448 | // -> ε 449 | 450 | return SYNTAX_OK; 451 | } 452 | 453 | 454 | /** 455 | * Implementation of rule. 456 | * 457 | * @return Given exit code. 458 | */ 459 | static int statement(PData* data) 460 | { 461 | int result; 462 | 463 | // -> DIM ID AS TYPE EOL 464 | if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_DIM) 465 | { 466 | if (data->in_while_or_if) return SYNTAX_ERR; 467 | 468 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_IDENTIFIER); 469 | 470 | // add id to the local symbol table 471 | bool internal_error; 472 | data->lhs_id = sym_table_add_symbol(&data->local_table, data->token.attribute.string->str, &internal_error); 473 | if (!data->lhs_id || sym_table_search(&data->global_table, data->token.attribute.string->str)) 474 | { 475 | if (internal_error) return ERROR_INTERNAL; 476 | else return SEM_ERR_UNDEFINED_VAR; 477 | } 478 | 479 | GENERATE_CODE(generate_var_declare, data->lhs_id->identifier); 480 | 481 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_AS); 482 | 483 | // get next token and check for TYPE token 484 | GET_TOKEN(); 485 | if (data->token.type == TOKEN_TYPE_KEYWORD) 486 | { 487 | switch (data->token.attribute.keyword) 488 | { 489 | case KEYWORD_INTEGER: 490 | data->lhs_id->type = TYPE_INT; 491 | break; 492 | 493 | case KEYWORD_DOUBLE: 494 | data->lhs_id->type = TYPE_DOUBLE; 495 | break; 496 | 497 | case KEYWORD_STRING: 498 | data->lhs_id->type = TYPE_STRING; 499 | break; 500 | 501 | default: 502 | return SYNTAX_ERR; 503 | } 504 | } 505 | else 506 | return SYNTAX_ERR; 507 | 508 | GET_TOKEN_AND_CHECK_RULE(def_var); 509 | CHECK_TYPE(TOKEN_TYPE_EOL); 510 | 511 | // get next token and execute rule 512 | GET_TOKEN(); 513 | return statement(data); 514 | } 515 | 516 | // -> IF THEN EOL ELSE EOL END IF EOL 517 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_IF) 518 | { 519 | data->label_deep++; 520 | data->in_while_or_if = true; 521 | 522 | data->lhs_id = sym_table_search(&data->global_table, "%exp_result"); 523 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 524 | data->lhs_id->type = TYPE_BOOL; 525 | 526 | char *function_id = data->current_id ? data->current_id->identifier : ""; 527 | int current_label_index = data->label_index; 528 | data->label_index += 2; 529 | 530 | GENERATE_CODE(generate_if_head); 531 | 532 | GET_TOKEN_AND_CHECK_RULE(expression); 533 | CHECK_KEYWORD(KEYWORD_THEN); 534 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 535 | 536 | GENERATE_CODE(generate_if_start, function_id, current_label_index, data->label_deep); 537 | 538 | GET_TOKEN_AND_CHECK_RULE(statement); 539 | CHECK_KEYWORD(KEYWORD_ELSE); 540 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 541 | 542 | GENERATE_CODE(generate_if_else_part, function_id, current_label_index, data->label_deep); 543 | 544 | GET_TOKEN_AND_CHECK_RULE(statement); 545 | 546 | CHECK_KEYWORD(KEYWORD_END); 547 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_IF); 548 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 549 | 550 | GENERATE_CODE(generate_if_end, function_id, current_label_index + 1, data->label_deep); 551 | 552 | data->label_deep--; 553 | data->in_while_or_if = false; 554 | 555 | // get next token and execute rule 556 | GET_TOKEN(); 557 | return statement(data); 558 | } 559 | 560 | // -> DO WHILE EOL LOOP EOL 561 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_DO) 562 | { 563 | data->label_deep++; 564 | data->in_while_or_if = true; 565 | 566 | GET_TOKEN_AND_CHECK_KEYWORD(KEYWORD_WHILE); 567 | 568 | data->lhs_id = sym_table_search(&data->global_table, "%exp_result"); 569 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 570 | data->lhs_id->type = TYPE_BOOL; 571 | 572 | char *function_id = data->current_id ? data->current_id->identifier : ""; 573 | int current_label_index = data->label_index; 574 | data->label_index += 2; 575 | 576 | GENERATE_CODE(generate_while_head, function_id, current_label_index, data->label_deep); 577 | 578 | GET_TOKEN_AND_CHECK_RULE(expression); 579 | CHECK_TYPE(TOKEN_TYPE_EOL); 580 | 581 | GENERATE_CODE(generate_while_start, function_id, current_label_index + 1, data->label_deep); 582 | 583 | GET_TOKEN_AND_CHECK_RULE(statement); 584 | 585 | CHECK_KEYWORD(KEYWORD_LOOP); 586 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 587 | 588 | GENERATE_CODE(generate_while_end, function_id, current_label_index + 1, data->label_deep); 589 | 590 | data->label_deep--; 591 | data->in_while_or_if = false; 592 | 593 | // get next token and execute rule 594 | GET_TOKEN(); 595 | return statement(data); 596 | } 597 | 598 | // -> ID = EOL 599 | else if (data->token.type == TOKEN_TYPE_IDENTIFIER) 600 | { 601 | // check for existence of variable 602 | data->lhs_id = sym_table_search(&data->local_table, data->token.attribute.string->str); 603 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 604 | 605 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_ASSIGN); 606 | GET_TOKEN_AND_CHECK_RULE(def_value); 607 | CHECK_TYPE(TOKEN_TYPE_EOL); 608 | 609 | // get next token and execute rule 610 | GET_TOKEN(); 611 | return statement(data); 612 | } 613 | 614 | // -> INPUT ID EOL 615 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_INPUT) 616 | { 617 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_IDENTIFIER); 618 | 619 | data->lhs_id = sym_table_search(&data->local_table, data->token.attribute.string->str); 620 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 621 | 622 | if (data->lhs_id->type != TYPE_INT && data->lhs_id->type != TYPE_DOUBLE && data->lhs_id->type != TYPE_STRING) 623 | { 624 | return SEM_ERR_OTHER; 625 | } 626 | 627 | GENERATE_CODE(generate_input, data->lhs_id->identifier, data->lhs_id->type); 628 | 629 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_EOL); 630 | 631 | // get next token and execute rule 632 | GET_TOKEN(); 633 | return statement(data); 634 | } 635 | 636 | // -> PRINT ; EOL 637 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_PRINT) 638 | { 639 | data->lhs_id = sym_table_search(&data->global_table, "%exp_result"); 640 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 641 | data->lhs_id->type = TYPE_UNDEFINED; 642 | 643 | GET_TOKEN_AND_CHECK_RULE(expression); 644 | CHECK_TYPE(TOKEN_TYPE_SEMICOLON); 645 | 646 | GENERATE_CODE(generate_print); 647 | 648 | GET_TOKEN_AND_CHECK_RULE(print); 649 | CHECK_TYPE(TOKEN_TYPE_EOL); 650 | 651 | // get next token and execute rule 652 | GET_TOKEN(); 653 | return statement(data); 654 | } 655 | 656 | // -> RETURN EOL 657 | else if (data->token.type == TOKEN_TYPE_KEYWORD && data->token.attribute.keyword == KEYWORD_RETURN) 658 | { 659 | // scope doesn't have this type of rule 660 | if (!data->in_function) return SYNTAX_ERR; 661 | 662 | data->lhs_id = sym_table_search(&data->global_table, "%exp_result"); 663 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 664 | data->lhs_id->type = data->current_id->type; 665 | 666 | GET_TOKEN_AND_CHECK_RULE(expression); 667 | 668 | GENERATE_CODE(generate_function_return, data->current_id->identifier); 669 | 670 | CHECK_TYPE(TOKEN_TYPE_EOL); 671 | 672 | // get next token and execute rule 673 | GET_TOKEN(); 674 | return statement(data); 675 | } 676 | 677 | // -> ε 678 | else if (data->token.type == TOKEN_TYPE_EOL) 679 | { 680 | GET_TOKEN(); 681 | return statement(data); 682 | } 683 | 684 | return SYNTAX_OK; 685 | } 686 | 687 | 688 | /** 689 | * Implementation of rule. 690 | * 691 | * @return Given exit code. 692 | */ 693 | static int def_var(PData* data) 694 | { 695 | int result; 696 | 697 | GENERATE_CODE(generate_var_default_value, data->lhs_id->identifier, data->lhs_id->type); 698 | 699 | // -> = 700 | if (data->token.type == TOKEN_TYPE_ASSIGN) 701 | { 702 | GET_TOKEN_AND_CHECK_RULE(expression); 703 | } 704 | 705 | // -> ε 706 | 707 | return SYNTAX_OK; 708 | } 709 | 710 | 711 | /** 712 | * Implementation of rule. 713 | * 714 | * @return Given exit code. 715 | */ 716 | static int def_value(PData* data) 717 | { 718 | int result; 719 | if (data->token.type == TOKEN_TYPE_IDENTIFIER || data->token.type == TOKEN_TYPE_KEYWORD) 720 | { 721 | // -> ID ( ) 722 | if (data->token.type == TOKEN_TYPE_IDENTIFIER) 723 | { 724 | data->rhs_id = sym_table_search(&data->global_table, data->token.attribute.string->str); 725 | } 726 | 727 | if (data->token.type == TOKEN_TYPE_KEYWORD) 728 | { 729 | switch (data->token.attribute.keyword) 730 | { 731 | // -> ASC ( ) 732 | case KEYWORD_ASC: 733 | data->rhs_id = sym_table_search(&data->global_table, "asc"); 734 | break; 735 | 736 | // -> CHR ( ) 737 | case KEYWORD_CHR: 738 | data->rhs_id = sym_table_search(&data->global_table, "chr"); 739 | break; 740 | 741 | // -> LENGTH ( ) 742 | case KEYWORD_LENGTH: 743 | data->rhs_id = sym_table_search(&data->global_table, "length"); 744 | break; 745 | 746 | // -> SUBSTR ( ) 747 | case KEYWORD_SUBSTR: 748 | data->rhs_id = sym_table_search(&data->global_table, "substr"); 749 | break; 750 | default: 751 | return SYNTAX_ERR; 752 | } 753 | } 754 | 755 | if (data->rhs_id) 756 | { 757 | // check type compatibilty 758 | // if either one expression is string, we cannot implicitly convert 759 | if (data->lhs_id->type != data->rhs_id->type) 760 | if (data->lhs_id->type == TYPE_STRING || data->rhs_id->type == TYPE_STRING) 761 | return SEM_ERR_TYPE_COMPAT; 762 | 763 | GENERATE_CODE(generate_function_before_pass_params); 764 | 765 | GET_TOKEN_AND_CHECK_TYPE(TOKEN_TYPE_LEFT_BRACKET); 766 | GET_TOKEN_AND_CHECK_RULE(arg); 767 | CHECK_TYPE(TOKEN_TYPE_RIGHT_BRACKET); 768 | GET_TOKEN(); 769 | 770 | if (data->rhs_id->params->length != data->param_index) return SEM_ERR_TYPE_COMPAT; 771 | 772 | GENERATE_CODE(generate_function_call, data->rhs_id->identifier); 773 | GENERATE_CODE(generate_function_retval_assign, data->lhs_id->identifier, data->lhs_id->type, data->rhs_id->type); 774 | 775 | return SYNTAX_OK; 776 | } 777 | 778 | data->rhs_id = sym_table_search(&data->local_table, data->token.attribute.string->str); 779 | if (!data->rhs_id) 780 | return SEM_ERR_UNDEFINED_VAR; 781 | } 782 | 783 | // -> 784 | CHECK_RULE(expression); 785 | 786 | return SYNTAX_OK; 787 | } 788 | 789 | 790 | /** 791 | * Implementation of rule. 792 | * 793 | * @return Given exit code. 794 | */ 795 | static int arg(PData* data) 796 | { 797 | int result; 798 | 799 | // currently processed argument 800 | data->param_index = 0; 801 | 802 | // -> 803 | // if token is value 804 | if (IS_VALUE(data->token)) 805 | { 806 | CHECK_RULE(value); 807 | GET_TOKEN_AND_CHECK_RULE(arg_n); 808 | } 809 | 810 | // -> ε 811 | 812 | return SYNTAX_OK; 813 | } 814 | 815 | 816 | /** 817 | * Implementation of rule. 818 | * 819 | * @return Given exit code. 820 | */ 821 | static int arg_n(PData* data) 822 | { 823 | int result; 824 | 825 | // -> , 826 | if (data->token.type == TOKEN_TYPE_COMMA) 827 | { 828 | GET_TOKEN_AND_CHECK_RULE(value); 829 | GET_TOKEN_AND_CHECK_RULE(arg_n); 830 | } 831 | 832 | // -> ε 833 | 834 | return SYNTAX_OK; 835 | } 836 | 837 | 838 | /** 839 | * Implementation of rule. 840 | * 841 | * @return Given exit code. 842 | */ 843 | static int value(PData* data) 844 | { 845 | // check number of arguments 846 | if (data->rhs_id->params->length == data->param_index) 847 | return SEM_ERR_TYPE_COMPAT; 848 | 849 | GENERATE_CODE(generate_function_pass_param, data->token, data->param_index); 850 | 851 | switch (data->token.type) 852 | { 853 | // -> DOUBLE_NUMBER 854 | case TOKEN_TYPE_DOUBLE_NUMBER: 855 | if (data->rhs_id->params->str[data->param_index] == 's') 856 | return SEM_ERR_TYPE_COMPAT; 857 | if (data->rhs_id->params->str[data->param_index] == 'i') 858 | GENERATE_CODE(generate_function_convert_passed_param, TYPE_DOUBLE, TYPE_INT, data->param_index); 859 | 860 | break; 861 | 862 | // -> INT_NUMBER 863 | case TOKEN_TYPE_INT_NUMBER: 864 | if (data->rhs_id->params->str[data->param_index] == 's') 865 | return SEM_ERR_TYPE_COMPAT; 866 | if (data->rhs_id->params->str[data->param_index] == 'd') 867 | GENERATE_CODE(generate_function_convert_passed_param, TYPE_INT, TYPE_DOUBLE, data->param_index); 868 | 869 | break; 870 | 871 | // -> STRING 872 | case TOKEN_TYPE_STRING: 873 | if (data->rhs_id->params->str[data->param_index] != 's') 874 | return SEM_ERR_TYPE_COMPAT; 875 | 876 | break; 877 | 878 | // -> IDENTIFIER 879 | case TOKEN_TYPE_IDENTIFIER:; // ; C evil magic 880 | TData* id = sym_table_search(&data->local_table, data->token.attribute.string->str); 881 | if (!id) return SEM_ERR_UNDEFINED_VAR; 882 | 883 | switch (id->type) 884 | { 885 | case TYPE_INT: 886 | if (data->rhs_id->params->str[data->param_index] == 's') 887 | return SEM_ERR_TYPE_COMPAT; 888 | if (data->rhs_id->params->str[data->param_index] == 'd') 889 | GENERATE_CODE(generate_function_convert_passed_param, TYPE_INT, TYPE_DOUBLE, data->param_index); 890 | 891 | break; 892 | 893 | case TYPE_DOUBLE: 894 | if (data->rhs_id->params->str[data->param_index] == 's') 895 | return SEM_ERR_TYPE_COMPAT; 896 | if (data->rhs_id->params->str[data->param_index] == 'i') 897 | GENERATE_CODE(generate_function_convert_passed_param, TYPE_DOUBLE, TYPE_INT, data->param_index); 898 | 899 | break; 900 | 901 | case TYPE_STRING: 902 | if (data->rhs_id->params->str[data->param_index] != 's') 903 | return SEM_ERR_TYPE_COMPAT; 904 | 905 | break; 906 | 907 | default: // shouldn't get here 908 | return ERROR_INTERNAL; 909 | } 910 | 911 | break; 912 | 913 | default: 914 | return SYNTAX_ERR; 915 | } 916 | 917 | // increment argument position 918 | data->param_index++; 919 | 920 | return SYNTAX_OK; 921 | } 922 | 923 | 924 | /** 925 | * Implementation of rule. 926 | * 927 | * @return Given exit code. 928 | */ 929 | static int print(PData* data) 930 | { 931 | int result; 932 | 933 | // -> ε 934 | if (data->token.type == TOKEN_TYPE_EOL) 935 | { 936 | return SYNTAX_OK; 937 | } 938 | 939 | data->lhs_id = sym_table_search(&data->global_table, "%exp_result"); 940 | if (!data->lhs_id) return SEM_ERR_UNDEFINED_VAR; 941 | data->lhs_id->type = TYPE_UNDEFINED; 942 | 943 | // -> ; 944 | CHECK_RULE(expression); 945 | CHECK_TYPE(TOKEN_TYPE_SEMICOLON); 946 | 947 | GENERATE_CODE(generate_print); 948 | 949 | GET_TOKEN_AND_CHECK_RULE(print); 950 | 951 | return SYNTAX_OK; 952 | } 953 | 954 | 955 | /** 956 | * Initialize variable needed for analysis. 957 | * 958 | * @return True if inicialization was successful, false otherwise. 959 | */ 960 | static bool init_variables(PData* data) 961 | { 962 | sym_table_init(&data->global_table); 963 | sym_table_init(&data->local_table); 964 | 965 | data->current_id = NULL; 966 | data->lhs_id = NULL; 967 | data->rhs_id = NULL; 968 | 969 | data->param_index = 0; 970 | data->label_index = 0; 971 | data->label_deep = -1; 972 | 973 | data->in_function = false; 974 | data->in_declaration = false; 975 | data->in_while_or_if = false; 976 | data->non_declared_function = false; 977 | 978 | // init default functions 979 | 980 | bool internal_error; 981 | TData* id; 982 | 983 | // Length(s As String) As Integer 984 | id = sym_table_add_symbol(&data->global_table, "length", &internal_error); 985 | if (internal_error) return false; 986 | 987 | id->defined = true; 988 | id->type = TYPE_INT; 989 | if (!sym_table_add_param(id, TYPE_STRING)) return false; 990 | 991 | // SubStr(s As String, i As Integer, n As Integer) As String 992 | id = sym_table_add_symbol(&data->global_table, "substr", &internal_error); 993 | if (internal_error) return false; 994 | 995 | id->defined = true; 996 | id->type = TYPE_STRING; 997 | if (!sym_table_add_param(id, TYPE_STRING)) return false; 998 | if (!sym_table_add_param(id, TYPE_INT)) return false; 999 | if (!sym_table_add_param(id, TYPE_INT)) return false; 1000 | 1001 | // Asc(s As String, i As Integer) As Integer 1002 | id = sym_table_add_symbol(&data->global_table, "asc", &internal_error); 1003 | if (internal_error) return false; 1004 | 1005 | id->defined = true; 1006 | id->type = TYPE_INT; 1007 | if (!sym_table_add_param(id, TYPE_STRING)) return false; 1008 | if (!sym_table_add_param(id, TYPE_INT)) return false; 1009 | 1010 | // Chr(i As Integer) As String 1011 | id = sym_table_add_symbol(&data->global_table, "chr", &internal_error); 1012 | if (internal_error) return false; 1013 | 1014 | id->defined = true; 1015 | id->type = TYPE_STRING; 1016 | if (!sym_table_add_param(id, TYPE_INT)) return false; 1017 | 1018 | // Global variable %exp_result for storing result of expression. 1019 | id = sym_table_add_symbol(&data->global_table, "%exp_result", &internal_error); 1020 | if (internal_error) return false; 1021 | id->defined = true; 1022 | id->type = TYPE_UNDEFINED; 1023 | id->global = true; 1024 | 1025 | return true; 1026 | } 1027 | 1028 | 1029 | /** 1030 | * Frees symbol tables 1031 | */ 1032 | static void free_variables(PData* data) 1033 | { 1034 | sym_table_free(&data->global_table); 1035 | sym_table_free(&data->local_table); 1036 | } 1037 | 1038 | 1039 | int analyse() 1040 | { 1041 | int result; 1042 | 1043 | Dynamic_string string; 1044 | if (!dynamic_string_init(&string)) return ERROR_INTERNAL; 1045 | set_dynamic_string(&string); 1046 | 1047 | PData parser_data; 1048 | if (!init_variables(&parser_data)) 1049 | { 1050 | dynamic_string_free(&string); 1051 | return ERROR_INTERNAL; 1052 | } 1053 | 1054 | if ((result = get_next_token(&parser_data.token)) == SCANNER_TOKEN_OK) 1055 | { 1056 | if (!code_generator_start()) 1057 | { 1058 | dynamic_string_free(&string); 1059 | free_variables(&parser_data); 1060 | return ERROR_INTERNAL; 1061 | } 1062 | 1063 | result = prog(&parser_data); 1064 | } 1065 | 1066 | dynamic_string_free(&string); 1067 | free_variables(&parser_data); 1068 | 1069 | return result; 1070 | } 1071 | -------------------------------------------------------------------------------- /analysis.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Syntactical and semantical analysis interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Matej Karas 8 | * @author Dominik Harmim 9 | */ 10 | 11 | 12 | #ifndef _ANALYSIS_H 13 | #define _ANALYSIS_H 14 | 15 | 16 | #include 17 | 18 | #include "symtable.h" 19 | #include "scanner.h" 20 | 21 | 22 | #define GENERATE_CODE(_callback, ...) \ 23 | if (!_callback(__VA_ARGS__)) return ERROR_INTERNAL 24 | 25 | 26 | /** 27 | * @struct Parser's internal data representation. 28 | */ 29 | typedef struct 30 | { 31 | Sym_table global_table; /// Global symbol table 32 | Sym_table local_table; /// Local symbol table 33 | 34 | Token token; /// Token 35 | 36 | TData* current_id; /// ID of currently processed function 37 | TData* lhs_id; /// ID of left-hand-side variable 38 | TData* rhs_id; /// ID of right-hand-side function (expression?) 39 | 40 | unsigned param_index; /// Index of currently checked param 41 | int label_index; /// Index for generating unique labels. 42 | int label_deep; /// Deep of labes. 43 | 44 | bool in_function; /// Defines if the parser is in function 45 | bool in_declaration; /// Defines if param rule should add or check it's params 46 | bool in_while_or_if; /// Defines if the parser is in construction while, if or then 47 | bool non_declared_function; /// Function that has been only defined 48 | } PData; 49 | 50 | 51 | /** 52 | * Starts syntactic and semantic anlysis. 53 | * 54 | * @return Appropriate error code. 55 | */ 56 | int analyse(); 57 | 58 | 59 | #endif //_ANALYSIS_H 60 | -------------------------------------------------------------------------------- /code_generator.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Code generator implementation. 5 | * 6 | * @author Dominik Harmim 7 | */ 8 | 9 | 10 | #include 11 | 12 | #include "dynamic_string.h" 13 | #include "scanner.h" 14 | #include "code_generator.h" 15 | 16 | 17 | /// macros 18 | #define ADD_INST(_inst) \ 19 | if (!dynamic_string_add_const_str(&code, (_inst "\n"))) return false 20 | 21 | #define ADD_CODE(_code) \ 22 | if (!dynamic_string_add_const_str(&code, (_code))) return false 23 | 24 | #define ADD_CODE_INT(_code) \ 25 | do { \ 26 | char str[MAX_DIGITS]; \ 27 | sprintf(str, "%d", _code); \ 28 | ADD_CODE(str); \ 29 | } while (0) 30 | 31 | 32 | #define MAX_DIGITS 40 /// Maximum digits for converting number to string. 33 | 34 | /// built-in functions 35 | // Length(s As String) As Integer 36 | #define FUNCTION_LENGTH \ 37 | "\n # Built-in function Length" \ 38 | "\n LABEL $length" \ 39 | "\n PUSHFRAME" \ 40 | "\n DEFVAR LF@%retval" \ 41 | "\n STRLEN LF@%retval LF@%0" \ 42 | "\n POPFRAME" \ 43 | "\n RETURN" 44 | 45 | // SubStr(s As String, i As Integer, n As Integer) As String 46 | #define FUNCTION_SUBSTR \ 47 | "\n # Built-in function SubStr" \ 48 | "\n LABEL $substr" \ 49 | "\n PUSHFRAME" \ 50 | "\n DEFVAR LF@%retval" \ 51 | "\n MOVE LF@%retval string@" \ 52 | "\n DEFVAR LF@length_str" \ 53 | "\n CREATEFRAME" \ 54 | "\n DEFVAR TF@%0" \ 55 | "\n MOVE TF@%0 LF@%0" \ 56 | "\n CALL $length" \ 57 | "\n MOVE LF@length_str TF@%retval" \ 58 | "\n DEFVAR LF@ret_cond" \ 59 | "\n LT LF@ret_cond LF@length_str int@0" \ 60 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 61 | "\n EQ LF@ret_cond LF@length_str int@0" \ 62 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 63 | "\n LT LF@ret_cond LF@%1 int@0" \ 64 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 65 | "\n EQ LF@ret_cond LF@%1 int@0" \ 66 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 67 | "\n GT LF@ret_cond LF@%1 LF@length_str" \ 68 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 69 | "\n EQ LF@ret_cond LF@%2 int@0" \ 70 | "\n JUMPIFEQ $substr$return LF@ret_cond bool@true" \ 71 | "\n DEFVAR LF@max_n" \ 72 | "\n MOVE LF@max_n LF@length_str" \ 73 | "\n SUB LF@max_n LF@max_n LF@%1" \ 74 | "\n ADD LF@max_n LF@max_n int@1" \ 75 | "\n DEFVAR LF@edit_n_cond" \ 76 | "\n LT LF@edit_n_cond LF@%2 int@0" \ 77 | "\n JUMPIFEQ $substr$edit_n LF@edit_n_cond bool@true" \ 78 | "\n GT LF@edit_n_cond LF@%2 LF@max_n" \ 79 | "\n JUMPIFEQ $substr$edit_n LF@edit_n_cond bool@true" \ 80 | "\n JUMP $substr$process" \ 81 | "\n LABEL $substr$edit_n" \ 82 | "\n MOVE LF@%2 LF@max_n" \ 83 | "\n LABEL $substr$process" \ 84 | "\n DEFVAR LF@index" \ 85 | "\n MOVE LF@index LF@%1" \ 86 | "\n SUB LF@index LF@index int@1" \ 87 | "\n DEFVAR LF@char" \ 88 | "\n DEFVAR LF@process_loop_cond" \ 89 | "\n LABEL $substr$process_loop" \ 90 | "\n GETCHAR LF@char LF@%0 LF@index" \ 91 | "\n CONCAT LF@%retval LF@%retval LF@char" \ 92 | "\n ADD LF@index LF@index int@1" \ 93 | "\n SUB LF@%2 LF@%2 int@1" \ 94 | "\n GT LF@process_loop_cond LF@%2 int@0" \ 95 | "\n JUMPIFEQ $substr$process_loop LF@process_loop_cond bool@true" \ 96 | "\n LABEL $substr$return" \ 97 | "\n POPFRAME" \ 98 | "\n RETURN" 99 | 100 | // Asc(s As String, i As Integer) As Integer 101 | #define FUNCTION_ASC \ 102 | "\n # Built-in function Asc" \ 103 | "\n LABEL $asc" \ 104 | "\n PUSHFRAME" \ 105 | "\n DEFVAR LF@%retval" \ 106 | "\n MOVE LF@%retval int@0" \ 107 | "\n DEFVAR LF@cond_length" \ 108 | "\n LT LF@cond_length LF@%1 int@1" \ 109 | "\n JUMPIFEQ $asc$return LF@cond_length bool@true" \ 110 | "\n DEFVAR LF@length_str" \ 111 | "\n CREATEFRAME" \ 112 | "\n DEFVAR TF@%0" \ 113 | "\n MOVE TF@%0 LF@%0" \ 114 | "\n CALL $length" \ 115 | "\n MOVE LF@length_str TF@%retval" \ 116 | "\n GT LF@cond_length LF@%1 LF@length_str" \ 117 | "\n JUMPIFEQ $asc$return LF@cond_length bool@true" \ 118 | "\n SUB LF@%1 LF@%1 int@1" \ 119 | "\n STRI2INT LF@%retval LF@%0 LF@%1" \ 120 | "\n LABEL $asc$return" \ 121 | "\n POPFRAME" \ 122 | "\n RETURN" 123 | 124 | // Chr(i As Integer) As String 125 | #define FUNCTION_CHR \ 126 | "\n # Built-in function Chr" \ 127 | "\n LABEL $chr" \ 128 | "\n PUSHFRAME" \ 129 | "\n DEFVAR LF@%retval" \ 130 | "\n MOVE LF@%retval string@" \ 131 | "\n DEFVAR LF@cond_range" \ 132 | "\n LT LF@cond_range LF@%0 int@0" \ 133 | "\n JUMPIFEQ $chr$return LF@cond_range bool@true" \ 134 | "\n GT LF@cond_range LF@%0 int@255" \ 135 | "\n JUMPIFEQ $chr$return LF@cond_range bool@true" \ 136 | "\n INT2CHAR LF@%retval LF@%0" \ 137 | "\n LABEL $chr$return" \ 138 | "\n POPFRAME" \ 139 | "\n RETURN" 140 | 141 | 142 | Dynamic_string code; /// String for generating code. 143 | 144 | 145 | /** 146 | * Defines built-in functions. 147 | * 148 | * @return True if it was successful, false otherwise. 149 | */ 150 | static bool define_built_in_functions() 151 | { 152 | ADD_INST(FUNCTION_LENGTH); 153 | ADD_INST(FUNCTION_SUBSTR); 154 | ADD_INST(FUNCTION_ASC); 155 | ADD_INST(FUNCTION_CHR); 156 | 157 | return true; 158 | } 159 | 160 | 161 | /** 162 | * Generates file header, defines global variables and jump to main function. 163 | * 164 | * @return True if it was successful, false otherwise. 165 | */ 166 | static bool generate_file_header() 167 | { 168 | ADD_INST("# Start of program"); 169 | 170 | ADD_INST(".IFJcode17"); 171 | 172 | ADD_INST("DEFVAR GF@%input_prompt"); 173 | ADD_INST("MOVE GF@%input_prompt string@?\\032"); 174 | 175 | ADD_INST("DEFVAR GF@%tmp_op1"); 176 | ADD_INST("DEFVAR GF@%tmp_op2"); 177 | ADD_INST("DEFVAR GF@%tmp_op3"); 178 | 179 | ADD_INST("DEFVAR GF@%exp_result"); 180 | 181 | ADD_INST("JUMP $$main"); 182 | 183 | return true; 184 | } 185 | 186 | 187 | bool code_generator_start() 188 | { 189 | if (!dynamic_string_init(&code)) return false; 190 | 191 | if (!generate_file_header()) return false; 192 | 193 | if (!define_built_in_functions()) return false; 194 | 195 | return true; 196 | } 197 | 198 | 199 | void code_generator_clear() 200 | { 201 | dynamic_string_free(&code); 202 | } 203 | 204 | 205 | void code_generator_flush(FILE *destination_file) 206 | { 207 | fputs(code.str, destination_file); 208 | code_generator_clear(); 209 | } 210 | 211 | 212 | bool generate_main_scope_start() 213 | { 214 | ADD_INST("\n# Main scope"); 215 | 216 | ADD_INST("LABEL $$main"); 217 | ADD_INST("CREATEFRAME"); 218 | ADD_INST("PUSHFRAME"); 219 | 220 | return true; 221 | } 222 | 223 | 224 | bool generate_main_scope_end() 225 | { 226 | ADD_INST("# End of main scope"); 227 | 228 | ADD_INST("POPFRAME"); 229 | ADD_INST("CLEARS"); 230 | 231 | return true; 232 | } 233 | 234 | 235 | bool generate_function_start(char *function_id) 236 | { 237 | ADD_CODE("\n# Start of function "); ADD_CODE(function_id); ADD_CODE("\n"); 238 | 239 | ADD_CODE("LABEL $"); ADD_CODE(function_id); ADD_CODE("\n"); 240 | ADD_INST("PUSHFRAME"); 241 | 242 | return true; 243 | } 244 | 245 | 246 | bool generate_function_end(char *function_id) 247 | { 248 | ADD_CODE("# End of function "); ADD_CODE(function_id); ADD_CODE("\n"); 249 | 250 | ADD_CODE("LABEL $"); ADD_CODE(function_id); ADD_CODE("%return\n"); 251 | ADD_INST("POPFRAME"); 252 | ADD_INST("RETURN"); 253 | 254 | return true; 255 | } 256 | 257 | 258 | /** 259 | * Generates default value of variable. 260 | * 261 | * @param type Data type of variable. 262 | * @return True if it was successful, false otherwise. 263 | */ 264 | static bool generate_default_var_value(Data_type type) 265 | { 266 | switch (type) 267 | { 268 | case TYPE_INT: 269 | ADD_CODE("int@0"); 270 | break; 271 | 272 | case TYPE_DOUBLE: 273 | ADD_CODE("float@0.0"); 274 | break; 275 | 276 | case TYPE_STRING: 277 | ADD_CODE("string@"); 278 | break; 279 | 280 | case TYPE_BOOL: 281 | ADD_CODE("bool@false"); 282 | 283 | default: 284 | return false; 285 | } 286 | 287 | return true; 288 | } 289 | 290 | 291 | bool generate_function_retval(Data_type type) 292 | { 293 | ADD_INST("DEFVAR LF@%retval"); 294 | 295 | ADD_CODE("MOVE LF@%retval "); 296 | if (!generate_default_var_value(type)) return false; 297 | ADD_CODE("\n"); 298 | 299 | return true; 300 | } 301 | 302 | 303 | bool generate_function_call(char *function_id) 304 | { 305 | ADD_CODE("CALL $"); ADD_CODE(function_id); ADD_CODE("\n"); 306 | 307 | return true; 308 | } 309 | 310 | 311 | bool generate_function_retval_assign(char *l_val_id, Data_type l_type, Data_type ret_type) 312 | { 313 | if (l_type == TYPE_INT && ret_type == TYPE_DOUBLE) 314 | { 315 | ADD_INST("FLOAT2R2EINT TF@%retval TF@%retval"); 316 | } 317 | else if (l_type == TYPE_DOUBLE && ret_type == TYPE_INT) 318 | { 319 | ADD_INST("INT2FLOAT TF@%retval TF@%retval"); 320 | } 321 | 322 | ADD_CODE("MOVE LF@"); ADD_CODE(l_val_id); ADD_CODE(" TF@%retval\n"); 323 | 324 | return true; 325 | } 326 | 327 | 328 | bool generate_function_param_declare(char *param_id, int index) 329 | { 330 | ADD_CODE("DEFVAR LF@"); ADD_CODE(param_id); ADD_CODE("\n"); 331 | ADD_CODE("MOVE LF@"); ADD_CODE(param_id); ADD_CODE(" LF@%"); ADD_CODE_INT(index); ADD_CODE("\n"); 332 | 333 | return true; 334 | } 335 | 336 | 337 | bool generate_function_before_pass_params() 338 | { 339 | ADD_INST("CREATEFRAME"); 340 | 341 | return true; 342 | } 343 | 344 | 345 | bool generate_function_convert_passed_param(Data_type from, Data_type to, int index) 346 | { 347 | if (from == TYPE_DOUBLE && to == TYPE_INT) 348 | { 349 | ADD_CODE("FLOAT2R2EINT TF@%"); ADD_CODE_INT(index); ADD_CODE(" TF@%"); ADD_CODE_INT(index); ADD_CODE("\n"); 350 | } 351 | else if (from == TYPE_INT && to == TYPE_DOUBLE) 352 | { 353 | ADD_CODE("INT2FLOAT TF@%"); ADD_CODE_INT(index); ADD_CODE(" TF@%"); ADD_CODE_INT(index); ADD_CODE("\n"); 354 | } 355 | 356 | return true; 357 | } 358 | 359 | 360 | /** 361 | * Generates term value. 362 | * 363 | * @param token Token with term value. 364 | * @return True if it was successful, false otherwise. 365 | */ 366 | static bool generate_term_value(Token token) 367 | { 368 | char term_str[MAX_DIGITS]; 369 | unsigned char c; 370 | 371 | Dynamic_string tmp_string; 372 | if (!dynamic_string_init(&tmp_string)) return false; 373 | 374 | switch (token.type) 375 | { 376 | case TOKEN_TYPE_INT_NUMBER: 377 | sprintf(term_str, "%d", token.attribute.integer); 378 | ADD_CODE("int@"); ADD_CODE(term_str); 379 | break; 380 | 381 | case TOKEN_TYPE_DOUBLE_NUMBER: 382 | sprintf(term_str, "%g", token.attribute.decimal); 383 | ADD_CODE("float@"); ADD_CODE(term_str); 384 | break; 385 | 386 | case TOKEN_TYPE_STRING: 387 | for (int i = 0; (c = (unsigned char) (token.attribute.string->str)[i]) != '\0'; i++) 388 | { 389 | if (c == '#' || c == '\\' || c <= 32 || !isprint(c)) 390 | { 391 | dynamic_string_add_char(&tmp_string, '\\'); 392 | sprintf(term_str, "%03d", c); 393 | dynamic_string_add_const_str(&tmp_string, term_str); 394 | } 395 | else 396 | { 397 | dynamic_string_add_char(&tmp_string, c); 398 | } 399 | } 400 | ADD_CODE("string@"); ADD_CODE(tmp_string.str); 401 | break; 402 | 403 | case TOKEN_TYPE_IDENTIFIER: 404 | ADD_CODE("LF@"); ADD_CODE(token.attribute.string->str); 405 | break; 406 | 407 | default: 408 | dynamic_string_free(&tmp_string); 409 | return false; 410 | } 411 | 412 | dynamic_string_free(&tmp_string); 413 | 414 | return true; 415 | } 416 | 417 | 418 | bool generate_function_pass_param(Token token, int index) 419 | { 420 | ADD_CODE("DEFVAR TF@%"); ADD_CODE_INT(index); ADD_CODE("\n"); 421 | 422 | ADD_CODE("MOVE TF@%"); ADD_CODE_INT(index); ADD_CODE(" "); 423 | if (!generate_term_value(token)) return false; 424 | ADD_CODE("\n"); 425 | 426 | return true; 427 | } 428 | 429 | 430 | bool generate_function_return(char *function_id) 431 | { 432 | ADD_INST("MOVE LF@%retval GF@%exp_result"); 433 | ADD_CODE("JUMP $"); ADD_CODE(function_id); ADD_CODE("%return\n"); 434 | 435 | return true; 436 | } 437 | 438 | 439 | bool generate_var_declare(char *var_id) 440 | { 441 | ADD_CODE("DEFVAR LF@"); ADD_CODE(var_id); ADD_CODE("\n"); 442 | 443 | return true; 444 | } 445 | 446 | 447 | bool generate_var_default_value(char *var_id, Data_type type) 448 | { 449 | ADD_CODE("MOVE LF@"); ADD_CODE(var_id); ADD_CODE(" "); 450 | if (!generate_default_var_value(type)) return false; 451 | ADD_CODE("\n"); 452 | 453 | return true; 454 | } 455 | 456 | 457 | bool generate_input(char *var_id, Data_type type) 458 | { 459 | ADD_INST("WRITE GF@%input_prompt"); 460 | 461 | ADD_CODE("READ LF@"); ADD_CODE(var_id); ADD_CODE(" "); 462 | switch (type) 463 | { 464 | case TYPE_INT: 465 | ADD_CODE("int"); 466 | break; 467 | 468 | case TYPE_DOUBLE: 469 | ADD_CODE("float"); 470 | break; 471 | 472 | case TYPE_STRING: 473 | ADD_CODE("string"); 474 | break; 475 | 476 | default: 477 | return false; 478 | } 479 | ADD_CODE("\n"); 480 | 481 | return true; 482 | } 483 | 484 | 485 | bool generate_print() 486 | { 487 | ADD_INST("WRITE GF@%exp_result"); 488 | 489 | return true; 490 | } 491 | 492 | 493 | bool generate_push(Token token) 494 | { 495 | ADD_CODE("PUSHS "); 496 | if (!generate_term_value(token)) return false; 497 | ADD_CODE("\n"); 498 | 499 | return true; 500 | } 501 | 502 | 503 | bool generate_stack_operation(Prec_rules_enum rule) 504 | { 505 | switch (rule) 506 | { 507 | case NT_PLUS_NT: 508 | ADD_INST("ADDS"); 509 | break; 510 | 511 | case NT_MINUS_NT: 512 | ADD_INST("SUBS"); 513 | break; 514 | 515 | case NT_MUL_NT: 516 | ADD_INST("MULS"); 517 | break; 518 | 519 | case NT_DIV_NT: 520 | ADD_INST("DIVS"); 521 | break; 522 | 523 | case NT_IDIV_NT: 524 | ADD_INST("POPS GF@%tmp_op1"); 525 | ADD_INST("INT2FLOATS"); 526 | ADD_INST("PUSHS GF@%tmp_op1"); 527 | ADD_INST("INT2FLOATS"); 528 | ADD_INST("DIVS"); 529 | ADD_INST("FLOAT2INTS"); 530 | break; 531 | 532 | case NT_EQ_NT: 533 | ADD_INST("EQS"); 534 | break; 535 | 536 | case NT_NEQ_NT: 537 | ADD_INST("EQS"); 538 | ADD_INST("NOTS"); 539 | break; 540 | 541 | case NT_LEQ_NT: 542 | ADD_INST("POPS GF@%tmp_op1"); 543 | ADD_INST("POPS GF@%tmp_op2"); 544 | ADD_INST("PUSHS GF@%tmp_op2"); 545 | ADD_INST("PUSHS GF@%tmp_op1"); 546 | ADD_INST("LTS"); 547 | ADD_INST("PUSHS GF@%tmp_op2"); 548 | ADD_INST("PUSHS GF@%tmp_op1"); 549 | ADD_INST("EQS"); 550 | ADD_INST("ORS"); 551 | break; 552 | 553 | case NT_LTN_NT: 554 | ADD_INST("LTS"); 555 | break; 556 | 557 | case NT_MEQ_NT: 558 | ADD_INST("POPS GF@%tmp_op1"); 559 | ADD_INST("POPS GF@%tmp_op2"); 560 | ADD_INST("PUSHS GF@%tmp_op2"); 561 | ADD_INST("PUSHS GF@%tmp_op1"); 562 | ADD_INST("GTS"); 563 | ADD_INST("PUSHS GF@%tmp_op2"); 564 | ADD_INST("PUSHS GF@%tmp_op1"); 565 | ADD_INST("EQS"); 566 | ADD_INST("ORS"); 567 | break; 568 | 569 | case NT_MTN_NT: 570 | ADD_INST("GTS"); 571 | break; 572 | 573 | default: 574 | break; 575 | } 576 | 577 | return true; 578 | } 579 | 580 | 581 | bool generate_concat_stack_strings() 582 | { 583 | ADD_INST("POPS GF@%tmp_op3"); 584 | ADD_INST("POPS GF@%tmp_op2"); 585 | ADD_INST("CONCAT GF@%tmp_op1 GF@%tmp_op2 GF@%tmp_op3"); 586 | ADD_INST("PUSHS GF@%tmp_op1"); 587 | 588 | return true; 589 | } 590 | 591 | 592 | bool generate_save_expression_result(char *var_id, Data_type ret_type, Data_type l_type, char *frame) 593 | { 594 | if (l_type == TYPE_INT && ret_type == TYPE_DOUBLE) 595 | { 596 | ADD_INST("FLOAT2R2EINTS"); 597 | } 598 | else if (l_type == TYPE_DOUBLE && ret_type == TYPE_INT) 599 | { 600 | ADD_INST("INT2FLOATS"); 601 | } 602 | 603 | ADD_CODE("POPS "); ADD_CODE(frame); ADD_CODE("@"); ADD_CODE(var_id); ADD_CODE("\n"); 604 | 605 | return true; 606 | } 607 | 608 | 609 | bool generate_stack_op1_to_double() 610 | { 611 | ADD_INST("INT2FLOATS"); 612 | 613 | return true; 614 | } 615 | 616 | 617 | bool generate_stack_op1_to_integer() 618 | { 619 | ADD_INST("FLOAT2R2EINTS"); 620 | 621 | return true; 622 | } 623 | 624 | 625 | bool generate_stack_op2_to_double() 626 | { 627 | ADD_INST("POPS GF@%tmp_op1"); 628 | ADD_INST("INT2FLOATS"); 629 | ADD_INST("PUSHS GF@%tmp_op1"); 630 | 631 | return true; 632 | } 633 | 634 | 635 | bool generate_stack_op2_to_integer() 636 | { 637 | ADD_INST("POPS GF@%tmp_op1"); 638 | ADD_INST("FLOAT2R2EINTS"); 639 | ADD_INST("PUSHS GF@%tmp_op1"); 640 | 641 | return true; 642 | } 643 | 644 | 645 | /** 646 | * Generates label. 647 | * 648 | * @param function_id Identifier of function in which is this label. 649 | * @param label_index Index of label. 650 | * @param label_deep Deep of label. 651 | * @return True if it was successful, false otherwise. 652 | */ 653 | static bool generate_label(char *function_id, int label_index, int label_deep) 654 | { 655 | ADD_CODE("LABEL $"); ADD_CODE(function_id); ADD_CODE("%"); ADD_CODE_INT(label_deep); 656 | ADD_CODE("%"); ADD_CODE_INT(label_index); ADD_CODE("\n"); 657 | 658 | return true; 659 | } 660 | 661 | 662 | bool generate_if_head() 663 | { 664 | ADD_INST("\n# If Then"); 665 | 666 | return true; 667 | } 668 | 669 | 670 | bool generate_if_start(char *function_id, int label_index, int label_deep) 671 | { 672 | ADD_CODE("JUMPIFEQ $"); ADD_CODE(function_id); ADD_CODE("%"); ADD_CODE_INT(label_deep); 673 | ADD_CODE("%"); ADD_CODE_INT(label_index); ADD_CODE(" GF@%exp_result bool@false\n"); 674 | 675 | return true; 676 | } 677 | 678 | 679 | bool generate_if_else_part(char *function_id, int label_index, int label_deep) 680 | { 681 | ADD_CODE("JUMP $"); ADD_CODE(function_id); ADD_CODE("%"); ADD_CODE_INT(label_deep); 682 | ADD_CODE("%"); ADD_CODE_INT(label_index + 1); ADD_CODE("\n"); 683 | 684 | ADD_INST("# Else"); 685 | 686 | if (!generate_label(function_id, label_index, label_deep)) return false; 687 | 688 | return true; 689 | } 690 | 691 | 692 | bool generate_if_end(char *function_id, int label_index, int label_deep) 693 | { 694 | ADD_INST("# End If"); 695 | 696 | if (!generate_label(function_id, label_index, label_deep)) return false; 697 | 698 | return true; 699 | } 700 | 701 | 702 | bool generate_while_head(char *function_id, int label_index, int label_deep) 703 | { 704 | ADD_INST("\n# Do While"); 705 | 706 | if (!generate_label(function_id, label_index, label_deep)) return false; 707 | 708 | return true; 709 | } 710 | 711 | 712 | bool generate_while_start(char *function_id, int label_index, int label_deep) 713 | { 714 | ADD_CODE("JUMPIFEQ $"); ADD_CODE(function_id); ADD_CODE("%"); ADD_CODE_INT(label_deep); 715 | ADD_CODE("%"); ADD_CODE_INT(label_index); ADD_CODE(" GF@%exp_result bool@false"); ADD_CODE("\n"); 716 | 717 | return true; 718 | } 719 | 720 | 721 | bool generate_while_end(char *function_id, int label_index, int label_deep) 722 | { 723 | ADD_CODE("JUMP $"); ADD_CODE(function_id); ADD_CODE("%"); ADD_CODE_INT(label_deep); 724 | ADD_CODE("%"); ADD_CODE_INT(label_index - 1); ADD_CODE("\n"); 725 | 726 | ADD_INST("# Loop"); 727 | 728 | if (!generate_label(function_id, label_index, label_deep)) return false; 729 | 730 | return true; 731 | } 732 | -------------------------------------------------------------------------------- /code_generator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Code generator interface. 5 | * 6 | * @author Dominik Harmim 7 | */ 8 | 9 | 10 | #ifndef _CODE_GENERATOR_H 11 | #define _CODE_GENERATOR_H 12 | 13 | 14 | #include 15 | #include 16 | 17 | #include "symtable.h" 18 | #include "scanner.h" 19 | #include "expression.h" 20 | 21 | 22 | /** 23 | * Inicialization of code generator. 24 | * Define built-in functions, etc. 25 | * 26 | * @return True if it was successful, false otherwise. 27 | */ 28 | bool code_generator_start(); 29 | 30 | /** 31 | * Clear resources. 32 | */ 33 | void code_generator_clear(); 34 | 35 | /** 36 | * Flush generated code to destination file. 37 | * 38 | * @param destination_file Pointer to destination file. 39 | */ 40 | void code_generator_flush(FILE *destination_file); 41 | 42 | /** 43 | * Generates start of main scope. 44 | * 45 | * @return True if it was successful, false otherwise. 46 | */ 47 | bool generate_main_scope_start(); 48 | 49 | /** 50 | * Generates end of main scope. 51 | * 52 | * @return True if it was successful, false otherwise. 53 | */ 54 | bool generate_main_scope_end(); 55 | 56 | /** 57 | * Generates start of function. 58 | * 59 | * @param function_id Function identifier. 60 | * @return True if it was successful, false otherwise. 61 | */ 62 | bool generate_function_start(char *function_id); 63 | 64 | /** 65 | * Generates end of function. 66 | * 67 | * @param function_id Function identifier. 68 | * @return True if it was successful, false otherwise. 69 | */ 70 | bool generate_function_end(char *function_id); 71 | 72 | /** 73 | * Generates declaration and definition of function return value. 74 | * 75 | * @param type Data type of function return value. 76 | * @return True if it was successful, false otherwise. 77 | */ 78 | bool generate_function_retval(Data_type type); 79 | 80 | /** 81 | * Generates function call (jump to the function). 82 | * 83 | * @param function_id Function identifier. 84 | * @return True if it was successful, false otherwise. 85 | */ 86 | bool generate_function_call(char *function_id); 87 | 88 | /** 89 | * Generates assignment function return value of L-variable. 90 | * 91 | * @param l_val_id Identifier of L-variable. 92 | * @param l_type L-variable data type. 93 | * @param ret_type Data type of function return value. 94 | * @return True if it was successful, false otherwise. 95 | */ 96 | bool generate_function_retval_assign(char *l_val_id, Data_type l_type, Data_type ret_type); 97 | 98 | /** 99 | * Generates local variable for function parameter. 100 | * 101 | * @param param_id Parameter identifier. 102 | * @param index Parameter index (position). 103 | * @return True if it was successful, false otherwise. 104 | */ 105 | bool generate_function_param_declare(char *param_id, int index); 106 | 107 | /** 108 | * Generates code for preparation for pass parameters to function. 109 | * 110 | * @return True if it was successful, false otherwise. 111 | */ 112 | bool generate_function_before_pass_params(); 113 | 114 | /** 115 | * Generates passing parameter to function. 116 | * 117 | * @param token Token with passed parameter value. 118 | * @param index Parameter index (position). 119 | * @return True if it was successful, false otherwise. 120 | */ 121 | bool generate_function_pass_param(Token token, int index); 122 | 123 | /** 124 | * Generates convert passed parameter data type. 125 | * 126 | * @param from Data type to convert from. 127 | * @param to Data type to conver to. 128 | * @param index Parameter index (position). 129 | * @return True if it was successful, false otherwise. 130 | */ 131 | bool generate_function_convert_passed_param(Data_type from, Data_type to, int index); 132 | 133 | /** 134 | * Generates return statement in function. 135 | * 136 | * @param function_id Function identifier. 137 | * @return True if it was successful, false otherwise. 138 | */ 139 | bool generate_function_return(char *function_id); 140 | 141 | /** 142 | * Generates variable declaration. 143 | * 144 | * @param var_id Variable identifier. 145 | * @return True if it was successful, false otherwise. 146 | */ 147 | bool generate_var_declare(char *var_id); 148 | 149 | /** 150 | * Generates assignment default value to variable. 151 | * 152 | * @param var_id Variable identifier. 153 | * @param type Data type of variable. 154 | * @return True if it was successful, false otherwise. 155 | */ 156 | bool generate_var_default_value(char *var_id, Data_type type); 157 | 158 | /** 159 | * Generates input statement. 160 | * 161 | * @param var_id Identifier of variable read value. 162 | * @param type Data type of read value. 163 | * @return True if it was successful, false otherwise. 164 | */ 165 | bool generate_input(char *var_id, Data_type type); 166 | 167 | /** 168 | * Generates print of expression result. 169 | * 170 | * @return True if it was successful, false otherwise. 171 | */ 172 | bool generate_print(); 173 | 174 | /** 175 | * Generates push value to data stack. 176 | * 177 | * @param token Token with value to be pushed. 178 | * @return True if it was successful, false otherwise. 179 | */ 180 | bool generate_push(Token token); 181 | 182 | /** 183 | * Generates operation with top data stack items. 184 | * 185 | * @param rule Expression rule. 186 | * @return True if it was successful, false otherwise. 187 | */ 188 | bool generate_stack_operation(Prec_rules_enum rule); 189 | 190 | /** 191 | * Generates concatition of top data stack items. 192 | * 193 | * @return True if it was successful, false otherwise. 194 | */ 195 | bool generate_concat_stack_strings(); 196 | 197 | /** 198 | * Generates save expression result from data stack. 199 | * 200 | * @param var_id Variable identifier for expression result. 201 | * @param ret_type Data type of expression in data stack. 202 | * @param l_type Data type of variable for save result. 203 | * @param frame Variable frame of given variable. 204 | * @return True if it was successful, false otherwise. 205 | */ 206 | bool generate_save_expression_result(char *var_id, Data_type ret_type, Data_type l_type, char *frame); 207 | 208 | /** 209 | * Generates convert data stack top item to double. 210 | * 211 | * @return True if it was successful, false otherwise. 212 | */ 213 | bool generate_stack_op1_to_double(); 214 | 215 | /** 216 | * Generates convert data stack top item to integer. 217 | * 218 | * @return True if it was successful, false otherwise. 219 | */ 220 | bool generate_stack_op1_to_integer(); 221 | 222 | /** 223 | * Generates convert data stack top-1 item to double. 224 | * 225 | * @return True if it was successful, false otherwise. 226 | */ 227 | bool generate_stack_op2_to_double(); 228 | 229 | /** 230 | * Generates convert data stack top-1 item to integer. 231 | * 232 | * @return True if it was successful, false otherwise. 233 | */ 234 | bool generate_stack_op2_to_integer(); 235 | 236 | /** 237 | * Generates If-Then head (before processing If-Then expression). 238 | * 239 | * @return True if it was successful, false otherwise. 240 | */ 241 | bool generate_if_head(); 242 | 243 | /** 244 | * Generates If-Then start (after processing If-Then expression). 245 | * 246 | * @param function_id Identifier of function in which is this statement. 247 | * @param label_index Index of label. 248 | * @param label_deep Deep of label. 249 | * @return True if it was successful, false otherwise. 250 | */ 251 | bool generate_if_start(char *function_id, int label_index, int label_deep); 252 | 253 | /** 254 | * Generates If-Then Else part (after processing If-Then statement). 255 | * 256 | * @param function_id Identifier of function in which is this statement. 257 | * @param label_index Index of label. 258 | * @param label_deep Deep of label. 259 | * @return True if it was successful, false otherwise. 260 | */ 261 | bool generate_if_else_part(char *function_id, int label_index, int label_deep); 262 | 263 | /** 264 | * Generates If-Then end (after processing If-Then Else statement). 265 | * 266 | * @param function_id Identifier of function in which is this statement. 267 | * @param label_index Index of label. 268 | * @param label_deep Deep of label. 269 | * @return True if it was successful, false otherwise. 270 | */ 271 | bool generate_if_end(char *function_id, int label_index, int label_deep); 272 | 273 | /** 274 | * Generates Do-While head (before processing expression). 275 | * 276 | * @param function_id Identifier of function in which is this statement. 277 | * @param label_index Index of label. 278 | * @param label_deep Deep of label. 279 | * @return True if it was successful, false otherwise. 280 | */ 281 | bool generate_while_head(char *function_id, int label_index, int label_deep); 282 | 283 | /** 284 | * Generates Do-While head (after processing expression). 285 | * 286 | * @param function_id Identifier of function in which is this statement. 287 | * @param label_index Index of label. 288 | * @param label_deep Deep of label. 289 | * @return True if it was successful, false otherwise. 290 | */ 291 | bool generate_while_start(char *function_id, int label_index, int label_deep); 292 | 293 | /** 294 | * Generates Do-While head (after processing statement). 295 | * 296 | * @param function_id Identifier of function in which is this statement. 297 | * @param label_index Index of label. 298 | * @param label_deep Deep of label. 299 | * @return True if it was successful, false otherwise. 300 | */ 301 | bool generate_while_end(char *function_id, int label_index, int label_deep); 302 | 303 | 304 | #endif //_CODE_GENERATOR_H 305 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Core latex/pdflatex auxiliary files: 2 | /*.aux 3 | /*.fls 4 | /*.log 5 | 6 | ## Build tool auxiliary files: 7 | /*.fdb_latexmk 8 | /*.synctex.gz 9 | 10 | ## Intermediate documents: 11 | /*.dvi 12 | /*.ps 13 | 14 | # Converted images 15 | *-eps-converted-to* 16 | 17 | # Bibtex 18 | /*.blg 19 | /*.bbl 20 | /*.out 21 | 22 | /*.toc 23 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Author: Dominik Harmim 2 | 3 | DOC = dokumentace 4 | 5 | INC_DIR = inc 6 | 7 | 8 | $(DOC).pdf: $(DOC).tex 9 | pdflatex $< 10 | make bibtex 11 | pdflatex $< 12 | pdflatex $< 13 | 14 | 15 | .PHONY: bibtex 16 | bibtex: $(DOC).bib 17 | bibtex $(DOC) 18 | 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -f $(DOC).aux $(DOC).dvi $(DOC).log $(DOC).ps $(DOC).synctex.gz \ 23 | $(DOC).fls $(DOC).fdb_latexmk $(DOC).bbl $(DOC).blg $(DOC).out \ 24 | $(INC_DIR)/*-eps-converted-to* $(DOC).toc 25 | -------------------------------------------------------------------------------- /doc/czechiso.bst: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `czechiso.bst', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% merlin.mbs (with options: `head,exlang,lang,vonx,nm-rev,jnrlst,aunm-semi,nmlm,x3,m3,atit-u,vnum-nr,volp-sp,jwdpg,pp-last,jwdvol,num-xser,ser-vol,ser-ed,pg-bk,add-pub,isbn,issn,doi,edpar,edby,blk-tit,au-col,čeština,pp,and-xcom,xand,eprint,url,url-nl,nfss,,{}') 8 | %% czech.mbs (with options: `exlang,lang,vonx,nm-rev,jnrlst,aunm-semi,nmlm,x3,m3,atit-u,vnum-nr,volp-sp,jwdpg,pp-last,jwdvol,num-xser,ser-vol,ser-ed,pg-bk,add-pub,isbn,issn,doi,edpar,edby,blk-tit,au-col,čeština,pp,and-xcom,xand,eprint,url,url-nl,nfss,,{}') 9 | %% merlin.mbs (with options: `tail,exlang,lang,vonx,nm-rev,jnrlst,aunm-semi,nmlm,x3,m3,atit-u,vnum-nr,volp-sp,jwdpg,pp-last,jwdvol,num-xser,ser-vol,ser-ed,pg-bk,add-pub,isbn,issn,doi,edpar,edby,blk-tit,au-col,čeština,pp,and-xcom,xand,eprint,url,url-nl,nfss,,{}') 10 | %% ---------------------------------------- 11 | %% *** Styl snažící se být v souladu s normou ISO 690. Pomocí latex makebst vytvořil David Martinek. *** 12 | %% 13 | %% Copyright 1994-2004 Patrick W Daly 14 | % =============================================================== 15 | % IMPORTANT NOTICE: 16 | % This bibliographic style (bst) file has been generated from one or 17 | % more master bibliographic style (mbs) files, listed above. 18 | % 19 | % This generated file can be redistributed and/or modified under the terms 20 | % of the LaTeX Project Public License Distributed from CTAN 21 | % archives in directory macros/latex/base/lppl.txt; either 22 | % version 1 of the License, or any later version. 23 | % =============================================================== 24 | % Name and version information of the main mbs file: 25 | % \ProvidesFile{merlin.mbs}[2004/02/09 4.13 (PWD, AO, DPC)] 26 | % For use with BibTeX version 0.99a or later 27 | %------------------------------------------------------------------- 28 | % This bibliography style file is intended for texts in 29 | % This is a numerical citation style, and as such is standard LaTeX. 30 | % It requires no extra package to interface to the main text. 31 | % The form of the \bibitem entries is 32 | % \bibitem{key}... 33 | % Usage of \cite is as follows: 34 | % \cite{key} ==>> [#] 35 | % \cite[chap. 2]{key} ==>> [#, chap. 2] 36 | % where # is a number determined by the ordering in the reference list. 37 | % The order in the reference list is alphabetical by authors. 38 | %--------------------------------------------------------------------- 39 | 40 | ENTRY 41 | { address 42 | archive 43 | author 44 | booktitle 45 | chapter 46 | doi 47 | edition 48 | editor 49 | eid 50 | eprint 51 | howpublished 52 | institution 53 | isbn 54 | issn 55 | journal 56 | key 57 | language 58 | month 59 | note 60 | number 61 | organization 62 | pages 63 | publisher 64 | school 65 | series 66 | title 67 | type 68 | url 69 | volume 70 | year 71 | } 72 | {} 73 | { label } 74 | INTEGERS { output.state before.all mid.sentence after.sentence after.block } 75 | FUNCTION {init.state.consts} 76 | { #0 'before.all := 77 | #1 'mid.sentence := 78 | #2 'after.sentence := 79 | #3 'after.block := 80 | } 81 | STRINGS { s t} 82 | FUNCTION {output.nonnull} 83 | { 's := 84 | output.state mid.sentence = 85 | { ", " * write$ } 86 | { output.state after.block = 87 | { add.period$ write$ 88 | newline$ 89 | "\newblock " write$ 90 | } 91 | { output.state before.all = 92 | 'write$ 93 | { add.period$ " " * write$ } 94 | if$ 95 | } 96 | if$ 97 | mid.sentence 'output.state := 98 | } 99 | if$ 100 | s 101 | } 102 | FUNCTION {output} 103 | { duplicate$ empty$ 104 | 'pop$ 105 | 'output.nonnull 106 | if$ 107 | } 108 | FUNCTION {output.check} 109 | { 't := 110 | duplicate$ empty$ 111 | { pop$ "empty " t * " in " * cite$ * warning$ } 112 | 'output.nonnull 113 | if$ 114 | } 115 | FUNCTION {fin.entry} 116 | { add.period$ 117 | write$ 118 | newline$ 119 | } 120 | 121 | FUNCTION {new.block} 122 | { output.state before.all = 123 | 'skip$ 124 | { after.block 'output.state := } 125 | if$ 126 | } 127 | FUNCTION {new.sentence} 128 | { output.state after.block = 129 | 'skip$ 130 | { output.state before.all = 131 | 'skip$ 132 | { after.sentence 'output.state := } 133 | if$ 134 | } 135 | if$ 136 | } 137 | FUNCTION {add.blank} 138 | { " " * before.all 'output.state := 139 | } 140 | 141 | FUNCTION {add.colon} 142 | { duplicate$ empty$ 143 | 'skip$ 144 | { ":" * add.blank } 145 | if$ 146 | } 147 | 148 | FUNCTION {date.block} 149 | { 150 | skip$ 151 | } 152 | 153 | FUNCTION {not} 154 | { { #0 } 155 | { #1 } 156 | if$ 157 | } 158 | FUNCTION {and} 159 | { 'skip$ 160 | { pop$ #0 } 161 | if$ 162 | } 163 | FUNCTION {or} 164 | { { pop$ #1 } 165 | 'skip$ 166 | if$ 167 | } 168 | FUNCTION {new.block.checka} 169 | { empty$ 170 | 'skip$ 171 | 'new.block 172 | if$ 173 | } 174 | FUNCTION {new.block.checkb} 175 | { empty$ 176 | swap$ empty$ 177 | and 178 | 'skip$ 179 | 'new.block 180 | if$ 181 | } 182 | FUNCTION {new.sentence.checka} 183 | { empty$ 184 | 'skip$ 185 | 'new.sentence 186 | if$ 187 | } 188 | FUNCTION {new.sentence.checkb} 189 | { empty$ 190 | swap$ empty$ 191 | and 192 | 'skip$ 193 | 'new.sentence 194 | if$ 195 | } 196 | FUNCTION {field.or.null} 197 | { duplicate$ empty$ 198 | { pop$ "" } 199 | 'skip$ 200 | if$ 201 | } 202 | FUNCTION {emphasize} 203 | { duplicate$ empty$ 204 | { pop$ "" } 205 | { "\emph{" swap$ * "}" * } 206 | if$ 207 | } 208 | FUNCTION {tie.or.space.prefix} 209 | { duplicate$ text.length$ #3 < 210 | { "~" } 211 | { " " } 212 | if$ 213 | swap$ 214 | } 215 | 216 | FUNCTION {capitalize} 217 | { "u" change.case$ "t" change.case$ } 218 | 219 | FUNCTION {space.word} 220 | { " " swap$ * " " * } 221 | % Here are the language-specific definitions for explicit words. 222 | % Each function has a name bbl.xxx where xxx is the English word. 223 | %------------------------------------------------------------------- 224 | % Begin module: 225 | % \ProvidesFile{czech.mbs}[2006/03/31 1.0 (DM)] 226 | 227 | % The language selected here is CZECH 228 | FUNCTION {bbl.and} 229 | { "a"} 230 | 231 | FUNCTION {bbl.etal} 232 | { "aj." } 233 | 234 | FUNCTION {bbl.editors} 235 | { "editoři" } 236 | 237 | FUNCTION {bbl.editor} 238 | { "editor" } 239 | 240 | FUNCTION {bbl.edby} 241 | { "editace" } 242 | 243 | FUNCTION {bbl.edition} 244 | { "vydání" } 245 | 246 | FUNCTION {bbl.volume} 247 | { "ročník" } 248 | 249 | FUNCTION {bbl.of} 250 | { "" } 251 | 252 | FUNCTION {bbl.number} 253 | { "číslo" } 254 | 255 | FUNCTION {bbl.nr} 256 | { "č." } 257 | 258 | FUNCTION {bbl.in} 259 | { "in" } 260 | 261 | FUNCTION {bbl.pages} 262 | { "s." } 263 | 264 | FUNCTION {bbl.page} 265 | { "str." } 266 | 267 | FUNCTION {bbl.chapter} 268 | { "kapitola" } 269 | 270 | FUNCTION {bbl.techrep} 271 | { "Technická Zpráva" } 272 | 273 | FUNCTION {bbl.mthesis} 274 | { "Diplomová práce" } 275 | 276 | FUNCTION {bbl.phdthesis} 277 | { "Dizertační práce" } 278 | 279 | FUNCTION {bbl.first} 280 | { "První" } 281 | 282 | FUNCTION {bbl.second} 283 | { "Druhé" } 284 | 285 | FUNCTION {bbl.third} 286 | { "Třetí" } 287 | 288 | FUNCTION {bbl.fourth} 289 | { "Čtvrté" } 290 | 291 | FUNCTION {bbl.fifth} 292 | { "Páté" } 293 | 294 | FUNCTION {bbl.st} 295 | { "" } 296 | 297 | FUNCTION {bbl.nd} 298 | { "" } 299 | 300 | FUNCTION {bbl.rd} 301 | { "" } 302 | 303 | FUNCTION {bbl.th} 304 | { "" } 305 | 306 | MACRO {jan} {"Leden"} 307 | 308 | MACRO {feb} {"Únor"} 309 | 310 | MACRO {mar} {"Březen"} 311 | 312 | MACRO {apr} {"Duben"} 313 | 314 | MACRO {may} {"Květen"} 315 | 316 | MACRO {jun} {"Červen"} 317 | 318 | MACRO {jul} {"Červenec"} 319 | 320 | MACRO {aug} {"Srpen"} 321 | 322 | MACRO {sep} {"Září"} 323 | 324 | MACRO {oct} {"Říjen"} 325 | 326 | MACRO {nov} {"Listopad"} 327 | 328 | MACRO {dec} {"Prosinec"} 329 | 330 | % End module: czech.mbs 331 | %% Copyright 1994-2004 Patrick W Daly 332 | MACRO {acmcs} {"ACM Computing Surveys"} 333 | 334 | MACRO {acta} {"Acta Informatica"} 335 | 336 | MACRO {cacm} {"Communications of the ACM"} 337 | 338 | MACRO {ibmjrd} {"IBM Journal of Research and Development"} 339 | 340 | MACRO {ibmsj} {"IBM Systems Journal"} 341 | 342 | MACRO {ieeese} {"IEEE Transactions on Software Engineering"} 343 | 344 | MACRO {ieeetc} {"IEEE Transactions on Computers"} 345 | 346 | MACRO {ieeetcad} 347 | {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"} 348 | 349 | MACRO {ipl} {"Information Processing Letters"} 350 | 351 | MACRO {jacm} {"Journal of the ACM"} 352 | 353 | MACRO {jcss} {"Journal of Computer and System Sciences"} 354 | 355 | MACRO {scp} {"Science of Computer Programming"} 356 | 357 | MACRO {sicomp} {"SIAM Journal on Computing"} 358 | 359 | MACRO {tocs} {"ACM Transactions on Computer Systems"} 360 | 361 | MACRO {tods} {"ACM Transactions on Database Systems"} 362 | 363 | MACRO {tog} {"ACM Transactions on Graphics"} 364 | 365 | MACRO {toms} {"ACM Transactions on Mathematical Software"} 366 | 367 | MACRO {toois} {"ACM Transactions on Office Information Systems"} 368 | 369 | MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"} 370 | 371 | MACRO {tcs} {"Theoretical Computer Science"} 372 | FUNCTION {bibinfo.check} 373 | { swap$ 374 | duplicate$ missing$ 375 | { 376 | pop$ pop$ 377 | "" 378 | } 379 | { duplicate$ empty$ 380 | { 381 | swap$ pop$ 382 | } 383 | { swap$ 384 | pop$ 385 | } 386 | if$ 387 | } 388 | if$ 389 | } 390 | FUNCTION {bibinfo.warn} 391 | { swap$ 392 | duplicate$ missing$ 393 | { 394 | swap$ "missing " swap$ * " in " * cite$ * warning$ pop$ 395 | "" 396 | } 397 | { duplicate$ empty$ 398 | { 399 | swap$ "empty " swap$ * " in " * cite$ * warning$ 400 | } 401 | { swap$ 402 | pop$ 403 | } 404 | if$ 405 | } 406 | if$ 407 | } 408 | FUNCTION {format.eprint} 409 | { eprint duplicate$ empty$ 410 | 'skip$ 411 | { "\eprint" 412 | archive empty$ 413 | 'skip$ 414 | { "[" * archive * "]" * } 415 | if$ 416 | "{" * swap$ * "}" * 417 | } 418 | if$ 419 | } 420 | FUNCTION {write.url} 421 | { url empty$ 422 | { skip$ } 423 | { "\urlprefix\url{" url * "}" * write$ newline$ } 424 | if$ 425 | } 426 | 427 | STRINGS { bibinfo} 428 | INTEGERS { nameptr namesleft numnames } 429 | 430 | FUNCTION {format.names} 431 | { 'bibinfo := 432 | duplicate$ empty$ 'skip$ { 433 | 's := 434 | "" 't := 435 | #1 'nameptr := 436 | s num.names$ 'numnames := 437 | numnames 'namesleft := 438 | { namesleft #0 > } 439 | { s nameptr 440 | "{vv~}{ll}{, f.}{, jj}" 441 | format.name$ 442 | bibinfo bibinfo.check 443 | 't := 444 | nameptr #1 > 445 | { 446 | nameptr #3 447 | #1 + = 448 | numnames #3 449 | > and 450 | { "others" 't := 451 | #1 'namesleft := } 452 | 'skip$ 453 | if$ 454 | namesleft #1 > 455 | { "; " * t * } 456 | { 457 | ";" * 458 | s nameptr "{ll}" format.name$ duplicate$ "others" = 459 | { 't := } 460 | { pop$ } 461 | if$ 462 | t "others" = 463 | { 464 | " " * bbl.etal * 465 | } 466 | { " " * t * } 467 | if$ 468 | } 469 | if$ 470 | } 471 | 't 472 | if$ 473 | nameptr #1 + 'nameptr := 474 | namesleft #1 - 'namesleft := 475 | } 476 | while$ 477 | } if$ 478 | } 479 | FUNCTION {format.names.ed} 480 | { 481 | 'bibinfo := 482 | duplicate$ empty$ 'skip$ { 483 | 's := 484 | "" 't := 485 | #1 'nameptr := 486 | s num.names$ 'numnames := 487 | numnames 'namesleft := 488 | { namesleft #0 > } 489 | { s nameptr 490 | "{f.~}{vv~}{ll}{, jj}" 491 | format.name$ 492 | bibinfo bibinfo.check 493 | 't := 494 | nameptr #1 > 495 | { 496 | namesleft #1 > 497 | { "; " * t * } 498 | { 499 | ";" * 500 | s nameptr "{ll}" format.name$ duplicate$ "others" = 501 | { 't := } 502 | { pop$ } 503 | if$ 504 | t "others" = 505 | { 506 | 507 | " " * bbl.etal * 508 | } 509 | { " " * t * } 510 | if$ 511 | } 512 | if$ 513 | } 514 | 't 515 | if$ 516 | nameptr #1 + 'nameptr := 517 | namesleft #1 - 'namesleft := 518 | } 519 | while$ 520 | } if$ 521 | } 522 | FUNCTION {format.authors} 523 | { author "author" format.names 524 | } 525 | FUNCTION {get.bbl.editor} 526 | { editor num.names$ #1 > 'bbl.editors 'bbl.editor if$ } 527 | 528 | FUNCTION {format.editors} 529 | { editor "editor" format.names duplicate$ empty$ 'skip$ 530 | { 531 | " " * 532 | get.bbl.editor 533 | "(" swap$ * ")" * 534 | * 535 | } 536 | if$ 537 | } 538 | FUNCTION {format.book.pages} 539 | { pages "pages" bibinfo.check 540 | duplicate$ empty$ 'skip$ 541 | { " " * bbl.pages * } 542 | if$ 543 | } 544 | FUNCTION {format.isbn} 545 | { isbn "isbn" bibinfo.check 546 | duplicate$ empty$ 'skip$ 547 | { 548 | "ISBN " swap$ * 549 | } 550 | if$ 551 | } 552 | 553 | FUNCTION {format.issn} 554 | { issn "issn" bibinfo.check 555 | duplicate$ empty$ 'skip$ 556 | { 557 | "ISSN " swap$ * 558 | } 559 | if$ 560 | } 561 | 562 | FUNCTION {format.doi} 563 | { doi "doi" bibinfo.check 564 | duplicate$ empty$ 'skip$ 565 | { 566 | "\doi{" swap$ * "}" * 567 | } 568 | if$ 569 | } 570 | FUNCTION {select.language} 571 | { duplicate$ empty$ 572 | 'pop$ 573 | { language empty$ 574 | 'skip$ 575 | { "{\selectlanguage{" language * "}" * swap$ * "}" * } 576 | if$ 577 | } 578 | if$ 579 | } 580 | 581 | FUNCTION {format.note} 582 | { 583 | note empty$ 584 | { "" } 585 | { note #1 #1 substring$ 586 | duplicate$ "{" = 587 | 'skip$ 588 | { output.state mid.sentence = 589 | { "l" } 590 | { "u" } 591 | if$ 592 | change.case$ 593 | } 594 | if$ 595 | note #2 global.max$ substring$ * "note" bibinfo.check 596 | } 597 | if$ 598 | } 599 | 600 | FUNCTION {format.title} 601 | { title 602 | "title" bibinfo.check 603 | duplicate$ empty$ 'skip$ 604 | { 605 | select.language 606 | } 607 | if$ 608 | } 609 | FUNCTION {output.bibitem} 610 | { newline$ 611 | "\bibitem{" write$ 612 | cite$ write$ 613 | "}" write$ 614 | newline$ 615 | "" 616 | before.all 'output.state := 617 | } 618 | 619 | FUNCTION {n.dashify} 620 | { 621 | 't := 622 | "" 623 | { t empty$ not } 624 | { t #1 #1 substring$ "-" = 625 | { t #1 #2 substring$ "--" = not 626 | { "--" * 627 | t #2 global.max$ substring$ 't := 628 | } 629 | { { t #1 #1 substring$ "-" = } 630 | { "-" * 631 | t #2 global.max$ substring$ 't := 632 | } 633 | while$ 634 | } 635 | if$ 636 | } 637 | { t #1 #1 substring$ * 638 | t #2 global.max$ substring$ 't := 639 | } 640 | if$ 641 | } 642 | while$ 643 | } 644 | 645 | FUNCTION {word.in} 646 | { bbl.in capitalize 647 | " " * } 648 | 649 | FUNCTION {format.date} 650 | { 651 | month "month" bibinfo.check 652 | duplicate$ empty$ 653 | year "year" bibinfo.check duplicate$ empty$ 654 | { swap$ 'skip$ 655 | { "there's a month but no year in " cite$ * warning$ } 656 | if$ 657 | * 658 | } 659 | { swap$ 'skip$ 660 | { 661 | swap$ 662 | " " * swap$ 663 | } 664 | if$ 665 | * 666 | } 667 | if$ 668 | } 669 | FUNCTION {format.btitle} 670 | { title "title" bibinfo.check 671 | duplicate$ empty$ 'skip$ 672 | { 673 | emphasize 674 | select.language 675 | } 676 | if$ 677 | } 678 | FUNCTION {either.or.check} 679 | { empty$ 680 | 'pop$ 681 | { "can't use both " swap$ * " fields in " * cite$ * warning$ } 682 | if$ 683 | } 684 | FUNCTION {format.bvolume} 685 | { volume empty$ 686 | { "" } 687 | { bbl.volume volume tie.or.space.prefix 688 | "volume" bibinfo.check * * 689 | series "series" bibinfo.check 690 | duplicate$ empty$ 'pop$ 691 | { emphasize ", " * swap$ * } 692 | if$ 693 | "volume and number" number either.or.check 694 | } 695 | if$ 696 | } 697 | FUNCTION {format.number.series} 698 | { volume empty$ 699 | { number empty$ 700 | { series field.or.null } 701 | { series empty$ 702 | { number "number" bibinfo.check } 703 | { output.state mid.sentence = 704 | { bbl.number } 705 | { bbl.number capitalize } 706 | if$ 707 | number tie.or.space.prefix "number" bibinfo.check * * 708 | bbl.in space.word * 709 | series "series" bibinfo.check * 710 | } 711 | if$ 712 | } 713 | if$ 714 | } 715 | { "" } 716 | if$ 717 | } 718 | FUNCTION {is.num} 719 | { chr.to.int$ 720 | duplicate$ "0" chr.to.int$ < not 721 | swap$ "9" chr.to.int$ > not and 722 | } 723 | 724 | FUNCTION {extract.num} 725 | { duplicate$ 't := 726 | "" 's := 727 | { t empty$ not } 728 | { t #1 #1 substring$ 729 | t #2 global.max$ substring$ 't := 730 | duplicate$ is.num 731 | { s swap$ * 's := } 732 | { pop$ "" 't := } 733 | if$ 734 | } 735 | while$ 736 | s empty$ 737 | 'skip$ 738 | { pop$ s } 739 | if$ 740 | } 741 | 742 | FUNCTION {convert.edition} 743 | { extract.num "l" change.case$ 's := 744 | s "first" = s "1" = or 745 | { bbl.first 't := } 746 | { s "second" = s "2" = or 747 | { bbl.second 't := } 748 | { s "third" = s "3" = or 749 | { bbl.third 't := } 750 | { s "fourth" = s "4" = or 751 | { bbl.fourth 't := } 752 | { s "fifth" = s "5" = or 753 | { bbl.fifth 't := } 754 | { s #1 #1 substring$ is.num 755 | { s bbl.th * 't := } 756 | { edition 't := } 757 | if$ 758 | } 759 | if$ 760 | } 761 | if$ 762 | } 763 | if$ 764 | } 765 | if$ 766 | } 767 | if$ 768 | t 769 | } 770 | 771 | FUNCTION {format.edition} 772 | { edition duplicate$ empty$ 'skip$ 773 | { 774 | convert.edition 775 | output.state mid.sentence = 776 | { "l" } 777 | { "t" } 778 | if$ change.case$ 779 | "edition" bibinfo.check 780 | " " * bbl.edition * 781 | } 782 | if$ 783 | } 784 | INTEGERS { multiresult } 785 | FUNCTION {multi.page.check} 786 | { 't := 787 | #0 'multiresult := 788 | { multiresult not 789 | t empty$ not 790 | and 791 | } 792 | { t #1 #1 substring$ 793 | duplicate$ "-" = 794 | swap$ duplicate$ "," = 795 | swap$ "+" = 796 | or or 797 | { #1 'multiresult := } 798 | { t #2 global.max$ substring$ 't := } 799 | if$ 800 | } 801 | while$ 802 | multiresult 803 | } 804 | FUNCTION {format.pages} 805 | { pages duplicate$ empty$ 'skip$ 806 | { duplicate$ multi.page.check 807 | { 808 | bbl.pages swap$ 809 | n.dashify 810 | } 811 | { 812 | bbl.page swap$ 813 | } 814 | if$ 815 | tie.or.space.prefix 816 | "pages" bibinfo.check 817 | * * 818 | } 819 | if$ 820 | } 821 | FUNCTION {format.journal.pages} 822 | { pages duplicate$ empty$ 'pop$ 823 | { swap$ duplicate$ empty$ 824 | { pop$ pop$ format.pages } 825 | { 826 | ": " * 827 | swap$ 828 | n.dashify 829 | pages multi.page.check 830 | 'bbl.pages 831 | 'bbl.page 832 | if$ 833 | swap$ tie.or.space.prefix 834 | "pages" bibinfo.check 835 | * * 836 | * 837 | } 838 | if$ 839 | } 840 | if$ 841 | } 842 | FUNCTION {format.journal.eid} 843 | { eid "eid" bibinfo.check 844 | duplicate$ empty$ 'pop$ 845 | { swap$ duplicate$ empty$ 'skip$ 846 | { 847 | ": " * 848 | } 849 | if$ 850 | swap$ * 851 | } 852 | if$ 853 | } 854 | FUNCTION {format.vol.num.pages} 855 | { volume field.or.null 856 | duplicate$ empty$ 'skip$ 857 | { 858 | bbl.volume swap$ tie.or.space.prefix 859 | "volume" bibinfo.check 860 | * * 861 | } 862 | if$ 863 | number "number" bibinfo.check duplicate$ empty$ 'skip$ 864 | { 865 | swap$ duplicate$ empty$ 866 | { "there's a number but no volume in " cite$ * warning$ } 867 | 'skip$ 868 | if$ 869 | swap$ 870 | ", " bbl.nr * number tie.or.space.prefix pop$ * swap$ * 871 | } 872 | if$ * 873 | } 874 | 875 | FUNCTION {format.chapter.pages} 876 | { chapter empty$ 877 | { "" } 878 | { type empty$ 879 | { bbl.chapter } 880 | { type "l" change.case$ 881 | "type" bibinfo.check 882 | } 883 | if$ 884 | chapter tie.or.space.prefix 885 | "chapter" bibinfo.check 886 | * * 887 | } 888 | if$ 889 | } 890 | 891 | FUNCTION {format.booktitle} 892 | { 893 | booktitle "booktitle" bibinfo.check 894 | emphasize 895 | } 896 | FUNCTION {format.in.ed.booktitle} 897 | { format.booktitle duplicate$ empty$ 'skip$ 898 | { 899 | format.bvolume duplicate$ empty$ 'pop$ 900 | { ", " swap$ * * } 901 | if$ 902 | editor "editor" format.names.ed duplicate$ empty$ 'pop$ 903 | { 904 | bbl.edby 905 | " " * swap$ * 906 | swap$ 907 | "," * 908 | " " * swap$ 909 | * } 910 | if$ 911 | word.in swap$ * 912 | } 913 | if$ 914 | } 915 | FUNCTION {empty.misc.check} 916 | { author empty$ title empty$ howpublished empty$ 917 | month empty$ year empty$ note empty$ 918 | and and and and and 919 | key empty$ not and 920 | { "all relevant fields are empty in " cite$ * warning$ } 921 | 'skip$ 922 | if$ 923 | } 924 | FUNCTION {format.thesis.type} 925 | { type duplicate$ empty$ 926 | 'pop$ 927 | { swap$ pop$ 928 | "t" change.case$ "type" bibinfo.check 929 | } 930 | if$ 931 | } 932 | FUNCTION {format.tr.number} 933 | { number "number" bibinfo.check 934 | type duplicate$ empty$ 935 | { pop$ bbl.techrep } 936 | 'skip$ 937 | if$ 938 | "type" bibinfo.check 939 | swap$ duplicate$ empty$ 940 | { pop$ "t" change.case$ } 941 | { tie.or.space.prefix * * } 942 | if$ 943 | } 944 | FUNCTION {format.article.crossref} 945 | { 946 | key duplicate$ empty$ 947 | { pop$ 948 | journal duplicate$ empty$ 949 | { "need key or journal for " cite$ * " to crossref " * crossref * warning$ } 950 | { "journal" bibinfo.check emphasize word.in swap$ * } 951 | if$ 952 | } 953 | { word.in swap$ * " " *} 954 | if$ 955 | " \cite{" * crossref * "}" * 956 | } 957 | FUNCTION {format.crossref.editor} 958 | { editor #1 "{vv~}{ll}" format.name$ 959 | "editor" bibinfo.check 960 | editor num.names$ duplicate$ 961 | #2 > 962 | { pop$ 963 | "editor" bibinfo.check 964 | " " * bbl.etal 965 | * 966 | } 967 | { #2 < 968 | 'skip$ 969 | { editor #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = 970 | { 971 | "editor" bibinfo.check 972 | " " * bbl.etal 973 | * 974 | } 975 | { 976 | bbl.and space.word 977 | * editor #2 "{vv~}{ll}" format.name$ 978 | "editor" bibinfo.check 979 | * 980 | } 981 | if$ 982 | } 983 | if$ 984 | } 985 | if$ 986 | } 987 | FUNCTION {format.book.crossref} 988 | { volume duplicate$ empty$ 989 | { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ 990 | pop$ word.in 991 | } 992 | { bbl.volume 993 | capitalize 994 | swap$ tie.or.space.prefix "volume" bibinfo.check * * bbl.of space.word * 995 | } 996 | if$ 997 | editor empty$ 998 | editor field.or.null author field.or.null = 999 | or 1000 | { key empty$ 1001 | { series empty$ 1002 | { "need editor, key, or series for " cite$ * " to crossref " * 1003 | crossref * warning$ 1004 | "" * 1005 | } 1006 | { series emphasize * } 1007 | if$ 1008 | } 1009 | { key * } 1010 | if$ 1011 | } 1012 | { format.crossref.editor * } 1013 | if$ 1014 | " \cite{" * crossref * "}" * 1015 | } 1016 | FUNCTION {format.incoll.inproc.crossref} 1017 | { 1018 | editor empty$ 1019 | editor field.or.null author field.or.null = 1020 | or 1021 | { key empty$ 1022 | { format.booktitle duplicate$ empty$ 1023 | { "need editor, key, or booktitle for " cite$ * " to crossref " * 1024 | crossref * warning$ 1025 | } 1026 | { word.in swap$ * } 1027 | if$ 1028 | } 1029 | { word.in key * " " *} 1030 | if$ 1031 | } 1032 | { word.in format.crossref.editor * " " *} 1033 | if$ 1034 | " \cite{" * crossref * "}" * 1035 | } 1036 | FUNCTION {format.org.or.pub} 1037 | { 't := 1038 | "" 1039 | address empty$ t empty$ and 1040 | 'skip$ 1041 | { 1042 | address "address" bibinfo.check * 1043 | t empty$ 1044 | 'skip$ 1045 | { address empty$ 1046 | 'skip$ 1047 | { ": " * } 1048 | if$ 1049 | t * 1050 | } 1051 | if$ 1052 | } 1053 | if$ 1054 | } 1055 | FUNCTION {format.publisher.address} 1056 | { publisher "publisher" bibinfo.warn format.org.or.pub 1057 | } 1058 | 1059 | FUNCTION {format.organization.address} 1060 | { organization "organization" bibinfo.check format.org.or.pub 1061 | } 1062 | 1063 | FUNCTION {article} 1064 | { output.bibitem 1065 | format.authors "author" output.check 1066 | add.colon 1067 | format.title "title" output.check 1068 | new.sentence 1069 | crossref missing$ 1070 | { 1071 | journal 1072 | "journal" bibinfo.check 1073 | emphasize 1074 | "journal" output.check 1075 | format.vol.num.pages output 1076 | format.date "year" output.check 1077 | } 1078 | { format.article.crossref output.nonnull 1079 | } 1080 | if$ 1081 | eid empty$ 1082 | { format.journal.pages } 1083 | { format.journal.eid } 1084 | if$ 1085 | format.issn output 1086 | format.doi output 1087 | format.note output 1088 | format.eprint output 1089 | fin.entry 1090 | write.url 1091 | } 1092 | FUNCTION {book} 1093 | { output.bibitem 1094 | author empty$ 1095 | { format.editors "author and editor" output.check 1096 | add.colon 1097 | } 1098 | { format.authors output.nonnull 1099 | add.colon 1100 | crossref missing$ 1101 | { "author and editor" editor either.or.check } 1102 | 'skip$ 1103 | if$ 1104 | } 1105 | if$ 1106 | format.btitle "title" output.check 1107 | crossref missing$ 1108 | { format.bvolume output 1109 | new.sentence 1110 | format.number.series output 1111 | format.publisher.address output 1112 | } 1113 | { 1114 | new.sentence 1115 | format.book.crossref output.nonnull 1116 | } 1117 | if$ 1118 | format.edition output 1119 | format.date "year" output.check 1120 | format.isbn output 1121 | format.book.pages output 1122 | format.doi output 1123 | format.note output 1124 | format.eprint output 1125 | fin.entry 1126 | write.url 1127 | } 1128 | FUNCTION {booklet} 1129 | { output.bibitem 1130 | format.authors output 1131 | add.colon 1132 | format.title "title" output.check 1133 | new.sentence 1134 | howpublished "howpublished" bibinfo.check output 1135 | address "address" bibinfo.check output 1136 | format.date output 1137 | format.isbn output 1138 | format.book.pages output 1139 | format.doi output 1140 | format.note output 1141 | format.eprint output 1142 | fin.entry 1143 | write.url 1144 | } 1145 | 1146 | FUNCTION {inbook} 1147 | { output.bibitem 1148 | author empty$ 1149 | { format.editors "author and editor" output.check 1150 | add.colon 1151 | } 1152 | { format.authors output.nonnull 1153 | add.colon 1154 | crossref missing$ 1155 | { "author and editor" editor either.or.check } 1156 | 'skip$ 1157 | if$ 1158 | } 1159 | if$ 1160 | format.btitle "title" output.check 1161 | crossref missing$ 1162 | { 1163 | format.bvolume output 1164 | format.chapter.pages "chapter and pages" output.check 1165 | new.sentence 1166 | format.number.series output 1167 | format.publisher.address output 1168 | } 1169 | { 1170 | format.chapter.pages "chapter and pages" output.check 1171 | new.sentence 1172 | format.book.crossref output.nonnull 1173 | } 1174 | if$ 1175 | format.edition output 1176 | format.date "year" output.check 1177 | crossref missing$ 1178 | { format.isbn output } 1179 | 'skip$ 1180 | if$ 1181 | format.pages "pages" output.check 1182 | format.doi output 1183 | format.note output 1184 | format.eprint output 1185 | fin.entry 1186 | write.url 1187 | } 1188 | 1189 | FUNCTION {incollection} 1190 | { output.bibitem 1191 | format.authors "author" output.check 1192 | add.colon 1193 | format.title "title" output.check 1194 | new.sentence 1195 | crossref missing$ 1196 | { format.in.ed.booktitle "booktitle" output.check 1197 | format.number.series output 1198 | format.chapter.pages output 1199 | format.publisher.address output 1200 | format.edition output 1201 | format.date "year" output.check 1202 | format.isbn output 1203 | } 1204 | { format.incoll.inproc.crossref output.nonnull 1205 | format.chapter.pages output 1206 | } 1207 | if$ 1208 | format.pages "pages" output.check 1209 | format.doi output 1210 | format.note output 1211 | format.eprint output 1212 | fin.entry 1213 | write.url 1214 | } 1215 | FUNCTION {inproceedings} 1216 | { output.bibitem 1217 | format.authors "author" output.check 1218 | add.colon 1219 | format.title "title" output.check 1220 | new.sentence 1221 | crossref missing$ 1222 | { format.in.ed.booktitle "booktitle" output.check 1223 | format.number.series output 1224 | publisher empty$ 1225 | { format.organization.address output } 1226 | { organization "organization" bibinfo.check output 1227 | format.publisher.address output 1228 | } 1229 | if$ 1230 | format.date "year" output.check 1231 | format.isbn output 1232 | format.issn output 1233 | } 1234 | { format.incoll.inproc.crossref output.nonnull 1235 | } 1236 | if$ 1237 | format.pages "pages" output.check 1238 | format.doi output 1239 | format.note output 1240 | format.eprint output 1241 | fin.entry 1242 | write.url 1243 | } 1244 | FUNCTION {conference} { inproceedings } 1245 | FUNCTION {manual} 1246 | { output.bibitem 1247 | author empty$ 1248 | { organization "organization" bibinfo.check 1249 | duplicate$ empty$ 'pop$ 1250 | { output 1251 | address "address" bibinfo.check output 1252 | } 1253 | if$ 1254 | } 1255 | { format.authors output.nonnull } 1256 | if$ 1257 | add.colon 1258 | format.btitle "title" output.check 1259 | new.sentence 1260 | author empty$ 1261 | { organization empty$ 1262 | { 1263 | address "address" bibinfo.check output 1264 | } 1265 | 'skip$ 1266 | if$ 1267 | } 1268 | { 1269 | organization "organization" bibinfo.check output 1270 | address "address" bibinfo.check output 1271 | } 1272 | if$ 1273 | format.edition output 1274 | format.date output 1275 | format.doi output 1276 | format.note output 1277 | format.eprint output 1278 | fin.entry 1279 | write.url 1280 | } 1281 | 1282 | FUNCTION {mastersthesis} 1283 | { output.bibitem 1284 | format.authors "author" output.check 1285 | add.colon 1286 | format.btitle 1287 | "title" output.check 1288 | new.sentence 1289 | bbl.mthesis format.thesis.type output.nonnull 1290 | school "school" bibinfo.warn output 1291 | address "address" bibinfo.check output 1292 | format.date "year" output.check 1293 | format.doi output 1294 | format.note output 1295 | format.eprint output 1296 | fin.entry 1297 | write.url 1298 | } 1299 | 1300 | FUNCTION {misc} 1301 | { output.bibitem 1302 | format.authors output 1303 | add.colon 1304 | format.title output 1305 | new.sentence 1306 | howpublished "howpublished" bibinfo.check output 1307 | format.date output 1308 | format.doi output 1309 | format.note output 1310 | format.eprint output 1311 | fin.entry 1312 | write.url 1313 | empty.misc.check 1314 | } 1315 | FUNCTION {phdthesis} 1316 | { output.bibitem 1317 | format.authors "author" output.check 1318 | add.colon 1319 | format.btitle 1320 | "title" output.check 1321 | new.sentence 1322 | bbl.phdthesis format.thesis.type output.nonnull 1323 | school "school" bibinfo.warn output 1324 | address "address" bibinfo.check output 1325 | format.date "year" output.check 1326 | format.doi output 1327 | format.note output 1328 | format.eprint output 1329 | fin.entry 1330 | write.url 1331 | } 1332 | 1333 | FUNCTION {proceedings} 1334 | { output.bibitem 1335 | editor empty$ 1336 | { organization "organization" bibinfo.check output 1337 | } 1338 | { format.editors output.nonnull } 1339 | if$ 1340 | add.colon 1341 | format.btitle "title" output.check 1342 | format.bvolume output 1343 | format.number.series output 1344 | editor empty$ 1345 | { publisher empty$ 1346 | 'skip$ 1347 | { 1348 | format.publisher.address output 1349 | } 1350 | if$ 1351 | } 1352 | { publisher empty$ 1353 | { 1354 | format.organization.address output } 1355 | { 1356 | organization "organization" bibinfo.check output 1357 | format.publisher.address output 1358 | } 1359 | if$ 1360 | } 1361 | if$ 1362 | format.date "year" output.check 1363 | format.isbn output 1364 | format.issn output 1365 | format.doi output 1366 | format.note output 1367 | format.eprint output 1368 | fin.entry 1369 | write.url 1370 | } 1371 | 1372 | FUNCTION {techreport} 1373 | { output.bibitem 1374 | format.authors "author" output.check 1375 | add.colon 1376 | format.title 1377 | "title" output.check 1378 | new.sentence 1379 | format.tr.number output.nonnull 1380 | institution "institution" bibinfo.warn output 1381 | address "address" bibinfo.check output 1382 | format.date "year" output.check 1383 | format.doi output 1384 | format.note output 1385 | format.eprint output 1386 | fin.entry 1387 | write.url 1388 | } 1389 | 1390 | FUNCTION {unpublished} 1391 | { output.bibitem 1392 | format.authors "author" output.check 1393 | add.colon 1394 | format.title "title" output.check 1395 | format.date output 1396 | format.doi output 1397 | format.note "note" output.check 1398 | format.eprint output 1399 | fin.entry 1400 | write.url 1401 | } 1402 | 1403 | FUNCTION {default.type} { misc } 1404 | READ 1405 | FUNCTION {sortify} 1406 | { purify$ 1407 | "l" change.case$ 1408 | } 1409 | INTEGERS { len } 1410 | FUNCTION {chop.word} 1411 | { 's := 1412 | 'len := 1413 | s #1 len substring$ = 1414 | { s len #1 + global.max$ substring$ } 1415 | 's 1416 | if$ 1417 | } 1418 | FUNCTION {sort.format.names} 1419 | { 's := 1420 | #1 'nameptr := 1421 | "" 1422 | s num.names$ 'numnames := 1423 | numnames 'namesleft := 1424 | { namesleft #0 > } 1425 | { s nameptr 1426 | "{ll{ }}{ f{ }}{ jj{ }}" 1427 | format.name$ 't := 1428 | nameptr #1 > 1429 | { 1430 | nameptr #3 1431 | #1 + = 1432 | numnames #3 1433 | > and 1434 | { "others" 't := 1435 | #1 'namesleft := } 1436 | 'skip$ 1437 | if$ 1438 | " " * 1439 | namesleft #1 = t "others" = and 1440 | { "zzzzz" * } 1441 | { t sortify * } 1442 | if$ 1443 | } 1444 | { t sortify * } 1445 | if$ 1446 | nameptr #1 + 'nameptr := 1447 | namesleft #1 - 'namesleft := 1448 | } 1449 | while$ 1450 | } 1451 | 1452 | FUNCTION {sort.format.title} 1453 | { 't := 1454 | "A " #2 1455 | "An " #3 1456 | "The " #4 t chop.word 1457 | chop.word 1458 | chop.word 1459 | sortify 1460 | #1 global.max$ substring$ 1461 | } 1462 | FUNCTION {author.sort} 1463 | { author empty$ 1464 | { key empty$ 1465 | { "to sort, need author or key in " cite$ * warning$ 1466 | "" 1467 | } 1468 | { key sortify } 1469 | if$ 1470 | } 1471 | { author sort.format.names } 1472 | if$ 1473 | } 1474 | FUNCTION {author.editor.sort} 1475 | { author empty$ 1476 | { editor empty$ 1477 | { key empty$ 1478 | { "to sort, need author, editor, or key in " cite$ * warning$ 1479 | "" 1480 | } 1481 | { key sortify } 1482 | if$ 1483 | } 1484 | { editor sort.format.names } 1485 | if$ 1486 | } 1487 | { author sort.format.names } 1488 | if$ 1489 | } 1490 | FUNCTION {author.organization.sort} 1491 | { author empty$ 1492 | { organization empty$ 1493 | { key empty$ 1494 | { "to sort, need author, organization, or key in " cite$ * warning$ 1495 | "" 1496 | } 1497 | { key sortify } 1498 | if$ 1499 | } 1500 | { "The " #4 organization chop.word sortify } 1501 | if$ 1502 | } 1503 | { author sort.format.names } 1504 | if$ 1505 | } 1506 | FUNCTION {editor.organization.sort} 1507 | { editor empty$ 1508 | { organization empty$ 1509 | { key empty$ 1510 | { "to sort, need editor, organization, or key in " cite$ * warning$ 1511 | "" 1512 | } 1513 | { key sortify } 1514 | if$ 1515 | } 1516 | { "The " #4 organization chop.word sortify } 1517 | if$ 1518 | } 1519 | { editor sort.format.names } 1520 | if$ 1521 | } 1522 | FUNCTION {presort} 1523 | { type$ "book" = 1524 | type$ "inbook" = 1525 | or 1526 | 'author.editor.sort 1527 | { type$ "proceedings" = 1528 | 'editor.organization.sort 1529 | { type$ "manual" = 1530 | 'author.organization.sort 1531 | 'author.sort 1532 | if$ 1533 | } 1534 | if$ 1535 | } 1536 | if$ 1537 | " " 1538 | * 1539 | year field.or.null sortify 1540 | * 1541 | " " 1542 | * 1543 | title field.or.null 1544 | sort.format.title 1545 | * 1546 | #1 entry.max$ substring$ 1547 | 'sort.key$ := 1548 | } 1549 | ITERATE {presort} 1550 | SORT 1551 | STRINGS { longest.label } 1552 | INTEGERS { number.label longest.label.width } 1553 | FUNCTION {initialize.longest.label} 1554 | { "" 'longest.label := 1555 | #1 'number.label := 1556 | #0 'longest.label.width := 1557 | } 1558 | FUNCTION {longest.label.pass} 1559 | { number.label int.to.str$ 'label := 1560 | number.label #1 + 'number.label := 1561 | label width$ longest.label.width > 1562 | { label 'longest.label := 1563 | label width$ 'longest.label.width := 1564 | } 1565 | 'skip$ 1566 | if$ 1567 | } 1568 | EXECUTE {initialize.longest.label} 1569 | ITERATE {longest.label.pass} 1570 | FUNCTION {begin.bib} 1571 | { preamble$ empty$ 1572 | 'skip$ 1573 | { preamble$ write$ newline$ } 1574 | if$ 1575 | "\begin{thebibliography}{" longest.label * "}" * 1576 | write$ newline$ 1577 | "\providecommand{\url}[1]{\texttt{#1}}" 1578 | write$ newline$ 1579 | "\providecommand{\urlprefix}{Dostupné z: }" 1580 | write$ newline$ 1581 | "\expandafter\ifx\csname urlstyle\endcsname\relax" 1582 | write$ newline$ 1583 | " \providecommand{\doi}[1]{doi:\discretionary{}{}{}#1}\else" 1584 | write$ newline$ 1585 | " \providecommand{\doi}{doi:\discretionary{}{}{}\begingroup \urlstyle{rm}\Url}\fi" 1586 | write$ newline$ 1587 | "\providecommand{\selectlanguage}[1]{\relax}" 1588 | write$ newline$ 1589 | "\providecommand{\eprint}[2][]{\url{#2}}" 1590 | write$ newline$ 1591 | } 1592 | EXECUTE {begin.bib} 1593 | EXECUTE {init.state.consts} 1594 | ITERATE {call.type$} 1595 | FUNCTION {end.bib} 1596 | { newline$ 1597 | "\end{thebibliography}" write$ newline$ 1598 | } 1599 | EXECUTE {end.bib} 1600 | %% End of customized bst file 1601 | %% 1602 | %% End of file `czechiso.bst'. 1603 | -------------------------------------------------------------------------------- /doc/dokumentace.bib: -------------------------------------------------------------------------------- 1 | @InBook{Mecklenburgc2005, 2 | author = {Robert William. Mecklenburg and Andrew. Oram}, 3 | title = {Managing projects with GNU make}, 4 | publisher = {O'Reilly}, 5 | address = {Cambridge [Mass.]}, 6 | year = {2005}, 7 | edition = {3}, 8 | ISBN = {05-9600-610-1}, 9 | } 10 | 11 | @Misc{PJWHashFunction2017, 12 | author = {Wikipedia}, 13 | title = {PJW hash function}, 14 | howpublished = {[online]}, 15 | year = {rev. 5. srpen 2017}, 16 | note = {[vid. 2017-11-29]}, 17 | url = {https://en.wikipedia.org/wiki/PJW_hash_function}, 18 | } 19 | 20 | @InBook{Honzik1991, 21 | author = {Jan Maxmilián Honzík and Tomáš Hruška and Michal Máčel}, 22 | title = {Vybrané kapitoly z programovacích technik}, 23 | publisher = {VUT v Brně}, 24 | address = {Brno}, 25 | year = {1991}, 26 | edition = {3}, 27 | ISBN = {80-214-0345-4}, 28 | } 29 | -------------------------------------------------------------------------------- /doc/dokumentace.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/dokumentace.pdf -------------------------------------------------------------------------------- /doc/dokumentace.tex: -------------------------------------------------------------------------------- 1 | % Author: Dominik Harmim 2 | % Author: Vojtěch Hertl 3 | % Author: Timotej Halás 4 | % Author: Matej Karas 5 | 6 | 7 | \documentclass[a4paper, 11pt]{article} 8 | 9 | 10 | \usepackage[czech]{babel} 11 | \usepackage[utf8]{inputenc} 12 | \usepackage[left=2cm, top=3cm, text={17cm, 24cm}]{geometry} 13 | \usepackage{times} 14 | \usepackage{verbatim} 15 | \usepackage{enumitem} 16 | \usepackage{graphicx} % vkládání obrázků 17 | \usepackage[unicode]{hyperref} 18 | \hypersetup{ 19 | colorlinks = true, 20 | hypertexnames = false, 21 | citecolor = red 22 | } 23 | 24 | 25 | \newcommand{\RNum}[1]{\uppercase\expandafter{\romannumeral #1\relax}} % makro na sázení římských čísel 26 | 27 | 28 | \begin{document} 29 | 30 | 31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Titulní stránka %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 | \begin{titlepage} 33 | \begin{center} 34 | \includegraphics[width=0.77\linewidth]{inc/FIT_logo.pdf} \\ 35 | 36 | \vspace{\stretch{0.382}} 37 | 38 | \Huge{Projektová dokumentace} \\ 39 | \LARGE{\textbf{Implementace překladače imperativního jazyka IFJ17}} \\ 40 | \Large{Tým 104, varianta \RNum{2}} 41 | \vspace{\stretch{0.618}} 42 | \end{center} 43 | 44 | \begin{minipage}{0.4 \textwidth} 45 | {\Large \today} 46 | \end{minipage} 47 | \hfill 48 | \begin{minipage}[r]{0.6 \textwidth} 49 | \Large 50 | \begin{tabular}{l l l} 51 | \textbf{Dominik Harmim} & \textbf{(xharmi00)} & \quad 25\,\% \\ 52 | Vojtěch Hertl & (xhertl04) & \quad 25\,\% \\ 53 | Timotej Halás & (xhalas10) & \quad 25\,\% \\ 54 | Matej Karas & (xkaras34) & \quad 25\,\% \\ 55 | \end{tabular} 56 | \end{minipage} 57 | \end{titlepage} 58 | 59 | 60 | 61 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Obsah %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 62 | \pagenumbering{roman} 63 | \setcounter{page}{1} 64 | \tableofcontents 65 | \clearpage 66 | 67 | 68 | 69 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Úvod %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 70 | \pagenumbering{arabic} 71 | \setcounter{page}{1} 72 | 73 | \section{Úvod} 74 | 75 | Cílem projektu bylo vytvořit program v~jazyce~C, který načte zdrojový kód zapsaný ve zdrojovém jazyce IFJ17, 76 | jenž je zjednodušenou podmnožinou jazyka FreeBASIC a~přeloží jej do cílového jazyka IFJcode17 (mezikód). 77 | 78 | Program funguje jako konzolová aplikace, které načítá zdrojový program ze standardního vstupu a~generuje 79 | výsledný mezikód na standardní výstup nebo v~případě chyby vrací odpovídající chybový kód. 80 | 81 | 82 | 83 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Návrh a implementace %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 84 | \section{Návrh a~implementace} 85 | 86 | Projekt jsme sestavili z~několika námi implementovaných dílčích částí, které jsou představeny v~této kapitole. 87 | Je zde také uvedeno, jakým způsobem spolu jednotlivé dílčí části spolupracují. 88 | 89 | 90 | \subsection{Lexikální analýza} 91 | 92 | Při tvorbě překladače jsme začali implementací lexikální analýzy. Hlavní funkce této analýzy je \texttt{get\_next\_token}, 93 | pomocí níž se čte znak po znaku ze zdrojového souboru a~převádí na strukturu \texttt{token}, která se skládá z~typu a~atributu. 94 | Typy tokenu jsou \texttt{EOF}, \texttt{EOL}, prázdný token, identifikátory, klíčová slova, celé či desetinné číslo, řetězec 95 | a~také porovnávací a~aritmetické operátory a~ostatní znaky, které mohou být použity v~jazyce IFJ2017. Typ atributu je 96 | \texttt{union} a~dělí se podle typu tokenu. Pokud je typ tokenu řetězec nebo identifikátor, pak bude atribut daný řetězec, 97 | když by byl typ tokenu klíčové slovo, přiřadí atributu dané klíčové slovo, pokud číslo, atribut bude ono číslo, u~ostatních 98 | případů není atribut použit. S~takto vytvořeným tokenem poté pracují další analýzy. 99 | 100 | Celý lexikální analyzátor je implementován jako deterministický konečný automat podle předem vytvořeného diagramu 101 | \ref{figure:fa_graph}. Konečný automat je v~jazyce C~jako jeden nekonečně opakující se \texttt{switch}, kde každý případ 102 | \texttt{case} je ekvivalentní k~jednomu stavu automatu. Pokud načtený znak nesouhlasí s~žádným znakem, který jazyk povoluje, 103 | program je ukončen a~vrací chybu 1. Jinak se přechází do dalších stavů a~načítají se další znaky, dokud nemáme hotový jeden 104 | token, který potom vracíme a~ukončíme tuto funkci. 105 | 106 | Za zmínku stojí zpracovávání identifikátorů a~klíčových slov, kde načítáme znaky a~průběžně ukládáme do dynamického pole 107 | a~jakmile odcházíme ze stavu identifikátoru, porovnáváme, jestli načtený řetězec není shodný s~nějakým z~klíčových slov a~podle 108 | toho se rozhodneme, zda vracíme atribut a~typ tokenu klíčové slovo či identifikátor. 109 | 110 | Pro zpracovávání escape sekvencí v~řetězci máme vytvořeno pole o velikosti 4, které je zpočátku vynulováno a poté se postupně 111 | naplňuje přečtenými čísly, vždy na pozici podle toho, kolikáté číslo zrovna uvažujeme a nakonec celé třímístné čislo převedeme 112 | do ASCII podoby. 113 | 114 | 115 | \subsection{Syntaktická analýza} 116 | 117 | Nejdůležitější částí celého programu je syntaktická analýza. 118 | 119 | Až na výrazy se syntaktická analýza řídí LL -- gramatikou a~metodou rekurzivního sestupu podle pravidel v~LL -- tabulce 120 | \ref{table:ll_gramatika}. Každé pravidlo má svou vlastní funkci, která dostává přes parametr ukazatel na \texttt{PData}, 121 | což je struktura obsahující proměnné nutné pro správné fungování analýzy. Syntaktická analýza žádá po lexikálním 122 | analyzátoru tokeny pomocí \texttt{get\_next\_token}. Tyto tokeny načítá lexikální analyzátor ze zdrojového souboru 123 | a~zároveň provádí lexikální analýzu. 124 | 125 | Aby se příkaz návratu z~funkce nevyskytoval v~hlavním těle programu, vyřešili jsme to pomocí bool proměnné \texttt{in\_function}, 126 | která symbolizuje, že se právě nacházíme ve funkci. 127 | 128 | \subsubsection{Zpracování výrazů pomocí precedenční syntaktické analýzy} 129 | 130 | Precedenční analýza je v~syntaktické analýze definována a~volána jako ostatní pravidla v~LL -- gramatice, 131 | ale je zvlášť implementována v~souboru \texttt{expression.c}, a~její rozhraní je v~souboru \texttt{expression.h}. 132 | 133 | Při zpracovávání výrazů je použita precedenční tabulka \ref{table:prec_table}. Jelikož operátory \texttt{+} a~– mají 134 | stejnou asociativitu a~prioritu, mohli jsme je zjednodušit do jednoho sloupce a~řádku tabulky \texttt{+ -}. 135 | Totéž jsme mohli udělat s~operátory \texttt{*} a~\texttt{/} (v~tabulce sloupec a~řádek \texttt{* /}) a~také 136 | všemi relačními operátory (v~tabulce sloupec a~řádek \texttt{r}). Řádek a~sloupec \texttt{i} symbolizuje 137 | identifikátor, číselnou hodnotu nebo řetězec. Pro získání indexu ze symbolu do tabulky jsme si vytvořili 138 | funkci \texttt{get\_prec\_table\_index}, které se jako parametr zadá symbol, a~vrátí index do tabulky. 139 | Mezi povolené symboly ve výrazech patří všechny operátory, literály, které jsou v~tabulce zastoupeny již 140 | dříve popsaným \texttt{i}, a~závorky. Tyto symboly jsou terminály. Všechny ostatní symboly, které výraz 141 | obsahovat nemůže, jsou zastoupeny symbolem \texttt{\$}. Řádky tabulky označují vrchní terminál v~zásobníku 142 | symbolů a~sloupce symbol v~aktuálním tokenu. 143 | 144 | Po spuštění analýzy se podle vrchního terminálu v~zásobníku symbolů a~symbolu aktuálního tokenu provádějí různé 145 | operace a~v~některých případech se volá funkce \texttt{get\_next\_token}. 146 | 147 | Pokud znak z~tabulky je \texttt{<}, vložíme zarážku za vrchní terminál, aktuální symbol vložíme na vrchol zásobníku 148 | a~zavoláme funkci \texttt{get\_next\_token}. Pokud znak z~tabulky je \texttt{>} a~existuje pravidlo, podle kterého 149 | se dají zredukovat položky zásobníku symbolů od nejvrchnější položky až po zarážku, tak je zredukujeme a~odstraníme 150 | zarážku. Během provádění redukce podle stanovených pravidel se otestuje sémantika levého a~pravého operandu a~pokud 151 | je to potřebné a~možné, provede se implicitní přetypování jednoho nebo obou operandů. Pokud znak je \texttt{=}, aktuální 152 | symbol vložíme na vrchol zásobníku a~zavoláme funkci \texttt{get\_next\_token}. To opakujeme až do chvíle, kdy na místě 153 | v~tabulce určeném vrchním terminálem v~zásobníku symbolů a~symbolem aktuálního tokenu žádný znak není. Pokud vrchní 154 | terminál i~aktuální symbol je \texttt{\$} a~na vrcholu zásobníku zůstal jeden výsledný neterminál, syntaktická analýza je 155 | úspěšná, v~jiných případech končí neúspěšně. 156 | 157 | 158 | \subsection{Sémantická analýza} 159 | 160 | Ve struktuře \texttt{PData} jsou uloženy tabulky symbolů, lokální pro lokální proměnné, a~globální pro funkce. 161 | Tabulky symbolů jsou implementovány jako tabulky s~náhodným rozptýlením a~slouží ke kontrole, zda daný 162 | identifikátor existuje a~zda souhlasí jeho datový typ, případně návratová hodnota. 163 | 164 | 165 | \subsection{Generování cílového kódu} 166 | 167 | Generování cílového kódu pro nás znamená generování mezikódu IFJcode17. Kód je generován na standardní výstup 168 | po dokončení všech analýz, přičemž jednotlivé dílčí části jsou generovány v~průběhu analýz do interní paměti 169 | programu. Při generování mezikódu jsou generovány i~komentáře pro jeho zpřehlednění. 170 | 171 | \subsubsection{Rozhraní generátoru kódu} 172 | 173 | Generování kódu je implementováno jako samostatný modul v~souboru \texttt{code\_generator.c}, 174 | jehož rozhraní se nachází v~souboru \texttt{code\_generator.h} Rozhraní nabízí 3 základní funkce. 175 | Jedná se o~funkci, která připraví generování kódu, tato funkce se volá na začátku syntaktické analýzy. 176 | Funkce, která uvolní z~paměti všechny rezervované prostředky generátorem kódu se volá po dokončení 177 | syntaktické analýzy. Poslední důležitá funkce generuje vytvořený kód na patřičný výstup. Rozhraní 178 | nabízí spoustu dalších funkcí pro generování jednotlivých částí programu, např. generování začátku 179 | funkce, volání funkce, deklarace proměnnné, příkazu \texttt{input}, funkcí pro zpracování výrazů, 180 | podmínek, cyklů, aj. Všechny tyto funkce se volají ve správnou chvíli a~se správnými 181 | parametry v~průběhu syntaktické analýzy a~syntaktické analýzy výrazů. 182 | 183 | \subsubsection{Začátek generování} 184 | 185 | Na začátku generování jsou inicializovány potřebné datové struktury (které jsou na závěr 186 | opět uvolněny), vygenerována hlavička mezikódu, která zahrnuje potřebné náležitosti pro korektní 187 | interpretaci mezikódu, definici globálních proměnných na globálním rámci a~skok do hlavního 188 | těla programu. Poté jsou vygenerovány vestavěné funkce, které jsou zapsány přímo v~jazyce IFJcode17. 189 | 190 | \subsubsection{Generování funkcí} 191 | 192 | Každá funkce je tvořena návěštím ve tvaru \texttt{\$identifikator\_funkce} a~svým lokálním rámcem. 193 | Pro funkce se vždy generuje výchozí návratová hodnota podle jejího návratového datového typu, 194 | která se ukládá na lokální rámec, který je po odchodu z~funkce dostupný jako dočasný rámec. 195 | Před zavoláním funkce jsou hodnoty parametrů uloženy do dočasného rámce s~číslem, které 196 | odpovídá pořadí daného parametru a~po vstupu do funkce jsou všechny předané parametry uloženy na 197 | lokální rámce se svým názvev. Při generování návratu z~funkce je předán výsledek zpracovaného 198 | výrazu do proměnné s~návratovou hodnotou funkce a~proveden skok na konec funkce. 199 | 200 | \subsubsection{Generování výrazů} 201 | 202 | Všechny výrazy jsou během syntaktické analýzy výrazů ukládány na datový zásobník a~v~pravou 203 | chvíli jsou provedeny patřičné operace s~hodnotami na vrcholu zásobníku. Hodnota zpracovaného 204 | výrazu se ukládá do globální proměnné na globálním rámci. 205 | 206 | \subsubsection{Generování návěští} 207 | 208 | Všechna návěští, pokud neuvažujeme návěští pro funkce a~jiná speciální návěští, jsou generovány 209 | ve tvaru \texttt{\$identifikator\_funkce\%label\_deep\%label\_index}, kde 210 | \texttt{identifikator\_funkce} zajistí unikátnost návěští mezi jednotlivými funkcemi, 211 | \texttt{label\_deep} je hloubka zanoření návěští (například u~podmínek a~cyklů) 212 | a~\texttt{label\_index} je číselná hodnota, která se inkrementuje s~každým dalším návěštím uvnitř 213 | dané funkce. 214 | 215 | 216 | \subsection{Překladový systém} 217 | 218 | Projekt je možné přeložit dvěma způsoby, buď nástrojem CMake nebo nástrojem GNU Make. Oba způsoby vytvoří 219 | spustitelný soubor \texttt{ifj17\_compiler}. 220 | 221 | \subsubsection{CMake} 222 | 223 | Při vývoji, ladění a~testování projketu někteří z~nás používali CMake, protože překlad tímto nástrojem 224 | je integrován ve vývojovém prostředí CLion, které většina členů týmu používala a~protože nastavení pravidel 225 | pro překlad tímto nástrojem je jednoduché. 226 | 227 | V~souboru \texttt{CMakeLists.txt} jsou nastavena pravidla pro překlad. Je zde nastaven překladač \texttt{gcc} 228 | a~všechny potřebné parametry pro překlad. Dále je zde uvedeno, že se pro překlad mají používat všechny soubory s~příponou 229 | \texttt{.c} a~\texttt{.h} a~taky je zde uveden název spustitelného souboru. 230 | 231 | Soubory pro překlad je možné vygenerovat příkazem \texttt{cmake~.} a~překlad provést příkazem \texttt{cmake~--build~.} 232 | nebo pro toto využít nástroje vývojového prostředí. 233 | 234 | \subsubsection{GNU Make} 235 | 236 | Jedním z~požadavků pro odevzdání projektu bylo přiložit k~odevzdanému projektu i~soubor \texttt{Makefile} a~přeložení 237 | projektu příkazem \texttt{make}. Z~tohoto důvodu jsme vytvořili překladový systém i~pomocí nástroje GNU Make. 238 | 239 | V~souboru \texttt{Makefile} jsou nastavena pravidla pro překlad. Je zde nastaven překladač \texttt{gcc} 240 | a~všechny potřebné parametry pro překlad. Vytvořili jsme pravidla, které nejdříve vytvoří objektové soubory 241 | ze všech souborů s~příponou \texttt{.c} v~daném adresáři za pomoci vytvořených automatických pravidel a~překladače \texttt{gcc}, 242 | který dokáže automaticky generovat tyto pravidla, více viz \emph{Dependecy Generation} v~kapitole \emph{C and C++} 243 | knihy \cite{Mecklenburgc2005}. Následně je ze všech objektových souborů vytvořen jeden spustitelný 244 | soubor. 245 | 246 | Nástroj GNU Make jsme mimo překlad využili i~pro automatické zabalení projektu pro odezvdání, generování dokumentace, 247 | spouštění automatických testů a~čištění dočasných souborů. 248 | 249 | 250 | 251 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Speciální algoritmy a datové struktury %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 252 | \section{Speciální algoritmy a~datové struktury} 253 | 254 | Při tvorbě překladače jsme implementovali několik speciálních datových struktur, 255 | které jsou v~této kapitole představeny. 256 | 257 | 258 | \subsection{Tabulka s~rozptýlenými položkami} 259 | 260 | Implementovali jsme tabulku s~rozptýlenými položkami, která slouží jako tabulka symbolů, což byl mimo 261 | jiné i~požadavek vyplývající ze zadání, který souvisí s~předmětem IAL. Tabulku jsme implementovali 262 | jako tabulku s~explicitním zřetězením synonym, protože jsome potřebovali, aby počet položek, 263 | který do ní můžeme uložit byl dynamický a~teoreticky neomezený. Pro zřetězení synonym jsme použili 264 | lineárně vázané seznamy. Velikost mapovacího pole jsme dimenzovali tak, aby byla rovna prvočíslu a~její 265 | naplnění nepřesáhlo 75\,\%. Jako optimální velikost jsme tedy zvolili číslo 27\,457. 266 | 267 | Jako mapovací funkci jsme použili GNU ELF Hash použitou v~algoritmu, který používá UNIX ELF. 268 | Tato funkce je variantou PJW Hash. Použití této mapovací funkce jsme vyhodnotili jako nejvhodnější, 269 | více viz \cite{PJWHashFunction2017}. Funkci jsme upravili tak, aby vracela datový typ a~hodnotu, 270 | která je přípustná pro naše mapovací pole. 271 | 272 | Každá položka tabulky obsahuje unikátní klíč v~podobě řetězce, kterým je identifikátor funkce 273 | či proměnné. Dále obsahuje ukazatel na další prvek (synonymum) a~datovou část. Datová část obsahuje 274 | informaci o~tom, jakého datového typu je daný symbol (v~případě funkce se jedná o~datový typ 275 | návratové hodnoty), pravdivostní hodnoty, které indikují, jestli byl daný symbol již definován 276 | a~zda je symbol globální. Posledním prvkem datové části, který se používá pouze v~případě, že se 277 | jedná o~funkci, je počet a~datové typy formálních parametrů funkce, který je ve formátu řetezce 278 | (Pokud např. daná funkce má dva parametry, první typu \texttt{Double} a~druhý typu \texttt{Integer}, 279 | uloží se následující řetězec \texttt{di}.). 280 | 281 | Implementovali jsme i~několik potřebných funkcí, které tvoří rozhraní pro práci s~tabulkou. 282 | Jedná se o~následující funkce: inicializace, přidání nové položky, přidání určitého parametru 283 | pro konkrétní položku tabulky (v případě, že se jedná o~funkci), vyhledání položky, odstranění 284 | položky a~uvolnění tabulky z paměti. Při implementaci jsme se inspirovali studijní literaturou 285 | předmětu IAL \cite{Honzik1991}. 286 | 287 | 288 | \subsection{Dynamický řetězec} 289 | 290 | Vytvořili jsme strukturu \texttt{Dynamic\_string} pro práci s~řetězci dynamické délky, kterou používáme 291 | např. pro ukládání řezězce nebo identifikátoru u~atributu tokenu v~lexikální analýze, protože dopředu nevíme, 292 | jak bude tento řetězec dlouhý. 293 | 294 | Daná struktura a~rozhraní operací nad ní je popsáno v~souboru \texttt{dynamic\_string.h} a~operace jsou implementovány 295 | v~souboru \texttt{dynamic\_string.c}. 296 | 297 | Struktura v~sobě uchovává ukazatel na řezězec, velikost řetězce a~informaci o~tom, kolik paměti je pro 298 | řetězec vyhrazeno. Implementovali jsme operace pro inicializaci struktury, uvolnění vyhrazených dat, 299 | přidání znaku na konec řezězce, porovnávání řetězců, kopie celých řezězců a~další. Při inicializaci struktury 300 | se vyhradí paměťový prostor pouze pro určitý počet znaků (určeno konstantou), až při nedostatku vyhrazené 301 | paměti se velikost paměti zvýší. 302 | 303 | 304 | \subsection{Zásobník symbolů pro precedenční syntaktickou analýzu} 305 | 306 | Implementovali jsme zásobník symbolů v~souboru \texttt{symstack.c}, který používáme při precedenční syntaktické analýze. 307 | Jeho rozhraní je v~souboru \texttt{symstack.h}. Má implementovány základní zásobníkové operace jako 308 | \texttt{symbol\_stack\_push}, \texttt{symbol\_stack\_pop} a~\texttt{symbol\_stack\_top}. Pro účely 309 | precedenční syntaktické analýzy jsme implementovali další funkce jako \texttt{symbol\_stack\_top\_terminal}, 310 | která vrátí nejvrchnější terminál, a~funkci \texttt{symbol\_stack\_insert\_after\_top\_terminal}, 311 | která vloží symbol za nejvrchnější terminál. 312 | 313 | Struktura položky zásobníku obsahuje symbol, který jsme získali během precedenční analýzy z~tokenu pomocí funkce 314 | \texttt{get\_symbol\_from\_token}, jejímž parametrem je ukazatel na token, a~vrátí symbol ve formě výčtového typu. 315 | Dále obsahuje datový typ tohoto symbolu, který je inicializován pouze tehdy, pokud symbol může mít datový typ, 316 | a~používá se při sémantické kontrole. Struktura také obsahuje ukazatel na další položku. 317 | 318 | 319 | 320 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Práce v týmu %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 321 | \section{Práce v~týmu} 322 | 323 | \subsection{Způsob práce v~týmu} 324 | 325 | Na projektu jsme začali pracovat koncem října. Práci jsme si dělili postupně, tj. neměli jsme od začátku 326 | stanovený kompletní plán rozdělení práce. Na dílčích částech projektu pracovali většinou jednotlivci nebo 327 | dvojice členů týmu. Nejprve jsme si stanovili strukturu projektu a~vytvořili překladový systém. 328 | 329 | \subsubsection{Verzovací systém} 330 | 331 | Pro správu souborů projektu jsme používali verzovací systém Git. Jako vzdálený repositář jsme používali \mbox{GitHub}. 332 | 333 | Git nám umožnil pracovat na více úkolech na projektu současně v~tzv. větvích. Většinu úkolů jsme nejdříve připravili 334 | do větve a~až po otestování a~schválení úprav ostatními členy týmu jsme tyto úpravy začlenili do hlavní 335 | vývojové větve. 336 | 337 | \subsubsection{Komunikace} 338 | 339 | Komunikace mezi členy týmů probíhala převážně osobně nebo prostřednictvím aplikace Slack, kde jsme si buď 340 | psali přímo mezi sebou nebo si vytvářeli různé skupinové konverzace podle tématu. 341 | 342 | V~průběhu řešení projektu jsme měli i~několik osobních setkání, kde jsem probírali a~řešili problémy 343 | týkající se různých částí projektu. 344 | 345 | 346 | \subsection{Rozdělení práce mezi členy týmu} 347 | 348 | Práci na projektu jsme si rozdělili rovnoměrně s~ohledem na její složitost a~časovou náročnost. 349 | Každý tedy dostal procentuální hodnocení 25\,\%. 350 | Tabulka \ref{table:rozdeleni_prace} shrnuje rozdělení práce v~týmu mezi jednotlivými členy. 351 | \bigskip 352 | \begin{table}[ht] 353 | \centering 354 | \begin{tabular}{| l | l |} 355 | \hline 356 | Člen týmu & Přidělená práce \\ \hline 357 | \textbf{Dominik Harmim} & \begin{tabular}{l} vedení týmu, organizace práce, dohlížení na provádění práce, 358 | konzultace, \\ kontrola, testování, dokumentace, struktura projektu, generování cílového kódu \end{tabular} \\ 359 | Vojtěch Hertl & \begin{tabular}{l} lexikální analýza, testování, dokumentace, prezentace \end{tabular} \\ 360 | Timotej Halás & \begin{tabular}{l} syntaktická analýza, syntaktická analýza výrazů, sémantická analýza, testování, \\ 361 | dokumentace \end{tabular} \\ 362 | Matej Karas & \begin{tabular}{l} syntaktická analýza, sémantická analýza, testování, dokumentace \end{tabular} \\ \hline 363 | \end{tabular} 364 | \caption{Rozdělení práce v~týmu mezi jednotlivými členy} 365 | \label{table:rozdeleni_prace} 366 | \end{table} 367 | 368 | 369 | 370 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Závěr %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 371 | \section{Závěr} 372 | 373 | Projekt nás zprvu trochu zaskočil svým rozsahem a~složitostí. Postupem času, až jsme získali dostatek 374 | znalostí o~tvorbě překladačů na přednáškách IFJ, jsme projekt začali řešit. 375 | 376 | Náš tým jsme měli sestaven velmi brzy, byli jsme již předem domluveni na komunikačních kanálech, 377 | osobních schůzkách a~na používání verzovacího systému, tudíž jsme s~týmovou prací neměli žádný 378 | problém a~pracovalo se nám společně velmi dobře. 379 | 380 | Na projektu jsme začali pracovat trochu později, takže jsme neměli časovou rezervu, ale nakonec 381 | jsme všechno bez problému stihli. Jednotlivé části projektu jsme řešili většinou individuálně za použití 382 | znalostí z~přednáškek nebo materiálů do předmětů IFJ a~IAL. 383 | 384 | V~průběhu vývoje jsme se potýkali s~menšími problémy týkajícími se nejasností v~zadání, ale 385 | tyto jsme vyřešili díky fóru k~projektu. Správnost řešení jsme si ověřili automatickými 386 | testy a~pokusným odevzdáním, díky čemuž jsme byli schopni projekt ještě více odladit. 387 | 388 | Tento projekt nám celkově přinesl spoustu znalostí ohledně fungování překladačů, prakticky nám 389 | objasnil probíranou látku v~předmětech IFJ a~IAL a~přinesl nám zkušennosti s~projekty tohoto rozsahu. 390 | 391 | 392 | 393 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Citace %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 394 | \clearpage 395 | \bibliographystyle{czechiso} 396 | \renewcommand{\refname}{Literatura} 397 | \bibliography{dokumentace} 398 | 399 | 400 | 401 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Přílohy %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 402 | \clearpage 403 | \appendix 404 | 405 | 406 | \section{Diagram konečného automatu specifikující lexikální analyzátor} 407 | \begin{figure}[!ht] 408 | \centering 409 | \vspace{-1.2cm} 410 | \includegraphics[width=0.95\linewidth]{inc/FA_graph.pdf} 411 | \caption{Diagram konečného automatu specifikující lexikální analyzátor} 412 | \label{figure:fa_graph} 413 | \end{figure} 414 | 415 | 416 | \section{LL -- gramatika} 417 | \begin{table}[!ht] 418 | \centering 419 | \begin{enumerate}[noitemsep] 420 | \item \verb| -> DECLARE FUNCTION ID ( ) AS | 421 | \item \verb| -> FUNCTION ID ( ) AS EOL END| \newline \verb|FUNCTION | 422 | \item \verb| -> EOL | 423 | \item \verb| -> | 424 | 425 | \item \verb| -> SCOPE EOL END SCOPE | 426 | \item \verb| -> EOL | 427 | \item \verb| -> EOF| 428 | 429 | \item \verb| -> ID AS | 430 | \item \verb| -> |$\varepsilon$ 431 | 432 | \item \verb| -> , ID AS | 433 | \item \verb| -> |$\varepsilon$ 434 | 435 | \item \verb| -> DIM ID AS EOL | 436 | \item \verb| -> IF THEN EOL ELSE EOL| \newline \verb| END IF EOL | 437 | \item \verb| -> DO WHILE EOL LOOP EOL| \newline \verb|| 438 | \item \verb| -> ID = EOL | 439 | \item \verb| -> INPUT ID EOL | 440 | \item \verb| -> PRINT ; EOL | 441 | \item \verb| -> RETURN EOL | 442 | \item \verb| -> |$\varepsilon$ 443 | 444 | \item \verb| -> = | 445 | \item \verb| -> |$\varepsilon$ 446 | 447 | \item \verb| -> ID ( )| 448 | \item \verb| -> ASC ( )| 449 | \item \verb| -> CHR ( )| 450 | \item \verb| -> LENGTH ( )| 451 | \item \verb| -> SUBSTR ( )| 452 | \item \verb| -> | 453 | 454 | \item \verb| -> | 455 | \item \verb| -> |$\varepsilon$ 456 | 457 | \item \verb| -> , | 458 | \item \verb| -> |$\varepsilon$ 459 | 460 | \item \verb| -> INT_VALUE| 461 | \item \verb| -> DOUBLE_VALUE| 462 | \item \verb| -> STRING_VALUE| 463 | \item \verb| -> ID| 464 | 465 | \item \verb| -> ; | 466 | \item \verb| -> |$\varepsilon$ 467 | 468 | \item \verb| -> INTEGER| 469 | \item \verb| -> DOUBLE| 470 | \item \verb| -> STRING| 471 | \end{enumerate} 472 | 473 | \caption{LL -- gramatika řídící syntaktickou analýzu} 474 | \label{table:ll_gramatika} 475 | \end{table} 476 | 477 | 478 | \section{LL -- tabulka} 479 | \begin{table}[!ht] 480 | \centering 481 | \includegraphics[width=1\linewidth]{inc/LL_table.pdf} 482 | \caption{LL -- tabulka použitá při syntaktické analýze} 483 | \label{table:ll_table} 484 | \end{table} 485 | 486 | 487 | \section{Precedenční tabulka} 488 | \begin{table}[!ht] 489 | \centering 490 | \includegraphics[width=0.7\linewidth]{inc/prec_table.pdf} 491 | \caption{Precedenční tabulka použitá při precedenční syntaktické analýze výrazů} 492 | \label{table:prec_table} 493 | \end{table} 494 | 495 | 496 | \end{document} 497 | -------------------------------------------------------------------------------- /doc/inc/FA_graph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/inc/FA_graph.pdf -------------------------------------------------------------------------------- /doc/inc/FA_graph.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/inc/FA_graph.vsdx -------------------------------------------------------------------------------- /doc/inc/FIT_logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/inc/FIT_logo.pdf -------------------------------------------------------------------------------- /doc/inc/LL_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/inc/LL_table.pdf -------------------------------------------------------------------------------- /doc/inc/prec_table.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/doc/inc/prec_table.pdf -------------------------------------------------------------------------------- /dynamic_string.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Dynamic string. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | 14 | #include "dynamic_string.h" 15 | 16 | 17 | #define DYNAMIC_STRING_LEN_INC 8 /// Inicialization lenght of string. 18 | 19 | 20 | void dynamic_string_clear(Dynamic_string *s) 21 | { 22 | s->length = 0; 23 | s->str[s->length] = '\0'; 24 | } 25 | 26 | 27 | bool dynamic_string_init(Dynamic_string *s) 28 | { 29 | if (!(s->str = (char *) malloc(DYNAMIC_STRING_LEN_INC))) 30 | { 31 | return false; 32 | } 33 | 34 | dynamic_string_clear(s); 35 | s->alloc_size = DYNAMIC_STRING_LEN_INC; 36 | 37 | return true; 38 | } 39 | 40 | 41 | void dynamic_string_free(Dynamic_string *s) 42 | { 43 | free(s->str); 44 | } 45 | 46 | 47 | bool dynamic_string_add_char(Dynamic_string *s, char c) 48 | { 49 | if (s->length + 1 >= s->alloc_size) 50 | { 51 | unsigned int new_size = s->length + DYNAMIC_STRING_LEN_INC; 52 | if (!(s->str = (char *) realloc(s->str, new_size))) 53 | { 54 | return false; 55 | } 56 | s->alloc_size = new_size; 57 | } 58 | 59 | s->str[s->length++] = c; 60 | s->str[s->length] = '\0'; 61 | 62 | return true; 63 | } 64 | 65 | 66 | bool dynamic_string_add_const_str(Dynamic_string *s, const char *const_string) 67 | { 68 | unsigned int const_str_length = (unsigned int) strlen(const_string); 69 | 70 | if (s->length + const_str_length + 1 >= s->alloc_size) 71 | { 72 | unsigned int new_size = s->length + const_str_length + 1; 73 | if (!(s->str = (char *) realloc(s->str, new_size))) 74 | { 75 | return false; 76 | } 77 | s->alloc_size = new_size; 78 | } 79 | 80 | s->length += const_str_length; 81 | strcat(s->str, const_string); 82 | s->str[s->length] = '\0'; 83 | 84 | return true; 85 | } 86 | 87 | 88 | int dynamic_string_cmp_const_str(Dynamic_string *dynamic_string, const char *const_string) 89 | { 90 | return strcmp(dynamic_string->str, const_string); 91 | } 92 | 93 | 94 | bool dynamic_string_copy(Dynamic_string *src, Dynamic_string *dest) 95 | { 96 | unsigned int new_length = src->length; 97 | if (new_length >= dest->alloc_size) 98 | { 99 | if (!(dest->str = (char *) realloc(dest->str, new_length + 1))) 100 | { 101 | return false; 102 | } 103 | dest->alloc_size = new_length + 1; 104 | } 105 | 106 | strcpy(dest->str, src->str); 107 | dest->length = new_length; 108 | 109 | return true; 110 | } 111 | -------------------------------------------------------------------------------- /dynamic_string.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Dynamic string interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #ifndef _DYNAMIC_STRING_H 12 | #define _DYNAMIC_STRING_H 13 | 14 | 15 | #include 16 | 17 | 18 | /** 19 | * @struct Representation of dynamic string. 20 | */ 21 | typedef struct 22 | { 23 | char *str; /// string ened by '\0' byte 24 | unsigned int length; /// lenght of string 25 | unsigned int alloc_size; /// number of chars alocated for string 26 | } Dynamic_string; 27 | 28 | 29 | /** 30 | * Inicialization of dynamic string. 31 | * 32 | * @param s Pointer to dynamic string. 33 | * @return True if inicialization was successful, false otherwise. 34 | */ 35 | bool dynamic_string_init(Dynamic_string *s); 36 | 37 | /** 38 | * Frees alocated memory for dynamic string. 39 | * 40 | * @param s Pointer to dynamic string. 41 | */ 42 | void dynamic_string_free(Dynamic_string *s); 43 | 44 | /** 45 | * Clear dynamic string content. 46 | * 47 | * @param s Pointer to dynamic string. 48 | */ 49 | void dynamic_string_clear(Dynamic_string *s); 50 | 51 | /** 52 | * Add char to the end of dynamic string. 53 | * 54 | * @param s Dynamic string. 55 | * @param c Char to add. 56 | * @return True if it was successful, false otherwise. 57 | */ 58 | bool dynamic_string_add_char(Dynamic_string *s, char c); 59 | 60 | /** 61 | * Add constant string to the end of dynamic string. 62 | * 63 | * @param s Dynamic string. 64 | * @param const_string Constant string. 65 | * @return True if it was successful, false otherwise. 66 | */ 67 | bool dynamic_string_add_const_str(Dynamic_string *s, const char *const_string); 68 | 69 | /** 70 | * Compare dynamic string with constant string. 71 | * 72 | * @param dynamic_string Dynamic string. 73 | * @param const_string Constant string. 74 | * @return Returns 1, 0, or -1, according as the s1 is greater than, equal to, or less than the s2. 75 | */ 76 | int dynamic_string_cmp_const_str(Dynamic_string *dynamic_string, const char *const_string); 77 | 78 | /** 79 | * Copy src string to dest string. 80 | * 81 | * @param src Source string. 82 | * @param dest Destination string. 83 | * @return True if it was successful, false otherwise. 84 | */ 85 | bool dynamic_string_copy(Dynamic_string *src, Dynamic_string *dest); 86 | 87 | 88 | #endif // _DYNAMIC_STRING_H 89 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Error definitions. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #ifndef _ERROR_H 12 | #define _ERROR_H 13 | 14 | 15 | // Error codes. 16 | #define SCANNER_TOKEN_OK 0 /// Token is OK. 17 | #define SYNTAX_OK 0 /// Syntax is ok. 18 | #define SCANNER_ERROR_LEX 1 /// Lex structure error. 19 | #define SYNTAX_ERR 2 /// Syntax error. 20 | #define SEM_ERR_UNDEFINED_VAR 3 /// Semantic error - undefined variable. 21 | #define SEM_ERR_TYPE_COMPAT 4 /// Semantic error - types uncompatible. 22 | #define SEM_ERR_OTHER 6 /// Semantic error - other. 23 | #define ERROR_INTERNAL 99 /// Internal error, eg. malloc error etc. 24 | 25 | 26 | #endif //_ERROR_H 27 | -------------------------------------------------------------------------------- /expression.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Implementation of parsing of expressions. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #include "scanner.h" 12 | #include "expression.h" 13 | #include "symstack.h" 14 | #include "error.h" 15 | #include "code_generator.h" 16 | 17 | 18 | #define TABLE_SIZE 8 19 | 20 | #define FREE_RESOURCES_RETURN(_return) \ 21 | do { \ 22 | symbol_stack_free(&stack); \ 23 | return _return; \ 24 | } while(0) 25 | 26 | 27 | Symbol_stack stack; 28 | 29 | /** 30 | * @enum Precedence table symbols. 31 | */ 32 | typedef enum 33 | { 34 | S, /// < SHIFT 35 | E, /// = EQUAL 36 | R, /// > REDUCE 37 | N /// # ERROR 38 | } Prec_table_sign_enum; 39 | 40 | /** 41 | * @enum Indexes for precedence table. 42 | */ 43 | typedef enum 44 | { 45 | I_PLUS_MINUS, /// 0 +- 46 | I_MUL_DIV, /// 1 */ 47 | I_IDIV, /// 2 \ / 48 | I_REL_OP, /// 3 r 49 | I_LEFT_BRACKET, /// 4 ( 50 | I_RIGHT_BRACKET, /// 5 ) 51 | I_DATA, /// 6 i 52 | I_DOLLAR /// 7 $ 53 | } Prec_table_index_enum; 54 | 55 | // Precedence table 56 | int prec_table[TABLE_SIZE][TABLE_SIZE] = 57 | { 58 | // |+- | */| \ | r | ( | ) | i | $ | 59 | { R , S , S , R , S , R , S , R }, /// +- 60 | { R , R , R , R , S , R , S , R }, /// */ 61 | { R , S , R , R , S , R , S , R }, /// \ / 62 | { S , S , S , N , S , R , S , R }, /// r (realtion operators) = <> <= < >= > 63 | { S , S , S , S , S , E , S , N }, /// ( 64 | { R , R , R , R , N , R , N , R }, /// ) 65 | { R , R , R , R , N , R , N , R }, /// i (id, int, double, string) 66 | { S , S , S , S , S , N , S , N } /// $ 67 | }; 68 | 69 | 70 | /** 71 | * Function converts symbol to precedence table index. 72 | * 73 | * @param symbol Symbol to be converted. 74 | * @return Returns precedence table index. 75 | */ 76 | static Prec_table_index_enum get_prec_table_index(Prec_table_symbol_enum symbol) 77 | { 78 | switch (symbol) 79 | { 80 | case PLUS: 81 | case MINUS: 82 | return I_PLUS_MINUS; 83 | 84 | case MUL: 85 | case DIV: 86 | return I_MUL_DIV; 87 | 88 | case IDIV: 89 | return I_IDIV; 90 | 91 | case EQ: 92 | case NEQ: 93 | case LEQ: 94 | case LTN: 95 | case MEQ: 96 | case MTN: 97 | return I_REL_OP; 98 | 99 | case LEFT_BRACKET: 100 | return I_LEFT_BRACKET; 101 | 102 | case RIGHT_BRACKET: 103 | return I_RIGHT_BRACKET; 104 | 105 | case IDENTIFIER: 106 | case INT_NUMBER: 107 | case DOUBLE_NUMBER: 108 | case STRING: 109 | return I_DATA; 110 | 111 | default: 112 | return I_DOLLAR; 113 | } 114 | } 115 | 116 | 117 | /** 118 | * Function converts token type to symbol. 119 | * 120 | * @param token Pointer to token. 121 | * @return Returns dollar if symbol is not supported or converted symbol if symbol is supported. 122 | */ 123 | static Prec_table_symbol_enum get_symbol_from_token(Token* token) 124 | { 125 | switch (token->type) 126 | { 127 | case TOKEN_TYPE_PLUS: 128 | return PLUS; 129 | case TOKEN_TYPE_MINUS: 130 | return MINUS; 131 | case TOKEN_TYPE_MUL: 132 | return MUL; 133 | case TOKEN_TYPE_DIV: 134 | return DIV; 135 | case TOKEN_TYPE_IDIV: 136 | return IDIV; 137 | case TOKEN_TYPE_ASSIGN: 138 | return EQ; 139 | case TOKEN_TYPE_NEQ: 140 | return NEQ; 141 | case TOKEN_TYPE_LEQ: 142 | return LEQ; 143 | case TOKEN_TYPE_LTN: 144 | return LTN; 145 | case TOKEN_TYPE_MEQ: 146 | return MEQ; 147 | case TOKEN_TYPE_MTN: 148 | return MTN; 149 | case TOKEN_TYPE_LEFT_BRACKET: 150 | return LEFT_BRACKET; 151 | case TOKEN_TYPE_RIGHT_BRACKET: 152 | return RIGHT_BRACKET; 153 | case TOKEN_TYPE_IDENTIFIER: 154 | return IDENTIFIER; 155 | case TOKEN_TYPE_INT_NUMBER: 156 | return INT_NUMBER; 157 | case TOKEN_TYPE_DOUBLE_NUMBER: 158 | return DOUBLE_NUMBER; 159 | case TOKEN_TYPE_STRING: 160 | return STRING; 161 | default: 162 | return DOLLAR; 163 | } 164 | } 165 | 166 | 167 | /** 168 | * Function converts token type to data type. 169 | * 170 | * @param token Pointer to token. 171 | * @param data Pointer to parser's internal data. 172 | * @return Returns data type of actual token. 173 | */ 174 | static Data_type get_data_type(Token* token, PData* data) 175 | { 176 | TData* symbol; 177 | 178 | switch (token->type) 179 | { 180 | case TOKEN_TYPE_IDENTIFIER: 181 | symbol = sym_table_search(&data->local_table, token->attribute.string->str); 182 | if (symbol == NULL) 183 | return TYPE_UNDEFINED; 184 | return symbol->type; 185 | 186 | case TOKEN_TYPE_INT_NUMBER: 187 | return TYPE_INT; 188 | 189 | case TOKEN_TYPE_DOUBLE_NUMBER: 190 | return TYPE_DOUBLE; 191 | 192 | case TOKEN_TYPE_STRING: 193 | return TYPE_STRING; 194 | default: 195 | return TYPE_UNDEFINED; 196 | } 197 | } 198 | 199 | 200 | /** 201 | * Function function counts number of symbols after stop symbol on stack. 202 | * 203 | * @param stop_found Pointer to bool variable which will be changed to true if stop was found else to false. 204 | * @return Number of charatcters after stop symbol. Is valid only when stop_found was set to true. 205 | */ 206 | static int num_of_symbols_after_stop(bool* stop_found) 207 | { 208 | Symbol_stack_item* tmp = symbol_stack_top(&stack); 209 | int count = 0; 210 | 211 | while (tmp != NULL) 212 | { 213 | if (tmp->symbol != STOP) 214 | { 215 | *stop_found = false; 216 | count++; 217 | } 218 | else 219 | { 220 | *stop_found = true; 221 | break; 222 | } 223 | 224 | tmp = tmp->next; 225 | } 226 | 227 | return count; 228 | } 229 | 230 | 231 | /** 232 | * Function tests if symbols in parameters are valid according to rules. 233 | * 234 | * @param num Number of valid symbols in parameter. 235 | * @param op1 Symbol 1. 236 | * @param op2 Symbol 2. 237 | * @param op3 Symbol 3. 238 | * @return NOT_A_RULE if no rule is found or returns rule which is valid. 239 | */ 240 | static Prec_rules_enum test_rule(int num, Symbol_stack_item* op1, Symbol_stack_item* op2, Symbol_stack_item* op3) 241 | { 242 | switch (num) 243 | { 244 | case 1: 245 | // rule E -> i 246 | if (op1->symbol == IDENTIFIER || op1->symbol == INT_NUMBER || op1->symbol == DOUBLE_NUMBER || op1->symbol == STRING) 247 | return OPERAND; 248 | 249 | return NOT_A_RULE; 250 | 251 | case 3: 252 | // rule E -> (E) 253 | if (op1->symbol == LEFT_BRACKET && op2->symbol == NON_TERM && op3->symbol == RIGHT_BRACKET) 254 | return LBR_NT_RBR; 255 | 256 | if (op1->symbol == NON_TERM && op3->symbol == NON_TERM) 257 | { 258 | switch (op2->symbol) 259 | { 260 | // rule E -> E + E 261 | case PLUS: 262 | return NT_PLUS_NT; 263 | 264 | // rule E -> E - E 265 | case MINUS: 266 | return NT_MINUS_NT; 267 | 268 | // rule E -> E * E 269 | case MUL: 270 | return NT_MUL_NT; 271 | 272 | // rule E -> E / E 273 | case DIV: 274 | return NT_DIV_NT; 275 | 276 | // rule E -> E \ E 277 | case IDIV: 278 | return NT_IDIV_NT; 279 | 280 | // rule E -> E = E 281 | case EQ: 282 | return NT_EQ_NT; 283 | 284 | // rule E -> E <> E 285 | case NEQ: 286 | return NT_NEQ_NT; 287 | 288 | // rule E -> E <= E 289 | case LEQ: 290 | return NT_LEQ_NT; 291 | 292 | // rule E -> E < E 293 | case LTN: 294 | return NT_LTN_NT; 295 | 296 | // rule E -> E >= E 297 | case MEQ: 298 | return NT_MEQ_NT; 299 | 300 | // rule E -> E > E 301 | case MTN: 302 | return NT_MTN_NT; 303 | 304 | // invalid operator 305 | default: 306 | return NOT_A_RULE; 307 | } 308 | } 309 | return NOT_A_RULE; 310 | } 311 | return NOT_A_RULE; 312 | } 313 | 314 | 315 | /** 316 | * Function checks semantics of operands according to rule. 317 | * 318 | * @param rule Pointer to table. 319 | * @param op1 Symbol 1. 320 | * @param op2 Symbol 2. 321 | * @param op3 Symbol 3. 322 | * @param final_type Sets data type which will be after executing rule. 323 | * @return Given exit code. 324 | */ 325 | static int check_semantics(Prec_rules_enum rule, Symbol_stack_item* op1, Symbol_stack_item* op2, Symbol_stack_item* op3, Data_type* final_type) 326 | { 327 | bool retype_op1_to_double = false; 328 | bool retype_op3_to_double = false; 329 | bool retype_op1_to_integer = false; 330 | bool retype_op3_to_integer = false; 331 | 332 | if (rule == OPERAND) 333 | { 334 | if (op1->data_type == TYPE_UNDEFINED) 335 | return SEM_ERR_UNDEFINED_VAR; 336 | 337 | if (op1->data_type == TYPE_BOOL) 338 | return SEM_ERR_TYPE_COMPAT; 339 | } 340 | 341 | if (rule == LBR_NT_RBR) 342 | { 343 | if (op2->data_type == TYPE_UNDEFINED) 344 | return SEM_ERR_UNDEFINED_VAR; 345 | } 346 | 347 | if (rule != OPERAND && rule != LBR_NT_RBR) 348 | { 349 | if (op1->data_type == TYPE_UNDEFINED || op3->data_type == TYPE_UNDEFINED) 350 | return SEM_ERR_UNDEFINED_VAR; 351 | 352 | if (op1->data_type == TYPE_BOOL || op3->data_type == TYPE_BOOL) 353 | return SEM_ERR_TYPE_COMPAT; 354 | } 355 | 356 | switch (rule) 357 | { 358 | case OPERAND: 359 | *final_type = op1->data_type; 360 | break; 361 | 362 | case LBR_NT_RBR: 363 | *final_type = op2->data_type; 364 | break; 365 | 366 | case NT_PLUS_NT: 367 | case NT_MINUS_NT: 368 | case NT_MUL_NT: 369 | if (op1->data_type == TYPE_STRING && op3->data_type == TYPE_STRING && rule == NT_PLUS_NT) 370 | { 371 | *final_type = TYPE_STRING; 372 | break; 373 | } 374 | 375 | if (op1->data_type == TYPE_INT && op3->data_type == TYPE_INT) 376 | { 377 | *final_type = TYPE_INT; 378 | break; 379 | } 380 | 381 | if (op1->data_type == TYPE_STRING || op3->data_type == TYPE_STRING) 382 | return SEM_ERR_TYPE_COMPAT; 383 | 384 | *final_type = TYPE_DOUBLE; 385 | 386 | if (op1->data_type == TYPE_INT) 387 | retype_op1_to_double = true; 388 | 389 | if (op3->data_type == TYPE_INT) 390 | retype_op3_to_double = true; 391 | 392 | break; 393 | 394 | case NT_DIV_NT: 395 | *final_type = TYPE_DOUBLE; 396 | 397 | if (op1->data_type == TYPE_STRING || op3->data_type == TYPE_STRING) 398 | return SEM_ERR_TYPE_COMPAT; 399 | 400 | if (op1->data_type == TYPE_INT) 401 | retype_op1_to_double = true; 402 | 403 | if (op3->data_type == TYPE_INT) 404 | retype_op3_to_double = true; 405 | 406 | break; 407 | 408 | case NT_IDIV_NT: 409 | *final_type = TYPE_INT; 410 | 411 | if (op1->data_type == TYPE_STRING || op3->data_type == TYPE_STRING) 412 | return SEM_ERR_TYPE_COMPAT; 413 | 414 | if (op1->data_type == TYPE_DOUBLE) 415 | retype_op1_to_integer = true; 416 | 417 | if (op3->data_type == TYPE_DOUBLE) 418 | retype_op3_to_integer = true; 419 | 420 | break; 421 | 422 | case NT_EQ_NT: 423 | case NT_NEQ_NT: 424 | case NT_LEQ_NT: 425 | case NT_LTN_NT: 426 | case NT_MEQ_NT: 427 | case NT_MTN_NT: 428 | *final_type = TYPE_BOOL; 429 | 430 | if (op1->data_type == TYPE_INT && op3->data_type == TYPE_DOUBLE) 431 | retype_op1_to_double = true; 432 | 433 | else if (op1->data_type == TYPE_DOUBLE && op3->data_type == TYPE_INT) 434 | retype_op3_to_double = true; 435 | 436 | else if (op1->data_type != op3->data_type) 437 | return SEM_ERR_TYPE_COMPAT; 438 | 439 | break; 440 | 441 | default: 442 | break; 443 | } 444 | 445 | if (retype_op1_to_double) 446 | { 447 | GENERATE_CODE(generate_stack_op2_to_double); 448 | } 449 | 450 | if (retype_op3_to_double) 451 | { 452 | GENERATE_CODE(generate_stack_op1_to_double); 453 | } 454 | 455 | if (retype_op1_to_integer) 456 | { 457 | GENERATE_CODE(generate_stack_op2_to_integer); 458 | } 459 | 460 | if (retype_op3_to_integer) 461 | { 462 | GENERATE_CODE(generate_stack_op1_to_integer); 463 | } 464 | 465 | return SYNTAX_OK; 466 | } 467 | 468 | 469 | /** 470 | * Function reduces symbols after STOP symbol if rule for reducing is found. 471 | * 472 | * @param data Pointer to table. 473 | * @return Given exit code. 474 | */ 475 | static int reduce_by_rule(PData* data) 476 | { 477 | (void) data; 478 | 479 | int result; 480 | 481 | Symbol_stack_item* op1 = NULL; 482 | Symbol_stack_item* op2 = NULL; 483 | Symbol_stack_item* op3 = NULL; 484 | Data_type final_type; 485 | Prec_rules_enum rule_for_code_gen; 486 | bool found = false; 487 | 488 | int count = num_of_symbols_after_stop(&found); 489 | 490 | 491 | if (count == 1 && found) 492 | { 493 | op1 = stack.top; 494 | rule_for_code_gen = test_rule(count, op1, NULL, NULL); 495 | } 496 | else if (count == 3 && found) 497 | { 498 | op1 = stack.top->next->next; 499 | op2 = stack.top->next; 500 | op3 = stack.top; 501 | rule_for_code_gen = test_rule(count, op1, op2, op3); 502 | } 503 | else 504 | return SYNTAX_ERR; 505 | 506 | if (rule_for_code_gen == NOT_A_RULE) 507 | { 508 | return SYNTAX_ERR; 509 | } 510 | else 511 | { 512 | if ((result = check_semantics(rule_for_code_gen, op1, op2, op3, &final_type))) 513 | return result; 514 | 515 | if (rule_for_code_gen == NT_PLUS_NT && final_type == TYPE_STRING) 516 | { 517 | GENERATE_CODE(generate_concat_stack_strings); 518 | } 519 | else GENERATE_CODE(generate_stack_operation, rule_for_code_gen); 520 | 521 | symbol_stack_pop_count(&stack, count + 1); 522 | symbol_stack_push(&stack, NON_TERM, final_type); 523 | } 524 | 525 | return SYNTAX_OK; 526 | } 527 | 528 | 529 | int expression(PData* data) 530 | { 531 | int result = SYNTAX_ERR; 532 | 533 | symbol_stack_init(&stack); 534 | 535 | if (!symbol_stack_push(&stack, DOLLAR, TYPE_UNDEFINED)) 536 | FREE_RESOURCES_RETURN(ERROR_INTERNAL); 537 | 538 | Symbol_stack_item* top_stack_terminal; 539 | Prec_table_symbol_enum actual_symbol; 540 | 541 | bool success = false; 542 | 543 | do 544 | { 545 | actual_symbol = get_symbol_from_token(&data->token); 546 | top_stack_terminal = symbol_stack_top_terminal(&stack); 547 | 548 | if (top_stack_terminal == NULL) 549 | FREE_RESOURCES_RETURN(ERROR_INTERNAL); 550 | 551 | switch (prec_table[get_prec_table_index(top_stack_terminal->symbol)][get_prec_table_index(actual_symbol)]) 552 | { 553 | case S: 554 | if (!symbol_stack_insert_after_top_terminal(&stack, STOP, TYPE_UNDEFINED)) 555 | FREE_RESOURCES_RETURN(ERROR_INTERNAL); 556 | 557 | if(!symbol_stack_push(&stack, actual_symbol, get_data_type(&data->token, data))) 558 | FREE_RESOURCES_RETURN(ERROR_INTERNAL); 559 | 560 | if (actual_symbol == IDENTIFIER || actual_symbol == INT_NUMBER || actual_symbol == DOUBLE_NUMBER || actual_symbol == STRING) 561 | { 562 | GENERATE_CODE(generate_push, data->token); 563 | } 564 | 565 | if ((result = get_next_token(&data->token))) 566 | FREE_RESOURCES_RETURN(result); 567 | break; 568 | 569 | case E: 570 | symbol_stack_push(&stack, actual_symbol, get_data_type(&data->token, data)); 571 | 572 | if ((result = get_next_token(&data->token))) 573 | FREE_RESOURCES_RETURN(result); 574 | break; 575 | 576 | case R: 577 | if ((result = reduce_by_rule(data))) 578 | FREE_RESOURCES_RETURN(result); 579 | break; 580 | 581 | case N: 582 | if (actual_symbol == DOLLAR && top_stack_terminal->symbol == DOLLAR) 583 | success = true; 584 | else 585 | FREE_RESOURCES_RETURN(SYNTAX_ERR); 586 | break; 587 | } 588 | } while (!success); 589 | 590 | Symbol_stack_item* final_non_terminal = symbol_stack_top(&stack); 591 | if (final_non_terminal == NULL) 592 | FREE_RESOURCES_RETURN(ERROR_INTERNAL); 593 | if (final_non_terminal->symbol != NON_TERM) 594 | FREE_RESOURCES_RETURN(SYNTAX_ERR); 595 | 596 | if (data->lhs_id != NULL) 597 | { 598 | char *frame = "LF"; 599 | if (data->lhs_id->global) frame = "GF"; 600 | 601 | switch (data->lhs_id->type) 602 | { 603 | case TYPE_INT: 604 | if (final_non_terminal->data_type == TYPE_STRING) 605 | FREE_RESOURCES_RETURN(SEM_ERR_TYPE_COMPAT); 606 | 607 | GENERATE_CODE(generate_save_expression_result, data->lhs_id->identifier, final_non_terminal->data_type, TYPE_INT, frame); 608 | break; 609 | 610 | case TYPE_DOUBLE: 611 | if (final_non_terminal->data_type == TYPE_STRING) 612 | FREE_RESOURCES_RETURN(SEM_ERR_TYPE_COMPAT); 613 | 614 | GENERATE_CODE(generate_save_expression_result, data->lhs_id->identifier, final_non_terminal->data_type, TYPE_DOUBLE, frame); 615 | break; 616 | 617 | case TYPE_STRING: 618 | if (final_non_terminal->data_type != TYPE_STRING) 619 | FREE_RESOURCES_RETURN(SEM_ERR_TYPE_COMPAT); 620 | 621 | GENERATE_CODE(generate_save_expression_result, data->lhs_id->identifier, TYPE_STRING, TYPE_STRING, frame); 622 | break; 623 | 624 | case TYPE_UNDEFINED: 625 | GENERATE_CODE(generate_save_expression_result, data->lhs_id->identifier, final_non_terminal->data_type, TYPE_UNDEFINED, frame); 626 | break; 627 | 628 | case TYPE_BOOL: 629 | if (final_non_terminal->data_type != TYPE_BOOL) 630 | FREE_RESOURCES_RETURN(SEM_ERR_TYPE_COMPAT); 631 | 632 | GENERATE_CODE(generate_save_expression_result, data->lhs_id->identifier, final_non_terminal->data_type, TYPE_BOOL, frame); 633 | break; 634 | 635 | default: 636 | break; 637 | } 638 | } 639 | 640 | FREE_RESOURCES_RETURN(SYNTAX_OK); 641 | } 642 | -------------------------------------------------------------------------------- /expression.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Expression interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #ifndef _EXPRESSION_H 12 | #define _EXPRESSION_H 13 | 14 | 15 | #include "analysis.h" 16 | 17 | 18 | /** 19 | * @enum Rules used for parsing. 20 | */ 21 | typedef enum 22 | { 23 | NT_EQ_NT, /// E -> E = E 24 | NT_NEQ_NT, /// E -> E <> E 25 | NT_LEQ_NT, /// E -> E <= E 26 | NT_LTN_NT, /// E -> E < E 27 | NT_MEQ_NT, /// E -> E => E 28 | NT_MTN_NT, /// E -> E > E 29 | NT_PLUS_NT, /// E -> E + E 30 | NT_MINUS_NT, /// E -> E - E 31 | NT_IDIV_NT, /// E -> E \ E 32 | NT_MUL_NT, /// E -> E * E 33 | NT_DIV_NT, /// E -> E / E 34 | LBR_NT_RBR, /// E -> (E) 35 | OPERAND, /// E -> i 36 | NOT_A_RULE /// rule doesn't exist 37 | } Prec_rules_enum; 38 | 39 | /** 40 | * @enum Symbols used for precednece analysis. 41 | */ 42 | typedef enum 43 | { 44 | PLUS, /// + 45 | MINUS, /// - 46 | MUL, /// * 47 | DIV, /// / 48 | IDIV, /// \ / 49 | EQ, /// = 50 | NEQ, /// <> 51 | LEQ, /// <= 52 | LTN, /// < 53 | MEQ, /// >= 54 | MTN, /// > 55 | LEFT_BRACKET, /// ( 56 | RIGHT_BRACKET, /// ) 57 | IDENTIFIER, /// ID 58 | INT_NUMBER, /// int 59 | DOUBLE_NUMBER, /// double 60 | STRING, /// string 61 | DOLLAR, /// $ 62 | STOP, /// stop symbol used when reducing 63 | NON_TERM /// non-terminal 64 | } Prec_table_symbol_enum; 65 | 66 | 67 | /** 68 | * Implementation of rule. Parses expressions. 69 | * 70 | * @param data Pointer to parser's internal data. 71 | * @return Given exit code. 72 | */ 73 | int expression(PData* data); 74 | 75 | 76 | #endif //_EXPRESSION_H 77 | -------------------------------------------------------------------------------- /ifj17_compiler.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Main file of compiler. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | * @author Matej Karas 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | 15 | #include "analysis.h" 16 | #include "code_generator.h" 17 | 18 | 19 | //#define DEBUG 1 20 | 21 | 22 | /** 23 | * Main function. 24 | * 25 | * @return EXIT_SUCCESS (0), if compilation was seccessful, appropriate error code otherwise. 26 | */ 27 | int main() 28 | { 29 | FILE* source_file; 30 | 31 | #ifdef DEBUG 32 | if (!(source_file = fopen("test.bas", "r"))) 33 | { 34 | source_file = stdin; 35 | } 36 | #else 37 | source_file = stdin; 38 | #endif 39 | 40 | set_source_file(source_file); 41 | 42 | int error_code; 43 | if ((error_code = analyse())) 44 | { 45 | code_generator_clear(); 46 | return error_code; 47 | } 48 | 49 | code_generator_flush(stdout); 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | -------------------------------------------------------------------------------- /ifj2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/ifj2017.pdf -------------------------------------------------------------------------------- /prezentace/IFJ_prezentace.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/IFJ_prezentace.pdf -------------------------------------------------------------------------------- /prezentace/IFJ_prezentace.tex: -------------------------------------------------------------------------------- 1 | % Author: Vojtěch Hertl 2 | 3 | \documentclass[11pt, hyperref={unicode}]{beamer} 4 | 5 | \usepackage[czech]{babel} 6 | \usepackage[utf8]{inputenc} 7 | \usepackage{times} % font 8 | \usepackage{graphics} % vkládání obrázků 9 | 10 | % téma prezentace 11 | % metropolis progressbar bug - kvuli tomu je na každém snímku varování Overfull \hbox 12 | \usetheme[progressbar=frametitle]{metropolis} 13 | % číslování aktuální snímek/celkem snímků 14 | \setbeamertemplate{footline}[frame number] 15 | 16 | 17 | \title{Formální jazyky a překladače} 18 | \subtitle{ 19 | Implementace překladace imperativního jazyka IFJ17. 20 | Tým 104, varianta II. 21 | } 22 | \author[Dominik Harmim \& Timotej Halás \& Matej Karas \& Vojtěch Hertl] 23 | { 24 | \texorpdfstring{ 25 | \begin{columns} 26 | \column{.45\linewidth} 27 | \centering 28 | Dominik Harmim\\ 29 | \footnotesize{\texttt{xharmi00@stud.fit.vutbr.cz}} 30 | \column{.45\linewidth} 31 | \centering 32 | Timotej Halás\\ 33 | \footnotesize{\texttt{xhalas10@stud.fit.vutbr.cz}} 34 | \end{columns} 35 | \vspace{0.5cm} 36 | \begin{columns} 37 | \column{.45\linewidth} 38 | \centering 39 | Matej Karas\\ 40 | \footnotesize{\texttt{xkaras34@stud.fit.vutbr.cz}} 41 | \column{.45\linewidth} 42 | \centering 43 | Vojtěch Hertl\\ 44 | \footnotesize{\texttt{xhertl04@stud.fit.vutbr.cz}} 45 | \end{columns} 46 | \vspace{1cm} 47 | } 48 | {Dominik Harmim \& Timotej Halás \& Matej Karas \& Vojtěch Hertl} 49 | } 50 | \date{\today} 51 | \institute 52 | { 53 | Vysoké učení technické v~Brně\\ 54 | Fakulta informačních technologií 55 | 56 | \bigskip 57 | 58 | \scalebox{0.3}{\includegraphics{img/FIT_barevne_CMYK_CZ.eps}} 59 | } 60 | 61 | 62 | \begin{document} 63 | 64 | 65 | 66 | \maketitle 67 | 68 | 69 | 70 | \begin{frame}{Přehled} 71 | % číslovaný obsah 72 | \setbeamertemplate{section in toc}[sections numbered] 73 | % skrytí podsekcí v obsahu 74 | \tableofcontents[hideallsubsections] 75 | \end{frame} 76 | 77 | 78 | 79 | \section{Struktura programu} 80 | 81 | \begin{frame} 82 | \frametitle<1>{Zdrojový kód} 83 | \frametitle<2>{Hlavní program} 84 | \frametitle<3>{Syntaktický analyzátor} 85 | \frametitle<4>{Lexikální analyzátor} 86 | \frametitle<5>{Výrazy} 87 | \frametitle<6>{Generování kódu} 88 | 89 | 90 | \begin{overprint} 91 | \onslide<1>\centerline{\includegraphics[width=0.95\linewidth]{img/zdrojovy_kod.pdf}}% 92 | \onslide<2>\centerline{\includegraphics[width=0.95\linewidth]{img/main.pdf}}% 93 | \onslide<3>\centerline{\includegraphics[width=0.95\linewidth]{img/parser.pdf}}% 94 | \onslide<4>\centerline{\includegraphics[width=0.95\linewidth]{img/scanner.pdf}}% 95 | \onslide<5>\centerline{\includegraphics[width=0.95\linewidth]{img/vyrazy.pdf}}% 96 | \onslide<6>\centerline{\includegraphics[width=0.95\linewidth]{img/generovani_kodu.pdf}}% 97 | \end{overprint} 98 | \vspace{-10cm} 99 | \begin{overprint} 100 | \onslide<1> 101 | \begin{itemize} 102 | \item IFJ17. 103 | \end{itemize} 104 | \onslide<2> 105 | \begin{itemize} 106 | \item Nastavení vstupního souboru. 107 | \item Zahájení syntaktické analýzy. 108 | \item Generování kódu nebo vracení chyby na výstup. 109 | \end{itemize} 110 | \onslide<3> 111 | \begin{itemize} 112 | \item Implementace podle LL gramatiky a LL tabulky. 113 | \item Volání lexikální analýzy pro každý token. 114 | \item Volání generování vnitřního kódu. 115 | \item Tabulka symbolů. 116 | \end{itemize} 117 | \onslide<4> 118 | \begin{itemize} 119 | \item Implementace podle konečného automatu. 120 | \item Načítá ze vstupu a vrací token. 121 | \end{itemize} 122 | \onslide<5> 123 | \begin{itemize} 124 | \item Implementace podle precedenční tabulky. 125 | \item Precedenční syntaktická analýza. 126 | \item Sémantická analýza. 127 | \item Volání generování matematických a logických instrukcí. 128 | \end{itemize} 129 | \onslide<6> 130 | \begin{itemize} 131 | \item Generování mezikódu IFJcode17. 132 | \item Generování vestavěných funkcí. 133 | \end{itemize} 134 | \end{overprint} 135 | 136 | \end{frame} 137 | 138 | \section{Práce v~týmu} 139 | 140 | \begin{frame}{Práce v~týmu} 141 | \begin{alertblock}{GitHub} 142 | \begin{itemize} 143 | \item GitHub jako vzdálený repositář. 144 | \item Práce ve své větvi. 145 | \end{itemize} 146 | \end{alertblock} 147 | \begin{alertblock}{Slack} 148 | \begin{itemize} 149 | \item Tématicky rozdělné skupinové konverzace. 150 | \item Automatické notifikace z~GitHubu. 151 | \item Upozornění na blížící se deadline. 152 | \end{itemize} 153 | \end{alertblock} 154 | 155 | \end{frame} 156 | 157 | \section{Dotazy} 158 | 159 | \begin{frame}{Dotazy} 160 | \vspace{-5cm} 161 | \Large{Děkuji za pozornost, prostor pro Vaše dotazy.} 162 | \end{frame} 163 | \end{document} 164 | -------------------------------------------------------------------------------- /prezentace/img/FIT_barevne_CMYK_CZ-eps-converted-to.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/FIT_barevne_CMYK_CZ-eps-converted-to.pdf -------------------------------------------------------------------------------- /prezentace/img/FIT_barevne_CMYK_CZ.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/FIT_barevne_CMYK_CZ.eps -------------------------------------------------------------------------------- /prezentace/img/Generovani_kodu.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Generovani_kodu.pdf -------------------------------------------------------------------------------- /prezentace/img/Generovani_kodu.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Generovani_kodu.vsdx -------------------------------------------------------------------------------- /prezentace/img/Main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Main.pdf -------------------------------------------------------------------------------- /prezentace/img/Main.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Main.vsdx -------------------------------------------------------------------------------- /prezentace/img/Parser.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Parser.pdf -------------------------------------------------------------------------------- /prezentace/img/Parser.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Parser.vsdx -------------------------------------------------------------------------------- /prezentace/img/Scanner.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Scanner.pdf -------------------------------------------------------------------------------- /prezentace/img/Scanner.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Scanner.vsdx -------------------------------------------------------------------------------- /prezentace/img/Vyrazy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Vyrazy.pdf -------------------------------------------------------------------------------- /prezentace/img/Vyrazy.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Vyrazy.vsdx -------------------------------------------------------------------------------- /prezentace/img/Zdrojovy_kod.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Zdrojovy_kod.pdf -------------------------------------------------------------------------------- /prezentace/img/Zdrojovy_kod.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/prezentace/img/Zdrojovy_kod.vsdx -------------------------------------------------------------------------------- /rozdeleni: -------------------------------------------------------------------------------- 1 | xharmi00:25 2 | xhertl04:25 3 | xhalas10:25 4 | xkaras34:25 5 | -------------------------------------------------------------------------------- /scanner.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Scanner implementation. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | * @author Vojtěch Hertl 9 | * @author Matej Karas 10 | */ 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "error.h" 18 | #include "scanner.h" 19 | 20 | 21 | // Scanner states. 22 | #define SCANNER_STATE_START 200 /// Starting state every new token processing starts here and initializes other states 23 | #define SCANNER_STATE_COMMENTARY 201 /// Line commentary, ignores every symbol, ends with EOL 24 | #define SCANNER_STATE_BACKSLASH 202 /// Operator / OR start of block comment - next symbol must be ' 25 | #define SCANNER_STATE_BLOCK_COMMENTARY 203 /// Starts with /' and ignores every symbol except ' - this might be the end of block comment 26 | #define SCANNER_STATE_BLOCK_COMMENTARY_LEAVE 204 /// Ends with '/ the ' is read, if the next symbol is /, leave, else if ', stay, else go back to state before 27 | #define SCANNER_STATE_IDENTIFIER_OR_KEYWORD 205 /// Starts with letter or _, if next symbols are alphanumeric or _, add them to string, which is later compared with reserved words | Returns either keyword or string as ID 28 | #define SCANNER_STATE_NUMBER 206 /// Start of number processing, accepts numbers, e/E and . | Can return integer number 29 | #define SCANNER_STATE_NUMBER_POINT 207 /// If symbol was ., the number has type double 30 | #define SCANNER_STATE_NUMBER_DOUBLE 208 /// The last symbol was number | Can return double number 31 | #define SCANNER_STATE_NUMBER_EXPONENT 209 /// The last symbol was e or E, the number has type double, continues with optional symbols +/- or number 32 | #define SCANNER_STATE_NUMBER_EXPONENT_SIGN 210 /// Optional symbol was read, continue with numbers only 33 | #define SCANNER_STATE_NUMBER_EXPONENT_FINAL 211 /// Returns double number with exponent 34 | #define SCANNER_STATE_STRING_START 212 /// String starts with !" else returns error 35 | #define SCANNER_STATE_STRING 213 /// Sequence !" was read, ends with ", if ASCII value is lower than 32, returns error, these symbols can be written using escape sequence | Returns string 36 | #define SCANNER_STATE_STRING_ESCAPE 214 /// If symbol \ was loaded, can replace char with escape sequence symbols 37 | #define SCANNER_STATE_STRING_ESCAPE_ZERO 215 /// 0 loaded, accepts only digits 38 | #define SCANNER_STATE_STRING_ESCAPE_ZERO_ZERO 216 /// first 0 then 0 was loaded, accepts only 1 to 9, because 000 is not allowed, returns symbol with ASCII value 39 | #define SCANNER_STATE_STRING_ESCAPE_ONE 217 /// 1 was loaded, accepts only digits 40 | #define SCANNER_STATE_STRING_ESCAPE_TWO 218 /// 2 was loaded, accepts onlz digits from 0 to 5, because 255 is the max number allowed 41 | #define SCANNER_STATE_STRING_ESCAPE_TWO_FIVE 219 /// first 2 then 5 was loaded, accepts only 0 to 5 digits, returns symbol with ASCII value 42 | #define SCANNER_STATE_STRING_ESCAPE_REST 220 /// the rest cases, when the third number can be only digit, from 001 to 249 43 | #define SCANNER_STATE_LESS_THAN 221 /// Starts with < | Returns <>, <= or < 44 | #define SCANNER_STATE_MORE_THAN 222 /// Starts with > | Returns > or >= 45 | #define SCANNER_STATE_EOL 223 /// End of line 46 | 47 | 48 | FILE *source_file; /// Source file that will be scanned 49 | Dynamic_string *dynamic_string; /// Dynamic string that will be written into 50 | 51 | 52 | /** 53 | * Free resources and returns exit code. 54 | * 55 | * @param exit_code Exit code. 56 | * @param str Dynamic string to be freed. 57 | * @return Given exit code. 58 | */ 59 | static int free_resources(int exit_code, Dynamic_string *str) 60 | { 61 | dynamic_string_free(str); 62 | 63 | return exit_code; 64 | } 65 | 66 | 67 | /** 68 | * Processing identifier, checks if it is keyword. 69 | * 70 | * @param str Identifier which is being compared. 71 | * @param token Pointer to output token. 72 | * @return 0 (SCANNER_TOKEN_OK) if token is OK, otherwise in case of lex error one of SCANNER_ERROR_... constant. 73 | */ 74 | static int process_identifier(Dynamic_string *str, Token *token) 75 | { 76 | if (!dynamic_string_cmp_const_str(str, "and")) token->attribute.keyword = KEYWORD_AND; 77 | else if (!dynamic_string_cmp_const_str(str, "as")) token->attribute.keyword = KEYWORD_AS; 78 | else if (!dynamic_string_cmp_const_str(str, "asc")) token->attribute.keyword = KEYWORD_ASC; 79 | else if (!dynamic_string_cmp_const_str(str, "boolean")) token->attribute.keyword = KEYWORD_BOOLEAN; 80 | else if (!dynamic_string_cmp_const_str(str, "continue")) token->attribute.keyword = KEYWORD_CONTINUE; 81 | else if (!dynamic_string_cmp_const_str(str, "declare")) token->attribute.keyword = KEYWORD_DECLARE; 82 | else if (!dynamic_string_cmp_const_str(str, "dim")) token->attribute.keyword = KEYWORD_DIM; 83 | else if (!dynamic_string_cmp_const_str(str, "do")) token->attribute.keyword = KEYWORD_DO; 84 | else if (!dynamic_string_cmp_const_str(str, "double")) token->attribute.keyword = KEYWORD_DOUBLE; 85 | else if (!dynamic_string_cmp_const_str(str, "else")) token->attribute.keyword = KEYWORD_ELSE; 86 | else if (!dynamic_string_cmp_const_str(str, "elseif")) token->attribute.keyword = KEYWORD_ELSEIF; 87 | else if (!dynamic_string_cmp_const_str(str, "end")) token->attribute.keyword = KEYWORD_END; 88 | else if (!dynamic_string_cmp_const_str(str, "exit")) token->attribute.keyword = KEYWORD_EXIT; 89 | else if (!dynamic_string_cmp_const_str(str, "false")) token->attribute.keyword = KEYWORD_FALSE; 90 | else if (!dynamic_string_cmp_const_str(str, "for")) token->attribute.keyword = KEYWORD_FOR; 91 | else if (!dynamic_string_cmp_const_str(str, "chr")) token->attribute.keyword = KEYWORD_CHR; 92 | else if (!dynamic_string_cmp_const_str(str, "function")) token->attribute.keyword = KEYWORD_FUNCTION; 93 | else if (!dynamic_string_cmp_const_str(str, "if")) token->attribute.keyword = KEYWORD_IF; 94 | else if (!dynamic_string_cmp_const_str(str, "input")) token->attribute.keyword = KEYWORD_INPUT; 95 | else if (!dynamic_string_cmp_const_str(str, "integer")) token->attribute.keyword = KEYWORD_INTEGER; 96 | else if (!dynamic_string_cmp_const_str(str, "length")) token->attribute.keyword = KEYWORD_LENGTH; 97 | else if (!dynamic_string_cmp_const_str(str, "loop")) token->attribute.keyword = KEYWORD_LOOP; 98 | else if (!dynamic_string_cmp_const_str(str, "next")) token->attribute.keyword = KEYWORD_NEXT; 99 | else if (!dynamic_string_cmp_const_str(str, "not")) token->attribute.keyword = KEYWORD_NOT; 100 | else if (!dynamic_string_cmp_const_str(str, "or")) token->attribute.keyword = KEYWORD_OR; 101 | else if (!dynamic_string_cmp_const_str(str, "print")) token->attribute.keyword = KEYWORD_PRINT; 102 | else if (!dynamic_string_cmp_const_str(str, "return")) token->attribute.keyword = KEYWORD_RETURN; 103 | else if (!dynamic_string_cmp_const_str(str, "scope")) token->attribute.keyword = KEYWORD_SCOPE; 104 | else if (!dynamic_string_cmp_const_str(str, "shared")) token->attribute.keyword = KEYWORD_SHARED; 105 | else if (!dynamic_string_cmp_const_str(str, "string")) token->attribute.keyword = KEYWORD_STRING; 106 | else if (!dynamic_string_cmp_const_str(str, "static")) token->attribute.keyword = KEYWORD_STATIC; 107 | else if (!dynamic_string_cmp_const_str(str, "substr")) token->attribute.keyword = KEYWORD_SUBSTR; 108 | else if (!dynamic_string_cmp_const_str(str, "then")) token->attribute.keyword = KEYWORD_THEN; 109 | else if (!dynamic_string_cmp_const_str(str, "true")) token->attribute.keyword = KEYWORD_TRUE; 110 | else if (!dynamic_string_cmp_const_str(str, "while")) token->attribute.keyword = KEYWORD_WHILE; 111 | else token->type = TOKEN_TYPE_IDENTIFIER; 112 | 113 | if (token->type != TOKEN_TYPE_IDENTIFIER) 114 | { 115 | token->type = TOKEN_TYPE_KEYWORD; 116 | return free_resources(SCANNER_TOKEN_OK, str); 117 | } 118 | 119 | if (!dynamic_string_copy(str, token->attribute.string)) 120 | { 121 | return free_resources(ERROR_INTERNAL, str); 122 | } 123 | 124 | return free_resources(SCANNER_TOKEN_OK, str); 125 | } 126 | 127 | 128 | /** 129 | * Processing integer. 130 | * 131 | * @param str Integer which is being processed. 132 | * @param token Pointer to output token. 133 | * @return 0 (SCANNER_TOKEN_OK) if token is OK, otherwise in case of lex error one of SCANNER_ERROR_... constant. 134 | */ 135 | static int process_integer(Dynamic_string *str, Token *token) 136 | { 137 | char *endptr; 138 | 139 | int val = (int) strtol(str->str, &endptr, 10); 140 | if (*endptr) 141 | { 142 | return free_resources(ERROR_INTERNAL, str); 143 | } 144 | 145 | token->attribute.integer = val; 146 | token->type = TOKEN_TYPE_INT_NUMBER; 147 | 148 | return free_resources(SCANNER_TOKEN_OK, str); 149 | } 150 | 151 | 152 | /** 153 | * Processing decimal. 154 | * 155 | * @param str Decimal which is being processed. 156 | * @param token Pointer to output token. 157 | * @return 0 (SCANNER_TOKEN_OK) if token is OK, otherwise in case of lex error one of SCANNER_ERROR_... constant. 158 | */ 159 | static int process_decimal(Dynamic_string *str, Token *token) 160 | { 161 | char *endptr; 162 | 163 | double val = strtod(str->str, &endptr); 164 | if (*endptr) 165 | { 166 | return free_resources(ERROR_INTERNAL, str); 167 | } 168 | 169 | token->attribute.decimal = val; 170 | token->type = TOKEN_TYPE_DOUBLE_NUMBER; 171 | 172 | return free_resources(SCANNER_TOKEN_OK, str); 173 | } 174 | 175 | 176 | void set_source_file(FILE *f) 177 | { 178 | source_file = f; 179 | } 180 | 181 | 182 | void set_dynamic_string(Dynamic_string *string) 183 | { 184 | dynamic_string = string; 185 | } 186 | 187 | 188 | int get_next_token(Token *token) 189 | { 190 | if (source_file == NULL) 191 | { 192 | return ERROR_INTERNAL; 193 | } 194 | 195 | if (dynamic_string == NULL) 196 | { 197 | return ERROR_INTERNAL; 198 | } 199 | 200 | token->attribute.string = dynamic_string; 201 | 202 | // inicialization 203 | Dynamic_string string; 204 | Dynamic_string *str = &string; 205 | if (!dynamic_string_init(str)) 206 | { 207 | return ERROR_INTERNAL; 208 | } 209 | 210 | int state = SCANNER_STATE_START; 211 | token->type = TOKEN_TYPE_EMPTY; 212 | 213 | char c, *endptr, strnum[4] = { 0 }; 214 | 215 | // reading chars from source_file 216 | while (true) 217 | { 218 | c = (char) getc(source_file); 219 | 220 | switch (state) 221 | { 222 | case (SCANNER_STATE_START): 223 | if (c == '\n') 224 | { 225 | state = SCANNER_STATE_EOL; 226 | } 227 | else if (isspace(c)) 228 | { 229 | state = SCANNER_STATE_START; 230 | } 231 | else if (c == '\'') 232 | { 233 | state = SCANNER_STATE_COMMENTARY; 234 | } 235 | else if (c == '/') 236 | { 237 | state = SCANNER_STATE_BACKSLASH; 238 | } 239 | else if (isalpha(c) || c == '_') 240 | { 241 | if (!dynamic_string_add_char(str, (char) tolower(c))) 242 | { 243 | return free_resources(ERROR_INTERNAL, str); 244 | } 245 | state = SCANNER_STATE_IDENTIFIER_OR_KEYWORD; 246 | } 247 | else if (isdigit(c)) 248 | { 249 | if (!dynamic_string_add_char(str, c)) 250 | { 251 | return free_resources(ERROR_INTERNAL, str); 252 | } 253 | state = SCANNER_STATE_NUMBER; 254 | } 255 | else if (c == '!') 256 | { 257 | state = SCANNER_STATE_STRING_START; 258 | } 259 | else if (c == '<') 260 | { 261 | state = SCANNER_STATE_LESS_THAN; 262 | } 263 | else if (c == '>') 264 | { 265 | state = SCANNER_STATE_MORE_THAN; 266 | } 267 | else if (c == '=') 268 | { 269 | token->type = TOKEN_TYPE_ASSIGN; 270 | return free_resources(SCANNER_TOKEN_OK, str); 271 | } 272 | else if (c == '+') 273 | { 274 | token->type = TOKEN_TYPE_PLUS; 275 | return free_resources(SCANNER_TOKEN_OK, str); 276 | } 277 | else if (c == '-') 278 | { 279 | token->type = TOKEN_TYPE_MINUS; 280 | return free_resources(SCANNER_TOKEN_OK, str); 281 | } 282 | else if (c == '*') 283 | { 284 | token->type = TOKEN_TYPE_MUL; 285 | return free_resources(SCANNER_TOKEN_OK, str); 286 | } 287 | else if (c == '\\') 288 | { 289 | token->type = TOKEN_TYPE_IDIV; 290 | return free_resources(SCANNER_TOKEN_OK, str); 291 | } 292 | else if (c == '(') 293 | { 294 | token->type = TOKEN_TYPE_LEFT_BRACKET; 295 | return free_resources(SCANNER_TOKEN_OK, str); 296 | } 297 | else if (c == ')') 298 | { 299 | token->type = TOKEN_TYPE_RIGHT_BRACKET; 300 | return free_resources(SCANNER_TOKEN_OK, str); 301 | } 302 | else if (c == ',') 303 | { 304 | token->type = TOKEN_TYPE_COMMA; 305 | return free_resources(SCANNER_TOKEN_OK, str); 306 | } 307 | else if (c == ';') 308 | { 309 | token->type = TOKEN_TYPE_SEMICOLON; 310 | return free_resources(SCANNER_TOKEN_OK, str); 311 | } 312 | else if (c == EOF) 313 | { 314 | token->type = TOKEN_TYPE_EOF; 315 | return free_resources(SCANNER_TOKEN_OK, str); 316 | } 317 | else 318 | { 319 | return free_resources(SCANNER_ERROR_LEX, str); 320 | } 321 | 322 | break; 323 | 324 | case (SCANNER_STATE_COMMENTARY): 325 | if (c == '\n' || c == EOF) 326 | { 327 | state = SCANNER_STATE_START; 328 | ungetc(c, source_file); 329 | } 330 | 331 | break; 332 | 333 | case (SCANNER_STATE_BACKSLASH): 334 | if (c == '\'') 335 | { 336 | state = SCANNER_STATE_BLOCK_COMMENTARY; 337 | } 338 | else 339 | { 340 | ungetc(c, source_file); 341 | token->type = TOKEN_TYPE_DIV; 342 | return free_resources(SCANNER_TOKEN_OK, str); 343 | } 344 | 345 | break; 346 | 347 | case (SCANNER_STATE_BLOCK_COMMENTARY): 348 | if (c == '\'') 349 | { 350 | state = SCANNER_STATE_BLOCK_COMMENTARY_LEAVE; 351 | } 352 | else if (c == EOF) 353 | { 354 | return free_resources(SCANNER_ERROR_LEX, str); 355 | } 356 | 357 | break; 358 | 359 | case (SCANNER_STATE_BLOCK_COMMENTARY_LEAVE): 360 | if (c == EOF) 361 | { 362 | return free_resources(SCANNER_ERROR_LEX, str); 363 | } 364 | else if (c == '/') 365 | { 366 | state = SCANNER_STATE_START; 367 | } 368 | else if (c == '\'') 369 | { 370 | state = SCANNER_STATE_BLOCK_COMMENTARY_LEAVE; 371 | } 372 | else 373 | { 374 | state = SCANNER_STATE_BLOCK_COMMENTARY; 375 | } 376 | 377 | break; 378 | 379 | case (SCANNER_STATE_IDENTIFIER_OR_KEYWORD): 380 | if (isalnum(c) || c == '_') 381 | { 382 | if (!dynamic_string_add_char(str, (char) tolower(c))) 383 | { 384 | return free_resources(ERROR_INTERNAL, str); 385 | } 386 | } 387 | else 388 | { 389 | ungetc(c, source_file); 390 | return process_identifier(str, token); 391 | } 392 | 393 | break; 394 | 395 | case (SCANNER_STATE_NUMBER): 396 | if (isdigit(c)) 397 | { 398 | if (!dynamic_string_add_char(str, c)) 399 | { 400 | return free_resources(ERROR_INTERNAL, str); 401 | } 402 | } 403 | else if (c == '.') 404 | { 405 | state = SCANNER_STATE_NUMBER_POINT; 406 | if (!dynamic_string_add_char(str, c)) 407 | { 408 | return free_resources(ERROR_INTERNAL, str); 409 | } 410 | } 411 | else if (tolower(c) == 'e') 412 | { 413 | state = SCANNER_STATE_NUMBER_EXPONENT; 414 | if (!dynamic_string_add_char(str, c)) 415 | { 416 | return free_resources(ERROR_INTERNAL, str); 417 | } 418 | } 419 | else 420 | { 421 | ungetc(c, source_file); 422 | return process_integer(str, token); 423 | } 424 | 425 | break; 426 | 427 | case (SCANNER_STATE_NUMBER_POINT): 428 | if (isdigit(c)) 429 | { 430 | state = SCANNER_STATE_NUMBER_DOUBLE; 431 | if (!dynamic_string_add_char(str, c)) 432 | { 433 | return free_resources(ERROR_INTERNAL, str); 434 | } 435 | } 436 | else 437 | { 438 | return free_resources(SCANNER_ERROR_LEX, str); 439 | } 440 | 441 | break; 442 | 443 | case (SCANNER_STATE_NUMBER_DOUBLE): 444 | if (isdigit(c)) 445 | { 446 | if (!dynamic_string_add_char(str, c)) 447 | { 448 | return free_resources(ERROR_INTERNAL, str); 449 | } 450 | } 451 | else if (tolower(c) == 'e') 452 | { 453 | state = SCANNER_STATE_NUMBER_EXPONENT; 454 | if (!dynamic_string_add_char(str, c)) 455 | { 456 | return free_resources(ERROR_INTERNAL, str); 457 | } 458 | } 459 | else 460 | { 461 | ungetc(c, source_file); 462 | return process_decimal(str, token); 463 | } 464 | 465 | break; 466 | 467 | case (SCANNER_STATE_NUMBER_EXPONENT): 468 | if (isdigit(c)) 469 | { 470 | state = SCANNER_STATE_NUMBER_EXPONENT_FINAL; 471 | if (!dynamic_string_add_char(str, c)) 472 | { 473 | return free_resources(ERROR_INTERNAL, str); 474 | } 475 | } 476 | else if (c == '+' || c == '-') 477 | { 478 | state = SCANNER_STATE_NUMBER_EXPONENT_SIGN; 479 | if (!dynamic_string_add_char(str, c)) 480 | { 481 | return free_resources(ERROR_INTERNAL, str); 482 | } 483 | } 484 | else 485 | { 486 | return free_resources(SCANNER_ERROR_LEX, str); 487 | } 488 | 489 | break; 490 | 491 | case (SCANNER_STATE_NUMBER_EXPONENT_SIGN): 492 | if (isdigit(c)) 493 | { 494 | state = SCANNER_STATE_NUMBER_EXPONENT_FINAL; 495 | if (!dynamic_string_add_char(str, c)) 496 | { 497 | return free_resources(ERROR_INTERNAL, str); 498 | } 499 | } 500 | else 501 | { 502 | return free_resources(SCANNER_ERROR_LEX, str); 503 | } 504 | 505 | break; 506 | 507 | case (SCANNER_STATE_NUMBER_EXPONENT_FINAL): 508 | if (isdigit(c)) 509 | { 510 | if (!dynamic_string_add_char(str, c)) 511 | { 512 | return free_resources(ERROR_INTERNAL, str); 513 | } 514 | } 515 | else 516 | { 517 | ungetc(c, source_file); 518 | return process_decimal(str, token); 519 | } 520 | 521 | break; 522 | 523 | case (SCANNER_STATE_STRING_START): 524 | if (c == '"') 525 | { 526 | state = SCANNER_STATE_STRING; 527 | } 528 | else 529 | { 530 | return free_resources(SCANNER_ERROR_LEX, str); 531 | } 532 | 533 | break; 534 | 535 | case (SCANNER_STATE_STRING): 536 | if (c < 32) 537 | { 538 | return free_resources(SCANNER_ERROR_LEX, str); 539 | } 540 | else if (c == '\\') 541 | { 542 | state = SCANNER_STATE_STRING_ESCAPE; 543 | } 544 | else if (c == '"') 545 | { 546 | if (!dynamic_string_copy(str, token->attribute.string)) 547 | { 548 | return free_resources(ERROR_INTERNAL, str); 549 | } 550 | token->type = TOKEN_TYPE_STRING; 551 | 552 | return free_resources(SCANNER_TOKEN_OK, str); 553 | } 554 | else 555 | { 556 | if (!dynamic_string_add_char(str, c)) 557 | { 558 | return free_resources(ERROR_INTERNAL, str); 559 | } 560 | } 561 | 562 | break; 563 | 564 | case (SCANNER_STATE_STRING_ESCAPE): 565 | if (c < 32) 566 | { 567 | return free_resources(SCANNER_ERROR_LEX, str); 568 | } 569 | else if (c == 'n') 570 | { 571 | c = '\n'; 572 | if (!dynamic_string_add_char(str, c)) 573 | { 574 | return free_resources(ERROR_INTERNAL, str); 575 | } 576 | state = SCANNER_STATE_STRING; 577 | } 578 | else if (c == '"') 579 | { 580 | c = '"'; 581 | if (!dynamic_string_add_char(str, c)) 582 | { 583 | return free_resources(ERROR_INTERNAL, str); 584 | } 585 | state = SCANNER_STATE_STRING; 586 | } 587 | else if (c == 't') 588 | { 589 | c = '\t'; 590 | if (!dynamic_string_add_char(str, c)) 591 | { 592 | return free_resources(ERROR_INTERNAL, str); 593 | } 594 | state = SCANNER_STATE_STRING; 595 | } 596 | else if (c == '\\') 597 | { 598 | c = '\\'; 599 | if (!dynamic_string_add_char(str, c)) 600 | { 601 | return free_resources(ERROR_INTERNAL, str); 602 | } 603 | state = SCANNER_STATE_STRING; 604 | } 605 | else if (c == '0') 606 | { 607 | strnum[0] = c; 608 | state = SCANNER_STATE_STRING_ESCAPE_ZERO; 609 | } 610 | else if (c == '1') 611 | { 612 | strnum[0] = c; 613 | state = SCANNER_STATE_STRING_ESCAPE_ONE; 614 | } 615 | else if (c == '2') 616 | { 617 | strnum[0] = c; 618 | state = SCANNER_STATE_STRING_ESCAPE_TWO; 619 | } 620 | else 621 | { 622 | return free_resources(SCANNER_ERROR_LEX, str); 623 | } 624 | 625 | break; 626 | 627 | case (SCANNER_STATE_STRING_ESCAPE_ZERO): 628 | if (c == '0') 629 | { 630 | strnum[1] = c; 631 | state = SCANNER_STATE_STRING_ESCAPE_ZERO_ZERO; 632 | } 633 | else if (isdigit(c)) 634 | { 635 | strnum[1] = c; 636 | state = SCANNER_STATE_STRING_ESCAPE_REST; 637 | } 638 | else 639 | { 640 | return free_resources(SCANNER_ERROR_LEX, str); 641 | } 642 | 643 | break; 644 | 645 | case (SCANNER_STATE_STRING_ESCAPE_ZERO_ZERO): 646 | if (isdigit(c) && c != '0') 647 | { 648 | strnum[2] = c; 649 | state = SCANNER_STATE_STRING; 650 | 651 | int val = (int) strtol(strnum, &endptr, 10); 652 | if (*endptr) 653 | { 654 | return free_resources(ERROR_INTERNAL, str); 655 | } 656 | 657 | c = (char) val; 658 | if (!dynamic_string_add_char(str, c)) 659 | { 660 | return free_resources(ERROR_INTERNAL, str); 661 | } 662 | } 663 | else 664 | { 665 | return free_resources(SCANNER_ERROR_LEX, str); 666 | } 667 | 668 | break; 669 | 670 | case (SCANNER_STATE_STRING_ESCAPE_ONE): 671 | if (isdigit(c)) 672 | { 673 | strnum[1] = c; 674 | state = SCANNER_STATE_STRING_ESCAPE_REST; 675 | } 676 | else 677 | { 678 | return free_resources(SCANNER_ERROR_LEX, str); 679 | } 680 | 681 | break; 682 | 683 | case (SCANNER_STATE_STRING_ESCAPE_TWO): 684 | if (isdigit(c)) 685 | { 686 | if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4') 687 | { 688 | strnum[1] = c; 689 | state = SCANNER_STATE_STRING_ESCAPE_REST; 690 | } 691 | else if (c == '5') 692 | { 693 | strnum[1] = c; 694 | state = SCANNER_STATE_STRING_ESCAPE_TWO_FIVE; 695 | } 696 | else 697 | { 698 | return free_resources(SCANNER_ERROR_LEX, str); 699 | } 700 | } 701 | else 702 | { 703 | return free_resources(SCANNER_ERROR_LEX, str); 704 | } 705 | 706 | break; 707 | 708 | case (SCANNER_STATE_STRING_ESCAPE_TWO_FIVE): 709 | if (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5') 710 | { 711 | strnum[2] = c; 712 | state = SCANNER_STATE_STRING; 713 | 714 | int val = (int) strtol(strnum, &endptr, 10); 715 | if (*endptr) 716 | { 717 | return free_resources(ERROR_INTERNAL, str); 718 | } 719 | 720 | c = (char) val; 721 | if (!dynamic_string_add_char(str, c)) 722 | { 723 | return free_resources(ERROR_INTERNAL, str); 724 | } 725 | } 726 | else 727 | { 728 | return free_resources(SCANNER_ERROR_LEX, str); 729 | } 730 | 731 | break; 732 | 733 | case (SCANNER_STATE_STRING_ESCAPE_REST): 734 | if (isdigit(c)) 735 | { 736 | strnum[2] = c; 737 | state = SCANNER_STATE_STRING; 738 | 739 | int val = (int) strtol(strnum, &endptr, 10); 740 | if (*endptr) 741 | { 742 | return free_resources(ERROR_INTERNAL, str); 743 | } 744 | 745 | c = (char) val; 746 | if (!dynamic_string_add_char(str, c)) 747 | { 748 | return free_resources(ERROR_INTERNAL, str); 749 | } 750 | } 751 | else 752 | { 753 | return free_resources(SCANNER_ERROR_LEX, str); 754 | } 755 | 756 | break; 757 | 758 | case (SCANNER_STATE_LESS_THAN): 759 | if (c == '>') 760 | { 761 | token->type = TOKEN_TYPE_NEQ; 762 | } 763 | else if (c == '=') 764 | { 765 | token->type = TOKEN_TYPE_LEQ; 766 | } 767 | else 768 | { 769 | ungetc(c, source_file); 770 | token->type = TOKEN_TYPE_LTN; 771 | } 772 | 773 | return free_resources(SCANNER_TOKEN_OK, str); 774 | 775 | case (SCANNER_STATE_MORE_THAN): 776 | if (c == '=') 777 | { 778 | token->type = TOKEN_TYPE_MEQ; 779 | } 780 | else 781 | { 782 | ungetc(c, source_file); 783 | token->type = TOKEN_TYPE_MTN; 784 | } 785 | 786 | return free_resources(SCANNER_TOKEN_OK, str); 787 | 788 | case (SCANNER_STATE_EOL): 789 | if (isspace(c)) 790 | { 791 | break; 792 | } 793 | 794 | ungetc(c, source_file); 795 | token->type = TOKEN_TYPE_EOL; 796 | return free_resources(SCANNER_TOKEN_OK, str); 797 | } 798 | } 799 | } 800 | -------------------------------------------------------------------------------- /scanner.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Scanner interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | * @author Vojtěch Hertl 9 | */ 10 | 11 | 12 | #ifndef _SCANNER_H 13 | #define _SCANNER_H 14 | 15 | 16 | #include 17 | 18 | #include "dynamic_string.h" 19 | 20 | 21 | /** 22 | * @enum Reserved keywords. 23 | */ 24 | typedef enum 25 | { 26 | KEYWORD_AND, 27 | KEYWORD_AS, 28 | KEYWORD_ASC, 29 | KEYWORD_BOOLEAN, 30 | KEYWORD_CONTINUE, 31 | KEYWORD_DECLARE, 32 | KEYWORD_DIM, 33 | KEYWORD_DO, 34 | KEYWORD_DOUBLE, 35 | KEYWORD_ELSE, 36 | KEYWORD_ELSEIF, 37 | KEYWORD_END, 38 | KEYWORD_EXIT, 39 | KEYWORD_FALSE, 40 | KEYWORD_FOR, 41 | KEYWORD_CHR, 42 | KEYWORD_FUNCTION, 43 | KEYWORD_IF, 44 | KEYWORD_INPUT, 45 | KEYWORD_INTEGER, 46 | KEYWORD_LENGTH, 47 | KEYWORD_LOOP, 48 | KEYWORD_NEXT, 49 | KEYWORD_NOT, 50 | KEYWORD_OR, 51 | KEYWORD_PRINT, 52 | KEYWORD_RETURN, 53 | KEYWORD_SCOPE, 54 | KEYWORD_SHARED, 55 | KEYWORD_STRING, 56 | KEYWORD_STATIC, 57 | KEYWORD_SUBSTR, 58 | KEYWORD_THEN, 59 | KEYWORD_TRUE, 60 | KEYWORD_WHILE, 61 | } Keyword; 62 | 63 | /** 64 | * @enum Type of token. 65 | */ 66 | typedef enum 67 | { 68 | TOKEN_TYPE_EOF, /// End of file 69 | TOKEN_TYPE_EOL, /// End of line 70 | TOKEN_TYPE_EMPTY, /// Empty 71 | TOKEN_TYPE_IDENTIFIER, /// Identifier 72 | TOKEN_TYPE_KEYWORD, /// Keyword 73 | 74 | TOKEN_TYPE_INT_NUMBER, /// Integer number 75 | TOKEN_TYPE_DOUBLE_NUMBER, /// Double number 76 | TOKEN_TYPE_STRING, /// String 77 | 78 | TOKEN_TYPE_NEQ, /// Not equal <> 79 | TOKEN_TYPE_LEQ, /// Less or equal <= 80 | TOKEN_TYPE_LTN, /// Less than < 81 | TOKEN_TYPE_MEQ, /// More or equal >= 82 | TOKEN_TYPE_MTN, /// More than > 83 | 84 | // operators 85 | TOKEN_TYPE_ASSIGN, /// Assign = 86 | TOKEN_TYPE_PLUS, /// Plus + 87 | TOKEN_TYPE_MINUS, /// Minus - 88 | TOKEN_TYPE_MUL, /// Multiplication * 89 | TOKEN_TYPE_DIV, /// Division / result always double 90 | TOKEN_TYPE_IDIV, /// Integer division \ only works with integers 91 | 92 | TOKEN_TYPE_LEFT_BRACKET, /// Left bracket ( 93 | TOKEN_TYPE_RIGHT_BRACKET, /// Right bracket ) 94 | TOKEN_TYPE_COMMA, /// Comma , 95 | TOKEN_TYPE_SEMICOLON, /// Semicolon ; 96 | } Token_type; 97 | 98 | /** 99 | * @union Token attribute. 100 | */ 101 | typedef union 102 | { 103 | Dynamic_string *string; /// String or identifier value. 104 | int integer; /// Integer value. 105 | Keyword keyword; /// Keyword, one of the KEYWORD_... constant 106 | double decimal; /// Decimal value. 107 | } Token_attribute; 108 | 109 | /** 110 | * @struct Token representation. 111 | */ 112 | typedef struct 113 | { 114 | Token_type type; /// Token type, one of token_type constants. 115 | Token_attribute attribute; /// Attribute of token. 116 | } Token; 117 | 118 | 119 | /** 120 | * Sets source file to be scanned. 121 | * 122 | * @param f Pointer to source file. 123 | */ 124 | void set_source_file(FILE *f); 125 | 126 | /** 127 | * Sets dynamic string to be written into. 128 | * 129 | * @param string Pointer to dynamic string. 130 | */ 131 | void set_dynamic_string(Dynamic_string *string); 132 | 133 | /** 134 | * This is the main function of scanner, scans token after token and sends it further. 135 | * 136 | * @param token Pointer to output token. 137 | * @return 0 (SCANNER_TOKEN_OK) if token is OK, otherwise in case of lex error one of SCANNER_ERROR_... constant. 138 | */ 139 | int get_next_token(Token *token); 140 | 141 | 142 | #endif //_SCANNER_H 143 | -------------------------------------------------------------------------------- /symstack.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Stack of symbols implementation. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #include 12 | 13 | #include "symstack.h" 14 | 15 | 16 | void symbol_stack_init(Symbol_stack* stack) 17 | { 18 | stack->top = NULL; 19 | } 20 | 21 | 22 | bool symbol_stack_push(Symbol_stack* stack, Prec_table_symbol_enum symbol, Data_type type) 23 | { 24 | Symbol_stack_item* new_item = (Symbol_stack_item*)malloc(sizeof(Symbol_stack_item)); 25 | 26 | if (new_item == NULL) 27 | return false; 28 | 29 | new_item->symbol = symbol; 30 | new_item->data_type = type; 31 | new_item->next = stack->top; 32 | 33 | stack->top = new_item; 34 | 35 | return true; 36 | } 37 | 38 | 39 | bool symbol_stack_pop(Symbol_stack* stack) 40 | { 41 | if (stack->top != NULL) 42 | { 43 | Symbol_stack_item* tmp = stack->top; 44 | stack->top = tmp->next; 45 | free(tmp); 46 | 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | 53 | void symbol_stack_pop_count(Symbol_stack* stack, int count) 54 | { 55 | for (int i = 0; i < count; i++) 56 | { 57 | symbol_stack_pop(stack); 58 | } 59 | } 60 | 61 | 62 | Symbol_stack_item* symbol_stack_top_terminal(Symbol_stack* stack) 63 | { 64 | for (Symbol_stack_item* tmp = stack->top; tmp != NULL; tmp = tmp->next) 65 | { 66 | if (tmp->symbol < STOP) 67 | return tmp; 68 | } 69 | 70 | return NULL; 71 | } 72 | 73 | 74 | bool symbol_stack_insert_after_top_terminal(Symbol_stack* stack, Prec_table_symbol_enum symbol, Data_type type) 75 | { 76 | Symbol_stack_item* prev = NULL; 77 | 78 | for (Symbol_stack_item* tmp = stack->top; tmp != NULL; tmp = tmp->next) 79 | { 80 | if (tmp->symbol < STOP) 81 | { 82 | Symbol_stack_item* new_item = (Symbol_stack_item*)malloc(sizeof(Symbol_stack_item)); 83 | 84 | if (new_item == NULL) 85 | return false; 86 | 87 | new_item->symbol = symbol; 88 | new_item->data_type = type; 89 | 90 | if (prev == NULL) 91 | { 92 | new_item->next = stack->top; 93 | stack->top = new_item; 94 | } 95 | else 96 | { 97 | new_item->next = prev->next; 98 | prev->next = new_item; 99 | } 100 | 101 | return true; 102 | } 103 | 104 | prev = tmp; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | 111 | Symbol_stack_item* symbol_stack_top(Symbol_stack* stack) 112 | { 113 | return stack->top; 114 | } 115 | 116 | 117 | void symbol_stack_free(Symbol_stack* stack) 118 | { 119 | while (symbol_stack_pop(stack)); 120 | } 121 | -------------------------------------------------------------------------------- /symstack.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Stack of tokens interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #ifndef _SYMSTACK_H 12 | #define _SYMSTACK_H 13 | 14 | 15 | #include 16 | 17 | #include "expression.h" 18 | #include "symtable.h" 19 | 20 | 21 | /** 22 | * @struct Stack item represetation. 23 | */ 24 | typedef struct stack_item 25 | { 26 | Prec_table_symbol_enum symbol; /// Symbol of stack item. 27 | Data_type data_type; /// Data type used for semantic analysis. 28 | struct stack_item *next; /// Pointer to next stack item. 29 | } Symbol_stack_item; 30 | 31 | /** 32 | * @struct Stack representation. 33 | */ 34 | typedef struct 35 | { 36 | Symbol_stack_item *top; /// Pointer to stack item on top of stack. 37 | } Symbol_stack; 38 | 39 | 40 | /** 41 | * Function initializes stack. 42 | * 43 | * @param stack Pointer to stack. 44 | */ 45 | void symbol_stack_init(Symbol_stack* stack); 46 | 47 | /** 48 | * Function pushes symbol to stack and sets its data type. 49 | * 50 | * @param stack Pointer to stack. 51 | * @param symbol Symbol to be pushed. 52 | * @param type Data type to be set. 53 | * @return True if successfull else false. 54 | */ 55 | bool symbol_stack_push(Symbol_stack* stack, Prec_table_symbol_enum symbol, Data_type type); 56 | 57 | /** 58 | * Function pops top symbol from stack. 59 | * 60 | * @param stack Pointer to stack. 61 | * @return True if successfull else false. 62 | */ 63 | bool symbol_stack_pop(Symbol_stack* stack); 64 | 65 | /** 66 | * Function pops stack more times. 67 | * 68 | * @param stack Pointer to stack. 69 | * @param count How many times stack will be popped. 70 | */ 71 | void symbol_stack_pop_count(Symbol_stack* stack, int count); 72 | 73 | /** 74 | * Function returns top termial. 75 | * 76 | * @param stack Pointer to stack. 77 | * @return Returns pointer to top terminal. 78 | */ 79 | Symbol_stack_item* symbol_stack_top_terminal(Symbol_stack* stack); 80 | 81 | /** 82 | * Function inserts symbol after top terminal. 83 | * 84 | * @param stack Pointer to stack. 85 | * @param symbol Symbol to be pushed. 86 | * @param type Data type to be set. 87 | * @return True if successfull else false. 88 | */ 89 | bool symbol_stack_insert_after_top_terminal(Symbol_stack* stack, Prec_table_symbol_enum symbol, Data_type type); 90 | 91 | /** 92 | * Function returns top symbol. 93 | * 94 | * @param stack Pointer to stack. 95 | * @return Pointer to symbol on top of stack. 96 | */ 97 | Symbol_stack_item* symbol_stack_top(Symbol_stack* stack); 98 | 99 | /** 100 | * Function frees resources used for stack. 101 | * 102 | * @param stack Pointer to stack. 103 | */ 104 | void symbol_stack_free(Symbol_stack* stack); 105 | 106 | 107 | #endif //_SYMSTACK_H 108 | -------------------------------------------------------------------------------- /symtable.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Symbol table implementation using hash table. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | 14 | #include "symtable.h" 15 | 16 | 17 | /** 18 | * Calculates index to table (hash). 19 | * GNU Hash ELF. Algorithm implementation used in UNIX ELF. 20 | * @link https://blogs.oracle.com/ali/gnu-hash-elf-sections 21 | * @link https://en.wikipedia.org/wiki/PJW_hash_function 22 | * 23 | * @param str String from which hash will be calculated. 24 | * @return Returns calculated hash. 25 | */ 26 | static unsigned long hash_function(const char *str) 27 | { 28 | unsigned long hash = 0, x = 0; 29 | 30 | for (char c = *str; c != '\0'; c = *(++str)) 31 | { 32 | hash = (hash << 4) + c; 33 | if ((x = hash & 0xF0000000L) != 0) 34 | { 35 | hash ^= (x >> 24); 36 | } 37 | hash &= ~x; 38 | } 39 | 40 | return hash % MAX_SYMTABLE_SIZE; 41 | } 42 | 43 | 44 | void sym_table_init(Sym_table *table) 45 | { 46 | if (table == NULL) 47 | return; 48 | 49 | for (int i = 0; i < MAX_SYMTABLE_SIZE; i++) 50 | { 51 | (*table)[i] = NULL; 52 | } 53 | } 54 | 55 | 56 | TData *sym_table_add_symbol(Sym_table *table, const char *key, bool* alloc_failed) 57 | { 58 | *alloc_failed = false; 59 | 60 | if (table == NULL || key == NULL) 61 | { 62 | *alloc_failed = true; 63 | return NULL; 64 | } 65 | 66 | unsigned long index = hash_function(key); 67 | Sym_table_item *tmp_last = NULL; 68 | 69 | for (Sym_table_item *tmp = (*table)[index]; tmp != NULL; tmp = tmp->next) 70 | { 71 | if (!strcmp(key, tmp->key)) 72 | { 73 | return NULL; 74 | } 75 | 76 | tmp_last = tmp; 77 | } 78 | 79 | Sym_table_item *new_item = (Sym_table_item *)malloc(sizeof(Sym_table_item)); 80 | if (new_item == NULL) 81 | { 82 | *alloc_failed = true; 83 | return NULL; 84 | } 85 | 86 | if (!(new_item->key = (char *)malloc((strlen(key) + 1) * sizeof(char)))) 87 | { 88 | free(new_item); 89 | *alloc_failed = true; 90 | return NULL; 91 | } 92 | if (!(new_item->data.params = (Dynamic_string *)malloc(sizeof(Dynamic_string)))) 93 | { 94 | free(new_item->key); 95 | free(new_item); 96 | *alloc_failed = true; 97 | return NULL; 98 | } 99 | if (!dynamic_string_init(new_item->data.params)) 100 | { 101 | free(new_item->key); 102 | free(new_item); 103 | free(new_item->data.params); 104 | *alloc_failed = true; 105 | return NULL; 106 | } 107 | 108 | strcpy(new_item->key, key); 109 | new_item->data.identifier = new_item->key; 110 | new_item->data.type = TYPE_UNDEFINED; 111 | new_item->data.defined = false; 112 | new_item->data.global = false; 113 | new_item->next = NULL; 114 | 115 | if (tmp_last == NULL) 116 | (*table)[index] = new_item; 117 | else 118 | tmp_last->next = new_item; 119 | 120 | return &new_item->data; 121 | } 122 | 123 | 124 | bool sym_table_add_param(TData *data, int data_type) 125 | { 126 | if (data == NULL) 127 | return false; 128 | 129 | switch (data_type) 130 | { 131 | case (TYPE_INT): 132 | if (!dynamic_string_add_char(data->params, 'i')) 133 | { 134 | return false; 135 | } 136 | break; 137 | 138 | case (TYPE_DOUBLE): 139 | if (!dynamic_string_add_char(data->params, 'd')) 140 | { 141 | return false; 142 | } 143 | break; 144 | 145 | case (TYPE_STRING): 146 | if (!dynamic_string_add_char(data->params, 's')) 147 | { 148 | return false; 149 | } 150 | break; 151 | 152 | default: 153 | break; 154 | } 155 | 156 | return true; 157 | } 158 | 159 | 160 | TData *sym_table_search(Sym_table *table, const char *key) 161 | { 162 | if (table == NULL || key == NULL) 163 | return NULL; 164 | 165 | unsigned long index = hash_function(key); 166 | 167 | for (Sym_table_item *tmp = (*table)[index]; tmp != NULL; tmp = tmp->next) 168 | { 169 | if (!strcmp(key, tmp->key)) 170 | { 171 | return &tmp->data; 172 | } 173 | } 174 | 175 | return NULL; 176 | } 177 | 178 | 179 | bool sym_table_remove_symbol(Sym_table *table, const char *key) 180 | { 181 | if (table == NULL || key == NULL) 182 | return false; 183 | 184 | unsigned long index = hash_function(key); 185 | 186 | Sym_table_item *tmp_last = NULL; 187 | 188 | for (Sym_table_item *tmp = (*table)[index]; tmp != NULL; tmp = tmp->next) 189 | { 190 | if (!strcmp(key, tmp->key)) 191 | { 192 | if (tmp_last == NULL) 193 | { 194 | (*table)[index] = (*table)[index]->next; 195 | } 196 | else 197 | { 198 | tmp_last->next = tmp->next; 199 | } 200 | 201 | free(tmp->key); 202 | 203 | if (tmp->data.params != NULL) 204 | { 205 | dynamic_string_free(tmp->data.params); 206 | free(tmp->data.params); 207 | } 208 | 209 | free(tmp); 210 | 211 | return true; 212 | } 213 | tmp_last = tmp; 214 | } 215 | 216 | return false; 217 | } 218 | 219 | 220 | void sym_table_free(Sym_table *table) 221 | { 222 | if (table == NULL) 223 | return; 224 | 225 | Sym_table_item *tmp_next = NULL; 226 | 227 | for (int i = 0; i < MAX_SYMTABLE_SIZE; i++) 228 | { 229 | for (Sym_table_item *tmp = (*table)[i]; tmp != NULL; tmp = tmp_next) 230 | { 231 | tmp_next = tmp->next; 232 | free(tmp->key); 233 | 234 | if (tmp->data.params != NULL) 235 | { 236 | dynamic_string_free(tmp->data.params); 237 | free(tmp->data.params); 238 | } 239 | 240 | free(tmp); 241 | } 242 | 243 | (*table)[i] = NULL; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /symtable.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Project: Implementace překladače imperativního jazyka IFJ17. 3 | * 4 | * @brief Symbol table interface. 5 | * 6 | * @author Timotej Halás 7 | * @author Dominik Harmim 8 | * @author Matej Karas 9 | */ 10 | 11 | 12 | #ifndef _SYMTABLE_H 13 | #define _SYMTABLE_H 14 | 15 | 16 | #include 17 | 18 | #include "dynamic_string.h" 19 | 20 | 21 | #define MAX_SYMTABLE_SIZE 27457 /// Symbol table size. (prime number) Try keep the load factor at 75% or less. 22 | 23 | 24 | /** 25 | * @enum Data types. 26 | */ 27 | typedef enum 28 | { 29 | TYPE_UNDEFINED, /// Data type undefined 30 | TYPE_INT, /// Integer data type 31 | TYPE_DOUBLE, /// Double data type 32 | TYPE_STRING, /// String data type 33 | TYPE_BOOL, /// Bool data type (actaully it's kinda imaginary) 34 | } Data_type; 35 | 36 | /** 37 | * @struct Item data representation. 38 | */ 39 | typedef struct 40 | { 41 | Data_type type; /// Data type of symbol / return type of function 42 | bool defined; /// Defined if current function was defined 43 | Dynamic_string *params; /// parameters in string form 44 | char *identifier; /// Data identifier (key). 45 | bool global; /// Global (internal) variable. 46 | } TData; 47 | 48 | /** 49 | * @struct Symbol table item representation. 50 | */ 51 | typedef struct htab_listitem 52 | { 53 | char *key; /// identifier 54 | TData data; /// data 55 | struct htab_listitem *next; /// ptr to next item 56 | } Sym_table_item; 57 | 58 | // Symbol table 59 | typedef Sym_table_item* Sym_table[MAX_SYMTABLE_SIZE]; 60 | 61 | 62 | /** 63 | * Initialisation of symbol table. 64 | * 65 | * @param table Pointer to table. 66 | */ 67 | void sym_table_init(Sym_table *table); 68 | 69 | /** 70 | * Appends item to symbol table of. 71 | * 72 | * @param table Pointer to table. 73 | * @param key Identifier of function or variable. 74 | * @param alloc_success True if allocation failed, otherwise true. 75 | * @return Returns NULL if error or item is existing else returns pointer to added item. 76 | */ 77 | TData *sym_table_add_symbol(Sym_table *table, const char *key, bool* alloc_failed); 78 | 79 | /** 80 | * Function appends parameter to symbol. 81 | * 82 | * @param token Pointer to output token. 83 | * @param data_type Data type of parameter of function. 84 | * @return Returns true if adding was succesfull else returns false. 85 | */ 86 | bool sym_table_add_param(TData *data, int data_type); 87 | 88 | /** 89 | * Function finds symbol and renturns its data. 90 | * 91 | * @param table Pointer to table. 92 | * @param key Identifier of function or variable. 93 | * @return NULL if symbol is not existing or pointer to data of symbol if it was successfuly found. 94 | */ 95 | TData *sym_table_search(Sym_table *table, const char *key); 96 | 97 | /** 98 | * Function removes symbol. 99 | * 100 | * @param table Pointer to table. 101 | * @param key Identifier of function or variable. 102 | * @return Returns true if removal was successfull esle returns false. 103 | */ 104 | bool sym_table_remove_symbol(Sym_table *table, const char *key); 105 | 106 | /** 107 | * Function frees all used resources for symbol table. 108 | * 109 | * @param table Pointer to table. 110 | */ 111 | void sym_table_free(Sym_table *table); 112 | 113 | 114 | #endif //_SYMTABLE_H 115 | -------------------------------------------------------------------------------- /tests/ic17int_linux64_2017-10-30/README: -------------------------------------------------------------------------------- 1 | ic17int - referencni interpret ciloveho jazyka IFJcode17 2 | verze 2017-11-21 3 | 4 | Prubezne doplnovane informace najdete na Wiki predmetu IFJ na strance ic17int. 5 | 6 | CHANGES: 7 | * 2017-11-21: 8 | * opravena chyba pri nacitani desetinnych cisel, upraven --verbose vypis desetinnych cisel (misto %g je pouzit format s presnejsim vypisem) 9 | * 2017-10-30: 10 | * opravena chyba pri znovupouziti promenne (napr. pomoci GETCHAR) 11 | * 2017-10-27: 12 | * oprava chovani instrukce FLOAT2INT (nyni jiz zaokrouhluje oseknutim dle dokumentace) 13 | * oprava preklepu ve vypisu --help 14 | -------------------------------------------------------------------------------- /tests/ic17int_linux64_2017-10-30/ic17int: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/tests/ic17int_linux64_2017-10-30/ic17int -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/.gitignore: -------------------------------------------------------------------------------- 1 | !/*.dll 2 | !/*.exe 3 | -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/README: -------------------------------------------------------------------------------- 1 | ic17int - referencni interpret ciloveho jazyka IFJcode17 2 | verze 2017-11-21 3 | 4 | Prubezne doplnovane informace najdete na Wiki predmetu IFJ na strance ic17int. 5 | 6 | CHANGES: 7 | * 2017-11-21: 8 | * opravena chyba pri nacitani desetinnych cisel, upraven --verbose vypis desetinnych cisel (misto %g je pouzit format s presnejsim vypisem) 9 | * 2017-10-30: 10 | * opravena chyba pri znovupouziti promenne (napr. pomoci GETCHAR) 11 | * 2017-10-27: 12 | * oprava chovani instrukce FLOAT2INT (nyni jiz zaokrouhluje oseknutim dle dokumentace) 13 | * oprava preklepu ve vypisu --help 14 | -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/cyggcc_s-seh-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/tests/ic17int_win64_2017-10-30/cyggcc_s-seh-1.dll -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/cygstdc++-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/tests/ic17int_win64_2017-10-30/cygstdc++-6.dll -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/cygwin1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/tests/ic17int_win64_2017-10-30/cygwin1.dll -------------------------------------------------------------------------------- /tests/ic17int_win64_2017-10-30/ic17int.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harmim/vut-ifj-project/b9e8fded898ce9f1c3718834026c5b0b7211e4ea/tests/ic17int_win64_2017-10-30/ic17int.exe -------------------------------------------------------------------------------- /tests/ifj17.bas: -------------------------------------------------------------------------------- 1 | /' Zajisteni zakladni kompatibility IFJ17->FreeBASIC '/ 2 | 3 | Function Length(s As String) As Integer 4 | Return Len(s) 5 | End Function 6 | 7 | Function SubStr(s as String, i As Integer, n As Integer) As String 8 | Return Mid(s, i, n) 9 | End Function 10 | 11 | /' Zde bude nasledovat program jazyka IFJ17 '/ 12 | -------------------------------------------------------------------------------- /tests/is_it_ok.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # pouziti: is_it_ok.sh xlogin01.zip testdir 4 | # 5 | # - POZOR: obsah adresare zadaneho druhym parametrem bude VYMAZAN! 6 | # - rozbali archiv studenta xlogin01.zip do adresare testdir a overi formalni pozadavky pro odevzdani projektu IFJ 7 | # - nasledne vyzkousi kompilaci 8 | # - detaily prubehu jsou logovany do souboru is_it_ok.log v adresari testdir 9 | 10 | # Autor: Zbynek Krivka 11 | # Verze: 1.2 (2012-11-23) 12 | 13 | LOG="is_it_ok.log" 14 | 15 | # implicit task indetifier from config.sh 16 | if [[ $# -ne 2 ]]; then 17 | echo "ERROR: Missing arguments or too much arguments!" 18 | echo "Usage: $0 ARCHIVE TESTDIR" 19 | echo " This script checks formal requirements for archive with solution of IFJ project." 20 | echo " ARCHIVE - the filename of archive to check" 21 | echo " TESTDIR - temporary directory that can be deleted/removed during testing!" 22 | exit 2 23 | fi 24 | 25 | # extrakce archivu 26 | function unpack_archive () { 27 | local ext=`echo $1 | cut -d . -f 2,3` 28 | echo -n "Archive extraction: " 29 | RETCODE=100 30 | if [[ "$ext" = "zip" ]]; then 31 | unzip -o $1 >> $LOG 2>&1 32 | RETCODE=$? 33 | elif [[ "$ext" = "gz" || "$ext" = "tgz" || "$ext" = "tar.gz" ]]; then 34 | tar xfz $1 >> $LOG 2>&1 35 | RETCODE=$? 36 | elif [[ "$ext" = "tbz2" || "$ext" = "tar.bz2" ]]; then 37 | tar xfj $1 >> $LOG 2>&1 38 | RETCODE=$? 39 | fi 40 | if [[ $RETCODE -eq 0 ]]; then 41 | echo "OK" 42 | elif [[ $RETCODE -eq 100 ]]; then 43 | echo "ERROR (unsupported extension)" 44 | exit 1 45 | else 46 | echo "ERROR (code $RETCODE)" 47 | exit 1 48 | fi 49 | } 50 | 51 | # prevod jmen souboru obsahujicich nepovolene znaky 52 | function to_small () { 53 | local N=`echo $1 | tr "[:upper:]" "[:lower:]"` 54 | if [ "$N" != "$1" ]; then 55 | mv "$1" "$N" 2>/dev/null 56 | echo "ERROR ($1 -> $N)" 57 | exit 1 58 | fi 59 | } 60 | 61 | # flattening aktualniho adresare + to_small 62 | function flattening () { 63 | local FILE="" 64 | local NFILE="" 65 | local FILES=`find . -name '*' -type f` 66 | for FILE in $FILES; do 67 | NFILE=./${FILE##*/} 68 | if [ "$FILE" != "$NFILE" ]; then 69 | mv "$FILE" ${NFILE} 2>/dev/null 70 | echo "ERROR ($FILE -> $NFILE)" 71 | exit 1 72 | fi 73 | F=`basename $FILE` 74 | if [ "$F" != "Makefile" ]; then 75 | to_small ${NFILE} 76 | fi 77 | done 78 | echo "OK" 79 | } 80 | 81 | # stare odstraneni DOSovskych radku (nyni mozno pouzit i utilitu dos2unix) 82 | function remove_CR () { 83 | FILES=`ls $* 2>/dev/null` 84 | for FILE in $FILES; do 85 | mv -f "$FILE" "$FILE.tmp" 86 | tr -d "\015" < "$FILE.tmp" > "$FILE" 87 | rm -f "$FILE.tmp" 88 | done 89 | } 90 | 91 | # 0) Priprava testdir a overeni serveru 92 | rm -rf $2 2>/dev/null 93 | mkdir $2 2>/dev/null 94 | cp $1 $2 2>/dev/null 95 | 96 | echo -n "Testing on Merlin: " 97 | HN=`hostname` 98 | if [[ $HN = "merlin.fit.vutbr.cz" ]]; then 99 | echo "Yes" 100 | else 101 | echo "No" 102 | fi 103 | 104 | 105 | # 1) Extrahovat do testdir 106 | cd $2 107 | touch $LOG 108 | ARCHIVE=`basename $1` 109 | NAME=`echo $ARCHIVE | cut -d . -f 1 | egrep "x[a-z]{5}[0-9][0-9a-z]"` 110 | echo -n "Archive name ($ARCHIVE): " 111 | if [[ -n $NAME ]]; then 112 | echo "OK" 113 | else 114 | echo "ERROR (the name does not correspond to a login)" 115 | fi 116 | 117 | unpack_archive ${ARCHIVE} 118 | 119 | # 2) Normalizace jmen na mala pismena 120 | echo -n "Normalization of filenames: " 121 | flattening 122 | 123 | # 3) Dokumentace 124 | echo -n "Searching for dokumentace.pdf: " 125 | if [[ -f "dokumentace.pdf" ]]; then 126 | echo "OK" 127 | else 128 | echo "ERROR (not found)" 129 | fi 130 | 131 | # 4) Priprava kompilace 132 | remove_CR *.mak *.c *.cpp *.cc *.h *.c++ *.hpp 133 | chmod 644 * 134 | 135 | echo -n "Project compilation: " 136 | # 5) Kompilace 137 | if [[ -f Makefile ]]; then 138 | ( make ) >> $LOG 2>&1 139 | RETCODE=$? 140 | if [[ -z $RETCODE ]]; then 141 | echo "ERROR (returns code $RETCODE)" 142 | exit 1 143 | fi 144 | else 145 | echo "ERROR (missing Makefile)" 146 | exit 1 147 | fi 148 | echo "OK" 149 | 150 | # 6) Najdi prelozeny binarni soubor 151 | echo -n "Searching for created binary file: " 152 | EXE=`ls -F | grep "*" | tr -d "*" | grep "" -m 1` # A naj�t bin�rku... 153 | if [[ -f $EXE ]]; then 154 | echo "OK ($EXE)" 155 | else 156 | echo "ERROR (not found)" 157 | exit 1 158 | fi 159 | 160 | # 7) Kontrola, ze nebyl vytvoren podadresar 161 | echo -n "Searching for new subdirectories: " 162 | DIR_COUNT=`find -type d | grep -v "^\.$" | wc -l` 163 | if [[ $DIR_COUNT -eq 0 ]]; then 164 | echo "OK (None)" 165 | else 166 | echo "ERROR (found $DIR_COUNT subdirectory/ies)" 167 | exit 1 168 | fi 169 | 170 | # 8) Kontrola rozdeleni 171 | echo -n "Presence of file rozdeleni: " 172 | IFS="$IFS:" 173 | if [[ -f rozdeleni ]]; then 174 | 175 | # zpracovani souboru rozdeleni 176 | unset LOGINS 177 | unset PERCENTS 178 | unset ARCHNAME 179 | declare -a LOGINS 180 | { 181 | i=0 182 | while read -a RADEK; do 183 | if [[ "${RADEK[0]}" != "" ]]; then 184 | LOGINS[$i]=${RADEK[0]} 185 | PERCENTS[$i]=`echo ${RADEK[1]} | tr -cd [:digit:]` 186 | ((TMP_PROC+=${PERCENTS[$i]:-0})) 187 | ((i++)) 188 | if [[ "$NAME" = "${RADEK[0]}" ]]; then 189 | ARCHNAME=$NAME 190 | fi 191 | else 192 | echo "ERROR (empty line occured)" 193 | exit 1 194 | fi 195 | done 196 | } < rozdeleni 197 | 198 | # kontrola formatu rozdeleni a souctu procent 199 | if [[ -n $RADEK ]]; then 200 | echo "ERROR (the last line is not ended properly)" 201 | elif [[ $TMP_PROC -ne 100 ]]; then 202 | echo "ERROR (sum != 100%)" 203 | elif [[ -z $ARCHNAME ]]; then 204 | echo "ERROR (rozdeleni does not contain the leader's login $NAME)" 205 | else 206 | echo "OK" 207 | fi 208 | 209 | else 210 | echo "ERROR (file not found)" 211 | fi 212 | 213 | # 9) Kontrola rozsireni 214 | echo -n "Presence of file rozsireni (optional): " 215 | if [[ -f rozsireni ]]; then 216 | echo "Yes" 217 | else 218 | echo "No" 219 | fi 220 | --------------------------------------------------------------------------------