diff --git a/src/ast/basic.rs b/src/ast/basic.rs index 8a722c3..b9e5df0 100644 --- a/src/ast/basic.rs +++ b/src/ast/basic.rs @@ -2,8 +2,6 @@ use std::fmt; use crate::span::{HasSpan, Span}; -use super::{TableConstr, TableConstrElem, TableLit, TableLitElem}; - #[derive(Clone)] pub enum Line { Empty, @@ -39,6 +37,17 @@ impl Space { span, } } + + pub fn then(mut self, other: Self) -> Self { + self.lines.extend(other.lines); + self.span = self.span.join(other.span); + self + } + + pub fn then_line(mut self, line: Line) -> Self { + self.lines.push(line); + self + } } #[derive(Clone)] @@ -69,76 +78,21 @@ impl HasSpan for Ident { } #[derive(Debug, Clone)] -pub struct BoundedSeparated { - pub elems: Vec<(Space, E, Space)>, - pub trailing: Option, - pub span: Span, +pub enum Separated { + Empty(Span), + NonEmpty { + first_elem: E, + last_elems: Vec<(S1, E)>, + trailing: Option, + span: Span, + }, } -impl HasSpan for BoundedSeparated { +impl HasSpan for Separated { fn span(&self) -> Span { - self.span - } -} - -impl BoundedSeparated { - pub fn new(span: Span) -> Self { - Self { - elems: vec![], - trailing: None, - span, + match self { + Self::Empty(span) => *span, + Self::NonEmpty { span, .. } => *span, } } - - pub fn then(mut self, elem: E) -> Self { - self.elems - .push((Space::empty(self.span), elem, Space::empty(self.span))); - self - } - - pub fn map(self, f: impl Fn(E) -> E2) -> BoundedSeparated { - let elems = self - .elems - .into_iter() - .map(|(s0, e, s1)| (s0, f(e), s1)) - .collect::>(); - - BoundedSeparated { - elems, - trailing: self.trailing, - span: self.span, - } - } - - pub fn remove_map( - self, - f: impl Fn(E) -> Result, - ) -> (BoundedSeparated, Vec<(Space, E2, Space)>) { - let mut kept = vec![]; - let mut removed = vec![]; - for (s0, elem, s1) in self.elems { - match f(elem) { - Ok(elem) => kept.push((s0, elem, s1)), - Err(elem) => removed.push((s0, elem, s1)), - } - } - let new = BoundedSeparated { - elems: kept, - trailing: self.trailing, - span: self.span, - }; - (new, removed) - } -} - -impl BoundedSeparated { - pub fn table_lit(self) -> TableLit { - TableLit(self) - } -} - -impl BoundedSeparated { - pub fn table_constr(self) -> TableConstr { - TableConstr(self) - } } diff --git a/src/ast/call.rs b/src/ast/call.rs index 6a9d13e..547e1f1 100644 --- a/src/ast/call.rs +++ b/src/ast/call.rs @@ -46,38 +46,3 @@ impl HasSpan for Call { } } } - -impl Call { - pub fn arg(base: Box, arg: Box, span: Span) -> Self { - Self::Arg { - expr: base, - s0: Space::empty(span), - s1: Space::empty(span), - arg, - s2: Space::empty(span), - span, - } - } - - pub fn no_arg(base: Box, span: Span) -> Self { - Self::NoArg { - expr: base, - s0: Space::empty(span), - s1: Space::empty(span), - span, - } - } - - pub fn constr(base: Box, constr: TableConstr, span: Span) -> Self { - Self::Constr { - expr: base, - s0: Space::empty(span), - constr, - span, - } - } - - pub fn expr(self) -> Expr { - Expr::Call(self) - } -} diff --git a/src/ast/expr.rs b/src/ast/expr.rs index d0fa1ff..9b10502 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -242,9 +242,3 @@ impl HasSpan for Expr { } } } - -impl Expr { - pub fn boxed(self) -> Box { - Box::new(self) - } -} diff --git a/src/ast/field.rs b/src/ast/field.rs index 5afd98f..1972686 100644 --- a/src/ast/field.rs +++ b/src/ast/field.rs @@ -67,34 +67,3 @@ impl HasSpan for Field { } } } - -impl Field { - pub fn access(base: Box, index: Box, span: Span) -> Self { - Self::Access { - expr: base, - s0: Space::empty(span), - s1: Space::empty(span), - index, - s2: Space::empty(span), - span, - } - } - - pub fn assign(base: Box, index: Box, value: Box, span: Span) -> Self { - Self::Assign { - expr: base, - s0: Space::empty(span), - s1: Space::empty(span), - index, - s2: Space::empty(span), - s3: Space::empty(span), - s4: Space::empty(span), - value, - span, - } - } - - pub fn expr(self) -> Expr { - Expr::Field(self) - } -} diff --git a/src/ast/func_def.rs b/src/ast/func_def.rs index 5cdd210..e1e89ad 100644 --- a/src/ast/func_def.rs +++ b/src/ast/func_def.rs @@ -99,41 +99,3 @@ impl HasSpan for FuncDef { } } } - -impl FuncDef { - pub fn anon_no_arg(body: Box, span: Span) -> Self { - Self::AnonNoArg { - s0: Space::empty(span), - s1: Space::empty(span), - s2: Space::empty(span), - body, - span, - } - } - - pub fn anon_arg(arg: Ident, body: Box, span: Span) -> Self { - Self::AnonArg { - s0: Space::empty(span), - s1: Space::empty(span), - arg, - s2: Space::empty(span), - s3: Space::empty(span), - body, - span, - } - } - - pub fn anon_destr(pattern: TablePattern, body: Box, span: Span) -> Self { - Self::AnonDestr { - s0: Space::empty(span), - pattern, - s1: Space::empty(span), - body, - span, - } - } - - pub fn expr(self) -> Expr { - Expr::FuncDef(self) - } -} diff --git a/src/ast/lit.rs b/src/ast/lit.rs index ecd166c..63ced41 100644 --- a/src/ast/lit.rs +++ b/src/ast/lit.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::builtin::Builtin; use crate::span::{HasSpan, Span}; -use super::{BoundedSeparated, Expr, Ident, Space}; +use super::{Expr, Ident, Separated, Space}; #[derive(Clone)] pub enum NumLitStr { @@ -93,19 +93,6 @@ pub struct StringLit { pub span: Span, } -impl StringLit { - pub fn from_ident(ident: Ident) -> Self { - Self { - elems: vec![StringLitElem::Plain(ident.name)], - span: ident.span, - } - } - - pub fn lit(self) -> Lit { - Lit::String(self) - } -} - impl HasSpan for StringLit { fn span(&self) -> Span { self.span @@ -138,31 +125,20 @@ impl HasSpan for TableLitElem { } } -impl TableLitElem { - pub fn named(name: Ident, value: Box, span: Span) -> Self { - Self::Named { - name, - s0: Space::empty(span), - s1: Space::empty(span), - value, - span, - } - } -} - /// `'{ a, foo: b }` +/// +/// Structure: `'{ s0 elems s1 }` #[derive(Debug, Clone)] -pub struct TableLit(pub BoundedSeparated); +pub struct TableLit { + pub s0: Space, + pub elems: Separated, + pub s1: Space, + pub span: Span, +} impl HasSpan for TableLit { fn span(&self) -> Span { - self.0.span() - } -} - -impl TableLit { - pub fn lit(self) -> Lit { - Lit::Table(self) + self.span } } @@ -220,9 +196,3 @@ impl HasSpan for Lit { } } } - -impl Lit { - pub fn expr(self) -> Expr { - Expr::Lit(self) - } -} diff --git a/src/ast/program.rs b/src/ast/program.rs index a77abf4..2020225 100644 --- a/src/ast/program.rs +++ b/src/ast/program.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{BoundedSeparated, Expr, Space, TableLitElem}; +use super::{Expr, Separated, Space, TableLitElem}; #[derive(Debug, Clone)] pub enum Program { @@ -12,10 +12,12 @@ pub enum Program { span: Span, }, - /// Structure: `s0 module elems` + /// Structure: `s0 module s1 elems s2` Module { s0: Space, - elems: BoundedSeparated, + s1: Space, + elems: Separated, + s2: Space, span: Span, }, } diff --git a/src/ast/table_constr.rs b/src/ast/table_constr.rs index 38c409d..d347ce1 100644 --- a/src/ast/table_constr.rs +++ b/src/ast/table_constr.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{BoundedSeparated, Expr, Ident, Space, TableLitElem}; +use super::{Expr, Separated, Space, TableLitElem}; #[derive(Debug, Clone)] pub enum TableConstrElem { @@ -30,28 +30,19 @@ impl HasSpan for TableConstrElem { } } -impl TableConstrElem { - pub fn positional(value: Box) -> Self { - Self::Lit(TableLitElem::Positional(value)) - } - - pub fn named(name: Ident, value: Box, span: Span) -> Self { - Self::Lit(TableLitElem::named(name, value, span)) - } -} - /// `{ a, b, foo: c, [d]: e }` +/// +/// Structure: `{ s0 elems s1 }` #[derive(Debug, Clone)] -pub struct TableConstr(pub BoundedSeparated); +pub struct TableConstr { + pub s0: Space, + pub elems: Separated, + pub s1: Space, + pub span: Span, +} impl HasSpan for TableConstr { fn span(&self) -> Span { - self.0.span() - } -} - -impl TableConstr { - pub fn expr(self) -> Expr { - Expr::TableConstr(self) + self.span } } diff --git a/src/ast/table_destr.rs b/src/ast/table_destr.rs index d94b4eb..eb88dcb 100644 --- a/src/ast/table_destr.rs +++ b/src/ast/table_destr.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{BoundedSeparated, Expr, Ident, Space}; +use super::{Expr, Ident, Separated, Space}; // TODO Make table patterns recursive @@ -34,11 +34,16 @@ impl HasSpan for TablePatternElem { /// /// Structure: `{ s0 elems s1 }` #[derive(Debug, Clone)] -pub struct TablePattern(pub BoundedSeparated); +pub struct TablePattern { + pub s0: Space, + pub elems: Separated, + pub s1: Space, + pub span: Span, +} impl HasSpan for TablePattern { fn span(&self) -> Span { - self.0.span() + self.span } } @@ -61,26 +66,3 @@ impl HasSpan for TableDestr { self.span } } - -impl TableDestr { - pub fn new(local: bool, pattern: TablePattern, value: Box, span: Span) -> Self { - let local = if local { - Some(Space::empty(span)) - } else { - None - }; - - Self { - local, - pattern, - s0: Space::empty(span), - s1: Space::empty(span), - value, - span, - } - } - - pub fn expr(self) -> Expr { - Expr::TableDestr(self) - } -} diff --git a/src/ast/var.rs b/src/ast/var.rs index d5f0eab..6ec6026 100644 --- a/src/ast/var.rs +++ b/src/ast/var.rs @@ -56,54 +56,3 @@ impl HasSpan for Var { } } } - -impl Var { - pub fn access(index: Box, span: Span) -> Self { - Self::Access { - s0: Space::empty(span), - index, - s1: Space::empty(span), - span, - } - } - - pub fn assign(local: bool, index: Box, value: Box, span: Span) -> Self { - let local = if local { - Some(Space::empty(span)) - } else { - None - }; - - Self::Assign { - local, - s0: Space::empty(span), - index, - s1: Space::empty(span), - s2: Space::empty(span), - s3: Space::empty(span), - value, - span, - } - } - - pub fn assign_ident(local: bool, name: Ident, value: Box, span: Span) -> Self { - let local = if local { - Some(Space::empty(span)) - } else { - None - }; - - Self::AssignIdent { - local, - name, - s0: Space::empty(span), - s1: Space::empty(span), - value, - span, - } - } - - pub fn expr(self) -> Expr { - Expr::Var(self) - } -} diff --git a/src/builtin.rs b/src/builtin.rs index 6ef18c8..f0e504d 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -12,21 +12,6 @@ pub enum Builtin { Scope, Arg, Destructure, - Neg, - Not, - Mul, - Div, - Mod, - Add, - Sub, - Eq, - Ne, - Gt, - Ge, - Lt, - Le, - And, - Or, } impl fmt::Debug for Builtin { @@ -41,21 +26,6 @@ impl fmt::Debug for Builtin { Self::Scope => write!(f, "'scope"), Self::Arg => write!(f, "'arg"), Self::Destructure => write!(f, "'destructure"), - Self::Neg => write!(f, "'neg"), - Self::Not => write!(f, "'not"), - Self::Mul => write!(f, "'mul"), - Self::Div => write!(f, "'div"), - Self::Mod => write!(f, "'mod"), - Self::Add => write!(f, "'add"), - Self::Sub => write!(f, "'sub"), - Self::Eq => write!(f, "'eq"), - Self::Ne => write!(f, "'ne"), - Self::Gt => write!(f, "'gt"), - Self::Ge => write!(f, "'ge"), - Self::Lt => write!(f, "'lt"), - Self::Le => write!(f, "'le"), - Self::And => write!(f, "'and"), - Self::Or => write!(f, "'or"), } } } diff --git a/src/desugar.rs b/src/desugar.rs index 2be2a3f..db66687 100644 --- a/src/desugar.rs +++ b/src/desugar.rs @@ -2,9 +2,7 @@ mod basic; mod call; mod expr; mod field; -mod func_def; mod lit; mod program; mod table_constr; -mod table_destr; mod var; diff --git a/src/desugar/basic.rs b/src/desugar/basic.rs index 156445f..c4d8a51 100644 --- a/src/desugar/basic.rs +++ b/src/desugar/basic.rs @@ -1,24 +1,181 @@ -use crate::ast::BoundedSeparated; +use std::mem; -impl BoundedSeparated { - pub fn desugar(self, desugar_elem: impl Fn(E) -> (E, bool)) -> (Self, bool) { - let mut desugared = false; - let mut elems = vec![]; - for (s0, elem, s1) in self.elems { - if desugared { - elems.push((s0, elem, s1)); - } else { - let (elem, elem_desugared) = desugar_elem(elem); - desugared = desugared || elem_desugared; - elems.push((s0, elem, s1)); +use crate::ast::Separated; +use crate::span::Span; + +impl Separated { + pub fn desugar_elem(self, desugar_elem: impl Fn(E) -> (E, bool)) -> (Self, bool) { + match self { + Self::Empty(span) => (Self::Empty(span), false), + + Self::NonEmpty { + first_elem, + last_elems, + trailing, + span, + } => { + let (new_first_elem, mut desugared) = desugar_elem(first_elem); + let mut new_last_elems = vec![]; + for (separator, elem) in last_elems { + if desugared { + new_last_elems.push((separator, elem)); + } else { + let (elem, elem_desugared) = desugar_elem(elem); + desugared = desugared || elem_desugared; + new_last_elems.push((separator, elem)); + } + } + + let new = Self::NonEmpty { + first_elem: new_first_elem, + last_elems: new_last_elems, + trailing, + span, + }; + (new, desugared) } } - - let new = Self { - elems, - trailing: self.trailing, - span: self.span, - }; - (new, desugared) + } +} + +/// A version of [`Separated`] that includes its leading and trailing +/// whitespace. +enum Separated2 { + // In theory, all of the second space would've already been consumed by the + // first space parser, but we still need to keep track of it because I + // haven't yet found a nicer way to model this data. + Empty(S, S, Span), + NonEmpty { + elems: Vec<(S, E, S)>, + trailing: Option, + span: Span, + }, +} + +impl Separated2 { + pub fn from_separated(before: S, separated: Separated, after: S) -> Self { + match separated { + Separated::Empty(span) => Self::Empty(before, after, span), + + Separated::NonEmpty { + first_elem, + last_elems, + trailing, + span, + } => { + let mut next_before = before; + let mut next_elem = first_elem; + let mut elems = vec![]; + + for ((prev_after, this_before), elem) in last_elems { + elems.push((next_before, next_elem, prev_after)); + next_before = this_before; + next_elem = elem; + } + + let trailing = if let Some(trailing) = trailing { + elems.push((next_before, next_elem, trailing)); + Some(after) + } else { + elems.push((next_before, next_elem, after)); + None + }; + + Self::NonEmpty { + elems, + trailing, + span, + } + } + } + } + + pub fn into_separated(self) -> (S, Separated, S) { + match self { + Self::Empty(before, after, span) => (before, Separated::Empty(span), after), + + Self::NonEmpty { + elems, + trailing, + span, + } => { + let mut elems = elems.into_iter(); + + let (before, first_elem, mut after) = elems + .next() + .expect("non-empty Separated2 must have elements"); + + let mut last_elems = vec![]; + for (this_before, elem, this_after) in elems { + last_elems.push(((after, this_before), elem)); + after = this_after; + } + + let trailing = if let Some(mut trailing) = trailing { + mem::swap(&mut after, &mut trailing); + Some(trailing) + } else { + None + }; + + let separated = Separated::NonEmpty { + first_elem, + last_elems, + trailing, + span, + }; + + (before, separated, after) + } + } + } +} + +pub enum Decision { + Keep(A), + Remove(B), +} + +impl Separated2 { + pub fn remove( + self, + decide: impl Fn(E) -> Decision, + ) -> (Separated2, Vec<(S, E2, S)>) { + match self { + Self::Empty(s0, s1, span) => (Separated2::Empty(s0, s1, span), vec![]), + Self::NonEmpty { + elems, + trailing, + span, + } => { + let mut kept = vec![]; + let mut removed = vec![]; + for (s0, elem, s1) in elems { + match decide(elem) { + Decision::Keep(elem) => kept.push((s0, elem, s1)), + Decision::Remove(elem) => removed.push((s0, elem, s1)), + } + } + todo!() + } + } + } +} + +pub struct Removed { + before: S, + separated: Separated, + after: S, + removed: Vec<(S, B, S)>, +} + +impl Separated { + pub fn remove_elems( + self, + before: S, + after: S, + separate: impl Fn(E) -> Decision, + ) -> Removed { + todo!() } } diff --git a/src/desugar/call.rs b/src/desugar/call.rs index c231875..690f99e 100644 --- a/src/desugar/call.rs +++ b/src/desugar/call.rs @@ -1,4 +1,4 @@ -use crate::ast::{BoundedSeparated, Call, Expr, Ident, Lit, TableLitElem}; +use crate::ast::{Call, Expr, Ident, Lit, Separated, Space, TableLit, TableLitElem}; // TODO Add span for just the parentheses to ast, or limit span to parentheses @@ -7,37 +7,74 @@ impl Call { match self { Self::Arg { expr, - s0: _, - s1: _, + s0, + s1, arg, - s2: _, + s2, span, } => { - let new = BoundedSeparated::new(span) - .then(TableLitElem::named(Ident::new("call", span), expr, span)) - .then(TableLitElem::named(Ident::new("arg", span), arg, span)) - .table_lit(); - (new.lit().expr(), true) + // `expr s0 ( s1 arg s2 )` + // -> `'{ s0 call: expr, arg: s1 arg s2 }` + let call = TableLitElem::Named { + name: Ident::new("call", span), + s0: Space::empty(span), + s1: Space::empty(span), + value: expr, + span, + }; + let arg = TableLitElem::Named { + name: Ident::new("arg", span), + s0: Space::empty(span), + s1, + value: arg, + span, + }; + let elems = Separated::NonEmpty { + first_elem: call, + last_elems: vec![((Space::empty(span), Space::empty(span)), arg)], + trailing: None, + span, + }; + let new = Expr::Lit(Lit::Table(TableLit { + s0, + elems, + s1: s2, + span, + })); + (new, true) } - Self::NoArg { - expr, - s0: _, - s1: _, - span, - } => { - let new = Self::arg(expr, Lit::Nil(span).expr().boxed(), span); - (new.expr(), true) + Self::NoArg { expr, s0, s1, span } => { + // `expr s0 ( s1 )` + // -> `expr s0 ( s1 nil )` + let new = Expr::Call(Self::Arg { + expr, + s0, + s1, + arg: Box::new(Expr::Lit(Lit::Nil(span))), + s2: Space::empty(span), + span, + }); + (new, true) } Self::Constr { expr, - s0: _, + s0, constr, span, } => { - let new = Self::arg(expr, constr.expr().boxed(), span); - (new.expr(), true) + // `expr s0 {..}` + // -> `expr s0 ( {..} )` + let new = Expr::Call(Self::Arg { + expr, + s0, + s1: Space::empty(span), + arg: Box::new(Expr::TableConstr(constr)), + s2: Space::empty(span), + span, + }); + (new, true) } } } diff --git a/src/desugar/expr.rs b/src/desugar/expr.rs index b7f4b08..0c8e569 100644 --- a/src/desugar/expr.rs +++ b/src/desugar/expr.rs @@ -1,78 +1,84 @@ -use crate::ast::{BinOp, BoundedSeparated, Call, Expr, Lit, TableConstrElem}; -use crate::builtin::Builtin; +use crate::ast::Expr; impl Expr { pub fn desugar(self) -> (Self, bool) { match self { Self::Lit(lit) => { let (lit, desugared) = lit.desugar(); - (lit.expr(), desugared) + (Self::Lit(lit), desugared) } Self::Call(call) => call.desugar(), Self::Field(field) => field.desugar(), Self::Var(var) => var.desugar(), - Self::TableConstr(constr) => constr.desugar(), - Self::TableDestr(destr) => destr.desugar(), - Self::FuncDef(def) => def.desugar(), + + Self::TableConstr(constr) => (Self::TableConstr(constr), false), // TODO Implement + Self::TableDestr(destr) => (Self::TableDestr(destr), false), // TODO Implement + Self::FuncDef(def) => (Self::FuncDef(def), false), // TODO Implement Self::Paren { - s0: _, + s0, inner, - s1: _, - span: _, - } => (*inner, true), + s1, + span, + } => ( + Self::Paren { + s0, + inner, + s1, + span, + }, + false, + ), // TODO Implement Self::Neg { minus, - s0: _, + s0, expr, span, - } => { - let new = Call::arg(Lit::Builtin(Builtin::Neg, minus).expr().boxed(), expr, span); - (new.expr(), true) - } + } => ( + Self::Neg { + minus, + s0, + expr, + span, + }, + false, + ), // TODO Implement Self::Not { not, - s0: _, + s0, expr, span, - } => { - let new = Call::arg(Lit::Builtin(Builtin::Not, not).expr().boxed(), expr, span); - (new.expr(), true) - } + } => ( + Self::Not { + not, + s0, + expr, + span, + }, + false, + ), // TODO Implement Self::BinOp { left, - s0: _, + s0, op, - s1: _, + s1, right, span, - } => { - let builtin = match op { - BinOp::Mul => Builtin::Mul, - BinOp::Div => Builtin::Div, - BinOp::Mod => Builtin::Mod, - BinOp::Add => Builtin::Add, - BinOp::Sub => Builtin::Sub, - BinOp::Eq => Builtin::Eq, - BinOp::Neq => Builtin::Ne, - BinOp::Gt => Builtin::Gt, - BinOp::Ge => Builtin::Ge, - BinOp::Lt => Builtin::Lt, - BinOp::Le => Builtin::Le, - BinOp::And => Builtin::And, - BinOp::Or => Builtin::Or, - }; - let constr = BoundedSeparated::new(span) - .then(TableConstrElem::positional(left)) - .then(TableConstrElem::positional(right)) - .table_constr(); - let new = Call::constr(Lit::Builtin(builtin, span).expr().boxed(), constr, span); - (new.expr(), true) - } + } => ( + Self::BinOp { + left, + s0, + op, + s1, + right, + span, + }, + false, + ), // TODO Implement } } } diff --git a/src/desugar/field.rs b/src/desugar/field.rs index 3c269e2..7618326 100644 --- a/src/desugar/field.rs +++ b/src/desugar/field.rs @@ -1,4 +1,7 @@ -use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem}; +use crate::ast::{ + Call, Expr, Field, Line, Lit, Separated, Space, StringLit, StringLitElem, TableConstr, + TableConstrElem, TableLitElem, +}; use crate::builtin::Builtin; impl Field { @@ -6,80 +9,133 @@ impl Field { match self { Self::Access { expr, - s0: _, - s1: _, + s0, + s1, index, - s2: _, + s2, span, } => { - let constr = BoundedSeparated::new(span) - .then(TableConstrElem::positional(expr)) - .then(TableConstrElem::positional(index)) - .table_constr(); - let new = Call::constr( - Lit::Builtin(Builtin::Get, span).expr().boxed(), + // ` expr s0 [ s1 index s2 ]` + // -> `'get s0 { expr, s1 index s2 }` + let elems = Separated::NonEmpty { + first_elem: TableConstrElem::Lit(TableLitElem::Positional(expr)), + last_elems: vec![( + (Space::empty(span), s1), + TableConstrElem::Lit(TableLitElem::Positional(index)), + )], + trailing: None, + span, + }; + let constr = TableConstr { + s0: Space::empty(span), + elems, + s1: s2, + span, + }; + let new = Expr::Call(Call::Constr { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::Get, span))), + s0, constr, span, - ); - (new.expr(), true) + }); + (new, true) } Self::Assign { expr, - s0: _, - s1: _, + s0, + s1, index, - s2: _, - s3: _, - s4: _, + s2, + s3, + s4, value, span, } => { - let constr = BoundedSeparated::new(span) - .then(TableConstrElem::positional(expr)) - .then(TableConstrElem::positional(index)) - .then(TableConstrElem::positional(value)) - .table_constr(); - let new = Call::constr( - Lit::Builtin(Builtin::Set, span).expr().boxed(), + // `expr s0 [ s1 index s2 ] s3 = s4 value` + // -> `'set s0 { expr, s1 index s2, s3 s4 value }` + let elems = Separated::NonEmpty { + first_elem: TableConstrElem::Lit(TableLitElem::Positional(expr)), + last_elems: vec![ + ( + (Space::empty(span), s1), + TableConstrElem::Lit(TableLitElem::Positional(index)), + ), + ( + (s2, s3.then_line(Line::Empty).then(s4)), + TableConstrElem::Lit(TableLitElem::Positional(value)), + ), + ], + trailing: None, + span, + }; + let constr = TableConstr { + s0: Space::empty(span), + elems, + s1: Space::empty(span), + span, + }; + let new = Expr::Call(Call::Constr { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::Set, span))), + s0, constr, span, - ); - (new.expr(), true) + }); + (new, true) } Self::AccessIdent { expr, - s0: _, - s1: _, + s0, + s1, ident, span, } => { - let new = Self::access( + // `expr s0 . s1 ident´ + // -> `expr s0 [ s1 ident_str ]` + let ident_str = Expr::Lit(Lit::String(StringLit { + elems: vec![StringLitElem::Plain(ident.name)], + span: ident.span, + })); + let new = Expr::Field(Self::Access { expr, - StringLit::from_ident(ident).lit().expr().boxed(), + s0, + s1, + index: Box::new(ident_str), + s2: Space::empty(span), span, - ); - (new.expr(), true) + }); + (new, true) } Self::AssignIdent { expr, - s0: _, - s1: _, + s0, + s1, ident, - s2: _, - s3: _, + s2, + s3, value, span, } => { - let new = Self::assign( + // `expr s0 . s1 ident s2 = s3 value` + // -> `expr s0 [ s1 ident_str ] s2 = s3 value` + let ident_str = Expr::Lit(Lit::String(StringLit { + elems: vec![StringLitElem::Plain(ident.name)], + span: ident.span, + })); + let new = Expr::Field(Self::Assign { expr, - StringLit::from_ident(ident).lit().expr().boxed(), + s0, + s1, + index: Box::new(ident_str), + s2: Space::empty(span), + s3: s2, + s4: s3, value, span, - ); - (new.expr(), true) + }); + (new, true) } } } diff --git a/src/desugar/func_def.rs b/src/desugar/func_def.rs deleted file mode 100644 index 2a549d1..0000000 --- a/src/desugar/func_def.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::ast::{ - BoundedSeparated, Call, Expr, FuncDef, Ident, Lit, Space, TableConstrElem, TableDestr, - TableLitElem, Var, -}; -use crate::builtin::Builtin; - -impl FuncDef { - pub fn desugar(self) -> (Expr, bool) { - match self { - Self::AnonNoArg { - s0: _, - s1: _, - s2: _, - body, - span, - } => { - let quote = BoundedSeparated::new(span) - .then(TableLitElem::named(Ident::new("quote", span), body, span)) - .table_lit(); - let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span); - let new = BoundedSeparated::new(span) - .then(TableConstrElem::positional(Box::new(quote.lit().expr()))) - .then(TableConstrElem::named( - Ident::new("scope", span), - scope.expr().boxed(), - span, - )) - .table_constr(); - (new.expr(), true) - } - - Self::AnonArg { - s0: _, - s1: _, - arg, - s2: _, - s3: _, - body, - span, - } => { - let arg_call = Call::no_arg(Lit::Builtin(Builtin::Arg, span).expr().boxed(), span); - let arg_assign = Var::assign_ident(true, arg, arg_call.expr().boxed(), span); - let body = BoundedSeparated::new(span) - .then(TableLitElem::Positional(arg_assign.expr().boxed())) - .then(TableLitElem::Positional(body)) - .table_lit(); - let new = Self::AnonNoArg { - s0: Space::empty(span), - s1: Space::empty(span), - s2: Space::empty(span), - body: body.lit().expr().boxed(), - span, - }; - (new.expr(), true) - } - - Self::AnonDestr { - s0: _, - pattern, - s1: _, - body, - span, - } => { - let arg_call = Call::no_arg(Lit::Builtin(Builtin::Arg, span).expr().boxed(), span); - let arg_destr = TableDestr::new(true, pattern, arg_call.expr().boxed(), span); - let body = BoundedSeparated::new(span) - .then(TableLitElem::Positional(arg_destr.expr().boxed())) - .then(TableLitElem::Positional(body)) - .table_lit(); - let new = Self::AnonNoArg { - s0: Space::empty(span), - s1: Space::empty(span), - s2: Space::empty(span), - body: body.lit().expr().boxed(), - span, - }; - (new.expr(), true) - } - - Self::NamedNoArg { - local, - s0: _, - name, - s1: _, - s2: _, - s3: _, - body, - span, - } => { - let anon = Self::anon_no_arg(body, span); - let new = Var::assign_ident(local.is_some(), name, anon.expr().boxed(), span); - (new.expr(), true) - } - - Self::NamedArg { - local, - s0: _, - name, - s1: _, - s2: _, - arg, - s3: _, - s4: _, - body, - span, - } => { - let anon = Self::anon_arg(arg, body, span); - let new = Var::assign_ident(local.is_some(), name, anon.expr().boxed(), span); - (new.expr(), true) - } - - Self::NamedDestr { - local, - s0: _, - name, - s1: _, - pattern, - s2: _, - body, - span, - } => { - let anon = Self::anon_destr(pattern, body, span); - let new = Var::assign_ident(local.is_some(), name, anon.expr().boxed(), span); - (new.expr(), true) - } - } - } -} diff --git a/src/desugar/lit.rs b/src/desugar/lit.rs index 3b6fd80..348c3c7 100644 --- a/src/desugar/lit.rs +++ b/src/desugar/lit.rs @@ -1,11 +1,11 @@ -use crate::ast::{Expr, Lit, TableLit, TableLitElem}; +use crate::ast::{Lit, TableLit, TableLitElem}; impl TableLitElem { pub fn desugar(self) -> (Self, bool) { match self { Self::Positional(expr) => { let (expr, desugared) = expr.desugar(); - (Self::Positional(expr.boxed()), desugared) + (Self::Positional(Box::new(expr)), desugared) } Self::Named { @@ -20,7 +20,7 @@ impl TableLitElem { name, s0, s1, - value: value.boxed(), + value: Box::new(value), span, }; (new, desugared) @@ -31,18 +31,21 @@ impl TableLitElem { impl TableLit { pub fn desugar(self) -> (Self, bool) { - let (elems, removed) = self.0.remove_map(|e| match e { - TableLitElem::Named { value, .. } if matches!(*value, Expr::Lit(Lit::Nil(_))) => { - Err(()) - } - e => Ok(e), - }); - if removed.is_empty() { - let (elems, desugared) = elems.desugar(|e| e.desugar()); - (elems.table_lit(), desugared) - } else { - (elems.table_lit(), true) - } + let Self { + s0, + elems, + s1, + span, + } = self; + + let (elems, desugared) = elems.desugar_elem(|e| e.desugar()); + let new = Self { + s0, + elems, + s1, + span, + }; + (new, desugared) } } @@ -51,7 +54,7 @@ impl Lit { match self { Self::Table(table) => { let (table, desugared) = table.desugar(); - (table.lit(), desugared) + (Self::Table(table), desugared) } lit => (lit, false), diff --git a/src/desugar/program.rs b/src/desugar/program.rs index 615998b..7960cbc 100644 --- a/src/desugar/program.rs +++ b/src/desugar/program.rs @@ -1,4 +1,4 @@ -use crate::ast::{Program, Space}; +use crate::ast::{Expr, Lit, Program, Space, TableLit}; impl Program { pub fn desugar(self) -> (Self, bool) { @@ -9,12 +9,24 @@ impl Program { (new, desugared) } - Self::Module { s0, elems, span } => { - // `s0 module elems` - // -> `s0 table` + Self::Module { + s0, + s1, + elems, + s2, + span, + } => { + // `s0 module s1 elems s2` + // -> `s0 '{ s1 elems s2 } empty` + let table = TableLit { + s0: s1, + elems, + s1: s2, + span, + }; let new = Self::Expr { s0, - expr: elems.table_lit().lit().expr(), + expr: Expr::Lit(Lit::Table(table)), s1: Space::empty(span), span, }; diff --git a/src/desugar/table_constr.rs b/src/desugar/table_constr.rs index 263d9d4..1fd7e62 100644 --- a/src/desugar/table_constr.rs +++ b/src/desugar/table_constr.rs @@ -1,39 +1,17 @@ -use crate::ast::{ - BoundedSeparated, Expr, Field, Ident, TableConstr, TableConstrElem, TableLitElem, -}; -use crate::span::HasSpan; +use crate::ast::{Expr, Lit, TableConstr, TableLit}; impl TableConstr { pub fn desugar(self) -> (Expr, bool) { - let span = self.span(); + let Self { + s0, + elems, + s1, + span, + } = self; - let (elems, setters) = self.0.remove_map(|e| match e { - TableConstrElem::Lit(lit) => Ok(lit), - TableConstrElem::Indexed { - s0: _, - index, - s1: _, - s2: _, - s3: _, - value, - span, - } => Err((index, value, span)), - }); + // `{ s0 elems s1 }` + // -> `'{ raw: '{ s0 lit_elems s1 } }` surrounded by 'set calls - let mut expr = BoundedSeparated::new(span) - .then(TableLitElem::named( - Ident::new("raw", span), - elems.table_lit().lit().expr().boxed(), - span, - )) - .table_lit() - .lit() - .expr(); - - for (_, (index, value, span), _) in setters { - expr = Field::assign(expr.boxed(), index, value, span).expr(); - } - - (expr, true) + todo!() } } diff --git a/src/desugar/table_destr.rs b/src/desugar/table_destr.rs deleted file mode 100644 index 2216977..0000000 --- a/src/desugar/table_destr.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::ast::{ - BoundedSeparated, Call, Expr, Ident, Lit, StringLit, TableConstr, TableConstrElem, TableDestr, - TableLitElem, TablePattern, TablePatternElem, -}; -use crate::builtin::Builtin; - -fn pattern_to_constr(pattern: TablePattern) -> TableConstr { - pattern - .0 - .map(|e| match e { - TablePatternElem::Positional(ident) => { - TableConstrElem::positional(StringLit::from_ident(ident).lit().expr().boxed()) - } - - TablePatternElem::Named { - name, - s0, - s1, - ident, - span, - } => TableConstrElem::Lit(TableLitElem::Named { - name, - s0, - s1, - value: StringLit::from_ident(ident).lit().expr().boxed(), - span, - }), - }) - .table_constr() -} - -impl TableDestr { - pub fn desugar(self) -> (Expr, bool) { - let Self { - local, - pattern, - s0: _, - s1: _, - value, - span, - } = self; - - let mut constr = BoundedSeparated::new(span) - .then(TableConstrElem::positional( - pattern_to_constr(pattern).expr().boxed(), - )) - .then(TableConstrElem::positional(value)); - if local.is_some() { - constr = constr.then(TableConstrElem::named( - Ident::new("local", span), - Lit::Bool(true, span).expr().boxed(), - span, - )); - } - - let new = Call::constr( - Lit::Builtin(Builtin::Destructure, span).expr().boxed(), - constr.table_constr(), - span, - ); - (new.expr(), true) - } -} diff --git a/src/desugar/var.rs b/src/desugar/var.rs index 0ab0a76..887d115 100644 --- a/src/desugar/var.rs +++ b/src/desugar/var.rs @@ -1,4 +1,7 @@ -use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem, Var}; +use crate::ast::{ + Call, Expr, Field, Line, Lit, Separated, Space, StringLit, StringLitElem, TableConstr, + TableConstrElem, TableLitElem, Var, +}; use crate::builtin::Builtin; use crate::span::HasSpan; @@ -6,76 +9,152 @@ impl Var { pub fn desugar(self) -> (Expr, bool) { match self { Self::Access { - s0: _, + s0, index, - s1: _, + s1, span, } => { - let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span); - let new = Field::access(scope.expr().boxed(), index, span); - (new.expr(), true) + // `[ s0 index s1 ]` + // -> `'scope()[ s0 index s1 ]` + let scope = Expr::Call(Call::NoArg { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::Scope, span))), + s0: Space::empty(span), + s1: Space::empty(span), + span, + }); + let new = Expr::Field(Field::Access { + expr: Box::new(scope), + s0: Space::empty(span), + s1: s0, + index, + s2: s1, + span, + }); + (new, true) } Self::Assign { local: None, - s0: _, + s0, index, - s1: _, - s2: _, - s3: _, + s1, + s2, + s3, value, span, } => { - let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span); - let new = Field::assign(scope.expr().boxed(), index, value, span); - (new.expr(), true) + // `[ s0 index s1 ] s2 = s3 value` + // -> `'scope()[ s0 index s1 ] s2 = s3 value` + let scope = Expr::Call(Call::NoArg { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::Scope, span))), + s0: Space::empty(span), + s1: Space::empty(span), + span, + }); + let new = Expr::Field(Field::Assign { + expr: Box::new(scope), + s0: Space::empty(span), + s1: s0, + index, + s2: s1, + s3: s2, + s4: s3, + value, + span, + }); + (new, true) } Self::Assign { - local: Some(_), - s0: _, + local: Some(local), + s0, index, - s1: _, - s2: _, - s3: _, + s1, + s2, + s3, value, span, } => { - let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span); - let constr = BoundedSeparated::new(span) - .then(TableConstrElem::positional(scope.expr().boxed())) - .then(TableConstrElem::positional(index)) - .then(TableConstrElem::positional(value)) - .table_constr(); - let new = Call::constr( - Lit::Builtin(Builtin::SetRaw, span).expr().boxed(), + // `local [ s0 index s1 ] s2 = s3 value` + // --> `'setraw { 'scope(), local s0 index s1, s2 s3 value }` + let scope = Expr::Call(Call::NoArg { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::Scope, span))), + s0: Space::empty(span), + s1: Space::empty(span), + span, + }); + let elems = Separated::NonEmpty { + first_elem: TableConstrElem::Lit(TableLitElem::Positional(Box::new(scope))), + last_elems: vec![ + ( + (Space::empty(span), local.then_line(Line::Empty).then(s0)), + TableConstrElem::Lit(TableLitElem::Positional(index)), + ), + ( + (s1, s2.then_line(Line::Empty).then(s3)), + TableConstrElem::Lit(TableLitElem::Positional(value)), + ), + ], + trailing: None, + span, + }; + let constr = TableConstr { + s0: Space::empty(span), + elems, + s1: Space::empty(span), + span, + }; + let new = Expr::Call(Call::Constr { + expr: Box::new(Expr::Lit(Lit::Builtin(Builtin::SetRaw, span))), + s0: Space::empty(span), constr, span, - ); - (new.expr(), true) + }); + (new, true) } Self::AccessIdent(name) => { + // `name` + // -> `[ name_str ]` let span = name.span(); - let new = Self::access(StringLit::from_ident(name).lit().expr().boxed(), span); - (new.expr(), true) + let name_str = Expr::Lit(Lit::String(StringLit { + elems: vec![StringLitElem::Plain(name.name)], + span, + })); + let new = Expr::Var(Self::Access { + s0: Space::empty(span), + index: Box::new(name_str), + s1: Space::empty(span), + span, + }); + (new, true) } Self::AssignIdent { local, name, - s0: _, - s1: _, + s0, + s1, value, span, } => { - let new = Self::assign( - local.is_some(), - StringLit::from_ident(name).lit().expr().boxed(), + // `local name s0 = s1 value` + // -> `local [ name_str ] s0 = s1 value` + let name_str = Expr::Lit(Lit::String(StringLit { + elems: vec![StringLitElem::Plain(name.name)], + span: name.span, + })); + let new = Expr::Var(Self::Assign { + local, + s0: Space::empty(span), + index: Box::new(name_str), + s1: Space::empty(span), + s2: s0, + s3: s1, value, span, - ); - (new.expr(), true) + }); + (new, true) } } } diff --git a/src/main.rs b/src/main.rs index 0ac0aed..a40fedc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,7 +80,7 @@ fn main() -> anyhow::Result<()> { .parse(stream) .map_err(|e| anyhow!("{e:?}"))?; - print!("{}", pretty::pretty_to_string(program, 100)); + println!("{}", pretty::pretty_to_string(program, 100)); } Command::Desugar { diff --git a/src/parser/basic.rs b/src/parser/basic.rs index 9ca8d09..2a65c68 100644 --- a/src/parser/basic.rs +++ b/src/parser/basic.rs @@ -3,7 +3,7 @@ use chumsky::prelude::*; use chumsky::text::Character; -use crate::ast::{BoundedSeparated, Ident, Line, Space}; +use crate::ast::{Ident, Line, Separated, Space}; use crate::span::Span; pub type Error = Simple; @@ -62,42 +62,23 @@ pub fn local(space: EParser) -> EParser> { // This function is more of a utility function. Because of this and to keep the // code nicer, I have decided that the rules specified in the `parser` module // don't apply to it. -pub fn bounded_separated( - space: impl Parser + Clone + 'static, - start: impl Parser + 'static, - end: impl Parser + 'static, - separator: impl Parser + 'static, +pub fn separated_by( elem: impl Parser + Clone + 'static, -) -> EParser> { - start - .ignore_then(space.clone()) - .then( - elem.clone() - .then(space.clone()) - .then_ignore(separator) - .then(space.clone()) - .repeated(), - ) - .then(elem.then(space).or_not()) - .then_ignore(end) - .map_with_span(|((s0, first_elems), last_elem), span| { - let mut space_before_elem = s0; - let mut elems = vec![]; - for ((elem, s1), s2) in first_elems { - elems.push((space_before_elem, elem, s1)); - space_before_elem = s2; - } - let trailing = if let Some((elem, s1)) = last_elem { - elems.push((space_before_elem, elem, s1)); - None - } else { - Some(space_before_elem) - }; - BoundedSeparated { - elems, + separator: impl Parser + 'static, + trailing_separator: impl Parser + 'static, +) -> EParser> { + elem.clone() + .then(separator.then(elem).repeated()) + .then(trailing_separator.or_not()) + .or_not() + .map_with_span(|s, span| match s { + Some(((first_elem, last_elems), trailing)) => Separated::NonEmpty { + first_elem, + last_elems, trailing, span, - } + }, + None => Separated::Empty(span), }) .boxed() } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index bd2b373..3bae445 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -18,7 +18,7 @@ fn atom_paren( .then_ignore(just(')')) .map_with_span(|((s0, inner), s1), span| Expr::Paren { s0, - inner: inner.boxed(), + inner: Box::new(inner), s1, span, }) @@ -63,11 +63,11 @@ fn left_assoc( over.then(op_over.repeated()) .foldl(|left, (s0, op, s1, right)| Expr::BinOp { span: left.span().join(right.span()), - left: left.boxed(), + left: Box::new(left), s0, op, s1, - right: right.boxed(), + right: Box::new(right), }) .boxed() } @@ -89,11 +89,11 @@ fn right_assoc( .then(over) .foldr(|(left, s0, op, s1), right| Expr::BinOp { span: left.span().join(right.span()), - left: left.boxed(), + left: Box::new(left), s0, op, s1, - right: right.boxed(), + right: Box::new(right), }) .boxed() } diff --git a/src/parser/func_def.rs b/src/parser/func_def.rs index 3bb273d..54765f1 100644 --- a/src/parser/func_def.rs +++ b/src/parser/func_def.rs @@ -19,7 +19,7 @@ fn func_def_anon_no_arg( s0, s1, s2, - body: body.boxed(), + body: Box::new(body), span, }) } @@ -45,7 +45,7 @@ fn func_def_anon_arg( arg, s2, s3, - body: body.boxed(), + body: Box::new(body), span, }, ) @@ -65,7 +65,7 @@ fn func_def_anon_destr( s0, pattern, s1, - body: body.boxed(), + body: Box::new(body), span, }) } @@ -94,7 +94,7 @@ fn func_def_named_no_arg( s1, s2, s3, - body: body.boxed(), + body: Box::new(body), span, }, ) @@ -128,7 +128,7 @@ fn func_def_named_arg( arg, s3, s4, - body: body.boxed(), + body: Box::new(body), span, }, ) @@ -157,7 +157,7 @@ fn func_def_named_destr( s1, pattern, s2, - body: body.boxed(), + body: Box::new(body), span, } }) diff --git a/src/parser/lit.rs b/src/parser/lit.rs index e368684..0bdf12a 100644 --- a/src/parser/lit.rs +++ b/src/parser/lit.rs @@ -7,7 +7,7 @@ use crate::ast::{ }; use crate::builtin::Builtin; -use super::basic::{bounded_separated, EParser, Error}; +use super::basic::{separated_by, EParser, Error}; fn builtin_lit() -> impl Parser { just('\'').ignore_then(choice(( @@ -132,7 +132,7 @@ pub fn table_lit_elem( ) -> EParser { let positional = expr .clone() - .map(|value| TableLitElem::Positional(value.boxed())); + .map(|value| TableLitElem::Positional(Box::new(value))); let named = ident .then(space.clone()) @@ -143,7 +143,7 @@ pub fn table_lit_elem( name, s0, s1, - value: value.boxed(), + value: Box::new(value), span, }); @@ -154,14 +154,20 @@ fn table_lit( space: EParser, table_lit_elem: EParser, ) -> impl Parser { - bounded_separated( - space, - just("'{").to(()), - just('}').to(()), - just(',').to(()), - table_lit_elem, - ) - .map(TableLit) + let separator = space.clone().then_ignore(just(',')).then(space.clone()); + let trailing_separator = space.clone().then_ignore(just(',')); + + space + .clone() + .then(separated_by(table_lit_elem, separator, trailing_separator)) + .then(space) + .delimited_by(just("'{"), just('}')) + .map_with_span(|((s0, elems), s1), span| TableLit { + s0, + elems, + s1, + span, + }) } pub fn lit(space: EParser, table_lit_elem: EParser) -> EParser { diff --git a/src/parser/prefix.rs b/src/parser/prefix.rs index f53c78f..ddae0bd 100644 --- a/src/parser/prefix.rs +++ b/src/parser/prefix.rs @@ -17,7 +17,7 @@ enum Prefix { impl Prefix { fn into_expr(self, span: Span, expr: Expr) -> Expr { - let expr = expr.boxed(); + let expr = Box::new(expr); match self { Self::Neg { minus, s0 } => Expr::Neg { minus, diff --git a/src/parser/program.rs b/src/parser/program.rs index 96c3cab..0b46f94 100644 --- a/src/parser/program.rs +++ b/src/parser/program.rs @@ -4,7 +4,7 @@ use chumsky::prelude::*; use crate::ast::{Expr, Program, Space, TableLitElem}; -use super::basic::{bounded_separated, EParser}; +use super::basic::{separated_by, EParser}; pub fn program( space: EParser, @@ -17,17 +17,21 @@ pub fn program( .then(space.clone()) .map_with_span(|((s0, expr), s1), span| Program::Expr { s0, expr, s1, span }); + let separator = space.clone().then_ignore(just(',')).then(space.clone()); + let trailing_separator = space.clone().then_ignore(just(',')); let module = space .clone() .then_ignore(text::keyword("module")) - .then(bounded_separated( - space, - empty(), - empty(), - just(',').to(()), - table_lit_elem, - )) - .map_with_span(|(s0, elems), span| Program::Module { s0, elems, span }); + .then(space.clone()) + .then(separated_by(table_lit_elem, separator, trailing_separator)) + .then(space.clone()) + .map_with_span(|(((s0, s1), elems), s2), span| Program::Module { + s0, + s1, + elems, + s2, + span, + }); module.or(lit).boxed() } diff --git a/src/parser/suffix.rs b/src/parser/suffix.rs index ca25830..cb02008 100644 --- a/src/parser/suffix.rs +++ b/src/parser/suffix.rs @@ -57,38 +57,31 @@ enum Suffix { impl Suffix { fn into_expr(self, span: Span, expr: Expr) -> Expr { - let expr = expr.boxed(); + let expr = Box::new(expr); match self { - Self::CallArg { s0, s1, arg, s2 } => Call::Arg { + Self::CallArg { s0, s1, arg, s2 } => Expr::Call(Call::Arg { expr, s0, s1, arg, s2, span, - } - .expr(), - - Self::CallNoArg { s0, s1 } => Call::NoArg { expr, s0, s1, span }.expr(), - - Self::CallConstr { s0, constr } => Call::Constr { + }), + Self::CallNoArg { s0, s1 } => Expr::Call(Call::NoArg { expr, s0, s1, span }), + Self::CallConstr { s0, constr } => Expr::Call(Call::Constr { expr, s0, constr, span, - } - .expr(), - - Self::FieldAccess { s0, s1, index, s2 } => Field::Access { + }), + Self::FieldAccess { s0, s1, index, s2 } => Expr::Field(Field::Access { expr, s0, s1, index, s2, span, - } - .expr(), - + }), Self::FieldAssign { s0, s1, @@ -97,7 +90,7 @@ impl Suffix { s3, s4, value, - } => Field::Assign { + } => Expr::Field(Field::Assign { expr, s0, s1, @@ -107,18 +100,14 @@ impl Suffix { s4, value, span, - } - .expr(), - - Self::FieldAccessIdent { s0, s1, ident } => Field::AccessIdent { + }), + Self::FieldAccessIdent { s0, s1, ident } => Expr::Field(Field::AccessIdent { expr, s0, s1, ident, span, - } - .expr(), - + }), Self::FieldAssignIdent { s0, s1, @@ -126,7 +115,7 @@ impl Suffix { s2, s3, value, - } => Field::AssignIdent { + } => Expr::Field(Field::AssignIdent { expr, s0, s1, @@ -135,8 +124,7 @@ impl Suffix { s3, value, span, - } - .expr(), + }), } } } @@ -155,7 +143,7 @@ fn suffix_call_arg( .map(|(((s0, s1), arg), s2)| Suffix::CallArg { s0, s1, - arg: arg.boxed(), + arg: Box::new(arg), s2, }) } @@ -192,7 +180,7 @@ fn suffix_field_access( .map(|(((s0, s1), index), s2)| Suffix::FieldAccess { s0, s1, - index: index.boxed(), + index: Box::new(index), s2, }) } @@ -216,11 +204,11 @@ fn suffix_field_assign( |((((((s0, s1), index), s2), s3), s4), value)| Suffix::FieldAssign { s0, s1, - index: index.boxed(), + index: Box::new(index), s2, s3, s4, - value: value.boxed(), + value: Box::new(value), }, ) } @@ -258,7 +246,7 @@ fn suffix_field_assign_ident( ident, s2, s3, - value: value.boxed(), + value: Box::new(value), }, ) } diff --git a/src/parser/table_constr.rs b/src/parser/table_constr.rs index b294329..fd6aefd 100644 --- a/src/parser/table_constr.rs +++ b/src/parser/table_constr.rs @@ -4,7 +4,7 @@ use chumsky::prelude::*; use crate::ast::{Expr, Space, TableConstr, TableConstrElem, TableLitElem}; -use super::basic::{bounded_separated, EParser, Error}; +use super::basic::{separated_by, EParser, Error}; fn table_constr_elem( space: EParser, @@ -25,11 +25,11 @@ fn table_constr_elem( .map_with_span( |(((((s0, index), s1), s2), s3), value), span| TableConstrElem::Indexed { s0, - index: index.boxed(), + index: Box::new(index), s1, s2, s3, - value: value.boxed(), + value: Box::new(value), span, }, ); @@ -43,13 +43,19 @@ pub fn table_constr( expr: EParser, ) -> EParser { let elem = table_constr_elem(space.clone(), table_lit_elem, expr); - bounded_separated( - space, - just('{').to(()), - just('}').to(()), - just(',').to(()), - elem, - ) - .map(TableConstr) - .boxed() + let separator = space.clone().then_ignore(just(',')).then(space.clone()); + let trailing_separator = space.clone().then_ignore(just(',')); + + space + .clone() + .then(separated_by(elem, separator, trailing_separator)) + .then(space) + .delimited_by(just('{'), just('}')) + .map_with_span(|((s0, elems), s1), span| TableConstr { + s0, + elems, + s1, + span, + }) + .boxed() } diff --git a/src/parser/table_destr.rs b/src/parser/table_destr.rs index c215a19..4fb5eda 100644 --- a/src/parser/table_destr.rs +++ b/src/parser/table_destr.rs @@ -4,7 +4,7 @@ use chumsky::prelude::*; use crate::ast::{Expr, Ident, Space, TableDestr, TablePattern, TablePatternElem}; -use super::basic::{bounded_separated, EParser, Error}; +use super::basic::{separated_by, EParser, Error}; fn table_pattern_elem( space: EParser, @@ -31,15 +31,21 @@ fn table_pattern_elem( pub fn table_pattern(space: EParser, ident: EParser) -> EParser { let elem = table_pattern_elem(space.clone(), ident); - bounded_separated( - space, - just('{').to(()), - just('}').to(()), - just(',').to(()), - elem, - ) - .map(TablePattern) - .boxed() + let separator = space.clone().then_ignore(just(',')).then(space.clone()); + let trailing_separator = space.clone().then_ignore(just(',')); + + space + .clone() + .then(separated_by(elem, separator, trailing_separator)) + .then(space) + .delimited_by(just('{'), just('}')) + .map_with_span(|((s0, elems), s1), span| TablePattern { + s0, + elems, + s1, + span, + }) + .boxed() } pub fn table_destr( @@ -59,7 +65,7 @@ pub fn table_destr( pattern, s0, s1, - value: value.boxed(), + value: Box::new(value), span, }) .boxed() diff --git a/src/parser/var.rs b/src/parser/var.rs index c5806dc..80722e1 100644 --- a/src/parser/var.rs +++ b/src/parser/var.rs @@ -14,7 +14,7 @@ fn var_access(space: EParser, expr: EParser) -> impl Parser>(p: P, width: usize) -> p.pretty(&RcAllocator) .render(width, &mut out) .expect("p could not be rendered"); - let mut s = String::from_utf8(out).expect("p created non-utf8 string"); - s.push('\n'); - s + String::from_utf8(out).expect("p created non-utf8 string") } diff --git a/src/pretty/basic.rs b/src/pretty/basic.rs index abc55ee..b6812b0 100644 --- a/src/pretty/basic.rs +++ b/src/pretty/basic.rs @@ -1,8 +1,6 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; -use crate::ast::{BoundedSeparated, Ident}; - -use super::NEST_DEPTH; +use crate::ast::{Ident, Separated}; impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Ident { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { @@ -10,32 +8,40 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Ident { } } -impl BoundedSeparated { - pub fn pretty<'a, D, FE>( +impl Separated { + pub fn pretty<'a, D, FE, FS1, FS2>( self, allocator: &'a D, - start: DocBuilder<'a, D>, - end: DocBuilder<'a, D>, - separator: DocBuilder<'a, D>, - elem_pretty: FE, + elem_to_doc: FE, + separator_to_doc: FS1, + trailing_separator_to_doc: FS2, ) -> DocBuilder<'a, D> where D: DocAllocator<'a>, - D::Doc: Clone, FE: Fn(E) -> DocBuilder<'a, D>, + FS1: Fn(S1) -> DocBuilder<'a, D>, + FS2: Fn(S2) -> DocBuilder<'a, D>, { - let elems_empty = self.elems.is_empty(); - allocator - .intersperse( - self.elems - .into_iter() - .map(|(s0, elem, s1)| allocator.line().append(elem_pretty(elem))), - separator.clone(), - ) - .append(self.trailing.filter(|_| !elems_empty).map(|s| separator)) - .nest(NEST_DEPTH) - .append(allocator.line()) - .enclose(start, end) - .group() + match self { + Self::Empty(_) => allocator.nil(), + Self::NonEmpty { + first_elem, + last_elems, + trailing, + span: _span, + } => elem_to_doc(first_elem) + .append( + allocator.concat( + last_elems + .into_iter() + .map(|(s, e)| separator_to_doc(s).append(elem_to_doc(e))), + ), + ) + .append( + trailing + .map(trailing_separator_to_doc) + .unwrap_or_else(|| allocator.nil()), + ), + } } } diff --git a/src/pretty/call.rs b/src/pretty/call.rs index 6cf26fc..84d2b5b 100644 --- a/src/pretty/call.rs +++ b/src/pretty/call.rs @@ -2,11 +2,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Call; -impl<'a, D> Pretty<'a, D> for Call -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Call { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Arg { diff --git a/src/pretty/expr.rs b/src/pretty/expr.rs index ddf7634..7919963 100644 --- a/src/pretty/expr.rs +++ b/src/pretty/expr.rs @@ -22,11 +22,7 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for BinOp { } } -impl<'a, D> Pretty<'a, D> for Expr -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Expr { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Lit(lit) => lit.pretty(allocator), diff --git a/src/pretty/field.rs b/src/pretty/field.rs index eb547f0..ce5b28f 100644 --- a/src/pretty/field.rs +++ b/src/pretty/field.rs @@ -2,11 +2,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Field; -impl<'a, D> Pretty<'a, D> for Field -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Field { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Access { diff --git a/src/pretty/func_def.rs b/src/pretty/func_def.rs index dce23e6..6fd8140 100644 --- a/src/pretty/func_def.rs +++ b/src/pretty/func_def.rs @@ -2,11 +2,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::FuncDef; -impl<'a, D> Pretty<'a, D> for FuncDef -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for FuncDef { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::AnonNoArg { diff --git a/src/pretty/lit.rs b/src/pretty/lit.rs index 84f3055..45dd034 100644 --- a/src/pretty/lit.rs +++ b/src/pretty/lit.rs @@ -2,6 +2,8 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::{Lit, NumLit, StringLit, StringLitElem, TableLit, TableLitElem}; +use super::NEST_DEPTH; + impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for NumLit { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { allocator.text(format!("{self:?}")) @@ -30,11 +32,7 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for StringLit { } } -impl<'a, D> Pretty<'a, D> for TableLitElem -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableLitElem { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Positional(expr) => expr.pretty(allocator), @@ -52,27 +50,23 @@ where } } -impl<'a, D> Pretty<'a, D> for TableLit -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableLit { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - self.0.pretty( - allocator, - allocator.text("'{"), - allocator.text("}"), - allocator.text(","), - |e| e.pretty(allocator), - ) + self.elems + .pretty( + allocator, + |e| allocator.line().append(e.pretty(allocator)), + |(s0, s1)| allocator.text(","), + |s| allocator.text(","), + ) + .nest(NEST_DEPTH) + .append(allocator.line()) + .enclose(allocator.text("'{"), allocator.text("}")) + .group() } } -impl<'a, D> Pretty<'a, D> for Lit -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Lit { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Nil(_) => allocator.text("nil"), diff --git a/src/pretty/program.rs b/src/pretty/program.rs index 1ff7863..20c5cb1 100644 --- a/src/pretty/program.rs +++ b/src/pretty/program.rs @@ -2,11 +2,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Program; -impl<'a, D> Pretty<'a, D> for Program -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Program { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Expr { @@ -15,19 +11,22 @@ where s1, span: _, } => expr.pretty(allocator), - - Self::Module { s0, elems, span: _ } => { - allocator.text("module").append(allocator.line()).append( - allocator - .intersperse( - elems.elems.into_iter().map(|(s0, elem, s1)| { - allocator.line().append(elem.pretty(allocator)) - }), - allocator.text(","), - ) - .append(elems.trailing.map(|s| allocator.text(","))), - ) - } + Self::Module { + s0, + s1, + elems, + s2, + span: _, + } => allocator + .text("module") + .append(allocator.line()) + .append(allocator.line()) + .append(elems.pretty( + allocator, + |e| e.pretty(allocator), + |(s0, s1)| allocator.text(",").append(allocator.line()), + |s| allocator.text(","), + )), } } } diff --git a/src/pretty/table_constr.rs b/src/pretty/table_constr.rs index 3b3fdb1..5c9a058 100644 --- a/src/pretty/table_constr.rs +++ b/src/pretty/table_constr.rs @@ -2,11 +2,9 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::{TableConstr, TableConstrElem}; -impl<'a, D> Pretty<'a, D> for TableConstrElem -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +use super::NEST_DEPTH; + +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableConstrElem { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Lit(lit) => lit.pretty(allocator), @@ -27,18 +25,18 @@ where } } -impl<'a, D> Pretty<'a, D> for TableConstr -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableConstr { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - self.0.pretty( - allocator, - allocator.text("{"), - allocator.text("}"), - allocator.text(","), - |e| e.pretty(allocator), - ) + self.elems + .pretty( + allocator, + |e| allocator.line().append(e.pretty(allocator)), + |(s0, s1)| allocator.text(","), + |s| allocator.text(","), + ) + .nest(NEST_DEPTH) + .append(allocator.line()) + .braces() + .group() } } diff --git a/src/pretty/table_destr.rs b/src/pretty/table_destr.rs index 777d0ae..50fab7b 100644 --- a/src/pretty/table_destr.rs +++ b/src/pretty/table_destr.rs @@ -2,6 +2,8 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::{TableDestr, TablePattern, TablePatternElem}; +use super::NEST_DEPTH; + impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePatternElem { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { @@ -20,27 +22,23 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePatternElem { } } -impl<'a, D> Pretty<'a, D> for TablePattern -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePattern { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - self.0.pretty( - allocator, - allocator.text("{"), - allocator.text("}"), - allocator.text(","), - |e| e.pretty(allocator), - ) + self.elems + .pretty( + allocator, + |e| allocator.line().append(e.pretty(allocator)), + |(s0, s1)| allocator.text(","), + |s| allocator.text(","), + ) + .nest(NEST_DEPTH) + .append(allocator.line()) + .braces() + .group() } } -impl<'a, D> Pretty<'a, D> for TableDestr -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableDestr { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { // TODO Handle spaces self.local diff --git a/src/pretty/var.rs b/src/pretty/var.rs index 662b34e..a3b5418 100644 --- a/src/pretty/var.rs +++ b/src/pretty/var.rs @@ -2,11 +2,7 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Var; -impl<'a, D> Pretty<'a, D> for Var -where - D: DocAllocator<'a>, - D::Doc: Clone, -{ +impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Var { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Access {