zoisite 0.1.0

Zoisite is a programming language designed specifically for competitive programming.
Documentation
use std::cell::RefCell;
use std::iter;

use crate::hir::Identifier;
use crate::r#type::{FuncType, Type};
use crate::scope::{FnId, Scope, VarId};
use ecow::EcoString;

pub struct ResolveContext {
    pub global_scope: Scope,
    pub scope_stack: Vec<Scope>,
    pub variables: Vec<VariableInfo>,
    pub functions: Vec<FuncInfo>,
    pub places: Vec<Place>,
    next_ty_var_id: usize,
}

impl Default for ResolveContext {
    fn default() -> Self {
        let global_place = Place(0);
        let root_place = Place(1);
        ResolveContext {
            global_scope: Scope::new(global_place),
            scope_stack: vec![Scope::new(root_place)],
            variables: Vec::new(),
            functions: Vec::new(),
            places: vec![global_place, root_place],
            next_ty_var_id: 0,
        }
    }
}

impl ResolveContext {
    pub fn define_builtins(&mut self) {
        self.define_global_fn(EcoString::from("printInt"), vec![EcoString::from("n")], vec![Type::Int], Type::Unit);
        self.define_global_fn(EcoString::from("printFloat"), vec![EcoString::from("n")], vec![Type::Float], Type::Unit);
        self.define_global_fn(EcoString::from("printStr"), vec![EcoString::from("n")], vec![Type::Str], Type::Unit);
        self.define_global_fn(EcoString::from("inputInt"), vec![], vec![], Type::Int);
        self.define_global_fn(EcoString::from("inputStr"), vec![EcoString::from("len")], vec![Type::Int], Type::Str);
        self.define_global_fn(EcoString::from("chr"), vec![EcoString::from("n")], vec![Type::Int], Type::Char);
        self.define_global_fn(EcoString::from("ord"), vec![EcoString::from("ch")], vec![Type::Char], Type::Int);
        self.define_global_fn(EcoString::from("str"), vec![EcoString::from("n")], vec![Type::Int], Type::Str);
        self.define_global_fn(EcoString::from("float"), vec![EcoString::from("n")], vec![Type::Int], Type::Float);
        let a = self.new_ty_var();
        self.define_global_fn(EcoString::from("some"), vec![EcoString::from("val")], vec![a.clone()], a.wrap_in_option());
    }
    pub fn define_var(&mut self, name: EcoString, ident: Option<Identifier>, ty_hint: Option<Type>) -> VarId {
        let scope = self.scope_stack.last_mut().unwrap();
        let id = VarId(self.variables.len());
        self.variables.push(VariableInfo {
            name: name.clone(),
            ident,
            id,
            place: scope.place,
            ty: RefCell::new(Type::Invalid),
            ty_hint,
        });
        scope.define_var(name, id);
        id
    }
    pub fn define_fn(&mut self, name: EcoString, params: Vec<VarId>, return_ty: Type) -> FuncInfo {
        let idx = self.scope_stack.len() - 2;
        let scope = self.scope_stack.get_mut(idx).unwrap();
        let place = scope.place;
        let id = FnId(self.functions.len());
        scope.define_fn(name.clone(), params.len(), id);
        let params_ty = params.iter().map(|&var_id| self.get_var(var_id).ty_hint.clone().unwrap()).collect();
        let fun_info = FuncInfo {
            name,
            id,
            params,
            place,
            ty: FuncType::new(params_ty, return_ty),
            instances: Vec::new(),
        };
        self.functions.push(fun_info.clone());
        fun_info
    }
    pub fn define_global_fn(&mut self, name: EcoString, params: Vec<EcoString>, params_ty: Vec<Type>, return_ty: Type) -> FuncInfo {
        self.push_scope(true);
        let params: Vec<_> = params.iter().zip(params_ty.clone())
            .map(|(name, ty)| self.define_var(name.clone(), None, Some(ty)))
            .collect();
        self.pop_scope();
        let place = self.global_scope.place;
        let id = FnId(self.functions.len());
        self.global_scope.define_fn(name.clone(), params.len(), id);
        let fun_info = FuncInfo {
            name,
            id,
            params,
            place,
            ty: FuncType::new(params_ty, return_ty),
            instances: Vec::new(),
        };
        self.functions.push(fun_info.clone());
        fun_info
    }
    pub fn resolve_ty(&mut self, name: &EcoString) -> Type {
        match name.as_str() {
            "int" => Type::Int,
            "float" => Type::Float,
            "bool" => Type::Bool,
            "str" => Type::Str,
            "char" => Type::Char,
            "unit" => Type::Unit,
            _ => Type::Invalid,
        }
    }
    pub fn resolve_var(&mut self, name: &EcoString) -> Option<VarId> {
        let place = self.scope_stack.last_mut().unwrap().place;
        self.scope_stack.iter().rev()
            .filter(|scope| place == scope.place)
            .chain(iter::once(&self.global_scope))
            .find_map(|scope| scope.resolve_var(name))
    }
    pub fn resolve_fn(&mut self, name: &EcoString, num_params: usize) -> Option<FnId> {
        self.scope_stack.iter().rev()
            .chain(iter::once(&self.global_scope))
            .find_map(|scope| scope.resolve_fn(name, num_params))
    }
    pub fn add_instance(&mut self, fn_id: FnId, instance: &FuncType) {
        let fn_info = &mut self.functions[fn_id.0];
        fn_info.instances.push(instance.clone());
    }
    pub fn push_scope(&mut self, update_place: bool) {
        let scope = self.scope_stack.last_mut().unwrap();
        let place = if update_place {
            let new_place = Place(self.places.len());
            self.places.push(new_place);
            new_place
        } else { scope.place };
        self.scope_stack.push(Scope::new(place));
    }
    pub fn pop_scope(&mut self) {
        self.scope_stack.pop();
    }
    pub fn get_var(&self, var_id: VarId) -> &VariableInfo {
        &self.variables[var_id.0]
    }
    pub fn get_fn(&self, fn_id: FnId) -> &FuncInfo {
        &self.functions[fn_id.0]
    }
    pub fn new_ty_var(&mut self) -> Type {
        let id = self.next_ty_var_id;
        self.next_ty_var_id += 1;
        Type::TyVar(id)
    }
}

pub struct VariableInfo {
    pub ident: Option<Identifier>,
    pub name: EcoString,
    pub id: VarId,
    pub place: Place,
    pub ty: RefCell<Type>,
    pub ty_hint: Option<Type>,
}

#[derive(Debug, Clone)]
pub struct FuncInfo {
    pub name: EcoString,
    pub id: FnId,
    pub params: Vec<VarId>,
    pub place: Place,
    pub ty: FuncType,
    pub instances: Vec<FuncType>,
}

#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct Place(usize);