#![no_std]
#![warn(missing_docs, missing_debug_implementations)]
extern crate alloc;
#[doc(hidden)]
pub use crate::helpers::create_span_ref;
pub use crate::{
parser::{Error, SpannedError},
traits::{Features, Grammar, GrammarExt},
};
use nom_locate::{LocatedSpan, LocatedSpanEx};
use alloc::{boxed::Box, vec, vec::Vec};
use core::fmt;
pub mod grammars;
mod helpers;
mod parser;
mod traits;
pub type Span<'a> = LocatedSpan<&'a str>;
pub type Spanned<'a, T> = LocatedSpanEx<&'a str, T>;
pub type NomResult<'a, T> = nom::IResult<Span<'a>, T, SpannedError<'a>>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Context {
Var,
Fun,
Expr,
}
impl fmt::Display for Context {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Context::Var => formatter.write_str("variable"),
Context::Fun => formatter.write_str("function call"),
Context::Expr => formatter.write_str("arithmetic expression"),
}
}
}
impl Context {
fn new(s: &str) -> Self {
use self::Context::*;
match s {
"var" => Var,
"fn" => Fun,
"expr" => Expr,
_ => unreachable!(),
}
}
fn to_str(self) -> &'static str {
use self::Context::*;
match self {
Var => "var",
Fun => "fn",
Expr => "expr",
}
}
}
#[derive(Debug)]
pub enum Expr<'a, T>
where
T: Grammar,
{
Variable,
Literal(T::Lit),
FnDefinition(FnDefinition<'a, T>),
Function {
name: Box<SpannedExpr<'a, T>>,
args: Vec<SpannedExpr<'a, T>>,
},
Method {
name: Span<'a>,
receiver: Box<SpannedExpr<'a, T>>,
args: Vec<SpannedExpr<'a, T>>,
},
Unary {
op: Spanned<'a, UnaryOp>,
inner: Box<SpannedExpr<'a, T>>,
},
Binary {
lhs: Box<SpannedExpr<'a, T>>,
op: Spanned<'a, BinaryOp>,
rhs: Box<SpannedExpr<'a, T>>,
},
Tuple(Vec<SpannedExpr<'a, T>>),
Block(Block<'a, T>),
}
impl<T: Grammar> Expr<'_, T> {
pub fn binary_lhs(&self) -> Option<&SpannedExpr<'_, T>> {
match self {
Expr::Binary { ref lhs, .. } => Some(lhs),
_ => None,
}
}
pub fn binary_rhs(&self) -> Option<&SpannedExpr<'_, T>> {
match self {
Expr::Binary { ref rhs, .. } => Some(rhs),
_ => None,
}
}
}
impl<T: Grammar> Clone for Expr<'_, T> {
fn clone(&self) -> Self {
match self {
Self::Variable => Self::Variable,
Self::Literal(lit) => Self::Literal(lit.clone()),
Self::FnDefinition(function) => Self::FnDefinition(function.clone()),
Self::Tuple(tuple) => Self::Tuple(tuple.clone()),
Self::Block(block) => Self::Block(block.clone()),
Self::Function { name, args } => Self::Function {
name: name.clone(),
args: args.clone(),
},
Self::Method {
name,
receiver,
args,
} => Self::Method {
name: *name,
receiver: receiver.clone(),
args: args.clone(),
},
Self::Unary { op, inner } => Self::Unary {
op: *op,
inner: inner.clone(),
},
Self::Binary { op, lhs, rhs } => Self::Binary {
op: *op,
lhs: lhs.clone(),
rhs: rhs.clone(),
},
}
}
}
impl<T> PartialEq for Expr<'_, T>
where
T: Grammar,
T::Lit: PartialEq,
T::Type: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
use self::Expr::*;
match (self, other) {
(Variable, Variable) => true,
(Literal(this), Literal(that)) => this == that,
(FnDefinition(this), FnDefinition(that)) => this == that,
(Tuple(this), Tuple(that)) => this == that,
(Block(this), Block(that)) => this == that,
(
Function { name, args },
Function {
name: that_name,
args: that_args,
},
) => name == that_name && args == that_args,
(
Method {
name,
receiver,
args,
},
Method {
name: that_name,
receiver: that_receiver,
args: that_args,
},
) => name == that_name && receiver == that_receiver && args == that_args,
(
Unary { op, inner },
Unary {
op: that_op,
inner: that_inner,
},
) => op == that_op && inner == that_inner,
(
Binary { lhs, op, rhs },
Binary {
lhs: that_lhs,
op: that_op,
rhs: that_rhs,
},
) => op == that_op && lhs == that_lhs && rhs == that_rhs,
_ => false,
}
}
}
pub type SpannedExpr<'a, T> = Spanned<'a, Expr<'a, T>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnaryOp {
Neg,
Not,
}
impl fmt::Display for UnaryOp {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
UnaryOp::Neg => formatter.write_str("negation"),
UnaryOp::Not => formatter.write_str("logical negation"),
}
}
}
impl UnaryOp {
pub const PRIORITY: usize = 5;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Power,
Eq,
NotEq,
And,
Or,
}
impl fmt::Display for BinaryOp {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Add => formatter.write_str("addition"),
Self::Sub => formatter.write_str("subtraction"),
Self::Mul => formatter.write_str("multiplication"),
Self::Div => formatter.write_str("division"),
Self::Power => formatter.write_str("exponentiation"),
Self::Eq => formatter.write_str("comparison"),
Self::NotEq => formatter.write_str("non-equality comparison"),
Self::And => formatter.write_str("AND"),
Self::Or => formatter.write_str("OR"),
}
}
}
impl BinaryOp {
pub fn as_str(self) -> &'static str {
match self {
Self::Add => "+",
Self::Sub => "-",
Self::Mul => "*",
Self::Div => "/",
Self::Power => "^",
Self::Eq => "==",
Self::NotEq => "!=",
Self::And => "&&",
Self::Or => "||",
}
}
pub fn priority(self) -> usize {
match self {
Self::And | Self::Or => 0,
Self::Eq | Self::NotEq => 1,
Self::Add | Self::Sub => 2,
Self::Mul | Self::Div => 3,
Self::Power => 4,
}
}
pub fn is_arithmetic(self) -> bool {
match self {
Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Power => true,
_ => false,
}
}
pub fn is_comparison(self) -> bool {
match self {
Self::Eq | Self::NotEq => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Op {
Unary(UnaryOp),
Binary(BinaryOp),
}
impl fmt::Display for Op {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Op::Unary(inner) => fmt::Display::fmt(inner, formatter),
Op::Binary(inner) => fmt::Display::fmt(inner, formatter),
}
}
}
impl From<UnaryOp> for Op {
fn from(value: UnaryOp) -> Self {
Op::Unary(value)
}
}
impl From<BinaryOp> for Op {
fn from(value: BinaryOp) -> Self {
Op::Binary(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LvalueLen {
Exact(usize),
AtLeast(usize),
}
impl LvalueLen {
pub fn matches(self, value: usize) -> bool {
match self {
Self::Exact(len) => value == len,
Self::AtLeast(len) => value >= len,
}
}
}
impl fmt::Display for LvalueLen {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Exact(len) => write!(formatter, "{}", len),
Self::AtLeast(len) => write!(formatter, "at least {}", len),
}
}
}
impl From<usize> for LvalueLen {
fn from(value: usize) -> Self {
Self::Exact(value)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Destructure<'a, T> {
pub start: Vec<SpannedLvalue<'a, T>>,
pub middle: Option<Spanned<'a, DestructureRest<'a, T>>>,
pub end: Vec<SpannedLvalue<'a, T>>,
}
impl<T> Destructure<'_, T> {
pub fn len(&self) -> LvalueLen {
if self.middle.is_some() {
LvalueLen::AtLeast(self.start.len() + self.end.len())
} else {
LvalueLen::Exact(self.start.len())
}
}
pub fn is_empty(&self) -> bool {
self.start.is_empty()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DestructureRest<'a, T> {
Unnamed,
Named {
variable: Span<'a>,
ty: Option<Spanned<'a, T>>,
},
}
impl<'a, T> DestructureRest<'a, T> {
pub fn to_lvalue(&self) -> Option<SpannedLvalue<'a, T>> {
match self {
Self::Named { variable, .. } => {
Some(create_span_ref(variable, Lvalue::Variable { ty: None }))
}
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Lvalue<'a, T> {
Variable {
ty: Option<Spanned<'a, T>>,
},
Tuple(Destructure<'a, T>),
}
pub type SpannedLvalue<'a, T> = Spanned<'a, Lvalue<'a, T>>;
#[derive(Debug)]
pub enum Statement<'a, T>
where
T: Grammar,
{
Expr(SpannedExpr<'a, T>),
Assignment {
lhs: SpannedLvalue<'a, T::Type>,
rhs: Box<SpannedExpr<'a, T>>,
},
}
impl<T: Grammar> Clone for Statement<'_, T> {
fn clone(&self) -> Self {
match self {
Self::Expr(expr) => Self::Expr(expr.clone()),
Self::Assignment { lhs, rhs } => Self::Assignment {
lhs: lhs.clone(),
rhs: rhs.clone(),
},
}
}
}
impl<T> PartialEq for Statement<'_, T>
where
T: Grammar,
T::Lit: PartialEq,
T::Type: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
use self::Statement::*;
match (self, other) {
(Expr(this), Expr(that)) => this == that,
(
Assignment { lhs, rhs },
Assignment {
lhs: that_lhs,
rhs: that_rhs,
},
) => lhs == that_lhs && rhs == that_rhs,
_ => false,
}
}
}
pub type SpannedStatement<'a, T> = Spanned<'a, Statement<'a, T>>;
#[derive(Debug)]
pub struct Block<'a, T>
where
T: Grammar,
{
pub statements: Vec<SpannedStatement<'a, T>>,
pub return_value: Option<Box<SpannedExpr<'a, T>>>,
}
impl<T: Grammar> Clone for Block<'_, T> {
fn clone(&self) -> Self {
Self {
statements: self.statements.clone(),
return_value: self.return_value.clone(),
}
}
}
impl<T> PartialEq for Block<'_, T>
where
T: Grammar,
T::Lit: PartialEq,
T::Type: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.return_value == other.return_value && self.statements == other.statements
}
}
impl<T: Grammar> Block<'_, T> {
pub fn empty() -> Self {
Self {
statements: vec![],
return_value: None,
}
}
}
#[derive(Debug)]
pub struct FnDefinition<'a, T>
where
T: Grammar,
{
pub args: Spanned<'a, Destructure<'a, T::Type>>,
pub body: Block<'a, T>,
}
impl<T: Grammar> Clone for FnDefinition<'_, T> {
fn clone(&self) -> Self {
Self {
args: self.args.clone(),
body: self.body.clone(),
}
}
}
impl<T> PartialEq for FnDefinition<'_, T>
where
T: Grammar,
T::Lit: PartialEq,
T::Type: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.args == other.args && self.body == other.body
}
}