mirror of
https://github.com/AleoHQ/leo.git
synced 2024-12-18 23:02:35 +03:00
start on type checking statements
This commit is contained in:
parent
4fb95d1195
commit
1a25db8e23
@ -76,7 +76,6 @@ impl<'a, V: ExpressionVisitor<'a>> VisitorDirector<'a, V> {
|
|||||||
|
|
||||||
pub fn visit_call(&mut self, input: &'a CallExpression) {
|
pub fn visit_call(&mut self, input: &'a CallExpression) {
|
||||||
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
||||||
self.visit_expression(&input.function);
|
|
||||||
for expr in input.arguments.iter() {
|
for expr in input.arguments.iter() {
|
||||||
self.visit_expression(expr);
|
self.visit_expression(expr);
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use leo_ast::Function;
|
use leo_ast::{DefinitionStatement, Function, FunctionInput};
|
||||||
use leo_errors::{AstError, Result};
|
use leo_errors::{AstError, Result};
|
||||||
use leo_span::Symbol;
|
use leo_span::Symbol;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ pub struct SymbolTable<'a> {
|
|||||||
functions: IndexMap<Symbol, &'a Function>,
|
functions: IndexMap<Symbol, &'a Function>,
|
||||||
/// Variables represents functions variable definitions and input variables.
|
/// Variables represents functions variable definitions and input variables.
|
||||||
/// This field is not populated till necessary.
|
/// This field is not populated till necessary.
|
||||||
variables: VariableSymbol<'a>,
|
pub(crate) variables: VariableSymbol<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SymbolTable<'a> {
|
impl<'a> SymbolTable<'a> {
|
||||||
@ -48,15 +48,35 @@ impl<'a> SymbolTable<'a> {
|
|||||||
self.variables.clear();
|
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.check_shadowing(&symbol)?;
|
||||||
self.functions.insert(symbol, function);
|
self.functions.insert(symbol, insert);
|
||||||
Ok(())
|
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)
|
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> {
|
impl<'a> Display for SymbolTable<'a> {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use leo_ast::{DefinitionStatement, FunctionInput, FunctionInputVariable};
|
use leo_ast::{DefinitionStatement, FunctionInput, Node};
|
||||||
use leo_errors::{AstError, Result};
|
use leo_errors::{AstError, Result};
|
||||||
use leo_span::Symbol;
|
use leo_span::Symbol;
|
||||||
|
|
||||||
@ -27,33 +27,20 @@ pub struct VariableSymbol<'a> {
|
|||||||
/// For example if we are in a if block inside a function.
|
/// For example if we are in a if block inside a function.
|
||||||
/// The parent would be the functions variables and inputs.
|
/// The parent would be the functions variables and inputs.
|
||||||
/// This field is populated as necessary.
|
/// 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.
|
/// The input variables defined in a scope.
|
||||||
/// This field is populated as necessary.
|
/// This field is populated as necessary.
|
||||||
inputs: IndexMap<Symbol, &'a FunctionInputVariable>,
|
pub(crate) inputs: IndexMap<Symbol, &'a FunctionInput>,
|
||||||
/// The variables defined in a scope.
|
/// The variables defined in a scope.
|
||||||
/// This field is populated as necessary.
|
/// This field is populated as necessary.
|
||||||
variables: IndexMap<Symbol, &'a DefinitionStatement>,
|
pub(crate) variables: IndexMap<Symbol, &'a DefinitionStatement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VariableSymbol<'a> {
|
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<()> {
|
pub fn check_shadowing(&self, symbol: &Symbol) -> Result<()> {
|
||||||
if let Some(input) = self.inputs.get(symbol) {
|
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) {
|
} else if let Some(var) = self.variables.get(symbol) {
|
||||||
Err(AstError::shadowed_variable(symbol, &var.span).into())
|
Err(AstError::shadowed_variable(symbol, &var.span).into())
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
|
@ -15,28 +15,101 @@
|
|||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use leo_ast::*;
|
use leo_ast::*;
|
||||||
|
use leo_errors::Result;
|
||||||
|
|
||||||
use crate::TypeChecker;
|
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> {}
|
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> {
|
impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
|
||||||
fn visit_return(&mut self, input: &'a ReturnStatement) -> VisitResult {
|
fn visit_return(&mut self, input: &'a ReturnStatement) -> VisitResult {
|
||||||
let parent = self.parent.unwrap();
|
let parent = self.parent.unwrap();
|
||||||
|
|
||||||
if let Some(func) = self.symbol_table.lookup_fn(&parent) {
|
if let Some(func) = self.symbol_table.lookup_fn(&parent) {
|
||||||
// if func.get_type()? != input.get_type()? {
|
self.compare_types(func.get_type(), input.get_type());
|
||||||
// // self.handler.emit_err(err);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VisitResult::VisitChildren
|
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> {
|
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
|
||||||
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
|
fn visit_function(&mut self, input: &'a Function) -> VisitResult {
|
||||||
self.symbol_table.clear_variables();
|
self.symbol_table.clear_variables();
|
||||||
self.parent = Some(input.name());
|
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
|
VisitResult::VisitChildren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user