diff --git a/coal-core/src/ast/builtin.rs b/coal-core/src/ast/builtin.rs index a6974ec..cb82f59 100644 --- a/coal-core/src/ast/builtin.rs +++ b/coal-core/src/ast/builtin.rs @@ -1,48 +1,47 @@ use std::collections::HashMap; -use super::Type; +use super::TypeIdentifierTree; -pub fn builtin_types() -> HashMap { +pub fn builtin_types() -> HashMap { let mut builtins = HashMap::new(); builtins.insert( "assert".to_owned(), - Type::Fn { - args_t: vec![Type::Bool], - ret_t: Box::new(Type::Void), - uses_self: false, + TypeIdentifierTree { + root: "fn".into(), + children: vec![ + // return value comes first for functions in type identifier trees + "void".into(), + "bool".into(), + ], }, ); builtins.insert( "assert_eq".to_owned(), - Type::Fn { - args_t: vec![Type::Any, Type::Any], - ret_t: Box::new(Type::Void), - uses_self: false, + TypeIdentifierTree { + root: "fn".into(), + children: vec!["any".into(), "any".into()], }, ); builtins.insert( "dbg".to_owned(), - Type::Fn { - args_t: vec![Type::Any], - ret_t: Box::new(Type::Void), - uses_self: false, + TypeIdentifierTree { + root: "fn".into(), + children: vec!["void".into(), "any".into()], }, ); builtins.insert( "print".to_owned(), - Type::Fn { - args_t: vec![Type::Any], - ret_t: Box::new(Type::Void), - uses_self: false, + TypeIdentifierTree { + root: "fn".into(), + children: vec!["void".into(), "any".into()], }, ); builtins.insert( "println".to_owned(), - Type::Fn { - args_t: vec![Type::Any], - ret_t: Box::new(Type::Void), - uses_self: false, + TypeIdentifierTree { + root: "fn".into(), + children: vec!["void".into(), "any".into()], }, ); diff --git a/coal-core/src/ast/expr.rs b/coal-core/src/ast/expr.rs index 91d9dfe..62d0bfe 100644 --- a/coal-core/src/ast/expr.rs +++ b/coal-core/src/ast/expr.rs @@ -2,11 +2,11 @@ use std::fmt; use crate::{ParserError, ParserErrorKind, Span, indent}; -use super::{Func, Ident, Infix, Literal, Param, Prefix, Stmt, Struct, Type}; +use super::{BaseType, Func, Ident, Infix, Literal, Param, Prefix, ResolvedType, Stmt, Struct}; #[derive(Clone, Debug, PartialEq)] pub enum Expr { - Ident(Ident, Type, Span), + Ident(Ident, ResolvedType, Span), Literal(Literal, Span), Prefix(Prefix, Box, Span), Infix(Infix, Box, Box, Span), @@ -33,7 +33,7 @@ pub enum Expr { Fn(Func), Closure { args: Vec, - ret_t: Type, + ret_t: ResolvedType, body: Vec, span: Span, }, @@ -41,20 +41,20 @@ pub enum Expr { Call { name: String, args: Vec, - ret_t: Type, + ret_t: ResolvedType, span: Span, }, MethodCall { lhs: Box, name: String, args: Vec, - ret_t: Type, + ret_t: ResolvedType, span: Span, }, AttrAccess { lhs: Box, name: String, - t: Type, + t: ResolvedType, span: Span, }, } @@ -88,12 +88,12 @@ impl Expr { pub fn is_indexable(&self) -> bool { match self { - Expr::Ident(_, t, _) => t.is_indexable(), + Expr::Ident(_, t, _) => t.base.is_indexable(), Expr::Literal(l, _) => l.is_indexable(), Expr::Prefix(_, rhs, _) => rhs.is_indexable(), Expr::Infix(_, lhs, rhs, _) => lhs.is_indexable() && rhs.is_indexable(), Expr::Index(lhs, _, _) => lhs.is_indexable(), - Expr::Call { ret_t, .. } => ret_t.is_indexable(), + Expr::Call { ret_t, .. } => ret_t.base.is_indexable(), _ => false, } } @@ -102,7 +102,7 @@ impl Expr { matches!(self, Expr::Fn(_) | Expr::Closure { .. }) } - pub fn ret_t(&self, expected: &Type, last: bool) -> Result { + pub fn ret_t(&self, expected: &ResolvedType, last: bool) -> Result { match self { Expr::If { then, elifs, alt, .. @@ -110,7 +110,7 @@ impl Expr { let mut returning = None; for stmt in then { let t = stmt.ret_t(expected, last)?; - if t != Type::Void && t != *expected { + if t.base != BaseType::Void && t != *expected { return Err(ParserError::new( ParserErrorKind::TypeMismatch( expected.clone().into(), @@ -159,18 +159,18 @@ impl Expr { )); } } - } else if last && *expected != Type::Void { + } else if last && expected.base != BaseType::Void { return Err(ParserError::new( ParserErrorKind::MissingElseClause, self.span(), )); } - Ok(Type::Void) + Ok(BaseType::Void.try_into().unwrap()) } Expr::While { body, .. } => { for stmt in body { let t = stmt.ret_t(expected, last)?; - if t != Type::Void && t != *expected { + if t.base != BaseType::Void && t != *expected { return Err(ParserError::new( ParserErrorKind::TypeMismatch( expected.clone().into(), @@ -180,9 +180,9 @@ impl Expr { )); } } - Ok(Type::Void) + Ok(BaseType::Void.try_into().unwrap()) } - _ => Ok(Type::Void), + _ => Ok(BaseType::Void.try_into().unwrap()), } } @@ -219,7 +219,7 @@ impl Expr { rets } - pub fn cast(&self, to: &Type) -> Expr { + pub fn cast(&self, to: &ResolvedType) -> Expr { match self { Expr::Literal(l, _) => Expr::Literal(l.cast(to), self.span()), _ => self.clone(), diff --git a/coal-core/src/ast/func.rs b/coal-core/src/ast/func.rs index 28121a3..28bb6e0 100644 --- a/coal-core/src/ast/func.rs +++ b/coal-core/src/ast/func.rs @@ -5,13 +5,13 @@ use std::{ use crate::{Span, indent}; -use super::{Param, Stmt, Type}; +use super::{BaseType, Param, ResolvedType, Stmt}; #[derive(Clone, Debug, PartialEq)] pub struct Func { pub name: String, pub args: Vec, - pub ret_t: Type, + pub ret_t: ResolvedType, pub body: Vec, pub span: Span, } @@ -37,7 +37,7 @@ impl Func { .map(|arg| format!("{arg}")) .collect::>() .join(", "); - if *ret_t == Type::Void { + if ret_t.base == BaseType::Void { writeln!(f, "{args}) {{")?; } else { writeln!(f, "{args}) -> {ret_t} {{")?; diff --git a/coal-core/src/ast/list.rs b/coal-core/src/ast/list.rs index 6b058b9..52bc170 100644 --- a/coal-core/src/ast/list.rs +++ b/coal-core/src/ast/list.rs @@ -5,17 +5,17 @@ use std::{ use crate::indent; -use super::{Expr, Type}; +use super::{Expr, ResolvedType}; #[derive(Clone, Debug, PartialEq, Default)] pub struct List { pub data: Vec, - pub t: Type, + pub t: ResolvedType, pub repeat: Option>, } impl List { - pub fn new(data: &[Expr], t: Type) -> Self { + pub fn new(data: &[Expr], t: ResolvedType) -> Self { List { data: data.to_owned(), t, @@ -23,7 +23,7 @@ impl List { } } - pub fn new_repeat(data: &[Expr], t: Type, repeat: Box) -> Self { + pub fn new_repeat(data: &[Expr], t: ResolvedType, repeat: Box) -> Self { List { data: data.to_owned(), t, diff --git a/coal-core/src/ast/literal.rs b/coal-core/src/ast/literal.rs index d841a81..7fe33be 100644 --- a/coal-core/src/ast/literal.rs +++ b/coal-core/src/ast/literal.rs @@ -1,8 +1,8 @@ use std::fmt; -use crate::indent; +use crate::{ResolvedType, indent}; -use super::{F32, F64, I32, I64, I128, List, Map, Type, U32, U64}; +use super::{F32, F64, I32, I64, I128, List, Map, U32, U64}; #[derive(Clone, Debug, PartialEq)] pub enum Literal { @@ -49,7 +49,7 @@ impl Literal { } } - pub fn set_type(&mut self, t1: Type, t2: Option) { + pub fn set_type(&mut self, t1: ResolvedType, t2: Option) { match self { Literal::List(l) => l.t = t1, Literal::Map(m) => { @@ -62,8 +62,8 @@ impl Literal { } } - pub fn cast(&self, to: &Type) -> Literal { - match (self, to) { + pub fn cast(&self, to: &ResolvedType) -> Literal { + match (self, &to.base) { (Literal::U32(from), &U64) => Literal::U64(*from as u64), (Literal::U32(from), &I32) => Literal::I32(*from as i32), (Literal::U32(from), &I64) => Literal::I64(*from as i64), diff --git a/coal-core/src/ast/map.rs b/coal-core/src/ast/map.rs index 8640ee4..6f1308f 100644 --- a/coal-core/src/ast/map.rs +++ b/coal-core/src/ast/map.rs @@ -2,16 +2,16 @@ use std::fmt; use crate::indent; -use super::{Expr, Type}; +use super::{BaseType, Expr, ResolvedType}; #[derive(Clone, Debug, PartialEq)] pub struct Map { pub data: Vec<(Expr, Expr)>, - pub t: (Type, Type), + pub t: (ResolvedType, ResolvedType), } impl Map { - pub fn new(data: &[(Expr, Expr)], t: (Type, Type)) -> Self { + pub fn new(data: &[(Expr, Expr)], t: (ResolvedType, ResolvedType)) -> Self { Map { data: data.to_owned(), t, @@ -66,7 +66,10 @@ impl Default for Map { fn default() -> Self { Map { data: vec![], - t: (Type::Unknown, Type::Unknown), + t: ( + BaseType::Unknown.try_into().unwrap(), + BaseType::Unknown.try_into().unwrap(), + ), } } } diff --git a/coal-core/src/ast/param.rs b/coal-core/src/ast/param.rs index 4c2fda2..ca82823 100644 --- a/coal-core/src/ast/param.rs +++ b/coal-core/src/ast/param.rs @@ -3,16 +3,16 @@ use std::{ hash::{Hash, Hasher}, }; -use crate::{Type, indent}; +use crate::{ResolvedType, indent}; #[derive(Clone, Debug, PartialEq)] pub struct Param { pub name: String, - pub t: Type, + pub t: ResolvedType, } impl Param { - pub fn new(name: &str, t: Type) -> Param { + pub fn new(name: &str, t: ResolvedType) -> Param { Param { name: name.to_string(), t, diff --git a/coal-core/src/ast/stmt.rs b/coal-core/src/ast/stmt.rs index 32d6601..7fbf0c8 100644 --- a/coal-core/src/ast/stmt.rs +++ b/coal-core/src/ast/stmt.rs @@ -1,15 +1,15 @@ use std::fmt; -use crate::{ParserError, ParserErrorKind, Span, indent}; +use crate::{ParserError, ParserErrorKind, ResolvedType, Span, indent}; -use super::{Comment, Expr, Ident, Infix, StructDecl, Type}; +use super::{BaseType, Comment, Expr, Ident, Infix, StructDecl}; #[derive(Clone, Debug, PartialEq)] pub enum Stmt { Void, Newline, Comment(Comment), - Let(Ident, Type, Expr), + Let(Ident, ResolvedType, Expr), Assign(Expr, Expr), OpAssign(Infix, Expr, Expr), Return(Expr), @@ -30,16 +30,19 @@ impl Stmt { } } - pub fn ret_t(&self, expected: &Type, last: bool) -> Result { + pub fn ret_t(&self, expected: &ResolvedType, last: bool) -> Result { match self { - Stmt::Return(e) => Type::try_from(e).map_err(|_| { + Stmt::Return(e) => ResolvedType::try_from(e).map_err(|_| { ParserError::new( - ParserErrorKind::TypeMismatch(expected.clone().into(), Type::Void.into()), + ParserErrorKind::TypeMismatch( + expected.clone().into(), + ResolvedType::try_from(BaseType::Void).unwrap().into(), + ), e.span(), ) }), Stmt::Expr(e) => e.ret_t(expected, last), - _ => Ok(Type::Void), + _ => Ok(BaseType::Void.try_into().unwrap()), } } diff --git a/coal-core/src/ast/types.rs b/coal-core/src/ast/types.rs index ce9b663..59d32fd 100644 --- a/coal-core/src/ast/types.rs +++ b/coal-core/src/ast/types.rs @@ -1,25 +1,28 @@ -use std::fmt; +use std::{fmt, iter, vec}; use crate::TokenKind; use super::{Expr, Func, Literal, Prefix, Stmt, Struct, StructDecl}; +#[derive(Copy, Clone, Debug, Hash, PartialEq)] +pub enum Arity { + Fixed(u8), + Variable, +} + +/// A base type. Can be modeled as a type-level function with 0 or more arguments. #[derive(Clone, Debug, Hash, PartialEq, Default)] -pub enum Type { +pub enum BaseType { Bool, Str, Num(Num), - List(Box), - Map(Box<(Type, Type)>), - Fn { - args_t: Vec, - ret_t: Box, - uses_self: bool, - }, + List, // arity 1 + Map, // arity 2 + Fn, // arity n, first type argument is return type Range, - UserDefined(String, Box), - StructDecl(String, Vec<(String, Type, bool)>, Vec<(String, Type)>), - Struct(String, Vec<(String, Type)>), + Aliased(String, Box), + // for now, we only support arity 0 structs + Struct(String, Vec<(String, Box)>), Nil, Any, Void, @@ -27,6 +30,24 @@ pub enum Type { Unknown, } +/// A fully resolved type for a particular term, including all type arguments. +/// For example, in list[list[str]], the base type is list, and the only arg is list[str]. +#[derive(Clone, Debug, Hash, PartialEq, Default)] +pub struct ResolvedType { + pub base: BaseType, + pub args: Vec, +} + +pub type TypeIdentifier = String; + +/// A tree of type identifiers. Allows us to represent nested +/// types like list[map[bool, MyCustomList[i32]]] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TypeIdentifierTree { + pub root: TypeIdentifier, + pub children: Vec, +} + #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum Num { U32, @@ -38,23 +59,23 @@ pub enum Num { F64, } -pub const U32: Type = Type::Num(Num::U32); -pub const U64: Type = Type::Num(Num::U64); -pub const I32: Type = Type::Num(Num::I32); -pub const I64: Type = Type::Num(Num::I64); -pub const I128: Type = Type::Num(Num::I128); -pub const F32: Type = Type::Num(Num::F32); -pub const F64: Type = Type::Num(Num::F64); +pub const U32: BaseType = BaseType::Num(Num::U32); +pub const U64: BaseType = BaseType::Num(Num::U64); +pub const I32: BaseType = BaseType::Num(Num::I32); +pub const I64: BaseType = BaseType::Num(Num::I64); +pub const I128: BaseType = BaseType::Num(Num::I128); +pub const F32: BaseType = BaseType::Num(Num::F32); +pub const F64: BaseType = BaseType::Num(Num::F64); #[derive(Clone, Debug, Hash, PartialEq)] pub struct MethodSignature { - pub args_t: Vec, - pub ret_t: Type, + pub args_t: Vec, + pub ret_t: ResolvedType, pub uses_self: bool, } impl MethodSignature { - pub fn new(args_t: &[Type], ret_t: Type, uses_self: bool) -> Self { + pub fn new(args_t: &[ResolvedType], ret_t: ResolvedType, uses_self: bool) -> Self { MethodSignature { args_t: args_t.to_owned(), ret_t, @@ -71,9 +92,18 @@ impl MethodSignature { } } -impl Type { +impl BaseType { + pub const fn arity(&self) -> Arity { + match self { + Self::Fn => Arity::Variable, + Self::List => Arity::Fixed(1), + Self::Map => Arity::Fixed(2), + _ => Arity::Fixed(0), + } + } + pub fn is_numeric(&self) -> bool { - matches!(self, Type::Num(_)) + matches!(self, BaseType::Num(_)) } pub fn is_int(&self) -> bool { @@ -81,76 +111,37 @@ impl Type { } pub fn is_hashable(&self) -> bool { - matches!(self, Type::Bool | Type::Str | Type::Num(_)) - } - - pub fn is_defined(&self) -> bool { - match self { - Type::List(t) => t.is_defined(), - Type::Map(t) => t.0.is_defined() && t.1.is_defined(), - Type::Unknown => false, - _ => true, - } + matches!(self, BaseType::Bool | BaseType::Str | BaseType::Num(_)) } pub fn is_composite(&self) -> bool { - match self { - Type::UserDefined(_, t) => t.is_composite(), - Type::List(_) - | Type::Map(_) - | Type::Fn { .. } - | Type::StructDecl(_, _, _) - | Type::Struct(_, _) => true, - _ => false, - } + self.arity() != Arity::Fixed(0) } pub fn is_indexable(&self) -> bool { - match self { - Type::List(_) | Type::Map(_) => true, - Type::UserDefined(_, t) => t.is_indexable(), - _ => false, + match self.arity() { + Arity::Fixed(0) | Arity::Variable => false, + Arity::Fixed(_) => true, } } pub fn name(&self) -> String { match self { - Type::UserDefined(name, _) => name.to_owned(), - Type::StructDecl(name, _, _) => name.to_owned(), - Type::Struct(name, _) => name.to_owned(), + BaseType::Aliased(name, _) => name.to_owned(), + BaseType::Struct(name, _) => name.to_owned(), _ => self.to_string(), } } - pub fn extract(&self) -> &Self { - match self { - Type::List(t) => t, - t => t, - } - } - - pub fn into_struct_t(self) -> Option { - if let Type::StructDecl(name, attrs, fns) = self { - Some(Type::Struct( - name.to_owned(), - attrs - .iter() - .map(|(s, t, _)| (s.clone(), t.clone())) - .chain(fns) - .collect(), - )) - } else { - None - } - } - - pub fn partial_eq(&self, other: &Type) -> bool { - if let (Type::Struct(a_name, a_attrs), Type::Struct(b_name, b_attrs)) = (self, other) { + pub fn partial_eq(&self, other: &BaseType) -> bool { + if let (BaseType::Struct(a_name, a_attrs), BaseType::Struct(b_name, b_attrs)) = + (self, other) + { if a_name != b_name { return false; } for (_, attr_t) in a_attrs { - if !matches!(attr_t, Type::Fn { .. }) && a_name != b_name && a_attrs != b_attrs { + if !matches!(attr_t.base, BaseType::Fn) && a_name != b_name && a_attrs != b_attrs { return false; } } @@ -159,135 +150,58 @@ impl Type { self == other } +} - pub fn sig(&self, method: &str) -> Option { - match self { - Type::Num(_) => self.num_sig(method), - Type::Str => self.str_sig(method), - Type::List(t) => self.list_sig(method, t), - Type::Map(t) => self.map_sig(method, t), - Type::Struct(_, attrs) => self.struct_sig(method, attrs), - Type::StructDecl(_, _, funcs) => self.struct_sig(method, funcs), - _ => self.global_sig(method), - } - } - - fn global_sig(&self, method: &str) -> Option { - match method { - "to_s" => Some(MethodSignature::new(&[], Type::Str, false)), - _ => None, - } - } - - fn num_sig(&self, method: &str) -> Option { - self.global_sig(method) - } - - fn str_sig(&self, method: &str) -> Option { - match method { - "len" => Some(MethodSignature::new(&[], U64, false)), - "split" => Some(MethodSignature::new( - &[Type::Str], - Type::List(Box::new(Type::Str)), - false, - )), - _ => None, - } - } - - fn list_sig(&self, method: &str, t: &Type) -> Option { - match method { - "len" => Some(MethodSignature::new(&[], U64, false)), - "push" => Some(MethodSignature::new( - std::slice::from_ref(t), - Type::Void, - false, - )), - "pop" => Some(MethodSignature::new(&[], t.clone(), false)), - "get" => Some(MethodSignature::new(&[I64], t.clone(), false)), - "first" => Some(MethodSignature::new(&[], t.clone(), false)), - "last" => Some(MethodSignature::new(&[], t.clone(), false)), - "join" => Some(MethodSignature::new(&[Type::Str], Type::Str, false)), - "map" => Some(MethodSignature::new( - &[Type::Fn { - args_t: vec![t.clone()], - ret_t: Box::new(t.clone()), - uses_self: false, - }], - Type::List(Box::new(Type::Unknown)), - false, - )), - "clear" => Some(MethodSignature::new(&[], Type::Void, false)), - _ => None, - } - } - - fn map_sig(&self, method: &str, (kt, vt): &(Type, Type)) -> Option { - match method { - "len" => Some(MethodSignature::new(&[], U64, false)), - "get" => Some(MethodSignature::new( - std::slice::from_ref(kt), - vt.clone(), - false, - )), - "remove" => Some(MethodSignature::new( - std::slice::from_ref(kt), - Type::Void, - false, - )), - "clear" => Some(MethodSignature::new(&[], Type::Void, false)), - _ => None, - } - } +impl TryFrom for ResolvedType { + type Error = String; - fn struct_sig(&self, method: &str, attrs: &Vec<(String, Type)>) -> Option { - for (name, t) in attrs { - if name == method - && let Type::Fn { - args_t, - ret_t, - uses_self, - } = t - { - return Some(MethodSignature::new(args_t, *ret_t.clone(), *uses_self)); - } + fn try_from(base: BaseType) -> Result { + match base.arity() { + Arity::Fixed(0) => Ok(ResolvedType { base, args: vec![] }), + Arity::Fixed(_) => Err("Cannot auto-convert fixed arity type".to_string()), + Arity::Variable => Err("Cannot auto-convert variable arity type".to_string()), } - None } } -impl From<&Literal> for Type { +impl From<&Literal> for ResolvedType { fn from(literal: &Literal) -> Self { match literal { - Literal::Str(_) => Type::Str, - Literal::U32(_) => U32, - Literal::U64(_) => U64, - Literal::I32(_) => I32, - Literal::I64(_) => I64, - Literal::I128(_) => I128, - Literal::F32(_) => F32, - Literal::F64(_) => F64, - Literal::Bool(_) => Type::Bool, - Literal::List(l) => Type::List(Box::new(l.t.to_owned())), - Literal::Map(m) => Type::Map(Box::new(m.t.to_owned())), - Literal::Nil => Type::Nil, + Literal::Str(_) => BaseType::Str.try_into().unwrap(), + Literal::U32(_) => U32.try_into().unwrap(), + Literal::U64(_) => U64.try_into().unwrap(), + Literal::I32(_) => I32.try_into().unwrap(), + Literal::I64(_) => I64.try_into().unwrap(), + Literal::I128(_) => I128.try_into().unwrap(), + Literal::F32(_) => F32.try_into().unwrap(), + Literal::F64(_) => F64.try_into().unwrap(), + Literal::Bool(_) => BaseType::Bool.try_into().unwrap(), + Literal::List(l) => ResolvedType { + base: BaseType::List, + args: vec![l.t.to_owned()], + }, + Literal::Map(m) => ResolvedType { + base: BaseType::Map, + args: vec![m.t.0.to_owned(), m.t.1.to_owned()], + }, + Literal::Nil => BaseType::Nil.try_into().unwrap(), } } } -impl From<&Vec> for Type { +impl From<&Vec> for ResolvedType { fn from(stmts: &Vec) -> Self { - let mut ret_t = Type::Void; + let mut ret_t: ResolvedType = BaseType::Void.try_into().unwrap(); for stmt in stmts { match stmt { Stmt::Return(expr) => { - if let Ok(t) = Type::try_from(expr) { + if let Ok(t) = ResolvedType::try_from(expr) { ret_t = t; } } Stmt::Expr(expr) if matches!(expr, Expr::If { .. }) => { - if let Ok(t) = Type::try_from(expr) { + if let Ok(t) = ResolvedType::try_from(expr) { ret_t = t; } } @@ -299,8 +213,8 @@ impl From<&Vec> for Type { } } -impl TryFrom<&TokenKind> for Type { - type Error = Type; +impl TryFrom<&TokenKind> for BaseType { + type Error = BaseType; fn try_from(token: &TokenKind) -> Result { match token { @@ -312,13 +226,13 @@ impl TryFrom<&TokenKind> for Type { "i128" => Ok(I128), "f32" => Ok(F32), "f64" => Ok(F64), - "str" => Ok(Type::Str), - "bool" => Ok(Type::Bool), - "range" => Ok(Type::Range), - "nil" => Ok(Type::Nil), - "any" => Ok(Type::Any), - "void" => Ok(Type::Void), - _ => Err(Type::Unknown), + "str" => Ok(BaseType::Str), + "bool" => Ok(BaseType::Bool), + "range" => Ok(BaseType::Range), + "nil" => Ok(BaseType::Nil), + "any" => Ok(BaseType::Any), + "void" => Ok(BaseType::Void), + _ => Err(BaseType::Unknown), }, TokenKind::U32(_) => Ok(U32), TokenKind::U64(_) => Ok(U64), @@ -327,109 +241,118 @@ impl TryFrom<&TokenKind> for Type { TokenKind::I128(_) => Ok(I128), TokenKind::F32(_) => Ok(F32), TokenKind::F64(_) => Ok(F64), - _ => Err(Type::Unknown), + _ => Err(BaseType::Unknown), } } } -impl TryFrom<&Expr> for Type { - type Error = Type; +impl TryFrom<&Expr> for ResolvedType { + type Error = ResolvedType; fn try_from(expr: &Expr) -> Result { match expr { Expr::Ident(_, t, _) => Ok(t.to_owned()), - Expr::Literal(l, _) => Ok(Type::from(l)), + Expr::Literal(l, _) => Ok(ResolvedType::from(l)), Expr::Prefix(prefix, rhs, _) => match prefix { - Prefix::Not => Ok(Type::Bool), - _ => Type::try_from(&**rhs), + Prefix::Not => Ok(BaseType::Bool.try_into().unwrap()), + _ => ResolvedType::try_from(&**rhs), }, Expr::Infix(_, lhs, rhs, _) => infer_infix_type(lhs, rhs), - Expr::Index(expr, idx, _) => match Type::try_from(&**expr) { - Ok(Type::List(t)) => match &**idx { - Expr::Index(i, _, _) => Type::try_from(&*i.clone()), - _ => Ok(*t.clone()), + Expr::Index(expr, idx, _) => match ResolvedType::try_from(&**expr) { + Ok(ResolvedType { + base: BaseType::List, + args, + }) => match &**idx { + Expr::Index(i, _, _) => ResolvedType::try_from(&*i.clone()), + _ => Ok(args[0].clone()), }, - Ok(Type::Map(t)) => Ok((*t).1), + Ok(ResolvedType { + base: BaseType::Map, + args, + }) => Ok(args[1].clone()), Ok(t) | Err(t) => Err(t), }, - Expr::Range(_, _, _) => Ok(U64), + Expr::Range(_, _, _) => Ok(U64.try_into().unwrap()), Expr::Fn(f @ Func { .. }) => Ok(f.into()), - Expr::Closure { args, ret_t, .. } => Ok(Type::Fn { - args_t: args.iter().map(|arg| arg.t.to_owned()).collect(), - ret_t: Box::new(ret_t.to_owned()), - uses_self: false, + Expr::Closure { args, ret_t, .. } => Ok(ResolvedType { + base: BaseType::Fn, + args: iter::once(ret_t.clone()) + .chain(args.iter().map(|arg| arg.t.to_owned())) + .collect(), }), Expr::Struct(s, _) => Ok(s.into()), Expr::Call { ret_t, .. } => Ok(ret_t.to_owned()), Expr::MethodCall { ret_t, .. } => Ok(ret_t.to_owned()), Expr::AttrAccess { t, .. } => Ok(t.to_owned()), - _ => Err(Type::Unknown), + _ => Err(ResolvedType::default()), } } } -fn infer_infix_type(lhs: &Expr, rhs: &Expr) -> Result { - let lhs_t = Type::try_from(lhs)?; - let rhs_t = Type::try_from(rhs)?; +fn infer_infix_type(lhs: &Expr, rhs: &Expr) -> Result { + let lhs_t = ResolvedType::try_from(lhs)?; + let rhs_t = ResolvedType::try_from(rhs)?; - if let Type::Num(lhs_num) = &lhs_t - && let Type::Num(rhs_num) = &rhs_t + if let BaseType::Num(lhs_num) = &lhs_t.base + && let BaseType::Num(rhs_num) = &rhs_t.base { - if lhs_t == I32 && rhs_t == U64 || lhs_t == U64 && rhs_t == I32 { - return Ok(I64); + if lhs_t.base == I32 && rhs_t.base == U64 || lhs_t.base == U64 && rhs_t.base == I32 { + return Ok(I64.try_into().unwrap()); } - return Ok(Type::Num(lhs_num.max(rhs_num).clone())); + return Ok(BaseType::Num(lhs_num.max(rhs_num).clone()) + .try_into() + .unwrap()); } - Err(Type::Unknown) + Err(ResolvedType::default()) } -impl From<&Func> for Type { +impl From<&Func> for ResolvedType { fn from(f: &Func) -> Self { - Type::Fn { - args_t: f.args.iter().map(|arg| arg.t.to_owned()).collect(), - ret_t: Box::new(f.ret_t.to_owned()), - uses_self: f.uses_self(), + ResolvedType { + base: BaseType::Fn, + args: iter::once(f.ret_t.clone()) + .chain(f.args.iter().map(|arg| arg.t.to_owned())) + .collect(), } } } -impl From<&StructDecl> for Type { +impl From<&StructDecl> for BaseType { fn from(s: &StructDecl) -> Self { let attrs = s .attrs .iter() - .map(|(p, v)| (p.name.clone(), p.t.clone(), v.is_some())) - .collect(); - let fns = s - .funcs - .iter() - .map(|f| (f.name.clone(), Type::from(f))) + .map(|(p, _)| (p.name.clone(), Box::new(p.t.clone()))) .collect(); - Type::StructDecl(s.name.to_owned(), attrs, fns) + BaseType::Struct(s.name.to_owned(), attrs) } } -impl From<&Struct> for Type { +impl From<&Struct> for ResolvedType { fn from(s: &Struct) -> Self { - Type::Struct( - s.name.to_owned(), - s.state - .iter() - .map(|(k, v)| { - let v_t = Type::try_from(v).unwrap_or_default(); - (k.clone(), v_t) - }) - .collect(), - ) + ResolvedType { + base: BaseType::Struct( + s.name.to_owned(), + s.state + .iter() + .map(|(k, v)| { + let v_t = ResolvedType::try_from(v).unwrap_or_default(); + (k.clone(), Box::new(v_t)) + }) + .collect(), + ), + // We only support arity 0 structs for now + args: vec![], + } } } -impl fmt::Display for Type { +impl fmt::Display for BaseType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Type::Bool => write!(f, "bool"), - Type::Str => write!(f, "str"), + BaseType::Bool => write!(f, "bool"), + BaseType::Str => write!(f, "str"), &U32 => write!(f, "u32"), &U64 => write!(f, "u64"), &I32 => write!(f, "i32"), @@ -437,24 +360,211 @@ impl fmt::Display for Type { &I128 => write!(f, "i128"), &F32 => write!(f, "f32"), &F64 => write!(f, "f64"), - Type::List(t) => write!(f, "list[{t}]"), - Type::Map(t) => write!(f, "map[{}, {}]", t.0, t.1), - Type::Fn { args_t, ret_t, .. } => { - let args_str = args_t + BaseType::List => write!(f, "list"), + BaseType::Map => write!(f, "map"), + BaseType::Fn => write!(f, "Fn"), + BaseType::Range => write!(f, "range"), + BaseType::Aliased(name, _) => write!(f, "{name}"), + BaseType::Struct(name, _) => write!(f, "{name}"), + BaseType::Nil => write!(f, "nil"), + BaseType::Any => write!(f, "any"), + BaseType::Void => write!(f, "void"), + BaseType::Unknown => write!(f, "unknown"), + } + } +} + +impl fmt::Display for ResolvedType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self.base { + base if self.args.is_empty() => write!(f, "{base}"), + BaseType::Fn => { + let args_str = self + .args .iter() + .skip(1) .map(|arg| format!("{arg}")) .collect::>() .join(", "); - write!(f, "Fn({args_str}) -> {ret_t}") + let ret_t = self.args[0].to_string(); + write!(f, "({args_str}) -> {ret_t}") + } + // format the rest like list[str] or map[str, i32] + base => { + let args_str = self + .args + .iter() + .map(|arg| format!("{arg}")) + .collect::>() + .join(", "); + write!(f, "{base}[{args_str}]") + } + } + } +} + +impl ResolvedType { + pub fn is_defined(&self) -> bool { + match self.base { + BaseType::List => self.args[0].is_defined(), + BaseType::Map => self.args[0].is_defined() && self.args[1].is_defined(), + BaseType::Unknown => false, + _ => true, + } + } + + pub fn extract(&self) -> &Self { + match &self.base { + BaseType::List => self.args[0].extract(), + _ => self, + } + } + + pub fn sig(&self, method: &str) -> Option { + match self.base { + BaseType::Num(_) => self.num_sig(method), + BaseType::Str => self.str_sig(method), + BaseType::List => self.list_sig(method, &self.args[0]), + BaseType::Map => self.map_sig(method, &(self.args[0].clone(), self.args[1].clone())), + BaseType::Struct(_, ref attrs) => self.struct_sig(method, attrs), + _ => self.global_sig(method), + } + } + + fn global_sig(&self, method: &str) -> Option { + match method { + "to_s" => Some(MethodSignature::new( + &[], + BaseType::Str.try_into().unwrap(), + false, + )), + _ => None, + } + } + + fn num_sig(&self, method: &str) -> Option { + self.global_sig(method) + } + + fn str_sig(&self, method: &str) -> Option { + match method { + "len" => Some(MethodSignature::new(&[], U64.try_into().unwrap(), false)), + "split" => Some(MethodSignature::new( + &[BaseType::Str.try_into().unwrap()], + ResolvedType { + base: BaseType::List, + args: vec![BaseType::Str.try_into().unwrap()], + }, + false, + )), + _ => None, + } + } + + fn list_sig(&self, method: &str, t: &ResolvedType) -> Option { + match method { + "len" => Some(MethodSignature::new(&[], U64.try_into().unwrap(), false)), + "push" => Some(MethodSignature::new( + std::slice::from_ref(t), + BaseType::Void.try_into().unwrap(), + false, + )), + "pop" => Some(MethodSignature::new(&[], t.clone(), false)), + "get" => Some(MethodSignature::new( + &[I64.try_into().unwrap()], + t.clone(), + false, + )), + "first" => Some(MethodSignature::new(&[], t.clone(), false)), + "last" => Some(MethodSignature::new(&[], t.clone(), false)), + "join" => Some(MethodSignature::new( + &[BaseType::Str.try_into().unwrap()], + BaseType::Str.try_into().unwrap(), + false, + )), + "map" => Some(MethodSignature::new( + &[ResolvedType { + base: BaseType::Fn, + args: vec![t.clone(), t.clone()], + }], + ResolvedType { + base: BaseType::List, + args: vec![BaseType::Unknown.try_into().unwrap()], + }, + false, + )), + "clear" => Some(MethodSignature::new( + &[], + BaseType::Void.try_into().unwrap(), + false, + )), + _ => None, + } + } + + fn map_sig( + &self, + method: &str, + (kt, vt): &(ResolvedType, ResolvedType), + ) -> Option { + match method { + "len" => Some(MethodSignature::new(&[], U64.try_into().unwrap(), false)), + "get" => Some(MethodSignature::new( + std::slice::from_ref(kt), + vt.clone(), + false, + )), + "remove" => Some(MethodSignature::new( + std::slice::from_ref(kt), + BaseType::Void.try_into().unwrap(), + false, + )), + "clear" => Some(MethodSignature::new( + &[], + BaseType::Void.try_into().unwrap(), + false, + )), + _ => None, + } + } + + fn struct_sig( + &self, + method: &str, + attrs: &Vec<(String, Box)>, + ) -> Option { + for (name, t) in attrs { + if name == method && BaseType::Fn == t.base { + return Some(MethodSignature::new( + &t.args[1..], + t.args[0].clone(), + // TODO: store uses_self in the Fn BaseType + false, + )); } - Type::Range => write!(f, "range"), - Type::UserDefined(name, _) => write!(f, "{name}"), - Type::StructDecl(name, _, _) => write!(f, "{name}"), - Type::Struct(name, _) => write!(f, "{name}"), - Type::Nil => write!(f, "nil"), - Type::Any => write!(f, "any"), - Type::Void => write!(f, "void"), - Type::Unknown => write!(f, "unknown"), + } + None + } +} + +impl From for TypeIdentifierTree { + fn from(resolved: ResolvedType) -> Self { + TypeIdentifierTree { + root: resolved.base.to_string(), + children: resolved + .args + .into_iter() + .map(TypeIdentifierTree::from) + .collect(), + } + } +} + +impl From<&str> for TypeIdentifierTree { + fn from(root: &str) -> Self { + TypeIdentifierTree { + root: root.into(), + children: vec![], } } } diff --git a/coal-core/src/parser/error.rs b/coal-core/src/parser/error.rs index a8448d9..14c3ae4 100644 --- a/coal-core/src/parser/error.rs +++ b/coal-core/src/parser/error.rs @@ -2,7 +2,7 @@ use std::fmt; use thiserror::Error; -use crate::{Span, TokenKind, Type}; +use crate::{ResolvedType, Span, TokenKind}; #[derive(Clone, Debug)] pub struct ParserError { @@ -38,7 +38,7 @@ pub enum ParserErrorKind { InvalidArgumentsLength(usize, usize), #[error("the type `{0}` cannot be indexed by `{1}`")] - InvalidIndex(Box, Box), + InvalidIndex(Box, Box), #[error("`self` is reserved for the first argument in a method argument list")] InvalidSelfPlacement, @@ -47,7 +47,7 @@ pub enum ParserErrorKind { InvalidStructAttr(String), #[error("method not found: `{0}.{1}()`")] - MethodNotFound(Box, String), + MethodNotFound(Box, String), #[error("`if` may be missing an `else` clause")] MissingElseClause, @@ -56,7 +56,7 @@ pub enum ParserErrorKind { NotFound(String), #[error("type `{0}` is not indexable")] - NonIndexableType(Box), + NonIndexableType(Box), #[error("syntax error: `{0}`")] SyntaxError(TokenKind), @@ -65,11 +65,11 @@ pub enum ParserErrorKind { TypeAnnotationsNeeded, #[error("type mismatch. expected: `{0}`, got: `{1}`")] - TypeMismatch(Box, Box), + TypeMismatch(Box, Box), #[error("unexpected token: `{0}`, expected: `{1}`")] UnexpectedToken(TokenKind, TokenKind), #[error("map key not hashable: type `{0}`")] - UnhashableMapKey(Box), + UnhashableMapKey(Box), } diff --git a/coal-core/src/parser/mod.rs b/coal-core/src/parser/mod.rs index 40a4673..834e07f 100644 --- a/coal-core/src/parser/mod.rs +++ b/coal-core/src/parser/mod.rs @@ -7,11 +7,11 @@ mod warning; #[cfg(test)] mod tests; -use std::{cell::RefCell, collections::HashSet, rc::Rc}; +use std::{cell::RefCell, collections::HashSet, iter, rc::Rc}; use crate::{ - Comment, ElifExpr, Expr, Func, Ident, Infix, Lexer, List, Literal, Map, Param, Prefix, Stmt, - Struct, StructDecl, Token, TokenKind, Type, U32, + BaseType, Comment, ElifExpr, Expr, Func, Ident, Infix, Lexer, List, Literal, Map, Param, + Prefix, ResolvedType, Stmt, Struct, StructDecl, Token, TokenKind, U32, }; pub use error::{ParserError, ParserErrorKind}; @@ -176,15 +176,14 @@ impl Parser { )); } - fn lookup_expr_type(&mut self, expr: &Expr) -> Option { - if let Expr::Ident(Ident(name), _, _) = &expr { - if let Some(t) = self.symbol_table.borrow().get(name) { - return Some(t.clone()); - } else if let Some(t) = self.symbol_table.borrow().get_ret_t(name) { - return Some(t.clone()); - } + fn lookup_expr_type(&mut self, expr: &Expr) -> Option { + if let Expr::Ident(Ident(name), _, _) = &expr + && let Some(t) = self.symbol_table.borrow().get(name) + { + return Some(t); } - Type::try_from(expr).ok() + + ResolvedType::try_from(expr).ok() } fn parse_block(&mut self) -> Vec { @@ -281,7 +280,7 @@ impl Parser { self.advance(); let mut expr = self.parse_expr(Precedence::Lowest)?; - let inf_t = Type::try_from(&expr); + let inf_t = ResolvedType::try_from(&expr); match &mut expr { Expr::Ident(Ident(name), _, _) => { @@ -295,11 +294,17 @@ impl Parser { // Empty composite literals are unknown when they're parsed, and need type annotations // Update their types using the declared type, or err match &declared_t { - Some(Type::List(t)) => { - l.set_type(*t.clone(), None); + Some(ResolvedType { + base: BaseType::List, + args, + }) if args.len() == 1 => { + l.set_type(args[0].clone(), None); } - Some(Type::Map(t)) => { - l.set_type(t.0.clone(), Some(t.1.clone())); + Some(ResolvedType { + base: BaseType::Map, + args, + }) if args.len() == 2 => { + l.set_type(args[0].clone(), Some(args[1].clone())); } _ => self.errors.push(ParserError::new( ParserErrorKind::TypeAnnotationsNeeded, @@ -315,14 +320,17 @@ impl Parser { let decl_tx = dt.extract(); let inf_tx = inf_t.extract(); - if !decl_tx.is_numeric() || !inf_tx.is_numeric() { - if inf_tx.is_composite() { + if !decl_tx.base.is_numeric() || !inf_tx.base.is_numeric() { + if inf_tx.base.is_composite() { declared_t = Some(dt.clone()); } else { expr = expr.cast(dt); declared_t = Some(inf_tx.clone()); } - } else if decl_tx.is_numeric() && inf_tx.is_numeric() && decl_tx != inf_tx { + } else if decl_tx.base.is_numeric() + && inf_tx.base.is_numeric() + && decl_tx != inf_tx + { expr = expr.cast(decl_tx); } } else { @@ -337,15 +345,10 @@ impl Parser { ParserErrorKind::TypeAnnotationsNeeded, ident_span, )); - Type::Unknown + ResolvedType::default() }); self.symbol_table.borrow().set(ident.name(), t.clone()); - if let Type::Fn { ret_t, .. } = &t { - self.symbol_table - .borrow() - .set_ret_t(ident.name(), *ret_t.clone()); - } self.consume_next(TokenKind::Semicolon); @@ -376,7 +379,7 @@ impl Parser { let rhs = self.parse_expr(Precedence::Lowest)?; let rhs_t = self.lookup_expr_type(&rhs)?; - if lhs_t != rhs_t && !(lhs_t.is_numeric() && rhs_t.is_numeric()) { + if lhs_t != rhs_t && !(lhs_t.base.is_numeric() && rhs_t.base.is_numeric()) { self.errors.push(ParserError::new( ParserErrorKind::TypeMismatch(lhs_t.into(), rhs_t.into()), rhs.span(), @@ -447,7 +450,7 @@ impl Parser { }; if let Some(v) = &default_val { - let val_t = Type::try_from(v).ok()?; + let val_t = ResolvedType::try_from(v).ok()?; if val_t != t { self.errors.push(ParserError::new( ParserErrorKind::TypeMismatch(t.clone().into(), val_t.into()), @@ -491,7 +494,14 @@ impl Parser { self.consume_newlines(); } - self.symbol_table.borrow().set(ident.name(), Type::from(&s)); + self.symbol_table.borrow().set( + ident.name(), + ResolvedType { + base: BaseType::from(&s), + // NOTE: We don't currently support generic structs + args: vec![], + }, + ); Some(Stmt::StructDecl(s, (start, self.curr_tok.span.1))) } @@ -503,7 +513,10 @@ impl Parser { TokenKind::Ident(name) => { let ident_t = self.symbol_table.borrow().get(name); match ident_t { - Some(Type::StructDecl(name, attrs, _)) => self.parse_struct_expr(&name, &attrs), + Some(ResolvedType { + base: BaseType::Struct(name, attrs), + args: _, + }) => self.parse_struct_expr(&name, &attrs), Some(t) => Some(Expr::Ident(Ident::from(name.as_str()), t, *span)), None => { self.errors.push(ParserError::new( @@ -656,7 +669,7 @@ impl Parser { let (start, _) = lhs.span(); self.advance(); // '[' - let lhs_t = Type::try_from(lhs).ok()?; + let lhs_t = ResolvedType::try_from(lhs).ok()?; if !lhs.is_indexable() { let (_, end) = self.curr_tok.span; @@ -667,16 +680,16 @@ impl Parser { } let idx = self.parse_expr(Precedence::Lowest)?; - let idx_t = Type::try_from(&idx).ok()?; + let idx_t = ResolvedType::try_from(&idx).ok()?; - match lhs_t { - Type::List(_) if !idx_t.is_int() => { + match lhs_t.base { + BaseType::List if !idx_t.base.is_int() => { self.errors.push(ParserError::new( ParserErrorKind::InvalidIndex(lhs_t.into(), idx_t.into()), idx.span(), )); } - Type::Map(ref t) if !idx_t.is_hashable() || t.0 != idx_t => { + BaseType::Map if !idx_t.base.is_hashable() || lhs_t.args[0] != idx_t => { self.errors.push(ParserError::new( ParserErrorKind::InvalidIndex(lhs_t.into(), idx_t.into()), idx.span(), @@ -720,13 +733,13 @@ impl Parser { if self.next_tok.kind == end_tok { self.advance(); - return Some(List::new(&data, Type::Unknown)); + return Some(List::new(&data, BaseType::Unknown.try_into().unwrap())); } self.advance(); let expr = self.parse_expr(Precedence::Lowest)?; - let t = Type::try_from(&expr).unwrap_or_default(); + let t = ResolvedType::try_from(&expr).unwrap_or_default(); data.push(expr.clone()); if self.next_tok.kind == TokenKind::Semicolon { @@ -749,7 +762,7 @@ impl Parser { let (start, _) = self.curr_tok.span; let expr = self.parse_expr(Precedence::Lowest)?; - match Type::try_from(&expr) { + match ResolvedType::try_from(&expr) { Ok(curr_t) => { if curr_t != t { self.errors.push(ParserError::new( @@ -777,7 +790,7 @@ impl Parser { Some(List::new(&data, t)) } - fn parse_repeat_expr_list(&mut self, expr: &Expr, t: &Type) -> Option { + fn parse_repeat_expr_list(&mut self, expr: &Expr, t: &ResolvedType) -> Option { let rhs = self.parse_expr(Precedence::Lowest)?; let (data, repeat) = match &rhs { Expr::Literal(Literal::U32(i), _) => { @@ -793,9 +806,12 @@ impl Parser { (vec![expr.clone(); *i as usize], Some(Box::new(rhs.clone()))) } Expr::Call { ret_t, .. } | Expr::MethodCall { ret_t, .. } => { - if !ret_t.is_numeric() { + if !ret_t.base.is_numeric() { self.errors.push(ParserError::new( - ParserErrorKind::TypeMismatch(U32.into(), t.clone().into()), + ParserErrorKind::TypeMismatch( + ResolvedType::try_from(U32).unwrap().into(), + t.clone().into(), + ), self.next_tok.span, )); (vec![], None) @@ -805,7 +821,10 @@ impl Parser { } _ => { self.errors.push(ParserError::new( - ParserErrorKind::TypeMismatch(U32.into(), t.clone().into()), + ParserErrorKind::TypeMismatch( + ResolvedType::try_from(U32).unwrap().into(), + t.clone().into(), + ), self.next_tok.span, )); (vec![], None) @@ -826,7 +845,10 @@ impl Parser { self.advance(); return Some(Map { data, - t: (Type::Unknown, Type::Unknown), + t: ( + BaseType::Unknown.try_into().unwrap(), + BaseType::Unknown.try_into().unwrap(), + ), }); } @@ -835,13 +857,13 @@ impl Parser { let mut keys = HashSet::new(); let k = self.parse_expr(Precedence::Lowest)?; - let kt = Type::try_from(&k).unwrap_or_default(); + let kt = ResolvedType::try_from(&k).unwrap_or_default(); self.expect_next(TokenKind::Colon)?; self.advance(); let v = self.parse_expr(Precedence::Lowest)?; - let vt = Type::try_from(&v).unwrap_or_default(); + let vt = ResolvedType::try_from(&v).unwrap_or_default(); keys.insert(k.to_string()); data.push((k, v)); @@ -863,11 +885,11 @@ impl Parser { )); } - if kt != Type::try_from(&k).unwrap_or_default() { + if kt != ResolvedType::try_from(&k).unwrap_or_default() { self.errors.push(ParserError::new( ParserErrorKind::TypeMismatch( kt.into(), - Type::try_from(&k).unwrap_or_default().into(), + ResolvedType::try_from(&k).unwrap_or_default().into(), ), (start, self.curr_tok.span.1), )); @@ -879,11 +901,11 @@ impl Parser { self.advance(); let v = self.parse_expr(Precedence::Lowest)?; - if vt != Type::try_from(&v).unwrap_or_default() { + if vt != ResolvedType::try_from(&v).unwrap_or_default() { self.errors.push(ParserError::new( ParserErrorKind::TypeMismatch( vt.into(), - Type::try_from(&v).unwrap_or_default().into(), + ResolvedType::try_from(&v).unwrap_or_default().into(), ), (start, self.curr_tok.span.1), )); @@ -916,8 +938,8 @@ impl Parser { args = self.parse_expr_list(TokenKind::Rparen)?; } - let mut lhs_t = Type::try_from(&lhs).unwrap_or_default(); - if let Type::Struct(name, _) = &lhs_t + let mut lhs_t = ResolvedType::try_from(&lhs).unwrap_or_default(); + if let BaseType::Struct(name, _) = &lhs_t.base && let Some(t) = self.symbol_table.borrow().get(name) { lhs_t = t.clone(); @@ -962,10 +984,23 @@ impl Parser { let attr = self.curr_tok.kind.to_string(); match &lhs { - Expr::Ident(s, Type::Struct(sname, _), _) => { - if let Some(Type::StructDecl(_, attrs, _)) = self.symbol_table.borrow().get(sname) { - let t = match attrs.iter().find(|(n, _, _)| *n == attr) { - Some((_, t, _)) => t.clone(), + Expr::Ident( + s, + ResolvedType { + base: BaseType::Struct(sname, _), + .. + }, + _, + ) => { + if let Some(BaseType::Struct(_, attrs)) = self + .symbol_table + .borrow() + .type_symbol_table + .borrow() + .get(sname) + { + let t = match attrs.iter().find(|(n, _)| *n == attr) { + Some((_, t)) => t.clone(), None => { self.errors.push(ParserError::new( ParserErrorKind::InvalidStructAttr(attr.to_owned()), @@ -979,7 +1014,7 @@ impl Parser { return Some(Expr::AttrAccess { lhs: Box::new(lhs), name: attr, - t, + t: *t, span, }); } else { @@ -990,12 +1025,17 @@ impl Parser { } } Expr::AttrAccess { .. } => { - if let Type::Struct(sname, _) = Type::try_from(&lhs).unwrap_or_default() - && let Some(Type::StructDecl(_, attrs, _)) = - self.symbol_table.borrow().get(&sname) + if let BaseType::Struct(sname, _) = + ResolvedType::try_from(&lhs).unwrap_or_default().base + && let Some(BaseType::Struct(_, attrs)) = self + .symbol_table + .borrow() + .type_symbol_table + .borrow() + .get(&sname) { - let t = match attrs.iter().find(|(n, _, _)| *n == attr) { - Some((_, t, _)) => t.clone(), + let t = match attrs.iter().find(|(n, _)| *n == attr) { + Some((_, t)) => t.clone(), None => { self.errors.push(ParserError::new( ParserErrorKind::InvalidStructAttr(attr.to_owned()), @@ -1009,7 +1049,7 @@ impl Parser { return Some(Expr::AttrAccess { lhs: Box::new(lhs), name: attr, - t, + t: *t, span, }); } @@ -1128,7 +1168,7 @@ impl Parser { self.expect_next(TokenKind::Lbrace)?; self.advance(); - let iter_t = Type::try_from(&expr) + let iter_t = ResolvedType::try_from(&expr) .map(|t| t.extract().to_owned()) .unwrap_or_default(); @@ -1166,9 +1206,6 @@ impl Parser { let st = SymbolTable::from(Rc::clone(&self.symbol_table)); for arg in args.iter() { st.set(arg.name.clone(), arg.t.clone()); - if let Type::Fn { ret_t, .. } = &arg.t { - st.set_ret_t(arg.name.clone(), *ret_t.clone()); - } } let (body, ret_stmts) = self.parse_fn_block(Rc::new(RefCell::new(st))); @@ -1177,7 +1214,10 @@ impl Parser { let ret_t = if let Some(dt) = declared_ret_t { if ret_stmts.is_empty() { self.errors.push(ParserError::new( - ParserErrorKind::TypeMismatch(dt.clone().into(), Type::Void.into()), + ParserErrorKind::TypeMismatch( + dt.clone().into(), + ResolvedType::try_from(BaseType::Void).unwrap().into(), + ), (start, self.curr_tok.span.1), )); } @@ -1190,13 +1230,9 @@ impl Parser { dt } else { - Type::from(&body) + ResolvedType::from(&body) }; - self.symbol_table - .borrow() - .set_ret_t(ident.name(), ret_t.clone()); - let func = Func { name: ident.name(), args, @@ -1221,14 +1257,11 @@ impl Parser { let st = SymbolTable::from(Rc::clone(&self.symbol_table)); for arg in args.iter() { st.set(arg.name.clone(), arg.t.clone()); - if let Type::Fn { ret_t, .. } = &arg.t { - st.set_ret_t(arg.name.clone(), *ret_t.clone()); - } } let (body, _) = self.parse_fn_block(Rc::new(RefCell::new(st))); self.check_unreachable(&body); - let ret_t = Type::from(&body); + let ret_t = ResolvedType::from(&body); Some(Expr::Closure { args, @@ -1238,7 +1271,11 @@ impl Parser { }) } - fn parse_struct_expr(&mut self, name: &str, attrs: &[(String, Type, bool)]) -> Option { + fn parse_struct_expr( + &mut self, + name: &str, + attrs: &[(String, Box)], + ) -> Option { let (start, _) = self.curr_tok.span; self.advance(); self.consume(TokenKind::Lbrace); @@ -1247,18 +1284,18 @@ impl Parser { while !matches!(self.curr_tok.kind, TokenKind::Rbrace | TokenKind::EOF) { if let TokenKind::Ident(ident) = &self.curr_tok.kind { let ident = ident.clone(); - if let Some((_, expected_t, _)) = - attrs.iter().find(|(attr_name, _, _)| *attr_name == ident) + if let Some((_, expected_t)) = + attrs.iter().find(|(attr_name, _)| *attr_name == ident) { self.expect_next(TokenKind::Colon)?; self.advance(); let val = self.parse_expr(Precedence::Lowest)?.cast(expected_t); - let val_t = Type::try_from(&val).ok()?; + let val_t = ResolvedType::try_from(&val).ok()?; - if !val_t.partial_eq(expected_t) { + if val_t != **expected_t { self.errors.push(ParserError::new( - ParserErrorKind::TypeMismatch(expected_t.clone().into(), val_t.into()), + ParserErrorKind::TypeMismatch(expected_t.clone(), val_t.into()), val.span(), )); } @@ -1276,8 +1313,8 @@ impl Parser { self.advance(); } - for (attr, _, default_val) in attrs { - if !state.iter().any(|(name, _)| name == attr) && !default_val { + for (attr, _) in attrs { + if !state.iter().any(|(name, _)| name == attr) { self.errors.push(ParserError::new( ParserErrorKind::AttrMissing(attr.to_owned()), (start, self.curr_tok.span.1), @@ -1308,7 +1345,11 @@ impl Parser { match ctx { FunctionContext::Struct(struct_decl) => args.push(Param { name: "self".into(), - t: Type::from(struct_decl).into_struct_t().unwrap(), + t: ResolvedType { + base: BaseType::from(struct_decl), + // NOTE: We do not currently support generic structs + args: vec![], + }, }), FunctionContext::Standard => unreachable!(), } @@ -1333,7 +1374,7 @@ impl Parser { Some(args) } - fn parse_ret_type(&mut self) -> Option { + fn parse_ret_type(&mut self) -> Option { match self.curr_tok.kind { TokenKind::Lbrace | TokenKind::EQ => None, _ => { @@ -1345,22 +1386,24 @@ impl Parser { } } - fn parse_type(&mut self) -> Option { + fn parse_type(&mut self) -> Option { match &self.curr_tok.kind { TokenKind::Ident(s) => match s.as_str() { "Fn" => self.parse_fn_type(), "list" => self.parse_list_type(), "map" => self.parse_map_type(), - _ => match self.symbol_table.borrow().get(s) { - Some(t) if matches!(t, Type::StructDecl(_, _, _)) => t.into_struct_t(), - _ => Type::try_from(&self.curr_tok.kind).ok(), + _ => match self.symbol_table.borrow().type_symbol_table.borrow().get(s) { + Some(t) => Some(t.try_into().unwrap()), + _ => BaseType::try_from(&self.curr_tok.kind) + .ok() + .and_then(|t| t.try_into().ok()), }, }, _ => None, } } - fn parse_fn_type(&mut self) -> Option { + fn parse_fn_type(&mut self) -> Option { self.advance(); // 'Fn' self.consume(TokenKind::Lparen); @@ -1379,28 +1422,29 @@ impl Parser { self.advance(); self.parse_ret_type().unwrap_or_default() } - _ => Type::Void, + _ => BaseType::Void.try_into().unwrap(), }; - Some(Type::Fn { - args_t: args, - ret_t: Box::new(ret_t), - // There is no way to indicate a method in a function type signature. - uses_self: false, + Some(ResolvedType { + base: BaseType::Fn, + args: iter::once(ret_t).chain(args).collect(), }) } - fn parse_list_type(&mut self) -> Option { + fn parse_list_type(&mut self) -> Option { self.advance(); // 'list' self.consume(TokenKind::Lbracket); let t = self.parse_type().unwrap_or_default(); self.consume_next(TokenKind::Rbracket); - Some(Type::List(Box::new(t))) + Some(ResolvedType { + base: BaseType::List, + args: vec![t], + }) } - fn parse_map_type(&mut self) -> Option { + fn parse_map_type(&mut self) -> Option { self.advance(); // 'map' self.consume(TokenKind::Lbracket); @@ -1411,7 +1455,10 @@ impl Parser { let vt = self.parse_type().unwrap_or_default(); self.consume_next(TokenKind::Rbracket); - Some(Type::Map(Box::new((kt, vt)))) + Some(ResolvedType { + base: BaseType::Map, + args: vec![kt, vt], + }) } fn check_unreachable(&mut self, stmts: &[Stmt]) { diff --git a/coal-core/src/parser/symbol_table.rs b/coal-core/src/parser/symbol_table.rs index 5689021..7ecd1cf 100644 --- a/coal-core/src/parser/symbol_table.rs +++ b/coal-core/src/parser/symbol_table.rs @@ -1,21 +1,129 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; -use crate::{Type, builtin_types}; +use crate::{BaseType, Num, ResolvedType, TypeIdentifier, TypeIdentifierTree, builtin_types}; + +#[derive(Clone, Debug, PartialEq)] +pub struct TypeSymbolTable { + pub scope: String, + // key: Cat + // value: Struct { "Cat", ["name", (string type)], [meow func] } + pub store: RefCell>, + pub outer: Option>>, +} + +impl TypeSymbolTable { + pub fn new( + store: HashMap, + outer: Rc>, + ) -> Self { + TypeSymbolTable { + scope: String::from("global"), + store: RefCell::new(store), + outer: Some(outer), + } + } + + pub fn get(&self, key: &str) -> Option { + self.store.borrow().get(key).cloned().or_else(|| { + self.outer + .as_ref() + .and_then(|outer| outer.borrow().get(key)) + }) + } + + pub fn set(&self, key: String, value: BaseType) { + self.store.borrow_mut().insert(key, value); + } + + pub fn resolve(&self, type_identifier_tree: &TypeIdentifierTree) -> Option { + let resolved_children: Vec = type_identifier_tree + .children + .iter() + .map(|child| self.resolve(child)) + .collect::>>()?; + + // TODO: factor out builtin types + arities to a separate file + match (type_identifier_tree.root.as_str(), resolved_children.len()) { + ("list", 1) => Some(ResolvedType { + base: BaseType::List, + args: vec![resolved_children[0].clone()], + }), + ("map", 2) => Some(ResolvedType { + base: BaseType::Map, + args: vec![resolved_children[0].clone(), resolved_children[1].clone()], + }), + ("fn", _) => Some(ResolvedType { + base: BaseType::Fn, + args: resolved_children, + }), + (ident, _) => self.get(ident).map(|base| ResolvedType { + base, + args: resolved_children, + }), + } + } +} + +impl Default for TypeSymbolTable { + fn default() -> Self { + TypeSymbolTable { + scope: String::from("global"), + store: RefCell::new(HashMap::from([ + ("bool".to_string(), BaseType::Bool), + ("str".to_string(), BaseType::Str), + ("u32".to_string(), BaseType::Num(Num::U32)), + ("u64".to_string(), BaseType::Num(Num::U64)), + ("i32".to_string(), BaseType::Num(Num::I32)), + ("i64".to_string(), BaseType::Num(Num::I64)), + ("i128".to_string(), BaseType::Num(Num::I128)), + ("f32".to_string(), BaseType::Num(Num::F32)), + ("f64".to_string(), BaseType::Num(Num::F64)), + ("range".to_string(), BaseType::Range), + ("nil".to_string(), BaseType::Nil), + ("any".to_string(), BaseType::Any), + ("void".to_string(), BaseType::Void), + ("unknown".to_string(), BaseType::Unknown), + ])), + outer: None, + } + } +} + +impl From>> for TypeSymbolTable { + fn from(outer: Rc>) -> Self { + let TypeSymbolTable { scope, .. } = outer.borrow().clone(); + Self { + scope, + store: RefCell::new(HashMap::new()), + outer: Some(outer), + } + } +} /// Keys of shape `__name__` are return types for functions #[derive(Clone, Debug, PartialEq)] pub struct SymbolTable { pub scope: String, - pub store: RefCell>, + // key: mycats: List + pub store: RefCell>, + pub type_symbol_table: Rc>, pub outer: Option>>, } impl SymbolTable { - pub fn new(store: HashMap, outer: Rc>) -> Self { + pub fn new( + store: HashMap, + outer: Rc>, + ) -> Self { SymbolTable { scope: String::from("global"), store: RefCell::new(store), - outer: Some(outer), + type_symbol_table: Rc::new(RefCell::new(TypeSymbolTable { + scope: String::from("global"), + store: RefCell::new(HashMap::new()), + outer: Some(Rc::clone(&outer.borrow().type_symbol_table)), + })), + outer: Some(Rc::clone(&outer)), } } @@ -28,24 +136,30 @@ impl SymbolTable { .is_some() } - pub fn get(&self, key: &str) -> Option { - self.store.borrow().get(key).cloned().or_else(|| { - self.outer - .as_ref() - .and_then(|outer| outer.borrow().get(key)) - }) - } - - pub fn get_ret_t(&self, key: &str) -> Option { - self.get(&format!("__{key}__")) + pub fn get(&self, key: &str) -> Option { + self.store + .borrow() + .get(key) + .and_then(|v| self.type_symbol_table.borrow().resolve(v)) + .or_else(|| { + self.outer + .as_ref() + .and_then(|outer| outer.borrow().get(key)) + }) } - pub fn set(&self, key: String, value: Type) { - self.store.borrow_mut().insert(key, value); + pub fn get_ret_t(&self, key: &str) -> Option { + self.get(key).and_then(|resolved_type| { + resolved_type + .args + .first() + .cloned() + .or_else(|| Some(ResolvedType::default())) + }) } - pub fn set_ret_t(&self, key: String, value: Type) { - self.set(format!("__{key}__"), value); + pub fn set(&self, key: String, value: ResolvedType) { + self.store.borrow_mut().insert(key, value.into()); } } @@ -54,6 +168,7 @@ impl Default for SymbolTable { SymbolTable { scope: String::from("global"), store: RefCell::new(builtin_types()), + type_symbol_table: Rc::new(RefCell::new(TypeSymbolTable::default())), outer: None, } } @@ -65,7 +180,10 @@ impl From>> for SymbolTable { Self { scope, store: RefCell::new(builtin_types()), - outer: Some(outer), + type_symbol_table: Rc::new(RefCell::new(TypeSymbolTable::from(Rc::clone( + &outer.borrow().type_symbol_table, + )))), + outer: Some(Rc::clone(&outer)), } } } diff --git a/coal-core/src/parser/tests.rs b/coal-core/src/parser/tests.rs index 5c8a2ba..a9e7836 100644 --- a/coal-core/src/parser/tests.rs +++ b/coal-core/src/parser/tests.rs @@ -91,7 +91,7 @@ fn test_parse_let_statements_inference() { ), Stmt::Let( Ident(String::from("z")), - Type::Str, + BaseType::Str, Expr::Literal(Literal::Str(String::from("hello")), ((3, 9), (3, 15))), ), ]; @@ -132,11 +132,11 @@ fn test_parse_assign_statements() { let expected = vec![ Stmt::Let( Ident::from("y"), - Type::Str, + BaseType::Str, Expr::Literal(Literal::Str(String::from("")), ((1, 9), (1, 10))), ), Stmt::Assign( - Expr::Ident(Ident::from("y"), Type::Str, ((2, 1), (2, 1))), + Expr::Ident(Ident::from("y"), BaseType::Str, ((2, 1), (2, 1))), Expr::Literal(Literal::from("foo"), ((2, 5), (2, 9))), ), ]; @@ -149,11 +149,11 @@ fn test_parse_assign_statements() { let expected = vec![ Stmt::Let( Ident::from("z"), - Type::Bool, + BaseType::Bool, Expr::Literal(Literal::Bool(false), ((1, 9), (1, 13))), ), Stmt::Assign( - Expr::Ident(Ident::from("z"), Type::Bool, ((2, 1), (2, 1))), + Expr::Ident(Ident::from("z"), BaseType::Bool, ((2, 1), (2, 1))), Expr::Literal(Literal::Bool(true), ((2, 5), (2, 8))), ), ]; @@ -167,7 +167,7 @@ fn test_parse_assign_statements() { Expr::Index( Box::new(Expr::Ident( Ident::from("x"), - Type::List(Box::new(I32)), + BaseType::List(Box::new(I32)), ((2, 1), (2, 1)), )), Box::new(Expr::Literal(Literal::I32(1), ((2, 3), (2, 3)))), @@ -207,17 +207,17 @@ fn test_parse_identifier_expressions() { let expected = vec![ Stmt::Expr(Expr::Ident( Ident::from("foo"), - Type::Unknown, + BaseType::Unknown, ((1, 1), (1, 3)), )), Stmt::Expr(Expr::Ident( Ident::from("bar"), - Type::Unknown, + BaseType::Unknown, ((1, 6), (1, 8)), )), Stmt::Expr(Expr::Ident( Ident::from("foobar"), - Type::Unknown, + BaseType::Unknown, ((1, 11), (1, 16)), )), ]; @@ -679,19 +679,19 @@ fn test_parse_if_expression() { Infix::LT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((1, 4), (1, 4)), )), Box::new(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((1, 8), (1, 8)), )), ((1, 4), (1, 8)), )), then: vec![Stmt::Return(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((1, 19), (1, 19)), ))], elifs: vec![], @@ -719,12 +719,12 @@ fn test_parse_nested_if_expression() { Infix::LT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((1, 4), (1, 4)), )), Box::new(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((1, 8), (1, 8)), )), ((1, 4), (1, 8)), @@ -735,7 +735,7 @@ fn test_parse_nested_if_expression() { Infix::GT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((2, 4), (2, 4)), )), Box::new(Expr::Literal(Literal::I32(1), ((2, 8), (2, 8)))), @@ -743,7 +743,7 @@ fn test_parse_nested_if_expression() { )), then: vec![Stmt::Return(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((3, 8), (3, 8)), ))], elifs: vec![], @@ -752,14 +752,14 @@ fn test_parse_nested_if_expression() { }), Stmt::Return(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((5, 8), (5, 8)), )), ], elifs: vec![], alt: Some(vec![Stmt::Return(Expr::Ident( Ident::from("z"), - Type::Unknown, + BaseType::Unknown, ((7, 8), (7, 8)), ))]), span: ((1, 1), (8, 1)), @@ -786,19 +786,19 @@ fn test_parse_elif_expression() { Infix::LT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((1, 4), (1, 4)), )), Box::new(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((1, 8), (1, 8)), )), ((1, 4), (1, 8)), )), then: vec![Stmt::Return(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((2, 8), (2, 8)), ))], elifs: vec![ @@ -807,19 +807,19 @@ fn test_parse_elif_expression() { Infix::GT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((3, 8), (3, 8)), )), Box::new(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((3, 12), (3, 12)), )), ((3, 8), (3, 12)), )), then: vec![Stmt::Return(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((4, 8), (4, 8)), ))], }, @@ -828,7 +828,7 @@ fn test_parse_elif_expression() { Infix::GT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((5, 8), (5, 8)), )), Box::new(Expr::Literal(Literal::I32(1), ((5, 12), (5, 12)))), @@ -842,7 +842,7 @@ fn test_parse_elif_expression() { ], alt: Some(vec![Stmt::Return(Expr::Ident( Ident::from("z"), - Type::Unknown, + BaseType::Unknown, ((8, 8), (8, 8)), ))]), span: ((1, 1), (9, 1)), @@ -860,12 +860,12 @@ fn test_parse_while_expression() { Infix::LT, Box::new(Expr::Ident( Ident::from("x"), - Type::Unknown, + BaseType::Unknown, ((1, 7), (1, 7)), )), Box::new(Expr::Ident( Ident::from("y"), - Type::Unknown, + BaseType::Unknown, ((1, 11), (1, 11)), )), ((1, 7), (1, 11)), @@ -889,7 +889,7 @@ fn test_parse_function_expressions() { Stmt::Expr(Expr::Fn(Func { name: String::from("foo"), args: vec![], - ret_t: Type::Void, + ret_t: BaseType::Void, body: vec![], span: ((1, 1), (1, 11)), })), @@ -951,7 +951,7 @@ fn test_parse_function_expressions() { Param::new("x", U32), Param::new( "f", - Type::Fn { + BaseType::Fn { args_t: vec![U32, U32], ret_t: Box::new(U32), uses_self: false, @@ -991,7 +991,7 @@ fn test_parse_closure_expressions() { Stmt::Expr(Expr::Closure { args: vec![], body: vec![], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((1, 1), (1, 5)), }), ), @@ -1000,7 +1000,7 @@ fn test_parse_closure_expressions() { Stmt::Expr(Expr::Closure { args: vec![Param::new("x", I32)], body: vec![], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((1, 1), (1, 11)), }), ), @@ -1056,7 +1056,7 @@ fn test_parse_closure_expressions() { ret_t: I32, span: ((1, 12), (3, 1)), }], - ret_t: Type::List(Box::new(Type::Unknown)), + ret_t: BaseType::List(Box::new(BaseType::Unknown)), span: ((1, 1), (3, 2)), }), ), @@ -1070,7 +1070,7 @@ fn test_parse_closure_expressions() { Stmt::Expr(Expr::MethodCall { lhs: Box::new(Expr::Ident( Ident::from("x"), - Type::List(Box::new(I32)), + BaseType::List(Box::new(I32)), ((2, 1), (2, 1)), )), name: String::from("map"), @@ -1083,7 +1083,7 @@ fn test_parse_closure_expressions() { ret_t: I32, span: ((2, 7), (4, 1)), }], - ret_t: Type::List(Box::new(Type::Unknown)), + ret_t: BaseType::List(Box::new(BaseType::Unknown)), span: ((2, 1), (4, 2)), }), ), @@ -1110,7 +1110,7 @@ fn test_parse_call_expression() { ((1, 15), (1, 19)), ), ], - ret_t: Type::Unknown, + ret_t: BaseType::Unknown, span: ((1, 1), (1, 20)), }); let actual = Parser::from(input).parse(); @@ -1136,14 +1136,14 @@ fn test_parse_infer_type_from_fn() { let input = "fn foo() -> u32 { return 0 }; let x = foo;"; let expected = Stmt::Let( Ident(String::from("x")), - Type::Fn { + BaseType::Fn { args_t: vec![], ret_t: Box::new(U32), uses_self: false, }, Expr::Ident( Ident::from("foo"), - Type::Fn { + BaseType::Fn { args_t: vec![], ret_t: Box::new(U32), uses_self: false, @@ -1255,10 +1255,10 @@ fn test_parse_lists() { &[Expr::Call { name: String::from("foo"), args: vec![], - ret_t: Type::Unknown, + ret_t: BaseType::Unknown, span: ((1, 2), (1, 6)), }], - Type::Unknown, + BaseType::Unknown, )), ((1, 1), (1, 7)), )), @@ -1275,10 +1275,10 @@ fn test_parse_lists() { &[Expr::Call { name: String::from("foo"), args: vec![], - ret_t: Type::Str, + ret_t: BaseType::Str, span: ((4, 2), (4, 6)), }], - Type::Str, + BaseType::Str, )), ((4, 1), (4, 7)), )), @@ -1309,7 +1309,7 @@ fn test_parse_lists() { Expr::Index( Box::new(Expr::Ident( Ident::from("x"), - Type::List(Box::new(I32)), + BaseType::List(Box::new(I32)), ((1, 21), (1, 21)), )), Box::new(Expr::Literal(Literal::I32(1), ((1, 23), (1, 23)))), @@ -1326,7 +1326,7 @@ fn test_parse_lists() { "let x: list[i32] = []", Stmt::Let( Ident(String::from("x")), - Type::List(Box::new(I32)), + BaseType::List(Box::new(I32)), Expr::Literal(Literal::List(List::new(&[], I32)), ((1, 20), (1, 21))), ), ), @@ -1372,7 +1372,7 @@ fn test_parse_maps() { Expr::Literal(Literal::I32(2), ((1, 19), (1, 19))), ), ], - (Type::Str, I32), + (BaseType::Str, I32), )), ((1, 1), (1, 20)), )), @@ -1424,7 +1424,7 @@ fn test_parse_iter() { ident: Ident::from("item"), expr: Box::new(Expr::Ident( Ident::from("list"), - Type::List(Box::new(I32)), + BaseType::List(Box::new(I32)), ((2, 13), (2, 16)), )), body: vec![Stmt::Let( @@ -1446,7 +1446,7 @@ fn test_parse_iter() { ident: Ident::from("item"), expr: Box::new(Expr::Ident( Ident::from("list"), - Type::List(Box::new(I64)), + BaseType::List(Box::new(I64)), ((2, 13), (2, 16)), )), body: vec![Stmt::Let( @@ -1516,7 +1516,7 @@ fn test_parse_struct_decls() { ( Param { name: String::from("y"), - t: Type::Str, + t: BaseType::Str, }, None, ), @@ -1541,7 +1541,7 @@ fn test_parse_struct_decls() { funcs: vec![Func { name: String::from("f"), args: vec![], - ret_t: Type::Str, + ret_t: BaseType::Str, body: vec![Stmt::Return(Expr::Literal( Literal::Str(String::from("foo")), ((3, 8), (3, 12)), @@ -1577,7 +1577,7 @@ fn test_parse_struct_decls() { ( Param { name: String::from("y"), - t: Type::Str, + t: BaseType::Str, }, Some(Expr::Literal( Literal::Str(String::from("foo")), @@ -1588,7 +1588,7 @@ fn test_parse_struct_decls() { funcs: vec![Func { name: String::from("f"), args: vec![], - ret_t: Type::Str, + ret_t: BaseType::Str, body: vec![Stmt::Return(Expr::Literal( Literal::Str(String::from("foo")), ((6, 8), (6, 12)), @@ -1599,6 +1599,51 @@ fn test_parse_struct_decls() { ((1, 1), (8, 1)), ), ), + ( + r#" + struct Foo6 { + y: str, + fn f(self) -> str { + return self.y; + } + } + "#, + Stmt::StructDecl( + StructDecl { + name: String::from("Foo6"), + attrs: vec![( + Param { + name: String::from("y"), + t: BaseType::Str, + }, + None, + )], + funcs: vec![Func { + name: String::from("f"), + args: vec![Param { + name: "self".to_string(), + t: BaseType::Struct("Foo6".to_string(), vec![("y".to_string(), BaseType::Str)]), + }], + ret_t: BaseType::Str, + body: vec![Stmt::Return(Expr::AttrAccess { + lhs: Box::new(Expr::Ident( + Ident::from("self"), + BaseType::Struct( + "Foo6".to_string(), + vec![("y".to_string(), BaseType::Str)], + ), + ((4, 16), (4, 19)), + )), + name: "y".to_string(), + t: BaseType::Str, + span: ((4, 16), (4, 21)), + })], + span: ((3, 1), (5, 1)), + }], + }, + ((1, 1), (6, 1)), + ), + ), ]); } @@ -1677,7 +1722,7 @@ fn test_parse_struct_attrs() { Stmt::Expr(Expr::AttrAccess { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), + BaseType::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), ((5, 1), (5, 3)), )), name: String::from("x"), @@ -1697,11 +1742,11 @@ fn test_parse_struct_attrs() { Stmt::Expr(Expr::AttrAccess { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), + BaseType::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), ((6, 1), (6, 3)), )), name: String::from("y"), - t: Type::Str, + t: BaseType::Str, span: ((6, 1), (6, 5)), }), ), @@ -1724,12 +1769,12 @@ fn test_parse_struct_methods() { Stmt::Expr(Expr::MethodCall { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![]), + BaseType::Struct(String::from("Foo"), vec![]), ((7, 1), (7, 3)), )), name: String::from("hello"), args: vec![], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((7, 1), (7, 11)), }), ), @@ -1747,12 +1792,12 @@ fn test_parse_struct_methods() { Stmt::Expr(Expr::MethodCall { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![]), + BaseType::Struct(String::from("Foo"), vec![]), ((8, 1), (8, 3)), )), name: String::from("hello"), args: vec![], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((8, 1), (8, 11)), }), ), @@ -1770,7 +1815,7 @@ fn test_parse_struct_methods() { Stmt::Expr(Expr::MethodCall { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), + BaseType::Struct(String::from("Foo"), vec![(String::from("x"), I32)]), ((8, 1), (8, 3)), )), name: String::from("hello"), @@ -1778,7 +1823,7 @@ fn test_parse_struct_methods() { Literal::Str(String::from("world")), ((8, 11), (8, 17)), )], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((8, 1), (8, 18)), }), ), @@ -1796,7 +1841,7 @@ fn test_parse_struct_methods() { Stmt::Expr(Expr::MethodCall { lhs: Box::new(Expr::Ident( Ident::from("foo"), - Type::Struct(String::from("Foo"), vec![]), + BaseType::Struct(String::from("Foo"), vec![]), ((8, 1), (8, 3)), )), name: String::from("hello"), @@ -1804,7 +1849,7 @@ fn test_parse_struct_methods() { Literal::Str(String::from("world")), ((8, 11), (8, 17)), )], - ret_t: Type::Void, + ret_t: BaseType::Void, span: ((8, 1), (8, 18)), }), ), diff --git a/coal-objects/src/object.rs b/coal-objects/src/object.rs index fb7d5ee..0ce0d85 100644 --- a/coal-objects/src/object.rs +++ b/coal-objects/src/object.rs @@ -7,7 +7,7 @@ use std::{ rc::Rc, }; -use coal_core::{Expr, F32, F64, I32, I64, I128, Literal, Type, U32, U64, ast, indent}; +use coal_core::{BaseType, Expr, F32, F64, I32, I64, I128, Literal, U32, U64, ast, indent}; use crate::{Builtin, Closure, CompiledFunc, Constant, Func, Struct}; @@ -31,7 +31,7 @@ pub enum Object { Closure(Closure), Struct(Struct), Return(Rc), - Type(Type), + Type(BaseType), Nil, Void, } @@ -168,7 +168,7 @@ impl Object { format!("0x{hash:x?}") } - pub fn cast(&self, to: &Type) -> Option { + pub fn cast(&self, to: &BaseType) -> Option { match (self, to) { // u32 (Object::U32(from), &U64) => Some(Object::U64(*from as u64)),