mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
write more tests for reset/reuse
This commit is contained in:
parent
95365959f2
commit
eb793b2b44
5 changed files with 362 additions and 1 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2222,6 +2222,7 @@ dependencies = [
|
|||
"roc_solve",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -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 {
|
||||
let module_id = symbol.module_id();
|
||||
let ident_id = symbol.ident_id();
|
||||
|
|
|
@ -13,6 +13,7 @@ roc_types = { path = "../types" }
|
|||
roc_can = { path = "../can" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_problem = { path = "../problem" }
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -11,6 +11,7 @@ use roc_problem::can::RuntimeError;
|
|||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
use std::collections::HashMap;
|
||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PartialProc<'a> {
|
||||
|
@ -799,7 +800,6 @@ pub enum MonoProblem {
|
|||
PatternProblem(crate::pattern::Error),
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
impl<'a> Expr<'a> {
|
||||
pub fn new(
|
||||
env: &mut Env<'a, '_>,
|
||||
|
@ -811,6 +811,156 @@ impl<'a> Expr<'a> {
|
|||
let result = from_can(env, can_expr, procs, &mut layout_cache);
|
||||
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 {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
extern crate bumpalo;
|
||||
extern crate roc_mono;
|
||||
|
||||
|
@ -80,6 +83,57 @@ mod test_mono {
|
|||
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]
|
||||
fn int_literal() {
|
||||
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
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue