3
3
This ensures that global and nonlocal keywords are picked up.
4
4
Then the compiler can use the symbol table to generate proper
5
5
load and store instructions for names.
6
+
7
+ Inspirational file: https://github.com/python/cpython/blob/master/Python/symtable.c
6
8
*/
7
9
10
+ use crate :: error:: { CompileError , CompileErrorType } ;
8
11
use rustpython_parser:: ast;
12
+ use rustpython_parser:: lexer:: Location ;
9
13
use std:: collections:: HashMap ;
10
14
11
- pub fn make_symbol_table ( program : & ast:: Program ) -> SymbolScope {
15
+ pub fn make_symbol_table ( program : & ast:: Program ) -> Result < SymbolScope , SymbolTableError > {
12
16
let mut builder = SymbolTableBuilder :: new ( ) ;
13
17
builder. enter_scope ( ) ;
14
- builder. scan_program ( program) . unwrap ( ) ;
18
+ builder. scan_program ( program) ? ;
15
19
assert ! ( builder. scopes. len( ) == 1 ) ;
16
- builder. scopes . pop ( ) . unwrap ( )
20
+ let symbol_table = builder. scopes . pop ( ) . unwrap ( ) ;
21
+ analyze_symbol_table ( & symbol_table, None ) ?;
22
+ Ok ( symbol_table)
17
23
}
18
24
19
- pub fn statements_to_symbol_table ( statements : & [ ast:: LocatedStatement ] ) -> SymbolScope {
25
+ pub fn statements_to_symbol_table (
26
+ statements : & [ ast:: LocatedStatement ] ,
27
+ ) -> Result < SymbolScope , SymbolTableError > {
20
28
let mut builder = SymbolTableBuilder :: new ( ) ;
21
29
builder. enter_scope ( ) ;
22
- builder. scan_statements ( statements) . unwrap ( ) ;
30
+ builder. scan_statements ( statements) ? ;
23
31
assert ! ( builder. scopes. len( ) == 1 ) ;
24
- builder. scopes . pop ( ) . unwrap ( )
32
+ let symbol_table = builder. scopes . pop ( ) . unwrap ( ) ;
33
+ analyze_symbol_table ( & symbol_table, None ) ?;
34
+ Ok ( symbol_table)
25
35
}
26
36
27
37
#[ derive( Debug ) ]
@@ -43,7 +53,22 @@ pub struct SymbolScope {
43
53
pub sub_scopes : Vec < SymbolScope > ,
44
54
}
45
55
46
- type SymbolTableResult = Result < ( ) , String > ;
56
+ #[ derive( Debug ) ]
57
+ pub struct SymbolTableError {
58
+ error : String ,
59
+ location : Location ,
60
+ }
61
+
62
+ impl From < SymbolTableError > for CompileError {
63
+ fn from ( error : SymbolTableError ) -> Self {
64
+ CompileError {
65
+ error : CompileErrorType :: SyntaxError ( error. error ) ,
66
+ location : error. location ,
67
+ }
68
+ }
69
+ }
70
+
71
+ type SymbolTableResult = Result < ( ) , SymbolTableError > ;
47
72
48
73
impl SymbolScope {
49
74
pub fn new ( ) -> Self {
@@ -69,6 +94,58 @@ impl std::fmt::Debug for SymbolScope {
69
94
}
70
95
}
71
96
97
+ /* Perform some sort of analysis on nonlocals, globals etc..
98
+ See also: https://github.com/python/cpython/blob/master/Python/symtable.c#L410
99
+ */
100
+ fn analyze_symbol_table (
101
+ symbol_scope : & SymbolScope ,
102
+ parent_symbol_scope : Option < & SymbolScope > ,
103
+ ) -> SymbolTableResult {
104
+ // println!("Analyzing {:?}, parent={:?} symbols={:?}", symbol_scope, parent_symbol_scope, symbol_scope.symbols);
105
+ // Analyze sub scopes:
106
+ for sub_scope in & symbol_scope. sub_scopes {
107
+ analyze_symbol_table ( & sub_scope, Some ( symbol_scope) ) ?;
108
+ }
109
+
110
+ // Analyze symbols:
111
+ for ( symbol_name, symbol_role) in & symbol_scope. symbols {
112
+ analyze_symbol ( symbol_name, symbol_role, parent_symbol_scope) ?;
113
+ }
114
+
115
+ Ok ( ( ) )
116
+ }
117
+
118
+ fn analyze_symbol (
119
+ symbol_name : & str ,
120
+ symbol_role : & SymbolRole ,
121
+ parent_symbol_scope : Option < & SymbolScope > ,
122
+ ) -> SymbolTableResult {
123
+ match symbol_role {
124
+ SymbolRole :: Nonlocal => {
125
+ // check if name is defined in parent scope!
126
+ if let Some ( parent_symbol_scope) = parent_symbol_scope {
127
+ if !parent_symbol_scope. symbols . contains_key ( symbol_name) {
128
+ return Err ( SymbolTableError {
129
+ error : format ! ( "no binding for nonlocal '{}' found" , symbol_name) ,
130
+ location : Default :: default ( ) ,
131
+ } ) ;
132
+ }
133
+ } else {
134
+ return Err ( SymbolTableError {
135
+ error : format ! (
136
+ "nonlocal {} defined at place without an enclosing scope" ,
137
+ symbol_name
138
+ ) ,
139
+ location : Default :: default ( ) ,
140
+ } ) ;
141
+ }
142
+ }
143
+ // TODO: add more checks for globals
144
+ _ => { }
145
+ }
146
+ Ok ( ( ) )
147
+ }
148
+
72
149
pub struct SymbolTableBuilder {
73
150
// Scope stack.
74
151
pub scopes : Vec < SymbolScope > ,
@@ -85,7 +162,7 @@ impl SymbolTableBuilder {
85
162
self . scopes . push ( scope) ;
86
163
}
87
164
88
- pub fn leave_scope ( & mut self ) {
165
+ fn leave_scope ( & mut self ) {
89
166
// Pop scope and add to subscopes of parent scope.
90
167
let scope = self . scopes . pop ( ) . unwrap ( ) ;
91
168
self . scopes . last_mut ( ) . unwrap ( ) . sub_scopes . push ( scope) ;
@@ -445,17 +522,43 @@ impl SymbolTableBuilder {
445
522
}
446
523
447
524
fn register_name ( & mut self , name : & str , role : SymbolRole ) -> SymbolTableResult {
525
+ let scope_depth = self . scopes . len ( ) ;
448
526
let current_scope = self . scopes . last_mut ( ) . unwrap ( ) ;
527
+ let location = Default :: default ( ) ;
449
528
if let Some ( _old_role) = current_scope. symbols . get ( name) {
450
529
// Role already set..
451
530
// debug!("TODO: {:?}", old_role);
452
531
match role {
453
- SymbolRole :: Global => return Err ( "global must appear first" . to_string ( ) ) ,
532
+ SymbolRole :: Global => {
533
+ return Err ( SymbolTableError {
534
+ error : format ! ( "name '{}' is used prior to global declaration" , name) ,
535
+ location,
536
+ } )
537
+ }
538
+ SymbolRole :: Nonlocal => {
539
+ return Err ( SymbolTableError {
540
+ error : format ! ( "name '{}' is used prior to nonlocal declaration" , name) ,
541
+ location,
542
+ } )
543
+ }
454
544
_ => {
455
545
// Ok?
456
546
}
457
547
}
458
548
} else {
549
+ match role {
550
+ SymbolRole :: Nonlocal => {
551
+ if scope_depth < 2 {
552
+ return Err ( SymbolTableError {
553
+ error : format ! ( "cannot define nonlocal '{}' at top level." , name) ,
554
+ location,
555
+ } ) ;
556
+ }
557
+ }
558
+ _ => {
559
+ // Ok!
560
+ }
561
+ }
459
562
current_scope. symbols . insert ( name. to_string ( ) , role) ;
460
563
}
461
564
Ok ( ( ) )
0 commit comments