Implement parser & type-checker for tuple indexes.
```aiken
fn foo() {
let tuple = #(1, 2, 3, 4)
tuple.1st + tuple.2nd + tuple.3rd + tuple.4th
}
```
This commit is contained in:
@@ -24,6 +24,17 @@ impl ParseError {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn invalid_tuple_index(span: Span, index: u32, suffix: Option<String>) -> Self {
|
||||
let hint = suffix.map(|suffix| format!("{index}{suffix}"));
|
||||
Self {
|
||||
kind: ErrorKind::InvalidTupleIndex { hint },
|
||||
span,
|
||||
while_parsing: None,
|
||||
expected: HashSet::new(),
|
||||
label: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ParseError {
|
||||
@@ -69,20 +80,22 @@ impl<T: Into<Pattern>> chumsky::Error<T> for ParseError {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Diagnostic, thiserror::Error)]
|
||||
pub enum ErrorKind {
|
||||
#[error("unexpected end")]
|
||||
#[error("Unexpected end")]
|
||||
UnexpectedEnd,
|
||||
#[error("{0}")]
|
||||
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
|
||||
Unexpected(Pattern),
|
||||
#[error("unclosed {start}")]
|
||||
#[error("Unclosed {start}")]
|
||||
Unclosed {
|
||||
start: Pattern,
|
||||
#[label]
|
||||
before_span: Span,
|
||||
before: Option<Pattern>,
|
||||
},
|
||||
#[error("no end branch")]
|
||||
#[error("No end branch")]
|
||||
NoEndBranch,
|
||||
#[error("Invalid tuple index{}", hint.as_ref().map(|s| format!("; did you mean '{s}' ?")).unwrap_or_default())]
|
||||
InvalidTupleIndex { hint: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]
|
||||
|
||||
@@ -2,6 +2,8 @@ use chumsky::prelude::*;
|
||||
|
||||
use crate::ast::Span;
|
||||
|
||||
use ordinal::Ordinal;
|
||||
|
||||
use super::{error::ParseError, token::Token};
|
||||
|
||||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||
@@ -17,6 +19,25 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||
))
|
||||
.map(|value| Token::Int { value });
|
||||
|
||||
let ordinal = text::int(10)
|
||||
.from_str()
|
||||
.unwrapped()
|
||||
.then_with(|index: u32| {
|
||||
choice((just("st"), just("nd"), just("rd"), just("th")))
|
||||
.map(move |suffix| (index, suffix))
|
||||
})
|
||||
.validate(|(index, suffix), span, emit| {
|
||||
let expected_suffix = Ordinal(index).suffix();
|
||||
if expected_suffix != suffix {
|
||||
emit(ParseError::invalid_tuple_index(
|
||||
span,
|
||||
index,
|
||||
Some(expected_suffix.to_string()),
|
||||
))
|
||||
}
|
||||
Token::Ordinal { index }
|
||||
});
|
||||
|
||||
let op = choice((
|
||||
just("==").to(Token::EqualEqual),
|
||||
just('=').to(Token::Equal),
|
||||
@@ -132,7 +153,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||
module_comments,
|
||||
doc_comments,
|
||||
comments,
|
||||
choice((keyword, int, op, grouping, string))
|
||||
choice((ordinal, keyword, int, op, grouping, string))
|
||||
.or(any().map(Token::Error).validate(|t, span, emit| {
|
||||
emit(ParseError::expected_input_found(
|
||||
span,
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::fmt;
|
||||
pub enum Token {
|
||||
Error(char),
|
||||
Name { name: String },
|
||||
Ordinal { index: u32 },
|
||||
UpName { name: String },
|
||||
DiscardName { name: String },
|
||||
Int { value: String },
|
||||
@@ -78,12 +79,17 @@ pub enum Token {
|
||||
|
||||
impl fmt::Display for Token {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let index_str;
|
||||
let s = match self {
|
||||
Token::Error(c) => {
|
||||
write!(f, "\"{}\"", c)?;
|
||||
return Ok(());
|
||||
}
|
||||
Token::Name { name } => name,
|
||||
Token::Ordinal { index } => {
|
||||
index_str = index.to_string();
|
||||
&index_str[..]
|
||||
}
|
||||
Token::UpName { name } => name,
|
||||
Token::DiscardName { name } => name,
|
||||
Token::Int { value } => value,
|
||||
|
||||
Reference in New Issue
Block a user