Compare commits

...
Sign in to create a new pull request.

28 commits

Author SHA1 Message Date
802d776f29 Include trailing newline in pretty print output 2022-11-22 23:14:07 +01:00
af4311ba98 Remove named nils as early as possible 2022-11-22 21:56:50 +01:00
f6c9835e29 Hide warnings in pretty module for now 2022-11-22 21:50:20 +01:00
736174d470 Desugar away named nil in table literals 2022-11-22 17:49:12 +01:00
39099037a5 Remove unused functions 2022-11-22 17:39:56 +01:00
f8fa044259 Desugar all expressions 2022-11-22 17:39:02 +01:00
26dc3c3469 Desugar all function definitions 2022-11-22 17:27:06 +01:00
40c28d9496 Simplify creating Var 2022-11-22 17:14:52 +01:00
6f7683ad1e Simplify creating Field 2022-11-22 17:06:56 +01:00
009be99aaa Simplify creating Call 2022-11-22 16:55:27 +01:00
78d08968eb Move BoundedSeparated helper functions into ast 2022-11-22 16:45:31 +01:00
58106c4c5a Simplify creating TableConstrElem 2022-11-22 16:44:33 +01:00
c191486864 Simplify creating TableLitElem 2022-11-22 16:35:16 +01:00
45caafdc38 Simplify creating Lit 2022-11-22 16:29:31 +01:00
fafc567447 Simplify creating Expr 2022-11-22 16:29:21 +01:00
42369628b6 Simplify boxing Expr 2022-11-22 15:32:23 +01:00
5a977e6dde Simplify creating TableLit and TableConstr 2022-11-22 15:21:40 +01:00
c769d9e16f Simplify creating BoundedSeparated
Along with this comes the decision to no longer track whitespace and
comments across desugarings in most cases. The main reason for this
decision is that not having to track whitespace simplifies the code and
reasoning necessary.

While implementing desugarings, I also realized that the current comment
model doesn't let me accurately track whitespace across desugarings. I'm
fairly sure I haven't yet found a good model for whitespace and
comments. Once I find one, adding pretty printing support for those
should hopefully be easy though.
2022-11-22 15:21:35 +01:00
d6a0bbf2af Desugar simple anonymous function definitions 2022-11-22 12:56:09 +01:00
830ffa92c4 Desugar table destructuring 2022-11-22 10:32:27 +01:00
d4797c5894 Simplify creating string literal from ident 2022-11-22 10:32:17 +01:00
af6c171eb4 Desugar table constructors 2022-11-22 09:44:29 +01:00
74d1f640b5 Fix pretty printing of empty BoundedSeparated 2022-11-22 09:21:44 +01:00
94ea196933 Remove Separated 2022-11-22 00:00:43 +01:00
198f56226e Switch TablePattern to BoundedSeparated 2022-11-21 23:57:58 +01:00
0e9cfd67c2 Switch TablConstr to BoundedSeparated 2022-11-21 23:54:40 +01:00
e3fa3500d4 Switch TableLit and Program to BoundedSeparated 2022-11-21 23:54:30 +01:00
a1867fdc4e Add BoundedSeparated with parser and printer
This type should be able to replace Separated (with a bit of effort).

The hope is that this type is a better representation for table-like
syntax with optional trailing delimiter that will let me remove_map
elements without too much difficulty. This is necessary for desugaring
table constructors.
2022-11-21 22:56:50 +01:00
44 changed files with 1019 additions and 660 deletions

View file

@ -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<E, S1, S2> {
Empty(Span),
NonEmpty {
first_elem: E,
last_elems: Vec<(S1, E)>,
trailing: Option<S2>,
span: Span,
},
pub struct BoundedSeparated<E> {
pub elems: Vec<(Space, E, Space)>,
pub trailing: Option<Space>,
pub span: Span,
}
impl<E, S1, S2> HasSpan for Separated<E, S1, S2> {
impl<E> HasSpan for BoundedSeparated<E> {
fn span(&self) -> Span {
match self {
Self::Empty(span) => *span,
Self::NonEmpty { span, .. } => *span,
}
self.span
}
}
impl<E> BoundedSeparated<E> {
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<E2>(self, f: impl Fn(E) -> E2) -> BoundedSeparated<E2> {
let elems = self
.elems
.into_iter()
.map(|(s0, e, s1)| (s0, f(e), s1))
.collect::<Vec<_>>();
BoundedSeparated {
elems,
trailing: self.trailing,
span: self.span,
}
}
pub fn remove_map<E1, E2>(
self,
f: impl Fn(E) -> Result<E1, E2>,
) -> (BoundedSeparated<E1>, 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<TableLitElem> {
pub fn table_lit(self) -> TableLit {
TableLit(self)
}
}
impl BoundedSeparated<TableConstrElem> {
pub fn table_constr(self) -> TableConstr {
TableConstr(self)
}
}

View file

@ -46,3 +46,38 @@ impl HasSpan for Call {
}
}
}
impl Call {
pub fn arg(base: Box<Expr>, arg: Box<Expr>, 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<Expr>, span: Span) -> Self {
Self::NoArg {
expr: base,
s0: Space::empty(span),
s1: Space::empty(span),
span,
}
}
pub fn constr(base: Box<Expr>, constr: TableConstr, span: Span) -> Self {
Self::Constr {
expr: base,
s0: Space::empty(span),
constr,
span,
}
}
pub fn expr(self) -> Expr {
Expr::Call(self)
}
}

View file

@ -242,3 +242,9 @@ impl HasSpan for Expr {
}
}
}
impl Expr {
pub fn boxed(self) -> Box<Self> {
Box::new(self)
}
}

View file

@ -67,3 +67,34 @@ impl HasSpan for Field {
}
}
}
impl Field {
pub fn access(base: Box<Expr>, index: Box<Expr>, 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<Expr>, index: Box<Expr>, value: Box<Expr>, 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)
}
}

View file

@ -99,3 +99,41 @@ impl HasSpan for FuncDef {
}
}
}
impl FuncDef {
pub fn anon_no_arg(body: Box<Expr>, 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<Expr>, 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<Expr>, span: Span) -> Self {
Self::AnonDestr {
s0: Space::empty(span),
pattern,
s1: Space::empty(span),
body,
span,
}
}
pub fn expr(self) -> Expr {
Expr::FuncDef(self)
}
}

View file

@ -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<TableLitElem, (Space, Space), Space>,
pub s1: Space,
pub span: Span,
impl TableLitElem {
pub fn named(name: Ident, value: Box<Expr>, 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<TableLitElem>);
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)
}
}

View file

@ -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<TableLitElem, (Space, Space), Space>,
s2: Space,
elems: BoundedSeparated<TableLitElem>,
span: Span,
},
}

View file

@ -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<TableConstrElem, (Space, Space), Space>,
pub s1: Space,
pub span: Span,
impl TableConstrElem {
pub fn positional(value: Box<Expr>) -> Self {
Self::Lit(TableLitElem::Positional(value))
}
pub fn named(name: Ident, value: Box<Expr>, span: Span) -> Self {
Self::Lit(TableLitElem::named(name, value, span))
}
}
/// `{ a, b, foo: c, [d]: e }`
#[derive(Debug, Clone)]
pub struct TableConstr(pub BoundedSeparated<TableConstrElem>);
impl HasSpan for TableConstr {
fn span(&self) -> Span {
self.span
self.0.span()
}
}
impl TableConstr {
pub fn expr(self) -> Expr {
Expr::TableConstr(self)
}
}

View file

@ -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<TablePatternElem, (Space, Space), Space>,
pub s1: Space,
pub span: Span,
}
pub struct TablePattern(pub BoundedSeparated<TablePatternElem>);
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<Expr>, 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)
}
}

View file

@ -56,3 +56,54 @@ impl HasSpan for Var {
}
}
}
impl Var {
pub fn access(index: Box<Expr>, span: Span) -> Self {
Self::Access {
s0: Space::empty(span),
index,
s1: Space::empty(span),
span,
}
}
pub fn assign(local: bool, index: Box<Expr>, value: Box<Expr>, 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<Expr>, 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)
}
}

View file

@ -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"),
}
}
}

View file

@ -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;

View file

@ -1,36 +1,24 @@
use crate::ast::Separated;
use crate::ast::BoundedSeparated;
impl<E, S1, S2> Separated<E, S1, S2> {
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<E> BoundedSeparated<E> {
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)
}
}

View file

@ -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)
}
}
}

View file

@ -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)
}
}
}
}

View file

@ -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)
}
}
}

128
src/desugar/func_def.rs Normal file
View file

@ -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)
}
}
}
}

View file

@ -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),

View file

@ -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,
};

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}

View file

@ -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 {

View file

@ -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<char, Span>;
@ -62,23 +62,42 @@ pub fn local(space: EParser<Space>) -> EParser<Option<Space>> {
// 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<E: 'static, S1: 'static, S2: 'static>(
pub fn bounded_separated<E: 'static>(
space: impl Parser<char, Space, Error = Error> + Clone + 'static,
start: impl Parser<char, (), Error = Error> + 'static,
end: impl Parser<char, (), Error = Error> + 'static,
separator: impl Parser<char, (), Error = Error> + 'static,
elem: impl Parser<char, E, Error = Error> + Clone + 'static,
separator: impl Parser<char, S1, Error = Error> + 'static,
trailing_separator: impl Parser<char, S2, Error = Error> + 'static,
) -> EParser<Separated<E, S1, S2>> {
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<BoundedSeparated<E>> {
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()
}

View file

@ -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()
}

View file

@ -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,
}
})

View file

@ -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<char, Builtin, Error = Error> {
just('\'').ignore_then(choice((
@ -132,7 +132,7 @@ pub fn table_lit_elem(
) -> EParser<TableLitElem> {
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<Space>,
table_lit_elem: EParser<TableLitElem>,
) -> impl Parser<char, TableLit, Error = Error> {
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<Space>, table_lit_elem: EParser<TableLitElem>) -> EParser<Lit> {

View file

@ -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,

View file

@ -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<Space>,
@ -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()
}

View file

@ -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(),
},
)
}

View file

@ -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<Space>,
@ -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<Expr>,
) -> EParser<TableConstr> {
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()
}

View file

@ -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<Space>,
@ -31,21 +31,15 @@ fn table_pattern_elem(
pub fn table_pattern(space: EParser<Space>, ident: EParser<Ident>) -> EParser<TablePattern> {
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()

View file

@ -14,7 +14,7 @@ fn var_access(space: EParser<Space>, expr: EParser<Expr>) -> impl Parser<char, V
.then_ignore(just(']'))
.map_with_span(|((s0, index), s1), span| Var::Access {
s0,
index: Box::new(index),
index: index.boxed(),
s1,
span,
})
@ -39,11 +39,11 @@ fn var_assign(
|((((((local, s0), index), s1), s2), s3), value), span| Var::Assign {
local,
s0,
index: Box::new(index),
index: index.boxed(),
s1,
s2,
s3,
value: Box::new(value),
value: value.boxed(),
span,
},
)
@ -67,7 +67,7 @@ fn var_assign_ident(
name,
s0,
s1,
value: Box::new(value),
value: value.boxed(),
span,
},
)

View file

@ -1,3 +1,6 @@
// TODO Remove this and print whitespace and comments properly
#![allow(unused_variables)]
use pretty::{Pretty, RcAllocator};
mod basic;
@ -18,5 +21,7 @@ pub fn pretty_to_string<P: Pretty<'static, RcAllocator>>(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
}

View file

@ -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<E, S1, S2> Separated<E, S1, S2> {
pub fn pretty<'a, D, FE, FS1, FS2>(
impl<E> BoundedSeparated<E> {
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()
}
}

View file

@ -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 {

View file

@ -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),

View file

@ -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 {

View file

@ -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 {

View file

@ -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"),

View file

@ -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(","))),
)
}
}
}
}

View file

@ -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),
)
}
}

View file

@ -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

View file

@ -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 {