Repository: tweag/nickel
Pull request #519 (2021-12-28 21:49:22)

Fix lists.lowercase docstring
It was mentioning `uppercase` in the example instead of `lowercase`,
likely due to a bad copy/paste.


Patch:
diff --git a/src/eval.rs b/src/eval.rs
index fde94d63..21aaeb2e 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -648,11 +648,7 @@ fn should_update(t: &Term) -> bool {
 
 /// Evaluate a Nickel term. Wrapper around [eval_closure](fn.eval_closure.html) that starts from an
 /// empty local environment and drops the final environment.
-pub fn eval<R>(
-    t0: RichTerm,
-    global_env: &Environment,
-    resolver: &mut R,
-) -> Result<RichTerm, EvalError>
+pub fn eval<R>(t0: RichTerm, global_env: &Environment, resolver: &mut R) -> Result<Term, EvalError>
 where
     R: ImportResolver,
 {
@@ -664,7 +660,7 @@ pub fn eval_full<R>(
     t0: RichTerm,
     global_env: &Environment,
     resolver: &mut R,
-) -> Result<RichTerm, EvalError>
+) -> Result<Term, EvalError>
 where
     R: ImportResolver,
 {
@@ -681,7 +677,7 @@ where
         ),
     );
     eval_closure(Closure::atomic_closure(wrapper), global_env, resolver, true)
-        .map(|(term, env)| subst(term, global_env, &env))
+        .map(|(term, env)| subst(term.into(), global_env, &env).into())
 }
 
 /// Evaluate a Nickel Term, stopping when a meta value is encountered at the top-level without
@@ -693,26 +689,27 @@ pub fn eval_meta<R>(
     t: RichTerm,
     global_env: &Environment,
     resolver: &mut R,
-) -> Result<RichTerm, EvalError>
+) -> Result<Term, EvalError>
 where
     R: ImportResolver,
 {
-    let (mut rt, env) = eval_closure(Closure::atomic_closure(t), global_env, resolver, false)?;
+    let (term, env) = eval_closure(Closure::atomic_closure(t), global_env, resolver, false)?;
 
-    match *rt.term {
-        Term::MetaValue(ref mut meta) => {
+    match term {
+        Term::MetaValue(mut meta) => {
             if let Some(t) = meta.value.take() {
+                let pos = t.pos;
                 let (evaluated, env) =
                     eval_closure(Closure { body: t, env }, global_env, resolver, true)?;
-                let substituted = subst(evaluated, global_env, &env);
+                let substituted = subst(RichTerm::new(evaluated, pos), global_env, &env);
 
-                meta.value = Some(substituted);
+                meta.value.replace(substituted);
             }
-        }
-        _ => (),
-    };
 
-    Ok(rt)
+            Ok(Term::MetaValue(meta))
+        }
+        term => Ok(term),
+    }
 }
 
 /// The main loop of evaluation.
@@ -741,7 +738,7 @@ pub fn eval_closure<R>(
     global_env: &Environment,
     resolver: &mut R,
     mut enriched_strict: bool,
-) -> Result<(RichTerm, Environment), EvalError>
+) -> Result<(Term, Environment), EvalError>
 where
     R: ImportResolver,
 {
@@ -1094,7 +1091,7 @@ where
                     env.insert(x, thunk);
                     Closure { body: t, env }
                 } else {
-                    return Ok((RichTerm::new(Term::Fun(x, t), pos), env));
+                    return Ok((Term::Fun(x, t), env));
                 }
             }
             // Otherwise, this is either an ill-formed application, or we are done
@@ -1109,7 +1106,7 @@ where
                         pos_app,
                     ));
                 } else {
-                    return Ok((RichTerm::new(t, pos), env));
+                    return Ok((t, env));
                 }
             }
         }
@@ -1339,7 +1336,7 @@ mod tests {
 
     /// Evaluate a term without import support.
     fn eval_no_import(t: RichTerm) -> Result<Term, EvalError> {
-        eval(t, &Environment::new(), &mut DummyResolver {}).map(Term::from)
+        eval(t, &Environment::new(), &mut DummyResolver {})
     }
 
     fn parse(s: &str) -> Option<RichTerm> {
@@ -1347,7 +1344,10 @@ mod tests {
 
         grammar::TermParser::new()
             .parse_term(id, lexer::Lexer::new(&s))
-            .map(RichTerm::without_pos)
+            .map(|mut t| {
+                t.clean_pos();
+                t
+            })
             .map_err(|err| println!("{:?}", err))
             .ok()
     }
@@ -1525,7 +1525,6 @@ mod tests {
                 &Environment::new(),
                 &mut resolver
             )
-            .map(Term::from)
             .unwrap(),
             Term::Num(2.0)
         );
@@ -1543,7 +1542,6 @@ mod tests {
                 &Environment::new(),
                 &mut resolver
             )
-            .map(Term::from)
             .unwrap(),
             Term::Bool(true)
         );
@@ -1625,23 +1623,14 @@ mod tests {
         );
 
         let t = mk_term::let_in("x", Term::Num(2.0), mk_term::var("x"));
-        assert_eq!(
-            eval(t, &global_env, &mut resolver).map(Term::from),
-            Ok(Term::Num(2.0))
-        );
+        assert_eq!(eval(t, &global_env, &mut resolver), Ok(Term::Num(2.0)));
 
         let t = mk_term::let_in("x", Term::Num(2.0), mk_term::var("g"));
-        assert_eq!(
-            eval(t, &global_env, &mut resolver).map(Term::from),
-            Ok(Term::Num(1.0))
-        );
+        assert_eq!(eval(t, &global_env, &mut resolver), Ok(Term::Num(1.0)));
 
         // Shadowing of global environment
         let t = mk_term::let_in("g", Term::Num(2.0), mk_term::var("g"));
-        assert_eq!(
-            eval(t, &global_env, &mut resolver).map(Term::from),
-            Ok(Term::Num(2.0))
-        );
+        assert_eq!(eval(t, &global_env, &mut resolver), Ok(Term::Num(2.0)));
     }
 
     fn mk_env(bindings: Vec<(&str, RichTerm)>) -> Environment {
diff --git a/src/program.rs b/src/program.rs
index 5050b827..77c93ddb 100644
--- a/src/program.rs
+++ b/src/program.rs
@@ -78,13 +78,13 @@ impl Program {
     }
 
     /// Parse if necessary, typecheck and then evaluate the program.
-    pub fn eval(&mut self) -> Result<RichTerm, Error> {
+    pub fn eval(&mut self) -> Result<Term, Error> {
         let (t, global_env) = self.prepare_eval()?;
         eval::eval(t, &global_env, &mut self.cache).map_err(|e| e.into())
     }
 
     /// Same as `eval`, but proceeds to a full evaluation.
-    pub fn eval_full(&mut self) -> Result<RichTerm, Error> {
+    pub fn eval_full(&mut self) -> Result<Term, Error> {
         let (t, global_env) = self.prepare_eval()?;
         eval::eval_full(t, &global_env, &mut self.cache).map_err(|e| e.into())
     }
@@ -167,7 +167,7 @@ pub fn query(
         cache.get_owned(file_id).unwrap()
     };
 
-    Ok(eval::eval_meta(t, &global_env.eval_env, cache)?.into())
+    Ok(eval::eval_meta(t, &global_env.eval_env, cache)?)
 }
 
 /// Pretty-print an error.
@@ -203,7 +203,6 @@ mod tests {
     use crate::error::EvalError;
     use crate::parser::{grammar, lexer};
     use crate::position::TermPos;
-    use crate::term::Term;
     use codespan::Files;
     use std::io::Cursor;
 
@@ -212,12 +211,15 @@ mod tests {
 
         grammar::TermParser::new()
             .parse_term(id, lexer::Lexer::new(&s))
-            .map(RichTerm::without_pos)
+            .map(|mut t| {
+                t.clean_pos();
+                t
+            })
             .map_err(|err| println!("{:?}", err))
             .ok()
     }
 
-    fn eval_full(s: &str) -> Result<RichTerm, Error> {
+    fn eval_full(s: &str) -> Result<Term, Error> {
         let src = Cursor::new(s);
 
         let mut p = Program::new_from_source(src, "<test>").map_err(|io_err| {
@@ -234,7 +236,14 @@ mod tests {
         use crate::mk_record;
         use crate::term::make as mk_term;
 
-        let t = eval_full("[(1 + 1), (\"a\" ++ \"b\"), ([ 1, [1 + 2] ])]").unwrap();
+        // Clean all the position information in a term.
+        fn clean_pos(t: Term) -> Term {
+            let mut tmp = RichTerm::new(t, TermPos::None);
+            tmp.clean_pos();
+            *tmp.term
+        }
+
+        let t = clean_pos(eval_full("[(1 + 1), (\"a\" ++ \"b\"), ([ 1, [1 + 2] ])]").unwrap());
         let mut expd = parse("[2, \"ab\", [1, [3]]]").unwrap();
 
         // String are parsed as StrChunks, but evaluated to Str, so we need to hack list a bit
@@ -244,15 +253,17 @@ mod tests {
             panic!();
         }
 
-        assert_eq!(t.without_pos(), expd.without_pos());
+        assert_eq!(t, *expd.term);
 
-        let t = eval_full("let x = 1 in let y = 1 + x in let z = {foo.bar.baz = y} in z").unwrap();
+        let t = clean_pos(
+            eval_full("let x = 1 in let y = 1 + x in let z = {foo.bar.baz = y} in z").unwrap(),
+        );
         // Records are parsed as RecRecords, so we need to build one by hand
         let expd = mk_record!((
             "foo",
             mk_record!(("bar", mk_record!(("baz", Term::Num(2.0)))))
         ));
-        assert_eq!(t.without_pos(), expd);
+        assert_eq!(t, *expd.term);
 
         // /!\ [MAY OVERFLOW STACK]
         // Check that substitution do not replace bound variables. Before the fixing commit, this
diff --git a/src/repl/mod.rs b/src/repl/mod.rs
index 8dd12eca..f30f9cc2 100644
--- a/src/repl/mod.rs
+++ b/src/repl/mod.rs
@@ -37,13 +37,13 @@ pub mod wasm_frontend;
 /// Result of the evaluation of an input.
 pub enum EvalResult {
     /// The input has been evaluated to a term.
-    Evaluated(RichTerm),
+    Evaluated(Term),
     /// The input was a toplevel let, which has been bound in the environment.
     Bound(Ident),
 }
 
-impl From<RichTerm> for EvalResult {
-    fn from(t: RichTerm) -> Self {
+impl From<Term> for EvalResult {
+    fn from(t: Term) -> Self {
         EvalResult::Evaluated(t)
     }
 }
diff --git a/src/repl/rustyline_frontend.rs b/src/repl/rustyline_frontend.rs
index 7d424fb7..b4ef1883 100644
--- a/src/repl/rustyline_frontend.rs
+++ b/src/repl/rustyline_frontend.rs
@@ -78,7 +78,7 @@ pub fn repl(histfile: PathBuf) -> Result<(), InitError> {
                     }),
                     Ok(Command::Print(exp)) => {
                         match repl.eval_full(&exp) {
-                            Ok(EvalResult::Evaluated(rt)) => println!("{}\n", rt.as_ref().deep_repr()),
+                            Ok(EvalResult::Evaluated(t)) => println!("{}\n", t.deep_repr()),
                             Ok(EvalResult::Bound(_)) => (),
                             Err(err) => program::report(repl.cache_mut(), err),
                         };
@@ -103,7 +103,7 @@ pub fn repl(histfile: PathBuf) -> Result<(), InitError> {
             }
             Ok(line) => {
                 match repl.eval(&line) {
-                    Ok(EvalResult::Evaluated(rt)) => println!("{}\n", rt.as_ref().shallow_repr()),
+                    Ok(EvalResult::Evaluated(t)) => println!("{}\n", t.shallow_repr()),
                     Ok(EvalResult::Bound(_)) => (),
                     Err(err) => program::report(repl.cache_mut(), err),
                 };
diff --git a/src/repl/simple_frontend.rs b/src/repl/simple_frontend.rs
index de6522f7..f3cf5419 100644
--- a/src/repl/simple_frontend.rs
+++ b/src/repl/simple_frontend.rs
@@ -65,7 +65,7 @@ pub fn input<R: Repl>(repl: &mut R, line: &str) -> Result<InputResult, InputErro
             Ok(Command::Print(exp)) => repl
                 .eval_full(&exp)
                 .map(|res| match res {
-                    EvalResult::Evaluated(rt) => InputResult::Success(rt.as_ref().deep_repr()),
+                    EvalResult::Evaluated(t) => InputResult::Success(t.deep_repr()),
                     EvalResult::Bound(_) => InputResult::Blank,
                 })
                 .map_err(InputError::from),
@@ -82,9 +82,7 @@ pub fn input<R: Repl>(repl: &mut R, line: &str) -> Result<InputResult, InputErro
     } else {
         repl.eval(&line)
             .map(|eval_res| match eval_res {
-                EvalResult::Evaluated(rt) => {
-                    InputResult::Success(format!("{}\n", rt.as_ref().shallow_repr()))
-                }
+                EvalResult::Evaluated(t) => InputResult::Success(format!("{}\n", t.shallow_repr())),
                 EvalResult::Bound(_) => InputResult::Success(String::new()),
             })
             .map_err(InputError::from)
diff --git a/src/serialize.rs b/src/serialize.rs
index 25e4386e..7cfd7713 100644
--- a/src/serialize.rs
+++ b/src/serialize.rs
@@ -299,8 +299,7 @@ mod tests {
                     mk_term::op2(BinaryOp::Eq(), from_json, evaluated.clone()),
                     &Environment::new(),
                     &mut $crate::cache::resolvers::DummyResolver {}
-                )
-                .map(Term::from),
+                ),
                 Ok(Term::Bool(true))
             );
             assert_eq!(
@@ -308,8 +307,7 @@ mod tests {
                     mk_term::op2(BinaryOp::Eq(), from_yaml, evaluated.clone()),
                     &Environment::new(),
                     &mut $crate::cache::resolvers::DummyResolver {}
-                )
-                .map(Term::from),
+                ),
                 Ok(Term::Bool(true))
             );
             assert_eq!(
@@ -317,8 +315,7 @@ mod tests {
                     mk_term::op2(BinaryOp::Eq(), from_toml, evaluated),
                     &Environment::new(),
                     &mut $crate::cache::resolvers::DummyResolver {}
-                )
-                .map(Term::from),
+                ),
                 Ok(Term::Bool(true))
             );
         };
diff --git a/src/term.rs b/src/term.rs
index ee6982d9..ef04c4d8 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -865,15 +865,10 @@ impl RichTerm {
     ///
     /// It allows to use rust `Eq` trait to compare the values of the underlying terms.
     #[cfg(test)]
-    pub fn without_pos(mut self) -> Self {
-        fn clean_pos(rt: &mut RichTerm) {
-            rt.pos = TermPos::None;
-            rt.term
-                .apply_to_rich_terms(|rt: &mut RichTerm| clean_pos(rt));
-        }
-
-        clean_pos(&mut self);
-        self
+    pub fn clean_pos(&mut self) {
+        self.pos = TermPos::None;
+        self.term
+            .apply_to_rich_terms(|rt: &mut Self| rt.clean_pos());
     }
 
     /// Set the position and return the term updated.
diff --git a/stdlib/strings.ncl b/stdlib/strings.ncl
index c95c8ecf..b7b2aa84 100644
--- a/stdlib/strings.ncl
+++ b/stdlib/strings.ncl
@@ -233,11 +233,11 @@
 
       For example:
       ```nickel
-        uppercase "A" =>
+        lowercase "A" =>
           "a"
-        uppercase "Æ" =>
+        lowercase "Æ" =>
           "æ"
-        uppercase "." =>
+        lowercase "." =>
           "."
       ```
       "#m


Test patch:
diff --git a/src/parser/tests.rs b/src/parser/tests.rs
index 61b23ab1..e2a5a6c6 100644
--- a/src/parser/tests.rs
+++ b/src/parser/tests.rs
@@ -18,7 +18,9 @@ fn parse(s: &str) -> Result<RichTerm, ParseError> {
 }
 
 fn parse_without_pos(s: &str) -> RichTerm {
-    parse(s).unwrap().without_pos()
+    let mut result = parse(s).unwrap();
+    result.clean_pos();
+    result
 }
 
 fn lex(s: &str) -> Result<Vec<(usize, Token, usize)>, InternalParseError> {
diff --git a/tests/common.rs b/tests/common.rs
index ffdcbbc8..6b67d04e 100644
--- a/tests/common.rs
+++ b/tests/common.rs
@@ -5,11 +5,11 @@ use std::io::Cursor;
 
 pub fn eval(s: impl std::string::ToString) -> Result<Term, Error> {
     let mut p = Program::new_from_source(Cursor::new(s.to_string()), "test").unwrap();
-    p.eval().map(Term::from)
+    p.eval()
 }
 
 pub fn eval_file(f: &str) -> Result<Term, Error> {
     let mut p =
         Program::new_from_file(format!("{}/tests/{}", env!("CARGO_MANIFEST_DIR"), f)).unwrap();
-    p.eval().map(Term::from)
+    p.eval()
 }
diff --git a/tests/examples.rs b/tests/examples.rs
index 4b5ed2e7..b36a956d 100644
--- a/tests/examples.rs
+++ b/tests/examples.rs
@@ -1,10 +1,10 @@
 use assert_matches::assert_matches;
 use nickel::error::{Error, EvalError};
 use nickel::program::Program;
-use nickel::term::RichTerm;
+use nickel::term::Term;
 use std::path::PathBuf;
 
-fn eval_file(file: &str) -> Result<RichTerm, Error> {
+fn eval_file(file: &str) -> Result<Term, Error> {
     let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
     path.push(format!("examples/{}", file));
     let mut p = Program::new_from_file(path).expect("could not load file as a program");
diff --git a/tests/imports.rs b/tests/imports.rs
index 34a2ba70..19aa3ba5 100644
--- a/tests/imports.rs
+++ b/tests/imports.rs
@@ -21,7 +21,7 @@ fn nested() {
         "should_be = 3",
     )
     .unwrap();
-    assert_eq!(prog.eval().map(Term::from), Ok(Term::Num(3.)));
+    assert_eq!(prog.eval(), Ok(Term::Num(3.)));
 }
 
 #[test]
@@ -31,7 +31,7 @@ fn root_path() {
         "should_be = 44",
     )
     .unwrap();
-    assert_eq!(prog.eval().map(Term::from), Ok(Term::Num(44.)));
+    assert_eq!(prog.eval(), Ok(Term::Num(44.)));
 }
 
 #[test]
@@ -41,7 +41,7 @@ fn multi_imports() {
         "should_be = 5",
     )
     .unwrap();
-    assert_eq!(prog.eval().map(Term::from), Ok(Term::Num(5.)));
+    assert_eq!(prog.eval(), Ok(Term::Num(5.)));
 }
 
 #[test]
@@ -97,7 +97,7 @@ fn serialize() {
         "shoud success",
     )
     .unwrap();
-    assert_eq!(prog.eval().map(Term::from), Ok(Term::Bool(true)));
+    assert_eq!(prog.eval(), Ok(Term::Bool(true)));
 }
 
 #[test]
@@ -107,8 +107,5 @@ fn circular_imports_fail() {
         "should_fail",
     )
     .unwrap();
-    assert_matches!(
-        prog.eval().map(Term::from),
-        Ok(Term::RecRecord(..)) | Ok(Term::Record(..))
-    );
+    assert_matches!(prog.eval(), Ok(Term::RecRecord(..)) | Ok(Term::Record(..)));
 }
diff --git a/tests/pass.rs b/tests/pass.rs
index df1970cd..fd30db70 100644
--- a/tests/pass.rs
+++ b/tests/pass.rs
@@ -12,7 +12,7 @@ fn run(path: impl Into<OsString>) {
     let path = path.into();
     let mut p = Program::new_from_file(path.clone()).expect("could not load file as a program");
     assert_eq!(
-        p.eval().map(Term::from),
+        p.eval(),
         Ok(Term::Bool(true)),
         "error evaluating {}",
         path.to_string_lossy(),