Merge pull request #1325 from RustPython/scoping

Add scope type and other symboltable properties.
This commit is contained in:
Windel Bouwman 2019-08-30 07:08:49 +02:00 committed by GitHub
commit ca09cc215d

View file

@ -11,6 +11,7 @@ use crate::error::{CompileError, CompileErrorType};
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
use rustpython_parser::ast; use rustpython_parser::ast;
use rustpython_parser::location::Location; use rustpython_parser::location::Location;
use std::fmt;
pub fn make_symbol_table(program: &ast::Program) -> Result<SymbolTable, SymbolTableError> { pub fn make_symbol_table(program: &ast::Program) -> Result<SymbolTable, SymbolTableError> {
let mut builder: SymbolTableBuilder = Default::default(); let mut builder: SymbolTableBuilder = Default::default();
@ -29,11 +30,17 @@ pub fn statements_to_symbol_table(
} }
/// Captures all symbols in the current scope, and has a list of subscopes in this scope. /// Captures all symbols in the current scope, and has a list of subscopes in this scope.
#[derive(Clone, Default)] #[derive(Clone)]
pub struct SymbolTable { pub struct SymbolTable {
/// The name of this symbol table. Often the name of the class or function. /// The name of this symbol table. Often the name of the class or function.
pub name: String, pub name: String,
/// The type of symbol table
pub typ: SymbolTableType,
/// The line number in the sourcecode where this symboltable begins.
pub line_number: usize,
/// A set of symbols present on this scope level. /// A set of symbols present on this scope level.
pub symbols: IndexMap<String, Symbol>, pub symbols: IndexMap<String, Symbol>,
@ -43,15 +50,34 @@ pub struct SymbolTable {
} }
impl SymbolTable { impl SymbolTable {
fn new(name: String) -> Self { fn new(name: String, typ: SymbolTableType, line_number: usize) -> Self {
SymbolTable { SymbolTable {
name, name,
typ,
line_number,
symbols: Default::default(), symbols: Default::default(),
sub_tables: vec![], sub_tables: vec![],
} }
} }
} }
#[derive(Clone)]
pub enum SymbolTableType {
Module,
Class,
Function,
}
impl fmt::Display for SymbolTableType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SymbolTableType::Module => write!(f, "module"),
SymbolTableType::Class => write!(f, "class"),
SymbolTableType::Function => write!(f, "function"),
}
}
}
/// Indicator for a single symbol what the scope of this symbol is. /// Indicator for a single symbol what the scope of this symbol is.
/// The scope can be unknown, which is unfortunate, but not impossible. /// The scope can be unknown, which is unfortunate, but not impossible.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -263,7 +289,7 @@ enum ExpressionContext {
impl SymbolTableBuilder { impl SymbolTableBuilder {
fn prepare(&mut self) { fn prepare(&mut self) {
self.enter_block("top") self.enter_scope("top", SymbolTableType::Module, 0)
} }
fn finish(&mut self) -> Result<SymbolTable, SymbolTableError> { fn finish(&mut self) -> Result<SymbolTable, SymbolTableError> {
@ -273,14 +299,13 @@ impl SymbolTableBuilder {
Ok(symbol_table) Ok(symbol_table)
} }
fn enter_block(&mut self, name: &str) { fn enter_scope(&mut self, name: &str, typ: SymbolTableType, line_number: usize) {
// let parent = Some(self.tables.last().unwrap().clone()); let table = SymbolTable::new(name.to_string(), typ, line_number);
let table = SymbolTable::new(name.to_string());
self.tables.push(table); self.tables.push(table);
} }
fn leave_block(&mut self) { /// Pop symbol table and add to sub table of parent table.
// Pop symbol table and add to sub table of parent table. fn leave_scope(&mut self) {
let table = self.tables.pop().unwrap(); let table = self.tables.pop().unwrap();
self.tables.last_mut().unwrap().sub_tables.push(table); self.tables.last_mut().unwrap().sub_tables.push(table);
} }
@ -348,9 +373,9 @@ impl SymbolTableBuilder {
if let Some(expression) = returns { if let Some(expression) = returns {
self.scan_expression(expression, &ExpressionContext::Load)?; self.scan_expression(expression, &ExpressionContext::Load)?;
} }
self.enter_function(name, args)?; self.enter_function(name, args, statement.location.row())?;
self.scan_statements(body)?; self.scan_statements(body)?;
self.leave_block(); self.leave_scope();
} }
ClassDef { ClassDef {
name, name,
@ -360,9 +385,9 @@ impl SymbolTableBuilder {
decorator_list, decorator_list,
} => { } => {
self.register_name(name, SymbolUsage::Assigned)?; self.register_name(name, SymbolUsage::Assigned)?;
self.enter_block(name); self.enter_scope(name, SymbolTableType::Class, statement.location.row());
self.scan_statements(body)?; self.scan_statements(body)?;
self.leave_block(); self.leave_scope();
self.scan_expressions(bases, &ExpressionContext::Load)?; self.scan_expressions(bases, &ExpressionContext::Load)?;
for keyword in keywords { for keyword in keywords {
self.scan_expression(&keyword.value, &ExpressionContext::Load)?; self.scan_expression(&keyword.value, &ExpressionContext::Load)?;
@ -612,9 +637,9 @@ impl SymbolTableBuilder {
} }
} }
Lambda { args, body } => { Lambda { args, body } => {
self.enter_function("lambda", args)?; self.enter_function("lambda", args, expression.location.row())?;
self.scan_expression(body, &ExpressionContext::Load)?; self.scan_expression(body, &ExpressionContext::Load)?;
self.leave_block(); self.leave_scope();
} }
IfExpression { test, body, orelse } => { IfExpression { test, body, orelse } => {
self.scan_expression(test, &ExpressionContext::Load)?; self.scan_expression(test, &ExpressionContext::Load)?;
@ -625,7 +650,12 @@ impl SymbolTableBuilder {
Ok(()) Ok(())
} }
fn enter_function(&mut self, name: &str, args: &ast::Parameters) -> SymbolTableResult { fn enter_function(
&mut self,
name: &str,
args: &ast::Parameters,
line_number: usize,
) -> SymbolTableResult {
// Evaluate eventual default parameters: // Evaluate eventual default parameters:
self.scan_expressions(&args.defaults, &ExpressionContext::Load)?; self.scan_expressions(&args.defaults, &ExpressionContext::Load)?;
for kw_default in &args.kw_defaults { for kw_default in &args.kw_defaults {
@ -644,7 +674,7 @@ impl SymbolTableBuilder {
self.scan_parameter_annotation(name)?; self.scan_parameter_annotation(name)?;
} }
self.enter_block(name); self.enter_scope(name, SymbolTableType::Function, line_number);
// Fill scope with parameter names: // Fill scope with parameter names:
self.scan_parameters(&args.args)?; self.scan_parameters(&args.args)?;