Add support for functions returning VOID (i.e. non-standard extension, allowing functions that do not return any data)
--- 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" */
--- 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!! */
--- 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);
--- 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 */
--- 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 */
--- 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 <leaf> prev_declared_derived_function_block_name
%type <leaf> 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! */
;
--- 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 @@
<INITIAL>{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;
-<body_state,vardecl_list_state>{disable_code_generation_pragma} return disable_code_generation_pragma_token;
-<body_state,vardecl_list_state>{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;
+<vardecl_list_state>{disable_code_generation_pragma}/(VAR) return disable_code_generation_pragma_token;
+<vardecl_list_state>{enable_code_generation_pragma}/(VAR) return enable_code_generation_pragma_token;
+<body_state>{disable_code_generation_pragma} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */
+<body_state>{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 <body_state> state is exclusive, so we have to include it here too. */
+<body_state>{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;
}
-<body_state,vardecl_list_state>{pragma} {/* return the pragmma without the enclosing '{' and '}' */
+<vardecl_list_state>{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 */
<INITIAL>{
- /* 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;
+}
+
+<get_pou_name_state>{
+{identifier} BEGIN(ignore_pou_state); yylval.ID=strdup(yytext); return identifier_token;
+. BEGIN(ignore_pou_state); unput_text(0);
+}
+
+<ignore_pou_state>{
+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 <vardecl_state> state, untill the end of the FUNCTION, FUNCTION_BLOCK
+ * continue in the <vardecl_state> 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;
-}
-
-<get_pou_name_state>{
-{identifier} BEGIN(ignore_pou_state); yylval.ID=strdup(yytext); return identifier_token;
-. BEGIN(ignore_pou_state); unput_text(0);
-}
-
-<ignore_pou_state>{
-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! */
- /*
-<INITIAL>{
-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) */
<header_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) */
<body_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) */
<il_state,st_state>{
END_FUNCTION yy_pop_state(); unput_text(0);
@@ -1214,8 +1232,9 @@
/* NOTE: pragmas are handled right at the beginning... */
/* The whitespace */
-<INITIAL,header_state,config_state,body_state,vardecl_list_state,vardecl_state,st_state,sfc_state,task_init_state,sfc_qualifier_state>{st_whitespace} /* Eat any whitespace */
+<INITIAL,header_state,config_state,vardecl_list_state,vardecl_state,st_state,sfc_state,task_init_state,sfc_qualifier_state>{st_whitespace} /* Eat any whitespace */
<il_state>{il_whitespace} /* Eat any whitespace */
+<body_state>{st_whitespace} append_bodystate_buffer(yytext); /* in body state we do not process any tokens, we simply store them for later processing! */
/* The comments */
<get_pou_name_state,ignore_pou_state,body_state,vardecl_list_state>{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("<<<append_bodystate_buffer>>> %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("<<<unput_bodystate_buffer>>>\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)
{
--- 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 <fname>' 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");
--- 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 */
/********************************/
--- 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("(");
--- 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;}