@@ -66,6 +66,7 @@ pub enum SymbolTableType {
6666 Module ,
6767 Class ,
6868 Function ,
69+ Comprehension ,
6970}
7071
7172impl fmt:: Display for SymbolTableType {
@@ -74,6 +75,7 @@ impl fmt::Display for SymbolTableType {
7475 SymbolTableType :: Module => write ! ( f, "module" ) ,
7576 SymbolTableType :: Class => write ! ( f, "class" ) ,
7677 SymbolTableType :: Function => write ! ( f, "function" ) ,
78+ SymbolTableType :: Comprehension => write ! ( f, "comprehension" ) ,
7779 }
7880 }
7981}
@@ -99,6 +101,10 @@ pub struct Symbol {
99101 pub is_assigned : bool ,
100102 pub is_parameter : bool ,
101103 pub is_free : bool ,
104+
105+ // indicates if the symbol gets a value assigned by a named expression in a comprehension
106+ // this is required to correct the scope in the analysis.
107+ pub is_assign_namedexpr_in_comprehension : bool ,
102108}
103109
104110impl Symbol {
@@ -111,6 +117,7 @@ impl Symbol {
111117 is_assigned : false ,
112118 is_parameter : false ,
113119 is_free : false ,
120+ is_assign_namedexpr_in_comprehension : false ,
114121 }
115122 }
116123
@@ -193,18 +200,117 @@ impl<'a> SymbolTableAnalyzer<'a> {
193200 for sub_table in sub_tables {
194201 self . analyze_symbol_table ( sub_table) ?;
195202 }
196- let ( symbols, _ ) = self . tables . pop ( ) . unwrap ( ) ;
203+ let ( symbols, st_typ ) = self . tables . pop ( ) . unwrap ( ) ;
197204
198205 // Analyze symbols:
199206 for symbol in symbols. values_mut ( ) {
200- self . analyze_symbol ( symbol) ?;
207+ self . analyze_symbol ( symbol, st_typ ) ?;
201208 }
202209
203210 Ok ( ( ) )
204211 }
205212
206- fn analyze_symbol ( & self , symbol : & mut Symbol ) -> SymbolTableResult {
207- match symbol. scope {
213+ fn analyze_symbol (
214+ & self ,
215+ symbol : & mut Symbol ,
216+ curr_st_typ : SymbolTableType ,
217+ ) -> SymbolTableResult {
218+ //assert!(!symbol.is_assign_namedexpr_in_comprehension || curr_st_typ==SymbolTableType::Comprehension);
219+ if symbol. is_assign_namedexpr_in_comprehension
220+ && curr_st_typ == SymbolTableType :: Comprehension
221+ {
222+ self . analyze_symbol_comprehension ( symbol) ?
223+ } else {
224+ match symbol. scope {
225+ SymbolScope :: Nonlocal => {
226+ // check if name is defined in parent table!
227+ let parent_symbol_table = self . tables . last ( ) ;
228+ // symbol.table.borrow().parent.clone();
229+
230+ if let Some ( ( symbols, _) ) = parent_symbol_table {
231+ let scope_depth = self . tables . len ( ) ;
232+ if !symbols. contains_key ( & symbol. name ) || scope_depth < 2 {
233+ return Err ( SymbolTableError {
234+ error : format ! ( "no binding for nonlocal '{}' found" , symbol. name) ,
235+ location : Default :: default ( ) ,
236+ } ) ;
237+ }
238+ } else {
239+ return Err ( SymbolTableError {
240+ error : format ! (
241+ "nonlocal {} defined at place without an enclosing scope" ,
242+ symbol. name
243+ ) ,
244+ location : Default :: default ( ) ,
245+ } ) ;
246+ }
247+ }
248+ SymbolScope :: Global => {
249+ // TODO: add more checks for globals?
250+ }
251+ SymbolScope :: Local => {
252+ // all is well
253+ }
254+ SymbolScope :: Unknown => {
255+ // Try hard to figure out what the scope of this symbol is.
256+
257+ if symbol. is_assigned || symbol. is_parameter {
258+ symbol. scope = SymbolScope :: Local ;
259+ } else {
260+ // Interesting stuff about the __class__ variable:
261+ // https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
262+ let found_in_outer_scope = symbol. name == "__class__"
263+ || self . tables . iter ( ) . skip ( 1 ) . any ( |( symbols, typ) | {
264+ * typ != SymbolTableType :: Class && symbols. contains_key ( & symbol. name )
265+ } ) ;
266+
267+ if found_in_outer_scope {
268+ // Symbol is in some outer scope.
269+ symbol. is_free = true ;
270+ } else if self . tables . is_empty ( ) {
271+ // Don't make assumptions when we don't know.
272+ symbol. scope = SymbolScope :: Unknown ;
273+ } else {
274+ // If there are scopes above we can assume global.
275+ symbol. scope = SymbolScope :: Global ;
276+ }
277+ }
278+ }
279+ }
280+ }
281+ Ok ( ( ) )
282+ }
283+
284+ fn analyze_symbol_comprehension ( & self , symbol : & mut Symbol ) -> SymbolTableResult {
285+ let parent_symbol_table = self . tables . last ( ) ;
286+
287+ // todo Iterate this over the scope hierarchy
288+ if let Some ( ( symbols, table_type) ) = parent_symbol_table {
289+ match table_type {
290+ SymbolTableType :: Module => {
291+ symbol. scope = SymbolScope :: Global ;
292+ }
293+ SymbolTableType :: Class => { }
294+ SymbolTableType :: Function => match symbols. get ( & symbol. name ) {
295+ Some ( _parent_symbol) => match symbol. scope {
296+ SymbolScope :: Global => {
297+ symbol. scope = SymbolScope :: Global ;
298+ }
299+ _ => {
300+ symbol. scope = SymbolScope :: Nonlocal ;
301+ }
302+ } ,
303+ None => {
304+ symbol. scope = SymbolScope :: Nonlocal ;
305+ }
306+ } ,
307+ SymbolTableType :: Comprehension => {
308+ // TODO check for conflicts
309+ }
310+ }
311+ }
312+
313+ /*match symbol.scope {
208314 SymbolScope::Nonlocal => {
209315 // check if name is defined in parent table!
210316 let parent_symbol_table = self.tables.last();
@@ -232,13 +338,25 @@ impl<'a> SymbolTableAnalyzer<'a> {
232338 // TODO: add more checks for globals?
233339 }
234340 SymbolScope::Local => {
235- // all is well
341+ let parent_symbol_table = self.tables.last();
342+
343+ if let Some((_, table_type)) = parent_symbol_table {
344+ let found_in_outer_scope = symbol.name == "__class__"
345+ || self.tables.iter().skip(1).any(|(symbols, typ)| {
346+ *typ != SymbolTableType::Class && symbols.contains_key(&symbol.name)
347+ });
348+ if found_in_outer_scope && table_type != &SymbolTableType::Module {
349+ symbol.scope = SymbolScope::Nonlocal;
350+ }
351+ } else {
352+ // keep local
353+ }
236354 }
237355 SymbolScope::Unknown => {
238356 // Try hard to figure out what the scope of this symbol is.
239357
240358 if symbol.is_assigned || symbol.is_parameter {
241- symbol. scope = SymbolScope :: Local ;
359+ symbol.scope = SymbolScope::Nonlocal ;
242360 } else {
243361 // Interesting stuff about the __class__ variable:
244362 // https://docs.python.org/3/reference/datamodel.html?highlight=__class__#creating-the-class-object
@@ -250,6 +368,7 @@ impl<'a> SymbolTableAnalyzer<'a> {
250368 if found_in_outer_scope {
251369 // Symbol is in some outer scope.
252370 symbol.is_free = true;
371+ // symbol.scope = SymbolScope::Nonlocal;
253372 } else if self.tables.is_empty() {
254373 // Don't make assumptions when we don't know.
255374 symbol.scope = SymbolScope::Unknown;
@@ -259,7 +378,8 @@ impl<'a> SymbolTableAnalyzer<'a> {
259378 }
260379 }
261380 }
262- }
381+ }*/
382+
263383 Ok ( ( ) )
264384 }
265385}
@@ -602,7 +722,7 @@ impl SymbolTableBuilder {
602722
603723 self . enter_scope (
604724 scope_name,
605- SymbolTableType :: Function ,
725+ SymbolTableType :: Comprehension ,
606726 expression. location . row ( ) ,
607727 ) ;
608728
@@ -681,8 +801,8 @@ impl SymbolTableBuilder {
681801 }
682802
683803 NamedExpression { left, right } => {
684- self . scan_expression ( right, & ExpressionContext :: Store ) ?;
685- self . scan_expression ( left, & ExpressionContext :: Load ) ?;
804+ self . scan_expression ( right, & ExpressionContext :: Load ) ?;
805+ self . scan_expression ( left, & ExpressionContext :: Store ) ?;
686806 }
687807 }
688808 Ok ( ( ) )
@@ -814,6 +934,8 @@ impl SymbolTableBuilder {
814934 }
815935 SymbolUsage :: Assigned => {
816936 symbol. is_assigned = true ;
937+ symbol. is_assign_namedexpr_in_comprehension =
938+ table. typ == SymbolTableType :: Comprehension ;
817939 }
818940 SymbolUsage :: Global => {
819941 if let SymbolScope :: Unknown = symbol. scope {
0 commit comments