@@ -49,12 +49,18 @@ def __init__(self, cs: cstruct, compiled: bool = True, align: bool = False):
4949 self .compiled = compiled
5050 self .align = align
5151 self .TOK = self ._tokencollection ()
52+ self ._conditionals = []
53+ self ._conditionals_depth = 0
5254
5355 @staticmethod
5456 def _tokencollection () -> TokenCollection :
5557 TOK = TokenCollection ()
5658 TOK .add (r"#\[(?P<values>[^\]]+)\](?=\s*)" , "CONFIG_FLAG" )
57- TOK .add (r"#define\s+(?P<name>[^\s]+)\s+(?P<value>[^\r\n]+)\s*" , "DEFINE" )
59+ TOK .add (r"#define\s+(?P<name>[^\s]+)(?P<value>[^\r\n]*)" , "DEFINE" )
60+ TOK .add (r"#ifdef\s+(?P<name>[^\s]+)\s*" , "IFDEF" )
61+ TOK .add (r"#ifndef\s+(?P<name>[^\s]+)\s*" , "IFNDEF" )
62+ TOK .add (r"#else\s*" , "ELSE" )
63+ TOK .add (r"#endif\s*" , "ENDIF" )
5864 TOK .add (r"typedef(?=\s)" , "TYPEDEF" )
5965 TOK .add (r"(?:struct|union)(?=\s|{)" , "STRUCT" )
6066 TOK .add (
@@ -80,12 +86,61 @@ def _identifier(self, tokens: TokenConsumer) -> str:
8086 idents .append (tokens .consume ())
8187 return " " .join ([i .value for i in idents ])
8288
89+ def _conditional (self , tokens : TokenConsumer ) -> None :
90+ token = tokens .consume ()
91+ pattern = self .TOK .patterns [token .token ]
92+ match = pattern .match (token .value ).groupdict ()
93+
94+ value = match ["name" ]
95+
96+ if token .token == self .TOK .IFDEF :
97+ self ._conditionals .append (value in self .cstruct .consts )
98+ elif token .token == self .TOK .IFNDEF :
99+ self ._conditionals .append (value not in self .cstruct .consts )
100+
101+ def _check_conditional (self , tokens : TokenConsumer ) -> bool :
102+ """Check and handle conditionals. Return a boolean indicating if we need to continue to the next token."""
103+ if self ._conditionals and self ._conditionals_depth == len (self ._conditionals ):
104+ # If we have a conditional and the depth matches, handle it accordingly
105+ if tokens .next == self .TOK .ELSE :
106+ # Flip the last conditional
107+ tokens .consume ()
108+ self ._conditionals [- 1 ] = not self ._conditionals [- 1 ]
109+ return True
110+
111+ if tokens .next == self .TOK .ENDIF :
112+ # Pop the last conditional
113+ tokens .consume ()
114+ self ._conditionals .pop ()
115+ self ._conditionals_depth -= 1
116+ return True
117+
118+ if tokens .next in (self .TOK .IFDEF , self .TOK .IFNDEF ):
119+ # If we encounter a new conditional, increase the depth
120+ self ._conditionals_depth += 1
121+
122+ if tokens .next == self .TOK .ENDIF :
123+ # Similarly, decrease the depth if needed
124+ self ._conditionals_depth -= 1
125+
126+ if self ._conditionals and not self ._conditionals [- 1 ]:
127+ # If the last conditional evaluated to False, skip the next token
128+ tokens .consume ()
129+ return True
130+
131+ if tokens .next in (self .TOK .IFDEF , self .TOK .IFNDEF ):
132+ # If the next token is a conditional, process it
133+ self ._conditional (tokens )
134+ return True
135+
136+ return False
137+
83138 def _constant (self , tokens : TokenConsumer ) -> None :
84139 const = tokens .consume ()
85140 pattern = self .TOK .patterns [self .TOK .DEFINE ]
86141 match = pattern .match (const .value ).groupdict ()
87142
88- value = match ["value" ]
143+ value = match ["value" ]. strip ()
89144 try :
90145 value = ast .literal_eval (value )
91146 except (ValueError , SyntaxError ):
@@ -208,6 +263,9 @@ def _struct(self, tokens: TokenConsumer, register: bool = False) -> type[Structu
208263 tokens .consume ()
209264 break
210265
266+ if self ._check_conditional (tokens ):
267+ continue
268+
211269 field = self ._parse_field (tokens )
212270 fields .append (field )
213271
@@ -266,7 +324,7 @@ def _parse_field(self, tokens: TokenConsumer) -> Field:
266324 return Field (None , type_ , None )
267325
268326 if tokens .next != self .TOK .NAME :
269- raise ParserError (f"line { self ._lineno (tokens .next )} : expected name" )
327+ raise ParserError (f"line { self ._lineno (tokens .next )} : expected name, got { tokens . next !r } " )
270328 nametok = tokens .consume ()
271329
272330 type_ , name , bits = self ._parse_field_type (type_ , nametok .value )
@@ -378,6 +436,9 @@ def parse(self, data: str) -> None:
378436 if token is None :
379437 break
380438
439+ if self ._check_conditional (tokens ):
440+ continue
441+
381442 if token == self .TOK .CONFIG_FLAG :
382443 self ._config_flag (tokens )
383444 elif token == self .TOK .DEFINE :
@@ -395,6 +456,9 @@ def parse(self, data: str) -> None:
395456 else :
396457 raise ParserError (f"line { self ._lineno (token )} : unexpected token { token !r} " )
397458
459+ if self ._conditionals :
460+ raise ParserError (f"line { self ._lineno (tokens .previous )} : unclosed conditional statement" )
461+
398462
399463class CStyleParser (Parser ):
400464 """Definition parser for C-like structure syntax.
0 commit comments