# HG changeset patch # User mjsousa # Date 1464271220 -3600 # Node ID 91bef6704b442e0d21d35745c523e9ede71852a7 # Parent 9414b0785849cab9c220d3bff8b0fe7f55015110 Add support for functions returning VOID (i.e. non-standard extension, allowing functions that do not return any data) diff -r 9414b0785849 -r 91bef6704b44 absyntax/absyntax.def --- a/absyntax/absyntax.def Thu May 26 14:26:33 2016 +0100 +++ b/absyntax/absyntax.def Thu May 26 15:00:20 2016 +0100 @@ -267,7 +267,7 @@ SYM_REF0(lword_type_name_c) SYM_REF0(string_type_name_c) SYM_REF0(wstring_type_name_c) -SYM_REF0(void_c) /* a non-standard extension! */ +SYM_REF0(void_type_name_c) /* a non-standard extension! */ /*****************************************************************/ /* Keywords defined in "Safety Software Technical Specification" */ diff -r 9414b0785849 -r 91bef6704b44 absyntax_utils/get_datatype_info.cc --- a/absyntax_utils/get_datatype_info.cc Thu May 26 14:26:33 2016 +0100 +++ b/absyntax_utils/get_datatype_info.cc Thu May 26 15:00:20 2016 +0100 @@ -127,6 +127,8 @@ void *visit(safestring_type_name_c *symbol) {return (void *)symbol;}; void *visit(safewstring_type_name_c *symbol) {return (void *)symbol;}; + void *visit(void_type_name_c *symbol) {return (void *)symbol;}; + /********************************/ /* B 1.3.3 - Derived data types */ /********************************/ @@ -260,6 +262,8 @@ void *visit(safestring_type_name_c *symbol) {return (void *)"SAFESTRING"; }; void *visit(safewstring_type_name_c *symbol) {return (void *)"SAFEWSTRING"; }; + void *visit(void_type_name_c *symbol) {return (void *)"VOID"; }; + /********************************/ /* B.1.3.2 - Generic data types */ /********************************/ @@ -1352,6 +1356,21 @@ + +bool get_datatype_info_c::is_VOID(symbol_c *type_symbol) { + if (type_symbol == NULL) {return false;} + if (typeid(*type_symbol) == typeid(void_type_name_c)) {return true;} + return false; +} + + + + + + + + + /* Can't we do away with this?? */ bool get_datatype_info_c::is_ANY_REAL_literal(symbol_c *type_symbol) { if (type_symbol == NULL) {return true;} /* Please make sure things will work correctly before changing this to false!! */ diff -r 9414b0785849 -r 91bef6704b44 absyntax_utils/get_datatype_info.hh --- a/absyntax_utils/get_datatype_info.hh Thu May 26 14:26:33 2016 +0100 +++ b/absyntax_utils/get_datatype_info.hh Thu May 26 15:00:20 2016 +0100 @@ -165,6 +165,8 @@ static bool is_ANY_SAFESTRING (symbol_c *type_symbol); static bool is_ANY_STRING_compatible (symbol_c *type_symbol); + // A non-standard extension --> data type 'VOID' (used for functions that do not return any data) + static bool is_VOID (symbol_c *type_symbol); diff -r 9414b0785849 -r 91bef6704b44 absyntax_utils/search_base_type.cc --- a/absyntax_utils/search_base_type.cc Thu May 26 14:26:33 2016 +0100 +++ b/absyntax_utils/search_base_type.cc Thu May 26 15:00:20 2016 +0100 @@ -185,7 +185,7 @@ void *search_base_type_c::visit(wstring_type_name_c *symbol) {return (void *)symbol;} /* A non standard datatype! */ -void *search_base_type_c::visit(void_c *symbol) {return (void *)symbol;} +void *search_base_type_c::visit(void_type_name_c *symbol) {return (void *)symbol;} /******************************************************/ /* Extensions to the base standard as defined in */ diff -r 9414b0785849 -r 91bef6704b44 absyntax_utils/search_base_type.hh --- a/absyntax_utils/search_base_type.hh Thu May 26 14:26:33 2016 +0100 +++ b/absyntax_utils/search_base_type.hh Thu May 26 15:00:20 2016 +0100 @@ -141,7 +141,7 @@ void *visit(lword_type_name_c *symbol); void *visit(string_type_name_c *symbol); void *visit(wstring_type_name_c *symbol); - void *visit(void_c *symbol); /* A non standard datatype! */ + void *visit(void_type_name_c *symbol); /* A non standard datatype! */ /******************************************************/ /* Extensions to the base standard as defined in */ diff -r 9414b0785849 -r 91bef6704b44 stage1_2/iec_bison.yy --- a/stage1_2/iec_bison.yy Thu May 26 14:26:33 2016 +0100 +++ b/stage1_2/iec_bison.yy Thu May 26 15:00:20 2016 +0100 @@ -373,6 +373,32 @@ %type prev_declared_derived_function_block_name %type prev_declared_program_type_name +/* Tokens used to help resolve a reduce/reduce conflict */ +/* The mentioned conflict only arises due to a non-standard feature added to matiec. + * Namely, the permission to call functions returning VOID as an ST statement. + * e.g.: FUNCTION foo: VOID + * VAR_INPUT i: INT; END_VAR; + * ... + * END_FUNCTION + * + * FUNCTION BAR: BOOL + * VAR b: bool; END_VAR + * foo(i:=42); <--- Calling foo outside an expression. Function invocation is considered an ST statement!! + * END_FUNCTION + * + * The above function invocation may also be reduced to a formal IL function invocation, so we get a + * reduce/reduce conflict to st_statement_list/instruction_list (or something equivalent). + * + * We solve this by having flex determine if it is ST or IL invocation (ST ends with a ';' !!). + * At the start of a function/FB/program body, flex will tell bison whether to expect ST or IL code! + * This is why we need the following two tokens! + * + * NOTE: flex was already determing whther it was parsing ST or IL code as it can only send + * EOL tokens when parsing IL. However, did this silently without telling bison about this. + * Now, it does + */ +%token start_ST_body_token +%token start_IL_body_token @@ -5013,7 +5039,7 @@ } /* | FUNCTION derived_function_name ':' VOID io_OR_function_var_declarations_list function_body END_FUNCTION */ | function_name_declaration ':' VOID io_OR_function_var_declarations_list function_body END_FUNCTION - {$$ = new function_declaration_c($1, new void_c(locloc(@3)), $4, $5, locloc(@$)); + {$$ = new function_declaration_c($1, new void_type_name_c(locloc(@3)), $4, $5, locloc(@$)); if (!runtime_options.disable_implicit_en_eno) add_en_eno_param_decl_c::add_to($$); /* add EN and ENO declarations, if not already there */ variable_name_symtable.pop(); direct_variable_symtable.pop(); @@ -5183,8 +5209,8 @@ function_body: - statement_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ -| instruction_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ + start_ST_body_token statement_list {$$ = $2;} +| start_IL_body_token instruction_list {$$ = $2;} /* | ladder_diagram | function_block_diagram @@ -5257,7 +5283,7 @@ {$$ = NULL; print_err_msg(locl(@2), locf(@3), "no variable(s) declared and body defined in function block declaration."); yynerrs++;} */ | FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations_list function_block_body END_OF_INPUT - {$$ = NULL; print_err_msg(locf(@1), locl(@2), "no variable(s) declared and body defined in function block declaration."); yynerrs++;} + {$$ = NULL; print_err_msg(locf(@1), locl(@2), "expecting END_FUNCTION_BLOCK before end of file."); yynerrs++;} | FUNCTION_BLOCK error END_FUNCTION_BLOCK {$$ = NULL; print_err_msg(locf(@2), locl(@2), "unknown error in function block declaration."); yyerrok;} /* ERROR_CHECK_END */ @@ -5366,9 +5392,21 @@ function_block_body: - statement_list {$$ = $1;} -| instruction_list {$$ = $1;} -| sequential_function_chart {$$ = $1;} + /* NOTE: start_ST_body_token is a dummy token generated by flex when it determines it is starting to parse a POU body in ST + * start_IL_body_token is a dummy token generated by flex when it determines it is starting to parse a POU body in IL + * These tokens help remove a reduce/reduce conflict in bison, between a formal function invocation in IL, and a + * function invocation used as a statement (a non-standard extension added to matiec) + * e.g: FUNCTION_BLOCK foo + * VAR ... END_VAR + * func_returning_void(in1 := 3 + * ); --> only the presence or absence of ';' will determine whether this is a IL or ST + * function invocation. (In standard ST this would be ilegal, in matiec we allow it + * when activated by a command line option) + * END_FUNCTION + */ + start_ST_body_token statement_list {$$ = $2;} +| start_IL_body_token instruction_list {$$ = $2;} +| sequential_function_chart {$$ = $1;} /* | ladder_diagram | function_block_diagram @@ -7867,6 +7905,7 @@ | subprogram_control_statement | selection_statement | iteration_statement +| function_invocation /* TODO: this must be conditional on command line parameter! */ ; diff -r 9414b0785849 -r 91bef6704b44 stage1_2/iec_flex.ll --- a/stage1_2/iec_flex.ll Thu May 26 14:26:33 2016 +0100 +++ b/stage1_2/iec_flex.ll Thu May 26 15:00:20 2016 +0100 @@ -226,6 +226,16 @@ void include_file(const char *include_filename); +/* The body_state tries to find a ';' before a END_PROGRAM, END_FUNCTION or END_FUNCTION_BLOCK or END_ACTION + * To do so, it must ignore comments and pragmas. This means that we cannot do this in a signle lex rule. + * However, we must store any text we consume in every rule, so we can push it back into the buffer + * once we have decided if we are parsing ST or IL code. The following functions manage that buffer used by + * the body_state. + */ +void append_bodystate_buffer(const char *yytext); +void unput_bodystate_buffer(void); +int isempty_bodystate_buffer(void); + int GetNextChar(char *b, int maxBuffer); %} @@ -958,20 +968,29 @@ {file_include_pragma} unput_text(0); yy_push_state(include_beg); /* Pragmas sent to syntax analyser (bison) */ -{disable_code_generation_pragma} return disable_code_generation_pragma_token; -{enable_code_generation_pragma} return enable_code_generation_pragma_token; -{disable_code_generation_pragma} return disable_code_generation_pragma_token; -{enable_code_generation_pragma} return enable_code_generation_pragma_token; - + /* NOTE: In the vardecl_list_state we only process the pragmas between two consecutive VAR .. END_VAR blocks. + * We do not process any pragmas trailing after the last END_VAR. We leave that to the body_state. + * This is because the pragmas are stored in a statement_list or instruction_list (in bison), + * but these lists must start with the special tokens start_IL_body_token/start_ST_body_token. + * This means that these special tokens must be generated (by the body_state) before processing + * the pragme => we cannot process the trailing pragmas in the vardecl_list_state state. + */ +{disable_code_generation_pragma} return disable_code_generation_pragma_token; +{enable_code_generation_pragma} return enable_code_generation_pragma_token; +{disable_code_generation_pragma}/(VAR) return disable_code_generation_pragma_token; +{enable_code_generation_pragma}/(VAR) return enable_code_generation_pragma_token; +{disable_code_generation_pragma} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */ +{enable_code_generation_pragma} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */ /* Any other pragma we find, we just pass it up to the syntax parser... */ /* Note that the state is exclusive, so we have to include it here too. */ +{pragma} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */ {pragma} {/* return the pragmma without the enclosing '{' and '}' */ int cut = yytext[1]=='{'?2:1; yytext[strlen(yytext)-cut] = '\0'; yylval.ID=strdup(yytext+cut); return pragma_token; } -{pragma} {/* return the pragmma without the enclosing '{' and '}' */ +{pragma}/(VAR) {/* return the pragmma without the enclosing '{' and '}' */ int cut = yytext[1]=='{'?2:1; yytext[strlen(yytext)-cut] = '\0'; yylval.ID=strdup(yytext+cut); @@ -1059,7 +1078,30 @@ /* INITIAL -> header_state */ { - /* NOTE: how about functions that do not declare variables, and go directly to the body_state??? +FUNCTION{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else {BEGIN(header_state);/* printf("\nChanging to header_state\n"); */} return FUNCTION; +FUNCTION_BLOCK{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else {BEGIN(header_state);/* printf("\nChanging to header_state\n"); */} return FUNCTION_BLOCK; +PROGRAM{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else {BEGIN(header_state);/* printf("\nChanging to header_state\n"); */} return PROGRAM; +CONFIGURATION{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else {BEGIN(config_state);/* printf("\nChanging to config_state\n"); */} return CONFIGURATION; +} + +{ +{identifier} BEGIN(ignore_pou_state); yylval.ID=strdup(yytext); return identifier_token; +. BEGIN(ignore_pou_state); unput_text(0); +} + +{ +END_FUNCTION unput_text(0); BEGIN(INITIAL); +END_FUNCTION_BLOCK unput_text(0); BEGIN(INITIAL); +END_PROGRAM unput_text(0); BEGIN(INITIAL); +END_CONFIGURATION unput_text(0); BEGIN(INITIAL); +.|\n {}/* Ignore text inside POU! (including the '\n' character!)) */ +} + + + /* header_state -> (vardecl_list_state) */ + /* NOTE: This transition assumes that all POUs with code (Function, FB, and Program) will always contain + * at least one VAR_XXX block. + * How about functions that do not declare variables, and go directly to the body_state??? * - According to Section 2.5.1.3 (Function Declaration), item 2 in the list, a FUNCTION * must have at least one input argument, so a correct declaration will have at least * one VAR_INPUT ... VAR_END construct! @@ -1073,44 +1115,22 @@ * All the above means that we needn't worry about PROGRAMs, FUNCTIONs or * FUNCTION_BLOCKs that do not have at least one VAR_END before the body_state. * If the code has an error, and no VAR_END before the body, we will simply - * continue in the state, untill the end of the FUNCTION, FUNCTION_BLOCK + * continue in the state, until the end of the FUNCTION, FUNCTION_BLOCK * or PROGAM. + * + * WARNING: From 2016-05 (May 2016) onwards, matiec supports a non-standard option in which a Function + * may be declared with no Input, Output or IN_OUT variables. This means that the above + * assumption is no longer valid. + * To make things simpler (i.e. so we do not need to change the transition conditions in the flex state machine), + * when using this non-standard extension matiec requires that Functions must include at least one + * VAR .. END_VAR block. This implies that the above assumption remains valid! + * This limitation of requiring a VAR .. END_VAR block is not really very limiting, as a function + * with no input and output parameters will probably need to do some 'work', and for that it will + * probably need some local variables declared in a VAR .. END_VAR block. + * Note however that in the extreme it might make sense to have a function with no variables whatsoever + * (e.g.: a function that only calls other functions that all return VOID - another non standard extension!). + * For now we do not consider this!! */ - -FUNCTION{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else BEGIN(header_state); return FUNCTION; -FUNCTION_BLOCK{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else BEGIN(header_state); return FUNCTION_BLOCK; -PROGRAM{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else BEGIN(header_state); return PROGRAM; -CONFIGURATION{st_whitespace} if (get_preparse_state()) BEGIN(get_pou_name_state); else BEGIN(config_state); return CONFIGURATION; -} - -{ -{identifier} BEGIN(ignore_pou_state); yylval.ID=strdup(yytext); return identifier_token; -. BEGIN(ignore_pou_state); unput_text(0); -} - -{ -END_FUNCTION unput_text(0); BEGIN(INITIAL); -END_FUNCTION_BLOCK unput_text(0); BEGIN(INITIAL); -END_PROGRAM unput_text(0); BEGIN(INITIAL); -END_CONFIGURATION unput_text(0); BEGIN(INITIAL); -.|\n {}/* Ignore text inside POU! (including the '\n' character!)) */ -} - - /* INITIAL -> body_state */ - /* required if the function, program, etc.. has no VAR block! */ - /* We comment it out since the standard does not allow this. */ - /* NOTE: Even if we were to include the following code, it */ - /* would have no effect whatsoever since the above */ - /* rules will take precendence! */ - /* -{ -FUNCTION BEGIN(body_state); return FUNCTION; -FUNCTION_BLOCK BEGIN(body_state); return FUNCTION_BLOCK; -PROGRAM BEGIN(body_state); return PROGRAM; -} - */ - - /* header_state -> (vardecl_list_state) */ { VAR | /* execute the next rule's action, i.e. fall-through! */ VAR_INPUT | @@ -1120,7 +1140,7 @@ VAR_GLOBAL | VAR_TEMP | VAR_CONFIG | -VAR_ACCESS unput_text(0); BEGIN(vardecl_list_state); +VAR_ACCESS unput_text(0); /* printf("\nChanging to vardecl_list_state\n") */; BEGIN(vardecl_list_state); } @@ -1140,7 +1160,7 @@ END_FUNCTION_BLOCK unput_text(0); BEGIN(INITIAL); END_PROGRAM unput_text(0); BEGIN(INITIAL); -. unput_text(0); yy_push_state(body_state); /* anything else, just change to body_state! */ +. unput_text(0); yy_push_state(body_state); //printf("\nChanging to body_state\n");/* anything else, just change to body_state! */ } @@ -1152,41 +1172,39 @@ /* body_state -> (il_state | st_state | sfc_state) */ { -INITIAL_STEP unput_text(0); BEGIN(sfc_state); - -{qualified_identifier} unput_text(0); BEGIN(st_state); /* will always be followed by '[' for an array access, or ':=' as the left hand of an assignment statement */ -{direct_variable_standard} unput_text(0); BEGIN(st_state); /* will always be followed by ':=' as the left hand of an assignment statement */ - -RETURN unput_text(0); BEGIN(st_state); -IF unput_text(0); BEGIN(st_state); -CASE unput_text(0); BEGIN(st_state); -FOR unput_text(0); BEGIN(st_state); -WHILE unput_text(0); BEGIN(st_state); -EXIT unput_text(0); BEGIN(st_state); -REPEAT unput_text(0); BEGIN(st_state); - - /* ':=' occurs only in transitions, and not Function or FB bodies! */ -:= unput_text(0); BEGIN(st_state); - -{identifier} {int token = get_identifier_token(yytext); - if ((token == prev_declared_fb_name_token) || (token == prev_declared_variable_name_token)) { - /* the code has a call to a function block OR has an assingment with a variable as the lvalue */ - unput_text(0); BEGIN(st_state); - } else - if (token == prev_declared_derived_function_name_token) { - /* the code has a call to a function - must be IL */ - unput_text(0); BEGIN(il_state); - } else { - /* Might be a lable in IL, or a bug in ST/IL code. We jump to IL */ - unput_text(0); BEGIN(il_state); - } - } - -. unput_text(0); BEGIN(il_state); /* Don't know what it could be. This is most likely a bug. Let's just to a random state... */ + /* 'INITIAL_STEP' always used in beginning of SFCs !! */ +INITIAL_STEP { if (isempty_bodystate_buffer()) {unput_text(0); BEGIN(sfc_state);} + else {append_bodystate_buffer(yytext);} + } + + /* ':=', at the very beginning of a 'body', occurs only in transitions and not Function, FB, or Program bodies! */ +:= { if (isempty_bodystate_buffer()) {unput_text(0); BEGIN(st_state);} /* We do _not_ return a start_ST_body_token here, as bison does not expect it! */ + else {append_bodystate_buffer(yytext);} + } + + /* check if ';' occurs before an END_FUNCTION, END_FUNCTION_BLOCK, END_PROGRAM or END_ACTION. (If true => we are parsing ST; If false => parsing IL). */ +END_ACTION | /* execute the next rule's action, i.e. fall-through! */ +END_FUNCTION | +END_FUNCTION_BLOCK | +END_PROGRAM { append_bodystate_buffer(yytext); unput_bodystate_buffer(); BEGIN(il_state); /*printf("returning start_IL_body_token\n");*/ return start_IL_body_token;} +.|\n { append_bodystate_buffer(yytext); + if (strcmp(yytext, ";") == 0) + {unput_bodystate_buffer(); BEGIN(st_state); /*printf("returning start_ST_body_token\n");*/ return start_ST_body_token;} + } + /* The following rules are not really necessary. They just make compilation faster in case the ST Statement List starts with one fot he following... */ +RETURN | /* execute the next rule's action, i.e. fall-through! */ +IF | +CASE | +FOR | +WHILE | +EXIT | +REPEAT { if (isempty_bodystate_buffer()) {unput_text(0); BEGIN(st_state); return start_ST_body_token;} + else {append_bodystate_buffer(yytext);} + } + } /* end of body_state lexical parser */ - /* (il_state | st_state) -> pop to $previous_state (vardecl_list_state or sfc_state) */ { END_FUNCTION yy_pop_state(); unput_text(0); @@ -1214,8 +1232,9 @@ /* NOTE: pragmas are handled right at the beginning... */ /* The whitespace */ -{st_whitespace} /* Eat any whitespace */ +{st_whitespace} /* Eat any whitespace */ {il_whitespace} /* Eat any whitespace */ +{st_whitespace} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */ /* The comments */ {comment_beg} yy_push_state(comment_state); @@ -1254,6 +1273,14 @@ * We solve this by NOT testing for function names here, and * handling this function and keyword clash in bison! */ + /* NOTE: The following code has been commented out as most users do not want matiec + * to allow the use of 'R1', 'IN' ... IL operators as identifiers, + * even though a literal reading of the standard allows this. + * We could add this as a commadnd line option, but it is not yet done. + * For now we just comment out the code, but leave it the commented code + * in so we can re-activate quickly (without having to go through old commits + * in the mercurial repository to figure out the missing code! + */ /* {identifier} {int token = get_identifier_token(yytext); // fprintf(stderr, "flex: analysing identifier '%s'...", yytext); @@ -1474,7 +1501,7 @@ /* B 1.5.1 - Functions */ /***********************/ /* Note: The following END_FUNCTION rule includes a BEGIN(INITIAL); command. - * This is necessary in case the input program being pased has syntax errors that force + * This is necessary in case the input program being parsed has syntax errors that force * flex's main state machine to never change to the il_state or the st_state * after changing to the body_state. * Ths BEGIN(INITIAL) command forces the flex state machine to re-synchronise with @@ -1490,7 +1517,7 @@ /* B 1.5.2 - Function Blocks */ /*****************************/ /* Note: The following END_FUNCTION_BLOCK rule includes a BEGIN(INITIAL); command. - * This is necessary in case the input program being pased has syntax errors that force + * This is necessary in case the input program being parsed has syntax errors that force * flex's main state machine to never change to the il_state or the st_state * after changing to the body_state. * Ths BEGIN(INITIAL) command forces the flex state machine to re-synchronise with @@ -1508,7 +1535,7 @@ /* B 1.5.3 - Programs */ /**********************/ /* Note: The following END_PROGRAM rule includes a BEGIN(INITIAL); command. - * This is necessary in case the input program being pased has syntax errors that force + * This is necessary in case the input program being parsed has syntax errors that force * flex's main state machine to never change to the il_state or the st_state * after changing to the body_state. * Ths BEGIN(INITIAL) command forces the flex state machine to re-synchronise with @@ -2034,6 +2061,47 @@ +/* The body_state tries to find a ';' before a END_PROGRAM, END_FUNCTION or END_FUNCTION_BLOCK or END_ACTION + * To do so, it must ignore comments and pragmas. This means that we cannot do this in a signle lex rule. + * However, we must store any text we consume in every rule, so we can push it back into the buffer + * once we have decided if we are parsing ST or IL code. The following functions manage that buffer used by + * the body_state. + */ +/* The buffer used by the body_state state */ +char *bodystate_buffer = NULL; + +/* append text to bodystate_buffer */ +void append_bodystate_buffer(const char *text) { + //printf("<<>> %d <%s><%s>\n", bodystate_buffer, (NULL != bodystate_buffer)?bodystate_buffer:"NULL", text); + long int old_len = 0; + if (NULL != bodystate_buffer) old_len = strlen(bodystate_buffer); + bodystate_buffer = (char *)realloc(bodystate_buffer, old_len + strlen(text) + 1); + if (NULL == bodystate_buffer) ERROR; + strcpy(bodystate_buffer + old_len, text); + //printf("=<%s> %d %d\n", (NULL != bodystate_buffer)?bodystate_buffer:NULL, old_len + strlen(text) + 1, bodystate_buffer); +} + +/* Return all data in bodystate_buffer back to flex, and empty bodystate_buffer. */ +void unput_bodystate_buffer(void) { + if (NULL == bodystate_buffer) ERROR; + //printf("<<>>\n%s\n", bodystate_buffer); + + for (long int i = strlen(bodystate_buffer)-1; i >= 0; i--) + unput(bodystate_buffer[i]); + + free(bodystate_buffer); + bodystate_buffer = NULL; +} + + +/* Return true if bodystate_buffer is empty */ +int isempty_bodystate_buffer(void) { + return (NULL == bodystate_buffer); +} + + + + /* Called by flex when it reaches the end-of-file */ int yywrap(void) { diff -r 9414b0785849 -r 91bef6704b44 stage4/generate_c/generate_c.cc --- a/stage4/generate_c/generate_c.cc Thu May 26 14:26:33 2016 +0100 +++ b/stage4/generate_c/generate_c.cc Thu May 26 15:00:20 2016 +0100 @@ -859,36 +859,55 @@ /* (B.2) Temporary variable for function's return value */ /* It will have the same name as the function itself! */ - s4o.print(s4o.indent_spaces); - symbol->type_name->accept(print_base); /* return type */ - s4o.print(" "); - symbol->derived_function_name->accept(print_base); - s4o.print(" = "); - { - /* get the default value of this variable's type */ - symbol_c *default_value = type_initial_value_c::get(symbol->type_name); - if (default_value == NULL) ERROR; - initialization_analyzer_c initialization_analyzer(default_value); - switch (initialization_analyzer.get_initialization_type()) { - case initialization_analyzer_c::struct_it: - { - generate_c_structure_initialization_c *structure_initialization = new generate_c_structure_initialization_c(&s4o); - structure_initialization->init_structure_default(symbol->type_name); - structure_initialization->init_structure_values(default_value); - delete structure_initialization; - } - break; - case initialization_analyzer_c::array_it: - { - generate_c_array_initialization_c *array_initialization = new generate_c_array_initialization_c(&s4o); - array_initialization->init_array_size(symbol->type_name); - array_initialization->init_array_values(default_value); - delete array_initialization; - } - break; - default: - default_value->accept(print_base); - break; + /* NOTE: matiec supports a non-standard syntax, in which functions do not return a value + * (declared as returning the special non-standard datatype VOID) + * e.g.: FUNCTION foo: VOID + * ... + * END_FUNCTION + * + * These functions cannot return any value, so they do not need a variable to + * store the return value. + * Note that any attemot to sto a value in the implicit variable + * e.g.: FUNCTION foo: VOID + * ... + * foo := 42; + * END_FUNCTION + * will always return a datatype incompatilibiyt error in stage 3 of matiec, + * so it is safe for stage 4 to assume that this return variable will never be needed + * if the function's return type is VOID. + */ + if (!get_datatype_info_c::is_VOID(symbol->type_name->datatype)) { // only print return variable if return datatype is not VOID + s4o.print(s4o.indent_spaces); + symbol->type_name->accept(print_base); /* return type */ + s4o.print(" "); + symbol->derived_function_name->accept(print_base); + s4o.print(" = "); + { + /* get the default value of this variable's type */ + symbol_c *default_value = type_initial_value_c::get(symbol->type_name); + if (default_value == NULL) ERROR; + initialization_analyzer_c initialization_analyzer(default_value); + switch (initialization_analyzer.get_initialization_type()) { + case initialization_analyzer_c::struct_it: + { + generate_c_structure_initialization_c *structure_initialization = new generate_c_structure_initialization_c(&s4o); + structure_initialization->init_structure_default(symbol->type_name); + structure_initialization->init_structure_values(default_value); + delete structure_initialization; + } + break; + case initialization_analyzer_c::array_it: + { + generate_c_array_initialization_c *array_initialization = new generate_c_array_initialization_c(&s4o); + array_initialization->init_array_size(symbol->type_name); + array_initialization->init_array_values(default_value); + delete array_initialization; + } + break; + default: + default_value->accept(print_base); + break; + } } } s4o.print(";\n\n"); @@ -909,9 +928,11 @@ s4o.print(s4o.indent_spaces + "*__ENO = __BOOL_LITERAL(FALSE);\n"); s4o.indent_left(); s4o.print(s4o.indent_spaces + "}\n"); - s4o.print(s4o.indent_spaces + "return "); - symbol->derived_function_name->accept(print_base); - s4o.print(";\n"); + if (!get_datatype_info_c::is_VOID(symbol->type_name->datatype)) { // only print return variable if return datatype is not VOID + s4o.print(s4o.indent_spaces + "return "); + symbol->derived_function_name->accept(print_base); + s4o.print(";\n"); + } s4o.indent_left(); s4o.print(s4o.indent_spaces + "}\n"); } @@ -930,9 +951,12 @@ vardecl->print(symbol->var_declarations_list); delete vardecl; - s4o.print(s4o.indent_spaces + "return "); - symbol->derived_function_name->accept(print_base); - s4o.print(";\n"); + if (!get_datatype_info_c::is_VOID(symbol->type_name->datatype)) { // only print 'return ' if return datatype is not VOID + s4o.print(s4o.indent_spaces + "return "); + symbol->derived_function_name->accept(print_base); + s4o.print(";\n"); + } + s4o.indent_left(); s4o.print(s4o.indent_spaces + "}\n\n\n"); diff -r 9414b0785849 -r 91bef6704b44 stage4/generate_c/generate_c_base.cc --- a/stage4/generate_c/generate_c_base.cc Thu May 26 14:26:33 2016 +0100 +++ b/stage4/generate_c/generate_c_base.cc Thu May 26 15:00:20 2016 +0100 @@ -658,6 +658,8 @@ void *visit(safestring_type_name_c *symbol) {s4o.print("STRING"); return NULL;} void *visit(safewstring_type_name_c *symbol) {s4o.print("WSTRING"); return NULL;} + void *visit(void_type_name_c *symbol) {s4o.print("void"); return NULL;} + /********************************/ /* B.1.3.2 - Generic data types */ /********************************/ diff -r 9414b0785849 -r 91bef6704b44 stage4/generate_c/generate_c_il.cc --- a/stage4/generate_c/generate_c_il.cc Thu May 26 14:26:33 2016 +0100 +++ b/stage4/generate_c/generate_c_il.cc Thu May 26 15:00:20 2016 +0100 @@ -884,8 +884,11 @@ int fdecl_mutiplicity = function_symtable.count(symbol->function_name); if (fdecl_mutiplicity == 0) ERROR; - this->implicit_variable_result.accept(*this); - s4o.print(" = "); + /* when function returns a void, we do not store the value in the default variable! */ + if (!get_datatype_info_c::is_VOID(symbol->datatype)) { + this->implicit_variable_result.accept(*this); + s4o.print(" = "); + } if (function_type_prefix != NULL) { s4o.print("("); @@ -1288,8 +1291,11 @@ /* function being called is NOT overloaded! */ f_decl = NULL; - this->implicit_variable_result.accept(*this); - s4o.print(" = "); + /* when function returns a void, we do not store the value in the default variable! */ + if (!get_datatype_info_c::is_VOID(symbol->datatype)) { + this->implicit_variable_result.accept(*this); + s4o.print(" = "); + } if (function_type_prefix != NULL) { s4o.print("("); diff -r 9414b0785849 -r 91bef6704b44 stage4/generate_iec/generate_iec.cc --- a/stage4/generate_iec/generate_iec.cc Thu May 26 14:26:33 2016 +0100 +++ b/stage4/generate_iec/generate_iec.cc Thu May 26 15:00:20 2016 +0100 @@ -414,7 +414,7 @@ void *visit( string_type_name_c *symbol) {s4o.print("STRING"); return NULL;} void *visit( wstring_type_name_c *symbol) {s4o.print("WSTRING"); return NULL;} -void *visit( void_c *symbol) {s4o.print("VOID"); return NULL;} /* a non-standard extension! */ +void *visit( void_type_name_c *symbol) {s4o.print("VOID"); return NULL;} /* a non-standard extension! */ void *visit( safetime_type_name_c *symbol) {s4o.print("SAFETIME"); return NULL;} void *visit( safebool_type_name_c *symbol) {s4o.print("SAFEBOOL"); return NULL;}