Skip to content

Commit

Permalink
type system supports type aliases. Wrapped records and type aliases i…
Browse files Browse the repository at this point in the history
…nto one Udt enum for easier handling
  • Loading branch information
eckertliam committed Jan 17, 2025
1 parent 6351709 commit 3530376
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 21 deletions.
61 changes: 42 additions & 19 deletions src/frontend/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ pub enum Type {
}
}

#[derive(Debug, PartialEq, Clone)]
pub struct Record {
pub name: String,
// type vars
pub generics: Vec<Type>,
pub fields: Vec<(String, Type)>,
}

impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -83,14 +75,45 @@ impl Display for Type {
}
}

#[derive(Debug, PartialEq, Clone)]
pub enum Udt {
Record {
name: String,
generics: Vec<Type>,
fields: Vec<(String, Type)>,
},
Alias {
name: String,
generics: Vec<Type>,
ty: Type,
}
}

impl Udt {
pub fn new_record(name: String, generics: Vec<Type>, fields: Vec<(String, Type)>) -> Self {
Self::Record { name, generics, fields }
}

pub fn new_alias(name: String, generics: Vec<Type>, ty: Type) -> Self {
Self::Alias { name, generics, ty }
}

pub fn name(&self) -> &str {
match self {
Udt::Record { name, .. } => name,
Udt::Alias { name, .. } => name,
}
}
}

type FnSig = (Vec<Type>, Type);

#[derive(Clone)]
pub struct TypeEnv {
pub fn_sig: Option<FnSig>,
parent: Option<Rc<RefCell<TypeEnv>>>,
bindings: HashMap<String, Type>,
record_types: HashMap<String, Record>,
udts: HashMap<String, Udt>,
}

impl TypeEnv {
Expand All @@ -100,7 +123,7 @@ impl TypeEnv {
fn_sig: None,
parent: None,
bindings: HashMap::new(),
record_types: HashMap::new(),
udts: HashMap::new(),
}
}

Expand All @@ -110,7 +133,7 @@ impl TypeEnv {
fn_sig: None,
parent: Some(Rc::new(RefCell::new(self.clone()))),
bindings: HashMap::new(),
record_types: HashMap::new(),
udts: HashMap::new(),
}
}

Expand All @@ -120,7 +143,7 @@ impl TypeEnv {
fn_sig: Some((params, return_type)),
parent: Some(Rc::new(RefCell::new(self.clone()))),
bindings: HashMap::new(),
record_types: HashMap::new(),
udts: HashMap::new(),
}
}

Expand All @@ -131,8 +154,8 @@ impl TypeEnv {
.or_else(|| self.parent.as_ref().and_then(|p| p.borrow().get(ident)))
}

pub fn get_record(&self, ident: &str) -> Option<Record> {
self.record_types.get(ident).cloned()
pub fn get_udt(&self, ident: &str) -> Option<Udt> {
self.udts.get(ident).cloned()
}

/// inserts a type into the top level type environment
Expand All @@ -144,16 +167,16 @@ impl TypeEnv {
}
}

pub fn insert_record_top(&mut self, record: Record) -> Result<(), String> {
pub fn insert_udt_top(&mut self, udt: Udt) -> Result<(), String> {
if self.parent.is_none() {
// ensure that the record name is not already in the type env
if self.record_types.contains_key(&record.name) {
return Err(format!("Record {} already exists in the type environment", record.name));
if self.udts.contains_key(udt.name()) {
return Err(format!("UDT {} already exists in the type environment", udt.name()));
}
self.record_types.insert(record.name.clone(), record);
self.udts.insert(udt.name().to_string(), udt);
Ok(())
} else {
self.parent.as_ref().unwrap().borrow_mut().insert_record_top(record)
self.parent.as_ref().unwrap().borrow_mut().insert_udt_top(udt)
}
}

Expand Down
23 changes: 21 additions & 2 deletions src/passes/type_pass.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Block, Expression, Program, Record, Statement, TokenKind, Type, TypeEnv};
use crate::{Block, Expression, Program, Statement, TokenKind, Type, TypeEnv, Udt};

// TODO: add handling for type var identifiers in expressions such as let x: TypeVar = 1; then x is a type var and let y = x + 1; will need resolution
// TODO: add handling for function calls in which the the callee has type vars in its signature
Expand All @@ -13,7 +13,10 @@ fn add_udt_pass(program: &Program, type_env: &mut TypeEnv) -> Result<Program, St
for stmt in program.statements.iter() {
match &stmt.node {
Statement::RecordDecl { name, fields, generics } => {
type_env.insert_record_top(Record { name: name.clone(), generics: generics.clone(), fields: fields.clone() })?;
type_env.insert_udt_top(Udt::Record { name: name.clone(), generics: generics.clone(), fields: fields.clone() })?;
}
Statement::AliasDecl { name, generics, ty } => {
type_env.insert_udt_top(Udt::Alias { name: name.clone(), generics: generics.clone(), ty: ty.clone() })?;
}
_ => new_program.add_statement(stmt.clone()),
}
Expand Down Expand Up @@ -406,6 +409,22 @@ mod tests {
env
}

#[test]
fn test_alias_declaration() {
let mut program = Program::new();
program.add_statement(Statement::new_alias_decl("Vec".to_string(), vec![], Type::Array { element_type: Box::new(Type::I64), size: 10 }, 1));
let new_env = type_check_program(&program).unwrap();
assert_eq!(new_env.get_udt("Vec"), Some(Udt::Alias { name: "Vec".to_string(), generics: vec![], ty: Type::Array { element_type: Box::new(Type::I64), size: 10 } }));
}

#[test]
fn test_record_declaration() {
let mut program = Program::new();
program.add_statement(Statement::new_record_decl("Point".to_string(), vec![], vec![("x".to_string(), Type::I64), ("y".to_string(), Type::I64)], 1));
let new_env = type_check_program(&program).unwrap();
assert_eq!(new_env.get_udt("Point"), Some(Udt::Record { name: "Point".to_string(), generics: vec![], fields: vec![("x".to_string(), Type::I64), ("y".to_string(), Type::I64)] }));
}

#[test]
fn test_literal_types() {
let mut env = create_test_env();
Expand Down

0 comments on commit 3530376

Please sign in to comment.