@@ -32,33 +32,53 @@ class _State:
3232 block_type : str | None = None # "bundle" | "body" | "promise" | None
3333 promise_type : str | None = None # "vars" | "files" | "classes" | ... | None
3434 attribute_name : str | None = None # "if" | "string" | "slist" | ... | None
35+ namespace : str = "default" # "ns" | "default" | ... |
3536
3637 def update (self , node ) -> "_State" :
3738 """Updates and returns the state that should apply to the children of `node`."""
39+ if node .type == "}" :
40+ if self .attribute_name == "slist" :
41+ self .attribute_name = None
42+ else :
43+ self .block_type = None
44+ self .promise_type = None
45+ return self
46+ if node .type == ";" :
47+ self .attribute_name = None
48+ return self
3849 if node .type == "bundle_block" :
39- return _State (block_type = "bundle" )
50+ self .block_type = "bundle"
51+ return self
4052 if node .type == "body_block" :
41- return _State (block_type = "body" )
53+ self .block_type = "body"
54+ return self
4255 if node .type == "promise_block" :
43- return _State (block_type = "promise" )
56+ self .block_type = "promise"
57+ return self
4458 if node .type == "bundle_section" :
45- for child in node .children :
46- if child .type == "promise_guard" :
47- return _State (
48- block_type = self .block_type ,
49- promise_type = _text (child )[:- 1 ], # strip trailing ':'
50- )
51- return _State (block_type = self .block_type )
59+ # A bundle_section is always: promise_guard, [promises], [class_guarded_promises...]
60+ # The promise_guard is guaranteed to exist by the grammar
61+ guard = next ((c for c in node .children if c .type == "promise_guard" ), None )
62+ if guard is None : # Should never happen
63+ print ("ERROR: Bundle section without a promise guard" )
64+ return self
65+
66+ self .promise_type = _text (guard )[:- 1 ] # strip trailing ':'
67+ return self
5268 if node .type == "attribute" :
5369 for child in node .children :
5470 if child .type == "attribute_name" :
55- return _State (
56- block_type = self .block_type ,
57- promise_type = self .promise_type ,
58- attribute_name = _text (child ),
59- )
71+ self .attribute_name = _text (child )
72+ if self .attribute_name == "namespace" :
73+ self .namespace = _text (child .next_named_sibling ).strip ("\" '" )
74+ return self
6075 return self
6176
77+ @staticmethod
78+ def qualify (name : str , namespace : str ) -> str :
79+ """If name is already qualified (contains ':'), return as-is. Otherwise prepend namespace."""
80+ return name if ":" in name else f"{ namespace } :{ name } "
81+
6282
6383def lint_cfbs_json (filename ) -> int :
6484 assert os .path .isfile (filename )
@@ -184,7 +204,8 @@ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
184204 if node .type == "calling_identifier" :
185205 if (
186206 strict
187- and _text (node ) in user_definition .get ("all_bundle_names" , set ())
207+ and state .qualify (_text (node ), state .namespace )
208+ in user_definition .get ("all_bundle_names" , set ())
188209 and state .promise_type in user_definition .get ("custom_promise_types" , set ())
189210 ):
190211 _highlight_range (node , lines )
@@ -193,11 +214,12 @@ def _node_checks(filename, lines, node, user_definition, strict, state: _State):
193214 )
194215 return 1
195216 if strict and (
196- _text (node )
197- not in BUILTIN_FUNCTIONS .union (
217+ state . qualify ( _text (node ), state . namespace )
218+ not in set .union (
198219 user_definition .get ("all_bundle_names" , set ()),
199220 user_definition .get ("all_body_names" , set ()),
200221 )
222+ and _text (node ) not in BUILTIN_FUNCTIONS
201223 ):
202224 _highlight_range (node , lines )
203225 print (
@@ -215,11 +237,9 @@ def _stateful_walk(
215237
216238 errors = _node_checks (filename , lines , node , user_definition , strict , state )
217239
218- child_state = state .update (node )
240+ state .update (node )
219241 for child in node .children :
220- errors += _stateful_walk (
221- filename , lines , child , user_definition , strict , child_state
222- )
242+ errors += _stateful_walk (filename , lines , child , user_definition , strict , state )
223243 return errors
224244
225245
@@ -239,18 +259,55 @@ def _walk(filename, lines, node, user_definition=None, strict=True) -> int:
239259 line = node .range .start_point [0 ] + 1
240260 column = node .range .start_point [1 ] + 1
241261
242- return _stateful_walk (filename , lines , node , user_definition , strict )
262+ state = _State ()
263+ ret = _stateful_walk (filename , lines , node , user_definition , strict , state = state )
264+ state = _State () # Clear state
265+ return ret
243266
244267
245268def _parse_user_definition (filename , lines , root_node ):
246- promise_blocks = _find_node_type (filename , lines , root_node , "promise_block_name" )
247- bundle_blocks = _find_node_type (filename , lines , root_node , "bundle_block_name" )
248- body_blocks = _find_node_type (filename , lines , root_node , "body_block_name" )
269+ ns = "default"
270+ promise_blocks = set ()
271+ bundle_blocks = set ()
272+ body_blocks = set ()
273+
274+ for child in root_node .children :
275+ if child .type == "body_block" :
276+ name_node = next (
277+ (c for c in child .named_children if c .type == "body_block_name" ),
278+ None ,
279+ )
280+ ns_attr = next (
281+ (
282+ c
283+ for c in _find_node_type (filename , lines , child , "attribute_name" )
284+ if _text (c ) == "namespace"
285+ ),
286+ None ,
287+ )
288+ if ns_attr is not None :
289+ ns = _text (ns_attr .next_named_sibling ).strip ("\" '" )
290+ elif name_node is not None :
291+ body_blocks .add (_State .qualify (_text (name_node ), ns ))
292+ elif child .type == "bundle_block" :
293+ name_node = next (
294+ (c for c in child .named_children if c .type == "bundle_block_name" ),
295+ None ,
296+ )
297+ if name_node is not None :
298+ bundle_blocks .add (_State .qualify (_text (name_node ), ns ))
299+ elif child .type == "promise_block" :
300+ name_node = next (
301+ (c for c in child .named_children if c .type == "promise_block_name" ),
302+ None ,
303+ )
304+ if name_node is not None :
305+ promise_blocks .add (_text (name_node ))
249306
250307 return {
251- "custom_promise_types" : { _text ( x ) for x in promise_blocks } ,
252- "all_bundle_names" : { _text ( x ) for x in bundle_blocks } ,
253- "all_body_names" : { _text ( x ) for x in body_blocks } ,
308+ "custom_promise_types" : promise_blocks ,
309+ "all_bundle_names" : bundle_blocks ,
310+ "all_body_names" : body_blocks ,
254311 }
255312
256313
0 commit comments