mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 08:20:47 +00:00
1332 lines
33 KiB
C++
1332 lines
33 KiB
C++
/*!
|
|
* @file test_goos.cpp
|
|
* Tests for the GOOS macro language.
|
|
*/
|
|
|
|
#include "common/goos/Interpreter.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace goos;
|
|
|
|
namespace {
|
|
// helper to evaluate a string as a goos expression.
|
|
std::string e(Interpreter& interp, const std::string& in) {
|
|
return interp.eval(interp.reader.read_from_string(in), interp.global_environment.as_env_ptr())
|
|
.print();
|
|
}
|
|
} // namespace
|
|
|
|
TEST(GoosBuiltins, Begin) {
|
|
Interpreter i;
|
|
// begin returns the last thing in the list
|
|
EXPECT_EQ(e(i, "(begin 1 2)"), "2");
|
|
// begin with nothing returns the empty list
|
|
EXPECT_EQ(e(i, "(begin)"), "()");
|
|
|
|
e(i, "(begin (define x 123) 3)");
|
|
EXPECT_EQ(e(i, "x"), "123");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(begin :test 1)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Read) {
|
|
Interpreter i;
|
|
// test that we can read a string into the reader at runtime.
|
|
EXPECT_EQ(e(i, "(read \"1\")"), "(top-level 1)");
|
|
|
|
i.disable_printfs();
|
|
// check bad expressions.
|
|
for (auto x :
|
|
{"(read 1)", "(read)", R"((read "a" "b"))", "(read :a b \"a\")", "(read \"(((((\")"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, ReadFile) {
|
|
Interpreter i;
|
|
// check that we can read a file.
|
|
EXPECT_EQ(e(i, "(read-file \"test/test_data/test_reader_file0.gc\")"), "(top-level (1 2 3 4))");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(read-file 1)", "(read-file)", "(read-file \"goal/test/not_a_real_file.gc\")"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, LoadFile) {
|
|
Interpreter i;
|
|
// check that we can read and execute a file.
|
|
e(i, "(load-file \"test/test_data/test_goos_file0.gs\")");
|
|
EXPECT_EQ(e(i, "x"), "23");
|
|
i.disable_printfs();
|
|
for (auto x : {"(load-file 1)", "(load-file)", "(load-file \"goal/test/not_a_real_file.gc\")"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, PrintAndInspect) {
|
|
Interpreter i;
|
|
i.disable_printfs();
|
|
// check that we can print/inspect (doesn't check result, just sees that it doesn't crash)
|
|
EXPECT_EQ(e(i, "(print 10)"), "()");
|
|
EXPECT_EQ(e(i, "(inspect 10)"), "()");
|
|
for (auto x : {"(print 1 2)", "(print)", "(print 'a :a b)", "(print :a b)", "(inspect 1 2)",
|
|
"(inspect)", "(inspect 'a :a b)", "(inspect :a b)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
// checks if two things are equal by running them through the GOOS eq? form.
|
|
bool eq(Interpreter& i, const std::string& a, const std::string& b) {
|
|
return e(i, "(eq? " + a + " " + " " + b + ")") == "#t";
|
|
}
|
|
} // namespace
|
|
|
|
TEST(GoosBuiltins, Equality) {
|
|
Interpreter i;
|
|
|
|
// STRING
|
|
EXPECT_TRUE(eq(i, "\"asdf\"", "\"asdf\""));
|
|
EXPECT_FALSE(eq(i, "\"asdgf\"", "\"asdf\""));
|
|
|
|
// INTEGER
|
|
EXPECT_TRUE(eq(i, "123", "0123"));
|
|
EXPECT_FALSE(eq(i, "123", "10123"));
|
|
|
|
// FLOAT
|
|
EXPECT_TRUE(eq(i, "1.23", "01.23"));
|
|
EXPECT_FALSE(eq(i, "1.23", "10.123"));
|
|
|
|
// CHAR
|
|
EXPECT_TRUE(eq(i, "#\\a", "#\\a"));
|
|
EXPECT_FALSE(eq(i, "#\\b", "#\\a"));
|
|
|
|
// SYMBOL
|
|
EXPECT_TRUE(eq(i, "'a", "'a"));
|
|
EXPECT_FALSE(eq(i, "'a", "'b"));
|
|
|
|
// ENV
|
|
EXPECT_TRUE(eq(i, "*global-env*", "*global-env*"));
|
|
EXPECT_FALSE(eq(i, "*global-env*", "*goal-env*"));
|
|
|
|
// LAMBDA
|
|
e(i, "(desfun test-fun-1 () 2)");
|
|
e(i, "(desfun test-fun-2 () 3)");
|
|
e(i, "(define test-fun-3 test-fun-1)");
|
|
|
|
EXPECT_TRUE(eq(i, "test-fun-1", "test-fun-3"));
|
|
EXPECT_FALSE(eq(i, "test-fun-1", "test-fun-2"));
|
|
|
|
// MACRO
|
|
e(i, "(defsmacro test-mac-1 () 2)");
|
|
e(i, "(defsmacro test-mac-2 () 3)");
|
|
e(i, "(define test-mac-3 test-mac-1)");
|
|
|
|
EXPECT_TRUE(eq(i, "test-mac-1", "test-mac-3"));
|
|
EXPECT_FALSE(eq(i, "test-mac-1", "test-mac-2"));
|
|
|
|
// EMPTY LIST
|
|
EXPECT_TRUE(eq(i, "'()", "'()"));
|
|
|
|
// PAIR
|
|
EXPECT_TRUE(eq(i, "'(1 2 3)", "'(1 2 3)"));
|
|
EXPECT_FALSE(eq(i, "'(1 2 3)", "'(1 2 4)"));
|
|
|
|
// ARRAY
|
|
EXPECT_TRUE(eq(i, "'#(1 2 3)", "'#(1 2 3)"));
|
|
EXPECT_FALSE(eq(i, "'#(1 2 3)", "'#(1 2 4)"));
|
|
EXPECT_FALSE(eq(i, "'#(1 2 3)", "'#(1 2 3 4)"));
|
|
EXPECT_FALSE(eq(i, "'#(1 2 3 4 5)", "'#(1 2 3 4)"));
|
|
i.disable_printfs();
|
|
for (auto x : {"(eq?)", "(eq? 1)", "(eq? 1 2 3)", "(eq? 1 2 :keyword 2)", "(eq? 1 :keyword 2)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Addition) {
|
|
Interpreter i;
|
|
// single element adding
|
|
EXPECT_EQ(e(i, "(+ 1)"), "1");
|
|
EXPECT_EQ(e(i, "(+ 1.)"), "1.0");
|
|
|
|
// two element adding
|
|
EXPECT_EQ(e(i, "(+ 1 2)"), "3");
|
|
EXPECT_EQ(e(i, "(+ 1.1 2.2)"), "3.3");
|
|
|
|
// mixed
|
|
EXPECT_EQ(e(i, "(+ 1 1.1)"), "2");
|
|
EXPECT_EQ(e(i, "(+ 1.1 1)"), "2.1");
|
|
|
|
// many, and check rounding happens at the right time
|
|
EXPECT_EQ(e(i, "(+ 1 1.4 1.4 1.4 1.4)"), "5");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(+)", "(+ 'a)", "(+ #\\a)", "(+ 1 :test 2)", "(+ 1 2 :test 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Multiplication) {
|
|
Interpreter i;
|
|
// single element adding
|
|
EXPECT_EQ(e(i, "(* 2)"), "2");
|
|
EXPECT_EQ(e(i, "(* 2.)"), "2.0");
|
|
|
|
// two element adding
|
|
EXPECT_EQ(e(i, "(* 3 2)"), "6");
|
|
EXPECT_EQ(e(i, "(* 1.1 2.2)"), "2.42");
|
|
|
|
// mixed
|
|
EXPECT_EQ(e(i, "(* 1 1.1)"), "1");
|
|
EXPECT_EQ(e(i, "(* 1.1 1)"), "1.1");
|
|
|
|
// many, and check rounding happens at the right time
|
|
EXPECT_EQ(e(i, "(* 3 1.4 1.4 1.4 1.4)"), "3");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(*)", "(* 'a)", "(* #\\a)", "(* 1 :test 2)", "(* 1 2 :test 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Subtraction) {
|
|
Interpreter i;
|
|
// single element adding
|
|
EXPECT_EQ(e(i, "(- 2)"), "-2");
|
|
EXPECT_EQ(e(i, "(- 2.)"), "-2.0");
|
|
|
|
// two element adding
|
|
EXPECT_EQ(e(i, "(- 3 2)"), "1");
|
|
EXPECT_EQ(e(i, "(- 1.1 2.2)"), "-1.1");
|
|
|
|
// mixed
|
|
EXPECT_EQ(e(i, "(- 1 1.1)"), "0");
|
|
EXPECT_EQ(e(i, "(- 1.1 1)"), "0.1");
|
|
|
|
// many, and check rounding happens at the right time
|
|
EXPECT_EQ(e(i, "(- 3 1.4 1.4 1.4 1.4)"), "-1");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(-)", "(- 'a)", "(- #\\a)", "(- 1 :test 2)", "(- 1 2 :test 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Division) {
|
|
Interpreter i;
|
|
|
|
// two element adding
|
|
EXPECT_EQ(e(i, "(/ 16 2)"), "8");
|
|
EXPECT_EQ(e(i, "(/ 9. 2.)"), "4.5");
|
|
|
|
// mixed
|
|
EXPECT_EQ(e(i, "(/ 3 2.)"), "1");
|
|
EXPECT_EQ(e(i, "(/ 3. 2)"), "1.5");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(/ 1)", "(/ 1.0)", "(/)", "(/ 'a)", "(/ #\\a)", "(/ 1 :test 2)",
|
|
"(/ 1 2 :test 3)", "(/ 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, NumberEquals) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(= 16 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(= 2 2)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(= 16.3 2.4)"), "#f");
|
|
EXPECT_EQ(e(i, "(= 2.2 2.2)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(= 2.0 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(= 2 2.2)"), "#t");
|
|
EXPECT_EQ(e(i, "(= 2.2 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(= 2 3.2)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(= 2 2 2 2 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(= 2 2 2 2.2 2.3)"), "#t");
|
|
EXPECT_EQ(e(i, "(= 2 2 2 2.2 3.0)"), "#f");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(= 1)", "(=)", "(= a 1)", "(= 'a 1)", "(= 1 2 :test 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, NumberLessThan) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(< 16 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(< 2 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(< 1 2)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(< 16.8 16.2)"), "#f");
|
|
EXPECT_EQ(e(i, "(< 2.2 2.2)"), "#f");
|
|
EXPECT_EQ(e(i, "(< 1.9 2.1)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(< 1.1 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(< 1 2.1)"), "#t");
|
|
EXPECT_EQ(e(i, "(< 2.2 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(< 2 2.2)"), "#f");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(< 1)", "(<)", "(< a 1)", "(< 'a 1)", "(< 1 2 :test 3)", "(< 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, NumberLessThanEqual) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(<= 16 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(<= 2 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(<= 1 2)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(<= 16.8 16.2)"), "#f");
|
|
EXPECT_EQ(e(i, "(<= 2.2 2.2)"), "#t");
|
|
EXPECT_EQ(e(i, "(<= 1.9 2.1)"), "#t");
|
|
|
|
EXPECT_EQ(e(i, "(<= 1.1 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(<= 1 2.1)"), "#t");
|
|
EXPECT_EQ(e(i, "(<= 2.2 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(<= 2 2.2)"), "#t");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(<= 1)", "(<=)", "(<= a 1)", "(<= 'a 1)", "(<= 1 2 :test 3)", "(<= 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, NumberGreaterThan) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(> 16 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(> 2 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(> 1 2)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(> 16.8 16.2)"), "#t");
|
|
EXPECT_EQ(e(i, "(> 2.2 2.2)"), "#f");
|
|
EXPECT_EQ(e(i, "(> 1.9 2.1)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(> 1.1 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(> 1 2.1)"), "#f");
|
|
EXPECT_EQ(e(i, "(> 2.2 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(> 2 2.2)"), "#f");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(> 1)", "(>)", "(> a 1)", "(> 'a 1)", "(> 1 2 :test 3)", "(> 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, NumberGreaterThanEqual) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(>= 16 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(>= 2 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(>= 1 2)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(>= 16.8 16.2)"), "#t");
|
|
EXPECT_EQ(e(i, "(>= 2.2 2.2)"), "#t");
|
|
EXPECT_EQ(e(i, "(>= 1.9 2.1)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(>= 1.1 2)"), "#f");
|
|
EXPECT_EQ(e(i, "(>= 1 2.1)"), "#f");
|
|
EXPECT_EQ(e(i, "(>= 2.2 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(>= 2 2.2)"), "#t");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(>= 1)", "(>=)", "(>= a 1)", "(>= 'a 1)", "(>= 1 2 :test 3)", "(>= 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Eval) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(eval '(+ 1 2))"), "3");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(eval bad-to-evaluate-me)", "(eval)", "(eval 1 2)", "(eval :test 3)",
|
|
"(eval 1 :test 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, CarCdr) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(car '(3 . 4))"), "3");
|
|
EXPECT_EQ(e(i, "(cdr '(3 . 4))"), "4");
|
|
i.disable_printfs();
|
|
for (auto x : {"(car)", "(car '(1 . 2) '(3 . 4))", "(cdr)", "(cdr '(1 . 2) '(3 . 4))"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, SetCarCdr) {
|
|
Interpreter i;
|
|
e(i, "(define x (cons 1 2))");
|
|
EXPECT_EQ(e(i, "(set-car! x (+ 1 3))"), "(4 . 2)");
|
|
EXPECT_EQ(e(i, "x"), "(4 . 2)");
|
|
EXPECT_EQ(e(i, "(set-cdr! x (+ 2 3))"), "(4 . 5)");
|
|
EXPECT_EQ(e(i, "x"), "(4 . 5)");
|
|
i.disable_printfs();
|
|
for (auto x : {"(set-car!)", "(set-car! '(1 . 2))", "(set-cdr!)", "(set-cdr! '(1 . 2))"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, GenSym) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(eq? (gensym) (gensym))"), "#f");
|
|
i.disable_printfs();
|
|
for (auto x : {"(gensym 1)", "(gensym :a 2)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Cons) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(cons 'a 2)"), "(a . 2)");
|
|
EXPECT_EQ(e(i, "(cons 'a (cons 2 (cons 3 '())))"), "(a 2 3)");
|
|
i.disable_printfs();
|
|
for (auto x : {"(cons 1)", "(cons)", "(const 1 2 3)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Null) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(null? 1)"), "#f");
|
|
EXPECT_EQ(e(i, "(null? '())"), "#t");
|
|
i.disable_printfs();
|
|
for (auto x : {"(null? 1 2)", "(null?)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Type) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(type? 'empty-list '())"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'empty-list 1)"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'integer 2)"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'integer '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'float 2.)"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'float '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'char #\\c)"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'char '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'symbol 'a)"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'symbol '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'string \"test\")"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'string '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'pair '(1 . 2))"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'pair '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'array '#(1 2))"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'array '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'lambda (lambda () 1))"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'lambda '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'macro (macro () 1))"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'macro '())"), "#f");
|
|
|
|
EXPECT_EQ(e(i, "(type? 'environment *global-env*)"), "#t");
|
|
EXPECT_EQ(e(i, "(type? 'environment '())"), "#f");
|
|
}
|
|
|
|
/*!
|
|
* Confirm that the global and goos env variables appear
|
|
*/
|
|
TEST(GoosEval, GlobalAndGoalEnv) {
|
|
Interpreter i;
|
|
Object goal_env, goos_env;
|
|
EXPECT_TRUE(i.get_global_variable_by_name("*global-env*", &goos_env));
|
|
EXPECT_TRUE(i.get_global_variable_by_name("*goal-env*", &goal_env));
|
|
|
|
auto& goal_vars = goal_env.as_env()->vars;
|
|
auto& goos_vars = goos_env.as_env()->vars;
|
|
|
|
EXPECT_TRUE(goal_vars.find(i.intern("*global-env*").as_symbol()) != goal_vars.end());
|
|
EXPECT_TRUE(goal_vars.find(i.intern("*goal-env*").as_symbol()) != goal_vars.end());
|
|
EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol()) != goos_vars.end());
|
|
EXPECT_TRUE(goos_vars.find(i.intern("*goal-env*").as_symbol()) != goos_vars.end());
|
|
|
|
EXPECT_TRUE(goos_vars.find(i.intern("*goal-env*").as_symbol())->second ==
|
|
goal_vars.find(i.intern("*goal-env*").as_symbol())->second);
|
|
EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol())->second ==
|
|
goal_vars.find(i.intern("*global-env*").as_symbol())->second);
|
|
EXPECT_TRUE(goos_vars.find(i.intern("*global-env*").as_symbol())->second !=
|
|
goos_vars.find(i.intern("*goal-env*").as_symbol())->second);
|
|
}
|
|
|
|
/*!
|
|
* Confirm that the GOOS Library is loaded automatically on interpreter start.
|
|
*/
|
|
TEST(GoosEval, GoosLibLoaded) {
|
|
Interpreter i;
|
|
Object loaded;
|
|
EXPECT_TRUE(i.get_global_variable_by_name("__goos-lib-loaded__", &loaded));
|
|
EXPECT_EQ(loaded.print(), "#t");
|
|
}
|
|
|
|
/*!
|
|
* Check that integers are evaluated.
|
|
*/
|
|
TEST(GoosEval, EvalSelfEvaluating) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "010"), "10");
|
|
EXPECT_EQ(e(i, "-010"), "-10");
|
|
EXPECT_EQ(e(i, "\"test\""), "\"test\"");
|
|
EXPECT_EQ(e(i, "1.2"), "1.2"); // this depends on how we decide to print floats
|
|
EXPECT_EQ(e(i, "#\\a"), "#\\a");
|
|
EXPECT_EQ(e(i, "#\\\\n"), "#\\\\n");
|
|
|
|
i.disable_printfs();
|
|
EXPECT_ANY_THROW(e(i, "#\\\\a"));
|
|
}
|
|
|
|
TEST(GoosEval, FloatEvalAndPrinting) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "0.9999979734420776"), "0.999998");
|
|
EXPECT_EQ(e(i, "0.999998"), e(i, "0.9999979734420776"));
|
|
EXPECT_EQ(e(i, "1."), "1.0");
|
|
EXPECT_EQ(e(i, ".03"), "0.03");
|
|
EXPECT_EQ(e(i, "0.02999999932944774627685546875"), e(i, "0.03"));
|
|
EXPECT_EQ(e(i, "0.5883"), e(i, "0.5882999897"));
|
|
}
|
|
|
|
/*!
|
|
* Check named "keyword" arguments
|
|
*/
|
|
TEST(GoosEval, KeywordArgs) {
|
|
Interpreter i;
|
|
e(i, "(desfun test (a b &key c &key d) c)");
|
|
EXPECT_EQ(e(i, "(test 1 2 :c 3 :d 4)"), "3");
|
|
EXPECT_EQ(e(i, "(test 1 2 :d 3 :c 4)"), "4");
|
|
|
|
e(i, "(desfun test (a b &key c &key d) (+ b d))");
|
|
EXPECT_EQ(e(i, "(test 1 2 :c 3 :d 4)"), "6");
|
|
EXPECT_EQ(e(i, "(test 1 2 :d 3 :c 4)"), "5");
|
|
EXPECT_EQ(e(i, "(test 1 :d 3 2 :c 4)"), "5");
|
|
EXPECT_EQ(e(i, "(test :d 3 1 2 :c 4)"), "5");
|
|
|
|
i.disable_printfs();
|
|
for (const auto& x : {"(test 1 2 :c 3 :d)", "(test 1 2 :c 3)", "(test 1 2 :c 3 :d 4 :e 3)",
|
|
"(test 1 2 :c 3 :d 4 :c 4)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosEval, KeywordArgsWithDefault) {
|
|
Interpreter i;
|
|
e(i, "(desfun test (a b &key (c 3) &key d) c)");
|
|
|
|
EXPECT_EQ(e(i, "(test 1 2 :c 7 :d 4)"), "7");
|
|
EXPECT_EQ(e(i, "(test 1 2 :d 4)"), "3");
|
|
|
|
e(i, "(desfun test (a b &key (c 3) &key d) c)");
|
|
i.disable_printfs();
|
|
for (const auto& x :
|
|
{"(test 1 2 :c 3 :d)", "(test 1 2 :c :d 3)", "(test 1 2 :d 3 :c )", "(test 1 2 :c 3)",
|
|
"(test 1 2 :c 3 :d 4 :e 3)", "(test 1 2 :c 3 :d 4 :c 4)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosIntegrated, Begin) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define *test-value* (begin
|
|
(define x 10)
|
|
#|
|
|
asdfasdfasdfa
|
|
|#
|
|
(define y 20)
|
|
(set! x y)
|
|
x
|
|
; y
|
|
)
|
|
)
|
|
)"),
|
|
"20");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Lambda1) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define x 10)
|
|
|
|
(define f (lambda (x) ((lambda () x))))
|
|
|
|
(define *test-actual* (f 20))
|
|
)"),
|
|
"20");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Lambda2) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define make-returner (lambda (thing-to-return)
|
|
(lambda () thing-to-return)))
|
|
|
|
(define my-func (make-returner "beans"))
|
|
(define fake-func (make-returner "cheese"))
|
|
(fake-func)
|
|
(define *test-actual* (fake-func))
|
|
(set! *test-actual* (my-func))
|
|
)"),
|
|
"\"beans\"");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Cond) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define s1 (cond
|
|
(#f a)
|
|
((+ 2 3) (+ 3 4))
|
|
(#t 3333)
|
|
(#t this-cannot-be-evaluated)
|
|
)
|
|
)
|
|
|
|
|
|
(define s2 (cond (2)))
|
|
|
|
(define s3 (cond (#f a)
|
|
(#t (define s4 10)
|
|
(+ 1 1)
|
|
)
|
|
)
|
|
)
|
|
|
|
(define *test-actual* (+ s1 s2 s3 s4))
|
|
)"),
|
|
"21");
|
|
}
|
|
|
|
TEST(GoosIntegrated, CountingChange) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define count-change
|
|
(lambda (amount)
|
|
(cc amount 5)
|
|
)
|
|
)
|
|
|
|
(define cc
|
|
(lambda (amount kinds-of-coins)
|
|
(cond
|
|
;; base case
|
|
((= amount 0) 1)
|
|
;; impossible
|
|
((or (< amount 0) (= kinds-of-coins 0)) 0)
|
|
;; otherwise
|
|
(#t (+ (cc amount (- kinds-of-coins 1))
|
|
(cc (- amount (first-denom kinds-of-coins)) kinds-of-coins)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
(define first-denom
|
|
(lambda (kinds-of-coins)
|
|
(cond
|
|
((= kinds-of-coins 1) 1)
|
|
((= kinds-of-coins 2) 5)
|
|
((= kinds-of-coins 3) 10)
|
|
((= kinds-of-coins 4) 25)
|
|
((= kinds-of-coins 5) 50)
|
|
)
|
|
)
|
|
)
|
|
|
|
(define *test-actual* (count-change 100))
|
|
)"),
|
|
"292");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Eval) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define *test-actual*
|
|
(eval '(+ 1 2)
|
|
)
|
|
)
|
|
)"),
|
|
"3");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Read) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define *test-actual*
|
|
(eval (read "(+ 1 2) ")
|
|
)
|
|
)
|
|
)"),
|
|
"3");
|
|
}
|
|
|
|
// todo - test read?
|
|
|
|
TEST(GoosIntegrated, LambdaWithRest) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define func (lambda (x &rest y) y))
|
|
(define x (func -123 2 3 1))
|
|
|
|
(define *test-actual* (+ (car x) (car (cdr x)) (car (cdr (cdr x)))))
|
|
)"),
|
|
"6");
|
|
}
|
|
|
|
TEST(GoosIntegrated, IntegerEquality1) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define x 2)
|
|
(set! x 1)
|
|
(define *test-actual* (eq? x 2))
|
|
)"),
|
|
"#f");
|
|
}
|
|
|
|
TEST(GoosIntegrated, IntegerEquality2) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define x 2)
|
|
(define *test-actual* (eq? x 2))
|
|
)"),
|
|
"#t");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Lambda3) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define my-func (lambda (a b c) b))
|
|
(define *test-actual* (my-func 1 2 3))
|
|
)"),
|
|
"2");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Lambda4) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define func1 (lambda () 1))
|
|
(define func2 (lambda () (func1)))
|
|
(set! func1 (lambda () 2))
|
|
(define *test-actual* (func2))
|
|
)"),
|
|
"2");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Lambda5) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define x 10)
|
|
(define my-func (lambda (x) x))
|
|
(define s1 (my-func 20))
|
|
(define s2 x)
|
|
(define s3 ((lambda (x) x) 30))
|
|
(define my-func-2 (lambda (x y) (set! y x) y))
|
|
(define s4 (my-func-2 11 12))
|
|
(define f3 (lambda (x) (set! x (+ 1 x)) x))
|
|
(define s5 (f3 14))
|
|
(define x ((lambda (x) (set! x (+ x 1)) x) 16))
|
|
(define s6 x)
|
|
(define f1 (lambda (x) (+ x 1)))
|
|
(define f2 (lambda (x) (+ 2 (f1 x))))
|
|
(define s7 (f2 (+ 1 2)))
|
|
(define *test-actual* (+ s1 s2 s3 s4 s5 s6 s7))
|
|
)"),
|
|
"109");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Let1) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define s1
|
|
(let ((x (+ 1 2))
|
|
(y (+ 3 4)))
|
|
(+ x y)
|
|
)
|
|
)
|
|
|
|
(define var-1 2)
|
|
|
|
(define s2
|
|
(let ((x (lambda () var-1)))
|
|
(set! var-1 1000)
|
|
(x)
|
|
)
|
|
)
|
|
|
|
(define *test-actual* (+ s1 s2))
|
|
)"),
|
|
"1010");
|
|
}
|
|
|
|
TEST(GoosIntegrated, Let2) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define s1
|
|
(let* ((x (+ 1 2))
|
|
(y (+ x 5)))
|
|
(+ x y)
|
|
)
|
|
)
|
|
|
|
(define var-1 2)
|
|
|
|
(define s2
|
|
(let ((x (lambda () var-1)))
|
|
(set! var-1 1000)
|
|
(x)
|
|
)
|
|
)
|
|
|
|
(define x 0)
|
|
(define s3
|
|
(let ((x (+ 1 2))
|
|
(y x))
|
|
y
|
|
)
|
|
)
|
|
|
|
(define x 0)
|
|
(define s4
|
|
(let* ((x (+ 1 2))
|
|
(y x))
|
|
y
|
|
)
|
|
)
|
|
|
|
(define *test-actual* (+ s1 s2 s3 s4))
|
|
)"),
|
|
"1014");
|
|
}
|
|
|
|
TEST(GoosIntegrated, WeirdSymbols) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
'+
|
|
'-
|
|
'...
|
|
'!..
|
|
'$.+
|
|
'%.-
|
|
'&.!
|
|
'*.:
|
|
'/:.
|
|
;':+. ; forget this one, it gets recognized as a keyword argument to (quote).
|
|
'<-.
|
|
'=.
|
|
'>.
|
|
'?.
|
|
'~.
|
|
'_.
|
|
'^.
|
|
1
|
|
)"),
|
|
"1");
|
|
}
|
|
|
|
TEST(GoosIntegrated, WhileLoop) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(define count 0)
|
|
(define sum 0)
|
|
|
|
(while (< count 100)
|
|
(set! sum (+ sum count))
|
|
(set! count (+ count 1))
|
|
)
|
|
|
|
(define *test-actual* sum)
|
|
)"),
|
|
"4950");
|
|
}
|
|
|
|
TEST(GoosLib, Desfun) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(desfun test-function (a b c &rest d)
|
|
(set! a (+ a b c))
|
|
(cons a d)
|
|
)
|
|
|
|
(test-function 1 2 3 4 5 6)
|
|
|
|
)"),
|
|
"(6 4 5 6)");
|
|
}
|
|
|
|
TEST(GoosLib, Factorial) {
|
|
Interpreter i;
|
|
// now large numbers are printed as hex.
|
|
EXPECT_EQ(e(i, "(factorial 10)"), "#x375f00");
|
|
}
|
|
|
|
TEST(GoosLib, ApplySimple) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(apply (lambda (x) (* x 2)) '(1 2 3))"), "(2 4 6)");
|
|
}
|
|
|
|
TEST(GoosLib, ApplyComplex1) {
|
|
Interpreter i;
|
|
e(i, "(define y 2)");
|
|
e(i, "(define test-func (lambda (x) (* x y)))");
|
|
EXPECT_EQ(e(i, "(apply test-func '(1 2 3))"), "(2 4 6)");
|
|
}
|
|
TEST(GoosLib, ApplyComplex2) {
|
|
Interpreter i;
|
|
e(i, "(define y 2)");
|
|
e(i, "(define test-func (lambda (x) (* x y)))");
|
|
e(i, "(set! y 4)");
|
|
EXPECT_EQ(e(i, "(apply test-func '(1 2 3))"), "(4 8 12)");
|
|
}
|
|
|
|
TEST(GoosLib, ApplyComplex3) {
|
|
Interpreter i;
|
|
e(i, "(desfun make-mult-by-x-fun (x) (lambda (y) (* y x)))");
|
|
e(i, "(define mult3 (make-mult-by-x-fun 3))");
|
|
e(i, "(define mult2 (make-mult-by-x-fun 2))");
|
|
EXPECT_EQ(e(i, "(apply mult2 '(1 2 3))"), "(2 4 6)");
|
|
EXPECT_EQ(e(i, "(apply mult3 '(1 2 3))"), "(3 6 9)");
|
|
EXPECT_EQ(e(i, "(apply mult2 '(1 2 3))"), "(2 4 6)");
|
|
}
|
|
|
|
TEST(GoosLib, Let) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(let ((x 1)
|
|
(y 2)
|
|
(z 3))
|
|
(+ x y z))
|
|
|
|
)"),
|
|
"6");
|
|
}
|
|
|
|
TEST(GoosLib, LetRec) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, R"(
|
|
(let* ((x 1) ; 1
|
|
(y (+ 2 x)) ; 3
|
|
(z (+ 3 y))) ; 6
|
|
(+ x y z))
|
|
)"),
|
|
"10");
|
|
}
|
|
|
|
/*!
|
|
* Test special case character printing.
|
|
*/
|
|
TEST(GoosObject, char_to_string) {
|
|
// printable
|
|
EXPECT_EQ("#\\d", goos::fixed_to_string('d'));
|
|
|
|
// special ones
|
|
EXPECT_EQ("#\\\\s", goos::fixed_to_string(' '));
|
|
EXPECT_EQ("#\\\\n", goos::fixed_to_string('\n'));
|
|
EXPECT_EQ("#\\\\t", goos::fixed_to_string('\t'));
|
|
|
|
// decimal for ones we don't have special cased
|
|
EXPECT_EQ("#\\{17}", goos::fixed_to_string(char(17)));
|
|
}
|
|
|
|
/*!
|
|
* Test the EmptyListObject
|
|
*/
|
|
TEST(GoosObject, EmptyList) {
|
|
// create two empty lists
|
|
Object nil = Object::make_empty_list();
|
|
Object nil2 = Object::make_empty_list();
|
|
|
|
// check type is set
|
|
EXPECT_TRUE(nil.is_empty_list());
|
|
|
|
// check equality operator
|
|
EXPECT_TRUE(nil == nil2);
|
|
|
|
// check print and inspect
|
|
EXPECT_EQ(nil.print(), "()");
|
|
EXPECT_EQ(nil.inspect(), "[empty list] ()\n");
|
|
}
|
|
|
|
/*!
|
|
* Test IntegerObject
|
|
*/
|
|
TEST(GoosObject, Integer) {
|
|
// create integer objects
|
|
Object io = Object::make_integer(-1234);
|
|
Object different = Object::make_integer(3);
|
|
Object same = Object::make_integer(-1234);
|
|
|
|
// check type
|
|
EXPECT_TRUE(io.is_int());
|
|
|
|
// check equality
|
|
EXPECT_TRUE(same == io);
|
|
EXPECT_FALSE(different == io);
|
|
|
|
// check changing the value through the as_int reference
|
|
different.as_int() = -1234;
|
|
EXPECT_TRUE(different.as_int() == -1234);
|
|
EXPECT_TRUE(different == same);
|
|
|
|
// check print and inspect
|
|
EXPECT_EQ(different.print(), "-1234");
|
|
EXPECT_EQ(different.inspect(), "[integer] -1234\n");
|
|
}
|
|
|
|
/*!
|
|
* Test FloatObject
|
|
*/
|
|
TEST(GoosObject, Float) {
|
|
// create integer objects
|
|
Object fo = Object::make_float(-12.34);
|
|
Object different = Object::make_float(3);
|
|
Object same = Object::make_float(-12.34);
|
|
|
|
// check type
|
|
EXPECT_FALSE(fo.is_int());
|
|
EXPECT_TRUE(fo.is_float());
|
|
|
|
// check equality
|
|
EXPECT_TRUE(same == fo);
|
|
EXPECT_FALSE(different == fo);
|
|
|
|
// check changing the value through the as_float reference
|
|
different.as_float() = -12.34;
|
|
EXPECT_TRUE(different.as_float() == -12.34);
|
|
EXPECT_TRUE(different == same);
|
|
|
|
// check print and inspect
|
|
EXPECT_EQ(different.print(), "-12.34");
|
|
EXPECT_EQ(different.inspect(), "[float] -12.34\n");
|
|
}
|
|
|
|
/*!
|
|
* Test CharObject
|
|
*/
|
|
TEST(GoosObject, Char) {
|
|
// create integer objects
|
|
Object co = Object::make_char('w');
|
|
Object different = Object::make_char('X');
|
|
Object same = Object::make_char('w');
|
|
|
|
// check type
|
|
EXPECT_FALSE(co.is_int());
|
|
EXPECT_TRUE(co.is_char());
|
|
|
|
// check equality
|
|
EXPECT_TRUE(same == co);
|
|
EXPECT_FALSE(different == co);
|
|
|
|
// check changing the value through the as_char reference
|
|
different.as_char() = 'w';
|
|
EXPECT_TRUE(different.as_char() == 'w');
|
|
EXPECT_TRUE(different == same);
|
|
|
|
// check print and inspect
|
|
EXPECT_EQ(different.print(), "#\\w");
|
|
EXPECT_EQ(different.inspect(), "[char] #\\w\n");
|
|
}
|
|
|
|
/*!
|
|
* Test SymbolObject
|
|
*/
|
|
TEST(GoosObject, Symbol) {
|
|
SymbolTable st, st2;
|
|
Object obj = SymbolObject::make_new(st, "test1");
|
|
Object obj2 = SymbolObject::make_new(st, "test2");
|
|
Object obj3 = SymbolObject::make_new(st, "test1");
|
|
Object obj4 = SymbolObject::make_new(st2, "test1");
|
|
|
|
// check type
|
|
EXPECT_TRUE(obj.is_symbol());
|
|
|
|
// check equality
|
|
EXPECT_TRUE(obj == obj3);
|
|
EXPECT_FALSE(obj == obj2);
|
|
EXPECT_FALSE(obj == obj4); // different because different st's
|
|
|
|
// check interning works
|
|
auto obj_as = obj.as_symbol();
|
|
auto obj2_as = obj2.as_symbol();
|
|
auto obj3_as = obj3.as_symbol();
|
|
auto obj4_as = obj4.as_symbol();
|
|
|
|
EXPECT_TRUE(obj_as == obj3_as);
|
|
EXPECT_FALSE(obj_as == obj2_as);
|
|
EXPECT_FALSE(obj_as == obj4_as); // different because different st's.
|
|
|
|
// check print and inspect
|
|
EXPECT_EQ(obj.print(), "test1");
|
|
EXPECT_EQ(obj.inspect(), "[symbol] test1\n");
|
|
}
|
|
|
|
/*!
|
|
* Test StringObject
|
|
*/
|
|
TEST(GoosObject, String) {
|
|
Object obj1 = StringObject::make_new("test1");
|
|
Object obj2 = StringObject::make_new("test2");
|
|
Object obj3 = StringObject::make_new("test1");
|
|
|
|
Object obj4 = StringObject::make_new("test-with\"quote");
|
|
|
|
EXPECT_TRUE(obj1.is_string());
|
|
|
|
EXPECT_TRUE(obj1 == obj3);
|
|
EXPECT_FALSE(obj1 == obj2);
|
|
|
|
obj2.as_string()->data = "test1";
|
|
EXPECT_TRUE(obj1 == obj2);
|
|
|
|
EXPECT_EQ(obj1.print(), "\"test1\"");
|
|
EXPECT_EQ(obj1.inspect(), "[string] \"test1\"\n");
|
|
EXPECT_EQ(obj4.print(), "\"test-with\\\"quote\"");
|
|
}
|
|
|
|
/*!
|
|
* Test PairObject
|
|
*/
|
|
TEST(GoosObject, Pair) {
|
|
Object obj = PairObject::make_new(Object::make_integer(1), Object::make_integer(2));
|
|
Object obj2 = PairObject::make_new(Object::make_integer(2), Object::make_integer(2));
|
|
Object obj3 = PairObject::make_new(Object::make_integer(1), Object::make_integer(2));
|
|
|
|
EXPECT_TRUE(obj.is_pair());
|
|
|
|
EXPECT_TRUE(obj == obj3);
|
|
EXPECT_FALSE(obj == obj2);
|
|
obj2.as_pair()->car = Object::make_integer(1);
|
|
EXPECT_TRUE(obj == obj2);
|
|
|
|
EXPECT_EQ(obj.print(), "(1 . 2)");
|
|
EXPECT_EQ(obj.inspect(), "[pair] (1 . 2)\n");
|
|
}
|
|
|
|
/*!
|
|
* Test proper list stuff
|
|
*/
|
|
TEST(GoosObject, PairList) {
|
|
Object obj = build_list({});
|
|
EXPECT_TRUE(obj.is_empty_list());
|
|
|
|
obj = build_list({Object::make_integer(1)});
|
|
EXPECT_EQ(obj.print(), "(1)");
|
|
|
|
obj = build_list({Object::make_integer(1), Object::make_integer(2), Object::make_integer(3)});
|
|
auto obj2 =
|
|
build_list({Object::make_integer(1), Object::make_integer(2), Object::make_integer(2)});
|
|
auto obj3 =
|
|
build_list({Object::make_integer(1), Object::make_integer(2), Object::make_integer(3)});
|
|
|
|
EXPECT_EQ(obj.print(), "(1 2 3)");
|
|
EXPECT_TRUE(obj == obj3);
|
|
EXPECT_FALSE(obj == obj2);
|
|
}
|
|
|
|
/*!
|
|
* Test ArrayObject
|
|
*/
|
|
TEST(GoosObject, Array) {
|
|
Object empty_array = ArrayObject::make_new({});
|
|
EXPECT_EQ(empty_array.as_array()->size(), 0);
|
|
EXPECT_TRUE(empty_array.is_array());
|
|
EXPECT_EQ(empty_array.print(), "#()");
|
|
|
|
auto array1 = ArrayObject::make_new(
|
|
{Object::make_integer(1), Object::make_integer(2), Object::make_integer(3)});
|
|
auto array2 = ArrayObject::make_new(
|
|
{Object::make_integer(1), Object::make_integer(2), Object::make_integer(4)});
|
|
auto array3 = ArrayObject::make_new(
|
|
{Object::make_integer(1), Object::make_integer(2), Object::make_integer(3)});
|
|
|
|
EXPECT_TRUE(array1 == array3);
|
|
EXPECT_TRUE(array1 != array2);
|
|
|
|
EXPECT_EQ(array1.print(), "#(1 2 3)");
|
|
EXPECT_EQ(array1.inspect(), "[array] size: 3 data: #(1 2 3)\n");
|
|
}
|
|
|
|
TEST(GoosSpecialForms, Define) {
|
|
Interpreter i;
|
|
|
|
e(i, "(define x 010)");
|
|
EXPECT_EQ(e(i, "x"), "10");
|
|
|
|
e(i, "(define :env *goal-env* x 20)");
|
|
EXPECT_EQ(e(i, "x"), "10");
|
|
|
|
Object goal_env;
|
|
EXPECT_TRUE(i.get_global_variable_by_name("*goal-env*", &goal_env));
|
|
|
|
auto x_in_goal_env = goal_env.as_env()->vars.find(i.intern("x").as_symbol());
|
|
EXPECT_TRUE(x_in_goal_env != goal_env.as_env()->vars.end());
|
|
EXPECT_EQ(x_in_goal_env->second.print(), "20");
|
|
|
|
// test automatic environment of define
|
|
e(i, "(begin (desfun test-define () (define x 500)) (test-define))");
|
|
EXPECT_EQ(e(i, "x"), "10");
|
|
|
|
// test manual setting of global env for define
|
|
e(i, "(begin (desfun test-define () (define :env *global-env* x 500)) (test-define))");
|
|
EXPECT_EQ(e(i, "x"), "500");
|
|
|
|
e(i, "(begin (desfun test-define () (define x :env *global-env* 600)) (test-define))");
|
|
EXPECT_EQ(e(i, "x"), "600");
|
|
|
|
e(i, "(begin (desfun test-define () (define x 700 :env *global-env*)) (test-define))");
|
|
EXPECT_EQ(e(i, "x"), "700");
|
|
|
|
i.disable_printfs();
|
|
EXPECT_ANY_THROW(e(i, "(define :env 3 x 10)"));
|
|
EXPECT_ANY_THROW(e(i, "(define :beans 3 x 10)"));
|
|
EXPECT_ANY_THROW(e(i, "(define x)"));
|
|
EXPECT_ANY_THROW(e(i, "(define x 1 2)"));
|
|
EXPECT_ANY_THROW(e(i, "(define 1)"));
|
|
EXPECT_ANY_THROW(e(i, "(define 1 2)"));
|
|
EXPECT_ANY_THROW(e(i, "(define)"));
|
|
}
|
|
|
|
TEST(GoosSpecialForms, Set) {
|
|
Interpreter i;
|
|
e(i, "(define x 10)");
|
|
EXPECT_EQ(e(i, "(set! x (+ 10 10))"), "20");
|
|
EXPECT_EQ(e(i, "x"), "20");
|
|
|
|
// set parent env
|
|
EXPECT_EQ(e(i, "(begin (desfun test-set () (set! x 30)) (test-set))"), "30");
|
|
EXPECT_EQ(e(i, "x"), "30");
|
|
|
|
// check non-global env (also a good check with two different variables named x)
|
|
EXPECT_EQ(e(i, "(begin (desfun test-set (x) (set! x (+ x 30))) (test-set x))"), "60");
|
|
EXPECT_EQ(e(i, "x"), "30");
|
|
|
|
i.disable_printfs();
|
|
for (auto x :
|
|
{"(set!)", "(set! x)", "(set! 1)", "(set! 1 1)", "(set! x 1 2)", "(set! x 1 :test 2)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosSpecialForms, Quote) {
|
|
Interpreter i;
|
|
e(i, "(define x 'y)");
|
|
EXPECT_EQ(e(i, "x"), "y");
|
|
e(i, "(define x (quote z))");
|
|
EXPECT_EQ(e(i, "x"), "z");
|
|
e(i, "(define x '(1 2 3))");
|
|
EXPECT_EQ(e(i, "x"), "(1 2 3)");
|
|
e(i, "(define x '(1 2 3 ,w))");
|
|
EXPECT_EQ(e(i, "x"), "(1 2 3 (unquote w))");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(quote)", "(quote 1 2)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosSpecialForms, DoubleQuote) {
|
|
Interpreter i;
|
|
e(i, "(define x ''y)");
|
|
EXPECT_EQ(e(i, "x"), "(quote y)");
|
|
e(i, "(define x `'`y)");
|
|
EXPECT_EQ(e(i, "x"), "(quote (quasiquote y))");
|
|
}
|
|
|
|
TEST(GoosSpecialForms, QuasiQuote) {
|
|
Interpreter i;
|
|
e(i, "(define x 'y)");
|
|
EXPECT_EQ(e(i, "(define z `(x ,x z))"), "(x y z)");
|
|
i.disable_printfs();
|
|
for (auto x : {"(quasiquote)", "(quasiquote 1 2)", "(unquote 1)", "(unquote-spicing 1)",
|
|
"`( (unquote-splicing) 2)", "`( (unquote-splicing 1 2) 2)",
|
|
"`( (unquote-splicing 1) 2)", "`( (unquote) 2)", "`( (unquote 1 2) 2)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosSpecialForms, QuasiQuoteSplicing) {
|
|
Interpreter i;
|
|
e(i, "(define x '(1 2 3))");
|
|
EXPECT_EQ(e(i, "(define z `(x ,x z))"), "(x (1 2 3) z)");
|
|
EXPECT_EQ(e(i, "(define z `(x ,@x z))"), "(x 1 2 3 z)");
|
|
}
|
|
|
|
TEST(GoosSpecialForms, Or) {
|
|
Interpreter i;
|
|
e(i, "(desfun rf () #f)");
|
|
e(i, "(desfun r1 () 1)");
|
|
EXPECT_EQ(e(i, "(or #f (rf) #f)"), "#f");
|
|
EXPECT_EQ(e(i, "(or #f (rf) (r1) 2 #f)"), "1");
|
|
EXPECT_EQ(e(i, "(or #f (rf) 1 2 cannot-be-evaluated)"), "1");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(or)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosSpecialForms, And) {
|
|
Interpreter i;
|
|
e(i, "(desfun rf () #f)");
|
|
e(i, "(desfun r1 () 1)");
|
|
EXPECT_EQ(e(i, "(and #f (rf) #f)"), "#f");
|
|
EXPECT_EQ(e(i, "(and (r1) 2 3 4 3 #t 3 2)"), "2");
|
|
EXPECT_EQ(e(i, "(and #f (rf) 1 2 cannot-be-evaluated 2 3 4)"), "#f");
|
|
|
|
i.disable_printfs();
|
|
for (auto x : {"(and)"}) {
|
|
EXPECT_ANY_THROW(e(i, x));
|
|
}
|
|
}
|
|
|
|
TEST(GoosBuiltins, Format) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(fmt #f \"{}, {}, {}\" 3 'bean \"str\")"), "\"3, bean, str\"");
|
|
}
|
|
|
|
TEST(GoosBuiltins, Error) {
|
|
Interpreter i;
|
|
EXPECT_ANY_THROW(e(i, "(error \"hi\")"));
|
|
}
|
|
|
|
TEST(GoosBuiltins, Ash) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(ash 3 2)"), "12");
|
|
EXPECT_EQ(e(i, "(ash 3 -1)"), "1");
|
|
}
|
|
|
|
TEST(GoosBuiltins, StringUtils) {
|
|
Interpreter i;
|
|
EXPECT_EQ(e(i, "(string-ref \"test\" 2)"), "#\\s");
|
|
EXPECT_EQ(e(i, "(string-length \"test\")"), "4");
|
|
EXPECT_EQ(e(i, "(string-append \"hello\" \" \" \"world\")"), "\"hello world\"");
|
|
EXPECT_EQ(e(i, "(symbol->string 'test)"), "\"test\"");
|
|
}
|
|
|
|
TEST(GoosBuiltins, HashTable) {
|
|
Interpreter i;
|
|
e(i, "(define ht (make-string-hash-table))");
|
|
EXPECT_EQ(e(i, "(car (hash-table-try-ref ht \"foo\"))"), "#f");
|
|
|
|
e(i, "(hash-table-set! ht \"foo\" 123)");
|
|
EXPECT_EQ(e(i, "(car (hash-table-try-ref ht \"bar\"))"), "#f");
|
|
EXPECT_EQ(e(i, "(car (hash-table-try-ref ht \"foo\"))"), "#t");
|
|
EXPECT_EQ(e(i, "(cdr (hash-table-try-ref ht \"foo\"))"), "123");
|
|
e(i, "(hash-table-set! ht \"foo\" 456)");
|
|
EXPECT_EQ(e(i, "(cdr (hash-table-try-ref ht \"foo\"))"), "456");
|
|
} |