Compare commits
66 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 802d776f29 | |||
| af4311ba98 | |||
| f6c9835e29 | |||
| 736174d470 | |||
| 39099037a5 | |||
| f8fa044259 | |||
| 26dc3c3469 | |||
| 40c28d9496 | |||
| 6f7683ad1e | |||
| 009be99aaa | |||
| 78d08968eb | |||
| 58106c4c5a | |||
| c191486864 | |||
| 45caafdc38 | |||
| fafc567447 | |||
| 42369628b6 | |||
| 5a977e6dde | |||
| c769d9e16f | |||
| d6a0bbf2af | |||
| 830ffa92c4 | |||
| d4797c5894 | |||
| af6c171eb4 | |||
| 74d1f640b5 | |||
| 94ea196933 | |||
| 198f56226e | |||
| 0e9cfd67c2 | |||
| e3fa3500d4 | |||
| a1867fdc4e | |||
| 9591b23082 | |||
| ca979edd7c | |||
| 27ba13e8a2 | |||
| 621ea8f1d4 | |||
| 290cef06cb | |||
| ebc48fff5a | |||
| 3f8320941a | |||
| b84d5ae0c8 | |||
| 5c8dd1969f | |||
| 7bfaebc05f | |||
| 29aa474b6a | |||
| a3d6efcaec | |||
| 52c1aeba35 | |||
| 8278442d3f | |||
| 13b7db79b0 | |||
| 8b21acac9e | |||
| 6eee1ba930 | |||
| 9d6cd580d4 | |||
| b3eaa40902 | |||
| c45a45f0b6 | |||
| e7416fbc1e | |||
| c7fc8584ff | |||
| 5bd43ee37a | |||
| 81e2a28b06 | |||
| fc139c31f4 | |||
| 412eaffc07 | |||
| f91e8ac9a2 | |||
| e6bbb37323 | |||
| 7833ef533d | |||
| 4156006ada | |||
| 03e7f10739 | |||
| 3ed3c4e8f8 | |||
| 2ba56f0c92 | |||
| 1a3772e6f7 | |||
| 23796e53a9 | |||
| 200b653e61 | |||
| 1b364061e4 | |||
| 6533c9dcf7 |
47 changed files with 2046 additions and 224 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
|
@ -120,6 +120,15 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
|
@ -146,6 +155,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.137"
|
version = "0.2.137"
|
||||||
|
|
@ -233,6 +251,24 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "remove_dir_all"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
@ -258,6 +294,21 @@ dependencies = [
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"clap",
|
"clap",
|
||||||
"pretty",
|
"pretty",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"remove_dir_all",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@ anyhow = "1.0.66"
|
||||||
chumsky = "0.8.0"
|
chumsky = "0.8.0"
|
||||||
clap = { version = "4.0.26", features = ["derive", "deprecated"] }
|
clap = { version = "4.0.26", features = ["derive", "deprecated"] }
|
||||||
pretty = "0.11.3"
|
pretty = "0.11.3"
|
||||||
|
tempfile = "3.3.0"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ use std::fmt;
|
||||||
|
|
||||||
use crate::span::{HasSpan, Span};
|
use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
|
use super::{TableConstr, TableConstrElem, TableLit, TableLitElem};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Line {
|
pub enum Line {
|
||||||
Empty,
|
Empty,
|
||||||
|
|
@ -30,12 +32,30 @@ impl HasSpan for Space {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Space {
|
||||||
|
pub fn empty(span: Span) -> Self {
|
||||||
|
Self {
|
||||||
|
lines: vec![],
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Ident {
|
pub struct Ident {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ident {
|
||||||
|
pub fn new<S: ToString>(name: S, span: Span) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Ident {
|
impl fmt::Debug for Ident {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "i#{}", self.name)
|
write!(f, "i#{}", self.name)
|
||||||
|
|
@ -47,3 +67,78 @@ impl HasSpan for Ident {
|
||||||
self.span
|
self.span
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct BoundedSeparated<E> {
|
||||||
|
pub elems: Vec<(Space, E, Space)>,
|
||||||
|
pub trailing: Option<Space>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> HasSpan for BoundedSeparated<E> {
|
||||||
|
fn span(&self) -> 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,44 @@ pub enum Call {
|
||||||
impl HasSpan for Call {
|
impl HasSpan for Call {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Call::Arg { span, .. } => *span,
|
Self::Arg { span, .. } => *span,
|
||||||
Call::NoArg { span, .. } => *span,
|
Self::NoArg { span, .. } => *span,
|
||||||
Call::Constr { span, .. } => *span,
|
Self::Constr { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,30 @@ use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
use super::{Call, Field, FuncDef, Lit, Space, TableConstr, TableDestr, Var};
|
use super::{Call, Field, FuncDef, Lit, Space, TableConstr, TableDestr, Var};
|
||||||
|
|
||||||
|
// Warning: If you change these precedences and associativities, you need to
|
||||||
|
// update the parser and pretty-printer as well.
|
||||||
|
|
||||||
|
// Warning: Operators at the same precedence must also have the same
|
||||||
|
// associativity.
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Assoc {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum BinOp {
|
pub enum BinOp {
|
||||||
/// `+`
|
|
||||||
Add,
|
|
||||||
/// `-`
|
|
||||||
Sub,
|
|
||||||
/// `*`
|
/// `*`
|
||||||
Mul,
|
Mul,
|
||||||
/// `/`
|
/// `/`
|
||||||
Div,
|
Div,
|
||||||
/// `%`
|
/// `%`
|
||||||
Mod,
|
Mod,
|
||||||
|
/// `+`
|
||||||
|
Add,
|
||||||
|
/// `-`
|
||||||
|
Sub,
|
||||||
/// `==`
|
/// `==`
|
||||||
Eq,
|
Eq,
|
||||||
/// `!=`
|
/// `!=`
|
||||||
|
|
@ -34,6 +46,36 @@ pub enum BinOp {
|
||||||
Or,
|
Or,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BinOp {
|
||||||
|
/// The higher the precedence, the more strongly the operator binds.
|
||||||
|
pub fn precedence(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Mul | Self::Div | Self::Mod => 4,
|
||||||
|
Self::Add | Self::Sub => 3,
|
||||||
|
Self::Eq | Self::Neq | Self::Gt | Self::Ge | Self::Lt | Self::Le => 2,
|
||||||
|
Self::And => 1,
|
||||||
|
Self::Or => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assoc(self) -> Assoc {
|
||||||
|
match self {
|
||||||
|
Self::Mul
|
||||||
|
| Self::Div
|
||||||
|
| Self::Mod
|
||||||
|
| Self::Add
|
||||||
|
| Self::Sub
|
||||||
|
| Self::Eq
|
||||||
|
| Self::Neq
|
||||||
|
| Self::Gt
|
||||||
|
| Self::Ge
|
||||||
|
| Self::Lt
|
||||||
|
| Self::Le => Assoc::Left,
|
||||||
|
Self::And | Self::Or => Assoc::Right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Lit(Lit),
|
Lit(Lit),
|
||||||
|
|
@ -186,17 +228,23 @@ impl fmt::Debug for Expr {
|
||||||
impl HasSpan for Expr {
|
impl HasSpan for Expr {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Expr::Lit(lit) => lit.span(),
|
Self::Lit(lit) => lit.span(),
|
||||||
Expr::Call(call) => call.span(),
|
Self::Call(call) => call.span(),
|
||||||
Expr::Field(field) => field.span(),
|
Self::Field(field) => field.span(),
|
||||||
Expr::Var(var) => var.span(),
|
Self::Var(var) => var.span(),
|
||||||
Expr::TableConstr(constr) => constr.span(),
|
Self::TableConstr(constr) => constr.span(),
|
||||||
Expr::TableDestr(destr) => destr.span(),
|
Self::TableDestr(destr) => destr.span(),
|
||||||
Expr::FuncDef(def) => def.span(),
|
Self::FuncDef(def) => def.span(),
|
||||||
Expr::Paren { span, .. } => *span,
|
Self::Paren { span, .. } => *span,
|
||||||
Expr::Neg { span, .. } => *span,
|
Self::Neg { span, .. } => *span,
|
||||||
Expr::Not { span, .. } => *span,
|
Self::Not { span, .. } => *span,
|
||||||
Expr::BinOp { span, .. } => *span,
|
Self::BinOp { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Expr {
|
||||||
|
pub fn boxed(self) -> Box<Self> {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,10 +60,41 @@ pub enum Field {
|
||||||
impl HasSpan for Field {
|
impl HasSpan for Field {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Field::Access { span, .. } => *span,
|
Self::Access { span, .. } => *span,
|
||||||
Field::Assign { span, .. } => *span,
|
Self::Assign { span, .. } => *span,
|
||||||
Field::AccessIdent { span, .. } => *span,
|
Self::AccessIdent { span, .. } => *span,
|
||||||
Field::AssignIdent { span, .. } => *span,
|
Self::AssignIdent { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,12 +90,50 @@ pub enum FuncDef {
|
||||||
impl HasSpan for FuncDef {
|
impl HasSpan for FuncDef {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
FuncDef::AnonNoArg { span, .. } => *span,
|
Self::AnonNoArg { span, .. } => *span,
|
||||||
FuncDef::AnonArg { span, .. } => *span,
|
Self::AnonArg { span, .. } => *span,
|
||||||
FuncDef::AnonDestr { span, .. } => *span,
|
Self::AnonDestr { span, .. } => *span,
|
||||||
FuncDef::NamedNoArg { span, .. } => *span,
|
Self::NamedNoArg { span, .. } => *span,
|
||||||
FuncDef::NamedArg { span, .. } => *span,
|
Self::NamedArg { span, .. } => *span,
|
||||||
FuncDef::NamedDestr { span, .. } => *span,
|
Self::NamedDestr { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::fmt;
|
||||||
use crate::builtin::Builtin;
|
use crate::builtin::Builtin;
|
||||||
use crate::span::{HasSpan, Span};
|
use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
use super::{Expr, Ident, Space};
|
use super::{BoundedSeparated, Expr, Ident, Space};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum NumLitStr {
|
pub enum NumLitStr {
|
||||||
|
|
@ -93,6 +93,19 @@ pub struct StringLit {
|
||||||
pub span: Span,
|
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 {
|
impl HasSpan for StringLit {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.span
|
self.span
|
||||||
|
|
@ -119,24 +132,37 @@ pub enum TableLitElem {
|
||||||
impl HasSpan for TableLitElem {
|
impl HasSpan for TableLitElem {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
TableLitElem::Positional(value) => value.span(),
|
Self::Positional(value) => value.span(),
|
||||||
TableLitElem::Named { span, .. } => *span,
|
Self::Named { 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 }`
|
/// `'{ a, foo: b }`
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TableLit {
|
pub struct TableLit(pub BoundedSeparated<TableLitElem>);
|
||||||
pub elems: Vec<(Space, TableLitElem, Space)>,
|
|
||||||
/// `Some` if there is a trailing comma, `None` otherwise.
|
|
||||||
pub trailing_comma: Option<Space>,
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSpan for TableLit {
|
impl HasSpan for TableLit {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.span
|
self.0.span()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableLit {
|
||||||
|
pub fn lit(self) -> Lit {
|
||||||
|
Lit::Table(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,12 +211,18 @@ impl fmt::Debug for Lit {
|
||||||
impl HasSpan for Lit {
|
impl HasSpan for Lit {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Lit::Nil(span) => *span,
|
Self::Nil(span) => *span,
|
||||||
Lit::Bool(_, span) => *span,
|
Self::Bool(_, span) => *span,
|
||||||
Lit::Builtin(_, span) => *span,
|
Self::Builtin(_, span) => *span,
|
||||||
Lit::Num(n) => n.span(),
|
Self::Num(n) => n.span(),
|
||||||
Lit::String(s) => s.span(),
|
Self::String(s) => s.span(),
|
||||||
Lit::Table(t) => t.span(),
|
Self::Table(t) => t.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Lit {
|
||||||
|
pub fn expr(self) -> Expr {
|
||||||
|
Expr::Lit(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::span::{HasSpan, Span};
|
use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
use super::{Expr, Space, TableLitElem};
|
use super::{BoundedSeparated, Expr, Space, TableLitElem};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Program {
|
pub enum Program {
|
||||||
|
|
@ -12,12 +12,10 @@ pub enum Program {
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Structure: `s0 module elems trailing_comma`
|
/// Structure: `s0 module elems`
|
||||||
Module {
|
Module {
|
||||||
s0: Space,
|
s0: Space,
|
||||||
elems: Vec<(Space, TableLitElem, Space)>,
|
elems: BoundedSeparated<TableLitElem>,
|
||||||
/// `Some` if there is a trailing comma, `None` otherwise.
|
|
||||||
trailing_comma: Option<Space>,
|
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -25,8 +23,8 @@ pub enum Program {
|
||||||
impl HasSpan for Program {
|
impl HasSpan for Program {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Program::Expr { span, .. } => *span,
|
Self::Expr { span, .. } => *span,
|
||||||
Program::Module { span, .. } => *span,
|
Self::Module { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::span::{HasSpan, Span};
|
use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
use super::{Expr, Space, TableLitElem};
|
use super::{BoundedSeparated, Expr, Ident, Space, TableLitElem};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TableConstrElem {
|
pub enum TableConstrElem {
|
||||||
|
|
@ -24,23 +24,34 @@ pub enum TableConstrElem {
|
||||||
impl HasSpan for TableConstrElem {
|
impl HasSpan for TableConstrElem {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
TableConstrElem::Lit(lit) => lit.span(),
|
Self::Lit(lit) => lit.span(),
|
||||||
TableConstrElem::Indexed { span, .. } => *span,
|
Self::Indexed { 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 }`
|
/// `{ a, b, foo: c, [d]: e }`
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TableConstr {
|
pub struct TableConstr(pub BoundedSeparated<TableConstrElem>);
|
||||||
pub elems: Vec<(Space, TableConstrElem, Space)>,
|
|
||||||
/// `Some` if there is a trailing comma, `None` otherwise.
|
|
||||||
pub trailing_comma: Option<Space>,
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSpan for TableConstr {
|
impl HasSpan for TableConstr {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.span
|
self.0.span()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableConstr {
|
||||||
|
pub fn expr(self) -> Expr {
|
||||||
|
Expr::TableConstr(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::span::{HasSpan, Span};
|
use crate::span::{HasSpan, Span};
|
||||||
|
|
||||||
use super::{Expr, Ident, Space};
|
use super::{BoundedSeparated, Expr, Ident, Space};
|
||||||
|
|
||||||
// TODO Make table patterns recursive
|
// TODO Make table patterns recursive
|
||||||
|
|
||||||
|
|
@ -24,24 +24,21 @@ pub enum TablePatternElem {
|
||||||
impl HasSpan for TablePatternElem {
|
impl HasSpan for TablePatternElem {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
TablePatternElem::Positional(ident) => ident.span(),
|
Self::Positional(ident) => ident.span(),
|
||||||
TablePatternElem::Named { span, .. } => *span,
|
Self::Named { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `'{ foo, bar: baz }`
|
/// `{ foo, bar: baz }`
|
||||||
|
///
|
||||||
|
/// Structure: `{ s0 elems s1 }`
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TablePattern {
|
pub struct TablePattern(pub BoundedSeparated<TablePatternElem>);
|
||||||
pub elems: Vec<(Space, TablePatternElem, Space)>,
|
|
||||||
/// `Some` if there is a trailing comma, `None` otherwise.
|
|
||||||
pub trailing_comma: Option<Space>,
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasSpan for TablePattern {
|
impl HasSpan for TablePattern {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.span
|
self.0.span()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,3 +61,26 @@ impl HasSpan for TableDestr {
|
||||||
self.span
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,61 @@ pub enum Var {
|
||||||
impl HasSpan for Var {
|
impl HasSpan for Var {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Var::Access { span, .. } => *span,
|
Self::Access { span, .. } => *span,
|
||||||
Var::Assign { span, .. } => *span,
|
Self::Assign { span, .. } => *span,
|
||||||
Var::AccessIdent(ident) => ident.span(),
|
Self::AccessIdent(ident) => ident.span(),
|
||||||
Var::AssignIdent { span, .. } => *span,
|
Self::AssignIdent { span, .. } => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,21 @@ pub enum Builtin {
|
||||||
Scope,
|
Scope,
|
||||||
Arg,
|
Arg,
|
||||||
Destructure,
|
Destructure,
|
||||||
|
Neg,
|
||||||
|
Not,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Eq,
|
||||||
|
Ne,
|
||||||
|
Gt,
|
||||||
|
Ge,
|
||||||
|
Lt,
|
||||||
|
Le,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Builtin {
|
impl fmt::Debug for Builtin {
|
||||||
|
|
@ -26,6 +41,21 @@ impl fmt::Debug for Builtin {
|
||||||
Self::Scope => write!(f, "'scope"),
|
Self::Scope => write!(f, "'scope"),
|
||||||
Self::Arg => write!(f, "'arg"),
|
Self::Arg => write!(f, "'arg"),
|
||||||
Self::Destructure => write!(f, "'destructure"),
|
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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/desugar.rs
Normal file
10
src/desugar.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
mod basic;
|
||||||
|
mod call;
|
||||||
|
mod expr;
|
||||||
|
mod field;
|
||||||
|
mod func_def;
|
||||||
|
mod lit;
|
||||||
|
mod program;
|
||||||
|
mod table_constr;
|
||||||
|
mod table_destr;
|
||||||
|
mod var;
|
||||||
24
src/desugar/basic.rs
Normal file
24
src/desugar/basic.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::ast::BoundedSeparated;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/desugar/call.rs
Normal file
44
src/desugar/call.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use crate::ast::{BoundedSeparated, Call, Expr, Ident, Lit, TableLitElem};
|
||||||
|
|
||||||
|
// TODO Add span for just the parentheses to ast, or limit span to parentheses
|
||||||
|
|
||||||
|
impl Call {
|
||||||
|
pub fn desugar(self) -> (Expr, bool) {
|
||||||
|
match self {
|
||||||
|
Self::Arg {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
arg,
|
||||||
|
s2: _,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = BoundedSeparated::new(span)
|
||||||
|
.then(TableLitElem::named(Ident::new("call", span), expr, span))
|
||||||
|
.then(TableLitElem::named(Ident::new("arg", span), arg, span))
|
||||||
|
.table_lit();
|
||||||
|
(new.lit().expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::NoArg {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Self::arg(expr, Lit::Nil(span).expr().boxed(), span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Constr {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
constr,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Self::arg(expr, constr.expr().boxed(), span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/desugar/expr.rs
Normal file
78
src/desugar/expr.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
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();
|
||||||
|
(lit.expr(), desugared)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Call(call) => call.desugar(),
|
||||||
|
Self::Field(field) => field.desugar(),
|
||||||
|
Self::Var(var) => var.desugar(),
|
||||||
|
Self::TableConstr(constr) => constr.desugar(),
|
||||||
|
Self::TableDestr(destr) => destr.desugar(),
|
||||||
|
Self::FuncDef(def) => def.desugar(),
|
||||||
|
|
||||||
|
Self::Paren {
|
||||||
|
s0: _,
|
||||||
|
inner,
|
||||||
|
s1: _,
|
||||||
|
span: _,
|
||||||
|
} => (*inner, true),
|
||||||
|
|
||||||
|
Self::Neg {
|
||||||
|
minus,
|
||||||
|
s0: _,
|
||||||
|
expr,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Call::arg(Lit::Builtin(Builtin::Neg, minus).expr().boxed(), expr, span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Not {
|
||||||
|
not,
|
||||||
|
s0: _,
|
||||||
|
expr,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Call::arg(Lit::Builtin(Builtin::Not, not).expr().boxed(), expr, span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::BinOp {
|
||||||
|
left,
|
||||||
|
s0: _,
|
||||||
|
op,
|
||||||
|
s1: _,
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let builtin = match op {
|
||||||
|
BinOp::Mul => Builtin::Mul,
|
||||||
|
BinOp::Div => Builtin::Div,
|
||||||
|
BinOp::Mod => Builtin::Mod,
|
||||||
|
BinOp::Add => Builtin::Add,
|
||||||
|
BinOp::Sub => Builtin::Sub,
|
||||||
|
BinOp::Eq => Builtin::Eq,
|
||||||
|
BinOp::Neq => Builtin::Ne,
|
||||||
|
BinOp::Gt => Builtin::Gt,
|
||||||
|
BinOp::Ge => Builtin::Ge,
|
||||||
|
BinOp::Lt => Builtin::Lt,
|
||||||
|
BinOp::Le => Builtin::Le,
|
||||||
|
BinOp::And => Builtin::And,
|
||||||
|
BinOp::Or => Builtin::Or,
|
||||||
|
};
|
||||||
|
let constr = BoundedSeparated::new(span)
|
||||||
|
.then(TableConstrElem::positional(left))
|
||||||
|
.then(TableConstrElem::positional(right))
|
||||||
|
.table_constr();
|
||||||
|
let new = Call::constr(Lit::Builtin(builtin, span).expr().boxed(), constr, span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/desugar/field.rs
Normal file
86
src/desugar/field.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem};
|
||||||
|
use crate::builtin::Builtin;
|
||||||
|
|
||||||
|
impl Field {
|
||||||
|
pub fn desugar(self) -> (Expr, bool) {
|
||||||
|
match self {
|
||||||
|
Self::Access {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
index,
|
||||||
|
s2: _,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let constr = BoundedSeparated::new(span)
|
||||||
|
.then(TableConstrElem::positional(expr))
|
||||||
|
.then(TableConstrElem::positional(index))
|
||||||
|
.table_constr();
|
||||||
|
let new = Call::constr(
|
||||||
|
Lit::Builtin(Builtin::Get, span).expr().boxed(),
|
||||||
|
constr,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Assign {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
index,
|
||||||
|
s2: _,
|
||||||
|
s3: _,
|
||||||
|
s4: _,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let constr = BoundedSeparated::new(span)
|
||||||
|
.then(TableConstrElem::positional(expr))
|
||||||
|
.then(TableConstrElem::positional(index))
|
||||||
|
.then(TableConstrElem::positional(value))
|
||||||
|
.table_constr();
|
||||||
|
let new = Call::constr(
|
||||||
|
Lit::Builtin(Builtin::Set, span).expr().boxed(),
|
||||||
|
constr,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::AccessIdent {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
ident,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Self::access(
|
||||||
|
expr,
|
||||||
|
StringLit::from_ident(ident).lit().expr().boxed(),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::AssignIdent {
|
||||||
|
expr,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
ident,
|
||||||
|
s2: _,
|
||||||
|
s3: _,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Self::assign(
|
||||||
|
expr,
|
||||||
|
StringLit::from_ident(ident).lit().expr().boxed(),
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
src/desugar/func_def.rs
Normal file
128
src/desugar/func_def.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/desugar/lit.rs
Normal file
60
src/desugar/lit.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
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(expr.boxed()), desugared)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Named {
|
||||||
|
name,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let (value, desugared) = value.desugar();
|
||||||
|
let new = Self::Named {
|
||||||
|
name,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
value: value.boxed(),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
(new, desugared)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableLit {
|
||||||
|
pub fn desugar(self) -> (Self, bool) {
|
||||||
|
let (elems, removed) = self.0.remove_map(|e| match e {
|
||||||
|
TableLitElem::Named { value, .. } if matches!(*value, Expr::Lit(Lit::Nil(_))) => {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
e => Ok(e),
|
||||||
|
});
|
||||||
|
if removed.is_empty() {
|
||||||
|
let (elems, desugared) = elems.desugar(|e| e.desugar());
|
||||||
|
(elems.table_lit(), desugared)
|
||||||
|
} else {
|
||||||
|
(elems.table_lit(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lit {
|
||||||
|
pub fn desugar(self) -> (Self, bool) {
|
||||||
|
match self {
|
||||||
|
Self::Table(table) => {
|
||||||
|
let (table, desugared) = table.desugar();
|
||||||
|
(table.lit(), desugared)
|
||||||
|
}
|
||||||
|
|
||||||
|
lit => (lit, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/desugar/program.rs
Normal file
25
src/desugar/program.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::ast::{Program, Space};
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn desugar(self) -> (Self, bool) {
|
||||||
|
match self {
|
||||||
|
Self::Expr { s0, expr, s1, span } => {
|
||||||
|
let (expr, desugared) = expr.desugar();
|
||||||
|
let new = Self::Expr { s0, expr, s1, span };
|
||||||
|
(new, desugared)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Module { s0, elems, span } => {
|
||||||
|
// `s0 module elems`
|
||||||
|
// -> `s0 table`
|
||||||
|
let new = Self::Expr {
|
||||||
|
s0,
|
||||||
|
expr: elems.table_lit().lit().expr(),
|
||||||
|
s1: Space::empty(span),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
(new, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/desugar/table_constr.rs
Normal file
39
src/desugar/table_constr.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/desugar/table_destr.rs
Normal file
63
src/desugar/table_destr.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/desugar/var.rs
Normal file
82
src/desugar/var.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::ast::{BoundedSeparated, Call, Expr, Field, Lit, StringLit, TableConstrElem, Var};
|
||||||
|
use crate::builtin::Builtin;
|
||||||
|
use crate::span::HasSpan;
|
||||||
|
|
||||||
|
impl Var {
|
||||||
|
pub fn desugar(self) -> (Expr, bool) {
|
||||||
|
match self {
|
||||||
|
Self::Access {
|
||||||
|
s0: _,
|
||||||
|
index,
|
||||||
|
s1: _,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span);
|
||||||
|
let new = Field::access(scope.expr().boxed(), index, span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Assign {
|
||||||
|
local: None,
|
||||||
|
s0: _,
|
||||||
|
index,
|
||||||
|
s1: _,
|
||||||
|
s2: _,
|
||||||
|
s3: _,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span);
|
||||||
|
let new = Field::assign(scope.expr().boxed(), index, value, span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Assign {
|
||||||
|
local: Some(_),
|
||||||
|
s0: _,
|
||||||
|
index,
|
||||||
|
s1: _,
|
||||||
|
s2: _,
|
||||||
|
s3: _,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let scope = Call::no_arg(Lit::Builtin(Builtin::Scope, span).expr().boxed(), span);
|
||||||
|
let constr = BoundedSeparated::new(span)
|
||||||
|
.then(TableConstrElem::positional(scope.expr().boxed()))
|
||||||
|
.then(TableConstrElem::positional(index))
|
||||||
|
.then(TableConstrElem::positional(value))
|
||||||
|
.table_constr();
|
||||||
|
let new = Call::constr(
|
||||||
|
Lit::Builtin(Builtin::SetRaw, span).expr().boxed(),
|
||||||
|
constr,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::AccessIdent(name) => {
|
||||||
|
let span = name.span();
|
||||||
|
let new = Self::access(StringLit::from_ident(name).lit().expr().boxed(), span);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::AssignIdent {
|
||||||
|
local,
|
||||||
|
name,
|
||||||
|
s0: _,
|
||||||
|
s1: _,
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
} => {
|
||||||
|
let new = Self::assign(
|
||||||
|
local.is_some(),
|
||||||
|
StringLit::from_ident(name).lit().expr().boxed(),
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
(new.expr(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/main.rs
95
src/main.rs
|
|
@ -1,12 +1,30 @@
|
||||||
use std::fs;
|
#![deny(unsafe_code)]
|
||||||
use std::path::PathBuf;
|
// Rustc lint groups
|
||||||
|
#![warn(future_incompatible)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
// Rustc lints
|
||||||
|
#![warn(noop_method_call)]
|
||||||
|
#![warn(single_use_lifetimes)]
|
||||||
|
#![warn(trivial_numeric_casts)]
|
||||||
|
#![warn(unused_crate_dependencies)]
|
||||||
|
#![warn(unused_extern_crates)]
|
||||||
|
#![warn(unused_import_braces)]
|
||||||
|
#![warn(unused_lifetimes)]
|
||||||
|
#![warn(unused_qualifications)]
|
||||||
|
// Clippy lints
|
||||||
|
#![warn(clippy::use_self)]
|
||||||
|
|
||||||
use ::pretty::{Pretty, RcAllocator};
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs, process};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
use chumsky::Parser as _;
|
use chumsky::Parser as _;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod builtin;
|
mod builtin;
|
||||||
|
mod desugar;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod pretty;
|
mod pretty;
|
||||||
mod span;
|
mod span;
|
||||||
|
|
@ -15,8 +33,19 @@ mod value;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
enum Command {
|
enum Command {
|
||||||
Parse { file: PathBuf },
|
Parse {
|
||||||
Pretty { file: PathBuf },
|
file: PathBuf,
|
||||||
|
},
|
||||||
|
Pretty {
|
||||||
|
file: PathBuf,
|
||||||
|
},
|
||||||
|
Desugar {
|
||||||
|
file: PathBuf,
|
||||||
|
#[arg(long, short, default_value = "diff")]
|
||||||
|
difftool: String,
|
||||||
|
#[arg(long, short = 'a')]
|
||||||
|
diffarg: Vec<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|
@ -43,24 +72,54 @@ fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::Pretty { file } => {
|
Command::Pretty { file } => {
|
||||||
let content = fs::read_to_string(&file)?;
|
let content = fs::read_to_string(&file)?;
|
||||||
let stream = span::stream_from_str(&content);
|
let stream = span::stream_from_str(&content);
|
||||||
match parser::parser().parse(stream) {
|
let program = parser::parser()
|
||||||
Ok(program) => {
|
.parse(stream)
|
||||||
println!("Successful parse");
|
.map_err(|e| anyhow!("{e:?}"))?;
|
||||||
let doc = program.pretty(&RcAllocator);
|
|
||||||
let mut out = vec![];
|
print!("{}", pretty::pretty_to_string(program, 100));
|
||||||
doc.render(100, &mut out)?;
|
|
||||||
let str = String::from_utf8(out)?;
|
|
||||||
println!("{str}");
|
|
||||||
}
|
|
||||||
Err(errs) => {
|
|
||||||
println!("Parsing failed");
|
|
||||||
for err in errs {
|
|
||||||
println!("{err:?}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Command::Desugar {
|
||||||
|
file,
|
||||||
|
difftool,
|
||||||
|
diffarg,
|
||||||
|
} => {
|
||||||
|
let content = fs::read_to_string(&file)?;
|
||||||
|
let stream = span::stream_from_str(&content);
|
||||||
|
let mut program = parser::parser()
|
||||||
|
.parse(stream)
|
||||||
|
.map_err(|e| anyhow!("{e:?}"))?;
|
||||||
|
|
||||||
|
let mut builder = tempfile::Builder::new();
|
||||||
|
builder.suffix(".tada");
|
||||||
|
|
||||||
|
let mut prev = builder.tempfile()?;
|
||||||
|
prev.write_all(pretty::pretty_to_string(program.clone(), 100).as_bytes())?;
|
||||||
|
prev.flush()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (new_program, desugared) = program.desugar();
|
||||||
|
program = new_program;
|
||||||
|
if !desugared {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut cur = builder.tempfile()?;
|
||||||
|
cur.write_all(pretty::pretty_to_string(program.clone(), 100).as_bytes())?;
|
||||||
|
cur.flush()?;
|
||||||
|
|
||||||
|
process::Command::new(&difftool)
|
||||||
|
.args(&diffarg)
|
||||||
|
.arg(prev.path())
|
||||||
|
.arg(cur.path())
|
||||||
|
.spawn()?
|
||||||
|
.wait()?;
|
||||||
|
|
||||||
|
prev = cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! # Rules
|
//! # Rules
|
||||||
//!
|
//!
|
||||||
|
//! - Parsers must not consume surrounding whitespace.
|
||||||
//! - Public parser functions must return [`basic::EParser`].
|
//! - Public parser functions must return [`basic::EParser`].
|
||||||
//! - Public parser functions must receive public subparsers via their arguments.
|
//! - Public parser functions must receive public subparsers via their arguments.
|
||||||
//! - Each public parser function must be called exactly once, inside this file.
|
//! - Each public parser function must be called exactly once, inside this file.
|
||||||
|
|
@ -17,7 +18,7 @@
|
||||||
|
|
||||||
mod basic;
|
mod basic;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod func_defs;
|
mod func_def;
|
||||||
mod lit;
|
mod lit;
|
||||||
mod prefix;
|
mod prefix;
|
||||||
mod program;
|
mod program;
|
||||||
|
|
@ -51,7 +52,7 @@ pub fn parser() -> impl Parser<char, Program, Error = Error> {
|
||||||
table_pattern.clone(),
|
table_pattern.clone(),
|
||||||
expr.clone(),
|
expr.clone(),
|
||||||
);
|
);
|
||||||
let func_def = func_defs::func_def(
|
let func_def = func_def::func_def(
|
||||||
space.clone(),
|
space.clone(),
|
||||||
ident.clone(),
|
ident.clone(),
|
||||||
local,
|
local,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
use chumsky::text::Character;
|
use chumsky::text::Character;
|
||||||
|
|
||||||
use crate::ast::{Ident, Line, Space};
|
use crate::ast::{BoundedSeparated, Ident, Line, Space};
|
||||||
use crate::span::Span;
|
use crate::span::Span;
|
||||||
|
|
||||||
pub type Error = Simple<char, Span>;
|
pub type Error = Simple<char, Span>;
|
||||||
|
|
@ -58,3 +58,46 @@ pub fn ident() -> EParser<Ident> {
|
||||||
pub fn local(space: EParser<Space>) -> EParser<Option<Space>> {
|
pub fn local(space: EParser<Space>) -> EParser<Option<Space>> {
|
||||||
text::keyword("local").ignore_then(space).or_not().boxed()
|
text::keyword("local").ignore_then(space).or_not().boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is more of a utility function. Because of this and to keep the
|
||||||
|
// code nicer, I have decided that the rules specified in the `parser` module
|
||||||
|
// don't apply to it.
|
||||||
|
pub fn bounded_separated<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,
|
||||||
|
) -> 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,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ fn atom_paren(
|
||||||
.then_ignore(just(')'))
|
.then_ignore(just(')'))
|
||||||
.map_with_span(|((s0, inner), s1), span| Expr::Paren {
|
.map_with_span(|((s0, inner), s1), span| Expr::Paren {
|
||||||
s0,
|
s0,
|
||||||
inner: Box::new(inner),
|
inner: inner.boxed(),
|
||||||
s1,
|
s1,
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
|
|
@ -63,11 +63,11 @@ fn left_assoc(
|
||||||
over.then(op_over.repeated())
|
over.then(op_over.repeated())
|
||||||
.foldl(|left, (s0, op, s1, right)| Expr::BinOp {
|
.foldl(|left, (s0, op, s1, right)| Expr::BinOp {
|
||||||
span: left.span().join(right.span()),
|
span: left.span().join(right.span()),
|
||||||
left: Box::new(left),
|
left: left.boxed(),
|
||||||
s0,
|
s0,
|
||||||
op,
|
op,
|
||||||
s1,
|
s1,
|
||||||
right: Box::new(right),
|
right: right.boxed(),
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
@ -89,11 +89,11 @@ fn right_assoc(
|
||||||
.then(over)
|
.then(over)
|
||||||
.foldr(|(left, s0, op, s1), right| Expr::BinOp {
|
.foldr(|(left, s0, op, s1), right| Expr::BinOp {
|
||||||
span: left.span().join(right.span()),
|
span: left.span().join(right.span()),
|
||||||
left: Box::new(left),
|
left: left.boxed(),
|
||||||
s0,
|
s0,
|
||||||
op,
|
op,
|
||||||
s1,
|
s1,
|
||||||
right: Box::new(right),
|
right: right.boxed(),
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
// TODO Rename this module to func_def for consistency
|
|
||||||
|
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::ast::{Expr, FuncDef, Ident, Space, TablePattern};
|
use crate::ast::{Expr, FuncDef, Ident, Space, TablePattern};
|
||||||
|
|
@ -21,7 +19,7 @@ fn func_def_anon_no_arg(
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
s2,
|
s2,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +45,7 @@ fn func_def_anon_arg(
|
||||||
arg,
|
arg,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -67,7 +65,7 @@ fn func_def_anon_destr(
|
||||||
s0,
|
s0,
|
||||||
pattern,
|
pattern,
|
||||||
s1,
|
s1,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +94,7 @@ fn func_def_named_no_arg(
|
||||||
s1,
|
s1,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -130,7 +128,7 @@ fn func_def_named_arg(
|
||||||
arg,
|
arg,
|
||||||
s3,
|
s3,
|
||||||
s4,
|
s4,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -159,7 +157,7 @@ fn func_def_named_destr(
|
||||||
s1,
|
s1,
|
||||||
pattern,
|
pattern,
|
||||||
s2,
|
s2,
|
||||||
body: Box::new(body),
|
body: body.boxed(),
|
||||||
span,
|
span,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -7,7 +7,7 @@ use crate::ast::{
|
||||||
};
|
};
|
||||||
use crate::builtin::Builtin;
|
use crate::builtin::Builtin;
|
||||||
|
|
||||||
use super::basic::{EParser, Error};
|
use super::basic::{bounded_separated, EParser, Error};
|
||||||
|
|
||||||
fn builtin_lit() -> impl Parser<char, Builtin, Error = Error> {
|
fn builtin_lit() -> impl Parser<char, Builtin, Error = Error> {
|
||||||
just('\'').ignore_then(choice((
|
just('\'').ignore_then(choice((
|
||||||
|
|
@ -132,7 +132,7 @@ pub fn table_lit_elem(
|
||||||
) -> EParser<TableLitElem> {
|
) -> EParser<TableLitElem> {
|
||||||
let positional = expr
|
let positional = expr
|
||||||
.clone()
|
.clone()
|
||||||
.map(|value| TableLitElem::Positional(Box::new(value)));
|
.map(|value| TableLitElem::Positional(value.boxed()));
|
||||||
|
|
||||||
let named = ident
|
let named = ident
|
||||||
.then(space.clone())
|
.then(space.clone())
|
||||||
|
|
@ -143,7 +143,7 @@ pub fn table_lit_elem(
|
||||||
name,
|
name,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -154,24 +154,14 @@ fn table_lit(
|
||||||
space: EParser<Space>,
|
space: EParser<Space>,
|
||||||
table_lit_elem: EParser<TableLitElem>,
|
table_lit_elem: EParser<TableLitElem>,
|
||||||
) -> impl Parser<char, TableLit, Error = Error> {
|
) -> impl Parser<char, TableLit, Error = Error> {
|
||||||
let elem = space
|
bounded_separated(
|
||||||
.clone()
|
space,
|
||||||
.then(table_lit_elem)
|
just("'{").to(()),
|
||||||
.then(space.clone())
|
just('}').to(()),
|
||||||
.map(|((s0, elem), s1)| (s0, elem, s1));
|
just(',').to(()),
|
||||||
|
table_lit_elem,
|
||||||
let trailing_comma = just(',').ignore_then(space).or_not();
|
)
|
||||||
|
.map(TableLit)
|
||||||
let elems = elem.separated_by(just(',')).then(trailing_comma);
|
|
||||||
|
|
||||||
just("'{")
|
|
||||||
.ignore_then(elems)
|
|
||||||
.then_ignore(just('}'))
|
|
||||||
.map_with_span(|(elems, trailing_comma), span| TableLit {
|
|
||||||
elems,
|
|
||||||
trailing_comma,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lit(space: EParser<Space>, table_lit_elem: EParser<TableLitElem>) -> EParser<Lit> {
|
pub fn lit(space: EParser<Space>, table_lit_elem: EParser<TableLitElem>) -> EParser<Lit> {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ enum Prefix {
|
||||||
|
|
||||||
impl Prefix {
|
impl Prefix {
|
||||||
fn into_expr(self, span: Span, expr: Expr) -> Expr {
|
fn into_expr(self, span: Span, expr: Expr) -> Expr {
|
||||||
let expr = Box::new(expr);
|
let expr = expr.boxed();
|
||||||
match self {
|
match self {
|
||||||
Self::Neg { minus, s0 } => Expr::Neg {
|
Self::Neg { minus, s0 } => Expr::Neg {
|
||||||
minus,
|
minus,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::ast::{Expr, Program, Space, TableLitElem};
|
use crate::ast::{Expr, Program, Space, TableLitElem};
|
||||||
|
|
||||||
use super::basic::EParser;
|
use super::basic::{bounded_separated, EParser};
|
||||||
|
|
||||||
pub fn program(
|
pub fn program(
|
||||||
space: EParser<Space>,
|
space: EParser<Space>,
|
||||||
|
|
@ -17,22 +17,17 @@ pub fn program(
|
||||||
.then(space.clone())
|
.then(space.clone())
|
||||||
.map_with_span(|((s0, expr), s1), span| Program::Expr { s0, expr, s1, span });
|
.map_with_span(|((s0, expr), s1), span| Program::Expr { s0, expr, s1, span });
|
||||||
|
|
||||||
let elem = space
|
|
||||||
.clone()
|
|
||||||
.then(table_lit_elem)
|
|
||||||
.then(space.clone())
|
|
||||||
.map(|((s0, elem), s1)| (s0, elem, s1));
|
|
||||||
let trailing_comma = just(',').ignore_then(space.clone()).or_not();
|
|
||||||
let module = space
|
let module = space
|
||||||
|
.clone()
|
||||||
.then_ignore(text::keyword("module"))
|
.then_ignore(text::keyword("module"))
|
||||||
.then(elem.separated_by(just(',')))
|
.then(bounded_separated(
|
||||||
.then(trailing_comma)
|
space,
|
||||||
.map_with_span(|((s0, elems), trailing_comma), span| Program::Module {
|
empty(),
|
||||||
s0,
|
empty(),
|
||||||
elems,
|
just(',').to(()),
|
||||||
trailing_comma,
|
table_lit_elem,
|
||||||
span,
|
))
|
||||||
});
|
.map_with_span(|(s0, elems), span| Program::Module { s0, elems, span });
|
||||||
|
|
||||||
module.or(lit).boxed()
|
module.or(lit).boxed()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,32 +57,39 @@ enum Suffix {
|
||||||
|
|
||||||
impl Suffix {
|
impl Suffix {
|
||||||
fn into_expr(self, span: Span, expr: Expr) -> Expr {
|
fn into_expr(self, span: Span, expr: Expr) -> Expr {
|
||||||
let expr = Box::new(expr);
|
let expr = expr.boxed();
|
||||||
match self {
|
match self {
|
||||||
Suffix::CallArg { s0, s1, arg, s2 } => Expr::Call(Call::Arg {
|
Self::CallArg { s0, s1, arg, s2 } => Call::Arg {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
arg,
|
arg,
|
||||||
s2,
|
s2,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
Suffix::CallNoArg { s0, s1 } => Expr::Call(Call::NoArg { expr, s0, s1, span }),
|
.expr(),
|
||||||
Suffix::CallConstr { s0, constr } => Expr::Call(Call::Constr {
|
|
||||||
|
Self::CallNoArg { s0, s1 } => Call::NoArg { expr, s0, s1, span }.expr(),
|
||||||
|
|
||||||
|
Self::CallConstr { s0, constr } => Call::Constr {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
constr,
|
constr,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
Suffix::FieldAccess { s0, s1, index, s2 } => Expr::Field(Field::Access {
|
.expr(),
|
||||||
|
|
||||||
|
Self::FieldAccess { s0, s1, index, s2 } => Field::Access {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
index,
|
index,
|
||||||
s2,
|
s2,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
Suffix::FieldAssign {
|
.expr(),
|
||||||
|
|
||||||
|
Self::FieldAssign {
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
index,
|
index,
|
||||||
|
|
@ -90,7 +97,7 @@ impl Suffix {
|
||||||
s3,
|
s3,
|
||||||
s4,
|
s4,
|
||||||
value,
|
value,
|
||||||
} => Expr::Field(Field::Assign {
|
} => Field::Assign {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
|
|
@ -100,22 +107,26 @@ impl Suffix {
|
||||||
s4,
|
s4,
|
||||||
value,
|
value,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
Suffix::FieldAccessIdent { s0, s1, ident } => Expr::Field(Field::AccessIdent {
|
.expr(),
|
||||||
|
|
||||||
|
Self::FieldAccessIdent { s0, s1, ident } => Field::AccessIdent {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
ident,
|
ident,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
Suffix::FieldAssignIdent {
|
.expr(),
|
||||||
|
|
||||||
|
Self::FieldAssignIdent {
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
ident,
|
ident,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
value,
|
value,
|
||||||
} => Expr::Field(Field::AssignIdent {
|
} => Field::AssignIdent {
|
||||||
expr,
|
expr,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
|
|
@ -124,7 +135,8 @@ impl Suffix {
|
||||||
s3,
|
s3,
|
||||||
value,
|
value,
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
|
.expr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +155,7 @@ fn suffix_call_arg(
|
||||||
.map(|(((s0, s1), arg), s2)| Suffix::CallArg {
|
.map(|(((s0, s1), arg), s2)| Suffix::CallArg {
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
arg: Box::new(arg),
|
arg: arg.boxed(),
|
||||||
s2,
|
s2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +192,7 @@ fn suffix_field_access(
|
||||||
.map(|(((s0, s1), index), s2)| Suffix::FieldAccess {
|
.map(|(((s0, s1), index), s2)| Suffix::FieldAccess {
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
index: Box::new(index),
|
index: index.boxed(),
|
||||||
s2,
|
s2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -204,11 +216,11 @@ fn suffix_field_assign(
|
||||||
|((((((s0, s1), index), s2), s3), s4), value)| Suffix::FieldAssign {
|
|((((((s0, s1), index), s2), s3), s4), value)| Suffix::FieldAssign {
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
index: Box::new(index),
|
index: index.boxed(),
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
s4,
|
s4,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -246,7 +258,7 @@ fn suffix_field_assign_ident(
|
||||||
ident,
|
ident,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::ast::{Expr, Space, TableConstr, TableConstrElem, TableLitElem};
|
use crate::ast::{Expr, Space, TableConstr, TableConstrElem, TableLitElem};
|
||||||
|
|
||||||
use super::basic::{EParser, Error};
|
use super::basic::{bounded_separated, EParser, Error};
|
||||||
|
|
||||||
fn table_constr_elem(
|
fn table_constr_elem(
|
||||||
space: EParser<Space>,
|
space: EParser<Space>,
|
||||||
table_lit_elem: EParser<TableLitElem>,
|
table_lit_elem: EParser<TableLitElem>,
|
||||||
expr: EParser<Expr>,
|
expr: EParser<Expr>,
|
||||||
) -> impl Parser<char, TableConstrElem, Error = Error> {
|
) -> impl Parser<char, TableConstrElem, Error = Error> + Clone {
|
||||||
let lit = table_lit_elem.map(TableConstrElem::Lit);
|
let lit = table_lit_elem.map(TableConstrElem::Lit);
|
||||||
|
|
||||||
let indexed = just('[')
|
let indexed = just('[')
|
||||||
|
|
@ -25,11 +25,11 @@ fn table_constr_elem(
|
||||||
.map_with_span(
|
.map_with_span(
|
||||||
|(((((s0, index), s1), s2), s3), value), span| TableConstrElem::Indexed {
|
|(((((s0, index), s1), s2), s3), value), span| TableConstrElem::Indexed {
|
||||||
s0,
|
s0,
|
||||||
index: Box::new(index),
|
index: index.boxed(),
|
||||||
s1,
|
s1,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -42,23 +42,14 @@ pub fn table_constr(
|
||||||
table_lit_elem: EParser<TableLitElem>,
|
table_lit_elem: EParser<TableLitElem>,
|
||||||
expr: EParser<Expr>,
|
expr: EParser<Expr>,
|
||||||
) -> EParser<TableConstr> {
|
) -> EParser<TableConstr> {
|
||||||
let elem = space
|
let elem = table_constr_elem(space.clone(), table_lit_elem, expr);
|
||||||
.clone()
|
bounded_separated(
|
||||||
.then(table_constr_elem(space.clone(), table_lit_elem, expr))
|
space,
|
||||||
.then(space.clone())
|
just('{').to(()),
|
||||||
.map(|((s0, elem), s1)| (s0, elem, s1));
|
just('}').to(()),
|
||||||
|
just(',').to(()),
|
||||||
let trailing_comma = just(',').ignore_then(space).or_not();
|
elem,
|
||||||
|
)
|
||||||
let elems = elem.separated_by(just(',')).then(trailing_comma);
|
.map(TableConstr)
|
||||||
|
|
||||||
just('{')
|
|
||||||
.ignore_then(elems)
|
|
||||||
.then_ignore(just('}'))
|
|
||||||
.map_with_span(|(elems, trailing_comma), span| TableConstr {
|
|
||||||
elems,
|
|
||||||
trailing_comma,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::ast::{Expr, Ident, Space, TableDestr, TablePattern, TablePatternElem};
|
use crate::ast::{Expr, Ident, Space, TableDestr, TablePattern, TablePatternElem};
|
||||||
|
|
||||||
use super::basic::{EParser, Error};
|
use super::basic::{bounded_separated, EParser, Error};
|
||||||
|
|
||||||
fn table_pattern_elem(
|
fn table_pattern_elem(
|
||||||
space: EParser<Space>,
|
space: EParser<Space>,
|
||||||
ident: EParser<Ident>,
|
ident: EParser<Ident>,
|
||||||
) -> impl Parser<char, TablePatternElem, Error = Error> {
|
) -> impl Parser<char, TablePatternElem, Error = Error> + Clone {
|
||||||
let positional = ident.clone().map(TablePatternElem::Positional);
|
let positional = ident.clone().map(TablePatternElem::Positional);
|
||||||
|
|
||||||
let named = ident
|
let named = ident
|
||||||
|
|
@ -30,24 +30,15 @@ fn table_pattern_elem(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table_pattern(space: EParser<Space>, ident: EParser<Ident>) -> EParser<TablePattern> {
|
pub fn table_pattern(space: EParser<Space>, ident: EParser<Ident>) -> EParser<TablePattern> {
|
||||||
let elem = space
|
let elem = table_pattern_elem(space.clone(), ident);
|
||||||
.clone()
|
bounded_separated(
|
||||||
.then(table_pattern_elem(space.clone(), ident))
|
space,
|
||||||
.then(space.clone())
|
just('{').to(()),
|
||||||
.map(|((s0, elem), s1)| (s0, elem, s1));
|
just('}').to(()),
|
||||||
|
just(',').to(()),
|
||||||
let trailing_comma = just(',').ignore_then(space).or_not();
|
elem,
|
||||||
|
)
|
||||||
let elems = elem.separated_by(just(',')).then(trailing_comma);
|
.map(TablePattern)
|
||||||
|
|
||||||
just('{')
|
|
||||||
.ignore_then(elems)
|
|
||||||
.then_ignore(just('}'))
|
|
||||||
.map_with_span(|(elems, trailing_comma), span| TablePattern {
|
|
||||||
elems,
|
|
||||||
trailing_comma,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +59,7 @@ pub fn table_destr(
|
||||||
pattern,
|
pattern,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ fn var_access(space: EParser<Space>, expr: EParser<Expr>) -> impl Parser<char, V
|
||||||
.then_ignore(just(']'))
|
.then_ignore(just(']'))
|
||||||
.map_with_span(|((s0, index), s1), span| Var::Access {
|
.map_with_span(|((s0, index), s1), span| Var::Access {
|
||||||
s0,
|
s0,
|
||||||
index: Box::new(index),
|
index: index.boxed(),
|
||||||
s1,
|
s1,
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
|
|
@ -39,11 +39,11 @@ fn var_assign(
|
||||||
|((((((local, s0), index), s1), s2), s3), value), span| Var::Assign {
|
|((((((local, s0), index), s1), s2), s3), value), span| Var::Assign {
|
||||||
local,
|
local,
|
||||||
s0,
|
s0,
|
||||||
index: Box::new(index),
|
index: index.boxed(),
|
||||||
s1,
|
s1,
|
||||||
s2,
|
s2,
|
||||||
s3,
|
s3,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -67,7 +67,7 @@ fn var_assign_ident(
|
||||||
name,
|
name,
|
||||||
s0,
|
s0,
|
||||||
s1,
|
s1,
|
||||||
value: Box::new(value),
|
value: value.boxed(),
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,27 @@
|
||||||
use pretty::{DocAllocator, DocBuilder, Pretty};
|
// TODO Remove this and print whitespace and comments properly
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
use crate::ast::Program;
|
use pretty::{Pretty, RcAllocator};
|
||||||
|
|
||||||
impl<'a, A: DocAllocator<'a>> Pretty<'a, A> for Program {
|
mod basic;
|
||||||
fn pretty(self, allocator: &'a A) -> DocBuilder<'a, A, ()> {
|
mod call;
|
||||||
allocator.text("Hello world")
|
mod expr;
|
||||||
}
|
mod field;
|
||||||
|
mod func_def;
|
||||||
|
mod lit;
|
||||||
|
mod program;
|
||||||
|
mod table_constr;
|
||||||
|
mod table_destr;
|
||||||
|
mod var;
|
||||||
|
|
||||||
|
const NEST_DEPTH: isize = 4;
|
||||||
|
|
||||||
|
pub fn pretty_to_string<P: Pretty<'static, RcAllocator>>(p: P, width: usize) -> String {
|
||||||
|
let mut out = vec![];
|
||||||
|
p.pretty(&RcAllocator)
|
||||||
|
.render(width, &mut out)
|
||||||
|
.expect("p could not be rendered");
|
||||||
|
let mut s = String::from_utf8(out).expect("p created non-utf8 string");
|
||||||
|
s.push('\n');
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/pretty/basic.rs
Normal file
41
src/pretty/basic.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
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> {
|
||||||
|
allocator.text(self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> BoundedSeparated<E> {
|
||||||
|
pub fn pretty<'a, D, FE>(
|
||||||
|
self,
|
||||||
|
allocator: &'a D,
|
||||||
|
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>,
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/pretty/call.rs
Normal file
36
src/pretty/call.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::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 {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
arg,
|
||||||
|
s2,
|
||||||
|
span: _,
|
||||||
|
} => expr
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(arg.pretty(allocator).parens()),
|
||||||
|
Self::NoArg {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
span: _,
|
||||||
|
} => expr.pretty(allocator).append(allocator.nil().parens()),
|
||||||
|
Self::Constr {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
constr,
|
||||||
|
span: _,
|
||||||
|
} => expr.pretty(allocator).append(constr.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/pretty/expr.rs
Normal file
131
src/pretty/expr.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::{Assoc, BinOp, Expr, Field, Var};
|
||||||
|
|
||||||
|
impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for BinOp {
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
allocator.text(match self {
|
||||||
|
Self::Mul => "*",
|
||||||
|
Self::Div => "/",
|
||||||
|
Self::Mod => "%",
|
||||||
|
Self::Add => "+",
|
||||||
|
Self::Sub => "-",
|
||||||
|
Self::Eq => "==",
|
||||||
|
Self::Neq => "!=",
|
||||||
|
Self::Gt => ">",
|
||||||
|
Self::Ge => ">=",
|
||||||
|
Self::Lt => "<",
|
||||||
|
Self::Le => "<=",
|
||||||
|
Self::And => "and",
|
||||||
|
Self::Or => "or",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
Self::Call(call) => call.pretty(allocator),
|
||||||
|
Self::Field(field) => field.pretty(allocator),
|
||||||
|
Self::Var(var) => var.pretty(allocator),
|
||||||
|
Self::TableConstr(constr) => constr.pretty(allocator),
|
||||||
|
Self::TableDestr(destr) => destr.pretty(allocator),
|
||||||
|
Self::FuncDef(def) => def.pretty(allocator),
|
||||||
|
Self::Paren {
|
||||||
|
s0,
|
||||||
|
inner,
|
||||||
|
s1,
|
||||||
|
span: _,
|
||||||
|
} => inner.pretty(allocator).parens(),
|
||||||
|
|
||||||
|
Self::Neg {
|
||||||
|
minus: _,
|
||||||
|
s0,
|
||||||
|
expr,
|
||||||
|
span: _,
|
||||||
|
} => {
|
||||||
|
let parenthesize = matches!(*expr, Self::BinOp { .. });
|
||||||
|
let inner = expr.pretty(allocator);
|
||||||
|
allocator
|
||||||
|
.text("-")
|
||||||
|
.append(if parenthesize { inner.parens() } else { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Not {
|
||||||
|
not: _,
|
||||||
|
s0,
|
||||||
|
expr,
|
||||||
|
span: _,
|
||||||
|
} => {
|
||||||
|
let parenthesize = matches!(*expr, Self::BinOp { .. });
|
||||||
|
let inner = expr.pretty(allocator);
|
||||||
|
allocator
|
||||||
|
.text("not ")
|
||||||
|
.append(if parenthesize { inner.parens() } else { inner })
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Add newlines and group properly
|
||||||
|
Self::BinOp {
|
||||||
|
left,
|
||||||
|
s0,
|
||||||
|
op,
|
||||||
|
s1,
|
||||||
|
right,
|
||||||
|
span: _,
|
||||||
|
} => {
|
||||||
|
// If we're left-associative, then the left subexpression can be
|
||||||
|
// at the same precedence and the right subexpression must be at
|
||||||
|
// a higher precedence.
|
||||||
|
|
||||||
|
// If we're right-associative, then the left subexpression must
|
||||||
|
// be at a higher precedence and the right subexpression can be
|
||||||
|
// at the same precedence.
|
||||||
|
|
||||||
|
// Minimum precedence that the left subexpression can be at
|
||||||
|
// without requiring parentheses.
|
||||||
|
let min_left_prec = match op.assoc() {
|
||||||
|
Assoc::Left => op.precedence(),
|
||||||
|
Assoc::Right => op.precedence() + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Minimum precedence that the right subexpression can be at
|
||||||
|
// without requiring parentheses.
|
||||||
|
let min_right_prec = match op.assoc() {
|
||||||
|
Assoc::Left => op.precedence() + 1,
|
||||||
|
Assoc::Right => op.precedence(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let left_paren = match *left {
|
||||||
|
// These end with an arbitrary expression on the right. If
|
||||||
|
// we don't add parentheses, we'll be assimilated into that
|
||||||
|
// expression.
|
||||||
|
Self::Field(Field::Assign { .. } | Field::AssignIdent { .. }) => true,
|
||||||
|
Self::Var(Var::Assign { .. } | Var::AssignIdent { .. }) => true,
|
||||||
|
|
||||||
|
Self::BinOp { op, .. } if op.precedence() < min_left_prec => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let right_paren =
|
||||||
|
matches!(*right, Self::BinOp { op, .. } if op.precedence() < min_right_prec);
|
||||||
|
|
||||||
|
let left = left.pretty(allocator);
|
||||||
|
let left = if left_paren { left.parens() } else { left };
|
||||||
|
|
||||||
|
let right = right.pretty(allocator);
|
||||||
|
let right = if right_paren { right.parens() } else { right };
|
||||||
|
|
||||||
|
left.append(allocator.space())
|
||||||
|
.append(op.pretty(allocator))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/pretty/field.rs
Normal file
68
src/pretty/field.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::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 {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
index,
|
||||||
|
s2,
|
||||||
|
span: _,
|
||||||
|
} => expr
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(index.pretty(allocator).brackets()),
|
||||||
|
Self::Assign {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
index,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
s4,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => expr
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(index.pretty(allocator).brackets())
|
||||||
|
.append(allocator.text(" = "))
|
||||||
|
.append(value.pretty(allocator)),
|
||||||
|
Self::AccessIdent {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
ident,
|
||||||
|
span: _,
|
||||||
|
} => expr
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(allocator.line_())
|
||||||
|
.append(allocator.text("."))
|
||||||
|
.append(ident.pretty(allocator))
|
||||||
|
.group(),
|
||||||
|
Self::AssignIdent {
|
||||||
|
expr,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
ident,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => expr
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(allocator.line_())
|
||||||
|
.append(allocator.text("."))
|
||||||
|
.append(ident.pretty(allocator))
|
||||||
|
.append(allocator.text(" = "))
|
||||||
|
.append(value.pretty(allocator))
|
||||||
|
.group(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/pretty/func_def.rs
Normal file
102
src/pretty/func_def.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::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 {
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
s2,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => allocator.text("function() ").append(body.pretty(allocator)),
|
||||||
|
|
||||||
|
Self::AnonArg {
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
arg,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => allocator
|
||||||
|
.text("function")
|
||||||
|
.append(arg.pretty(allocator).parens())
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(body.pretty(allocator)),
|
||||||
|
|
||||||
|
Self::AnonDestr {
|
||||||
|
s0,
|
||||||
|
pattern,
|
||||||
|
s1,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => allocator
|
||||||
|
.text("function")
|
||||||
|
.append(pattern.pretty(allocator))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(body.pretty(allocator)),
|
||||||
|
|
||||||
|
Self::NamedNoArg {
|
||||||
|
local,
|
||||||
|
s0,
|
||||||
|
name,
|
||||||
|
s1,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => local
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(allocator.text("function "))
|
||||||
|
.append(name)
|
||||||
|
.append(allocator.text("() "))
|
||||||
|
.append(body.pretty(allocator)),
|
||||||
|
|
||||||
|
Self::NamedArg {
|
||||||
|
local,
|
||||||
|
s0,
|
||||||
|
name,
|
||||||
|
s1,
|
||||||
|
s2,
|
||||||
|
arg,
|
||||||
|
s3,
|
||||||
|
s4,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => local
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(allocator.text("function "))
|
||||||
|
.append(name)
|
||||||
|
.append(arg.pretty(allocator).parens())
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(body.pretty(allocator)),
|
||||||
|
|
||||||
|
Self::NamedDestr {
|
||||||
|
local,
|
||||||
|
s0,
|
||||||
|
name,
|
||||||
|
s1,
|
||||||
|
pattern,
|
||||||
|
s2,
|
||||||
|
body,
|
||||||
|
span: _,
|
||||||
|
} => local
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(allocator.text("function "))
|
||||||
|
.append(name)
|
||||||
|
.append(pattern.pretty(allocator))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(body.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/pretty/lit.rs
Normal file
87
src/pretty/lit.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::{Lit, NumLit, StringLit, StringLitElem, TableLit, TableLitElem};
|
||||||
|
|
||||||
|
impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for NumLit {
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
allocator.text(format!("{self:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for StringLitElem {
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
match self {
|
||||||
|
Self::Plain(str) => allocator.text(str),
|
||||||
|
Self::Unicode(char) => allocator.text(format!("\\u{{{:x}}}", char as u32)),
|
||||||
|
Self::Backslash => allocator.text("\\\\"),
|
||||||
|
Self::DoubleQuote => allocator.text("\\\""),
|
||||||
|
Self::Tab => allocator.text("\\t"),
|
||||||
|
Self::CarriageReturn => allocator.text("\\r"),
|
||||||
|
Self::Newline => allocator.text("\\n"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for StringLit {
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
allocator
|
||||||
|
.concat(self.elems.into_iter().map(|e| e.pretty(allocator)))
|
||||||
|
.enclose(allocator.text("\""), allocator.text("\""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
Self::Named {
|
||||||
|
name,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => name
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(allocator.text(": "))
|
||||||
|
.append(value.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.0.pretty(
|
||||||
|
allocator,
|
||||||
|
allocator.text("'{"),
|
||||||
|
allocator.text("}"),
|
||||||
|
allocator.text(","),
|
||||||
|
|e| e.pretty(allocator),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"),
|
||||||
|
Self::Bool(false, _) => allocator.text("false"),
|
||||||
|
Self::Bool(true, _) => allocator.text("true"),
|
||||||
|
Self::Builtin(builtin, _) => allocator.text(format!("{builtin:?}")),
|
||||||
|
Self::Num(num) => num.pretty(allocator),
|
||||||
|
Self::String(string) => string.pretty(allocator),
|
||||||
|
Self::Table(table) => table.pretty(allocator),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/pretty/program.rs
Normal file
33
src/pretty/program.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::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 {
|
||||||
|
s0,
|
||||||
|
expr,
|
||||||
|
s1,
|
||||||
|
span: _,
|
||||||
|
} => expr.pretty(allocator),
|
||||||
|
|
||||||
|
Self::Module { s0, elems, span: _ } => {
|
||||||
|
allocator.text("module").append(allocator.line()).append(
|
||||||
|
allocator
|
||||||
|
.intersperse(
|
||||||
|
elems.elems.into_iter().map(|(s0, elem, s1)| {
|
||||||
|
allocator.line().append(elem.pretty(allocator))
|
||||||
|
}),
|
||||||
|
allocator.text(","),
|
||||||
|
)
|
||||||
|
.append(elems.trailing.map(|s| allocator.text(","))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/pretty/table_constr.rs
Normal file
44
src/pretty/table_constr.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::{TableConstr, TableConstrElem};
|
||||||
|
|
||||||
|
impl<'a, D> Pretty<'a, D> for TableConstrElem
|
||||||
|
where
|
||||||
|
D: DocAllocator<'a>,
|
||||||
|
D::Doc: Clone,
|
||||||
|
{
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
match self {
|
||||||
|
Self::Lit(lit) => lit.pretty(allocator),
|
||||||
|
Self::Indexed {
|
||||||
|
s0,
|
||||||
|
index,
|
||||||
|
s1,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => index
|
||||||
|
.pretty(allocator)
|
||||||
|
.brackets()
|
||||||
|
.append(allocator.text(": "))
|
||||||
|
.append(value.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.0.pretty(
|
||||||
|
allocator,
|
||||||
|
allocator.text("{"),
|
||||||
|
allocator.text("}"),
|
||||||
|
allocator.text(","),
|
||||||
|
|e| e.pretty(allocator),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/pretty/table_destr.rs
Normal file
53
src/pretty/table_destr.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::{TableDestr, TablePattern, TablePatternElem};
|
||||||
|
|
||||||
|
impl<'a, D: DocAllocator<'a>> Pretty<'a, D> for TablePatternElem {
|
||||||
|
fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D> {
|
||||||
|
match self {
|
||||||
|
Self::Positional(ident) => ident.pretty(allocator),
|
||||||
|
Self::Named {
|
||||||
|
name,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
ident,
|
||||||
|
span: _,
|
||||||
|
} => name
|
||||||
|
.pretty(allocator)
|
||||||
|
.append(allocator.text(": "))
|
||||||
|
.append(ident.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.0.pretty(
|
||||||
|
allocator,
|
||||||
|
allocator.text("{"),
|
||||||
|
allocator.text("}"),
|
||||||
|
allocator.text(","),
|
||||||
|
|e| e.pretty(allocator),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(self.pattern.pretty(allocator))
|
||||||
|
.append(allocator.text(" = "))
|
||||||
|
.append(self.value.pretty(allocator))
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/pretty/var.rs
Normal file
49
src/pretty/var.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use pretty::{DocAllocator, DocBuilder, Pretty};
|
||||||
|
|
||||||
|
use crate::ast::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 {
|
||||||
|
s0,
|
||||||
|
index,
|
||||||
|
s1,
|
||||||
|
span: _,
|
||||||
|
} => index.pretty(allocator).brackets(),
|
||||||
|
Self::Assign {
|
||||||
|
local,
|
||||||
|
s0,
|
||||||
|
index,
|
||||||
|
s1,
|
||||||
|
s2,
|
||||||
|
s3,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => local
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(index.pretty(allocator).brackets())
|
||||||
|
.append(allocator.text(" = "))
|
||||||
|
.append(value.pretty(allocator)),
|
||||||
|
Self::AccessIdent(ident) => ident.pretty(allocator),
|
||||||
|
Self::AssignIdent {
|
||||||
|
local,
|
||||||
|
name,
|
||||||
|
s0,
|
||||||
|
s1,
|
||||||
|
value,
|
||||||
|
span: _,
|
||||||
|
} => local
|
||||||
|
.map(|s| allocator.text("local "))
|
||||||
|
.unwrap_or_else(|| allocator.nil())
|
||||||
|
.append(name.pretty(allocator))
|
||||||
|
.append(allocator.text(" = "))
|
||||||
|
.append(value.pretty(allocator)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue