forked from Rust-related/RustPython
Merge pull request #1325 from RustPython/scoping
Add scope type and other symboltable properties.
This commit is contained in:
@@ -11,6 +11,7 @@ use crate::error::{CompileError, CompileErrorType};
|
||||
use indexmap::map::IndexMap;
|
||||
use rustpython_parser::ast;
|
||||
use rustpython_parser::location::Location;
|
||||
use std::fmt;
|
||||
|
||||
pub fn make_symbol_table(program: &ast::Program) -> Result<SymbolTable, SymbolTableError> {
|
||||
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.
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone)]
|
||||
pub struct SymbolTable {
|
||||
/// The name of this symbol table. Often the name of the class or function.
|
||||
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.
|
||||
pub symbols: IndexMap<String, Symbol>,
|
||||
|
||||
@@ -43,15 +50,34 @@ pub struct SymbolTable {
|
||||
}
|
||||
|
||||
impl SymbolTable {
|
||||
fn new(name: String) -> Self {
|
||||
fn new(name: String, typ: SymbolTableType, line_number: usize) -> Self {
|
||||
SymbolTable {
|
||||
name,
|
||||
typ,
|
||||
line_number,
|
||||
symbols: Default::default(),
|
||||
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.
|
||||
/// The scope can be unknown, which is unfortunate, but not impossible.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -263,7 +289,7 @@ enum ExpressionContext {
|
||||
|
||||
impl SymbolTableBuilder {
|
||||
fn prepare(&mut self) {
|
||||
self.enter_block("top")
|
||||
self.enter_scope("top", SymbolTableType::Module, 0)
|
||||
}
|
||||
|
||||
fn finish(&mut self) -> Result<SymbolTable, SymbolTableError> {
|
||||
@@ -273,14 +299,13 @@ impl SymbolTableBuilder {
|
||||
Ok(symbol_table)
|
||||
}
|
||||
|
||||
fn enter_block(&mut self, name: &str) {
|
||||
// let parent = Some(self.tables.last().unwrap().clone());
|
||||
let table = SymbolTable::new(name.to_string());
|
||||
fn enter_scope(&mut self, name: &str, typ: SymbolTableType, line_number: usize) {
|
||||
let table = SymbolTable::new(name.to_string(), typ, line_number);
|
||||
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();
|
||||
self.tables.last_mut().unwrap().sub_tables.push(table);
|
||||
}
|
||||
@@ -348,9 +373,9 @@ impl SymbolTableBuilder {
|
||||
if let Some(expression) = returns {
|
||||
self.scan_expression(expression, &ExpressionContext::Load)?;
|
||||
}
|
||||
self.enter_function(name, args)?;
|
||||
self.enter_function(name, args, statement.location.row())?;
|
||||
self.scan_statements(body)?;
|
||||
self.leave_block();
|
||||
self.leave_scope();
|
||||
}
|
||||
ClassDef {
|
||||
name,
|
||||
@@ -360,9 +385,9 @@ impl SymbolTableBuilder {
|
||||
decorator_list,
|
||||
} => {
|
||||
self.register_name(name, SymbolUsage::Assigned)?;
|
||||
self.enter_block(name);
|
||||
self.enter_scope(name, SymbolTableType::Class, statement.location.row());
|
||||
self.scan_statements(body)?;
|
||||
self.leave_block();
|
||||
self.leave_scope();
|
||||
self.scan_expressions(bases, &ExpressionContext::Load)?;
|
||||
for keyword in keywords {
|
||||
self.scan_expression(&keyword.value, &ExpressionContext::Load)?;
|
||||
@@ -612,9 +637,9 @@ impl SymbolTableBuilder {
|
||||
}
|
||||
}
|
||||
Lambda { args, body } => {
|
||||
self.enter_function("lambda", args)?;
|
||||
self.enter_function("lambda", args, expression.location.row())?;
|
||||
self.scan_expression(body, &ExpressionContext::Load)?;
|
||||
self.leave_block();
|
||||
self.leave_scope();
|
||||
}
|
||||
IfExpression { test, body, orelse } => {
|
||||
self.scan_expression(test, &ExpressionContext::Load)?;
|
||||
@@ -625,7 +650,12 @@ impl SymbolTableBuilder {
|
||||
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:
|
||||
self.scan_expressions(&args.defaults, &ExpressionContext::Load)?;
|
||||
for kw_default in &args.kw_defaults {
|
||||
@@ -644,7 +674,7 @@ impl SymbolTableBuilder {
|
||||
self.scan_parameter_annotation(name)?;
|
||||
}
|
||||
|
||||
self.enter_block(name);
|
||||
self.enter_scope(name, SymbolTableType::Function, line_number);
|
||||
|
||||
// Fill scope with parameter names:
|
||||
self.scan_parameters(&args.args)?;
|
||||
|
||||
@@ -26,11 +26,12 @@ print(t)
|
||||
|
||||
shift = 3
|
||||
def print_node(node, indent=0):
|
||||
indents = ' ' * indent
|
||||
if isinstance(node, ast.AST):
|
||||
lineno = 'row={}'.format(node.lineno) if hasattr(node, 'lineno') else ''
|
||||
print(' '*indent, "NODE", node.__class__.__name__, lineno)
|
||||
print(indents, "NODE", node.__class__.__name__, lineno)
|
||||
for field in node._fields:
|
||||
print(' '*indent,'-', field)
|
||||
print(indents,'-', field)
|
||||
f = getattr(node, field)
|
||||
if isinstance(f, list):
|
||||
for f2 in f:
|
||||
@@ -38,7 +39,7 @@ def print_node(node, indent=0):
|
||||
else:
|
||||
print_node(f, indent=indent+shift)
|
||||
else:
|
||||
print(' '*indent, 'OBJ', node)
|
||||
print(indents, 'OBJ', node)
|
||||
|
||||
print_node(t)
|
||||
|
||||
@@ -53,18 +54,24 @@ flag_names = [
|
||||
]
|
||||
|
||||
def print_table(table, indent=0):
|
||||
print(' '*indent, 'table:', table.get_name())
|
||||
print(' '*indent, ' ', 'Syms:')
|
||||
indents = ' ' * indent
|
||||
print(indents, 'table:', table.get_name())
|
||||
print(indents, ' ', 'name:', table.get_name())
|
||||
print(indents, ' ', 'type:', table.get_type())
|
||||
print(indents, ' ', 'line:', table.get_lineno())
|
||||
print(indents, ' ', 'identifiers:', table.get_identifiers())
|
||||
print(indents, ' ', 'Syms:')
|
||||
for sym in table.get_symbols():
|
||||
flags = []
|
||||
for flag_name in flag_names:
|
||||
func = getattr(sym, flag_name)
|
||||
if func():
|
||||
flags.append(flag_name)
|
||||
print(' '*indent, ' sym:', sym.get_name(), 'flags:', ' '.join(flags))
|
||||
print(' '*indent, ' ', 'Child tables:')
|
||||
for child in table.get_children():
|
||||
print_table(child, indent=indent+shift)
|
||||
print(indents, ' sym:', sym.get_name(), 'flags:', ' '.join(flags))
|
||||
if table.has_children():
|
||||
print(indents, ' ', 'Child tables:')
|
||||
for child in table.get_children():
|
||||
print_table(child, indent=indent+shift)
|
||||
|
||||
table = symtable.symtable(source, 'a', 'exec')
|
||||
print_table(table)
|
||||
|
||||
@@ -430,10 +430,10 @@ impl SocketRef {
|
||||
self.timeout.borrow_mut().replace(Duration::from_secs(0));
|
||||
}
|
||||
if let Some(conn) = self.con.borrow_mut().as_mut() {
|
||||
return match conn.setblocking(value) {
|
||||
match conn.setblocking(value) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(vm.new_os_error(err.to_string())),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -83,8 +83,18 @@ impl PyValue for PySymbolTable {
|
||||
#[pyimpl]
|
||||
impl PySymbolTable {
|
||||
#[pymethod(name = "get_name")]
|
||||
fn get_name(&self, vm: &VirtualMachine) -> PyResult {
|
||||
Ok(vm.ctx.new_str(self.symtable.name.clone()))
|
||||
fn get_name(&self, _vm: &VirtualMachine) -> String {
|
||||
self.symtable.name.clone()
|
||||
}
|
||||
|
||||
#[pymethod(name = "get_type")]
|
||||
fn get_type(&self, _vm: &VirtualMachine) -> String {
|
||||
self.symtable.typ.to_string()
|
||||
}
|
||||
|
||||
#[pymethod(name = "get_lineno")]
|
||||
fn get_lineno(&self, _vm: &VirtualMachine) -> usize {
|
||||
self.symtable.line_number
|
||||
}
|
||||
|
||||
#[pymethod(name = "lookup")]
|
||||
@@ -100,6 +110,17 @@ impl PySymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "get_identifiers")]
|
||||
fn get_identifiers(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let symbols = self
|
||||
.symtable
|
||||
.symbols
|
||||
.keys()
|
||||
.map(|s| vm.ctx.new_str(s.to_string()))
|
||||
.collect();
|
||||
Ok(vm.ctx.new_list(symbols))
|
||||
}
|
||||
|
||||
#[pymethod(name = "get_symbols")]
|
||||
fn get_symbols(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let symbols = self
|
||||
@@ -111,6 +132,11 @@ impl PySymbolTable {
|
||||
Ok(vm.ctx.new_list(symbols))
|
||||
}
|
||||
|
||||
#[pymethod(name = "has_children")]
|
||||
fn has_children(&self, _vm: &VirtualMachine) -> bool {
|
||||
!self.symtable.sub_tables.is_empty()
|
||||
}
|
||||
|
||||
#[pymethod(name = "get_children")]
|
||||
fn get_children(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let children = self
|
||||
|
||||
Reference in New Issue
Block a user