write more tests for reset/reuse

This commit is contained in:
Folkert 2020-07-28 01:13:49 +02:00
parent 95365959f2
commit eb793b2b44
5 changed files with 362 additions and 1 deletions

1
Cargo.lock generated
View file

@ -2222,6 +2222,7 @@ dependencies = [
"roc_solve", "roc_solve",
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"ven_pretty",
] ]
[[package]] [[package]]

View file

@ -146,6 +146,17 @@ impl fmt::Debug for Symbol {
} }
} }
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let module_id = self.module_id();
let ident_id = self.ident_id();
match ident_id {
IdentId(value) => write!(f, "{:?}.{:?}", module_id, value),
}
}
}
fn fallback_debug_fmt(symbol: Symbol, f: &mut fmt::Formatter) -> fmt::Result { fn fallback_debug_fmt(symbol: Symbol, f: &mut fmt::Formatter) -> fmt::Result {
let module_id = symbol.module_id(); let module_id = symbol.module_id();
let ident_id = symbol.ident_id(); let ident_id = symbol.ident_id();

View file

@ -13,6 +13,7 @@ roc_types = { path = "../types" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_unify = { path = "../unify" } roc_unify = { path = "../unify" }
roc_problem = { path = "../problem" } roc_problem = { path = "../problem" }
ven_pretty = { path = "../../vendor/pretty" }
bumpalo = { version = "3.2", features = ["collections"] } bumpalo = { version = "3.2", features = ["collections"] }
[dev-dependencies] [dev-dependencies]

View file

@ -11,6 +11,7 @@ use roc_problem::can::RuntimeError;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_types::subs::{Content, FlatType, Subs, Variable}; use roc_types::subs::{Content, FlatType, Subs, Variable};
use std::collections::HashMap; use std::collections::HashMap;
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PartialProc<'a> { pub struct PartialProc<'a> {
@ -799,7 +800,6 @@ pub enum MonoProblem {
PatternProblem(crate::pattern::Error), PatternProblem(crate::pattern::Error),
} }
#[allow(clippy::too_many_arguments)]
impl<'a> Expr<'a> { impl<'a> Expr<'a> {
pub fn new( pub fn new(
env: &mut Env<'a, '_>, env: &mut Env<'a, '_>,
@ -811,6 +811,156 @@ impl<'a> Expr<'a> {
let result = from_can(env, can_expr, procs, &mut layout_cache); let result = from_can(env, can_expr, procs, &mut layout_cache);
function_r(env, env.arena.alloc(result)) function_r(env, env.arena.alloc(result))
} }
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, parens: bool) -> DocBuilder<'b, D, A>
where
D: DocAllocator<'b, A>,
D::Doc: Clone,
A: Clone,
{
use Expr::*;
match self {
Int(lit) => alloc.text(format!("{}i64", lit)),
Float(lit) => alloc.text(format!("{}f64", lit)),
Bool(lit) => alloc.text(format!("{}", lit)),
Byte(lit) => alloc.text(format!("{}u8", lit)),
Str(lit) => alloc.text(format!("{:?}", lit)),
Load(symbol) if parens => alloc.text(format!("(Load {})", symbol)),
Load(symbol) => alloc.text(format!("Load {}", symbol)),
DecAfter(symbol, expr) => expr
.to_doc(alloc, false)
.append(alloc.hardline())
.append(alloc.text(format!("Dec {}", symbol))),
Reset(symbol, expr) => alloc
.text(format!("Reset {}", symbol))
.append(alloc.hardline())
.append(expr.to_doc(alloc, false)),
Reuse(symbol, expr) => alloc
.text(format!("Reuse {}", symbol))
.append(alloc.hardline())
.append(expr.to_doc(alloc, false)),
Store(stores, expr) => {
let doc_stores = stores
.iter()
.map(|(symbol, _, expr)| {
alloc
.text(format!("Store {}: ", symbol))
.append(expr.to_doc(alloc, false))
})
.collect::<std::vec::Vec<_>>();
alloc
.intersperse(doc_stores, alloc.hardline())
.append(alloc.hardline())
.append(expr.to_doc(alloc, false))
}
Cond {
branching_symbol,
pass,
fail,
..
} => alloc
.text(format!("if {} then", branching_symbol))
.append(alloc.hardline())
.append(pass.1.to_doc(alloc, false).indent(4))
.append(alloc.hardline())
.append(alloc.text("else"))
.append(alloc.hardline())
.append(fail.1.to_doc(alloc, false).indent(4)),
Switch {
cond_symbol,
branches,
default_branch,
..
} => {
let default_doc = alloc
.text("default:")
.append(alloc.hardline())
.append(default_branch.1.to_doc(alloc, false).indent(4))
.indent(4);
let branches_docs = branches
.iter()
.map(|(tag, _, expr)| {
alloc
.text(format!("case {}:", tag))
.append(alloc.hardline())
.append(expr.to_doc(alloc, false).indent(4))
.indent(4)
})
.chain(std::iter::once(default_doc));
//
alloc
.text(format!("switch {}:", cond_symbol))
.append(alloc.hardline())
.append(
alloc.intersperse(branches_docs, alloc.hardline().append(alloc.hardline())),
)
.append(alloc.hardline())
}
Struct(fields) => alloc.text(format!("Struct {:?}", fields)),
Tag {
tag_name,
arguments,
..
} => {
let doc_tag = match tag_name {
TagName::Global(s) => alloc.text(s.as_str()),
TagName::Private(s) => alloc.text(format!("{}", s)),
};
let doc_args = arguments.iter().map(|(expr, _)| expr.to_doc(alloc, true));
let it = std::iter::once(doc_tag).chain(doc_args);
alloc.intersperse(it, alloc.space())
}
AccessAtIndex { index, expr, .. } if parens => alloc
.text(format!("(Access @{} ", index))
.append(expr.to_doc(alloc, false))
.append(alloc.text(")")),
AccessAtIndex { index, expr, .. } => alloc
.text(format!("Access @{} ", index))
.append(expr.to_doc(alloc, false)),
RunLowLevel(instr, arguments) => {
let doc_tag = alloc.text(format!("Lowlevel.{:?}", instr));
let doc_args = arguments.iter().map(|(expr, _)| expr.to_doc(alloc, true));
let it = std::iter::once(doc_tag).chain(doc_args);
if parens {
alloc
.text("(")
.append(alloc.intersperse(it, alloc.space()))
.append(alloc.text(")"))
} else {
alloc.intersperse(it, alloc.space())
}
}
CallByName { name, .. } => alloc.text("*magic*"),
_ => todo!("not yet implemented: {:?}", self),
}
}
pub fn to_pretty(&self, width: usize) -> String {
let allocator = BoxAllocator;
let mut w = std::vec::Vec::new();
self.to_doc::<_, ()>(&allocator, false)
.1
.render(width, &mut w)
.unwrap();
w.push(b'\n');
String::from_utf8(w).unwrap()
}
} }
enum IntOrFloat { enum IntOrFloat {

View file

@ -1,6 +1,9 @@
#[macro_use] #[macro_use]
extern crate pretty_assertions; extern crate pretty_assertions;
#[macro_use]
extern crate indoc;
extern crate bumpalo; extern crate bumpalo;
extern crate roc_mono; extern crate roc_mono;
@ -80,6 +83,57 @@ mod test_mono {
assert_eq!(get_expected(interns), mono_expr); assert_eq!(get_expected(interns), mono_expr);
} }
fn compiles_to_string(src: &str, expected: &str) {
let arena = Bump::new();
let CanExprOut {
loc_expr,
var_store,
var,
constraint,
home,
mut interns,
..
} = can_expr(src);
let subs = Subs::new(var_store.into());
let mut unify_problems = Vec::new();
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
// Compile and add all the Procs before adding main
let mut procs = Procs::default();
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
// assume 64-bit pointers
let pointer_size = std::mem::size_of::<u64>() as u32;
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
let mut mono_problems = Vec::new();
let mut mono_env = roc_mono::expr::Env {
arena: &arena,
subs: &mut subs,
problems: &mut mono_problems,
home,
ident_ids: &mut ident_ids,
pointer_size,
jump_counter: arena.alloc(0),
};
let mono_expr = Expr::new(&mut mono_env, loc_expr.value, &mut procs);
let procs =
roc_mono::expr::specialize_all(&mut mono_env, procs, &mut LayoutCache::default());
assert_eq!(
procs.runtime_errors,
roc_collections::all::MutMap::default()
);
// Put this module's ident_ids back in the interns
interns.all_ident_ids.insert(home, ident_ids);
let result = mono_expr.to_pretty(200);
assert_eq!(result, expected);
}
#[test] #[test]
fn int_literal() { fn int_literal() {
compiles_to("5", Int(5)); compiles_to("5", Int(5));
@ -748,4 +802,148 @@ mod test_mono {
}, },
) )
} }
#[test]
fn simple_to_string() {
compiles_to_string(
r#"
x = 3
x
"#,
indoc!(
r#"
Store Test.0: 3i64
Load Test.0
Dec Test.0
"#
),
)
}
#[test]
fn if_to_string() {
compiles_to_string(
r#"
if True then 1 else 2
"#,
indoc!(
r#"
Store Test.0: true
if Test.0 then
1i64
else
2i64
"#
),
)
}
#[test]
fn maybe_map_to_string() {
compiles_to_string(
r#"
maybe : [ Nothing, Just Int ]
maybe = Just 3
when maybe is
Just x -> Just (x + 1)
Nothing -> Nothing
"#,
indoc!(
r#"
Store Test.0: Just 0i64 3i64
Store Test.0: Load Test.0
Store Test.2: Lowlevel.And (Lowlevel.Eq 0i64 (Access @0 Load Test.0)) true
if Test.2 then
Reset Test.0
Reuse Test.0
Just 0i64 *magic*
else
Reset Test.0
Reuse Test.0
Nothing 1i64
Dec Test.0
"#
),
)
}
#[test]
fn very_maybe_map_to_string() {
compiles_to_string(
r#"
Maybe a : [ Nothing, Just a ]
veryMaybe : Maybe (Maybe Int)
veryMaybe = Just (Just 3)
when veryMaybe is
Just (Just _) -> Just (Just 1)
Just Nothing -> Just Nothing
Nothing -> Nothing
"#,
indoc!(
r#"
Store Test.1: Just 0i64 Just 0i64 3i64
Store Test.1: Load Test.1
Store Test.5: Lowlevel.And (Lowlevel.Eq 0i64 (Access @0 Load Test.1)) true
if Test.5 then
if Test.4 then
Just 0i64 Just 0i64 1i64
else
Just 0i64 Nothing 1i64
else
Reset Test.1
Reuse Test.1
Nothing 1i64
Dec Test.1
"#
),
)
}
#[test]
fn these_map_to_string() {
compiles_to_string(
r#"
These a b : [ This a, That b, These a b ]
these : These Int Int
these = These 1 2
when these is
This a -> This a
That b -> That b
These a b -> These b a
"#,
indoc!(
r#"
Store Test.1: These 1i64 1i64 2i64
Store Test.1: Load Test.1
switch Test.1:
case 2:
Reset Test.1
Reuse Test.1
This 2i64 (Load Test.2)
case 0:
Reset Test.1
Reuse Test.1
That 0i64 (Load Test.3)
default:
Reset Test.1
Reuse Test.1
These 1i64 (Load Test.5) (Load Test.4)
Dec Test.1
"#
),
)
}
} }