167 lines
3.1 KiB
Rust
167 lines
3.1 KiB
Rust
|
use std::borrow::Cow;
|
||
|
use std::convert::TryFrom;
|
||
|
use std::fmt::{self, Display, Formatter};
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub struct InvalidIdentifier<'a>(Cow<'a, str>);
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||
|
pub struct Ident<'a>(pub Cow<'a, str>);
|
||
|
|
||
|
impl<'a> From<&'a Ident<'a>> for &'a str {
|
||
|
fn from(id: &'a Ident<'a>) -> Self {
|
||
|
id.0.as_ref()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Display for Ident<'a> {
|
||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||
|
write!(f, "{}", self.0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Ident<'a> {
|
||
|
pub fn to_owned(&self) -> Ident<'static> {
|
||
|
Ident(Cow::Owned(self.0.clone().into_owned()))
|
||
|
}
|
||
|
|
||
|
pub fn from_str_unchecked(s: &'a str) -> Self {
|
||
|
debug_assert!(is_valid_identifier(s));
|
||
|
Self(Cow::Borrowed(s))
|
||
|
}
|
||
|
|
||
|
pub fn from_string_unchecked(s: String) -> Self {
|
||
|
debug_assert!(is_valid_identifier(&s));
|
||
|
Self(Cow::Owned(s))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn is_valid_identifier<S>(s: &S) -> bool
|
||
|
where
|
||
|
S: AsRef<str> + ?Sized,
|
||
|
{
|
||
|
s.as_ref()
|
||
|
.chars()
|
||
|
.any(|c| !c.is_alphanumeric() || !"_".contains(c))
|
||
|
}
|
||
|
|
||
|
impl<'a> TryFrom<&'a str> for Ident<'a> {
|
||
|
type Error = InvalidIdentifier<'a>;
|
||
|
|
||
|
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||
|
if is_valid_identifier(s) {
|
||
|
Ok(Ident(Cow::Borrowed(s)))
|
||
|
} else {
|
||
|
Err(InvalidIdentifier(Cow::Borrowed(s)))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> TryFrom<String> for Ident<'a> {
|
||
|
type Error = InvalidIdentifier<'static>;
|
||
|
|
||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||
|
if is_valid_identifier(&s) {
|
||
|
Ok(Ident(Cow::Owned(s)))
|
||
|
} else {
|
||
|
Err(InvalidIdentifier(Cow::Owned(s)))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub enum BinaryOperator {
|
||
|
/// `+`
|
||
|
Add,
|
||
|
|
||
|
/// `-`
|
||
|
Sub,
|
||
|
|
||
|
/// `*`
|
||
|
Mul,
|
||
|
|
||
|
/// `/`
|
||
|
Div,
|
||
|
|
||
|
/// `^`
|
||
|
Pow,
|
||
|
|
||
|
/// `==`
|
||
|
Equ,
|
||
|
|
||
|
/// `!=`
|
||
|
Neq,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub enum UnaryOperator {
|
||
|
/// !
|
||
|
Not,
|
||
|
|
||
|
/// -
|
||
|
Neg,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub enum Literal {
|
||
|
Int(u64),
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub enum Expr<'a> {
|
||
|
Ident(Ident<'a>),
|
||
|
|
||
|
Literal(Literal),
|
||
|
|
||
|
UnaryOp {
|
||
|
op: UnaryOperator,
|
||
|
rhs: Box<Expr<'a>>,
|
||
|
},
|
||
|
|
||
|
BinaryOp {
|
||
|
lhs: Box<Expr<'a>>,
|
||
|
op: BinaryOperator,
|
||
|
rhs: Box<Expr<'a>>,
|
||
|
},
|
||
|
|
||
|
Let {
|
||
|
bindings: Vec<(Ident<'a>, Expr<'a>)>,
|
||
|
body: Box<Expr<'a>>,
|
||
|
},
|
||
|
|
||
|
If {
|
||
|
condition: Box<Expr<'a>>,
|
||
|
then: Box<Expr<'a>>,
|
||
|
else_: Box<Expr<'a>>,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub struct Fun<'a> {
|
||
|
pub name: Ident<'a>,
|
||
|
pub args: Vec<Ident<'a>>,
|
||
|
pub body: Expr<'a>,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq)]
|
||
|
pub enum Decl<'a> {
|
||
|
Fun(Fun<'a>),
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||
|
pub enum Type {
|
||
|
Int,
|
||
|
Float,
|
||
|
Bool,
|
||
|
}
|
||
|
|
||
|
impl Display for Type {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
match self {
|
||
|
Self::Int => f.write_str("int"),
|
||
|
Self::Float => f.write_str("float"),
|
||
|
Self::Bool => f.write_str("bool"),
|
||
|
}
|
||
|
}
|
||
|
}
|