added location information for diagnostics
This commit is contained in:
parent
ed04cef431
commit
e72d96f165
@ -1,6 +1,8 @@
|
|||||||
|
use super::super::location::{FileID, Location};
|
||||||
use super::super::symbol_resolver::*;
|
use super::super::symbol_resolver::*;
|
||||||
use super::super::typedef::*;
|
use super::super::typedef::*;
|
||||||
use super::GlobalContext;
|
use super::GlobalContext;
|
||||||
|
use rustpython_parser::ast;
|
||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -17,34 +19,28 @@ pub struct InferenceContext<'a> {
|
|||||||
global: GlobalContext<'a>,
|
global: GlobalContext<'a>,
|
||||||
/// per source symbol resolver
|
/// per source symbol resolver
|
||||||
resolver: Box<dyn SymbolResolver>,
|
resolver: Box<dyn SymbolResolver>,
|
||||||
|
/// File ID
|
||||||
|
file: FileID,
|
||||||
|
|
||||||
/// list of primitive instances
|
|
||||||
primitives: Vec<Type>,
|
|
||||||
/// list of variable instances
|
|
||||||
variables: Vec<Type>,
|
|
||||||
/// identifier to (type, readable) mapping.
|
/// identifier to (type, readable) mapping.
|
||||||
/// an identifier might be defined earlier but has no value (for some code path), thus not
|
/// an identifier might be defined earlier but has no value (for some code path), thus not
|
||||||
/// readable.
|
/// readable.
|
||||||
sym_table: HashMap<&'a str, (Type, bool)>,
|
sym_table: HashMap<&'a str, (Type, bool, Location)>,
|
||||||
/// stack
|
/// stack
|
||||||
stack: ContextStack<'a>,
|
stack: ContextStack<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-trivial implementations here
|
// non-trivial implementations here
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
/// return a new `InferenceContext` from `TopLevelContext` and resolution function.
|
pub fn new(
|
||||||
pub fn new(global: GlobalContext, resolver: Box<dyn SymbolResolver>) -> InferenceContext {
|
global: GlobalContext,
|
||||||
let primitives = (0..global.primitive_defs.len())
|
resolver: Box<dyn SymbolResolver>,
|
||||||
.map(|v| TypeEnum::PrimitiveType(PrimitiveId(v)).into())
|
file: FileID,
|
||||||
.collect();
|
) -> InferenceContext {
|
||||||
let variables = (0..global.var_defs.len())
|
|
||||||
.map(|v| TypeEnum::TypeVariable(VariableId(v)).into())
|
|
||||||
.collect();
|
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
global,
|
global,
|
||||||
resolver,
|
resolver,
|
||||||
primitives,
|
file,
|
||||||
variables,
|
|
||||||
sym_table: HashMap::new(),
|
sym_table: HashMap::new(),
|
||||||
stack: ContextStack {
|
stack: ContextStack {
|
||||||
level: 0,
|
level: 0,
|
||||||
@ -56,7 +52,7 @@ impl<'a> InferenceContext<'a> {
|
|||||||
/// execute the function with new scope.
|
/// execute the function with new scope.
|
||||||
/// variable assignment would be limited within the scope (not readable outside), and type
|
/// variable assignment would be limited within the scope (not readable outside), and type
|
||||||
/// returns the list of variables assigned within the scope, and the result of the function
|
/// returns the list of variables assigned within the scope, and the result of the function
|
||||||
pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<&'a str>, R)
|
pub fn with_scope<F, R>(&mut self, f: F) -> (Vec<(&'a str, Type, Location)>, R)
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> R,
|
F: FnOnce(&mut Self) -> R,
|
||||||
{
|
{
|
||||||
@ -68,8 +64,8 @@ impl<'a> InferenceContext<'a> {
|
|||||||
let (_, level) = self.stack.sym_def.last().unwrap();
|
let (_, level) = self.stack.sym_def.last().unwrap();
|
||||||
if *level > self.stack.level {
|
if *level > self.stack.level {
|
||||||
let (name, _) = self.stack.sym_def.pop().unwrap();
|
let (name, _) = self.stack.sym_def.pop().unwrap();
|
||||||
self.sym_table.remove(name).unwrap();
|
let (t, _, l) = self.sym_table.remove(name).unwrap();
|
||||||
poped_names.push(name);
|
poped_names.push((name, t, l));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -79,8 +75,8 @@ impl<'a> InferenceContext<'a> {
|
|||||||
|
|
||||||
/// assign a type to an identifier.
|
/// assign a type to an identifier.
|
||||||
/// may return error if the identifier was defined but with different type
|
/// may return error if the identifier was defined but with different type
|
||||||
pub fn assign(&mut self, name: &'a str, ty: Type) -> Result<Type, String> {
|
pub fn assign(&mut self, name: &'a str, ty: Type, loc: ast::Location) -> Result<Type, String> {
|
||||||
if let Some((t, x)) = self.sym_table.get_mut(name) {
|
if let Some((t, x, _)) = self.sym_table.get_mut(name) {
|
||||||
if t == &ty {
|
if t == &ty {
|
||||||
if !*x {
|
if !*x {
|
||||||
self.stack.sym_def.push((name, self.stack.level));
|
self.stack.sym_def.push((name, self.stack.level));
|
||||||
@ -92,21 +88,19 @@ impl<'a> InferenceContext<'a> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.stack.sym_def.push((name, self.stack.level));
|
self.stack.sym_def.push((name, self.stack.level));
|
||||||
self.sym_table.insert(name, (ty.clone(), true));
|
self.sym_table.insert(
|
||||||
|
name,
|
||||||
|
(ty.clone(), true, Location::CodeRange(self.file, loc)),
|
||||||
|
);
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check if an identifier is already defined
|
|
||||||
pub fn defined(&self, name: &str) -> bool {
|
|
||||||
self.sym_table.get(name).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the type of an identifier
|
/// get the type of an identifier
|
||||||
/// may return error if the identifier is not defined, and cannot be resolved with the
|
/// may return error if the identifier is not defined, and cannot be resolved with the
|
||||||
/// resolution function.
|
/// resolution function.
|
||||||
pub fn resolve(&mut self, name: &str) -> Result<Type, String> {
|
pub fn resolve(&self, name: &str) -> Result<Type, String> {
|
||||||
if let Some((t, x)) = self.sym_table.get(name) {
|
if let Some((t, x, _)) = self.sym_table.get(name) {
|
||||||
if *x {
|
if *x {
|
||||||
Ok(t.clone())
|
Ok(t.clone())
|
||||||
} else {
|
} else {
|
||||||
@ -120,15 +114,24 @@ impl<'a> InferenceContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_location(&self, name: &str) -> Option<Location> {
|
||||||
|
if let Some((_, _, l)) = self.sym_table.get(name) {
|
||||||
|
Some(*l)
|
||||||
|
} else {
|
||||||
|
self.resolver.get_symbol_location(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trivial getters:
|
// trivial getters:
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
pub fn get_primitive(&self, id: PrimitiveId) -> Type {
|
pub fn get_primitive(&self, id: PrimitiveId) -> Type {
|
||||||
self.primitives.get(id.0).unwrap().clone()
|
TypeEnum::PrimitiveType(id).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_variable(&self, id: VariableId) -> Type {
|
pub fn get_variable(&self, id: VariableId) -> Type {
|
||||||
self.variables.get(id.0).unwrap().clone()
|
TypeEnum::TypeVariable(id).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_fn_def(&self, name: &str) -> Option<&FnDef> {
|
pub fn get_fn_def(&self, name: &str) -> Option<&FnDef> {
|
||||||
|
Loading…
Reference in New Issue
Block a user