diff --git a/clitests/coffee.psil b/clitests/coffee.psil new file mode 100644 index 0000000..ff51e7e --- /dev/null +++ b/clitests/coffee.psil @@ -0,0 +1,21 @@ +(define (getmax a b) + (if (> a b) a b) +) + +(define (coffee-shop-impl times last cur max) + (if (= times (nil)) + (getmax cur max) + (if (= last (car times)) + (coffee-shop-impl (cdr times) last (+ cur 1) max) + (coffee-shop-impl (cdr times) (car times) 1 (getmax cur max)) + ) + ) +) + +(define (coffee-shop times) + (coffee-shop-impl times (nil) 0 0) +) + +(coffee-shop (nil)) +(coffee-shop (quote( (8 0) (8 10) (8 10) (8 45) ))) +(coffee-shop (quote( (8 12) (10 11) (10 11) (15 15) (15 15) (15 15) (22 22) (22 22) (22 59) ))) \ No newline at end of file diff --git a/clitests/coffee.res b/clitests/coffee.res new file mode 100644 index 0000000..ac0ea60 --- /dev/null +++ b/clitests/coffee.res @@ -0,0 +1,3 @@ +0 +2 +3 \ No newline at end of file diff --git a/clitests/testall.sh b/clitests/testall.sh index 1f136b6..2dd360b 100755 --- a/clitests/testall.sh +++ b/clitests/testall.sh @@ -20,6 +20,7 @@ for FILE in *.psil; do continue fi + echo "$FILE OK" rm $FILE.res done diff --git a/src/vm/include/Cell.h b/src/vm/include/Cell.h index 6e13561..7ee25db 100644 --- a/src/vm/include/Cell.h +++ b/src/vm/include/Cell.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -53,21 +54,59 @@ struct ConsCell : public Cell { std::atomic _car = nullptr; std::atomic _cdr = nullptr; - void print(std::ostream &out) override { + void print_cons(std::ostream &out, std::set &seen) { std::stringstream res; if (_car) { if (_car.load()->_type == CellType::CONS) { - res << "("; - _car.load()->print(res); - res << ")"; + if (seen.find(_car) == seen.end()) { + res << "("; + seen.emplace(_car.load()); + dynamic_cast(*_car.load()).print_cons(res, seen); + res << ")"; + } else { + res << "(recursive)"; + } } else { _car.load()->print(res); } } if (_cdr) { if (_cdr.load()->_type == CellType::CONS) { - res << " "; + if (seen.find(_cdr) == seen.end()) { + res << " "; + seen.emplace(_cdr.load()); + dynamic_cast(*_cdr.load()).print_cons(res, seen); + } else { + res << " recursive"; + } + } else { + res << "."; _cdr.load()->print(res); + } + } + out << res.str(); + } + + void print(std::ostream &out) override { + std::stringstream res; + std::set seen; + if (_car) { + if (_car.load()->_type == CellType::CONS) { + res << "("; + seen.emplace(_car.load()); + dynamic_cast(*_car.load()).print_cons(res, seen); + res << ")"; + } else { + _car.load()->print(res); + } + } else { + res << "null "; + } + if (_cdr) { + if (_cdr.load()->_type == CellType::CONS) { + res << " "; + seen.emplace(_cdr.load()); + dynamic_cast(*_cdr.load()).print_cons(res, seen); } else { res << "."; _cdr.load()->print(res); diff --git a/src/vm/include/Handle.h b/src/vm/include/Handle.h index 8a83664..99d5687 100644 --- a/src/vm/include/Handle.h +++ b/src/vm/include/Handle.h @@ -30,14 +30,15 @@ public: } else if (_target->_type == CellType::STRATOM) { return dynamic_cast(*_target)._val == dynamic_cast(*rhs._target)._val; } - } + } else if (null() && rhs.null()) + return true; return false; } static Handle cons(const Handle &car, const Handle &cdr); - Handle car() { return dynamic_cast(*_target)._car.load(); } - Handle cdr() { return dynamic_cast(*_target)._cdr.load(); } + Handle car() const { return dynamic_cast(*_target)._car.load(); } + Handle cdr() const { return dynamic_cast(*_target)._cdr.load(); } CellValType val() { return dynamic_cast(*_target)._val; } std::string_view strval() { return dynamic_cast(*_target)._val; } @@ -48,7 +49,7 @@ public: bool atom() const { return type() != CellType::CONS; } - bool null() { + bool null() const { if (!_target) return true; if (type() == CellType::CONS && car() == nullptr && cdr() == nullptr) return true; return false; diff --git a/src/vm/src/Compiler.cpp b/src/vm/src/Compiler.cpp index 61ba2c0..180090f 100644 --- a/src/vm/src/Compiler.cpp +++ b/src/vm/src/Compiler.cpp @@ -4,37 +4,55 @@ #include "Compiler.h" -#include "Command.h" #include +#include #include +#include "Command.h" +#include "Logger.h" + + using namespace Command; -static std::unordered_map builtins{{"+", ADD}, {"-", SUB}, {"cons", CONS}, {"car", CAR}, - {"cdr", CDR}, {"=", EQ}, {">", GT}, {"<", LT}}; +static std::unordered_map builtins{{"+", ADD}, {"-", SUB}, {"cons", CONS}, {"car", CAR}, {"cdr", CDR}, + {"=", EQ}, {">", GT}, {"<", LT}, {"nil", NIL}}; Handle Compiler::compile(const Handle &src, Handle fake_env, const Handle &suffix) { Handle out; std::function compileArgsRaw = [&](Handle args) { Handle out; + std::stack rev; while (!args.null()) { - out.splice(compile(args.car(), fake_env)); + rev.push(args.car()); args = args.cdr(); } + while (!rev.empty()) { + out.splice(compile(rev.top(), fake_env)); + rev.pop(); + } return out; }; std::function compileArgsList = [&](Handle args, Handle env) { Handle out; out.append(make_cmd(NIL)); + std::stack rev; while (!args.null()) { - out.splice(compile(args.car(), env)); - out.append(make_cmd(CONS)); + rev.push(args.car()); args = args.cdr(); } + while (!args.null()) { + rev.push(args.car()); + args = args.cdr(); + } + while (!rev.empty()) { + out.splice(compile(rev.top(), env)); + out.append(make_cmd(CONS)); + rev.pop(); + } return out; }; @@ -60,7 +78,10 @@ Handle Compiler::compile(const Handle &src, Handle fake_env, const Handle &suffi Handle car = expr.car(); Handle cdr = expr.cdr(); if (car.atom()) { - if (builtins.find(car.strval()) != builtins.end()) { + if (car.strval() == "quote") { + out.append(make_cmd(LDC)); + out.splice(cdr); + } else if (builtins.find(car.strval()) != builtins.end()) { out.splice(compileArgsRaw(cdr)); out.append(make_cmd(builtins.at(car.strval()))); } else if (car.strval() == "read") { @@ -78,8 +99,6 @@ Handle Compiler::compile(const Handle &src, Handle fake_env, const Handle &suffi out.append(make_cmd(LDG)); out.append(compile(cdr.cdr().car(), Handle::cons(cdr.car().cdr(), fake_env), make_cmd(RET))); } else if (car.strval() == "let" || car.strval() == "letrec") { - std::vector> argBody; - Handle definitions = cdr.car(); Handle argNames; @@ -88,7 +107,6 @@ Handle Compiler::compile(const Handle &src, Handle fake_env, const Handle &suffi Handle body = cdr.cdr().car(); while (!definitions.null()) { - argBody.emplace_back(definitions.car().car(), definitions.car().cdr().car()); argNames.append(definitions.car().car()); argBodies.append(definitions.car().cdr().car()); definitions = definitions.cdr(); diff --git a/src/vm/src/Parser.cpp b/src/vm/src/Parser.cpp index 8143a5b..b1944df 100644 --- a/src/vm/src/Parser.cpp +++ b/src/vm/src/Parser.cpp @@ -29,6 +29,10 @@ Handle Parser::parseExpr() { return out; } + if (_tokenizer.peek() == ")") { + token = _tokenizer.peek(); + continue; + } out.append(parseExpr()); token = _tokenizer.peek(); } @@ -37,7 +41,7 @@ Handle Parser::parseExpr() { } else { token = _tokenizer.getNext(); if (token.find_first_not_of("0123456789") == std::string::npos || - (token.length() > 1 && token.at(0) == '0' && token.find_first_not_of("0123456789", 1) == std::string::npos)) { + (token.length() > 1 && token.at(0) == '-' && token.find_first_not_of("0123456789", 1) == std::string::npos)) { CellValType val = std::stoi(token); return Handle::makeNumCell(val); } else { diff --git a/src/vm/src/VM.cpp b/src/vm/src/VM.cpp index 33a773b..12fa256 100644 --- a/src/vm/src/VM.cpp +++ b/src/vm/src/VM.cpp @@ -23,6 +23,19 @@ void VM::step() { Handle poppedH = _c.pop(); // as to not complicate parser for tests... CellValType poppedCmd = poppedH.type() == CellType::STRATOM ? str_to_cmd.at(poppedH.strval()) : poppedH.val(); + + + Logger::log( + "VM", + [&](std::ostream &out) { + out << "Executing " << cmd_to_str.at(poppedCmd) << "\n"; + if (!_s.null()) out << "s:" << _s << "\n"; + if (!_e.null()) out << "e:" << _e << "\n"; + if (!_c.null()) out << "c:" << _c << "\n"; + if (!_d.null()) out << "d:" << _d << "\n"; + }, + Logger::TRACE); + if (poppedCmd == NIL) { _s.push(nullptr); } else if (poppedCmd == LDC) { @@ -115,17 +128,17 @@ void VM::step() { } else if (poppedCmd == SUB) { CellValType a1 = _s.pop().val(); CellValType a2 = _s.pop().val(); - _s.push(a2 - a1); + _s.push(a1 - a2); } else if (poppedCmd == EQ) { _s.push(_s.pop() == _s.pop() ? 1 : 0); } else if (poppedCmd == LT) { CellValType a1 = _s.pop().val(); CellValType a2 = _s.pop().val(); - _s.push(a2 < a1 ? 1 : 0); + _s.push(a1 < a2 ? 1 : 0); } else if (poppedCmd == GT) { CellValType a1 = _s.pop().val(); CellValType a2 = _s.pop().val(); - _s.push(a2 > a1 ? 1 : 0); + _s.push(a1 > a2 ? 1 : 0); } else if (poppedCmd == CAR) { _s.push(_s.pop().car()); } else if (poppedCmd == CDR) { @@ -159,7 +172,14 @@ void VM::step() { } else if (poppedCmd == LDG) { _globals_vals.append(Handle::cons(_c.pop(), _e)); } else if (poppedCmd == PRINT) { - if (!_s.null()) _outstream << _s.pop() << std::endl; + if (!_s.null()) { + Handle val = _s.pop(); + bool cons = !val.atom(); + if (cons) _outstream << "("; + _outstream << val; + if (cons) _outstream << ")"; + _outstream << std::endl; + } } else if (poppedCmd == READ) { std::string read; std::getline(_instream, read); @@ -169,4 +189,16 @@ void VM::step() { } else { assert(false); } + + + Logger::log( + "VM", + [&](std::ostream &out) { + out << "Executed " << cmd_to_str.at(poppedCmd) << "\n"; + if (!_s.null()) out << "s:" << _s << "\n"; + if (!_e.null()) out << "e:" << _e << "\n"; + if (!_c.null()) out << "c:" << _c << "\n"; + if (!_d.null()) out << "d:" << _d << "\n"; + }, + Logger::TRACE); } diff --git a/test/vm/CompilerTest.cpp b/test/vm/CompilerTest.cpp index ebd0cdc..10ad3bc 100644 --- a/test/vm/CompilerTest.cpp +++ b/test/vm/CompilerTest.cpp @@ -37,7 +37,7 @@ TEST(CompilerTest, BasicHello) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "3"); + EXPECT_EQ(ssout.str(), "3\n"); } TEST(CompilerTest, BasicLet) { @@ -52,7 +52,7 @@ TEST(CompilerTest, BasicLet) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "1"); + EXPECT_EQ(ssout.str(), "1\n"); } TEST(CompilerTest, BasicFn) { @@ -67,9 +67,48 @@ TEST(CompilerTest, BasicFn) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "5"); + EXPECT_EQ(ssout.str(), "5\n"); } +TEST(CompilerTest, BasicFn2) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (let ((plfn (lambda (a b) (- a b)))) (plfn 2 3)) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "-1\n"); +} + +TEST(CompilerTest, MultiLet) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (let ((plfn (lambda (a b) (- a b))) (plfn2 (lambda (a b) (- b a)))) (plfn 2 3)) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (let ((plfn (lambda (a b) (- a b))) (plfn2 (lambda (a b) (- b a)))) (plfn2 2 3)) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "-1\n1\n"); +} + + TEST(CompilerTest, BasicFnIfT) { std::stringstream ssin; std::stringstream ssout; @@ -82,7 +121,7 @@ TEST(CompilerTest, BasicFnIfT) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "1"); + EXPECT_EQ(ssout.str(), "1\n"); } TEST(CompilerTest, BasicFnIfF) { std::stringstream ssin; @@ -96,7 +135,7 @@ TEST(CompilerTest, BasicFnIfF) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "2"); + EXPECT_EQ(ssout.str(), "2\n"); } TEST(CompilerTest, RecursiveFn) { std::stringstream ssin; @@ -111,7 +150,7 @@ TEST(CompilerTest, RecursiveFn) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "55"); + EXPECT_EQ(ssout.str(), "55\n"); } TEST(CompilerTest, GlobalDefine) { @@ -126,7 +165,7 @@ TEST(CompilerTest, GlobalDefine) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "1"); + EXPECT_EQ(ssout.str(), "1\n"); } @@ -142,7 +181,61 @@ TEST(CompilerTest, GlobalDefineFn) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "5"); + EXPECT_EQ(ssout.str(), "5\n"); +} + +TEST(CompilerTest, GlobalDefineFnQuote) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (define (one) (quote (1 2))) EVAL LDC (one) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "(1 2)\n"); +} + +TEST(CompilerTest, GlobalDefineFnCar) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr( + "(LDC (define (carr l) (car l)) EVAL LDC (carr (quote (1 2))) EVAL PRINT LDC (carr (cdr (quote (1 2)))) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "1\n2\n"); +} + +TEST(CompilerTest, GlobalDefineFnEq) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (define (eqtest l) (= l ())) EVAL LDC (eqtest (quote ())) EVAL PRINT LDC (eqtest (nil)) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (define (eqtest l) (= l (nil))) EVAL LDC (eqtest (quote ())) EVAL PRINT LDC (eqtest (nil)) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "1\n1\n1\n1\n"); } TEST(CompilerTest, GlobalDefineFnMulti) { @@ -157,9 +250,25 @@ TEST(CompilerTest, GlobalDefineFnMulti) { vm.run(); } ssout.flush(); - EXPECT_EQ(ssout.str(), "6"); + EXPECT_EQ(ssout.str(), "6\n"); } +TEST(CompilerTest, GlobalDefineFnMultiTwo) { + std::stringstream ssin; + std::stringstream ssout; + { + + VM vm(ssin, ssout); + Parser parser; + parser.loadStr("(LDC (define (one x y) (- x y)) EVAL LDC (define (two x y) (one (+ x 1) y)) EVAL LDC (two 2 3) EVAL PRINT STOP)"); + vm.loadControl(parser.parseExpr()); + vm.run(); + } + ssout.flush(); + EXPECT_EQ(ssout.str(), "0\n"); +} + + TEST(CompilerTest, GlobalDefineFnRec) { std::stringstream ssin; std::stringstream ssout; @@ -176,5 +285,5 @@ TEST(CompilerTest, GlobalDefineFnRec) { Options::set_bool("command_strs", true); Logger::set_level("MemoryContext", Options::get_int("default_log_level")); ssout.flush(); - EXPECT_EQ(ssout.str(), "6765"); + EXPECT_EQ(ssout.str(), "6765\n"); } \ No newline at end of file