arrow-parser 0.0.2

Parser for the Arrow programming language
Documentation
use crate::ast::Expression;
use crate::ast::LiteralTemplatePart;
use crate::ast::Module;
use crate::ast::ModuleItem;
use crate::ast::Node;
use crate::ast::Statement;
use crate::symbol::Scope;
use crate::symbol::ScopeType;

// - TODO Detect usages of variables physically before their usage, including in nested closures. This is mostly a lint, and doesn't actually affect program correctness.
// - We can't run this analysis during parsing due to forward references, and we can't run this analysis during compilation due to needing a full pass to detect usages in nested closures before we compile their ancestor code.

pub struct LifetimeAnalyser {}

impl LifetimeAnalyser {
  fn process_variable(&mut self, scope: &Scope, name: &str) {
    if let Some((decl_scope, symbol)) = scope.find_symbol_declaration(name) {
      // Name refers to non-host variable.
      if let Some(decl_closure) = decl_scope.find_nearest_closure() {
        // The variable is inside a closure (might be in a nested block, but isn't a module variable).
        if let Some(usage_closure) = scope.find_nearest_closure() {
          // We are currently in a closure (might be in a nested block).
          if decl_closure != usage_closure {
            // We are using a variable in a different closure.
            decl_scope.borrow_mut().boxed_var_decls.insert(symbol);
            let mut cur = usage_closure;
            while cur != decl_scope {
              if cur.typ == ScopeType::Closure {
                cur.borrow_mut().boxed_var_captures.insert(symbol);
              };
              cur = cur.parent().unwrap();
            }
          };
        };
      };
    };
  }

  fn analyse_expression(&mut self, expr: &Node<Expression>) {
    match expr.stx.as_ref() {
      Expression::Binary { left, right, .. } => {
        self.analyse_expression(left);
        self.analyse_expression(right);
      }
      Expression::BindMethod { arguments, .. } => {
        for arg in arguments {
          self.analyse_expression(arg);
        }
      }
      Expression::Block { statements, result } => {
        for stmt in statements {
          self.analyse_statement(stmt);
        }
        if let Some(result) = result {
          self.analyse_expression(result);
        };
      }
      Expression::CallMethod {
        object, arguments, ..
      } => {
        self.analyse_expression(object);
        for arg in arguments {
          self.analyse_expression(arg);
        }
      }
      Expression::CallValue {
        callee, arguments, ..
      } => {
        self.analyse_expression(callee);
        for arg in arguments {
          self.analyse_expression(arg);
        }
      }
      Expression::Cast { value, .. } => {
        self.analyse_expression(value);
      }
      Expression::Field { object, .. } => {
        self.analyse_expression(object);
      }
      Expression::Index { object, index, .. } => {
        self.analyse_expression(object);
        self.analyse_expression(index);
      }
      Expression::If {
        condition,
        consequent,
        alternate,
      } => {
        self.analyse_expression(condition);
        self.analyse_expression(consequent);
        if let Some(alternate) = alternate {
          self.analyse_expression(alternate);
        };
      }
      Expression::Closure { body, .. } => {
        self.analyse_expression(body);
      }
      Expression::LiteralArray { entries } => {
        for ent in entries {
          self.analyse_expression(ent);
        }
      }
      Expression::LiteralBoolean { .. } => {}
      Expression::LiteralFloat { .. } => {}
      Expression::LiteralInt { .. } => {}
      Expression::LiteralNone { .. } => {}
      Expression::LiteralObject { fields } => {
        for (_, value) in fields {
          self.analyse_expression(value);
        }
      }
      Expression::LiteralTemplateExpr { parts } => {
        for part in parts {
          match part {
            LiteralTemplatePart::Substitution(v) => {
              self.analyse_expression(v);
            }
            LiteralTemplatePart::String(_) => {}
          };
        }
      }
      Expression::Unary { operand, .. } => {
        self.analyse_expression(operand);
      }
      Expression::Var { name } => {
        self.process_variable(&expr.scope, name);
      }
    }
  }

  fn analyse_statement(&mut self, stmt: &Node<Statement>) {
    match stmt.stx.as_ref() {
      Statement::Break => {}
      Statement::Continue => {}
      Statement::Expression { expression } => {
        self.analyse_expression(expression);
      }
      Statement::FieldAssign { object, value, .. } => {
        self.analyse_expression(object);
        self.analyse_expression(value);
      }
      Statement::For { iterable, body, .. } => {
        self.analyse_expression(iterable);
        self.analyse_expression(body);
      }
      Statement::IndexAssign {
        object,
        index,
        value,
        ..
      } => {
        self.analyse_expression(object);
        self.analyse_expression(index);
        self.analyse_expression(value);
      }
      Statement::Let { value, .. } => {
        self.analyse_expression(value);
      }
      Statement::Loop { body } => {
        self.analyse_expression(body);
      }
      Statement::Return { value } => {
        if let Some(value) = value {
          self.analyse_expression(value);
        };
      }
      Statement::VarAssign { variable, value } => {
        self.analyse_expression(value);
        self.process_variable(&stmt.scope, &variable);
      }
    };
  }

  fn analyse_module_item(&mut self, item: &Node<ModuleItem>) {
    match item.stx.as_ref() {
      ModuleItem::Impl { methods, .. } => {
        for (_, method) in methods {
          self.analyse_expression(method);
        }
      }
      ModuleItem::Statement { statement } => {
        self.analyse_statement(statement);
      }
    };
  }

  pub fn analyse_lifetimes(&mut self, module: &Module) {
    for item in module.items.iter() {
      self.analyse_module_item(item);
    }
  }
}