mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-26 11:45:00 +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) {
|
||||
if let VisitResult::VisitChildren = self.visitor.visit_call(input) {
|
||||
self.visit_expression(&input.function);
|
||||
for expr in input.arguments.iter() {
|
||||
self.visit_expression(expr);
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user