Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parenthesized compound statement support #422

Closed
JordanYates opened this issue Jul 6, 2021 · 4 comments
Closed

Parenthesized compound statement support #422

JordanYates opened this issue Jul 6, 2021 · 4 comments

Comments

@JordanYates
Copy link
Contributor

Compound statements enclosed in parentheses are a relatively common pattern, particularly when implementing single evaluation macros such as:

/** @brief Return larger value of two provided expressions.
 *
 * Macro ensures that expressions are evaluated only once.
 */
#define Z_MAX(a, b) ({ \
		/* random suffix to avoid naming conflict */ \
		__typeof__(a) _value_a_ = (a); \
		__typeof__(b) _value_b_ = (b); \
		_value_a_ > _value_b_ ? _value_a_ : _value_b_; \
	})

I cannot find whether the ({...}) pattern is an extension or standard C, the following page simply states that GNU C allows it. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html.

Regardless of its extension status, I haven't found a compiler on https://godbolt.org/ that fails to compile the following snippet:

int main(void)
{
	({ int x = 2; });
	return 2;
}

Attempting to parse the above using pycparser (release_2.20) results in:

Traceback (most recent call last):
  File "./test.py", line 93, in <module>
    validate(**vars(args))
  File "./test.py", line 81, in validate
    ast = parse_file("test.c")
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/__init__.py", line 90, in parse_file
    return parser.parse(text, filename)
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/c_parser.py", line 152, in parse
    debug=debuglevel)
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/ply/yacc.py", line 331, in parse
    return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/ply/yacc.py", line 1199, in parseopt_notrack
    tok = call_errorfunc(self.errorfunc, errtoken, self)
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/ply/yacc.py", line 193, in call_errorfunc
    r = errorfunc(token)
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/c_parser.py", line 1861, in p_error
    column=self.clex.find_tok_column(p)))
  File "/home/jordan/.local/lib/python3.6/site-packages/pycparser/plyparser.py", line 67, in _parse_error
    raise ParseError("%s: %s" % (coord, msg))
pycparser.plyparser.ParseError: test.c:4:3: before: {
@eliben
Copy link
Owner

eliben commented Jul 6, 2021

Thanks for the detailed report. I have no objections to adding support for this in pycparser; PRs welcome.

@JordanYates
Copy link
Contributor Author

    def p_parenthesized_compound_expression(self, p):
        """ assignment_expression : LPAREN compound_statement RPAREN 
        """
        p[0] = p[2]

is sufficient to parse

void main(void)
{
	int z;
	({});
	({ 1; });
	({ 1; 2; });
	int x = ({ 1; });
	int y = ({ int a = 1; a + 2; });
	z = ({ int a = 1; 2 * a; });
}

as:

FileAST:
  FuncDef:
    Decl: main, [], [], []
      FuncDecl:
        ParamList:
          Typename: None, []
            TypeDecl: None, []
              IdentifierType: ['void']
        TypeDecl: main, []
          IdentifierType: ['void']
    Compound:
      Decl: z, [], [], []
        TypeDecl: z, []
          IdentifierType: ['int']
      Compound:
      Compound:
        Constant: int, 1
      Compound:
        Constant: int, 1
        Constant: int, 2
      Decl: x, [], [], []
        TypeDecl: x, []
          IdentifierType: ['int']
        Compound:
          Constant: int, 1
      Decl: y, [], [], []
        TypeDecl: y, []
          IdentifierType: ['int']
        Compound:
          Decl: a, [], [], []
            TypeDecl: a, []
              IdentifierType: ['int']
            Constant: int, 1
          BinaryOp: +
            ID: a
            Constant: int, 2
      Assignment: =
        ID: z
        Compound:
          Decl: a, [], [], []
            TypeDecl: a, []
              IdentifierType: ['int']
            Constant: int, 1
          BinaryOp: *
            Constant: int, 2
            ID: a

I'm not sure if this sufficiently captures the "evaluates to last statement" nature though.

@eliben
Copy link
Owner

eliben commented Jul 7, 2021

Fixed by #423

@eliben eliben closed this as completed Jul 7, 2021
@Etherealflux
Copy link

I've found that these parenthesized compound statements are legal in more places than the parser currently allows. Some examples:

int x = (int) ({ 3; });
int y = ({ 3; }) + 5;

Adjusting the rule for the parenthesized statement to come from a primary_expression instead of an assignment_expression allows these lines to parse, but I'm unclear if this could lead to invalid code being accepted by the parser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants