diff options
author | Jakub Jelinek <jakub@redhat.com> | 2019-10-10 09:07:30 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2019-10-10 09:07:30 +0200 |
commit | 94e7f906ca5c73fb79d21ec54733e9e75a96c2b4 (patch) | |
tree | 5f686002d13f0905df1681a9408a9d96b23ee258 /gcc/c/c-parser.c | |
parent | 6ea20bd0218fd6386cef3701befa653cee6f1101 (diff) | |
download | gcc-94e7f906ca5c73fb79d21ec54733e9e75a96c2b4.zip gcc-94e7f906ca5c73fb79d21ec54733e9e75a96c2b4.tar.gz gcc-94e7f906ca5c73fb79d21ec54733e9e75a96c2b4.tar.bz2 |
c-common.h (c_omp_check_context_selector, [...]): Declare.
c-family/
* c-common.h (c_omp_check_context_selector,
c_omp_get_context_selector): Declare.
* c-omp.c (c_omp_declare_simd_clauses_to_numbers): Fix spelling
in diagnostic message.
(c_omp_check_context_selector, c_omp_get_context_selector): New
functions.
* c-attribs.c (c_common_attribute_table): Add "omp declare variant"
attribute.
(handle_omp_declare_variant_attribute): New function.
c/
* c-parser.c (c_parser_omp_all_clauses): Add NESTED_P argument, if
true, terminate processing on closing paren and don't skip to end of
pragma line.
(c_parser_omp_declare_simd): Handle also declare variant.
(omp_construct_selectors, omp_device_selectors,
omp_implementation_selectors, omp_user_selectors): New variables.
(c_parser_omp_context_selector,
c_parser_omp_context_selector_specification,
c_finish_omp_declare_variant): New functions.
(c_finish_omp_declare_simd): Handle both declare simd and
declare variant.
(c_parser_omp_declare): Handle declare variant.
cp/
* parser.h (struct cp_omp_declare_simd_data): Add variant_p member.
* parser.c (cp_ensure_no_omp_declare_simd): Handle both declare simd
and declare variant.
(cp_parser_oacc_all_clauses): Formatting fix.
(cp_parser_omp_all_clauses): Add NESTED_P argument, if true, terminate
processing on closing paren and don't skip to end of pragma line.
(cp_parser_omp_declare_simd): Add VARIANT_P argument. Handle also
declare variant.
(omp_construct_selectors, omp_device_selectors,
omp_implementation_selectors, omp_user_selectors): New variables.
(cp_parser_omp_context_selector,
cp_parser_omp_context_selector_specification,
cp_finish_omp_declare_variant): New functions.
(cp_parser_late_parsing_omp_declare_simd): Handle also declare variant.
(cp_parser_omp_declare): Handle declare variant.
testsuite/
* c-c++-common/gomp/declare-variant-1.c: New test.
* c-c++-common/gomp/declare-variant-2.c: New test.
* c-c++-common/gomp/declare-variant-3.c: New test.
* g++.dg/gomp/this-1.C: Adjust for diagnostic message spelling fix.
* gcc.dg/gomp/declare-variant-1.c: New test.
* gcc.dg/gomp/declare-variant-2.c: New test.
From-SVN: r276789
Diffstat (limited to 'gcc/c/c-parser.c')
-rw-r--r-- | gcc/c/c-parser.c | 545 |
1 files changed, 505 insertions, 40 deletions
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 6957297..2daaee8 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -15213,11 +15213,15 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask, } /* Parse all OpenMP clauses. The set clauses allowed by the directive - is a bitmask in MASK. Return the list of clauses found. */ + is a bitmask in MASK. Return the list of clauses found. + FINISH_P set if c_finish_omp_clauses should be called. + NESTED_P set if clauses should be terminated by closing paren instead + of end of pragma. */ static tree c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, - const char *where, bool finish_p = true) + const char *where, bool finish_p = true, + bool nested_p = false) { tree clauses = NULL; bool first = true; @@ -15229,6 +15233,9 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, const char *c_name; tree prev = clauses; + if (nested_p && c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) + break; + if (!first && c_parser_next_token_is (parser, CPP_COMMA)) c_parser_consume_token (parser); @@ -15513,7 +15520,8 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask, } saw_error: - c_parser_skip_to_pragma_eol (parser); + if (!nested_p) + c_parser_skip_to_pragma_eol (parser); if (finish_p) { @@ -18919,7 +18927,11 @@ check_clauses: } /* OpenMP 4.0: - # pragma omp declare simd declare-simd-clauses[optseq] new-line */ + # pragma omp declare simd declare-simd-clauses[optseq] new-line + + OpenMP 5.0: + # pragma omp declare variant (identifier) match(context-selector) new-line + */ #define OMP_DECLARE_SIMD_CLAUSE_MASK \ ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_SIMDLEN) \ @@ -18932,6 +18944,12 @@ check_clauses: static void c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) { + c_token *token = c_parser_peek_token (parser); + gcc_assert (token->type == CPP_NAME); + tree kind = token->value; + gcc_assert (strcmp (IDENTIFIER_POINTER (kind), "simd") == 0 + || strcmp (IDENTIFIER_POINTER (kind), "variant") == 0); + auto_vec<c_token> clauses; while (c_parser_next_token_is_not (parser, CPP_PRAGMA_EOL)) { @@ -18949,17 +18967,14 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) while (c_parser_next_token_is (parser, CPP_PRAGMA)) { - if (c_parser_peek_token (parser)->pragma_kind - != PRAGMA_OMP_DECLARE + if (c_parser_peek_token (parser)->pragma_kind != PRAGMA_OMP_DECLARE || c_parser_peek_2nd_token (parser)->type != CPP_NAME - || strcmp (IDENTIFIER_POINTER - (c_parser_peek_2nd_token (parser)->value), - "simd") != 0) + || c_parser_peek_2nd_token (parser)->value != kind) { - c_parser_error (parser, - "%<#pragma omp declare simd%> must be followed by " - "function declaration or definition or another " - "%<#pragma omp declare simd%>"); + error ("%<#pragma omp declare %s%> must be followed by " + "function declaration or definition or another " + "%<#pragma omp declare %s%>", + IDENTIFIER_POINTER (kind), IDENTIFIER_POINTER (kind)); return; } c_parser_consume_pragma (parser); @@ -19007,8 +19022,9 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) case pragma_struct: case pragma_param: case pragma_stmt: - c_parser_error (parser, "%<#pragma omp declare simd%> must be followed by " - "function declaration or definition"); + error ("%<#pragma omp declare %s%> must be followed by " + "function declaration or definition", + IDENTIFIER_POINTER (kind)); break; case pragma_compound: if (c_parser_next_token_is (parser, CPP_KEYWORD) @@ -19034,38 +19050,470 @@ c_parser_omp_declare_simd (c_parser *parser, enum pragma_context context) NULL, clauses); break; } - c_parser_error (parser, "%<#pragma omp declare simd%> must be followed by " - "function declaration or definition"); + error ("%<#pragma omp declare %s%> must be followed by " + "function declaration or definition", + IDENTIFIER_POINTER (kind)); break; default: gcc_unreachable (); } } -/* Finalize #pragma omp declare simd clauses after FNDECL has been parsed, - and put that into "omp declare simd" attribute. */ +static const char *const omp_construct_selectors[] = { + "simd", "target", "teams", "parallel", "for", NULL }; +static const char *const omp_device_selectors[] = { + "kind", "isa", "arch", NULL }; +static const char *const omp_implementation_selectors[] = { + "vendor", "extension", "atomic_default_mem_order", "unified_address", + "unified_shared_memory", "dynamic_allocators", "reverse_offload", NULL }; +static const char *const omp_user_selectors[] = { + "condition", NULL }; + +/* OpenMP 5.0: + + trait-selector: + trait-selector-name[([trait-score:]trait-property[,trait-property[,...]])] + + trait-score: + score(score-expression) */ + +static tree +c_parser_omp_context_selector (c_parser *parser, tree set, tree parms) +{ + tree ret = NULL_TREE; + do + { + tree selector; + if (c_parser_next_token_is (parser, CPP_KEYWORD) + || c_parser_next_token_is (parser, CPP_NAME)) + selector = c_parser_peek_token (parser)->value; + else + { + c_parser_error (parser, "expected trait selector name"); + return error_mark_node; + } + + tree properties = NULL_TREE; + const char *const *selectors = NULL; + bool allow_score = true; + bool allow_user = false; + int property_limit = 0; + enum { CTX_PROPERTY_NONE, CTX_PROPERTY_USER, CTX_PROPERTY_IDLIST, + CTX_PROPERTY_EXPR, CTX_PROPERTY_SIMD } property_kind + = CTX_PROPERTY_NONE; + switch (IDENTIFIER_POINTER (set)[0]) + { + case 'c': /* construct */ + selectors = omp_construct_selectors; + allow_score = false; + property_limit = 1; + property_kind = CTX_PROPERTY_SIMD; + break; + case 'd': /* device */ + selectors = omp_device_selectors; + allow_score = false; + allow_user = true; + property_limit = 3; + property_kind = CTX_PROPERTY_IDLIST; + break; + case 'i': /* implementation */ + selectors = omp_implementation_selectors; + allow_user = true; + property_limit = 3; + property_kind = CTX_PROPERTY_IDLIST; + break; + case 'u': /* user */ + selectors = omp_user_selectors; + property_limit = 1; + property_kind = CTX_PROPERTY_EXPR; + break; + default: + gcc_unreachable (); + } + for (int i = 0; ; i++) + { + if (selectors[i] == NULL) + { + if (allow_user) + { + property_kind = CTX_PROPERTY_USER; + break; + } + else + { + error_at (c_parser_peek_token (parser)->location, + "selector %qs not allowed for context selector " + "set %qs", IDENTIFIER_POINTER (selector), + IDENTIFIER_POINTER (set)); + c_parser_consume_token (parser); + return error_mark_node; + } + } + if (i == property_limit) + property_kind = CTX_PROPERTY_NONE; + if (strcmp (selectors[i], IDENTIFIER_POINTER (selector)) == 0) + break; + } + + c_parser_consume_token (parser); + + if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) + { + if (property_kind == CTX_PROPERTY_NONE) + { + error_at (c_parser_peek_token (parser)->location, + "selector %qs does not accept any properties", + IDENTIFIER_POINTER (selector)); + return error_mark_node; + } + + matching_parens parens; + parens.require_open (parser); + + c_token *token = c_parser_peek_token (parser); + if (allow_score + && c_parser_next_token_is (parser, CPP_NAME) + && strcmp (IDENTIFIER_POINTER (token->value), "score") == 0 + && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_PAREN) + { + c_parser_consume_token (parser); + + matching_parens parens2; + parens2.require_open (parser); + tree score = c_parser_expr_no_commas (parser, NULL).value; + parens2.skip_until_found_close (parser); + c_parser_require (parser, CPP_COLON, "expected %<:%>"); + if (score != error_mark_node) + { + mark_exp_read (score); + score = c_fully_fold (score, false, NULL); + if (!INTEGRAL_TYPE_P (TREE_TYPE (score)) + || !tree_fits_shwi_p (score)) + error_at (token->location, "score argument must be " + "constant integer expression"); + else + properties = tree_cons (get_identifier (" score"), + score, properties); + } + token = c_parser_peek_token (parser); + } + + switch (property_kind) + { + tree t; + case CTX_PROPERTY_USER: + do + { + t = c_parser_expr_no_commas (parser, NULL).value; + if (TREE_CODE (t) == STRING_CST) + properties = tree_cons (NULL_TREE, t, properties); + else if (t != error_mark_node) + { + mark_exp_read (t); + t = c_fully_fold (t, false, NULL); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t)) + || !tree_fits_shwi_p (t)) + error_at (token->location, "property must be " + "constant integer expression or string " + "literal"); + else + properties = tree_cons (NULL_TREE, t, properties); + } + + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + else + break; + } + while (1); + break; + case CTX_PROPERTY_IDLIST: + do + { + tree prop; + if (c_parser_next_token_is (parser, CPP_KEYWORD) + || c_parser_next_token_is (parser, CPP_NAME)) + prop = c_parser_peek_token (parser)->value; + else + { + c_parser_error (parser, "expected identifier"); + return error_mark_node; + } + c_parser_consume_token (parser); + + properties = tree_cons (prop, NULL_TREE, properties); + + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + else + break; + } + while (1); + break; + case CTX_PROPERTY_EXPR: + t = c_parser_expr_no_commas (parser, NULL).value; + if (t != error_mark_node) + { + mark_exp_read (t); + t = c_fully_fold (t, false, NULL); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t)) + || !tree_fits_shwi_p (t)) + error_at (token->location, "property must be " + "constant integer expression"); + else + properties = tree_cons (NULL_TREE, t, properties); + } + break; + case CTX_PROPERTY_SIMD: + if (parms == NULL_TREE) + { + error_at (token->location, "properties for %<simd%> " + "selector may not be specified in " + "%<metadirective%>"); + return error_mark_node; + } + tree c; + c = c_parser_omp_all_clauses (parser, + OMP_DECLARE_SIMD_CLAUSE_MASK, + "simd", true, true); + c = c_omp_declare_simd_clauses_to_numbers (parms + == error_mark_node + ? NULL_TREE : parms, + c); + properties = tree_cons (NULL_TREE, c, properties); + break; + default: + gcc_unreachable (); + } + + parens.skip_until_found_close (parser); + properties = nreverse (properties); + } + else if (property_kind == CTX_PROPERTY_IDLIST + || property_kind == CTX_PROPERTY_EXPR) + { + c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"); + return error_mark_node; + } + + ret = tree_cons (selector, properties, ret); + + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + else + break; + } + while (1); + + return nreverse (ret); +} + +/* OpenMP 5.0: + + trait-set-selector[,trait-set-selector[,...]] + + trait-set-selector: + trait-set-selector-name = { trait-selector[, trait-selector[, ...]] } + + trait-set-selector-name: + constructor + device + implementation + user */ + +static tree +c_parser_omp_context_selector_specification (c_parser *parser, tree parms) +{ + tree ret = NULL_TREE; + do + { + const char *setp = ""; + if (c_parser_next_token_is (parser, CPP_NAME)) + setp = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + switch (setp[0]) + { + case 'c': + if (strcmp (setp, "construct") == 0) + setp = NULL; + break; + case 'd': + if (strcmp (setp, "device") == 0) + setp = NULL; + break; + case 'i': + if (strcmp (setp, "implementation") == 0) + setp = NULL; + break; + case 'u': + if (strcmp (setp, "user") == 0) + setp = NULL; + break; + default: + break; + } + if (setp) + { + c_parser_error (parser, "expected %<construct%>, %<device%>, " + "%<implementation%> or %<user%>"); + return error_mark_node; + } + + tree set = c_parser_peek_token (parser)->value; + c_parser_consume_token (parser); + + if (!c_parser_require (parser, CPP_EQ, "expected %<=%>")) + return error_mark_node; + + matching_braces braces; + if (!braces.require_open (parser)) + return error_mark_node; + + tree selectors = c_parser_omp_context_selector (parser, set, parms); + if (selectors == error_mark_node) + ret = error_mark_node; + else if (ret != error_mark_node) + ret = tree_cons (set, selectors, ret); + + braces.skip_until_found_close (parser); + + if (c_parser_next_token_is (parser, CPP_COMMA)) + c_parser_consume_token (parser); + else + break; + } + while (1); + + if (ret == error_mark_node) + return ret; + return nreverse (ret); +} + +/* Finalize #pragma omp declare variant after FNDECL has been parsed, and put + that into "omp declare variant" attribute. */ + +static void +c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms) +{ + matching_parens parens; + if (!parens.require_open (parser)) + { + fail: + c_parser_skip_to_pragma_eol (parser, false); + return; + } + + if (c_parser_next_token_is_not (parser, CPP_NAME) + || c_parser_peek_token (parser)->id_kind != C_ID_ID) + { + c_parser_error (parser, "expected identifier"); + goto fail; + } + + c_token *token = c_parser_peek_token (parser); + tree variant = lookup_name (token->value); + + if (variant == NULL_TREE) + { + undeclared_variable (token->location, token->value); + variant = error_mark_node; + } + + c_parser_consume_token (parser); + + parens.require_close (parser); + + const char *clause = ""; + location_t match_loc = c_parser_peek_token (parser)->location; + if (c_parser_next_token_is (parser, CPP_NAME)) + clause = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + if (strcmp (clause, "match")) + { + c_parser_error (parser, "expected %<match%>"); + goto fail; + } + + c_parser_consume_token (parser); + + if (!parens.require_open (parser)) + goto fail; + + if (parms == NULL_TREE) + parms = error_mark_node; + + tree ctx = c_parser_omp_context_selector_specification (parser, parms); + if (ctx == error_mark_node) + goto fail; + ctx = c_omp_check_context_selector (match_loc, ctx); + if (ctx != error_mark_node && variant != error_mark_node) + { + if (TREE_CODE (variant) != FUNCTION_DECL) + { + error_at (token->location, "variant %qD is not a function", variant); + variant = error_mark_node; + } + else if (c_omp_get_context_selector (ctx, "construct", "simd") + == NULL_TREE + && !comptypes (TREE_TYPE (fndecl), TREE_TYPE (variant))) + { + error_at (token->location, "variant %qD and base %qD have " + "incompatible types", variant, fndecl); + variant = error_mark_node; + } + else if (fndecl_built_in_p (variant) + && (strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)), + "__builtin_", strlen ("__builtin_")) == 0 + || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)), + "__sync_", strlen ("__sync_")) == 0 + || strncmp (IDENTIFIER_POINTER (DECL_NAME (variant)), + "__atomic_", strlen ("__atomic_")) == 0)) + { + error_at (token->location, "variant %qD is a built-in", variant); + variant = error_mark_node; + } + if (variant != error_mark_node) + { + C_DECL_USED (variant) = 1; + tree attr = tree_cons (get_identifier ("omp declare variant"), + build_tree_list (variant, ctx), + DECL_ATTRIBUTES (fndecl)); + DECL_ATTRIBUTES (fndecl) = attr; + } + } + + parens.require_close (parser); + c_parser_skip_to_pragma_eol (parser); +} + +/* Finalize #pragma omp declare simd or #pragma omp declare variant + clauses after FNDECL has been parsed, and put that into "omp declare simd" + or "omp declare variant" attribute. */ static void c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms, vec<c_token> clauses) { - /* Normally first token is CPP_NAME "simd". CPP_EOF there indicates - error has been reported and CPP_PRAGMA that c_finish_omp_declare_simd - has already processed the tokens. */ + /* Normally first token is CPP_NAME "simd" or "variant". CPP_EOF there + indicates error has been reported and CPP_PRAGMA that + c_finish_omp_declare_simd has already processed the tokens. */ if (clauses.exists () && clauses[0].type == CPP_EOF) return; + const char *kind = "simd"; + if (clauses.exists () + && (clauses[0].type == CPP_NAME || clauses[0].type == CPP_PRAGMA)) + kind = IDENTIFIER_POINTER (clauses[0].value); + gcc_assert (strcmp (kind, "simd") == 0 || strcmp (kind, "variant") == 0); if (fndecl == NULL_TREE || TREE_CODE (fndecl) != FUNCTION_DECL) { - error ("%<#pragma omp declare simd%> not immediately followed by " - "a function declaration or definition"); + error ("%<#pragma omp declare %s%> not immediately followed by " + "a function declaration or definition", kind); clauses[0].type = CPP_EOF; return; } if (clauses.exists () && clauses[0].type != CPP_NAME) { error_at (DECL_SOURCE_LOCATION (fndecl), - "%<#pragma omp declare simd%> not immediately followed by " - "a single function declaration or definition"); + "%<#pragma omp declare %s%> not immediately followed by " + "a single function declaration or definition", kind); clauses[0].type = CPP_EOF; return; } @@ -19075,7 +19523,6 @@ c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms, unsigned int tokens_avail = parser->tokens_avail; gcc_assert (parser->tokens == &parser->tokens_buf[0]); - parser->tokens = clauses.address (); parser->tokens_avail = clauses.length (); @@ -19085,19 +19532,27 @@ c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms, { c_token *token = c_parser_peek_token (parser); gcc_assert (token->type == CPP_NAME - && strcmp (IDENTIFIER_POINTER (token->value), "simd") == 0); + && strcmp (IDENTIFIER_POINTER (token->value), kind) == 0); c_parser_consume_token (parser); parser->in_pragma = true; - tree c = NULL_TREE; - c = c_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK, - "#pragma omp declare simd"); - c = c_omp_declare_simd_clauses_to_numbers (parms, c); - if (c != NULL_TREE) - c = tree_cons (NULL_TREE, c, NULL_TREE); - c = build_tree_list (get_identifier ("omp declare simd"), c); - TREE_CHAIN (c) = DECL_ATTRIBUTES (fndecl); - DECL_ATTRIBUTES (fndecl) = c; + if (strcmp (kind, "simd") == 0) + { + tree c; + c = c_parser_omp_all_clauses (parser, OMP_DECLARE_SIMD_CLAUSE_MASK, + "#pragma omp declare simd"); + c = c_omp_declare_simd_clauses_to_numbers (parms, c); + if (c != NULL_TREE) + c = tree_cons (NULL_TREE, c, NULL_TREE); + c = build_tree_list (get_identifier ("omp declare simd"), c); + TREE_CHAIN (c) = DECL_ATTRIBUTES (fndecl); + DECL_ATTRIBUTES (fndecl) = c; + } + else + { + gcc_assert (strcmp (kind, "variant") == 0); + c_finish_omp_declare_variant (parser, fndecl, parms); + } } parser->tokens = &parser->tokens_buf[0]; @@ -19612,7 +20067,10 @@ c_parser_omp_declare_reduction (c_parser *parser, enum pragma_context context) #pragma omp declare simd declare-simd-clauses[optseq] new-line #pragma omp declare reduction (reduction-id : typename-list : expression) \ initializer-clause[opt] new-line - #pragma omp declare target new-line */ + #pragma omp declare target new-line + + OpenMP 5.0 + #pragma omp declare variant (identifier) match (context-selector) */ static void c_parser_omp_declare (c_parser *parser, enum pragma_context context) @@ -19645,10 +20103,17 @@ c_parser_omp_declare (c_parser *parser, enum pragma_context context) c_parser_omp_declare_target (parser); return; } + if (strcmp (p, "variant") == 0) + { + /* c_parser_consume_token (parser); done in + c_parser_omp_declare_simd. */ + c_parser_omp_declare_simd (parser, context); + return; + } } - c_parser_error (parser, "expected %<simd%> or %<reduction%> " - "or %<target%>"); + c_parser_error (parser, "expected %<simd%>, %<reduction%>, " + "%<target%> or %<variant%>"); c_parser_skip_to_pragma_eol (parser); } |