start on type checking statements

This commit is contained in:
gluax 2022-05-03 13:16:13 -07:00
parent 4fb95d1195
commit 1a25db8e23
5 changed files with 110 additions and 31 deletions

View File

@ -76,7 +76,6 @@ impl<'a, V: ExpressionVisitor<'a>> VisitorDirector<'a, V> {
pub fn visit_call(&mut self, input: &'a CallExpression) {
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
self.visit_expression(&input.function);
for expr in input.arguments.iter() {
self.visit_expression(expr);
}

View File

@ -16,7 +16,7 @@
use std::fmt::Display;
use leo_ast::Function;
use leo_ast::{DefinitionStatement, Function, FunctionInput};
use leo_errors::{AstError, Result};
use leo_span::Symbol;
@ -31,7 +31,7 @@ pub struct SymbolTable<'a> {
functions: IndexMap<Symbol, &'a Function>,
/// Variables represents functions variable definitions and input variables.
/// This field is not populated till necessary.
variables: VariableSymbol<'a>,
pub(crate) variables: VariableSymbol<'a>,
}
impl<'a> SymbolTable<'a> {
@ -48,15 +48,35 @@ impl<'a> SymbolTable<'a> {
self.variables.clear();
}
pub fn insert_fn(&mut self, symbol: Symbol, function: &'a Function) -> Result<()> {
pub fn insert_fn(&mut self, symbol: Symbol, insert: &'a Function) -> Result<()> {
self.check_shadowing(&symbol)?;
self.functions.insert(symbol, function);
self.functions.insert(symbol, insert);
Ok(())
}
pub fn lookup_fn(&mut self, symbol: &Symbol) -> Option<&&'a Function> {
pub fn insert_fn_input(&mut self, symbol: Symbol, insert: &'a FunctionInput) -> Result<()> {
self.check_shadowing(&symbol)?;
self.variables.inputs.insert(symbol, insert);
Ok(())
}
pub fn insert_variable(&mut self, symbol: Symbol, insert: &'a DefinitionStatement) -> Result<()> {
self.check_shadowing(&symbol)?;
self.variables.variables.insert(symbol, insert);
Ok(())
}
pub fn lookup_fn(&self, symbol: &Symbol) -> Option<&&'a Function> {
self.functions.get(symbol)
}
pub fn lookup_fn_input(&self, symbol: &Symbol) -> Option<&&'a FunctionInput> {
self.variables.inputs.get(symbol)
}
pub fn lookup_var(&self, symbol: &Symbol) -> Option<&&'a DefinitionStatement> {
self.variables.variables.get(symbol)
}
}
impl<'a> Display for SymbolTable<'a> {

View File

@ -17,7 +17,7 @@
use std::fmt::Display;
use indexmap::IndexMap;
use leo_ast::{DefinitionStatement, FunctionInput, FunctionInputVariable};
use leo_ast::{DefinitionStatement, FunctionInput, Node};
use leo_errors::{AstError, Result};
use leo_span::Symbol;
@ -27,33 +27,20 @@ pub struct VariableSymbol<'a> {
/// For example if we are in a if block inside a function.
/// The parent would be the functions variables and inputs.
/// This field is populated as necessary.
parent: Option<Box<VariableSymbol<'a>>>,
pub(crate) parent: Option<Box<VariableSymbol<'a>>>,
/// The input variables defined in a scope.
/// This field is populated as necessary.
inputs: IndexMap<Symbol, &'a FunctionInputVariable>,
pub(crate) inputs: IndexMap<Symbol, &'a FunctionInput>,
/// The variables defined in a scope.
/// This field is populated as necessary.
variables: IndexMap<Symbol, &'a DefinitionStatement>,
pub(crate) variables: IndexMap<Symbol, &'a DefinitionStatement>,
}
impl<'a> VariableSymbol<'a> {
pub fn new(parent: Option<Box<VariableSymbol<'a>>>, inputs: Vec<&'a FunctionInput>) -> Self {
Self {
parent,
inputs: inputs
.iter()
.map(|input| {
let inner = input.get_variable();
(inner.identifier.name, inner)
})
.collect(),
variables: IndexMap::new(),
}
}
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
if let Some(input) = self.inputs.get(symbol) {
Err(AstError::shadowed_function_input(symbol, &input.span).into())
let span = input.span();
Err(AstError::shadowed_function_input(symbol, span).into())
} else if let Some(var) = self.variables.get(symbol) {
Err(AstError::shadowed_variable(symbol, &var.span).into())
} else if let Some(parent) = &self.parent {

View File

@ -15,28 +15,101 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use leo_ast::*;
use leo_errors::Result;
use crate::TypeChecker;
impl<'a> TypeChecker<'a> {
fn compare_types(&self, t1: Result<Option<Type>>, t2: Result<Option<Type>>) {
match (t1, t2) {
(Ok(Some(t1)), Ok(Some(t2))) if t1 != t2 => self.handler.emit_err(todo!()),
(Ok(Some(t)), Ok(None)) | (Ok(None), Ok(Some(t))) => self.handler.emit_err(todo!()),
(Err(err), Ok(None)) | (Ok(None), Err(err)) => self.handler.emit_err(err),
(Err(err1), Err(err2)) => {
self.handler.emit_err(err1);
self.handler.emit_err(err2);
}
_ => {}
}
}
}
impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {}
// we can safely unwrap all self.parent instances because
// statements should always have some parent block
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_return(&mut self, input: &'a ReturnStatement) -> VisitResult {
let parent = self.parent.unwrap();
if let Some(func) = self.symbol_table.lookup_fn(&parent) {
// if func.get_type()? != input.get_type()? {
// // self.handler.emit_err(err);
// }
self.compare_types(func.get_type(), input.get_type());
}
VisitResult::VisitChildren
}
fn visit_definition(&mut self, input: &'a DefinitionStatement) -> VisitResult {
input.variable_names.iter().for_each(|v| {
if let Err(err) = self.symbol_table.insert_variable(v.identifier.name, input) {
self.handler.emit_err(err);
}
});
VisitResult::VisitChildren
}
fn visit_assign(&mut self, input: &'a AssignStatement) -> VisitResult {
let input_var = self.symbol_table.lookup_fn_input(&input.assignee.identifier.name);
let var = self.symbol_table.lookup_var(&input.assignee.identifier.name);
match (input_var, var) {
(Some(var), None) => {
self.compare_types(var.get_type(), input.value.get_type());
}
(None, Some(var)) => {
self.compare_types(var.get_type(), input.value.get_type());
}
(None, None) => self.handler.emit_err(todo!()),
// Don't have to error here as shadowing checks are done during insertions.
_ => {}
}
Default::default()
}
fn visit_conditional(&mut self, _input: &'a ConditionalStatement) -> VisitResult {
Default::default()
}
fn visit_iteration(&mut self, _input: &'a IterationStatement) -> VisitResult {
Default::default()
}
fn visit_console(&mut self, _input: &'a ConsoleStatement) -> VisitResult {
Default::default()
}
fn visit_expression_statement(&mut self, _input: &'a ExpressionStatement) -> VisitResult {
Default::default()
}
fn visit_block(&mut self, _input: &'a Block) -> VisitResult {
Default::default()
}
}
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
self.symbol_table.clear_variables();
self.parent = Some(input.name());
input.input.iter().for_each(|i| {
let symbol = i.get_variable().identifier.name;
if let Err(err) = self.symbol_table.insert_fn_input(symbol, i) {
self.handler.emit_err(err);
}
});
VisitResult::VisitChildren
}
}

View File

@ -291,9 +291,9 @@ create_messages!(
/// When the user tries to pass an implicit value.
@formatted
implicit_values_not_allowed {
args: (input: impl Display),
msg: format!("Could not parse the implicit value: {}.", input),
help: None,
args: (input: impl Display),
msg: format!("Could not parse the implicit value: {}.", input),
help: None,
}
/// When a escaped unicode char was given but no following closing symbol.