feat: add module constants

This commit is contained in:
rvcas 2022-12-01 15:15:06 -05:00 committed by Lucas
parent 34c8a58391
commit fedafed845
8 changed files with 298 additions and 54 deletions

View File

@ -185,6 +185,11 @@ pub enum Constant<T, RecordTag> {
value: String,
},
Tuple {
location: Span,
elements: Vec<Self>,
},
List {
location: Span,
elements: Vec<Self>,
@ -221,6 +226,9 @@ impl TypedConstant {
Constant::Int { .. } => builtins::int(),
Constant::String { .. } => builtins::string(),
Constant::ByteArray { .. } => builtins::byte_array(),
Constant::Tuple { elements, .. } => {
builtins::tuple(elements.iter().map(|e| e.tipo()).collect())
}
Constant::List { tipo, .. }
| Constant::Record { tipo, .. }
| Constant::Var { tipo, .. } => tipo.clone(),
@ -232,6 +240,7 @@ impl<A, B> Constant<A, B> {
pub fn location(&self) -> Span {
match self {
Constant::Int { location, .. }
| Constant::Tuple { location, .. }
| Constant::List { location, .. }
| Constant::String { location, .. }
| Constant::Record { location, .. }

View File

@ -351,6 +351,13 @@ impl<'comments> Formatter<'comments> {
module: Some(module),
..
} => docvec![module, ".", name],
Constant::Tuple { elements, .. } => "#"
.to_doc()
.append(wrap_args(
elements.iter().map(|e| (self.const_expr(e), false)),
))
.group(),
}
}

View File

@ -74,6 +74,7 @@ fn module_parser() -> impl Parser<Token, Vec<UntypedDefinition>, Error = ParseEr
data_parser(),
type_alias_parser(),
fn_parser(),
constant_parser(),
))
.repeated()
.then_ignore(end())
@ -265,6 +266,202 @@ pub fn fn_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseEr
)
}
fn constant_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
pub_parser()
.or_not()
.then_ignore(just(Token::Const))
.then(select! {Token::Name{name} => name})
.then(just(Token::Colon).ignore_then(type_parser()).or_not())
.then_ignore(just(Token::Equal))
.then(constant_value_parser())
.map_with_span(|(((public, name), annotation), value), span| {
ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant {
doc: None,
location: span,
public: public.is_some(),
name,
annotation,
value: Box::new(value),
tipo: (),
})
})
}
fn constant_value_parser() -> impl Parser<Token, ast::UntypedConstant, Error = ParseError> {
recursive(|r| {
let constant_string_parser =
select! {Token::String {value} => value}.map_with_span(|value, span| {
ast::UntypedConstant::String {
location: span,
value,
}
});
let constant_int_parser =
select! {Token::Int {value} => value}.map_with_span(|value, span| {
ast::UntypedConstant::Int {
location: span,
value,
}
});
let constant_tuple_parser = just(Token::Hash)
.ignore_then(r.clone())
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
.map_with_span(|elements, span| ast::UntypedConstant::Tuple {
location: span,
elements,
});
let constant_bytearray_parser = just(Token::Hash)
.ignore_then(
select! {Token::Int {value} => value}.validate(|value, span, emit| {
let byte: u8 = match value.parse() {
Ok(b) => b,
Err(_) => {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Byte),
));
0
}
};
byte
}),
)
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare))
.map_with_span(|bytes, span| ast::UntypedConstant::ByteArray {
location: span,
bytes,
});
let constant_list_parser = r
.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare))
.map_with_span(|elements, span| ast::UntypedConstant::List {
location: span,
elements,
tipo: (),
});
let constant_record_parser = choice((
choice((
select! {Token::Name { name } => name}
.then_ignore(just(Token::Dot))
.or_not()
.then(select! {Token::UpName { name } => name})
.then(
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.or_not()
.then(r.clone())
.validate(|(label_opt, value), span, emit| {
let label = if label_opt.is_some() {
label_opt
} else if let ast::UntypedConstant::Var { name, .. } = &value {
Some(name.clone())
} else {
emit(ParseError::expected_input_found(
value.location(),
None,
Some(error::Pattern::RecordPunning),
));
None
};
ast::CallArg {
location: span,
value,
label,
}
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
)
.map_with_span(
|((module, name), args), span| ast::UntypedConstant::Record {
location: span,
module,
name,
args,
tag: (),
tipo: (),
field_map: None,
},
),
select! {Token::Name { name } => name}
.then_ignore(just(Token::Dot))
.or_not()
.then(select! {Token::UpName { name } => name})
.then(
r.clone()
.map_with_span(|value, span| ast::CallArg {
location: span,
value,
label: None,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.map_with_span(
|((module, name), args), span| ast::UntypedConstant::Record {
location: span,
module,
name,
args,
tag: (),
tipo: (),
field_map: None,
},
),
)),
select! {Token::Name { name } => name}
.then_ignore(just(Token::Dot))
.then(select! {Token::Name{name} => name})
.map_with_span(|(module, name), span: Span| ast::UntypedConstant::Var {
location: span.union(span),
module: Some(module),
name,
constructor: None,
tipo: (),
}),
));
let constant_var_parser =
select! {Token::Name {name} => name}.map_with_span(|name, span| {
ast::UntypedConstant::Var {
location: span,
module: None,
name,
constructor: None,
tipo: (),
}
});
choice((
constant_string_parser,
constant_int_parser,
constant_tuple_parser,
constant_bytearray_parser,
constant_list_parser,
constant_record_parser,
constant_var_parser,
))
})
}
pub fn fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
choice((
select! {Token::Name {name} => name}
@ -445,6 +642,10 @@ pub fn expr_parser(
tail,
});
let block_parser = seq_r
.clone()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
let anon_fn_parser = just(Token::Fn)
.ignore_then(
anon_fn_param_parser()
@ -453,11 +654,7 @@ pub fn expr_parser(
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then(just(Token::RArrow).ignore_then(type_parser()).or_not())
.then(
seq_r
.clone()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
)
.then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)))
.map_with_span(
|((arguments, return_annotation), body), span| expr::UntypedExpr::Fn {
arguments,
@ -468,8 +665,6 @@ pub fn expr_parser(
},
);
let block_parser = seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
// TODO: do guards later
// let when_clause_guard_parser = just(Token::If);
@ -999,10 +1194,16 @@ pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = Parse
label: Some(name),
value: pattern,
}),
r.clone().map_with_span(|pattern, span| {
r.clone().validate(|pattern, span, emit| {
let label = if let ast::UntypedPattern::Var { name, .. } = &pattern {
Some(name.clone())
} else {
emit(ParseError::expected_input_found(
pattern.location(),
None,
Some(error::Pattern::RecordPunning),
));
None
};

View File

@ -5,7 +5,7 @@ use miette::Diagnostic;
use crate::{ast::Span, parser::token::Token};
#[derive(Debug, Diagnostic, thiserror::Error)]
#[error("{kind}")]
#[error("{kind}\n")]
pub struct ParseError {
pub kind: ErrorKind,
#[label]
@ -104,12 +104,17 @@ pub enum Pattern {
TermIdent,
#[error("Unexpected end of input")]
End,
#[error("Bad list spread pattern")]
#[error("Malformed list spread pattern")]
#[diagnostic(help("List spread in matches can\nuse have a discard or var"))]
Match,
#[error("Bad byte literal")]
#[error("Malformed byte literal")]
#[diagnostic(help("Bytes must be between 0-255"))]
Byte,
#[error("Unexpected pattern")]
#[diagnostic(help(
"If no label is provided then only variables\nmatching a field name are allowed"
))]
RecordPunning,
}
impl From<char> for Pattern {

View File

@ -8,7 +8,7 @@ use super::Type;
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum Error {
#[error("Duplicate argument\n\n{label}")]
#[error("Duplicate argument\n\n{label}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateArgument {
#[label]
@ -16,7 +16,7 @@ pub enum Error {
label: String,
},
#[error("Duplicate const\n\n{name}")]
#[error("Duplicate const\n\n{name}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateConstName {
#[label]
@ -26,7 +26,7 @@ pub enum Error {
name: String,
},
#[error("Duplicate import\n\n{name}")]
#[error("Duplicate import\n\n{name}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateImport {
#[label]
@ -36,7 +36,7 @@ pub enum Error {
name: String,
},
#[error("Duplicate field\n\n{label}")]
#[error("Duplicate field\n\n{label}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateField {
#[label]
@ -44,7 +44,7 @@ pub enum Error {
label: String,
},
#[error("Duplicate name\n\n{name}")]
#[error("Duplicate name\n\n{name}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateName {
#[label]
@ -54,7 +54,7 @@ pub enum Error {
name: String,
},
#[error("Duplicate type name\n\n{name}")]
#[error("Duplicate type name\n\n{name}\n")]
#[diagnostic(help("Try renaming it"))]
DuplicateTypeName {
#[label]
@ -64,7 +64,7 @@ pub enum Error {
name: String,
},
#[error("Incorrect arity\n\nExpected\n\n{expected}\n\nGiven\n\n{given}")]
#[error("Incorrect arity\n\nExpected\n\n{expected}\n\nGiven\n\n{given}\n")]
IncorrectArity {
#[label]
location: Span,
@ -73,7 +73,7 @@ pub enum Error {
labels: Vec<String>,
},
#[error("Incorrect number of clause patterns\n\nExpected\n\n{expected}\n\nGiven\n\n{given}")]
#[error("Incorrect number of clause patterns\n\nExpected\n\n{expected}\n\nGiven\n\n{given}\n")]
IncorrectNumClausePatterns {
#[label]
location: Span,
@ -81,7 +81,7 @@ pub enum Error {
given: usize,
},
#[error("Incorrect type arity for `{name}`\n\nExpected\n\n{expected}\n\nGiven\n\n{given}")]
#[error("Incorrect type arity for `{name}`\n\nExpected\n\n{expected}\n\nGiven\n\n{given}\n")]
IncorrectTypeArity {
#[label]
location: Span,
@ -90,7 +90,7 @@ pub enum Error {
given: usize,
},
#[error("Non-exhaustive pattern match")]
#[error("Non-exhaustive pattern match\n")]
NotExhaustivePatternMatch {
#[label]
location: Span,
@ -104,65 +104,65 @@ pub enum Error {
tipo: Arc<Type>,
},
#[error("Module\n\n{name}\n\ncontains keyword\n\n{keyword}")]
#[error("Module\n\n{name}\n\ncontains keyword\n\n{keyword}\n")]
KeywordInModuleName { name: String, keyword: String },
#[error("Clause guard {name} is not local")]
#[error("Clause guard {name} is not local\n")]
NonLocalClauseGuardVariable {
#[label]
location: Span,
name: String,
},
#[error("Positional argument after labeled")]
#[error("Positional argument after labeled\n")]
PositionalArgumentAfterLabeled {
#[label]
location: Span,
},
#[error("Private type leaked")]
#[error("Private type leaked\n")]
PrivateTypeLeak {
#[label]
location: Span,
leaked: Type,
},
#[error("Record access unknown type")]
#[error("Record access unknown type\n")]
RecordAccessUnknownType {
#[label]
location: Span,
},
#[error("Record update invalid constructor")]
#[error("Record update invalid constructor\n")]
RecordUpdateInvalidConstructor {
#[label]
location: Span,
},
#[error("{name} is a reserved module name")]
#[error("{name} is a reserved module name\n")]
ReservedModuleName { name: String },
#[error("Unexpected labeled argument\n\n{label}")]
#[error("Unexpected labeled argument\n\n{label}\n")]
UnexpectedLabeledArg {
#[label]
location: Span,
label: String,
},
#[error("Unexpected type hole")]
#[error("Unexpected type hole\n")]
UnexpectedTypeHole {
#[label]
location: Span,
},
#[error("Unknown labels")]
#[error("Unknown labels\n")]
UnknownLabels {
unknown: Vec<(String, Span)>,
valid: Vec<String>,
supplied: Vec<String>,
},
#[error("Unknown module\n\n{name}")]
#[error("Unknown module\n\n{name}\n")]
UnknownModule {
#[label]
location: Span,
@ -170,7 +170,7 @@ pub enum Error {
imported_modules: Vec<String>,
},
#[error("Unknown module field\n\n{name}\n\nin module\n\n{module_name}")]
#[error("Unknown module field\n\n{name}\n\nin module\n\n{module_name}\n")]
UnknownModuleField {
location: Span,
name: String,
@ -179,7 +179,7 @@ pub enum Error {
type_constructors: Vec<String>,
},
#[error("Unknown module value\n\n{name}")]
#[error("Unknown module value\n\n{name}\n")]
UnknownModuleValue {
#[label]
location: Span,
@ -188,7 +188,7 @@ pub enum Error {
value_constructors: Vec<String>,
},
#[error("Unknown type\n\n{name}\n\nin module\n\n{module_name}")]
#[error("Unknown type\n\n{name}\n\nin module\n\n{module_name}\n")]
UnknownModuleType {
#[label]
location: Span,
@ -197,7 +197,7 @@ pub enum Error {
type_constructors: Vec<String>,
},
#[error("Unknown record field\n\n{label}")]
#[error("Unknown record field\n\n{label}\n")]
UnknownRecordField {
#[label]
location: Span,
@ -207,7 +207,7 @@ pub enum Error {
situation: Option<UnknownRecordFieldSituation>,
},
#[error("Unknown type\n\n{name}")]
#[error("Unknown type\n\n{name}\n")]
UnknownType {
#[label]
location: Span,
@ -215,7 +215,7 @@ pub enum Error {
types: Vec<String>,
},
#[error("Unknown variable\n\n{name}")]
#[error("Unknown variable\n\n{name}\n")]
UnknownVariable {
#[label]
location: Span,
@ -223,14 +223,14 @@ pub enum Error {
variables: Vec<String>,
},
#[error("Unnecessary spread operator")]
#[error("Unnecessary spread operator\n")]
UnnecessarySpreadOperator {
#[label]
location: Span,
arity: usize,
},
#[error("Cannot update a type with multiple constructors")]
#[error("Cannot update a type with multiple constructors\n")]
UpdateMultiConstructorType {
#[label]
location: Span,
@ -271,7 +271,7 @@ pub enum Error {
name: String,
},
#[error("")]
#[error("Recursive type detected\n")]
RecursiveType {
#[label]
location: Span,
@ -363,7 +363,7 @@ impl Error {
#[derive(Debug, PartialEq, Clone, thiserror::Error, Diagnostic)]
pub enum Warning {
#[error("todo")]
#[error("Todo\n")]
Todo {
kind: TodoKind,
#[label]
@ -371,31 +371,31 @@ pub enum Warning {
tipo: Arc<Type>,
},
#[error("implicitly discarded result")]
#[error("Implicitly discarded result\n")]
ImplicitlyDiscardedResult {
#[label]
location: Span,
},
#[error("unused literal")]
#[error("Unused literal\n")]
UnusedLiteral {
#[label]
location: Span,
},
#[error("record update with no fields")]
#[error("Record update with no fields\n")]
NoFieldsRecordUpdate {
#[label]
location: Span,
},
#[error("record update using all fields")]
#[error("Record update using all fields\n")]
AllFieldsRecordUpdate {
#[label]
location: Span,
},
#[error("unused type {name}")]
#[error("Unused type {name}\n")]
UnusedType {
#[label]
location: Span,
@ -403,7 +403,7 @@ pub enum Warning {
name: String,
},
#[error("unused constructor {name}")]
#[error("Unused constructor {name}\n")]
UnusedConstructor {
#[label]
location: Span,
@ -411,35 +411,35 @@ pub enum Warning {
name: String,
},
#[error("unused imported value {name}")]
#[error("Unused imported value {name}\n")]
UnusedImportedValue {
#[label]
location: Span,
name: String,
},
#[error("unused imported module {name}")]
#[error("Unused imported module {name}\n")]
UnusedImportedModule {
#[label]
location: Span,
name: String,
},
#[error("unused private module constant {name}")]
#[error("Unused private module constant {name}\n")]
UnusedPrivateModuleConstant {
#[label]
location: Span,
name: String,
},
#[error("unused private function {name}")]
#[error("Unused private function {name}\n")]
UnusedPrivateFunction {
#[label]
location: Span,
name: String,
},
#[error("unused variable {name}")]
#[error("Unused variable {name}\n")]
UnusedVariable {
#[label]
location: Span,

View File

@ -1177,6 +1177,22 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
Ok((typed_pattern, typed_alternatives))
}
fn infer_const_tuple(
&mut self,
untyped_elements: Vec<UntypedConstant>,
location: Span,
) -> Result<TypedConstant, Error> {
let mut elements = Vec::with_capacity(untyped_elements.len());
for element in untyped_elements {
let element = self.infer_const(&None, element)?;
elements.push(element);
}
Ok(Constant::Tuple { elements, location })
}
// TODO: extract the type annotation checking into a infer_module_const
// function that uses this function internally
pub fn infer_const(
@ -1193,6 +1209,10 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
location, value, ..
} => Ok(Constant::String { location, value }),
Constant::Tuple {
elements, location, ..
} => self.infer_const_tuple(elements, location),
Constant::List {
elements, location, ..
} => self.infer_const_list(elements, location),

View File

@ -265,7 +265,7 @@ impl Diagnostic for Error {
#[derive(thiserror::Error)]
pub enum Warning {
#[error("type checking")]
#[error("Checking")]
Type {
path: PathBuf,
src: String,

View File

@ -3,6 +3,8 @@ use sample/mint
use sample/spend
use aiken/builtin
const something = 5
pub type Redeemer {
signer: ByteArray,
amount: Int,