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:
parent
7867793bcd
commit
bf7cdfba73
|
@ -79,6 +79,7 @@ dependencies = [
|
||||||
"indoc",
|
"indoc",
|
||||||
"itertools",
|
"itertools",
|
||||||
"miette",
|
"miette",
|
||||||
|
"ordinal",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"strum",
|
"strum",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1125,6 +1126,16 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1204,6 +1215,15 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordinal"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c80c1530f46e9d8985706d7deb80b83172b250538902f607dea6cd6028851083"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.3.0"
|
version = "6.3.0"
|
||||||
|
|
|
@ -15,6 +15,7 @@ chumsky = "0.8.0"
|
||||||
indexmap = "1.9.1"
|
indexmap = "1.9.1"
|
||||||
itertools = "0.10.5"
|
itertools = "0.10.5"
|
||||||
miette = "5.2.0"
|
miette = "5.2.0"
|
||||||
|
ordinal = "0.3.2"
|
||||||
strum = "0.24.1"
|
strum = "0.24.1"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
uplc = { path = '../uplc', version = "0.0.25" }
|
uplc = { path = '../uplc', version = "0.0.25" }
|
||||||
|
|
|
@ -191,11 +191,10 @@ pub enum Air {
|
||||||
},
|
},
|
||||||
|
|
||||||
// TupleIndex {
|
// TupleIndex {
|
||||||
// scope: Vec<u64>,
|
// scope: Vec<u64>,
|
||||||
//
|
// tipo: Arc<Type>,
|
||||||
// tipo: Arc<Type>,
|
// index: u64,
|
||||||
// index: u64,
|
// tuple: Box<Self>,
|
||||||
// tuple: Box<Self>,
|
|
||||||
// },
|
// },
|
||||||
Todo {
|
Todo {
|
||||||
scope: Vec<u64>,
|
scope: Vec<u64>,
|
||||||
|
|
|
@ -135,12 +135,13 @@ pub enum TypedExpr {
|
||||||
elems: Vec<Self>,
|
elems: Vec<Self>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// TupleIndex {
|
TupleIndex {
|
||||||
// location: Span,
|
location: Span,
|
||||||
// tipo: Arc<Type>,
|
tipo: Arc<Type>,
|
||||||
// index: u64,
|
index: usize,
|
||||||
// tuple: Box<Self>,
|
tuple: Box<Self>,
|
||||||
// },
|
},
|
||||||
|
|
||||||
Todo {
|
Todo {
|
||||||
location: Span,
|
location: Span,
|
||||||
label: Option<String>,
|
label: Option<String>,
|
||||||
|
@ -165,7 +166,7 @@ impl TypedExpr {
|
||||||
match self {
|
match self {
|
||||||
Self::Negate { .. } => bool(),
|
Self::Negate { .. } => bool(),
|
||||||
Self::Var { constructor, .. } => constructor.tipo.clone(),
|
Self::Var { constructor, .. } => constructor.tipo.clone(),
|
||||||
Self::Trace {then, ..} => then.tipo(),
|
Self::Trace { then, .. } => then.tipo(),
|
||||||
Self::Fn { tipo, .. }
|
Self::Fn { tipo, .. }
|
||||||
| Self::Int { tipo, .. }
|
| Self::Int { tipo, .. }
|
||||||
| Self::Todo { tipo, .. }
|
| Self::Todo { tipo, .. }
|
||||||
|
@ -177,7 +178,7 @@ impl TypedExpr {
|
||||||
| Self::Tuple { tipo, .. }
|
| Self::Tuple { tipo, .. }
|
||||||
| Self::String { tipo, .. }
|
| Self::String { tipo, .. }
|
||||||
| Self::ByteArray { tipo, .. }
|
| Self::ByteArray { tipo, .. }
|
||||||
// | Self::TupleIndex { tipo, .. }
|
| Self::TupleIndex { tipo, .. }
|
||||||
| Self::Assignment { tipo, .. }
|
| Self::Assignment { tipo, .. }
|
||||||
| Self::ModuleSelect { tipo, .. }
|
| Self::ModuleSelect { tipo, .. }
|
||||||
| Self::RecordAccess { tipo, .. }
|
| Self::RecordAccess { tipo, .. }
|
||||||
|
@ -221,9 +222,9 @@ impl TypedExpr {
|
||||||
| TypedExpr::Pipeline { .. }
|
| TypedExpr::Pipeline { .. }
|
||||||
| TypedExpr::ByteArray { .. }
|
| TypedExpr::ByteArray { .. }
|
||||||
| TypedExpr::Assignment { .. }
|
| TypedExpr::Assignment { .. }
|
||||||
// | TypedExpr::TupleIndex { .. }
|
| TypedExpr::TupleIndex { .. }
|
||||||
| TypedExpr::RecordAccess { .. } => None,
|
| TypedExpr::RecordAccess { .. } => None,
|
||||||
| TypedExpr::If { .. } => None,
|
TypedExpr::If { .. } => None,
|
||||||
|
|
||||||
// TODO: test
|
// TODO: test
|
||||||
// TODO: definition
|
// TODO: definition
|
||||||
|
@ -261,15 +262,12 @@ impl TypedExpr {
|
||||||
| Self::Pipeline { location, .. }
|
| Self::Pipeline { location, .. }
|
||||||
| Self::ByteArray { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
// | Self::TupleIndex { location, .. }
|
| Self::TupleIndex { location, .. }
|
||||||
| Self::ModuleSelect { location, .. }
|
| Self::ModuleSelect { location, .. }
|
||||||
| Self::RecordAccess { location, .. }
|
| Self::RecordAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. } => *location,
|
| Self::RecordUpdate { location, .. } => *location,
|
||||||
|
|
||||||
Self::If {
|
Self::If { branches, .. } => branches.first().body.type_defining_location(),
|
||||||
branches,
|
|
||||||
..
|
|
||||||
} => branches.first().body.type_defining_location(),
|
|
||||||
|
|
||||||
Self::Sequence {
|
Self::Sequence {
|
||||||
expressions,
|
expressions,
|
||||||
|
@ -301,7 +299,7 @@ impl TypedExpr {
|
||||||
| Self::Pipeline { location, .. }
|
| Self::Pipeline { location, .. }
|
||||||
| Self::ByteArray { location, .. }
|
| Self::ByteArray { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
// | Self::TupleIndex { location, .. }
|
| Self::TupleIndex { location, .. }
|
||||||
| Self::ModuleSelect { location, .. }
|
| Self::ModuleSelect { location, .. }
|
||||||
| Self::RecordAccess { location, .. }
|
| Self::RecordAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. } => *location,
|
| Self::RecordUpdate { location, .. } => *location,
|
||||||
|
@ -401,11 +399,13 @@ pub enum UntypedExpr {
|
||||||
location: Span,
|
location: Span,
|
||||||
elems: Vec<Self>,
|
elems: Vec<Self>,
|
||||||
},
|
},
|
||||||
// TupleIndex {
|
|
||||||
// location: Span,
|
TupleIndex {
|
||||||
// index: u64,
|
location: Span,
|
||||||
// tuple: Box<Self>,
|
index: usize,
|
||||||
// },
|
tuple: Box<Self>,
|
||||||
|
},
|
||||||
|
|
||||||
Todo {
|
Todo {
|
||||||
kind: TodoKind,
|
kind: TodoKind,
|
||||||
location: Span,
|
location: Span,
|
||||||
|
@ -490,7 +490,7 @@ impl UntypedExpr {
|
||||||
| Self::Tuple { location, .. }
|
| Self::Tuple { location, .. }
|
||||||
| Self::String { location, .. }
|
| Self::String { location, .. }
|
||||||
| Self::Assignment { location, .. }
|
| Self::Assignment { location, .. }
|
||||||
// | Self::TupleIndex { location, .. }
|
| Self::TupleIndex { location, .. }
|
||||||
| Self::FieldAccess { location, .. }
|
| Self::FieldAccess { location, .. }
|
||||||
| Self::RecordUpdate { location, .. }
|
| Self::RecordUpdate { location, .. }
|
||||||
| Self::Negate { location, .. }
|
| Self::Negate { location, .. }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use ordinal::Ordinal;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vec1::Vec1;
|
use vec1::Vec1;
|
||||||
|
|
||||||
|
@ -796,6 +797,14 @@ impl<'comments> Formatter<'comments> {
|
||||||
elems.iter().map(|e| (self.wrap_expr(e), false)),
|
elems.iter().map(|e| (self.wrap_expr(e), false)),
|
||||||
))
|
))
|
||||||
.group(),
|
.group(),
|
||||||
|
|
||||||
|
UntypedExpr::TupleIndex { index, tuple, .. } => {
|
||||||
|
let suffix = Ordinal(*index + 1).suffix().to_doc();
|
||||||
|
self.expr(tuple)
|
||||||
|
.append(".".to_doc())
|
||||||
|
.append((index + 1).to_doc())
|
||||||
|
.append(suffix)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
commented(document, comments)
|
commented(document, comments)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1089,6 +1089,7 @@ pub fn expr_parser(
|
||||||
enum Chain {
|
enum Chain {
|
||||||
Call(Vec<ParserArg>, Span),
|
Call(Vec<ParserArg>, Span),
|
||||||
FieldAccess(String, Span),
|
FieldAccess(String, Span),
|
||||||
|
TupleIndex(usize, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
let field_access_parser = just(Token::Dot)
|
let field_access_parser = just(Token::Dot)
|
||||||
|
@ -1097,6 +1098,19 @@ pub fn expr_parser(
|
||||||
})
|
})
|
||||||
.map_with_span(Chain::FieldAccess);
|
.map_with_span(Chain::FieldAccess);
|
||||||
|
|
||||||
|
let tuple_index_parser = just(Token::Dot)
|
||||||
|
.ignore_then(select! {
|
||||||
|
Token::Ordinal { index } => index,
|
||||||
|
})
|
||||||
|
.validate(|index, span, emit| {
|
||||||
|
if index < 1 {
|
||||||
|
emit(ParseError::invalid_tuple_index(span, index, None));
|
||||||
|
Chain::TupleIndex(0, span)
|
||||||
|
} else {
|
||||||
|
Chain::TupleIndex(index as usize - 1, span)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let call_parser = choice((
|
let call_parser = choice((
|
||||||
select! { Token::Name { name } => name }
|
select! { Token::Name { name } => name }
|
||||||
.then_ignore(just(Token::Colon))
|
.then_ignore(just(Token::Colon))
|
||||||
|
@ -1123,11 +1137,11 @@ pub fn expr_parser(
|
||||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||||
.map_with_span(Chain::Call);
|
.map_with_span(Chain::Call);
|
||||||
|
|
||||||
let chain = choice((field_access_parser, call_parser));
|
let chain = choice((tuple_index_parser, field_access_parser, call_parser));
|
||||||
|
|
||||||
let chained = expr_unit_parser
|
let chained = expr_unit_parser
|
||||||
.then(chain.repeated())
|
.then(chain.repeated())
|
||||||
.foldl(|e, chain| match chain {
|
.foldl(|expr, chain| match chain {
|
||||||
Chain::Call(args, span) => {
|
Chain::Call(args, span) => {
|
||||||
let mut holes = Vec::new();
|
let mut holes = Vec::new();
|
||||||
|
|
||||||
|
@ -1161,7 +1175,7 @@ pub fn expr_parser(
|
||||||
|
|
||||||
let call = expr::UntypedExpr::Call {
|
let call = expr::UntypedExpr::Call {
|
||||||
location: span,
|
location: span,
|
||||||
fun: Box::new(e),
|
fun: Box::new(expr),
|
||||||
arguments: args,
|
arguments: args,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1179,9 +1193,15 @@ pub fn expr_parser(
|
||||||
}
|
}
|
||||||
|
|
||||||
Chain::FieldAccess(label, span) => expr::UntypedExpr::FieldAccess {
|
Chain::FieldAccess(label, span) => expr::UntypedExpr::FieldAccess {
|
||||||
location: e.location().union(span),
|
location: expr.location().union(span),
|
||||||
label,
|
label,
|
||||||
container: Box::new(e),
|
container: Box::new(expr),
|
||||||
|
},
|
||||||
|
|
||||||
|
Chain::TupleIndex(index, span) => expr::UntypedExpr::TupleIndex {
|
||||||
|
location: expr.location().union(span),
|
||||||
|
index,
|
||||||
|
tuple: Box::new(expr),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,17 @@ impl ParseError {
|
||||||
}
|
}
|
||||||
self
|
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 {
|
impl PartialEq for ParseError {
|
||||||
|
@ -69,20 +80,22 @@ impl<T: Into<Pattern>> chumsky::Error<T> for ParseError {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Diagnostic, thiserror::Error)]
|
#[derive(Debug, PartialEq, Eq, Diagnostic, thiserror::Error)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
#[error("unexpected end")]
|
#[error("Unexpected end")]
|
||||||
UnexpectedEnd,
|
UnexpectedEnd,
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
|
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
|
||||||
Unexpected(Pattern),
|
Unexpected(Pattern),
|
||||||
#[error("unclosed {start}")]
|
#[error("Unclosed {start}")]
|
||||||
Unclosed {
|
Unclosed {
|
||||||
start: Pattern,
|
start: Pattern,
|
||||||
#[label]
|
#[label]
|
||||||
before_span: Span,
|
before_span: Span,
|
||||||
before: Option<Pattern>,
|
before: Option<Pattern>,
|
||||||
},
|
},
|
||||||
#[error("no end branch")]
|
#[error("No end branch")]
|
||||||
NoEndBranch,
|
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)]
|
#[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]
|
||||||
|
|
|
@ -2,6 +2,8 @@ use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::ast::Span;
|
use crate::ast::Span;
|
||||||
|
|
||||||
|
use ordinal::Ordinal;
|
||||||
|
|
||||||
use super::{error::ParseError, token::Token};
|
use super::{error::ParseError, token::Token};
|
||||||
|
|
||||||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
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 });
|
.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((
|
let op = choice((
|
||||||
just("==").to(Token::EqualEqual),
|
just("==").to(Token::EqualEqual),
|
||||||
just('=').to(Token::Equal),
|
just('=').to(Token::Equal),
|
||||||
|
@ -132,7 +153,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
module_comments,
|
module_comments,
|
||||||
doc_comments,
|
doc_comments,
|
||||||
comments,
|
comments,
|
||||||
choice((keyword, int, op, grouping, string))
|
choice((ordinal, keyword, int, op, grouping, string))
|
||||||
.or(any().map(Token::Error).validate(|t, span, emit| {
|
.or(any().map(Token::Error).validate(|t, span, emit| {
|
||||||
emit(ParseError::expected_input_found(
|
emit(ParseError::expected_input_found(
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::fmt;
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Error(char),
|
Error(char),
|
||||||
Name { name: String },
|
Name { name: String },
|
||||||
|
Ordinal { index: u32 },
|
||||||
UpName { name: String },
|
UpName { name: String },
|
||||||
DiscardName { name: String },
|
DiscardName { name: String },
|
||||||
Int { value: String },
|
Int { value: String },
|
||||||
|
@ -78,12 +79,17 @@ pub enum Token {
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
impl fmt::Display for Token {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let index_str;
|
||||||
let s = match self {
|
let s = match self {
|
||||||
Token::Error(c) => {
|
Token::Error(c) => {
|
||||||
write!(f, "\"{}\"", c)?;
|
write!(f, "\"{}\"", c)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Token::Name { name } => name,
|
Token::Name { name } => name,
|
||||||
|
Token::Ordinal { index } => {
|
||||||
|
index_str = index.to_string();
|
||||||
|
&index_str[..]
|
||||||
|
}
|
||||||
Token::UpName { name } => name,
|
Token::UpName { name } => name,
|
||||||
Token::DiscardName { name } => name,
|
Token::DiscardName { name } => name,
|
||||||
Token::Int { value } => value,
|
Token::Int { value } => value,
|
||||||
|
|
|
@ -1436,3 +1436,106 @@ fn record_create_unlabeled() {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_tuple() {
|
||||||
|
let code = indoc! {r#"
|
||||||
|
fn foo() {
|
||||||
|
let tuple = #(1, 2, 3, 4)
|
||||||
|
tuple.1st + tuple.2nd + tuple.3rd + tuple.4th
|
||||||
|
}
|
||||||
|
"#};
|
||||||
|
|
||||||
|
assert_definition(
|
||||||
|
code,
|
||||||
|
ast::UntypedDefinition::Fn(Function {
|
||||||
|
arguments: vec![],
|
||||||
|
body: expr::UntypedExpr::Sequence {
|
||||||
|
location: Span::new((), 13..86),
|
||||||
|
expressions: vec![
|
||||||
|
expr::UntypedExpr::Assignment {
|
||||||
|
location: Span::new((), 13..38),
|
||||||
|
value: Box::new(expr::UntypedExpr::Tuple {
|
||||||
|
location: Span::new((), 25..38),
|
||||||
|
elems: vec![
|
||||||
|
expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 27..28),
|
||||||
|
value: "1".to_string(),
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 30..31),
|
||||||
|
value: "2".to_string(),
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 33..34),
|
||||||
|
value: "3".to_string(),
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::Int {
|
||||||
|
location: Span::new((), 36..37),
|
||||||
|
value: "4".to_string(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
pattern: ast::Pattern::Var {
|
||||||
|
location: Span::new((), 17..22),
|
||||||
|
name: "tuple".to_string(),
|
||||||
|
},
|
||||||
|
kind: ast::AssignmentKind::Let,
|
||||||
|
annotation: None,
|
||||||
|
},
|
||||||
|
expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 41..86),
|
||||||
|
name: ast::BinOp::AddInt,
|
||||||
|
left: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 41..74),
|
||||||
|
name: ast::BinOp::AddInt,
|
||||||
|
left: Box::new(expr::UntypedExpr::BinOp {
|
||||||
|
location: Span::new((), 41..62),
|
||||||
|
name: ast::BinOp::AddInt,
|
||||||
|
left: Box::new(expr::UntypedExpr::TupleIndex {
|
||||||
|
location: Span::new((), 41..50),
|
||||||
|
index: 0,
|
||||||
|
tuple: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 41..46),
|
||||||
|
name: "tuple".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::TupleIndex {
|
||||||
|
location: Span::new((), 53..62),
|
||||||
|
index: 1,
|
||||||
|
tuple: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 53..58),
|
||||||
|
name: "tuple".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::TupleIndex {
|
||||||
|
location: Span::new((), 65..74),
|
||||||
|
index: 2,
|
||||||
|
tuple: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 65..70),
|
||||||
|
name: "tuple".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
right: Box::new(expr::UntypedExpr::TupleIndex {
|
||||||
|
location: Span::new((), 77..86),
|
||||||
|
index: 3,
|
||||||
|
tuple: Box::new(expr::UntypedExpr::Var {
|
||||||
|
location: Span::new((), 77..82),
|
||||||
|
name: "tuple".to_string(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
doc: None,
|
||||||
|
location: Span::new((), 0..8),
|
||||||
|
name: "foo".to_string(),
|
||||||
|
public: false,
|
||||||
|
return_annotation: None,
|
||||||
|
return_type: (),
|
||||||
|
end_position: 87,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use ordinal::Ordinal;
|
||||||
|
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
|
|
||||||
use crate::ast::{BinOp, Span, TodoKind};
|
use crate::ast::{BinOp, Span, TodoKind};
|
||||||
|
@ -283,6 +285,20 @@ pub enum Error {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("Trying to access tuple elements on something else than a tuple\n")]
|
||||||
|
NotATuple {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("Trying to access the {} element of a {}-tuple\n", Ordinal(*index + 1).to_string(), size)]
|
||||||
|
TupleIndexOutOfBound {
|
||||||
|
#[label]
|
||||||
|
location: Span,
|
||||||
|
index: usize,
|
||||||
|
size: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
|
|
@ -321,12 +321,13 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
..
|
..
|
||||||
} => self.infer_field_access(*container, label, location),
|
} => self.infer_field_access(*container, label, location),
|
||||||
|
|
||||||
// UntypedExpr::TupleIndex {
|
UntypedExpr::TupleIndex {
|
||||||
// location,
|
location,
|
||||||
// index,
|
index,
|
||||||
// tuple,
|
tuple,
|
||||||
// ..
|
..
|
||||||
// } => self.infer_tuple_index(*tuple, index, location),
|
} => self.infer_tuple_index(*tuple, index, location),
|
||||||
|
|
||||||
UntypedExpr::ByteArray { location, bytes } => {
|
UntypedExpr::ByteArray { location, bytes } => {
|
||||||
Ok(self.infer_byte_array(bytes, location))
|
Ok(self.infer_byte_array(bytes, location))
|
||||||
}
|
}
|
||||||
|
@ -1701,6 +1702,38 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_tuple_index(
|
||||||
|
&mut self,
|
||||||
|
tuple: UntypedExpr,
|
||||||
|
index: usize,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
let tuple = self.infer(tuple)?;
|
||||||
|
|
||||||
|
let tipo = match *tuple.tipo() {
|
||||||
|
Type::Tuple { ref elems, .. } => {
|
||||||
|
let size = elems.len();
|
||||||
|
if index >= size {
|
||||||
|
Err(Error::TupleIndexOutOfBound {
|
||||||
|
location,
|
||||||
|
index,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(elems[index].clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(Error::NotATuple { location }),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::TupleIndex {
|
||||||
|
location,
|
||||||
|
tipo,
|
||||||
|
index,
|
||||||
|
tuple: Box::new(tuple),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_todo(&mut self, location: Span, kind: TodoKind, label: Option<String>) -> TypedExpr {
|
fn infer_todo(&mut self, location: Span, kind: TodoKind, label: Option<String>) -> TypedExpr {
|
||||||
let tipo = self.new_unbound_var();
|
let tipo = self.new_unbound_var();
|
||||||
|
|
||||||
|
|
|
@ -532,6 +532,10 @@ impl<'a> CodeGenerator<'a> {
|
||||||
|
|
||||||
self.build_ir(then, ir_stack, scope);
|
self.build_ir(then, ir_stack, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypedExpr::TupleIndex { .. } => {
|
||||||
|
todo!("Tuple indexing not implementing yet");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue