Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add name canonicalization #538

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/canonicalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! First pass of the interpreter. This builds a [`ScopeMap`], instruction by instruction,
//! resolving names eagerly as they go. If a name cannot be resolved, then it is an undeclared
//! error.

use crate::ScopeMap;

use std::sync::atomic::{AtomicU64, Ordering};

fn create_anonymous_path() -> String {
static ANONYMOUS_PATH_COUNTER: AtomicU64 = AtomicU64::new(0);

let anon_path_count = ANONYMOUS_PATH_COUNTER.fetch_add(1, Ordering::SeqCst);

format!("<anonymous_{}>", anon_path_count)
}

pub trait Canonicalize: Clone {
/// Returns the current name of the [`Instruction`], before it gets
/// canonicalized.
///
/// ```ignore
/// // Say we have the current code
/// func outer() {
/// func inner() {
/// func innermost() {
/// // This function is nested: its canonicalize name should
/// // be outer::inner::innermost
/// }
/// }
/// }
/// ```
///
/// Calling `current_name()` on the node associated with the `innermost`
/// function declaration should return `innermost`, not its canonicalized
/// name.
fn current_name(&self) -> &str;

/// Set the name of an [`Instruction`]. This is used so that we can
/// implement `canonicalize` directly through the trait, without having
/// to rely on every [`Instruction`] implementing it.
fn set_name(&mut self, name: String);

/// Name separator used when canonicalizing paths
const SEPARATOR: &'static str = "@";

/// Return a copy of self with a canonicalized name
// FIXME: We probably need to Box<Self> right?
fn canonicalize(&self, current_path: &str) -> Self {
let mut new_self = self.clone();

// If the path is empty, then no need for a separator. We're at the
// beginning of an outermost scope
if !current_path.is_empty() {
let old_name = self.current_name();
new_self.set_name(format!("{}{}{}", current_path, Self::SEPARATOR, old_name));
}

new_self
}

/// Visitor-like function to build a [`ScopeMap`] instruction by instruction.
/// An [`Instruction`] implementing this needs to canonicalize, add itself
/// to the scope map in a proper way, and then visits its children with
/// its new canonicalized name.
fn build_scope_map(&self, scope_map: ScopeMap, current_path: &str) -> ScopeMap;
}
6 changes: 3 additions & 3 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub struct Context {
args: Vec<String>,

/// Contains the scopes of the context, in which are variables and functions
scope_map: ScopeMap<Var, Rc<FunctionDec>, Rc<TypeDec>>,
scope_map: ScopeMap,

/// Contains the functions shipping with the interpreter
builtins: Builtins,
Expand Down Expand Up @@ -397,7 +397,7 @@ impl Context {
}

/// Printer for the context's usage of the ScopeMap
impl<V: Instruction, F: Instruction, T: Instruction> Display for ScopeMap<V, Rc<F>, Rc<T>> {
impl Display for ScopeMap {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
for stack in self.scopes() {
writeln!(f, "{}", stack)?;
Expand All @@ -407,7 +407,7 @@ impl<V: Instruction, F: Instruction, T: Instruction> Display for ScopeMap<V, Rc<
}
}

impl<V: Instruction, F: Instruction, T: Instruction> Display for Scope<V, Rc<F>, Rc<T>> {
impl Display for Scope {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
for ty in self.types.values() {
writeln!(f, "{}", ty.print())?;
Expand Down
67 changes: 31 additions & 36 deletions src/context/scope_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,42 @@
//! scope. If the specified name cannot be found, it searches the other scopes, defined
//! before the current one, until it finds the correct component.

use std::collections::{HashMap, LinkedList};

use crate::instruction::{FunctionDec, TypeDec, Var};
use crate::{ErrKind, Error};

use std::collections::{HashMap, LinkedList};

/// A scope contains a set of available variables, functions and types
#[derive(Clone)]
pub struct Scope<V, F, T> {
pub(crate) variables: HashMap<String, V>,
pub(crate) functions: HashMap<String, F>,
pub(crate) types: HashMap<String, T>,
pub struct Scope {
pub(crate) variables: HashMap<String, Var>,
pub(crate) functions: HashMap<String, FunctionDec>,
pub(crate) types: HashMap<String, TypeDec>,
}

impl<V, F, T> Scope<V, F, T> {
impl Scope {
/// Get a reference on a variable from the scope map if is has been inserted already
pub fn get_variable(&self, name: &str) -> Option<&V> {
pub fn get_variable(&self, name: &str) -> Option<&Var> {
self.variables.get(name)
}

/// Get a mutable reference on a variable from the scope map if it has been inserted already
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut V> {
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut Var> {
self.variables.get_mut(name)
}

/// Get a reference on a function from the scope map if is has been inserted already
pub fn get_function(&self, name: &str) -> Option<&F> {
pub fn get_function(&self, name: &str) -> Option<&FunctionDec> {
self.functions.get(name)
}

/// Get a reference on a type from the scope map if is has been inserted already
pub fn get_type(&self, name: &str) -> Option<&T> {
pub fn get_type(&self, name: &str) -> Option<&TypeDec> {
self.types.get(name)
}

/// Add a variable to the most recently created scope, if it doesn't already exist
pub fn add_variable(&mut self, name: String, var: V) -> Result<(), Error> {
pub fn add_variable(&mut self, name: String, var: Var) -> Result<(), Error> {
match self.get_variable(&name) {
Some(_) => Err(Error::new(ErrKind::Context)
.with_msg(format!("variable already declared: {}", name))),
Expand All @@ -64,7 +65,7 @@ impl<V, F, T> Scope<V, F, T> {
}

/// Add a variable to the most recently created scope, if it doesn't already exist
pub fn add_function(&mut self, name: String, func: F) -> Result<(), Error> {
pub fn add_function(&mut self, name: String, func: FunctionDec) -> Result<(), Error> {
match self.get_function(&name) {
Some(_) => Err(Error::new(ErrKind::Context)
.with_msg(format!("function already declared: {}", name))),
Expand All @@ -76,7 +77,7 @@ impl<V, F, T> Scope<V, F, T> {
}

/// Add a type to the most recently created scope, if it doesn't already exist
pub fn add_type(&mut self, name: String, type_dec: T) -> Result<(), Error> {
pub fn add_type(self, name: String, type_dec: TypeDec) -> Result<(), Error> {
match self.get_type(&name) {
Some(_) => {
Err(Error::new(ErrKind::Context)
Expand All @@ -90,9 +91,9 @@ impl<V, F, T> Scope<V, F, T> {
}
}

impl<V, F, T> Default for Scope<V, F, T> {
/// Create a new empty Scope
fn default() -> Scope<V, F, T> {
impl Default for Scope {
/// Create a new empty [`ScopeMap`]
fn default() -> Scope {
Scope {
variables: HashMap::new(),
functions: HashMap::new(),
Expand All @@ -107,20 +108,20 @@ pub type ScopeStack<T> = LinkedList<T>;
/// A scope map keeps track of the currently available scopes and the current depth
/// level.
#[derive(Clone, Default)]
pub struct ScopeMap<V, F, T> {
scopes: ScopeStack<Scope<V, F, T>>,
pub struct ScopeMap {
scopes: ScopeStack<Scope>,
}

impl<V, F, T> ScopeMap<V, F, T> {
impl ScopeMap {
/// Create a new empty scope map, at depth 0
pub fn new() -> ScopeMap<V, F, T> {
pub fn new() -> ScopeMap {
ScopeMap {
scopes: ScopeStack::new(),
}
}

/// Get a reference on the scopes inside a ScopeMap
pub fn scopes(&self) -> &ScopeStack<Scope<V, F, T>> {
pub fn scopes(&self) -> &ScopeStack<Scope> {
&self.scopes
}

Expand All @@ -137,7 +138,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Maybe get a variable in any available scopes
pub fn get_variable(&self, name: &str) -> Option<&V> {
pub fn get_variable(&self, name: &str) -> Option<&Var> {
// FIXME: Use find for code quality?
for scope in self.scopes.iter() {
match scope.get_variable(name) {
Expand All @@ -149,7 +150,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
None
}

pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut V> {
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut Var> {
for scope in self.scopes.iter_mut() {
match scope.get_variable_mut(name) {
Some(v) => return Some(v),
Expand All @@ -161,7 +162,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Maybe get a function in any available scopes
pub fn get_function(&self, name: &str) -> Option<&F> {
pub fn get_function(&self, name: &str) -> Option<&FunctionDec> {
// FIXME: Use find for code quality?
for scope in self.scopes.iter() {
match scope.get_function(name) {
Expand All @@ -174,7 +175,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Maybe get a type in any available scopes
pub fn get_type(&self, name: &str) -> Option<&T> {
pub fn get_type(&self, name: &str) -> Option<&TypeDec> {
// FIXME: Use find for code quality?
for scope in self.scopes.iter() {
match scope.get_type(name) {
Expand All @@ -187,7 +188,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Add a variable to the current scope if it hasn't been added before
pub fn add_variable(&mut self, name: String, var: V) -> Result<(), Error> {
pub fn add_variable(&mut self, name: String, var: Var) -> Result<(), Error> {
match self.scopes.front_mut() {
Some(head) => head.add_variable(name, var),
None => Err(Error::new(ErrKind::Context)
Expand All @@ -205,7 +206,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Add a function to the current scope if it hasn't been added before
pub fn add_function(&mut self, name: String, func: F) -> Result<(), Error> {
pub fn add_function(&mut self, name: String, func: FunctionDec) -> Result<(), Error> {
match self.scopes.front_mut() {
Some(head) => head.add_function(name, func),
None => Err(Error::new(ErrKind::Context)
Expand All @@ -214,7 +215,7 @@ impl<V, F, T> ScopeMap<V, F, T> {
}

/// Add a type to the current scope if it hasn't been added before
pub fn add_type(&mut self, name: String, custom_type: T) -> Result<(), Error> {
pub fn add_type(&mut self, name: String, custom_type: TypeDec) -> Result<(), Error> {
match self.scopes.front_mut() {
Some(head) => head.add_type(name, custom_type),
None => Err(Error::new(ErrKind::Context)
Expand All @@ -234,7 +235,7 @@ mod tests {
};
}

fn new_scopemap() -> ScopeMap<Var, (), ()> {
fn new_scopemap() -> ScopeMap {
ScopeMap::new()
}

Expand Down Expand Up @@ -302,10 +303,4 @@ mod tests {

assert!(s.get_variable("a").is_none());
}

#[test]
fn t_scope_of_anything() {
let _ = Scope::<i32, i32, String>::default();
let _ = Scope::<(), (), ()>::default();
}
}
19 changes: 18 additions & 1 deletion src/instruction/function_declaration.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Function Declarations are used when adding a new function to the source. They contain
//! a name, a list of required arguments as well as an associated code block

use crate::canonicalize::Canonicalize;
use crate::generics::GenericMap;
use crate::instruction::{Block, DecArg, InstrKind, Instruction};
use crate::typechecker::{CheckedType, TypeCtx, TypeId};
use crate::Generic;
use crate::{log, Context, ErrKind, Error, ObjectInstance, TypeCheck};
use crate::{Generic, ScopeMap};
use crate::{Location, SpanTuple};

/// What "kind" of function is defined. There are four types of functions in jinko,
Expand Down Expand Up @@ -413,6 +414,22 @@ impl From<&str> for FunctionKind {
}
}

impl Canonicalize for FunctionDec {
fn set_name(&mut self, name: String) {
self.name = name
}

fn current_name(&self) -> &str {
self.name()
}

fn build_scope_map(&self, scope_map: ScopeMap, current_path: &str) -> ScopeMap {
scope_map.add_function(self.canonicalize(current_path));

scope_map
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// FIXME: Make crate attribute `#![warn(missing_docs)]`

pub mod builtins;
pub mod canonicalize;
mod context;
mod error;
#[cfg(feature = "ffi")]
Expand Down
2 changes: 1 addition & 1 deletion src/typechecker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct TypeCtx {
/// map: Variables, Functions and Types.
/// For functions, we keep a vector of argument types as well as the return type.
/// Custom types need to keep a type for themselves, as well as types for all their fields
types: ScopeMap<CheckedType, FunctionDec, CustomTypeType>,
types: ScopeMap,
/// Is the type context executing its second pass or not. The second pass of the typechecking
/// process is to resolve generic calls and make sure that the functions called on the
/// expanded types are actually present. Plus, this also allows us to call/instantiate
Expand Down