Implement tuple expressions, types, and patterns, all the way through the parser down to the typechecker. In LLVM, these are implemented as anonymous structs, using an `extract` instruction when they're pattern matched on to get out the individual fields. Currently the only limitation here is patterns aren't supported in function argument position, but you can still do something like fn xy = let (x, y) = xy in x + y Change-Id: I357f17e9d4052e741eda8605b6662822f331efde Reviewed-on: https://cl.tvl.fyi/c/depot/+/3027 Reviewed-by: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI
224 lines
5.5 KiB
Rust
224 lines
5.5 KiB
Rust
use std::borrow::Cow;
|
|
use std::convert::TryFrom;
|
|
use std::fmt::{self, Display};
|
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
|
use std::rc::Rc;
|
|
use std::result;
|
|
|
|
use derive_more::{Deref, From, TryInto};
|
|
use itertools::Itertools;
|
|
|
|
use super::{Error, Result};
|
|
use crate::ast::hir::Expr;
|
|
use crate::ast::{FunctionType, Ident, Type};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Function<'a> {
|
|
pub type_: FunctionType<'a>,
|
|
pub args: Vec<Ident<'a>>,
|
|
pub body: Expr<'a, Type<'a>>,
|
|
}
|
|
|
|
#[derive(From, TryInto)]
|
|
#[try_into(owned, ref)]
|
|
pub enum Val<'a> {
|
|
Int(i64),
|
|
Float(f64),
|
|
Bool(bool),
|
|
String(Cow<'a, str>),
|
|
Tuple(Vec<Value<'a>>),
|
|
Function(Function<'a>),
|
|
}
|
|
|
|
impl<'a> TryFrom<Val<'a>> for String {
|
|
type Error = ();
|
|
|
|
fn try_from(value: Val<'a>) -> result::Result<Self, Self::Error> {
|
|
match value {
|
|
Val::String(s) => Ok(s.into_owned()),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> fmt::Debug for Val<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Val::Int(x) => f.debug_tuple("Int").field(x).finish(),
|
|
Val::Float(x) => f.debug_tuple("Float").field(x).finish(),
|
|
Val::Bool(x) => f.debug_tuple("Bool").field(x).finish(),
|
|
Val::String(s) => f.debug_tuple("String").field(s).finish(),
|
|
Val::Function(Function { type_, .. }) => {
|
|
f.debug_struct("Function").field("type_", type_).finish()
|
|
}
|
|
Val::Tuple(members) => f.debug_tuple("Tuple").field(members).finish(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> PartialEq for Val<'a> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
match (self, other) {
|
|
(Val::Int(x), Val::Int(y)) => x == y,
|
|
(Val::Float(x), Val::Float(y)) => x == y,
|
|
(Val::Bool(x), Val::Bool(y)) => x == y,
|
|
(Val::Function(_), Val::Function(_)) => false,
|
|
(_, _) => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> From<u64> for Val<'a> {
|
|
fn from(i: u64) -> Self {
|
|
Self::from(i as i64)
|
|
}
|
|
}
|
|
|
|
impl<'a> Display for Val<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Val::Int(x) => x.fmt(f),
|
|
Val::Float(x) => x.fmt(f),
|
|
Val::Bool(x) => x.fmt(f),
|
|
Val::String(s) => write!(f, "{:?}", s),
|
|
Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_),
|
|
Val::Tuple(members) => write!(f, "({})", members.iter().join(", ")),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Val<'a> {
|
|
pub fn type_(&self) -> Type {
|
|
match self {
|
|
Val::Int(_) => Type::Int,
|
|
Val::Float(_) => Type::Float,
|
|
Val::Bool(_) => Type::Bool,
|
|
Val::String(_) => Type::CString,
|
|
Val::Function(Function { type_, .. }) => Type::Function(type_.clone()),
|
|
Val::Tuple(members) => Type::Tuple(members.iter().map(|expr| expr.type_()).collect()),
|
|
}
|
|
}
|
|
|
|
pub fn as_type<'b, T>(&'b self) -> Result<&'b T>
|
|
where
|
|
T: TypeOf + 'b + Clone,
|
|
&'b T: TryFrom<&'b Self>,
|
|
{
|
|
<&T>::try_from(self).map_err(|_| Error::InvalidType {
|
|
actual: self.type_().to_owned(),
|
|
expected: <T as TypeOf>::type_of(),
|
|
})
|
|
}
|
|
|
|
pub fn as_function<'b>(&'b self, function_type: FunctionType) -> Result<&'b Function<'a>> {
|
|
match self {
|
|
Val::Function(f) if f.type_ == function_type => Ok(&f),
|
|
_ => Err(Error::InvalidType {
|
|
actual: self.type_().to_owned(),
|
|
expected: Type::Function(function_type.to_owned()),
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn as_tuple(&self) -> Option<&Vec<Value<'a>>> {
|
|
if let Self::Tuple(v) = self {
|
|
Some(v)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn try_into_tuple(self) -> result::Result<Vec<Value<'a>>, Self> {
|
|
if let Self::Tuple(v) = self {
|
|
Ok(v)
|
|
} else {
|
|
Err(self)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone, Deref)]
|
|
pub struct Value<'a>(Rc<Val<'a>>);
|
|
|
|
impl<'a> Display for Value<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> From<T> for Value<'a>
|
|
where
|
|
Val<'a>: From<T>,
|
|
{
|
|
fn from(x: T) -> Self {
|
|
Self(Rc::new(x.into()))
|
|
}
|
|
}
|
|
|
|
impl<'a> Neg for Value<'a> {
|
|
type Output = Result<Value<'a>>;
|
|
|
|
fn neg(self) -> Self::Output {
|
|
Ok((-self.as_type::<i64>()?).into())
|
|
}
|
|
}
|
|
|
|
impl<'a> Add for Value<'a> {
|
|
type Output = Result<Value<'a>>;
|
|
|
|
fn add(self, rhs: Self) -> Self::Output {
|
|
Ok((self.as_type::<i64>()? + rhs.as_type::<i64>()?).into())
|
|
}
|
|
}
|
|
|
|
impl<'a> Sub for Value<'a> {
|
|
type Output = Result<Value<'a>>;
|
|
|
|
fn sub(self, rhs: Self) -> Self::Output {
|
|
Ok((self.as_type::<i64>()? - rhs.as_type::<i64>()?).into())
|
|
}
|
|
}
|
|
|
|
impl<'a> Mul for Value<'a> {
|
|
type Output = Result<Value<'a>>;
|
|
|
|
fn mul(self, rhs: Self) -> Self::Output {
|
|
Ok((self.as_type::<i64>()? * rhs.as_type::<i64>()?).into())
|
|
}
|
|
}
|
|
|
|
impl<'a> Div for Value<'a> {
|
|
type Output = Result<Value<'a>>;
|
|
|
|
fn div(self, rhs: Self) -> Self::Output {
|
|
Ok((self.as_type::<f64>()? / rhs.as_type::<f64>()?).into())
|
|
}
|
|
}
|
|
|
|
pub trait TypeOf {
|
|
fn type_of() -> Type<'static>;
|
|
}
|
|
|
|
impl TypeOf for i64 {
|
|
fn type_of() -> Type<'static> {
|
|
Type::Int
|
|
}
|
|
}
|
|
|
|
impl TypeOf for bool {
|
|
fn type_of() -> Type<'static> {
|
|
Type::Bool
|
|
}
|
|
}
|
|
|
|
impl TypeOf for f64 {
|
|
fn type_of() -> Type<'static> {
|
|
Type::Float
|
|
}
|
|
}
|
|
|
|
impl TypeOf for String {
|
|
fn type_of() -> Type<'static> {
|
|
Type::CString
|
|
}
|
|
}
|