doughlang: Expressions in patterns
ast: - Document operators - Parameterize Pat with Annotation - Consolidate Path and Lit into "Value" (Expr) - Consolidate NamedRecord/Namedtuple into PatOp::TypePrefixed - Allow Pat<Pat,*> patterns - Additional helper functions on Expr and Pat lexer: - Support inner-doc comment syntax `//!` - Cleans up `///` or `//!` prefix parser: - Make Parse::Prec `Default` - Allow expression elision after `..`/`..=` - Fix Parser::consume not updating elide_do state - Add token splitting, to make `&&Expr` and `&&Pat` easier - error: Embed Pat precedence in ParseError
This commit is contained in:
@@ -130,7 +130,7 @@ impl<'a> Visit<'a> for CountNodes {
|
|||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> {
|
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||||
self.patterns += 1;
|
self.patterns += 1;
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ impl ToLisp {
|
|||||||
PatOp::Slice => "slice",
|
PatOp::Slice => "slice",
|
||||||
PatOp::ArRep => "ar-rep",
|
PatOp::ArRep => "ar-rep",
|
||||||
PatOp::Typed => "typed",
|
PatOp::Typed => "typed",
|
||||||
|
PatOp::TypePrefixed => "type-prefixed",
|
||||||
|
PatOp::Generic => "generic-in",
|
||||||
PatOp::Fn => "fn",
|
PatOp::Fn => "fn",
|
||||||
PatOp::Alt => "alt",
|
PatOp::Alt => "alt",
|
||||||
}
|
}
|
||||||
@@ -196,28 +198,13 @@ impl<'a> Visit<'a> for ToLisp {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> {
|
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||||
match item {
|
match item {
|
||||||
Pat::Ignore => print!("(ignore)"),
|
Pat::Ignore => print!("(ignore)"),
|
||||||
Pat::Never => print!("(never)"),
|
Pat::Never => print!("(never)"),
|
||||||
Pat::MetId(id) => print!("(replace {id})"),
|
Pat::MetId(id) => print!("(replace {id})"),
|
||||||
Pat::Name(name) => print!("{name}"),
|
Pat::Name(name) => print!("{name}"),
|
||||||
Pat::Path(path) => path.visit_in(self)?,
|
Pat::Value(literal) => literal.visit_in(self)?,
|
||||||
Pat::NamedRecord(path, pat) => {
|
|
||||||
print!("(struct-pat ");
|
|
||||||
path.visit_in(self)?;
|
|
||||||
print!(" ");
|
|
||||||
pat.visit_in(self)?;
|
|
||||||
print!(")")
|
|
||||||
}
|
|
||||||
Pat::NamedTuple(path, pat) => {
|
|
||||||
print!("(named ");
|
|
||||||
path.visit_in(self)?;
|
|
||||||
print!(" ");
|
|
||||||
pat.visit_in(self)?;
|
|
||||||
print!(")")
|
|
||||||
}
|
|
||||||
Pat::Lit(literal) => literal.visit_in(self)?,
|
|
||||||
Pat::Op(pat_op, pats) => {
|
Pat::Op(pat_op, pats) => {
|
||||||
print!("({}", self.pat_op(*pat_op));
|
print!("({}", self.pat_op(*pat_op));
|
||||||
for pat in pats {
|
for pat in pats {
|
||||||
|
|||||||
140
examples/weight.rs
Normal file
140
examples/weight.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
//! Demonstrates the visitor pattern
|
||||||
|
|
||||||
|
use std::{convert::Infallible, error::Error};
|
||||||
|
|
||||||
|
use doughlang::{
|
||||||
|
ast::{
|
||||||
|
visit::{Visit, Walk},
|
||||||
|
*,
|
||||||
|
},
|
||||||
|
lexer::Lexer,
|
||||||
|
parser::{Parser, expr::Prec},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CODE: &str = r#"
|
||||||
|
let x: [i32; 4] = [1, 2, 3, 4];
|
||||||
|
|
||||||
|
use foo::bar::baz::qux::{a, b, c, d, e};
|
||||||
|
|
||||||
|
struct Point<T> {x: T, y: T}
|
||||||
|
|
||||||
|
let v = Point {
|
||||||
|
x, y: 20 + x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> (&str, bool, i128) {
|
||||||
|
// An if expression is like the ternary conditional operator in C
|
||||||
|
let y = if 10 < 50 {
|
||||||
|
"\u{1f988}"
|
||||||
|
} else {
|
||||||
|
"x"
|
||||||
|
}
|
||||||
|
|
||||||
|
// A `while` expression is like the while-else construct in Python,
|
||||||
|
// but it returns a value via the `break` keyword
|
||||||
|
let z = while false {
|
||||||
|
// do a thing repeatedly
|
||||||
|
break true
|
||||||
|
} else {
|
||||||
|
// If `while` does not `break`, fall through to the `else` expression
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The same is true of `for` expressions!
|
||||||
|
let w = for idx in 0..100 {
|
||||||
|
if idx > 2 * 2 {
|
||||||
|
break idx
|
||||||
|
}
|
||||||
|
} else 12345; // semicolon operator required here
|
||||||
|
|
||||||
|
|
||||||
|
// desugars to
|
||||||
|
let w = match ((0..100).into_iter()) {
|
||||||
|
__iter => loop match (__iter.next()) {
|
||||||
|
None => break 12345,
|
||||||
|
Some (idx) => {
|
||||||
|
if (idx > (2 * 2)) {
|
||||||
|
break idx
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// A block evaluates to its last expression,
|
||||||
|
// or Empty if there is none
|
||||||
|
// (🦈, false, 5)
|
||||||
|
(y, z, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let ast: Anno<Expr> = Parser::new(Lexer::new(CODE)).parse(Prec::MIN)?;
|
||||||
|
println!("{ast}");
|
||||||
|
let mut counters = Weight::new();
|
||||||
|
counters.visit(&ast);
|
||||||
|
println!("{counters:#?}");
|
||||||
|
println!("{}", CODE.len());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
struct Weight {
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Weight {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Visit<'a> for Weight {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn visit_expr<A: Annotation>(&mut self, item: &'a Expr<A>) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_ident(&mut self, item: &'a str) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_path(&mut self, item: &'a Path) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item) + size_of_val(item.parts.as_slice());
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_literal(&mut self, item: &'a Literal) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_make<A: Annotation>(&mut self, item: &'a Make<A>) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_makearm<A: Annotation>(&mut self, item: &'a MakeArm<A>) -> Result<(), Self::Error> {
|
||||||
|
self.size += size_of_val(item);
|
||||||
|
item.children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
samples/elf.cl
Normal file
12
samples/elf.cl
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
use num::{
|
||||||
|
i32, u32, u16, u8
|
||||||
|
}
|
||||||
|
|
||||||
|
type Elf32_Addr, Elf32_Half, Elf32_Off, Elf32_Sword, Elf32_Word, UChar = u32, u16, u32, i32, u32, u8;
|
||||||
|
|
||||||
|
let <T> Option<T> = type Some(T) | None;
|
||||||
|
// is equivalent to
|
||||||
|
enum Option<T> {
|
||||||
|
Some(T),
|
||||||
|
None
|
||||||
|
};
|
||||||
@@ -7,15 +7,15 @@ enum Expr {
|
|||||||
|
|
||||||
fn execute(expr: Expr) -> f64 {
|
fn execute(expr: Expr) -> f64 {
|
||||||
match expr {
|
match expr {
|
||||||
ExprAtom(value) => value,
|
Expr::Atom(value) => value,
|
||||||
ExprOp('*', [lhs, rhs]) => execute(lhs) * execute(rhs),
|
Expr::Op('*', [lhs, rhs]) => execute(lhs) * execute(rhs),
|
||||||
ExprOp('/', [lhs, rhs]) => execute(lhs) / execute(rhs),
|
Expr::Op('/', [lhs, rhs]) => execute(lhs) / execute(rhs),
|
||||||
ExprOp('%', [lhs, rhs]) => execute(lhs) % execute(rhs),
|
Expr::Op('%', [lhs, rhs]) => execute(lhs) % execute(rhs),
|
||||||
ExprOp('+', [lhs, rhs]) => execute(lhs) + execute(rhs),
|
Expr::Op('+', [lhs, rhs]) => execute(lhs) + execute(rhs),
|
||||||
ExprOp('-', [lhs, rhs]) => execute(lhs) - execute(rhs),
|
Expr::Op('-', [lhs, rhs]) => execute(lhs) - execute(rhs),
|
||||||
// ExprOp('>', [lhs, rhs]) => (execute(lhs) as u64 >> execute(rhs) as u64) as f64,
|
// Expr::Op('>', [lhs, rhs]) => (execute(lhs) as u64 >> execute(rhs) as u64) as f64,
|
||||||
// ExprOp('<', [lhs, rhs]) => (execute(lhs) as u64 << execute(rhs) as u64) as f64,
|
// Expr::Op('<', [lhs, rhs]) => (execute(lhs) as u64 << execute(rhs) as u64) as f64,
|
||||||
ExprOp('-', [lhs]) => - execute(lhs),
|
Expr::Op('-', [lhs]) => - execute(lhs),
|
||||||
other => {
|
other => {
|
||||||
panic("Unknown operation: " + fmt(other))
|
panic("Unknown operation: " + fmt(other))
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,9 @@ fn execute(expr: Expr) -> f64 {
|
|||||||
/// Formats an expression to a string
|
/// Formats an expression to a string
|
||||||
fn fmt_expr(expr: Expr) -> str {
|
fn fmt_expr(expr: Expr) -> str {
|
||||||
match expr {
|
match expr {
|
||||||
ExprAtom(value) => fmt(value),
|
Expr::Atom(value) => fmt(value),
|
||||||
ExprOp(operator, [lhs, rhs]) => fmt('(', fmt_expr(lhs), ' ', operator, ' ', fmt_expr(rhs), ')'),
|
Expr::Op(operator, [lhs, rhs]) => fmt('(', fmt_expr(lhs), ' ', operator, ' ', fmt_expr(rhs), ')'),
|
||||||
ExprOp(operator, [rhs]) => fmt(operator, fmt_expr(rhs)),
|
Expr::Op(operator, [rhs]) => fmt(operator, fmt_expr(rhs)),
|
||||||
_ => println("Unexpected expr: ", expr),
|
_ => println("Unexpected expr: ", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
225
src/ast.rs
225
src/ast.rs
@@ -65,78 +65,137 @@ pub enum Expr<A: Annotation = Span> {
|
|||||||
/// - Traditional binary and unary operators (add, sub, neg, assign)
|
/// - Traditional binary and unary operators (add, sub, neg, assign)
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Op {
|
pub enum Op {
|
||||||
// -- true operators
|
/// `Expr (; Expr)*`
|
||||||
Do, // Expr ; Expr
|
Do,
|
||||||
As, // Expr as Expr
|
/// `Expr as Expr`
|
||||||
Macro, // macro { (Pat => Expr)* }
|
As,
|
||||||
Block, // { Expr }
|
/// `macro { (Pat => Expr)* }`
|
||||||
Array, // [ Expr,* ]
|
Macro,
|
||||||
ArRep, // [ Expr ; Expr ]
|
/// `{ Expr }`
|
||||||
Group, // ( Expr ,?)
|
Block,
|
||||||
Tuple, // Expr (, Expr)*
|
/// `[ Expr,* ]`
|
||||||
Meta, // #[ Expr ]
|
Array,
|
||||||
|
/// `[ Expr ; Expr ]`
|
||||||
|
ArRep,
|
||||||
|
/// `( Expr )`
|
||||||
|
Group,
|
||||||
|
/// `Expr (, Expr)*`
|
||||||
|
Tuple,
|
||||||
|
/// `#[ Expr ]`
|
||||||
|
Meta,
|
||||||
|
|
||||||
Try, // Expr '?'
|
/// `Expr '?'`
|
||||||
Index, // Expr [ Expr,* ]
|
Try,
|
||||||
Call, // Expr ( Expr,* )
|
/// `Expr [ Expr,* ]`
|
||||||
|
Index,
|
||||||
|
/// `Expr ( Expr,* )`
|
||||||
|
Call,
|
||||||
|
|
||||||
Pub, // pub Expr
|
/// `pub Expr`
|
||||||
Const, // const Expr
|
Pub,
|
||||||
Static, // static Expr
|
/// `const Expr`
|
||||||
Loop, // loop Expr
|
Const,
|
||||||
Match, // match Expr { <Bind(Match, ..)>,* }
|
/// `static Expr`
|
||||||
If, // if Expr Expr (else Expr)?
|
Static,
|
||||||
While, // while Expr Expr (else Expr)?
|
/// `loop Expr`
|
||||||
Break, // break Expr
|
Loop,
|
||||||
Return, // return Expr
|
/// `match Expr { <Bind(Match, ..)>,* }`
|
||||||
Continue, // continue
|
Match,
|
||||||
|
/// `if Expr Expr (else Expr)?`
|
||||||
|
If,
|
||||||
|
/// `while Expr Expr (else Expr)?`
|
||||||
|
While,
|
||||||
|
/// `break Expr`
|
||||||
|
Break,
|
||||||
|
/// `return Expr`
|
||||||
|
Return,
|
||||||
|
/// `continue`
|
||||||
|
Continue,
|
||||||
|
|
||||||
Dot, // Expr . Expr
|
/// `Expr . Expr`
|
||||||
|
Dot,
|
||||||
|
|
||||||
RangeEx, // Expr? ..Expr
|
/// `Expr? ..Expr`
|
||||||
RangeIn, // Expr? ..=Expr
|
RangeEx,
|
||||||
Neg, // -Expr
|
/// `Expr? ..=Expr`
|
||||||
Not, // !Expr
|
RangeIn,
|
||||||
Identity, // !!Expr
|
/// `-Expr`
|
||||||
Refer, // &Expr
|
Neg,
|
||||||
Deref, // *Expr
|
/// `!Expr`
|
||||||
|
Not,
|
||||||
|
/// `!!Expr`
|
||||||
|
Identity,
|
||||||
|
/// `&Expr`
|
||||||
|
Refer,
|
||||||
|
/// `*Expr`
|
||||||
|
Deref,
|
||||||
|
|
||||||
Mul, // Expr * Expr
|
/// `Expr * Expr`
|
||||||
Div, // Expr / Expr
|
Mul,
|
||||||
Rem, // Expr % Expr
|
/// `Expr / Expr`
|
||||||
|
Div,
|
||||||
|
/// `Expr % Expr`
|
||||||
|
Rem,
|
||||||
|
|
||||||
Add, // Expr + Expr
|
/// `Expr + Expr`
|
||||||
Sub, // Expr - Expr
|
Add,
|
||||||
|
/// `Expr - Expr`
|
||||||
|
Sub,
|
||||||
|
|
||||||
Shl, // Expr << Expr
|
/// `Expr << Expr`
|
||||||
Shr, // Expr >> Expr
|
Shl,
|
||||||
|
/// `Expr >> Expr`
|
||||||
|
Shr,
|
||||||
|
|
||||||
And, // Expr & Expr
|
/// `Expr & Expr`
|
||||||
Xor, // Expr ^ Expr
|
And,
|
||||||
Or, // Expr | Expr
|
/// `Expr ^ Expr`
|
||||||
|
Xor,
|
||||||
|
/// `Expr | Expr`
|
||||||
|
Or,
|
||||||
|
|
||||||
Lt, // Expr < Expr
|
/// `Expr < Expr`
|
||||||
Leq, // Expr <= Expr
|
Lt,
|
||||||
Eq, // Expr == Expr
|
/// `Expr <= Expr`
|
||||||
Neq, // Expr != Expr
|
Leq,
|
||||||
Geq, // Expr >= Expr
|
/// `Expr == Expr`
|
||||||
Gt, // Expr > Expr
|
Eq,
|
||||||
|
/// `Expr != Expr`
|
||||||
|
Neq,
|
||||||
|
/// `Expr >= Expr`
|
||||||
|
Geq,
|
||||||
|
/// `Expr > Expr`
|
||||||
|
Gt,
|
||||||
|
|
||||||
LogAnd, // Expr && Expr
|
/// `Expr && Expr`
|
||||||
LogXor, // Expr ^^ Expr
|
LogAnd,
|
||||||
LogOr, // Expr || Expr
|
/// `Expr ^^ Expr`
|
||||||
|
LogXor,
|
||||||
|
/// `Expr || Expr`
|
||||||
|
LogOr,
|
||||||
|
|
||||||
Set, // Expr = Expr
|
/// `Expr = Expr`
|
||||||
MulSet, // Expr *= Expr
|
Set,
|
||||||
DivSet, // Expr /= Expr
|
/// `Expr *= Expr`
|
||||||
RemSet, // Expr %= Expr
|
MulSet,
|
||||||
AddSet, // Expr += Expr
|
/// `Expr /= Expr`
|
||||||
SubSet, // Expr -= Expr
|
DivSet,
|
||||||
ShlSet, // Expr <<= Expr
|
/// `Expr %= Expr`
|
||||||
ShrSet, // Expr >>= Expr
|
RemSet,
|
||||||
AndSet, // Expr &= Expr
|
/// `Expr += Expr`
|
||||||
XorSet, // Expr ^= Expr
|
AddSet,
|
||||||
OrSet, // Expr |= Expr
|
/// `Expr -= Expr`
|
||||||
|
SubSet,
|
||||||
|
/// `Expr <<= Expr`
|
||||||
|
ShlSet,
|
||||||
|
/// `Expr >>= Expr`
|
||||||
|
ShrSet,
|
||||||
|
/// `Expr &= Expr`
|
||||||
|
AndSet,
|
||||||
|
/// `Expr ^= Expr`
|
||||||
|
XorSet,
|
||||||
|
/// `Expr |= Expr`
|
||||||
|
OrSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A qualified identifier
|
/// A qualified identifier
|
||||||
@@ -181,11 +240,11 @@ pub enum Use {
|
|||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// let Pat (= Expr (else Expr)?)?
|
/// let Pat (= Expr (else Expr)?)?
|
||||||
/// type Pat (= Expr)?
|
/// type Pat (= Expr)?
|
||||||
/// struct Pat
|
|
||||||
/// enum Pat
|
|
||||||
/// fn Pat Expr
|
/// fn Pat Expr
|
||||||
/// mod Pat Expr
|
/// mod Pat Expr
|
||||||
/// impl Pat Expr
|
/// impl Pat Expr
|
||||||
|
/// struct Pat
|
||||||
|
/// enum Pat
|
||||||
/// for Pat in Expr Expr (else Expr)?
|
/// for Pat in Expr Expr (else Expr)?
|
||||||
/// Pat => Expr // in match
|
/// Pat => Expr // in match
|
||||||
/// ```
|
/// ```
|
||||||
@@ -193,7 +252,7 @@ pub enum Use {
|
|||||||
pub struct Bind<A: Annotation = Span>(
|
pub struct Bind<A: Annotation = Span>(
|
||||||
pub BindOp,
|
pub BindOp,
|
||||||
pub Vec<Path>,
|
pub Vec<Path>,
|
||||||
pub Pat,
|
pub Pat<A>,
|
||||||
pub Vec<Anno<Expr<A>, A>>,
|
pub Vec<Anno<Expr<A>, A>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -237,7 +296,7 @@ pub struct MakeArm<A: Annotation = Span>(pub String, pub Option<Anno<Expr<A>, A>
|
|||||||
///
|
///
|
||||||
/// This covers both patterns in Match expressions, and type annotations.
|
/// This covers both patterns in Match expressions, and type annotations.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Pat {
|
pub enum Pat<A: Annotation = Span> {
|
||||||
/// Matches anything without binding
|
/// Matches anything without binding
|
||||||
Ignore,
|
Ignore,
|
||||||
/// Matches nothing, ever
|
/// Matches nothing, ever
|
||||||
@@ -246,16 +305,10 @@ pub enum Pat {
|
|||||||
MetId(String),
|
MetId(String),
|
||||||
/// Matches anything, and binds it to a name
|
/// Matches anything, and binds it to a name
|
||||||
Name(String),
|
Name(String),
|
||||||
/// Matches against a named const value
|
/// Matches a value by equality comparison
|
||||||
Path(Path),
|
Value(Box<Anno<Expr<A>, A>>),
|
||||||
/// Matches a Struct Expression `Ident { Pat }`
|
|
||||||
NamedRecord(Path, Box<Pat>),
|
|
||||||
/// Matches a Tuple Struct Expression `Ident ( Pat )`
|
|
||||||
NamedTuple(Path, Box<Pat>),
|
|
||||||
/// Matches a literal value by equality comparison
|
|
||||||
Lit(Literal),
|
|
||||||
/// Matches a compound pattern
|
/// Matches a compound pattern
|
||||||
Op(PatOp, Vec<Pat>),
|
Op(PatOp, Vec<Pat<A>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Operators on lists of patterns
|
/// Operators on lists of patterns
|
||||||
@@ -285,6 +338,10 @@ pub enum PatOp {
|
|||||||
ArRep,
|
ArRep,
|
||||||
/// Matches a type annotation or struct member
|
/// Matches a type annotation or struct member
|
||||||
Typed,
|
Typed,
|
||||||
|
/// Matches a prefix-type-annotated structure
|
||||||
|
TypePrefixed,
|
||||||
|
/// Matches a generic specialization annotation
|
||||||
|
Generic,
|
||||||
/// Changes the binding mode to "function-body"
|
/// Changes the binding mode to "function-body"
|
||||||
Fn,
|
Fn,
|
||||||
/// Matches one of a list of alternatives
|
/// Matches one of a list of alternatives
|
||||||
@@ -321,6 +378,13 @@ impl<A: Annotation> Expr<A> {
|
|||||||
Self::Op(Op::Do, exprs)
|
Self::Op(Op::Do, exprs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_tuple(self, annotation: A) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Op(Op::Tuple, _) => self,
|
||||||
|
_ => Self::Op(Op::Tuple, vec![self.anno(annotation)]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn is_place(&self) -> bool {
|
pub const fn is_place(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
@@ -328,6 +392,10 @@ impl<A: Annotation> Expr<A> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn is_value(&self) -> bool {
|
||||||
|
!self.is_place()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub const fn as_slice(&self) -> Option<(Op, &[Anno<Expr<A>, A>])> {
|
pub const fn as_slice(&self) -> Option<(Op, &[Anno<Expr<A>, A>])> {
|
||||||
match self {
|
match self {
|
||||||
@@ -337,6 +405,15 @@ impl<A: Annotation> Expr<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A: Annotation> Pat<A> {
|
||||||
|
pub fn to_tuple(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Op(PatOp::Tuple, _) => self,
|
||||||
|
_ => Self::Op(PatOp::Tuple, vec![self]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for Path {
|
impl From<&str> for Path {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
Self { parts: vec![value.to_owned()] }
|
Self { parts: vec![value.to_owned()] }
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ impl<A: Annotation> Display for Expr<A> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Self::Op(op @ Op::Call, exprs) => match exprs.as_slice() {
|
Self::Op(op @ Op::Call, exprs) => match exprs.as_slice() {
|
||||||
[callee, args @ ..] => f.delimit(fmt!("{callee}("), ")").list(args, ", "),
|
[callee, Anno(Expr::Op(Op::Tuple, args), _)] => {
|
||||||
|
f.delimit(fmt!("{callee}("), ")").list(args, ", ")
|
||||||
|
}
|
||||||
|
[callee, args @ ..] => f.delimit(fmt!("{callee}(?"), "?)").list(args, ", "),
|
||||||
[] => write!(f, "{op}"),
|
[] => write!(f, "{op}"),
|
||||||
},
|
},
|
||||||
Self::Op(op @ Op::Index, exprs) => match exprs.as_slice() {
|
Self::Op(op @ Op::Index, exprs) => match exprs.as_slice() {
|
||||||
@@ -191,11 +194,14 @@ impl<A: Annotation> Display for Bind<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BindOp::Struct | BindOp::Enum => match pat {
|
BindOp::Struct | BindOp::Enum => match pat {
|
||||||
Pat::NamedRecord(name, bind) => match bind.as_ref() {
|
Pat::Op(PatOp::TypePrefixed, bind) => match bind.as_slice() {
|
||||||
Pat::Op(PatOp::Tuple, parts) => f
|
[name, Pat::Op(PatOp::Record, parts)] => f
|
||||||
.delimit_indented(fmt!("{name} {{"), "}")
|
.delimit_indented(fmt!("{name} {{"), "}")
|
||||||
.list_wrap("\n", parts, ",\n", ",\n"),
|
.list_wrap("\n", parts, ",\n", ",\n"),
|
||||||
other => write!(f, "{name} {{ {other} }}"),
|
[name, Pat::Op(PatOp::Tuple, parts)] => {
|
||||||
|
f.delimit(fmt!("{name}("), ")").list(parts, ", ")
|
||||||
|
}
|
||||||
|
_ => pat.fmt(f),
|
||||||
},
|
},
|
||||||
_ => pat.fmt(f),
|
_ => pat.fmt(f),
|
||||||
},
|
},
|
||||||
@@ -248,22 +254,14 @@ impl<A: Annotation> Display for MakeArm<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Pat {
|
impl<A: Annotation> Display for Pat<A> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Ignore => "_".fmt(f),
|
Self::Ignore => "_".fmt(f),
|
||||||
Self::Never => "!".fmt(f),
|
Self::Never => "!".fmt(f),
|
||||||
Self::Lit(literal) => literal.fmt(f),
|
Self::Value(literal) => literal.fmt(f),
|
||||||
Self::MetId(name) => write!(f, "`{name}"),
|
Self::MetId(name) => write!(f, "`{name}"),
|
||||||
Self::Name(name) => name.fmt(f),
|
Self::Name(name) => name.fmt(f),
|
||||||
Self::Path(path) => path.fmt(f),
|
|
||||||
Self::NamedRecord(name, bind) => match bind.as_ref() {
|
|
||||||
Pat::Op(PatOp::Tuple, parts) => {
|
|
||||||
f.delimit(fmt!("{name} {{ "), " }").list(parts, ", ")
|
|
||||||
}
|
|
||||||
other => write!(f, "{name} {{ {other} }}"),
|
|
||||||
},
|
|
||||||
Self::NamedTuple(name, bind) => write!(f, "{name} {bind}"),
|
|
||||||
Self::Op(PatOp::Record, pats) => f.delimit("{ ", " }").list(pats, ", "),
|
Self::Op(PatOp::Record, pats) => f.delimit("{ ", " }").list(pats, ", "),
|
||||||
Self::Op(PatOp::Tuple, pats) => f.delimit("(", ")").list(pats, ", "),
|
Self::Op(PatOp::Tuple, pats) => f.delimit("(", ")").list(pats, ", "),
|
||||||
Self::Op(PatOp::Slice, pats) => f.delimit("[", "]").list(pats, ", "),
|
Self::Op(PatOp::Slice, pats) => f.delimit("[", "]").list(pats, ", "),
|
||||||
@@ -273,6 +271,14 @@ impl Display for Pat {
|
|||||||
pats => f.list(pats, op),
|
pats => f.list(pats, op),
|
||||||
},
|
},
|
||||||
Self::Op(op @ PatOp::Alt, pats) => f.list(pats, op),
|
Self::Op(op @ PatOp::Alt, pats) => f.list(pats, op),
|
||||||
|
Self::Op(op @ PatOp::Generic, pats) => match pats.as_slice() {
|
||||||
|
[] => op.fmt(f),
|
||||||
|
[first, rest @ ..] => f.delimit(fmt!("{first}<"), ">").list(rest, ", "),
|
||||||
|
},
|
||||||
|
Self::Op(op @ PatOp::TypePrefixed, pats) => match pats.as_slice() {
|
||||||
|
[] => op.fmt(f),
|
||||||
|
[first, rest @ ..] => f.delimit(fmt!("{first} "), "").list(rest, ",? "),
|
||||||
|
},
|
||||||
Self::Op(op, pats) => match pats.as_slice() {
|
Self::Op(op, pats) => match pats.as_slice() {
|
||||||
[] => op.fmt(f),
|
[] => op.fmt(f),
|
||||||
[rest] => write!(f, "{op}{rest}"),
|
[rest] => write!(f, "{op}{rest}"),
|
||||||
@@ -297,6 +303,8 @@ impl Display for PatOp {
|
|||||||
Self::Slice => ", ",
|
Self::Slice => ", ",
|
||||||
Self::ArRep => "[;]",
|
Self::ArRep => "[;]",
|
||||||
Self::Typed => ": ",
|
Self::Typed => ": ",
|
||||||
|
Self::Generic => "T<>",
|
||||||
|
Self::TypePrefixed => "T()",
|
||||||
Self::Fn => " -> ",
|
Self::Fn => " -> ",
|
||||||
Self::Alt => " | ",
|
Self::Alt => " | ",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ pub trait Fold<A: Annotation> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes a [`Pat`], possibly transforms it, and produces a replacement [`Pat`]
|
/// Consumes a [`Pat`], possibly transforms it, and produces a replacement [`Pat`]
|
||||||
fn fold_pat(&mut self, pat: Pat) -> Result<Pat, Self::Error> {
|
fn fold_pat(&mut self, pat: Pat<A>) -> Result<Pat<A>, Self::Error> {
|
||||||
pat.children(self)
|
pat.children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ impl<A: Annotation> Foldable<A> for Use {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Foldable<A> for Pat {
|
impl<A: Annotation> Foldable<A> for Pat<A> {
|
||||||
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
fn fold_in<F: Fold<A> + ?Sized>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||||
folder.fold_pat(self)
|
folder.fold_pat(self)
|
||||||
}
|
}
|
||||||
@@ -149,14 +149,7 @@ impl<A: Annotation> Foldable<A> for Pat {
|
|||||||
Self::Never => Self::Never,
|
Self::Never => Self::Never,
|
||||||
Self::MetId(name) => Self::MetId(name.fold_in(folder)?),
|
Self::MetId(name) => Self::MetId(name.fold_in(folder)?),
|
||||||
Self::Name(name) => Self::Name(name.fold_in(folder)?),
|
Self::Name(name) => Self::Name(name.fold_in(folder)?),
|
||||||
Self::Path(path) => Self::Path(path.fold_in(folder)?),
|
Self::Value(expr) => Self::Value(expr.fold_in(folder)?),
|
||||||
Self::NamedRecord(path, pat) => {
|
|
||||||
Self::NamedRecord(path.fold_in(folder)?, pat.fold_in(folder)?)
|
|
||||||
}
|
|
||||||
Self::NamedTuple(path, pat) => {
|
|
||||||
Self::NamedTuple(path.fold_in(folder)?, pat.fold_in(folder)?)
|
|
||||||
}
|
|
||||||
Self::Lit(literal) => Self::Lit(literal.fold_in(folder)?),
|
|
||||||
Self::Op(op, pats) => Self::Op(op, pats.fold_in(folder)?),
|
Self::Op(op, pats) => Self::Op(op, pats.fold_in(folder)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::collections::HashMap;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Subst<A: Annotation> {
|
pub struct Subst<A: Annotation> {
|
||||||
pub exp: HashMap<String, Expr<A>>,
|
pub exp: HashMap<String, Expr<A>>,
|
||||||
pub pat: HashMap<String, Pat>,
|
pub pat: HashMap<String, Pat<A>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Default for Subst<A> {
|
impl<A: Annotation> Default for Subst<A> {
|
||||||
@@ -16,7 +16,7 @@ impl<A: Annotation> Default for Subst<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<A: Annotation> Subst<A> {
|
impl<A: Annotation> Subst<A> {
|
||||||
fn add_pat(&mut self, name: String, pat: &Pat) -> bool {
|
fn add_pat(&mut self, name: String, pat: &Pat<A>) -> bool {
|
||||||
if self.exp.contains_key(&name) {
|
if self.exp.contains_key(&name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ impl<A: Annotation> Match<A> for MakeArm<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: Annotation> Match<A> for Pat {
|
impl<A: Annotation> Match<A> for Pat<A> {
|
||||||
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
fn recurse(sub: &mut Subst<A>, pat: &Self, expr: &Self) -> bool {
|
||||||
match (pat, expr) {
|
match (pat, expr) {
|
||||||
(Pat::MetId(name), _) if name == "_" => true,
|
(Pat::MetId(name), _) if name == "_" => true,
|
||||||
@@ -172,14 +172,8 @@ impl<A: Annotation> Match<A> for Pat {
|
|||||||
(Pat::Never, _) => false,
|
(Pat::Never, _) => false,
|
||||||
(Pat::Name(pat), Pat::Name(expr)) => pat == expr,
|
(Pat::Name(pat), Pat::Name(expr)) => pat == expr,
|
||||||
(Pat::Name(_), _) => false,
|
(Pat::Name(_), _) => false,
|
||||||
(Pat::Path(_), Pat::Path(_)) => true,
|
(Pat::Value(pat), Pat::Value(expr)) => pat == expr,
|
||||||
(Pat::Path(_), _) => false,
|
(Pat::Value(_), _) => false,
|
||||||
(Pat::NamedRecord(_, pat), Pat::NamedRecord(_, expr)) => Match::recurse(sub, pat, expr),
|
|
||||||
(Pat::NamedRecord(..), _) => false,
|
|
||||||
(Pat::NamedTuple(_, pat), Pat::NamedTuple(_, expr)) => Match::recurse(sub, pat, expr),
|
|
||||||
(Pat::NamedTuple(..), _) => false,
|
|
||||||
(Pat::Lit(pat), Pat::Lit(expr)) => pat == expr,
|
|
||||||
(Pat::Lit(_), _) => false,
|
|
||||||
(Pat::Op(_, pat), Pat::Op(_, expr)) => Match::recurse(sub, pat, expr),
|
(Pat::Op(_, pat), Pat::Op(_, expr)) => Match::recurse(sub, pat, expr),
|
||||||
(Pat::Op(..), _) => false,
|
(Pat::Op(..), _) => false,
|
||||||
}
|
}
|
||||||
@@ -187,14 +181,13 @@ impl<A: Annotation> Match<A> for Pat {
|
|||||||
|
|
||||||
fn apply(&mut self, sub: &Subst<A>) {
|
fn apply(&mut self, sub: &Subst<A>) {
|
||||||
match self {
|
match self {
|
||||||
Pat::Ignore | Pat::Never | Pat::Name(_) | Pat::Path(_) | Pat::Lit(_) => {}
|
Pat::Ignore | Pat::Never | Pat::Name(_) => {}
|
||||||
|
Pat::Value(expr) => expr.apply(sub),
|
||||||
Pat::MetId(id) => {
|
Pat::MetId(id) => {
|
||||||
if let Some(expr) = sub.pat.get(id) {
|
if let Some(expr) = sub.pat.get(id) {
|
||||||
*self = expr.clone();
|
*self = expr.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Pat::NamedRecord(_, expr) => expr.apply(sub),
|
|
||||||
Pat::NamedTuple(_, expr) => expr.apply(sub),
|
|
||||||
Pat::Op(_, pats) => pats.apply(sub),
|
Pat::Op(_, pats) => pats.apply(sub),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub trait Visit<'a> {
|
|||||||
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
fn visit_use(&mut self, item: &'a Use) -> Result<(), Self::Error> {
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
fn visit_pat(&mut self, item: &'a Pat) -> Result<(), Self::Error> {
|
fn visit_pat<A: Annotation>(&mut self, item: &'a Pat<A>) -> Result<(), Self::Error> {
|
||||||
item.children(self)
|
item.children(self)
|
||||||
}
|
}
|
||||||
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
fn visit_bind<A: Annotation>(&mut self, item: &'a Bind<A>) -> Result<(), Self::Error> {
|
||||||
@@ -107,18 +107,13 @@ impl<'a> Walk<'a> for Use {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Walk<'a> for Pat {
|
impl<'a, A: Annotation> Walk<'a> for Pat<A> {
|
||||||
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
fn children<V: Visit<'a> + ?Sized>(&'a self, v: &mut V) -> Result<(), V::Error> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ignore | Self::Never => Ok(()),
|
Self::Ignore | Self::Never => Ok(()),
|
||||||
Self::MetId(id) => id.visit_in(v),
|
Self::MetId(id) => id.visit_in(v),
|
||||||
Self::Name(name) => name.visit_in(v),
|
Self::Name(name) => name.visit_in(v),
|
||||||
Self::Path(path) => path.visit_in(v),
|
Self::Value(literal) => literal.visit_in(v),
|
||||||
Self::NamedRecord(path, pat) | Self::NamedTuple(path, pat) => {
|
|
||||||
path.visit_in(v)?;
|
|
||||||
pat.visit_in(v)
|
|
||||||
}
|
|
||||||
Self::Lit(literal) => literal.visit_in(v),
|
|
||||||
Self::Op(_, pats) => pats.visit_in(v),
|
Self::Op(_, pats) => pats.visit_in(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/lexer.rs
12
src/lexer.rs
@@ -257,11 +257,19 @@ impl<'t> Lexer<'t> {
|
|||||||
/// Consumes characters until the lexer reaches a newline `'\n'`
|
/// Consumes characters until the lexer reaches a newline `'\n'`
|
||||||
pub fn line_comment(&mut self) -> Result<Token, LexError> {
|
pub fn line_comment(&mut self) -> Result<Token, LexError> {
|
||||||
let kind = match self.consume().peek() {
|
let kind = match self.consume().peek() {
|
||||||
Some('!' | '/') => TKind::Doc,
|
Some('/') => TKind::OutDoc,
|
||||||
|
Some('!') => TKind::InDoc,
|
||||||
_ => TKind::Comment,
|
_ => TKind::Comment,
|
||||||
};
|
};
|
||||||
while self.consume().peek().is_some_and(|c| c != '\n') {}
|
while self.consume().peek().is_some_and(|c| c != '\n') {}
|
||||||
Ok(self.produce(kind))
|
let (lexeme, _) = self.as_str();
|
||||||
|
let lexeme = lexeme
|
||||||
|
.strip_prefix("///")
|
||||||
|
.or_else(|| lexeme.strip_prefix("//!"))
|
||||||
|
.map(|lexeme| lexeme.strip_prefix(" ").unwrap_or(lexeme))
|
||||||
|
.unwrap_or(lexeme);
|
||||||
|
|
||||||
|
Ok(self.produce_with_lexeme(kind, Lexeme::String(lexeme.into())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes characters until the lexer reaches the end of a *nested* block comment.
|
/// Consumes characters until the lexer reaches the end of a *nested* block comment.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub trait Parse<'t> {
|
pub trait Parse<'t> {
|
||||||
type Prec: Copy;
|
type Prec: Copy + Default;
|
||||||
|
|
||||||
fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult<Self>
|
fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult<Self>
|
||||||
where Self: Sized;
|
where Self: Sized;
|
||||||
@@ -85,7 +85,10 @@ impl<'t> Parser<'t> {
|
|||||||
|
|
||||||
if let Ok(tok) = &tok {
|
if let Ok(tok) = &tok {
|
||||||
self.last_loc = tok.span;
|
self.last_loc = tok.span;
|
||||||
self.elide_do = matches!(tok.kind, TKind::RCurly | TKind::Semi)
|
self.elide_do = matches!(
|
||||||
|
tok.kind,
|
||||||
|
TKind::RCurly | TKind::Semi | TKind::DotDot | TKind::DotDotEq
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tok
|
tok
|
||||||
@@ -183,9 +186,26 @@ impl<'t> Parser<'t> {
|
|||||||
|
|
||||||
/// Consumes the currently peeked token without returning it.
|
/// Consumes the currently peeked token without returning it.
|
||||||
pub fn consume(&mut self) -> &mut Self {
|
pub fn consume(&mut self) -> &mut Self {
|
||||||
self.next_tok = None;
|
if self.next_tok.as_ref().is_some_and(|tok| tok.is_ok()) {
|
||||||
|
let _ = self.take();
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Consumes the next token, and attempts to split it into multiple.
|
||||||
|
///
|
||||||
|
/// If the next token cannot be split, it will be returned.
|
||||||
|
pub fn split(&mut self) -> PResult<Token> {
|
||||||
|
let Token { lexeme, kind, span } = self.next()?;
|
||||||
|
let kind = match kind.split() {
|
||||||
|
Err(_) => kind,
|
||||||
|
Ok((out, next)) => {
|
||||||
|
self.next_tok = Some(Ok(Token { lexeme: lexeme.clone(), kind: next, span }));
|
||||||
|
out
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Token { lexeme, kind, span })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t> Parse<'t> for Path {
|
impl<'t> Parse<'t> for Path {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use super::pat::Prec as PatPrec;
|
||||||
use crate::{ast::BindOp, lexer::LexError, span::Span, token::TKind};
|
use crate::{ast::BindOp, lexer::LexError, span::Span, token::TKind};
|
||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ pub enum ParseError {
|
|||||||
Expected(TKind, TKind, Span),
|
Expected(TKind, TKind, Span),
|
||||||
NotLiteral(TKind, Span),
|
NotLiteral(TKind, Span),
|
||||||
NotUse(TKind, Span),
|
NotUse(TKind, Span),
|
||||||
NotPattern(TKind, Span),
|
NotPattern(TKind, PatPrec, Span),
|
||||||
NotMatch(BindOp, BindOp, Span),
|
NotMatch(BindOp, BindOp, Span),
|
||||||
NotPrefix(TKind, Span),
|
NotPrefix(TKind, Span),
|
||||||
NotInfix(TKind, Span),
|
NotInfix(TKind, Span),
|
||||||
@@ -30,7 +31,9 @@ impl Display for ParseError {
|
|||||||
Self::Expected(e, tk, loc) => write!(f, "{loc}: Expected {e:?}, got {tk:?}."),
|
Self::Expected(e, tk, loc) => write!(f, "{loc}: Expected {e:?}, got {tk:?}."),
|
||||||
Self::NotLiteral(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a literal."),
|
Self::NotLiteral(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a literal."),
|
||||||
Self::NotUse(tk, loc) => write!(f, "{loc}: {tk:?} is no use!"),
|
Self::NotUse(tk, loc) => write!(f, "{loc}: {tk:?} is no use!"),
|
||||||
Self::NotPattern(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a pattern."),
|
Self::NotPattern(tk, prec, loc) => {
|
||||||
|
write!(f, "{loc}: {tk:?} is not valid in a {prec:?} pattern.")
|
||||||
|
}
|
||||||
Self::NotMatch(bk, ex, loc) => {
|
Self::NotMatch(bk, ex, loc) => {
|
||||||
write!(f, "{loc}: {bk:?} is not valid in a {ex:?} expression.")
|
write!(f, "{loc}: {bk:?} is not valid in a {ex:?} expression.")
|
||||||
}
|
}
|
||||||
@@ -46,6 +49,7 @@ pub type PResult<T> = Result<T, ParseError>;
|
|||||||
pub trait PResultExt<T> {
|
pub trait PResultExt<T> {
|
||||||
fn no_eof(self) -> PResult<T>;
|
fn no_eof(self) -> PResult<T>;
|
||||||
fn allow_eof(self) -> PResult<Option<T>>;
|
fn allow_eof(self) -> PResult<Option<T>>;
|
||||||
|
fn is_eof(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PResultExt<T> for PResult<T> {
|
impl<T> PResultExt<T> for PResult<T> {
|
||||||
@@ -62,6 +66,9 @@ impl<T> PResultExt<T> for PResult<T> {
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn is_eof(&self) -> bool {
|
||||||
|
matches!(self, Err(ParseError::EOF(_)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens a scope where [`ParseError::EOF`] is unexpected (See [`PResultExt::no_eof`])
|
/// Opens a scope where [`ParseError::EOF`] is unexpected (See [`PResultExt::no_eof`])
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ pub enum Ps {
|
|||||||
Lit, // Literal
|
Lit, // Literal
|
||||||
Use, // use Use
|
Use, // use Use
|
||||||
Def, // any definition (let, const, static, struct, enum, fn, ...)
|
Def, // any definition (let, const, static, struct, enum, fn, ...)
|
||||||
|
Doc, // Documentation Comment
|
||||||
For, // for Pat in Expr Expr else Expr
|
For, // for Pat in Expr Expr else Expr
|
||||||
Lambda0, // || Expr
|
Lambda0, // || Expr
|
||||||
Lambda, // | Pat,* | Expr
|
Lambda, // | Pat,* | Expr
|
||||||
@@ -90,6 +91,8 @@ pub enum Ps {
|
|||||||
/// and its [precedence level](Prec)
|
/// and its [precedence level](Prec)
|
||||||
fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
|
fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||||
Ok(match token.kind {
|
Ok(match token.kind {
|
||||||
|
TKind::OutDoc => (Ps::Doc, Prec::Max),
|
||||||
|
TKind::InDoc => (Ps::Doc, Prec::Max),
|
||||||
TKind::Do => (Ps::Op(Op::Do), Prec::Do),
|
TKind::Do => (Ps::Op(Op::Do), Prec::Do),
|
||||||
TKind::Semi => (Ps::End, Prec::Body),
|
TKind::Semi => (Ps::End, Prec::Body),
|
||||||
|
|
||||||
@@ -207,12 +210,9 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
fn parse(p: &mut Parser<'t>, level: usize) -> PResult<Self> {
|
fn parse(p: &mut Parser<'t>, level: usize) -> PResult<Self> {
|
||||||
const MIN: usize = Prec::MIN;
|
const MIN: usize = Prec::MIN;
|
||||||
|
|
||||||
// TODO: in-tree doc comments
|
|
||||||
while p.next_if(TKind::Doc)?.is_ok() {}
|
|
||||||
|
|
||||||
// Prefix
|
// Prefix
|
||||||
let tok @ &Token { kind, span, .. } = p.peek()?;
|
let tok @ &Token { kind, span, .. } = p.peek()?;
|
||||||
let ((op, prec), span) = (from_prefix(tok)?, span);
|
let (op, prec) = from_prefix(tok)?;
|
||||||
no_eof(move || {
|
no_eof(move || {
|
||||||
let mut head = match op {
|
let mut head = match op {
|
||||||
// "End" is produced when an "empty" expression is syntactically required.
|
// "End" is produced when an "empty" expression is syntactically required.
|
||||||
@@ -227,11 +227,19 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
Ps::Lit => Expr::Lit(p.parse(())?),
|
Ps::Lit => Expr::Lit(p.parse(())?),
|
||||||
Ps::Use => Expr::Use(p.consume().parse(())?),
|
Ps::Use => Expr::Use(p.consume().parse(())?),
|
||||||
Ps::Def => Expr::Bind(p.parse(None)?),
|
Ps::Def => Expr::Bind(p.parse(None)?),
|
||||||
|
Ps::Doc => {
|
||||||
|
let comment = Literal::Str(p.take_lexeme()?.string().unwrap());
|
||||||
|
let comment = Expr::Lit(comment).anno(span);
|
||||||
|
let next = p.parse(level)?;
|
||||||
|
Expr::Op(Op::Meta, vec![comment, next])
|
||||||
|
}
|
||||||
|
Ps::For => parse_for(p, ())?,
|
||||||
Ps::Lambda | Ps::Lambda0 => {
|
Ps::Lambda | Ps::Lambda0 => {
|
||||||
p.consume();
|
p.consume();
|
||||||
|
|
||||||
let args = if op == Ps::Lambda {
|
let args = if op == Ps::Lambda {
|
||||||
p.opt(PPrec::Tuple, TKind::Bar)?
|
p.opt(PPrec::Tuple, TKind::Bar)?
|
||||||
|
.map(Pat::to_tuple)
|
||||||
.unwrap_or(Pat::Op(PatOp::Tuple, vec![]))
|
.unwrap_or(Pat::Op(PatOp::Tuple, vec![]))
|
||||||
} else {
|
} else {
|
||||||
Pat::Op(PatOp::Tuple, vec![])
|
Pat::Op(PatOp::Tuple, vec![])
|
||||||
@@ -246,7 +254,6 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
vec![p.parse(Prec::Body.next())?],
|
vec![p.parse(Prec::Body.next())?],
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Ps::For => parse_for(p, ())?,
|
|
||||||
Ps::Op(Op::Match) => parse_match(p)?,
|
Ps::Op(Op::Match) => parse_match(p)?,
|
||||||
Ps::Op(Op::Meta) => Expr::Op(
|
Ps::Op(Op::Meta) => Expr::Op(
|
||||||
Op::Meta,
|
Op::Meta,
|
||||||
@@ -260,10 +267,10 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
),
|
),
|
||||||
Ps::Op(Op::Block) => Expr::Op(
|
Ps::Op(Op::Block) => Expr::Op(
|
||||||
Op::Block,
|
Op::Block,
|
||||||
p.consume().opt(MIN, TKind::RCurly)?.into_iter().collect(),
|
p.consume().opt(MIN, kind.flip())?.into_iter().collect(),
|
||||||
),
|
),
|
||||||
Ps::Op(Op::Array) => parse_array(p)?,
|
Ps::Op(Op::Array) => parse_array(p)?,
|
||||||
Ps::Op(Op::Group) => match p.consume().opt(MIN, TKind::RParen)? {
|
Ps::Op(Op::Group) => match p.consume().opt(MIN, kind.flip())? {
|
||||||
Some(value) => Expr::Op(Op::Group, vec![value]),
|
Some(value) => Expr::Op(Op::Group, vec![value]),
|
||||||
None => Expr::Op(Op::Tuple, vec![]),
|
None => Expr::Op(Op::Tuple, vec![]),
|
||||||
},
|
},
|
||||||
@@ -295,12 +302,11 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Infix and Postfix
|
// Infix and Postfix
|
||||||
while let Ok(Some(tok)) = p.peek().allow_eof()
|
while let Ok(Some(tok @ &Token { kind, .. })) = p.peek().allow_eof()
|
||||||
&& let Ok((op, prec)) = from_infix(tok)
|
&& let Ok((op, prec)) = from_infix(tok)
|
||||||
&& level <= prec.prev()
|
&& level <= prec.prev()
|
||||||
&& op != Ps::End
|
&& op != Ps::End
|
||||||
{
|
{
|
||||||
let kind = tok.kind;
|
|
||||||
let span = span.merge(p.span());
|
let span = span.merge(p.span());
|
||||||
|
|
||||||
head = match op {
|
head = match op {
|
||||||
@@ -320,7 +326,7 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
span,
|
span,
|
||||||
match p.consume().peek().allow_eof()? {
|
match p.consume().peek().allow_eof()? {
|
||||||
Some(_) => p.parse(prec.next())?,
|
Some(_) => p.parse(prec.next())?,
|
||||||
None => Anno(Default::default(), span),
|
None => Anno(Expr::Omitted, span),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Ps::Op(Op::Index) => Expr::Op(
|
Ps::Op(Op::Index) => Expr::Op(
|
||||||
@@ -328,15 +334,14 @@ impl<'t> Parse<'t> for Expr {
|
|||||||
p.consume()
|
p.consume()
|
||||||
.list(vec![head.anno(span)], 0, TKind::Comma, TKind::RBrack)?,
|
.list(vec![head.anno(span)], 0, TKind::Comma, TKind::RBrack)?,
|
||||||
),
|
),
|
||||||
Ps::Op(Op::Call) => Expr::Op(
|
Ps::Op(Op::Call) => {
|
||||||
Op::Call,
|
let head = head.anno(span);
|
||||||
vec![
|
let args = match p.consume().opt::<Anno<Expr>>(0, TKind::RParen)? {
|
||||||
head.anno(span),
|
None => Expr::Op(Op::Tuple, vec![]).anno(span),
|
||||||
p.consume()
|
Some(Anno(expr, span)) => expr.to_tuple(span).anno(span),
|
||||||
.opt(0, TKind::RParen)?
|
};
|
||||||
.unwrap_or_else(|| Expr::Op(Op::Tuple, vec![]).anno(span)),
|
Expr::Op(Op::Call, vec![head, args])
|
||||||
],
|
}
|
||||||
),
|
|
||||||
Ps::Op(op @ (Op::Tuple | Op::Dot | Op::LogAnd | Op::LogOr)) => Expr::Op(
|
Ps::Op(op @ (Op::Tuple | Op::Dot | Op::LogAnd | Op::LogOr)) => Expr::Op(
|
||||||
op,
|
op,
|
||||||
p.consume()
|
p.consume()
|
||||||
@@ -445,7 +450,7 @@ fn from_bind(p: &mut Parser<'_>) -> PResult<(BindOp, PPrec, Option<TKind>, Optio
|
|||||||
TKind::Enum => (BindOp::Enum, PPrec::Tuple, None, None, None),
|
TKind::Enum => (BindOp::Enum, PPrec::Tuple, None, None, None),
|
||||||
TKind::Fn => (BindOp::Fn, PPrec::Fn, None, Some(Prec::Body), None),
|
TKind::Fn => (BindOp::Fn, PPrec::Fn, None, Some(Prec::Body), None),
|
||||||
TKind::Mod => (BindOp::Mod, PPrec::Max, None, Some(Prec::Body), None),
|
TKind::Mod => (BindOp::Mod, PPrec::Max, None, Some(Prec::Body), None),
|
||||||
TKind::Impl => (BindOp::Impl, PPrec::Max, None, Some(Prec::Body), None),
|
TKind::Impl => (BindOp::Impl, PPrec::Fn, None, Some(Prec::Body), None),
|
||||||
TKind::Bar => (BindOp::Match, PPrec::Alt, Some(TKind::FatArrow), Some(Prec::Body), None),
|
TKind::Bar => (BindOp::Match, PPrec::Alt, Some(TKind::FatArrow), Some(Prec::Body), None),
|
||||||
// no consume!
|
// no consume!
|
||||||
_ => return Ok((BindOp::Match, PPrec::Alt, Some(TKind::FatArrow), Some(Prec::Body), None)),
|
_ => return Ok((BindOp::Match, PPrec::Alt, Some(TKind::FatArrow), Some(Prec::Body), None)),
|
||||||
@@ -481,12 +486,15 @@ impl<'t> Parse<'t> for Bind {
|
|||||||
return Ok(Self(level, generics, pat, vec![]));
|
return Ok(Self(level, generics, pat, vec![]));
|
||||||
};
|
};
|
||||||
|
|
||||||
// `=>` for match, `=`? for everything else
|
// `=>` for match, `=` for `let`, `type`
|
||||||
if let Some(arrow) = arrow
|
if let Some(arrow) = arrow {
|
||||||
&& p.next_if(arrow).allow_eof()?.is_none_or(|v| v.is_err())
|
if p.next_if(arrow).allow_eof()?.is_none_or(|v| v.is_err()) {
|
||||||
{
|
|
||||||
return Ok(Self(level, generics, pat, vec![]));
|
return Ok(Self(level, generics, pat, vec![]));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Allow prefix `=`? for the rest of them
|
||||||
|
p.next_if(TKind::Eq).allow_eof()?;
|
||||||
|
}
|
||||||
|
|
||||||
// `=` Expr
|
// `=` Expr
|
||||||
let body = p.parse(bodyp.value())?;
|
let body = p.parse(bodyp.value())?;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{PResult, PResultExt, Parse, ParseError, Parser};
|
use super::{PResult, PResultExt, Parse, ParseError, Parser, expr::Prec as ExPrec};
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::*,
|
ast::*,
|
||||||
token::{TKind, Token},
|
token::{TKind, Token},
|
||||||
@@ -8,9 +8,10 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// Lower (toward [Prec::Min]) precedence levels can contain
|
/// Lower (toward [Prec::Min]) precedence levels can contain
|
||||||
/// all higher (toward [Prec::Max]) precedence levels.
|
/// all higher (toward [Prec::Max]) precedence levels.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Prec {
|
pub enum Prec {
|
||||||
/// The lowest precedence
|
/// The lowest precedence
|
||||||
|
#[default]
|
||||||
Min,
|
Min,
|
||||||
/// "Alternate" pattern: `Pat | Pat`
|
/// "Alternate" pattern: `Pat | Pat`
|
||||||
Alt,
|
Alt,
|
||||||
@@ -18,7 +19,7 @@ pub enum Prec {
|
|||||||
Tuple,
|
Tuple,
|
||||||
/// Type annotation: `Pat : Pat`
|
/// Type annotation: `Pat : Pat`
|
||||||
Typed,
|
Typed,
|
||||||
/// Function pattern: `Pat -> Pat`
|
/// Function signature: `foo(bar: baz, ..) -> qux`
|
||||||
Fn,
|
Fn,
|
||||||
/// Range pattern: `Pat .. Pat`, `Pat ..= Pat`
|
/// Range pattern: `Pat .. Pat`, `Pat ..= Pat`
|
||||||
Range,
|
Range,
|
||||||
@@ -51,8 +52,52 @@ impl Prec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Prefix {
|
||||||
|
/// Consume (and disregard) this prefix token
|
||||||
|
Consume,
|
||||||
|
Underscore,
|
||||||
|
Never,
|
||||||
|
MetId,
|
||||||
|
Id,
|
||||||
|
Array,
|
||||||
|
Constant,
|
||||||
|
Op(PatOp),
|
||||||
|
Split(PatOp),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_prefix(token: &Token) -> PResult<(Prefix, Prec)> {
|
||||||
|
Ok(match token.kind {
|
||||||
|
TKind::True
|
||||||
|
| TKind::False
|
||||||
|
| TKind::Character
|
||||||
|
| TKind::Integer
|
||||||
|
| TKind::String
|
||||||
|
| TKind::Minus
|
||||||
|
| TKind::Const => (Prefix::Constant, Prec::Max),
|
||||||
|
TKind::Identifier if token.lexeme.str() == Some("_") => (Prefix::Underscore, Prec::Max),
|
||||||
|
TKind::ColonColon | TKind::Identifier => (Prefix::Id, Prec::Max),
|
||||||
|
TKind::Bang => (Prefix::Never, Prec::Max),
|
||||||
|
TKind::Amp => (Prefix::Op(PatOp::Ref), Prec::Max),
|
||||||
|
TKind::AmpAmp => (Prefix::Split(PatOp::Ref), Prec::Max),
|
||||||
|
TKind::Star => (Prefix::Op(PatOp::Ptr), Prec::Max),
|
||||||
|
TKind::Mut => (Prefix::Op(PatOp::Mut), Prec::Max),
|
||||||
|
TKind::Pub => (Prefix::Op(PatOp::Pub), Prec::Max),
|
||||||
|
TKind::Grave => (Prefix::MetId, Prec::Max),
|
||||||
|
|
||||||
|
TKind::Fn => (Prefix::Op(PatOp::Fn), Prec::Fn),
|
||||||
|
TKind::Bar => (Prefix::Consume, Prec::Alt),
|
||||||
|
TKind::DotDot => (Prefix::Op(PatOp::Rest), Prec::Max),
|
||||||
|
TKind::DotDotEq => (Prefix::Op(PatOp::RangeIn), Prec::Max),
|
||||||
|
TKind::LCurly => (Prefix::Op(PatOp::Record), Prec::Typed),
|
||||||
|
TKind::LParen => (Prefix::Op(PatOp::Tuple), Prec::Fn),
|
||||||
|
TKind::LBrack => (Prefix::Array, Prec::Max),
|
||||||
|
kind => Err(ParseError::NotPrefix(kind, token.span))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to map the incoming Token to a [pattern operator](PatOp)
|
/// Tries to map the incoming Token to a [pattern operator](PatOp)
|
||||||
/// and its [precedence level](Prec)
|
/// and its following [precedence level](Prec)
|
||||||
fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
|
fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
|
||||||
Some(match token.kind {
|
Some(match token.kind {
|
||||||
TKind::Arrow => (PatOp::Fn, Prec::Fn),
|
TKind::Arrow => (PatOp::Fn, Prec::Fn),
|
||||||
@@ -61,6 +106,10 @@ fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
|
|||||||
TKind::Comma => (PatOp::Tuple, Prec::Tuple),
|
TKind::Comma => (PatOp::Tuple, Prec::Tuple),
|
||||||
TKind::DotDot => (PatOp::RangeEx, Prec::Range),
|
TKind::DotDot => (PatOp::RangeEx, Prec::Range),
|
||||||
TKind::DotDotEq => (PatOp::RangeIn, Prec::Range),
|
TKind::DotDotEq => (PatOp::RangeIn, Prec::Range),
|
||||||
|
TKind::Lt => (PatOp::Generic, Prec::Fn),
|
||||||
|
TKind::LCurly => (PatOp::TypePrefixed, Prec::Typed),
|
||||||
|
// LParen is used in function signatures
|
||||||
|
TKind::LParen => (PatOp::TypePrefixed, Prec::Fn),
|
||||||
_ => None?,
|
_ => None?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -69,99 +118,70 @@ impl<'t> Parse<'t> for Pat {
|
|||||||
type Prec = Prec;
|
type Prec = Prec;
|
||||||
|
|
||||||
fn parse(p: &mut Parser<'t>, level: Prec) -> PResult<Self> {
|
fn parse(p: &mut Parser<'t>, level: Prec) -> PResult<Self> {
|
||||||
let tok = p.peek()?;
|
let tok @ &Token { kind, span, .. } = p.peek()?;
|
||||||
|
let (op, prec) = from_prefix(tok)?;
|
||||||
|
if level > prec {
|
||||||
|
return Err(ParseError::NotPattern(kind, level, span));
|
||||||
|
}
|
||||||
|
|
||||||
// Prefix
|
let mut head = match op {
|
||||||
let mut head = match tok.kind {
|
Prefix::Consume => p.consume().parse(level)?,
|
||||||
TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => {
|
Prefix::Underscore => p.consume().then(Pat::Ignore),
|
||||||
Pat::Lit(p.parse(())?)
|
Prefix::Never => p.consume().then(Pat::Never),
|
||||||
}
|
Prefix::MetId => Pat::MetId(p.consume().next()?.lexeme.to_string()),
|
||||||
TKind::Bar => p.consume().parse(level)?,
|
Prefix::Constant => Pat::Value(p.parse(ExPrec::Unary.value())?),
|
||||||
TKind::Bang => p.consume().then(Pat::Never),
|
Prefix::Array => parse_array_pat(p)?,
|
||||||
TKind::Fn => Pat::Op(PatOp::Fn, vec![p.consume().parse(Prec::Typed)?]),
|
Prefix::Id => {
|
||||||
TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?]),
|
let Anno(mut path, span): Anno<Path> = p.parse(())?;
|
||||||
TKind::Star => Pat::Op(PatOp::Ptr, vec![p.consume().parse(Prec::Max)?]),
|
|
||||||
TKind::Mut => Pat::Op(PatOp::Mut, vec![p.consume().parse(Prec::Max)?]),
|
|
||||||
TKind::Pub => Pat::Op(PatOp::Pub, vec![p.consume().parse(Prec::Max)?]),
|
|
||||||
TKind::AmpAmp => Pat::Op(
|
|
||||||
PatOp::Ref,
|
|
||||||
vec![Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?])],
|
|
||||||
),
|
|
||||||
TKind::Identifier => match tok.lexeme.str() {
|
|
||||||
Some("_") => p.consume().then(Pat::Ignore),
|
|
||||||
_ => {
|
|
||||||
let mut path: Path = p.parse(())?;
|
|
||||||
// TODO: make these postfix.
|
// TODO: make these postfix.
|
||||||
match p.peek().map(Token::kind) {
|
match path.parts.len() {
|
||||||
Ok(TKind::LParen) => Pat::NamedTuple(path, p.parse(Prec::Typed)?),
|
1 => Pat::Name(path.parts.pop().expect("name has 1 part")),
|
||||||
Ok(TKind::LCurly) if level <= Prec::Tuple.next() => Pat::NamedRecord(
|
_ => Pat::Value(Box::new(Anno(Expr::Id(path), span))),
|
||||||
path,
|
|
||||||
p.consume()
|
|
||||||
.opt(Prec::Tuple, TKind::RCurly)?
|
|
||||||
.unwrap_or_else(|| Box::new(Pat::Op(PatOp::Tuple, vec![]))),
|
|
||||||
),
|
|
||||||
Ok(_) | Err(ParseError::EOF(_)) => match path.parts.len() {
|
|
||||||
1 => Self::Name(path.parts.pop().expect("name has 1 part")),
|
|
||||||
_ => Self::Path(path),
|
|
||||||
},
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
Prefix::Op(op @ (PatOp::Record | PatOp::Tuple)) => Pat::Op(
|
||||||
TKind::Grave => Pat::MetId(p.consume().next()?.lexeme.to_string()),
|
op,
|
||||||
TKind::DotDot => Pat::Op(
|
|
||||||
PatOp::Rest,
|
|
||||||
// Identifier in Rest position always becomes binder
|
|
||||||
match p.consume().peek().allow_eof()?.map(Token::kind) {
|
|
||||||
Some(TKind::Identifier) => vec![Pat::Name(
|
|
||||||
p.take_lexeme()
|
|
||||||
.expect("should have lexeme")
|
|
||||||
.string()
|
|
||||||
.expect("should be string"),
|
|
||||||
)],
|
|
||||||
Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?],
|
|
||||||
_ => vec![],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TKind::DotDotEq => Pat::Op(
|
|
||||||
PatOp::RangeIn,
|
|
||||||
match p.consume().peek().allow_eof()?.map(Token::kind) {
|
|
||||||
Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?],
|
|
||||||
_ => vec![],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TKind::LCurly => Pat::Op(
|
|
||||||
PatOp::Record,
|
|
||||||
p.consume()
|
p.consume()
|
||||||
.list(vec![], Prec::Typed, TKind::Comma, TKind::RCurly)?,
|
.list(vec![], Prec::Tuple.next(), TKind::Comma, kind.flip())?,
|
||||||
),
|
),
|
||||||
TKind::LParen => Pat::Op(
|
Prefix::Op(op @ (PatOp::Rest | PatOp::RangeEx | PatOp::RangeIn)) => {
|
||||||
PatOp::Tuple,
|
// next token must continue a pattern
|
||||||
p.consume()
|
match p.consume().peek().allow_eof()? {
|
||||||
.list(vec![], Prec::Typed, TKind::Comma, TKind::RParen)?,
|
Some(tok) if from_prefix(tok).is_ok() => Pat::Op(op, vec![p.parse(prec)?]),
|
||||||
),
|
_ => Pat::Op(op, vec![]),
|
||||||
TKind::LBrack => parse_array_pat(p)?,
|
}
|
||||||
_ => Err(ParseError::NotPattern(tok.kind, tok.span))?,
|
}
|
||||||
|
Prefix::Op(op) => Pat::Op(op, vec![p.consume().parse(prec)?]),
|
||||||
|
Prefix::Split(op) => {
|
||||||
|
p.split()?;
|
||||||
|
Pat::Op(op, vec![p.parse(prec)?])
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
while let Ok(Some(tok)) = p.peek().allow_eof()
|
while let Ok(Some(tok @ &Token { kind, .. })) = p.peek().allow_eof()
|
||||||
&& let Some((op, prec)) = from_infix(tok)
|
&& let Some((op, prec)) = from_infix(tok)
|
||||||
&& level <= prec
|
&& level <= prec
|
||||||
{
|
{
|
||||||
let kind = tok.kind;
|
|
||||||
head = match op {
|
head = match op {
|
||||||
PatOp::Typed => Pat::Op(op, vec![head, p.consume().parse(prec.next())?]),
|
|
||||||
PatOp::Fn => Pat::Op(op, vec![head, p.consume().parse(Prec::Fn)?]),
|
|
||||||
PatOp::RangeEx | PatOp::RangeIn => Pat::Op(
|
PatOp::RangeEx | PatOp::RangeIn => Pat::Op(
|
||||||
op,
|
op,
|
||||||
match p.consume().peek().map(Token::kind) {
|
if let Some(tok) = p.consume().peek().allow_eof()?
|
||||||
Ok(TKind::Integer | TKind::Character | TKind::Identifier) => {
|
&& from_prefix(tok).is_ok()
|
||||||
vec![head, p.parse(prec.next())?]
|
{
|
||||||
}
|
vec![head, p.parse(prec)?]
|
||||||
_ => vec![head],
|
} else {
|
||||||
|
vec![head]
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
_ => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?),
|
PatOp::Generic => Pat::Op(
|
||||||
|
op,
|
||||||
|
p.consume()
|
||||||
|
.list(vec![head], prec, TKind::Comma, kind.flip())?,
|
||||||
|
),
|
||||||
|
PatOp::TypePrefixed => Pat::Op(op, vec![head, p.parse(prec)?]),
|
||||||
|
PatOp::Tuple => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?),
|
||||||
|
PatOp::Fn => Pat::Op(op, vec![head, p.consume().parse(prec)?]),
|
||||||
|
_ => Pat::Op(op, vec![head, p.consume().parse(prec.next())?]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(head)
|
Ok(head)
|
||||||
|
|||||||
224
src/token.rs
224
src/token.rs
@@ -66,8 +66,12 @@ impl std::fmt::Display for Lexeme {
|
|||||||
/// The lexical classification of a [Token].
|
/// The lexical classification of a [Token].
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum TKind {
|
pub enum TKind {
|
||||||
Comment, // Line or block comment
|
/// Line or block comment
|
||||||
Doc, // Doc comment
|
Comment,
|
||||||
|
/// Outer doc comment ///.*
|
||||||
|
OutDoc,
|
||||||
|
/// Inner doc comment: //!.*
|
||||||
|
InDoc,
|
||||||
|
|
||||||
As,
|
As,
|
||||||
Break,
|
Break,
|
||||||
@@ -100,59 +104,165 @@ pub enum TKind {
|
|||||||
Identifier, // or Keyword
|
Identifier, // or Keyword
|
||||||
Character,
|
Character,
|
||||||
String,
|
String,
|
||||||
Integer, // 0(x[0-9A-Fa-f]* | d[0-9]* | o[0-7]* | b[0-1]*) | [1-9][0-9]*
|
/// `0(x[0-9A-Fa-f]* | d[0-9]* | o[0-7]* | b[0-1]*) | [1-9][0-9]*`
|
||||||
LCurly, // {
|
Integer,
|
||||||
RCurly, // }
|
/// {
|
||||||
LBrack, // [
|
LCurly,
|
||||||
RBrack, // ]
|
/// }
|
||||||
LParen, // (
|
RCurly,
|
||||||
RParen, // )
|
/// [
|
||||||
Amp, // &
|
LBrack,
|
||||||
AmpAmp, // &&
|
/// ]
|
||||||
AmpEq, // &=
|
RBrack,
|
||||||
Arrow, // ->
|
/// (
|
||||||
At, // @
|
LParen,
|
||||||
Backslash, // \
|
/// )
|
||||||
Bang, // !
|
RParen,
|
||||||
BangBang, // !!
|
/// &
|
||||||
BangEq, // !=
|
Amp,
|
||||||
Bar, // |
|
/// &&
|
||||||
BarBar, // ||
|
AmpAmp,
|
||||||
BarEq, // |=
|
/// &=
|
||||||
Colon, // :
|
AmpEq,
|
||||||
ColonColon, // ::
|
/// ->
|
||||||
Comma, // ,
|
Arrow,
|
||||||
Dot, // .
|
/// @
|
||||||
DotDot, // ..
|
At,
|
||||||
DotDotEq, // ..=
|
/// \
|
||||||
Eq, // =
|
Backslash,
|
||||||
EqEq, // ==
|
/// !
|
||||||
FatArrow, // =>
|
Bang,
|
||||||
Grave, // `
|
/// !!
|
||||||
Gt, // >
|
BangBang,
|
||||||
GtEq, // >=
|
/// !=
|
||||||
GtGt, // >>
|
BangEq,
|
||||||
GtGtEq, // >>=
|
/// |
|
||||||
Hash, // #
|
Bar,
|
||||||
HashBang, // #!
|
/// ||
|
||||||
Lt, // <
|
BarBar,
|
||||||
LtEq, // <=
|
/// |=
|
||||||
LtLt, // <<
|
BarEq,
|
||||||
LtLtEq, // <<=
|
/// :
|
||||||
Minus, // -
|
Colon,
|
||||||
MinusEq, // -=
|
/// ::
|
||||||
Plus, // +
|
ColonColon,
|
||||||
PlusEq, // +=
|
/// ,
|
||||||
Question, // ?
|
Comma,
|
||||||
Rem, // %
|
/// .
|
||||||
RemEq, // %=
|
Dot,
|
||||||
Semi, // ;
|
/// ..
|
||||||
Slash, // /
|
DotDot,
|
||||||
SlashEq, // /=
|
/// ..=
|
||||||
Star, // *
|
DotDotEq,
|
||||||
StarEq, // *=
|
/// =
|
||||||
Tilde, // ~
|
Eq,
|
||||||
Xor, // ^
|
/// ==
|
||||||
XorEq, // ^=
|
EqEq,
|
||||||
XorXor, // ^^
|
/// =>
|
||||||
|
FatArrow,
|
||||||
|
/// \`
|
||||||
|
Grave,
|
||||||
|
/// >
|
||||||
|
Gt,
|
||||||
|
/// >=
|
||||||
|
GtEq,
|
||||||
|
/// >>
|
||||||
|
GtGt,
|
||||||
|
/// >>=
|
||||||
|
GtGtEq,
|
||||||
|
/// #
|
||||||
|
Hash,
|
||||||
|
/// #!
|
||||||
|
HashBang,
|
||||||
|
/// <
|
||||||
|
Lt,
|
||||||
|
/// <=
|
||||||
|
LtEq,
|
||||||
|
/// <<
|
||||||
|
LtLt,
|
||||||
|
/// <<=
|
||||||
|
LtLtEq,
|
||||||
|
/// -
|
||||||
|
Minus,
|
||||||
|
/// -=
|
||||||
|
MinusEq,
|
||||||
|
/// +
|
||||||
|
Plus,
|
||||||
|
/// +=
|
||||||
|
PlusEq,
|
||||||
|
/// ?
|
||||||
|
Question,
|
||||||
|
/// %
|
||||||
|
Rem,
|
||||||
|
/// %=
|
||||||
|
RemEq,
|
||||||
|
/// ;
|
||||||
|
Semi,
|
||||||
|
/// /
|
||||||
|
Slash,
|
||||||
|
/// /=
|
||||||
|
SlashEq,
|
||||||
|
/// *
|
||||||
|
Star,
|
||||||
|
/// *=
|
||||||
|
StarEq,
|
||||||
|
/// ~
|
||||||
|
Tilde,
|
||||||
|
/// ^
|
||||||
|
Xor,
|
||||||
|
/// ^=
|
||||||
|
XorEq,
|
||||||
|
/// ^^
|
||||||
|
XorXor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TKind {
|
||||||
|
pub const fn flip(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::LCurly => Self::RCurly,
|
||||||
|
Self::RCurly => Self::LCurly,
|
||||||
|
Self::LBrack => Self::RBrack,
|
||||||
|
Self::RBrack => Self::LBrack,
|
||||||
|
Self::LParen => Self::RParen,
|
||||||
|
Self::RParen => Self::LParen,
|
||||||
|
Self::Gt => Self::Lt,
|
||||||
|
Self::GtGt => Self::LtLt,
|
||||||
|
Self::LtLt => Self::GtGt,
|
||||||
|
Self::Lt => Self::Gt,
|
||||||
|
_ => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits a single [TKind] into two, if possible, or returns the original.
|
||||||
|
pub const fn split(self) -> Result<(Self, Self), Self> {
|
||||||
|
Ok(match self {
|
||||||
|
Self::AmpAmp => (Self::Amp, Self::Amp),
|
||||||
|
Self::AmpEq => (Self::Amp, Self::Eq),
|
||||||
|
Self::Arrow => (Self::Minus, Self::Gt),
|
||||||
|
Self::BangBang => (Self::Bang, Self::Bang),
|
||||||
|
Self::BangEq => (Self::Bang, Self::Eq),
|
||||||
|
Self::BarBar => (Self::Bar, Self::Bar),
|
||||||
|
Self::BarEq => (Self::Bar, Self::Eq),
|
||||||
|
Self::ColonColon => (Self::Colon, Self::Colon),
|
||||||
|
Self::DotDot => (Self::Dot, Self::Dot),
|
||||||
|
Self::DotDotEq => (Self::DotDot, Self::Eq),
|
||||||
|
Self::EqEq => (Self::Eq, Self::Eq),
|
||||||
|
Self::FatArrow => (Self::Eq, Self::Gt),
|
||||||
|
Self::GtEq => (Self::Gt, Self::Eq),
|
||||||
|
Self::GtGt => (Self::Gt, Self::Gt),
|
||||||
|
Self::GtGtEq => (Self::Gt, Self::GtEq),
|
||||||
|
Self::HashBang => (Self::Hash, Self::Bang),
|
||||||
|
Self::LtEq => (Self::Lt, Self::Eq),
|
||||||
|
Self::LtLt => (Self::Lt, Self::Lt),
|
||||||
|
Self::LtLtEq => (Self::Lt, Self::LtEq),
|
||||||
|
Self::MinusEq => (Self::Minus, Self::Eq),
|
||||||
|
Self::PlusEq => (Self::Plus, Self::Eq),
|
||||||
|
Self::RemEq => (Self::Rem, Self::Eq),
|
||||||
|
Self::SlashEq => (Self::Slash, Self::Eq),
|
||||||
|
Self::StarEq => (Self::Star, Self::Eq),
|
||||||
|
Self::XorEq => (Self::Xor, Self::Eq),
|
||||||
|
Self::XorXor => (Self::Xor, Self::Xor),
|
||||||
|
_ => return Err(self),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user