feat: typecheck If expressions
This commit is contained in:
parent
5244e58c9f
commit
825783ca61
|
@ -8,7 +8,11 @@ use clap::{Parser, Subcommand};
|
||||||
#[clap(propagate_version = true)]
|
#[clap(propagate_version = true)]
|
||||||
pub enum Args {
|
pub enum Args {
|
||||||
/// Build an aiken project
|
/// Build an aiken project
|
||||||
Build,
|
Build {
|
||||||
|
/// Path to project
|
||||||
|
#[clap(short, long)]
|
||||||
|
directory: Option<PathBuf>,
|
||||||
|
},
|
||||||
/// Typecheck a project project
|
/// Typecheck a project project
|
||||||
Check {
|
Check {
|
||||||
/// Path to project
|
/// Path to project
|
||||||
|
|
|
@ -55,7 +55,14 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(&self) {
|
pub fn report(&self) {
|
||||||
eprintln!("Error: {:?}", self)
|
match self {
|
||||||
|
Error::List(errors) => {
|
||||||
|
for error in errors {
|
||||||
|
eprintln!("Error: {:?}", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rest => eprintln!("Error: {:?}", rest),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ use uplc::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use aiken::{config::Config, error, project::Project};
|
use aiken::{config::Config, project::Project};
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
|
@ -28,14 +28,35 @@ fn main() -> miette::Result<()> {
|
||||||
let args = Args::default();
|
let args = Args::default();
|
||||||
|
|
||||||
match args {
|
match args {
|
||||||
Args::Build => {
|
Args::Build { directory } => {
|
||||||
// 1. load and parse modules
|
let project_path = if let Some(d) = directory {
|
||||||
// * lib - contains modules, types, and functions
|
d
|
||||||
// * contracts - contains validators
|
} else {
|
||||||
// * scripts - contains native scripts dsl
|
env::current_dir().into_diagnostic()?
|
||||||
// 2. type check everything
|
};
|
||||||
// 3. generate uplc and policy/address if relevant
|
|
||||||
todo!()
|
let config = Config::load(project_path.clone()).into_diagnostic()?;
|
||||||
|
|
||||||
|
let mut project = Project::new(config, project_path);
|
||||||
|
|
||||||
|
let build_result = project.build();
|
||||||
|
|
||||||
|
let warning_count = project.warnings.len();
|
||||||
|
|
||||||
|
for warning in project.warnings {
|
||||||
|
warning.report()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = build_result {
|
||||||
|
err.report();
|
||||||
|
|
||||||
|
miette::bail!(
|
||||||
|
"failed: {} error(s), {warning_count} warning(s)",
|
||||||
|
err.total(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("finished with {warning_count} warning(s)")
|
||||||
}
|
}
|
||||||
|
|
||||||
Args::Check { directory } => {
|
Args::Check { directory } => {
|
||||||
|
@ -49,24 +70,24 @@ fn main() -> miette::Result<()> {
|
||||||
|
|
||||||
let mut project = Project::new(config, project_path);
|
let mut project = Project::new(config, project_path);
|
||||||
|
|
||||||
let build_result = project.build();
|
let build_result = project.check();
|
||||||
|
|
||||||
|
let warning_count = project.warnings.len();
|
||||||
|
|
||||||
for warning in project.warnings {
|
for warning in project.warnings {
|
||||||
warning.report()
|
warning.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = build_result {
|
if let Err(err) = build_result {
|
||||||
match &err {
|
err.report();
|
||||||
error::Error::List(errors) => {
|
|
||||||
for error in errors {
|
|
||||||
eprintln!("Error: {:?}", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rest => eprintln!("Error: {:?}", rest),
|
|
||||||
}
|
|
||||||
|
|
||||||
// miette::bail!("failed: {} errors", err.total());
|
miette::bail!(
|
||||||
|
"failed: {} error(s), {warning_count} warning(s)",
|
||||||
|
err.total(),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("finished with {warning_count} warning(s)")
|
||||||
}
|
}
|
||||||
|
|
||||||
Args::Dev => {
|
Args::Dev => {
|
||||||
|
|
|
@ -50,15 +50,21 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&mut self) -> Result<(), Error> {
|
pub fn build(&mut self) -> Result<(), Error> {
|
||||||
|
self.compile(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(&mut self) -> Result<(), Error> {
|
||||||
|
self.compile(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, _uplc_gen: bool) -> Result<(), Error> {
|
||||||
self.read_source_files()?;
|
self.read_source_files()?;
|
||||||
|
|
||||||
let parsed_modules = self.parse_sources()?;
|
let parsed_modules = self.parse_sources()?;
|
||||||
|
|
||||||
let processing_sequence = parsed_modules.sequence()?;
|
let processing_sequence = parsed_modules.sequence()?;
|
||||||
|
|
||||||
let checked_modules = self.type_check(parsed_modules, processing_sequence)?;
|
let _checked_modules = self.type_check(parsed_modules, processing_sequence)?;
|
||||||
|
|
||||||
println!("{:?}", checked_modules);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -143,7 +149,8 @@ impl Project {
|
||||||
path,
|
path,
|
||||||
code,
|
code,
|
||||||
kind,
|
kind,
|
||||||
package,
|
// TODO: come back and figure out where to use this
|
||||||
|
package: _package,
|
||||||
ast,
|
ast,
|
||||||
}) = parsed_modules.remove(&name)
|
}) = parsed_modules.remove(&name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod error;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod pretty;
|
||||||
pub mod tipo;
|
pub mod tipo;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,436 @@
|
||||||
|
//! This module implements the functionality described in
|
||||||
|
//! ["Strictly Pretty" (2000) by Christian Lindig][0], with a few
|
||||||
|
//! extensions.
|
||||||
|
//!
|
||||||
|
//! This module is heavily influenced by Elixir's Inspect.Algebra and
|
||||||
|
//! JavaScript's Prettier.
|
||||||
|
//!
|
||||||
|
//! [0]: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200
|
||||||
|
//!
|
||||||
|
//! ## Extensions
|
||||||
|
//!
|
||||||
|
//! - `ForceBreak` from Prettier.
|
||||||
|
//! - `FlexBreak` from Elixir.
|
||||||
|
#![allow(clippy::wrong_self_convention)]
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests;
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! docvec {
|
||||||
|
() => {
|
||||||
|
Document::Vec(Vec::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
($($x:expr),+ $(,)?) => {
|
||||||
|
Document::Vec(vec![$($x.to_doc()),+])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {}
|
||||||
|
|
||||||
|
/// Coerce a value into a Document.
|
||||||
|
/// Note we do not implement this for String as a slight pressure to favour str
|
||||||
|
/// over String.
|
||||||
|
pub trait Documentable<'a> {
|
||||||
|
fn to_doc(self) -> Document<'a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for char {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for &'a str {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::Str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for isize {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for i64 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for usize {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for f64 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{:?}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for u64 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{:?}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for u32 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for u16 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for u8 {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::String(format!("{}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for Document<'a> {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Documentable<'a> for Vec<Document<'a>> {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
Document::Vec(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: Documentable<'a>> Documentable<'a> for Option<D> {
|
||||||
|
fn to_doc(self) -> Document<'a> {
|
||||||
|
self.map(Documentable::to_doc).unwrap_or_else(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat<'a>(docs: impl IntoIterator<Item = Document<'a>>) -> Document<'a> {
|
||||||
|
Document::Vec(docs.into_iter().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join<'a>(
|
||||||
|
docs: impl IntoIterator<Item = Document<'a>>,
|
||||||
|
separator: Document<'a>,
|
||||||
|
) -> Document<'a> {
|
||||||
|
concat(Itertools::intersperse(docs.into_iter(), separator))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Document<'a> {
|
||||||
|
/// A mandatory linebreak
|
||||||
|
Line(usize),
|
||||||
|
|
||||||
|
/// Forces contained groups to break
|
||||||
|
ForceBreak,
|
||||||
|
|
||||||
|
/// May break contained document based on best fit, thus flex break
|
||||||
|
FlexBreak(Box<Self>),
|
||||||
|
|
||||||
|
/// Renders `broken` if group is broken, `unbroken` otherwise
|
||||||
|
Break {
|
||||||
|
broken: &'a str,
|
||||||
|
unbroken: &'a str,
|
||||||
|
kind: BreakKind,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Join multiple documents together
|
||||||
|
Vec(Vec<Self>),
|
||||||
|
|
||||||
|
/// Nests the given document by the given indent
|
||||||
|
Nest(isize, Box<Self>),
|
||||||
|
|
||||||
|
/// Nests the given document to the current cursor position
|
||||||
|
NestCurrent(Box<Self>),
|
||||||
|
|
||||||
|
/// Nests the given document to the current cursor position
|
||||||
|
Group(Box<Self>),
|
||||||
|
|
||||||
|
/// A string to render
|
||||||
|
String(String),
|
||||||
|
|
||||||
|
/// A str to render
|
||||||
|
Str(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Mode {
|
||||||
|
Broken,
|
||||||
|
Unbroken,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fits(
|
||||||
|
mut limit: isize,
|
||||||
|
mut current_width: isize,
|
||||||
|
mut docs: VecDeque<(isize, Mode, &Document<'_>)>,
|
||||||
|
) -> bool {
|
||||||
|
loop {
|
||||||
|
if current_width > limit {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (indent, mode, document) = match docs.pop_front() {
|
||||||
|
Some(x) => x,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
match document {
|
||||||
|
Document::Line(_) => return true,
|
||||||
|
|
||||||
|
Document::ForceBreak => return false,
|
||||||
|
|
||||||
|
Document::Nest(i, doc) => docs.push_front((i + indent, mode, doc)),
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
|
Document::NestCurrent(doc) => docs.push_front((indent, mode, doc)),
|
||||||
|
|
||||||
|
Document::Group(doc) => docs.push_front((indent, Mode::Unbroken, doc)),
|
||||||
|
|
||||||
|
Document::Str(s) => limit -= s.len() as isize,
|
||||||
|
Document::String(s) => limit -= s.len() as isize,
|
||||||
|
|
||||||
|
Document::Break { unbroken, .. } => match mode {
|
||||||
|
Mode::Broken => return true,
|
||||||
|
Mode::Unbroken => current_width += unbroken.len() as isize,
|
||||||
|
},
|
||||||
|
|
||||||
|
Document::FlexBreak(doc) => docs.push_front((indent, mode, doc)),
|
||||||
|
|
||||||
|
Document::Vec(vec) => {
|
||||||
|
for doc in vec.iter().rev() {
|
||||||
|
docs.push_front((indent, mode.clone(), doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BreakKind {
|
||||||
|
Flex,
|
||||||
|
Strict,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format(
|
||||||
|
writer: &mut String,
|
||||||
|
limit: isize,
|
||||||
|
mut width: isize,
|
||||||
|
mut docs: VecDeque<(isize, Mode, &Document<'_>)>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
while let Some((indent, mode, document)) = docs.pop_front() {
|
||||||
|
match document {
|
||||||
|
Document::ForceBreak => (),
|
||||||
|
|
||||||
|
Document::Line(i) => {
|
||||||
|
for _ in 0..*i {
|
||||||
|
writer.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
for _ in 0..indent {
|
||||||
|
writer.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
width = indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flex breaks are NOT conditional to the mode
|
||||||
|
Document::Break {
|
||||||
|
broken,
|
||||||
|
unbroken,
|
||||||
|
kind: BreakKind::Flex,
|
||||||
|
} => {
|
||||||
|
let unbroken_width = width + unbroken.len() as isize;
|
||||||
|
|
||||||
|
if fits(limit, unbroken_width, docs.clone()) {
|
||||||
|
writer.push_str(unbroken);
|
||||||
|
|
||||||
|
width = unbroken_width;
|
||||||
|
} else {
|
||||||
|
writer.push_str(broken);
|
||||||
|
|
||||||
|
writer.push('\n');
|
||||||
|
|
||||||
|
for _ in 0..indent {
|
||||||
|
writer.push(' ');
|
||||||
|
}
|
||||||
|
width = indent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strict breaks are conditional to the mode
|
||||||
|
Document::Break {
|
||||||
|
broken,
|
||||||
|
unbroken,
|
||||||
|
kind: BreakKind::Strict,
|
||||||
|
} => {
|
||||||
|
width = match mode {
|
||||||
|
Mode::Unbroken => {
|
||||||
|
writer.push_str(unbroken);
|
||||||
|
|
||||||
|
width + unbroken.len() as isize
|
||||||
|
}
|
||||||
|
Mode::Broken => {
|
||||||
|
writer.push_str(broken);
|
||||||
|
|
||||||
|
writer.push('\n');
|
||||||
|
|
||||||
|
for _ in 0..indent {
|
||||||
|
writer.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
indent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::String(s) => {
|
||||||
|
width += s.len() as isize;
|
||||||
|
|
||||||
|
writer.push_str(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::Str(s) => {
|
||||||
|
width += s.len() as isize;
|
||||||
|
|
||||||
|
writer.push_str(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::Vec(vec) => {
|
||||||
|
for doc in vec.iter().rev() {
|
||||||
|
docs.push_front((indent, mode.clone(), doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::Nest(i, doc) => {
|
||||||
|
docs.push_front((indent + i, mode, doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::NestCurrent(doc) => {
|
||||||
|
docs.push_front((width, mode, doc));
|
||||||
|
}
|
||||||
|
|
||||||
|
Document::Group(doc) | Document::FlexBreak(doc) => {
|
||||||
|
// TODO: don't clone the doc
|
||||||
|
let mut group_docs = VecDeque::new();
|
||||||
|
|
||||||
|
group_docs.push_back((indent, Mode::Unbroken, doc.as_ref()));
|
||||||
|
|
||||||
|
if fits(limit, width, group_docs) {
|
||||||
|
docs.push_front((indent, Mode::Unbroken, doc));
|
||||||
|
} else {
|
||||||
|
docs.push_front((indent, Mode::Broken, doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nil<'a>() -> Document<'a> {
|
||||||
|
Document::Vec(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line<'a>() -> Document<'a> {
|
||||||
|
Document::Line(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lines<'a>(i: usize) -> Document<'a> {
|
||||||
|
Document::Line(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_break<'a>() -> Document<'a> {
|
||||||
|
Document::ForceBreak
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn break_<'a>(broken: &'a str, unbroken: &'a str) -> Document<'a> {
|
||||||
|
Document::Break {
|
||||||
|
broken,
|
||||||
|
unbroken,
|
||||||
|
kind: BreakKind::Strict,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flex_break<'a>(broken: &'a str, unbroken: &'a str) -> Document<'a> {
|
||||||
|
Document::Break {
|
||||||
|
broken,
|
||||||
|
unbroken,
|
||||||
|
kind: BreakKind::Flex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Document<'a> {
|
||||||
|
pub fn group(self) -> Self {
|
||||||
|
Self::Group(Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nest(self, indent: isize) -> Self {
|
||||||
|
Self::Nest(indent, Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nest_current(self) -> Self {
|
||||||
|
Self::NestCurrent(Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(self, second: impl Documentable<'a>) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Vec(mut vec) => {
|
||||||
|
vec.push(second.to_doc());
|
||||||
|
Self::Vec(vec)
|
||||||
|
}
|
||||||
|
first => Self::Vec(vec![first, second.to_doc()]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_pretty_string(self, limit: isize) -> String {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
|
self.pretty_print(limit, &mut buffer)
|
||||||
|
.expect("Writing to string buffer failed");
|
||||||
|
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surround(self, open: impl Documentable<'a>, closed: impl Documentable<'a>) -> Self {
|
||||||
|
open.to_doc().append(self).append(closed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pretty_print(&self, limit: isize, writer: &mut String) -> Result<(), Error> {
|
||||||
|
let mut docs = VecDeque::new();
|
||||||
|
|
||||||
|
docs.push_back((0, Mode::Unbroken, self));
|
||||||
|
|
||||||
|
format(writer, limit, 0, docs)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true when the document contains no printable characters
|
||||||
|
/// (whitespace and newlines are considered printable characters).
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
use Document::*;
|
||||||
|
match self {
|
||||||
|
Line(n) => *n == 0,
|
||||||
|
ForceBreak => true,
|
||||||
|
String(s) => s.is_empty(),
|
||||||
|
Str(s) => s.is_empty(),
|
||||||
|
// assuming `broken` and `unbroken` are equivalent
|
||||||
|
Break { broken, .. } => broken.is_empty(),
|
||||||
|
FlexBreak(d) | Nest(_, d) | NestCurrent(d) | Group(d) => d.is_empty(),
|
||||||
|
Vec(docs) => docs.iter().all(|d| d.is_empty()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
tipo::fields::FieldMap,
|
tipo::fields::FieldMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::environment::Environment;
|
use self::{environment::Environment, pretty::Printer};
|
||||||
|
|
||||||
mod environment;
|
mod environment;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -15,6 +15,7 @@ mod hydrator;
|
||||||
mod infer;
|
mod infer;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
mod pipe;
|
mod pipe;
|
||||||
|
mod pretty;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
@ -203,6 +204,18 @@ impl Type {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_pretty(&self, indent: usize) -> String {
|
||||||
|
Printer::new().pretty_print(self, indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_pretty_with_names(&self, names: HashMap<u64, String>, indent: usize) -> String {
|
||||||
|
let mut printer = Printer::new();
|
||||||
|
|
||||||
|
printer.with_names(names);
|
||||||
|
|
||||||
|
printer.pretty_print(self, indent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
@ -231,7 +231,11 @@ pub enum Error {
|
||||||
location: Span,
|
location: Span,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("")]
|
#[error(
|
||||||
|
"Type Mismatch\n\nExpected type:\n\n{}\n\nFound type:\n\n{}\n",
|
||||||
|
expected.to_pretty_with_names(rigid_type_names.clone(), 4),
|
||||||
|
given.to_pretty_with_names(rigid_type_names.clone(), 4)
|
||||||
|
)]
|
||||||
CouldNotUnify {
|
CouldNotUnify {
|
||||||
#[label]
|
#[label]
|
||||||
location: Span,
|
location: Span,
|
||||||
|
|
|
@ -6,9 +6,9 @@ use crate::{
|
||||||
ast::{
|
ast::{
|
||||||
Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant,
|
Annotation, Arg, ArgName, AssignmentKind, BinOp, CallArg, Clause, ClauseGuard, Constant,
|
||||||
RecordUpdateSpread, Span, SrcId, TodoKind, TypedArg, TypedCallArg, TypedClause,
|
RecordUpdateSpread, Span, SrcId, TodoKind, TypedArg, TypedCallArg, TypedClause,
|
||||||
TypedClauseGuard, TypedConstant, TypedMultiPattern, TypedRecordUpdateArg, UntypedArg,
|
TypedClauseGuard, TypedConstant, TypedIfBranch, TypedMultiPattern, TypedRecordUpdateArg,
|
||||||
UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedMultiPattern, UntypedPattern,
|
UntypedArg, UntypedClause, UntypedClauseGuard, UntypedConstant, UntypedIfBranch,
|
||||||
UntypedRecordUpdateArg,
|
UntypedMultiPattern, UntypedPattern, UntypedRecordUpdateArg,
|
||||||
},
|
},
|
||||||
builtins::{bool, byte_array, function, int, list, result, string},
|
builtins::{bool, byte_array, function, int, list, result, string},
|
||||||
expr::{TypedExpr, UntypedExpr},
|
expr::{TypedExpr, UntypedExpr},
|
||||||
|
@ -267,7 +267,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
location,
|
location,
|
||||||
branches,
|
branches,
|
||||||
final_else,
|
final_else,
|
||||||
} => todo!(),
|
} => self.infer_if(branches, *final_else, location),
|
||||||
|
|
||||||
UntypedExpr::Assignment {
|
UntypedExpr::Assignment {
|
||||||
location,
|
location,
|
||||||
|
@ -1422,6 +1422,60 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_if(
|
||||||
|
&mut self,
|
||||||
|
branches: Vec1<UntypedIfBranch>,
|
||||||
|
final_else: UntypedExpr,
|
||||||
|
location: Span,
|
||||||
|
) -> Result<TypedExpr, Error> {
|
||||||
|
let first = branches.first();
|
||||||
|
|
||||||
|
let condition = self.infer(first.condition.clone())?;
|
||||||
|
|
||||||
|
self.unify(bool(), condition.tipo(), condition.type_defining_location())?;
|
||||||
|
|
||||||
|
let body = self.infer(first.body.clone())?;
|
||||||
|
|
||||||
|
let tipo = body.tipo();
|
||||||
|
|
||||||
|
let mut typed_branches = Vec1::new(TypedIfBranch {
|
||||||
|
body,
|
||||||
|
condition,
|
||||||
|
location: first.location,
|
||||||
|
});
|
||||||
|
|
||||||
|
for branch in &branches[1..] {
|
||||||
|
let condition = self.infer(branch.condition.clone())?;
|
||||||
|
|
||||||
|
self.unify(bool(), condition.tipo(), condition.type_defining_location())?;
|
||||||
|
|
||||||
|
let body = self.infer(first.body.clone())?;
|
||||||
|
|
||||||
|
self.unify(tipo.clone(), body.tipo(), body.type_defining_location())?;
|
||||||
|
|
||||||
|
typed_branches.push(TypedIfBranch {
|
||||||
|
body,
|
||||||
|
condition,
|
||||||
|
location: branch.location,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let typed_final_else = self.infer(final_else)?;
|
||||||
|
|
||||||
|
self.unify(
|
||||||
|
tipo.clone(),
|
||||||
|
typed_final_else.tipo(),
|
||||||
|
typed_final_else.type_defining_location(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(TypedExpr::If {
|
||||||
|
location,
|
||||||
|
branches: typed_branches,
|
||||||
|
final_else: Box::new(typed_final_else),
|
||||||
|
tipo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_fn(
|
fn infer_fn(
|
||||||
&mut self,
|
&mut self,
|
||||||
args: Vec<UntypedArg>,
|
args: Vec<UntypedArg>,
|
||||||
|
|
|
@ -0,0 +1,382 @@
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::{Type, TypeVar};
|
||||||
|
use crate::{
|
||||||
|
docvec,
|
||||||
|
pretty::{nil, *},
|
||||||
|
};
|
||||||
|
|
||||||
|
const INDENT: isize = 2;
|
||||||
|
|
||||||
|
// TODO: use references instead of cloning strings and vectors
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Printer {
|
||||||
|
names: HashMap<u64, String>,
|
||||||
|
uid: u64,
|
||||||
|
// A mapping of printd type names to the module that they are defined in.
|
||||||
|
printed_types: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Printer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_names(&mut self, names: HashMap<u64, String>) {
|
||||||
|
self.names = names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a Type as a well formatted string.
|
||||||
|
///
|
||||||
|
pub fn pretty_print(&mut self, typ: &Type, initial_indent: usize) -> String {
|
||||||
|
let mut buffer = String::with_capacity(initial_indent);
|
||||||
|
|
||||||
|
for _ in 0..initial_indent {
|
||||||
|
buffer.push(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer
|
||||||
|
.to_doc()
|
||||||
|
.append(self.print(typ))
|
||||||
|
.nest(initial_indent as isize)
|
||||||
|
.to_pretty_string(80)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: have this function return a Document that borrows from the Type.
|
||||||
|
// Is this possible? The lifetime would have to go through the Arc<Refcell<Type>>
|
||||||
|
// for TypeVar::Link'd types.
|
||||||
|
pub fn print<'a>(&mut self, typ: &Type) -> Document<'a> {
|
||||||
|
match typ {
|
||||||
|
Type::App {
|
||||||
|
name, args, module, ..
|
||||||
|
} => {
|
||||||
|
let doc = if self.name_clashes_if_unqualified(name, module) {
|
||||||
|
qualify_type_name(module, name)
|
||||||
|
} else {
|
||||||
|
self.printed_types.insert(name.clone(), module.clone());
|
||||||
|
Document::String(name.clone())
|
||||||
|
};
|
||||||
|
if args.is_empty() {
|
||||||
|
doc
|
||||||
|
} else {
|
||||||
|
doc.append("(")
|
||||||
|
.append(self.args_to_aiken_doc(args))
|
||||||
|
.append(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::Fn { args, ret } => "fn("
|
||||||
|
.to_doc()
|
||||||
|
.append(self.args_to_aiken_doc(args))
|
||||||
|
.append(") ->")
|
||||||
|
.append(break_("", " ").append(self.print(ret)).nest(INDENT).group()),
|
||||||
|
|
||||||
|
Type::Var { tipo: typ, .. } => self.type_var_doc(&typ.borrow()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_clashes_if_unqualified(&mut self, tipo: &String, module: &String) -> bool {
|
||||||
|
match self.printed_types.get(tipo) {
|
||||||
|
None => false,
|
||||||
|
Some(previous_module) if module == previous_module => false,
|
||||||
|
Some(_different_module) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_var_doc<'a>(&mut self, typ: &TypeVar) -> Document<'a> {
|
||||||
|
match typ {
|
||||||
|
TypeVar::Link { tipo: ref typ, .. } => self.print(typ),
|
||||||
|
TypeVar::Unbound { id, .. } | TypeVar::Generic { id, .. } => self.generic_type_var(*id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generic_type_var<'a>(&mut self, id: u64) -> Document<'a> {
|
||||||
|
match self.names.get(&id) {
|
||||||
|
Some(n) => {
|
||||||
|
let typ_name = n.clone();
|
||||||
|
|
||||||
|
self.printed_types.insert(typ_name, "".to_string());
|
||||||
|
|
||||||
|
Document::String(n.clone())
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let n = self.next_letter();
|
||||||
|
|
||||||
|
self.names.insert(id, n.clone());
|
||||||
|
|
||||||
|
self.printed_types.insert(n.clone(), "".to_string());
|
||||||
|
|
||||||
|
Document::String(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_letter(&mut self) -> String {
|
||||||
|
let alphabet_length = 26;
|
||||||
|
let char_offset = 97;
|
||||||
|
let mut chars = vec![];
|
||||||
|
let mut n;
|
||||||
|
let mut rest = self.uid;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
n = rest % alphabet_length;
|
||||||
|
|
||||||
|
rest /= alphabet_length;
|
||||||
|
|
||||||
|
chars.push((n as u8 + char_offset) as char);
|
||||||
|
|
||||||
|
if rest == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rest -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
self.uid += 1;
|
||||||
|
|
||||||
|
chars.into_iter().rev().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args_to_aiken_doc<'a>(&mut self, args: &[Arc<Type>]) -> Document<'a> {
|
||||||
|
if args.is_empty() {
|
||||||
|
return nil();
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = concat(Itertools::intersperse(
|
||||||
|
args.iter().map(|t| self.print(t).group()),
|
||||||
|
break_(",", ", "),
|
||||||
|
));
|
||||||
|
|
||||||
|
break_("", "")
|
||||||
|
.append(args)
|
||||||
|
.nest(INDENT)
|
||||||
|
.append(break_(",", ""))
|
||||||
|
.group()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> {
|
||||||
|
if module.is_empty() {
|
||||||
|
docvec!["aiken.", Document::String(typ_name.to_string())]
|
||||||
|
} else {
|
||||||
|
Document::String([module, typ_name].join("."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use crate::builtins::{function, int};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn next_letter_test() {
|
||||||
|
let mut printer = Printer::new();
|
||||||
|
assert_eq!(printer.next_letter(), "a".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "b".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "c".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "d".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "e".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "f".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "g".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "h".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "i".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "j".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "k".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "l".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "m".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "n".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "o".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "p".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "q".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "r".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "s".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "t".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "u".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "v".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "w".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "x".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "y".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "z".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "aa".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ab".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ac".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ad".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ae".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "af".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ag".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ah".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ai".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "aj".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ak".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "al".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "am".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "an".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ao".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ap".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "aq".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ar".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "as".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "at".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "au".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "av".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "aw".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ax".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ay".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "az".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "ba".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bb".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bc".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bd".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "be".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bf".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bg".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bh".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bi".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bj".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bk".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bl".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bm".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bn".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bo".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bp".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bq".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "br".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bs".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bt".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bu".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bv".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bw".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bx".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "by".to_string());
|
||||||
|
assert_eq!(printer.next_letter(), "bz".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pretty_print_test() {
|
||||||
|
macro_rules! assert_string {
|
||||||
|
($src:expr, $typ:expr $(,)?) => {
|
||||||
|
let mut printer = Printer::new();
|
||||||
|
assert_eq!($typ.to_string(), printer.pretty_print(&$src, 0),);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_string!(
|
||||||
|
Type::App {
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
public: true,
|
||||||
|
args: vec![],
|
||||||
|
},
|
||||||
|
"Int",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
Type::App {
|
||||||
|
module: "".to_string(),
|
||||||
|
name: "Pair".to_string(),
|
||||||
|
public: true,
|
||||||
|
args: vec![
|
||||||
|
Arc::new(Type::App {
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
public: true,
|
||||||
|
args: vec![],
|
||||||
|
}),
|
||||||
|
Arc::new(Type::App {
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Bool".to_string(),
|
||||||
|
public: true,
|
||||||
|
args: vec![],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Pair(Int, Bool)",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
Type::Fn {
|
||||||
|
args: vec![
|
||||||
|
Arc::new(Type::App {
|
||||||
|
args: vec![],
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
public: true,
|
||||||
|
}),
|
||||||
|
Arc::new(Type::App {
|
||||||
|
args: vec![],
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Bool".to_string(),
|
||||||
|
public: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
ret: Arc::new(Type::App {
|
||||||
|
args: vec![],
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Bool".to_string(),
|
||||||
|
public: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
"fn(Int, Bool) -> Bool",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Link {
|
||||||
|
tipo: Arc::new(Type::App {
|
||||||
|
args: vec![],
|
||||||
|
module: "whatever".to_string(),
|
||||||
|
name: "Int".to_string(),
|
||||||
|
public: true,
|
||||||
|
}),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
"Int",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Unbound { id: 2231 })),
|
||||||
|
},
|
||||||
|
"a",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
function(
|
||||||
|
vec![Arc::new(Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Unbound { id: 78 })),
|
||||||
|
})],
|
||||||
|
Arc::new(Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Unbound { id: 2 })),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
"fn(a) -> b",
|
||||||
|
);
|
||||||
|
assert_string!(
|
||||||
|
function(
|
||||||
|
vec![Arc::new(Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Generic { id: 78 })),
|
||||||
|
})],
|
||||||
|
Arc::new(Type::Var {
|
||||||
|
tipo: Arc::new(RefCell::new(TypeVar::Generic { id: 2 })),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
"fn(a) -> b",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_test() {
|
||||||
|
assert_eq!(pretty_print(function(vec![], int())), "fn() -> Int");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
pretty_print(function(vec![int(), int(), int()], int())),
|
||||||
|
"fn(Int, Int, Int) -> Int"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pretty_print(typ: Arc<Type>) -> String {
|
||||||
|
Printer::new().pretty_print(&typ, 0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
pub type ScriptContext {
|
||||||
|
idk: Int
|
||||||
|
}
|
||||||
|
|
||||||
pub fn append(a: ByteArray, b: ByteArray) -> ByteArray {
|
pub fn append(a: ByteArray, b: ByteArray) -> ByteArray {
|
||||||
todo
|
todo
|
||||||
}
|
}
|
|
@ -9,7 +9,13 @@ pub type Redeemer {
|
||||||
Sell
|
Sell
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(datum: Datum, rdmr: Redeemer, ctx: ScriptContext) -> Bool {
|
pub fn validate(datum: Datum, rdmr: Redeemer, ctx: syntax.ScriptContext) -> Bool {
|
||||||
|
let thing = if True {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
"thing"
|
||||||
|
}
|
||||||
|
|
||||||
when rdmr is {
|
when rdmr is {
|
||||||
Buy -> True
|
Buy -> True
|
||||||
Sell -> datum.something == "Aiken"
|
Sell -> datum.something == "Aiken"
|
||||||
|
|
Loading…
Reference in New Issue