diff --git a/src/ast/basic.rs b/src/ast/basic.rs index b9e5df0..8a722c3 100644 --- a/src/ast/basic.rs +++ b/src/ast/basic.rs @@ -2,6 +2,8 @@ use std::fmt; use crate::span::{HasSpan, Span}; +use super::{TableConstr, TableConstrElem, TableLit, TableLitElem}; + #[derive(Clone)] pub enum Line { Empty, @@ -37,17 +39,6 @@ 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)] @@ -78,21 +69,76 @@ impl HasSpan for Ident { } #[derive(Debug, Clone)] -pub enum Separated { - Empty(Span), - NonEmpty { - first_elem: E, - last_elems: Vec<(S1, E)>, - trailing: Option, - span: Span, - }, +pub struct BoundedSeparated { + pub elems: Vec<(Space, E, Space)>, + pub trailing: Option, + pub span: Span, } -impl HasSpan for Separated { +impl HasSpan for BoundedSeparated { fn span(&self) -> Span { - match self { - Self::Empty(span) => *span, - Self::NonEmpty { span, .. } => *span, - } + self.span + } +} + +impl BoundedSeparated { + pub fn new(span: Span) -> Self { + Self { + elems: vec![], + trailing: None, + 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 547e1f1..6a9d13e 100644 --- a/src/ast/call.rs +++ b/src/ast/call.rs @@ -46,3 +46,38 @@ 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 9b10502..d0fa1ff 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -242,3 +242,9 @@ 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 1972686..5afd98f 100644 --- a/src/ast/field.rs +++ b/src/ast/field.rs @@ -67,3 +67,34 @@ 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 e1e89ad..5cdd210 100644 --- a/src/ast/func_def.rs +++ b/src/ast/func_def.rs @@ -99,3 +99,41 @@ 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 63ced41..ecd166c 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::{Expr, Ident, Separated, Space}; +use super::{BoundedSeparated, Expr, Ident, Space}; #[derive(Clone)] pub enum NumLitStr { @@ -93,6 +93,19 @@ 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 @@ -125,20 +138,31 @@ impl HasSpan for TableLitElem { } } -/// `'{ a, foo: b }` -/// -/// Structure: `'{ s0 elems s1 }` -#[derive(Debug, Clone)] -pub struct TableLit { - pub s0: Space, - pub elems: Separated, - pub s1: Space, - pub span: Span, +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 }` +#[derive(Debug, Clone)] +pub struct TableLit(pub BoundedSeparated); + impl HasSpan for TableLit { fn span(&self) -> Span { - self.span + self.0.span() + } +} + +impl TableLit { + pub fn lit(self) -> Lit { + Lit::Table(self) } } @@ -196,3 +220,9 @@ 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 2020225..a77abf4 100644 --- a/src/ast/program.rs +++ b/src/ast/program.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{Expr, Separated, Space, TableLitElem}; +use super::{BoundedSeparated, Expr, Space, TableLitElem}; #[derive(Debug, Clone)] pub enum Program { @@ -12,12 +12,10 @@ pub enum Program { span: Span, }, - /// Structure: `s0 module s1 elems s2` + /// Structure: `s0 module elems` Module { s0: Space, - s1: Space, - elems: Separated, - s2: Space, + elems: BoundedSeparated, span: Span, }, } diff --git a/src/ast/table_constr.rs b/src/ast/table_constr.rs index d347ce1..38c409d 100644 --- a/src/ast/table_constr.rs +++ b/src/ast/table_constr.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{Expr, Separated, Space, TableLitElem}; +use super::{BoundedSeparated, Expr, Ident, Space, TableLitElem}; #[derive(Debug, Clone)] pub enum TableConstrElem { @@ -30,19 +30,28 @@ impl HasSpan for TableConstrElem { } } -/// `{ a, b, foo: c, [d]: e }` -/// -/// Structure: `{ s0 elems s1 }` -#[derive(Debug, Clone)] -pub struct TableConstr { - pub s0: Space, - pub elems: Separated, - pub s1: Space, - pub span: Span, +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 }` +#[derive(Debug, Clone)] +pub struct TableConstr(pub BoundedSeparated); + impl HasSpan for TableConstr { fn span(&self) -> Span { - self.span + self.0.span() + } +} + +impl TableConstr { + pub fn expr(self) -> Expr { + Expr::TableConstr(self) } } diff --git a/src/ast/table_destr.rs b/src/ast/table_destr.rs index eb88dcb..d94b4eb 100644 --- a/src/ast/table_destr.rs +++ b/src/ast/table_destr.rs @@ -1,6 +1,6 @@ use crate::span::{HasSpan, Span}; -use super::{Expr, Ident, Separated, Space}; +use super::{BoundedSeparated, Expr, Ident, Space}; // TODO Make table patterns recursive @@ -34,16 +34,11 @@ impl HasSpan for TablePatternElem { /// /// Structure: `{ s0 elems s1 }` #[derive(Debug, Clone)] -pub struct TablePattern { - pub s0: Space, - pub elems: Separated, - pub s1: Space, - pub span: Span, -} +pub struct TablePattern(pub BoundedSeparated); impl HasSpan for TablePattern { fn span(&self) -> Span { - self.span + self.0.span() } } @@ -66,3 +61,26 @@ 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 6ec6026..d5f0eab 100644 --- a/src/ast/var.rs +++ b/src/ast/var.rs @@ -56,3 +56,54 @@ 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 f0e504d..6ef18c8 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -12,6 +12,21 @@ 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 { @@ -26,6 +41,21 @@ 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 7402709..2be2a3f 100644 --- a/src/desugar.rs +++ b/src/desugar.rs @@ -2,6 +2,9 @@ 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 ba2fde0..156445f 100644 --- a/src/desugar/basic.rs +++ b/src/desugar/basic.rs @@ -1,36 +1,24 @@ -use crate::ast::Separated; +use crate::ast::BoundedSeparated; -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) +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)); } } + + let new = Self { + elems, + trailing: self.trailing, + span: self.span, + }; + (new, desugared) } } diff --git a/src/desugar/call.rs b/src/desugar/call.rs index 690f99e..c231875 100644 --- a/src/desugar/call.rs +++ b/src/desugar/call.rs @@ -1,4 +1,4 @@ -use crate::ast::{Call, Expr, Ident, Lit, Separated, Space, TableLit, TableLitElem}; +use crate::ast::{BoundedSeparated, Call, Expr, Ident, Lit, TableLitElem}; // TODO Add span for just the parentheses to ast, or limit span to parentheses @@ -7,74 +7,37 @@ impl Call { match self { Self::Arg { expr, - s0, - s1, + s0: _, + s1: _, arg, - s2, + s2: _, span, } => { - // `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) + 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) } - 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::NoArg { + expr, + s0: _, + s1: _, + span, + } => { + let new = Self::arg(expr, Lit::Nil(span).expr().boxed(), span); + (new.expr(), true) } Self::Constr { expr, - s0, + s0: _, constr, span, } => { - // `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) + let new = Self::arg(expr, constr.expr().boxed(), span); + (new.expr(), true) } } } diff --git a/src/desugar/expr.rs b/src/desugar/expr.rs index 0c8e569..b7f4b08 100644 --- a/src/desugar/expr.rs +++ b/src/desugar/expr.rs @@ -1,84 +1,78 @@ -use crate::ast::Expr; +use crate::ast::{BinOp, BoundedSeparated, Call, Expr, Lit, TableConstrElem}; +use crate::builtin::Builtin; impl Expr { pub fn desugar(self) -> (Self, bool) { match self { Self::Lit(lit) => { let (lit, desugared) = lit.desugar(); - (Self::Lit(lit), desugared) + (lit.expr(), desugared) } Self::Call(call) => call.desugar(), Self::Field(field) => field.desugar(), Self::Var(var) => var.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::TableConstr(constr) => constr.desugar(), + Self::TableDestr(destr) => destr.desugar(), + Self::FuncDef(def) => def.desugar(), Self::Paren { - s0, + s0: _, inner, - s1, - span, - } => ( - Self::Paren { - s0, - inner, - s1, - span, - }, - false, - ), // TODO Implement + s1: _, + span: _, + } => (*inner, true), Self::Neg { minus, - s0, + s0: _, expr, span, - } => ( - Self::Neg { - minus, - s0, - expr, - span, - }, - false, - ), // TODO Implement + } => { + let new = Call::arg(Lit::Builtin(Builtin::Neg, minus).expr().boxed(), expr, span); + (new.expr(), true) + } Self::Not { not, - s0, + s0: _, expr, span, - } => ( - Self::Not { - not, - s0, - expr, - span, - }, - false, - ), // TODO Implement + } => { + let new = Call::arg(Lit::Builtin(Builtin::Not, not).expr().boxed(), expr, span); + (new.expr(), true) + } Self::BinOp { left, - s0, + s0: _, op, - s1, + s1: _, right, span, - } => ( - Self::BinOp { - left, - s0, - op, - s1, - right, - span, - }, - false, - ), // TODO Implement + } => { + 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) + } } } } diff --git a/src/desugar/field.rs b/src/desugar/field.rs index 7618326..3c269e2 100644 --- a/src/desugar/field.rs +++ b/src/desugar/field.rs @@ -1,7 +1,4 @@ -use crate::ast::{ - Call, Expr, Field, Line, Lit, Separated, Space, StringLit, StringLitElem, TableConstr, - TableConstrElem, TableLitElem, -}; +use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem}; use crate::builtin::Builtin; impl Field { @@ -9,133 +6,80 @@ impl Field { match self { Self::Access { expr, - s0, - s1, + s0: _, + s1: _, index, - s2, + s2: _, span, } => { - // ` 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, + 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(), constr, span, - }); - (new, true) + ); + (new.expr(), true) } Self::Assign { expr, - s0, - s1, + s0: _, + s1: _, index, - s2, - s3, - s4, + s2: _, + s3: _, + s4: _, value, span, } => { - // `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, + 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(), constr, span, - }); - (new, true) + ); + (new.expr(), true) } Self::AccessIdent { expr, - s0, - s1, + s0: _, + s1: _, ident, span, } => { - // `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 { + let new = Self::access( expr, - s0, - s1, - index: Box::new(ident_str), - s2: Space::empty(span), + StringLit::from_ident(ident).lit().expr().boxed(), span, - }); - (new, true) + ); + (new.expr(), true) } Self::AssignIdent { expr, - s0, - s1, + s0: _, + s1: _, ident, - s2, - s3, + s2: _, + s3: _, value, span, } => { - // `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 { + let new = Self::assign( expr, - s0, - s1, - index: Box::new(ident_str), - s2: Space::empty(span), - s3: s2, - s4: s3, + StringLit::from_ident(ident).lit().expr().boxed(), value, span, - }); - (new, true) + ); + (new.expr(), true) } } } diff --git a/src/desugar/func_def.rs b/src/desugar/func_def.rs new file mode 100644 index 0000000..2a549d1 --- /dev/null +++ b/src/desugar/func_def.rs @@ -0,0 +1,128 @@ +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 348c3c7..3b6fd80 100644 --- a/src/desugar/lit.rs +++ b/src/desugar/lit.rs @@ -1,11 +1,11 @@ -use crate::ast::{Lit, TableLit, TableLitElem}; +use crate::ast::{Expr, Lit, TableLit, TableLitElem}; impl TableLitElem { pub fn desugar(self) -> (Self, bool) { match self { Self::Positional(expr) => { let (expr, desugared) = expr.desugar(); - (Self::Positional(Box::new(expr)), desugared) + (Self::Positional(expr.boxed()), desugared) } Self::Named { @@ -20,7 +20,7 @@ impl TableLitElem { name, s0, s1, - value: Box::new(value), + value: value.boxed(), span, }; (new, desugared) @@ -31,21 +31,18 @@ impl TableLitElem { impl TableLit { pub fn desugar(self) -> (Self, bool) { - 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) + 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) + } } } @@ -54,7 +51,7 @@ impl Lit { match self { Self::Table(table) => { let (table, desugared) = table.desugar(); - (Self::Table(table), desugared) + (table.lit(), desugared) } lit => (lit, false), diff --git a/src/desugar/program.rs b/src/desugar/program.rs index 7960cbc..615998b 100644 --- a/src/desugar/program.rs +++ b/src/desugar/program.rs @@ -1,4 +1,4 @@ -use crate::ast::{Expr, Lit, Program, Space, TableLit}; +use crate::ast::{Program, Space}; impl Program { pub fn desugar(self) -> (Self, bool) { @@ -9,24 +9,12 @@ impl Program { (new, desugared) } - 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, - }; + Self::Module { s0, elems, span } => { + // `s0 module elems` + // -> `s0 table` let new = Self::Expr { s0, - expr: Expr::Lit(Lit::Table(table)), + expr: elems.table_lit().lit().expr(), s1: Space::empty(span), span, }; diff --git a/src/desugar/table_constr.rs b/src/desugar/table_constr.rs new file mode 100644 index 0000000..263d9d4 --- /dev/null +++ b/src/desugar/table_constr.rs @@ -0,0 +1,39 @@ +use crate::ast::{ + BoundedSeparated, Expr, Field, Ident, TableConstr, TableConstrElem, TableLitElem, +}; +use crate::span::HasSpan; + +impl TableConstr { + pub fn desugar(self) -> (Expr, bool) { + let span = self.span(); + + 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)), + }); + + 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) + } +} diff --git a/src/desugar/table_destr.rs b/src/desugar/table_destr.rs new file mode 100644 index 0000000..2216977 --- /dev/null +++ b/src/desugar/table_destr.rs @@ -0,0 +1,63 @@ +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 887d115..0ab0a76 100644 --- a/src/desugar/var.rs +++ b/src/desugar/var.rs @@ -1,7 +1,4 @@ -use crate::ast::{ - Call, Expr, Field, Line, Lit, Separated, Space, StringLit, StringLitElem, TableConstr, - TableConstrElem, TableLitElem, Var, -}; +use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem, Var}; use crate::builtin::Builtin; use crate::span::HasSpan; @@ -9,152 +6,76 @@ impl Var { pub fn desugar(self) -> (Expr, bool) { match self { Self::Access { - s0, + s0: _, index, - s1, + s1: _, span, } => { - // `[ 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) + 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) } Self::Assign { local: None, - s0, + s0: _, index, - s1, - s2, - s3, + s1: _, + s2: _, + s3: _, value, span, } => { - // `[ 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) + 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) } Self::Assign { - local: Some(local), - s0, + local: Some(_), + s0: _, index, - s1, - s2, - s3, + s1: _, + s2: _, + s3: _, value, span, } => { - // `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), + 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(), constr, span, - }); - (new, true) + ); + (new.expr(), true) } Self::AccessIdent(name) => { - // `name` - // -> `[ name_str ]` let span = name.span(); - 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) + let new = Self::access(StringLit::from_ident(name).lit().expr().boxed(), span); + (new.expr(), true) } Self::AssignIdent { local, name, - s0, - s1, + s0: _, + s1: _, value, span, } => { - // `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, + let new = Self::assign( + local.is_some(), + StringLit::from_ident(name).lit().expr().boxed(), value, span, - }); - (new, true) + ); + (new.expr(), true) } } } diff --git a/src/main.rs b/src/main.rs index a40fedc..0ac0aed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,7 +80,7 @@ fn main() -> anyhow::Result<()> { .parse(stream) .map_err(|e| anyhow!("{e:?}"))?; - println!("{}", pretty::pretty_to_string(program, 100)); + print!("{}", pretty::pretty_to_string(program, 100)); } Command::Desugar { diff --git a/src/parser/basic.rs b/src/parser/basic.rs index 2a65c68..9ca8d09 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::{Ident, Line, Separated, Space}; +use crate::ast::{BoundedSeparated, Ident, Line, Space}; use crate::span::Span; pub type Error = Simple; @@ -62,23 +62,42 @@ 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 separated_by( +pub fn bounded_separated( + space: impl Parser + Clone + 'static, + start: impl Parser + 'static, + end: impl Parser + 'static, + separator: impl Parser + 'static, elem: impl Parser + Clone + 'static, - 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, +) -> 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, trailing, span, - }, - None => Separated::Empty(span), + } }) .boxed() } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 3bae445..bd2b373 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: Box::new(inner), + inner: inner.boxed(), 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: Box::new(left), + left: left.boxed(), s0, op, s1, - right: Box::new(right), + right: right.boxed(), }) .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: Box::new(left), + left: left.boxed(), s0, op, s1, - right: Box::new(right), + right: right.boxed(), }) .boxed() } diff --git a/src/parser/func_def.rs b/src/parser/func_def.rs index 54765f1..3bb273d 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: Box::new(body), + body: body.boxed(), span, }) } @@ -45,7 +45,7 @@ fn func_def_anon_arg( arg, s2, s3, - body: Box::new(body), + body: body.boxed(), span, }, ) @@ -65,7 +65,7 @@ fn func_def_anon_destr( s0, pattern, s1, - body: Box::new(body), + body: body.boxed(), span, }) } @@ -94,7 +94,7 @@ fn func_def_named_no_arg( s1, s2, s3, - body: Box::new(body), + body: body.boxed(), span, }, ) @@ -128,7 +128,7 @@ fn func_def_named_arg( arg, s3, s4, - body: Box::new(body), + body: body.boxed(), span, }, ) @@ -157,7 +157,7 @@ fn func_def_named_destr( s1, pattern, s2, - body: Box::new(body), + body: body.boxed(), span, } }) diff --git a/src/parser/lit.rs b/src/parser/lit.rs index 0bdf12a..e368684 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::{separated_by, EParser, Error}; +use super::basic::{bounded_separated, 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(Box::new(value))); + .map(|value| TableLitElem::Positional(value.boxed())); let named = ident .then(space.clone()) @@ -143,7 +143,7 @@ pub fn table_lit_elem( name, s0, s1, - value: Box::new(value), + value: value.boxed(), span, }); @@ -154,20 +154,14 @@ fn table_lit( space: EParser, table_lit_elem: EParser, ) -> impl Parser { - 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, - }) + bounded_separated( + space, + just("'{").to(()), + just('}').to(()), + just(',').to(()), + table_lit_elem, + ) + .map(TableLit) } pub fn lit(space: EParser, table_lit_elem: EParser) -> EParser { diff --git a/src/parser/prefix.rs b/src/parser/prefix.rs index ddae0bd..f53c78f 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 = Box::new(expr); + let expr = expr.boxed(); match self { Self::Neg { minus, s0 } => Expr::Neg { minus, diff --git a/src/parser/program.rs b/src/parser/program.rs index 0b46f94..96c3cab 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::{separated_by, EParser}; +use super::basic::{bounded_separated, EParser}; pub fn program( space: EParser, @@ -17,21 +17,17 @@ 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(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, - }); + .then(bounded_separated( + space, + empty(), + empty(), + just(',').to(()), + table_lit_elem, + )) + .map_with_span(|(s0, elems), span| Program::Module { s0, elems, span }); module.or(lit).boxed() } diff --git a/src/parser/suffix.rs b/src/parser/suffix.rs index cb02008..ca25830 100644 --- a/src/parser/suffix.rs +++ b/src/parser/suffix.rs @@ -57,31 +57,38 @@ enum Suffix { impl Suffix { fn into_expr(self, span: Span, expr: Expr) -> Expr { - let expr = Box::new(expr); + let expr = expr.boxed(); match self { - Self::CallArg { s0, s1, arg, s2 } => Expr::Call(Call::Arg { + Self::CallArg { s0, s1, arg, s2 } => Call::Arg { expr, s0, s1, arg, s2, span, - }), - Self::CallNoArg { s0, s1 } => Expr::Call(Call::NoArg { expr, s0, s1, span }), - Self::CallConstr { s0, constr } => Expr::Call(Call::Constr { + } + .expr(), + + Self::CallNoArg { s0, s1 } => Call::NoArg { expr, s0, s1, span }.expr(), + + Self::CallConstr { s0, constr } => Call::Constr { expr, s0, constr, span, - }), - Self::FieldAccess { s0, s1, index, s2 } => Expr::Field(Field::Access { + } + .expr(), + + Self::FieldAccess { s0, s1, index, s2 } => Field::Access { expr, s0, s1, index, s2, span, - }), + } + .expr(), + Self::FieldAssign { s0, s1, @@ -90,7 +97,7 @@ impl Suffix { s3, s4, value, - } => Expr::Field(Field::Assign { + } => Field::Assign { expr, s0, s1, @@ -100,14 +107,18 @@ impl Suffix { s4, value, span, - }), - Self::FieldAccessIdent { s0, s1, ident } => Expr::Field(Field::AccessIdent { + } + .expr(), + + Self::FieldAccessIdent { s0, s1, ident } => Field::AccessIdent { expr, s0, s1, ident, span, - }), + } + .expr(), + Self::FieldAssignIdent { s0, s1, @@ -115,7 +126,7 @@ impl Suffix { s2, s3, value, - } => Expr::Field(Field::AssignIdent { + } => Field::AssignIdent { expr, s0, s1, @@ -124,7 +135,8 @@ impl Suffix { s3, value, span, - }), + } + .expr(), } } } @@ -143,7 +155,7 @@ fn suffix_call_arg( .map(|(((s0, s1), arg), s2)| Suffix::CallArg { s0, s1, - arg: Box::new(arg), + arg: arg.boxed(), s2, }) } @@ -180,7 +192,7 @@ fn suffix_field_access( .map(|(((s0, s1), index), s2)| Suffix::FieldAccess { s0, s1, - index: Box::new(index), + index: index.boxed(), s2, }) } @@ -204,11 +216,11 @@ fn suffix_field_assign( |((((((s0, s1), index), s2), s3), s4), value)| Suffix::FieldAssign { s0, s1, - index: Box::new(index), + index: index.boxed(), s2, s3, s4, - value: Box::new(value), + value: value.boxed(), }, ) } @@ -246,7 +258,7 @@ fn suffix_field_assign_ident( ident, s2, s3, - value: Box::new(value), + value: value.boxed(), }, ) } diff --git a/src/parser/table_constr.rs b/src/parser/table_constr.rs index fd6aefd..b294329 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::{separated_by, EParser, Error}; +use super::basic::{bounded_separated, 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: Box::new(index), + index: index.boxed(), s1, s2, s3, - value: Box::new(value), + value: value.boxed(), span, }, ); @@ -43,19 +43,13 @@ pub fn table_constr( expr: EParser, ) -> EParser { let elem = table_constr_elem(space.clone(), table_lit_elem, expr); - 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() + bounded_separated( + space, + just('{').to(()), + just('}').to(()), + just(',').to(()), + elem, + ) + .map(TableConstr) + .boxed() } diff --git a/src/parser/table_destr.rs b/src/parser/table_destr.rs index 4fb5eda..c215a19 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::{separated_by, EParser, Error}; +use super::basic::{bounded_separated, EParser, Error}; fn table_pattern_elem( space: EParser, @@ -31,21 +31,15 @@ fn table_pattern_elem( pub fn table_pattern(space: EParser, ident: EParser) -> EParser { let elem = table_pattern_elem(space.clone(), ident); - 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() + bounded_separated( + space, + just('{').to(()), + just('}').to(()), + just(',').to(()), + elem, + ) + .map(TablePattern) + .boxed() } pub fn table_destr( @@ -65,7 +59,7 @@ pub fn table_destr( pattern, s0, s1, - value: Box::new(value), + value: value.boxed(), span, }) .boxed() diff --git a/src/parser/var.rs b/src/parser/var.rs index 80722e1..c5806dc 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"); - String::from_utf8(out).expect("p created non-utf8 string") + let mut s = String::from_utf8(out).expect("p created non-utf8 string"); + s.push('\n'); + s } diff --git a/src/pretty/basic.rs b/src/pretty/basic.rs index b6812b0..abc55ee 100644 --- a/src/pretty/basic.rs +++ b/src/pretty/basic.rs @@ -1,6 +1,8 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; -use crate::ast::{Ident, Separated}; +use crate::ast::{BoundedSeparated, Ident}; + +use super::NEST_DEPTH; impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Ident { fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { @@ -8,40 +10,32 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Ident { } } -impl Separated { - pub fn pretty<'a, D, FE, FS1, FS2>( +impl BoundedSeparated { + pub fn pretty<'a, D, FE>( self, allocator: &'a D, - elem_to_doc: FE, - separator_to_doc: FS1, - trailing_separator_to_doc: FS2, + start: DocBuilder<'a, D>, + end: DocBuilder<'a, D>, + separator: DocBuilder<'a, D>, + elem_pretty: FE, ) -> 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>, { - 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()), - ), - } + 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() } } diff --git a/src/pretty/call.rs b/src/pretty/call.rs index 84d2b5b..6cf26fc 100644 --- a/src/pretty/call.rs +++ b/src/pretty/call.rs @@ -2,7 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Call; -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Call { +impl<'a, D> Pretty<'a, D> for Call +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 7919963..ddf7634 100644 --- a/src/pretty/expr.rs +++ b/src/pretty/expr.rs @@ -22,7 +22,11 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for BinOp { } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Expr { +impl<'a, D> Pretty<'a, D> for Expr +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 ce5b28f..eb547f0 100644 --- a/src/pretty/field.rs +++ b/src/pretty/field.rs @@ -2,7 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Field; -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Field { +impl<'a, D> Pretty<'a, D> for Field +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 6fd8140..dce23e6 100644 --- a/src/pretty/func_def.rs +++ b/src/pretty/func_def.rs @@ -2,7 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::FuncDef; -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for FuncDef { +impl<'a, D> Pretty<'a, D> for FuncDef +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 45dd034..84f3055 100644 --- a/src/pretty/lit.rs +++ b/src/pretty/lit.rs @@ -2,8 +2,6 @@ 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:?}")) @@ -32,7 +30,11 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for StringLit { } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableLitElem { +impl<'a, D> Pretty<'a, D> for TableLitElem +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Positional(expr) => expr.pretty(allocator), @@ -50,23 +52,27 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableLitElem { } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableLit { +impl<'a, D> Pretty<'a, D> for TableLit +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - 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() + self.0.pretty( + allocator, + allocator.text("'{"), + allocator.text("}"), + allocator.text(","), + |e| e.pretty(allocator), + ) } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Lit { +impl<'a, D> Pretty<'a, D> for Lit +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 20c5cb1..1ff7863 100644 --- a/src/pretty/program.rs +++ b/src/pretty/program.rs @@ -2,7 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Program; -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Program { +impl<'a, D> Pretty<'a, D> for Program +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Expr { @@ -11,22 +15,19 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Program { s1, span: _, } => expr.pretty(allocator), - 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(","), - )), + + 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(","))), + ) + } } } } diff --git a/src/pretty/table_constr.rs b/src/pretty/table_constr.rs index 5c9a058..3b3fdb1 100644 --- a/src/pretty/table_constr.rs +++ b/src/pretty/table_constr.rs @@ -2,9 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::{TableConstr, TableConstrElem}; -use super::NEST_DEPTH; - -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableConstrElem { +impl<'a, D> Pretty<'a, D> for TableConstrElem +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Lit(lit) => lit.pretty(allocator), @@ -25,18 +27,18 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableConstrElem { } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableConstr { +impl<'a, D> Pretty<'a, D> for TableConstr +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - 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() + self.0.pretty( + allocator, + allocator.text("{"), + allocator.text("}"), + allocator.text(","), + |e| e.pretty(allocator), + ) } } diff --git a/src/pretty/table_destr.rs b/src/pretty/table_destr.rs index 50fab7b..777d0ae 100644 --- a/src/pretty/table_destr.rs +++ b/src/pretty/table_destr.rs @@ -2,8 +2,6 @@ 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 { @@ -22,23 +20,27 @@ impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePatternElem { } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePattern { +impl<'a, D> Pretty<'a, D> for TablePattern +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { - 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() + self.0.pretty( + allocator, + allocator.text("{"), + allocator.text("}"), + allocator.text(","), + |e| e.pretty(allocator), + ) } } -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TableDestr { +impl<'a, D> Pretty<'a, D> for TableDestr +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ 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 a3b5418..662b34e 100644 --- a/src/pretty/var.rs +++ b/src/pretty/var.rs @@ -2,7 +2,11 @@ use pretty::{DocAllocator, DocBuilder, Pretty}; use crate::ast::Var; -impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for Var { +impl<'a, D> Pretty<'a, D> for Var +where + D: DocAllocator<'a>, + D::Doc: Clone, +{ fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> { match self { Self::Access {