diff options
author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-26 21:08:13 +0300 |
---|---|---|
committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-04-26 21:08:13 +0300 |
commit | 966f97ad7c92f559ae54d0214db90c370e3b48eb (patch) | |
tree | 5dfadd4f453cb0076039c70b0574e58dee8ff2a4 | |
parent | 930e0f29ff5637f38a878d2e61352b151698a0ea (diff) | |
download | posthaste-966f97ad7c92f559ae54d0214db90c370e3b48eb.tar.gz posthaste-966f97ad7c92f559ae54d0214db90c370e3b48eb.zip |
add check against proc calls as statements
-rw-r--r-- | gen/gen_parser.c | 236 | ||||
-rw-r--r-- | src/check.c | 65 | ||||
-rw-r--r-- | src/lower.c | 9 | ||||
-rw-r--r-- | src/parser.y | 36 |
4 files changed, 161 insertions, 185 deletions
diff --git a/gen/gen_parser.c b/gen/gen_parser.c index 7d6e67d..1d07254 100644 --- a/gen/gen_parser.c +++ b/gen/gen_parser.c @@ -79,44 +79,8 @@ #include <posthaste/date.h> #include <posthaste/ast.h> -#define FOREACH_TOKEN(M) \ - M(LPAREN) \ - M(RPAREN) \ - M(LSQUARE) \ - M(RSQUARE) \ - M(LCURLY) \ - M(RCURLY) \ - M(APOSTROPHE) \ - M(AMPERSAND) \ - M(COMMA) \ - M(DOT) \ - M(EQ) \ - M(LT) \ - M(PLUS) \ - M(MINUS) \ - M(MULT) \ - M(DIV) \ - M(VAR) \ - M(IS) \ - M(UNLESS) \ - M(OTHERWISE) \ - M(UNTIL) \ - M(DO) \ - M(DONE) \ - M(PROCEDURE) \ - M(FUNCTION) \ - M(RETURN) \ - M(PRINT) \ - M(END) \ - M(STRING) \ - M(DATE_LITERAL) \ - M(INT_LITERAL) \ - M(IDENT) \ - M(FUNC_IDENT) \ - M(PROC_IDENT) - - -#line 120 "gen/gen_parser.c" + +#line 84 "gen/gen_parser.c" # ifndef YY_CAST # ifdef __cplusplus @@ -199,14 +163,14 @@ extern int yydebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 60 "src/parser.y" +#line 24 "src/parser.y" struct ast *node; ph_date_t num; int64_t snum; char *str; -#line 210 "gen/gen_parser.c" +#line 174 "gen/gen_parser.c" }; typedef union YYSTYPE YYSTYPE; @@ -317,7 +281,7 @@ typedef enum yysymbol_kind_t yysymbol_kind_t; /* Second part of user prologue. */ -#line 143 "src/parser.y" +#line 107 "src/parser.y" /** Modifies the signature of yylex to fit our parser better. */ @@ -357,7 +321,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer, struct parser *parser, const char *msg); -#line 361 "gen/gen_parser.c" +#line 325 "gen/gen_parser.c" #ifdef short @@ -746,14 +710,14 @@ static const yytype_int8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 187, 187, 188, 191, 192, 195, 196, 197, 200, - 205, 206, 209, 210, 213, 216, 217, 220, 232, 244, - 245, 248, 249, 252, 257, 262, 263, 266, 267, 270, - 275, 280, 281, 284, 285, 288, 291, 292, 295, 296, - 299, 300, 301, 302, 303, 304, 307, 312, 315, 320, - 321, 324, 329, 330, 333, 338, 339, 342, 347, 350, - 353, 356, 357, 360, 363, 366, 367, 368, 371, 376, - 381, 382, 385, 396 + 0, 151, 151, 152, 155, 156, 159, 160, 161, 164, + 169, 170, 173, 174, 177, 180, 181, 184, 196, 208, + 209, 212, 213, 216, 221, 226, 227, 230, 231, 234, + 239, 244, 245, 248, 249, 252, 255, 256, 259, 260, + 263, 264, 265, 266, 267, 268, 271, 276, 279, 284, + 285, 288, 293, 294, 297, 302, 303, 306, 311, 314, + 317, 320, 321, 324, 327, 330, 331, 332, 335, 340, + 345, 346, 349, 360 }; #endif @@ -1809,51 +1773,51 @@ yyreduce: switch (yyn) { case 2: /* statement_list: statement "," statement_list */ -#line 187 "src/parser.y" +#line 151 "src/parser.y" { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1815 "gen/gen_parser.c" +#line 1779 "gen/gen_parser.c" break; case 4: /* definitions: definition definitions */ -#line 191 "src/parser.y" +#line 155 "src/parser.y" { (yyval.node) = (yyvsp[-1].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1821 "gen/gen_parser.c" +#line 1785 "gen/gen_parser.c" break; case 9: /* variable_definition: "var" IDENT "=" expression */ -#line 200 "src/parser.y" +#line 164 "src/parser.y" { (yyval.node) = gen_var_def((yyvsp[-2].str), (yyvsp[0].node), src_loc((yyloc))); } -#line 1829 "gen/gen_parser.c" +#line 1793 "gen/gen_parser.c" break; case 10: /* variable_definitions: variable_definition variable_definitions */ -#line 205 "src/parser.y" +#line 169 "src/parser.y" { (yyval.node) = (yyvsp[-1].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1835 "gen/gen_parser.c" +#line 1799 "gen/gen_parser.c" break; case 13: /* opt_variable_definitions: %empty */ -#line 210 "src/parser.y" +#line 174 "src/parser.y" { (yyval.node) = NULL; } -#line 1841 "gen/gen_parser.c" +#line 1805 "gen/gen_parser.c" break; case 14: /* return: "return" IDENT */ -#line 213 "src/parser.y" +#line 177 "src/parser.y" { (yyval.str) = (yyvsp[0].str); } -#line 1847 "gen/gen_parser.c" +#line 1811 "gen/gen_parser.c" break; case 16: /* opt_return: %empty */ -#line 217 "src/parser.y" +#line 181 "src/parser.y" { (yyval.str) = NULL; } -#line 1853 "gen/gen_parser.c" +#line 1817 "gen/gen_parser.c" break; case 17: /* function_definition: "function" FUNC_IDENT "{" opt_formals "}" return opt_variable_definitions "is" rvalue "end" "function" */ -#line 221 "src/parser.y" +#line 185 "src/parser.y" { struct ast *ret = gen_return((yyvsp[-2].node), (yyvsp[-2].node)->loc); (yyval.node) = gen_func_def((yyvsp[-9].str), @@ -1863,11 +1827,11 @@ yyreduce: ret, src_loc((yyloc))); } -#line 1867 "gen/gen_parser.c" +#line 1831 "gen/gen_parser.c" break; case 18: /* procedure_definition: "procedure" PROC_IDENT "{" opt_formals "}" opt_return opt_variable_definitions "is" statement_list "end" "procedure" */ -#line 234 "src/parser.y" +#line 198 "src/parser.y" { (yyval.node) = gen_proc_def((yyvsp[-9].str), (yyvsp[-5].str), @@ -1876,237 +1840,237 @@ yyreduce: (yyvsp[-2].node), src_loc((yyloc))); } -#line 1880 "gen/gen_parser.c" +#line 1844 "gen/gen_parser.c" break; case 19: /* formals: formal_arg "," formals */ -#line 244 "src/parser.y" +#line 208 "src/parser.y" { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1886 "gen/gen_parser.c" +#line 1850 "gen/gen_parser.c" break; case 22: /* opt_formals: %empty */ -#line 249 "src/parser.y" +#line 213 "src/parser.y" { (yyval.node) = NULL; } -#line 1892 "gen/gen_parser.c" +#line 1856 "gen/gen_parser.c" break; case 23: /* formal_arg: IDENT "[" IDENT "]" */ -#line 252 "src/parser.y" +#line 216 "src/parser.y" { (yyval.node) = gen_formal_def((yyvsp[-3].str), (yyvsp[-1].str), src_loc((yyloc))); } -#line 1900 "gen/gen_parser.c" +#line 1864 "gen/gen_parser.c" break; case 24: /* procedure_call: PROC_IDENT "(" opt_arguments ")" */ -#line 257 "src/parser.y" +#line 221 "src/parser.y" { (yyval.node) = gen_proc_call((yyvsp[-3].str), (yyvsp[-1].node), src_loc((yyloc))); } -#line 1908 "gen/gen_parser.c" +#line 1872 "gen/gen_parser.c" break; case 25: /* arguments: expression "," arguments */ -#line 262 "src/parser.y" +#line 226 "src/parser.y" { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1914 "gen/gen_parser.c" +#line 1878 "gen/gen_parser.c" break; case 28: /* opt_arguments: %empty */ -#line 267 "src/parser.y" +#line 231 "src/parser.y" { (yyval.node) = NULL; } -#line 1920 "gen/gen_parser.c" +#line 1884 "gen/gen_parser.c" break; case 29: /* assignment: lvalue "=" rvalue */ -#line 270 "src/parser.y" +#line 234 "src/parser.y" { (yyval.node) = gen_assign((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 1928 "gen/gen_parser.c" +#line 1892 "gen/gen_parser.c" break; case 30: /* ident: IDENT */ -#line 275 "src/parser.y" +#line 239 "src/parser.y" { (yyval.node) = gen_id((yyvsp[0].str), src_loc((yyloc))); } -#line 1936 "gen/gen_parser.c" +#line 1900 "gen/gen_parser.c" break; case 32: /* lvalue: ident "." IDENT */ -#line 281 "src/parser.y" +#line 245 "src/parser.y" { (yyval.node) = gen_dot((yyvsp[-2].node), (yyvsp[0].str), src_loc((yyloc))); } -#line 1942 "gen/gen_parser.c" +#line 1906 "gen/gen_parser.c" break; case 35: /* print_statement: "print" print_items */ -#line 288 "src/parser.y" +#line 252 "src/parser.y" { (yyval.node) = gen_print((yyvsp[0].node), src_loc((yyloc))); } -#line 1948 "gen/gen_parser.c" +#line 1912 "gen/gen_parser.c" break; case 36: /* print_items: print_item "&" print_items */ -#line 291 "src/parser.y" +#line 255 "src/parser.y" { (yyval.node) = (yyvsp[-2].node); (yyval.node)->n = (yyvsp[0].node); } -#line 1954 "gen/gen_parser.c" +#line 1918 "gen/gen_parser.c" break; case 38: /* print_item: STRING */ -#line 295 "src/parser.y" +#line 259 "src/parser.y" { (yyval.node) = gen_const_string((yyvsp[0].str), src_loc((yyloc))); } -#line 1960 "gen/gen_parser.c" +#line 1924 "gen/gen_parser.c" break; case 45: /* statement: "return" expression */ -#line 304 "src/parser.y" +#line 268 "src/parser.y" { (yyval.node) = gen_return((yyvsp[0].node), src_loc((yyloc))); } -#line 1966 "gen/gen_parser.c" +#line 1930 "gen/gen_parser.c" break; case 46: /* until_statement: "do" statement_list "until" expression */ -#line 307 "src/parser.y" +#line 271 "src/parser.y" { (yyval.node) = gen_until((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 1974 "gen/gen_parser.c" +#line 1938 "gen/gen_parser.c" break; case 47: /* unless_statement: "do" statement_list "unless" expression "done" */ -#line 312 "src/parser.y" +#line 276 "src/parser.y" { (yyval.node) = gen_unless((yyvsp[-3].node), (yyvsp[-1].node), NULL, src_loc((yyloc))); } -#line 1982 "gen/gen_parser.c" +#line 1946 "gen/gen_parser.c" break; case 48: /* unless_statement: "do" statement_list "unless" expression "otherwise" statement_list "done" */ -#line 315 "src/parser.y" +#line 279 "src/parser.y" { (yyval.node) = gen_unless((yyvsp[-5].node), (yyvsp[-3].node), (yyvsp[-1].node), src_loc((yyloc))); } -#line 1990 "gen/gen_parser.c" +#line 1954 "gen/gen_parser.c" break; case 50: /* expression: expression "=" simple_expr */ -#line 321 "src/parser.y" +#line 285 "src/parser.y" { (yyval.node) = gen_eq((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 1998 "gen/gen_parser.c" +#line 1962 "gen/gen_parser.c" break; case 51: /* expression: expression "<" simple_expr */ -#line 324 "src/parser.y" +#line 288 "src/parser.y" { (yyval.node) = gen_lt((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 2006 "gen/gen_parser.c" +#line 1970 "gen/gen_parser.c" break; case 53: /* simple_expr: simple_expr "+" term */ -#line 330 "src/parser.y" +#line 294 "src/parser.y" { (yyval.node) = gen_add((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 2014 "gen/gen_parser.c" +#line 1978 "gen/gen_parser.c" break; case 54: /* simple_expr: simple_expr "-" term */ -#line 333 "src/parser.y" +#line 297 "src/parser.y" { (yyval.node) = gen_sub((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 2022 "gen/gen_parser.c" +#line 1986 "gen/gen_parser.c" break; case 56: /* term: term "*" factor */ -#line 339 "src/parser.y" +#line 303 "src/parser.y" { (yyval.node) = gen_mul((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 2030 "gen/gen_parser.c" +#line 1994 "gen/gen_parser.c" break; case 57: /* term: term "/" factor */ -#line 342 "src/parser.y" +#line 306 "src/parser.y" { (yyval.node) = gen_div((yyvsp[-2].node), (yyvsp[0].node), src_loc((yyloc))); } -#line 2038 "gen/gen_parser.c" +#line 2002 "gen/gen_parser.c" break; case 58: /* factor: "+" atom */ -#line 347 "src/parser.y" +#line 311 "src/parser.y" { (yyval.node) = gen_pos((yyvsp[0].node), src_loc((yyloc))); } -#line 2046 "gen/gen_parser.c" +#line 2010 "gen/gen_parser.c" break; case 59: /* factor: "-" atom */ -#line 350 "src/parser.y" +#line 314 "src/parser.y" { (yyval.node) = gen_neg((yyvsp[0].node), src_loc((yyloc))); } -#line 2054 "gen/gen_parser.c" +#line 2018 "gen/gen_parser.c" break; case 62: /* atom: ident "'" IDENT */ -#line 357 "src/parser.y" +#line 321 "src/parser.y" { (yyval.node) = gen_attr((yyvsp[-2].node), (yyvsp[0].str), src_loc((yyloc))); } -#line 2062 "gen/gen_parser.c" +#line 2026 "gen/gen_parser.c" break; case 63: /* atom: INT_LITERAL */ -#line 360 "src/parser.y" +#line 324 "src/parser.y" { (yyval.node) = gen_const_int((yyvsp[0].snum), src_loc((yyloc))); } -#line 2070 "gen/gen_parser.c" +#line 2034 "gen/gen_parser.c" break; case 64: /* atom: DATE_LITERAL */ -#line 363 "src/parser.y" +#line 327 "src/parser.y" { (yyval.node) = gen_const_date((yyvsp[0].num), src_loc((yyloc))); } -#line 2078 "gen/gen_parser.c" +#line 2042 "gen/gen_parser.c" break; case 67: /* atom: "(" expression ")" */ -#line 368 "src/parser.y" +#line 332 "src/parser.y" { (yyval.node) = (yyvsp[-1].node); } -#line 2084 "gen/gen_parser.c" +#line 2048 "gen/gen_parser.c" break; case 68: /* function_call: FUNC_IDENT "(" opt_arguments ")" */ -#line 371 "src/parser.y" +#line 335 "src/parser.y" { (yyval.node) = gen_func_call((yyvsp[-3].str), (yyvsp[-1].node), src_loc((yyloc))); } -#line 2092 "gen/gen_parser.c" +#line 2056 "gen/gen_parser.c" break; case 69: /* unless_expression: "do" expression "unless" expression "otherwise" expression "done" */ -#line 376 "src/parser.y" +#line 340 "src/parser.y" { (yyval.node) = gen_unless_expr((yyvsp[-5].node), (yyvsp[-3].node), (yyvsp[-1].node), src_loc((yyloc))); } -#line 2100 "gen/gen_parser.c" +#line 2064 "gen/gen_parser.c" break; case 71: /* opt_definitions: %empty */ -#line 382 "src/parser.y" +#line 346 "src/parser.y" { (yyval.node) = NULL; } -#line 2106 "gen/gen_parser.c" +#line 2070 "gen/gen_parser.c" break; case 72: /* program: opt_definitions statement_list */ -#line 385 "src/parser.y" +#line 349 "src/parser.y" { if ((yyvsp[-1].node)) { ast_last((yyvsp[-1].node))->n = (yyvsp[0].node); @@ -2118,20 +2082,20 @@ yyreduce: parser->tree = (yyval.node); } -#line 2122 "gen/gen_parser.c" +#line 2086 "gen/gen_parser.c" break; case 73: /* program: error */ -#line 396 "src/parser.y" +#line 360 "src/parser.y" { /* make sure to catch parse errors */ parser->failed = true; } -#line 2131 "gen/gen_parser.c" +#line 2095 "gen/gen_parser.c" break; -#line 2135 "gen/gen_parser.c" +#line 2099 "gen/gen_parser.c" default: break; } @@ -2360,7 +2324,7 @@ yyreturnlab: return yyresult; } -#line 401 "src/parser.y" +#line 365 "src/parser.y" #include "gen_lexer.inc" diff --git a/src/check.c b/src/check.c index b4ea2b1..85e5459 100644 --- a/src/check.c +++ b/src/check.c @@ -35,6 +35,18 @@ static bool has_type(struct ast *n) return n->t != 0; } +static bool concrete_type(enum type_kind k) +{ + switch (k) { + case TYPE_VOID: + case TYPE_AUTO: + return false; + default: break; + } + + return true; +} + static int analyze(struct state *state, struct scope *scope, struct ast *n); static int analyze_list(struct state *state, struct scope *scope, struct ast *l) { @@ -46,6 +58,25 @@ static int analyze_list(struct state *state, struct scope *scope, struct ast *l) return 0; } +static int analyze_statement_list(struct state *state, struct scope *scope, struct ast *l) +{ + foreach_node(n, l) { + if (analyze(state, scope, n)) + return -1; + + if (n->k != AST_PROC_CALL) + continue; + + if (n->t != TYPE_VOID) { + semantic_error(scope, n, + "non-void proc call not allowed as statement"); + return -1; + } + } + + return 0; +} + static struct ast *file_scope_find_analyzed(struct state *state, struct scope *scope, char *id) { @@ -138,7 +169,7 @@ static int analyze_proc_def(struct state *state, struct scope *scope, if (analyze_list(&proc_state, proc_scope, proc_vars(p))) return -1; - if (analyze_list(&proc_state, proc_scope, proc_body(p))) + if (analyze_statement_list(&proc_state, proc_scope, proc_body(p))) return -1; struct ast *last = ast_last(proc_body(p)); @@ -168,6 +199,12 @@ static int analyze_var_def(struct state *state, struct scope *scope, if (analyze(state, scope, expr)) return -1; + if (!concrete_type(expr->t)) { + semantic_error(scope, n, "illegal type in variable definition: %s", + type_str(expr->t)); + return -1; + } + n->t = expr->t; return 0; } @@ -547,10 +584,22 @@ static int analyze_assign(struct state *state, struct scope *scope, if (analyze(state, scope, l)) return -1; + if (!concrete_type(l->t)) { + semantic_error(scope, n, "illegal type for lefthand side: %s", + type_str(l->t)); + return -1; + } + struct ast *r = assign_r(n); if (analyze(state, scope, r)) return -1; + if (!concrete_type(r->t)) { + semantic_error(scope, n, "illegal type for righthand side: %s", + type_str(r->t)); + return -1; + } + if (l->t != r->t) { semantic_error(scope, n, "type mismatch: %s vs %s", type_str(l->t), type_str(r->t)); @@ -667,7 +716,7 @@ static int analyze_until(struct state *state, struct scope *scope, scope_add_scope(scope, until_scope); struct ast *body = until_body(n); - if (analyze_list(state, until_scope, body)) + if (analyze_statement_list(state, until_scope, body)) return -1; struct ast *cond = until_cond(n); @@ -693,7 +742,7 @@ static int analyze_unless(struct state *state, struct scope *scope, scope_add_scope(scope, otherwise_scope); struct ast *body = unless_body(n); - if (analyze_list(state, unless_scope, body)) + if (analyze_statement_list(state, unless_scope, body)) return -1; struct ast *cond = unless_cond(n); @@ -707,7 +756,7 @@ static int analyze_unless(struct state *state, struct scope *scope, } struct ast *otherwise = unless_otherwise(n); - if (otherwise && analyze_list(state, otherwise_scope, otherwise)) + if (otherwise && analyze_statement_list(state, otherwise_scope, otherwise)) return -1; n->t = TYPE_VOID; @@ -824,11 +873,9 @@ int check(struct scope *scope, struct ast *root) } /* actually analyze all nodes */ - foreach_node(n, root) { - struct state state = {0}; - if (analyze(&state, scope, n)) - return -1; - } + struct state state = {0}; + if (analyze_statement_list(&state, scope, root)) + return -1; #ifdef DEBUG foreach_node(n, root) { diff --git a/src/lower.c b/src/lower.c index 7c5fdbd..1949bd5 100644 --- a/src/lower.c +++ b/src/lower.c @@ -299,11 +299,11 @@ static void lower_proc_call(struct fn *f, struct ast *c) output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l)); + f->sp -= count; + c->l = build_local_loc(f->sp); if (c->t != TYPE_VOID) output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0); - - f->sp -= count; } static void lower_func_call(struct fn *f, struct ast *c) @@ -322,11 +322,12 @@ static void lower_func_call(struct fn *f, struct ast *c) struct ast *def = file_scope_find(c->scope, func_call_id(c)); output_insn(f, CALL, null_loc(), null_loc(), null_loc(), loc_as_int(def->l)); + + f->sp -= count; + c->l = build_local_loc(f->sp); if (c->t != TYPE_VOID) output_insn(f, RETVAL, c->l, null_loc(), null_loc(), 0); - - f->sp -= count; } static void lower_eq(struct fn *f, struct ast *n) diff --git a/src/parser.y b/src/parser.y index 7aacd39..d053cce 100644 --- a/src/parser.y +++ b/src/parser.y @@ -9,42 +9,6 @@ #include <posthaste/date.h> #include <posthaste/ast.h> -#define FOREACH_TOKEN(M) \ - M(LPAREN) \ - M(RPAREN) \ - M(LSQUARE) \ - M(RSQUARE) \ - M(LCURLY) \ - M(RCURLY) \ - M(APOSTROPHE) \ - M(AMPERSAND) \ - M(COMMA) \ - M(DOT) \ - M(EQ) \ - M(LT) \ - M(PLUS) \ - M(MINUS) \ - M(MULT) \ - M(DIV) \ - M(VAR) \ - M(IS) \ - M(UNLESS) \ - M(OTHERWISE) \ - M(UNTIL) \ - M(DO) \ - M(DONE) \ - M(PROCEDURE) \ - M(FUNCTION) \ - M(RETURN) \ - M(PRINT) \ - M(END) \ - M(STRING) \ - M(DATE_LITERAL) \ - M(INT_LITERAL) \ - M(IDENT) \ - M(FUNC_IDENT) \ - M(PROC_IDENT) - %} %locations |