diff --git a/etc/editor/musique.vim b/etc/editor/musique.vim index 6abc2cd..318e517 100644 --- a/etc/editor/musique.vim +++ b/etc/editor/musique.vim @@ -18,7 +18,7 @@ syn match musiqueInteger display "[0-9][0-9_]*" syn keyword musiqueConstant true false nil -syn keyword musiqueDefaultBuiltins if len play +syn keyword musiqueDefaultBuiltins if len play permute par shuffle chord bpm oct note_on note_off flat update syn keyword musiqueLinuxBuiltins say syn match musiqueComment "--.*$" diff --git a/etc/tools/test.py b/etc/tools/test.py index 04a9d90..5c35402 100755 --- a/etc/tools/test.py +++ b/etc/tools/test.py @@ -116,18 +116,33 @@ def record_tests(file_paths: list): tc = Test_Case.from_run(res, []) tc.save(test_case_file) +def add_tests(file_paths: list): + to_be_added = [] + + for program_file in file_paths: + test_case_file = find_path_for_test_case(program_file) + if not os.path.exists(test_case_file): + print(f"Add test {program_file}? (yes/no)") + if "yes".startswith(input().strip().lower()): + to_be_added.append(program_file) + + record_tests(to_be_added) + + # list of files to test def main(): file_paths, mode = [], run_tests if len(argv) < 2: - print("[ERROR] Expected mode argument (either 'record' or 'test')") + print("[ERROR] Expected mode argument (either 'record', 'add' or 'test')") exit(1) if argv[1] == "test": mode = run_tests elif argv[1] == "record": mode = record_tests + elif argv[1] == "add": + mode = add_tests else: print(f"[ERROR] Unrecognized mode '{argv[1]}'") exit(1) @@ -142,6 +157,8 @@ def main(): file_paths.extend(glob(f"{path}/*.mq")) else: file_paths.append(path) + elif mode == add_tests: + print("Adding: " + path) mode(file_paths) diff --git a/examples/.tests_cache/assigments-state-management.mq.json b/examples/.tests_cache/assigments-state-management.mq.json new file mode 100644 index 0000000..0ec0fa6 --- /dev/null +++ b/examples/.tests_cache/assigments-state-management.mq.json @@ -0,0 +1,6 @@ +{ + "returncode": 0, + "stdout": "10\n20\n30\n20\n20\n20\n[1; 2; 3]\n[1; 10; 3]\n[1; 3; 3]\n", + "stderr": "", + "flags": [] +} \ No newline at end of file diff --git a/examples/.tests_cache/permutations.mq.json b/examples/.tests_cache/permutations.mq.json new file mode 100644 index 0000000..a1dc3cf --- /dev/null +++ b/examples/.tests_cache/permutations.mq.json @@ -0,0 +1,6 @@ +{ + "returncode": 0, + "stdout": "[1; 2; 3; 4; 5]\n[1; 2; 3; 5; 4]\n[1; 2; 4; 3; 5]\n[1; 2; 4; 5; 3]\n[1; 2; 5; 3; 4]\n[1; 2; 5; 4; 3]\n[1; 3; 2; 4; 5]\n[1; 3; 2; 5; 4]\n[1; 3; 4; 2; 5]\n[1; 3; 4; 5; 2]\n[1; 3; 5; 2; 4]\n[1; 3; 5; 4; 2]\n[1; 4; 2; 3; 5]\n[1; 4; 2; 5; 3]\n[1; 4; 3; 2; 5]\n[1; 4; 3; 5; 2]\n[1; 4; 5; 2; 3]\n[1; 4; 5; 3; 2]\n[1; 5; 2; 3; 4]\n[1; 5; 2; 4; 3]\n[1; 5; 3; 2; 4]\n[1; 5; 3; 4; 2]\n[1; 5; 4; 2; 3]\n[1; 5; 4; 3; 2]\n[2; 1; 3; 4; 5]\n[2; 1; 3; 5; 4]\n[2; 1; 4; 3; 5]\n[2; 1; 4; 5; 3]\n[2; 1; 5; 3; 4]\n[2; 1; 5; 4; 3]\n[2; 3; 1; 4; 5]\n[2; 3; 1; 5; 4]\n[2; 3; 4; 1; 5]\n[2; 3; 4; 5; 1]\n[2; 3; 5; 1; 4]\n[2; 3; 5; 4; 1]\n[2; 4; 1; 3; 5]\n[2; 4; 1; 5; 3]\n[2; 4; 3; 1; 5]\n[2; 4; 3; 5; 1]\n[2; 4; 5; 1; 3]\n[2; 4; 5; 3; 1]\n[2; 5; 1; 3; 4]\n[2; 5; 1; 4; 3]\n[2; 5; 3; 1; 4]\n[2; 5; 3; 4; 1]\n[2; 5; 4; 1; 3]\n[2; 5; 4; 3; 1]\n[3; 1; 2; 4; 5]\n[3; 1; 2; 5; 4]\n[3; 1; 4; 2; 5]\n[3; 1; 4; 5; 2]\n[3; 1; 5; 2; 4]\n[3; 1; 5; 4; 2]\n[3; 2; 1; 4; 5]\n[3; 2; 1; 5; 4]\n[3; 2; 4; 1; 5]\n[3; 2; 4; 5; 1]\n[3; 2; 5; 1; 4]\n[3; 2; 5; 4; 1]\n[3; 4; 1; 2; 5]\n[3; 4; 1; 5; 2]\n[3; 4; 2; 1; 5]\n[3; 4; 2; 5; 1]\n[3; 4; 5; 1; 2]\n[3; 4; 5; 2; 1]\n[3; 5; 1; 2; 4]\n[3; 5; 1; 4; 2]\n[3; 5; 2; 1; 4]\n[3; 5; 2; 4; 1]\n[3; 5; 4; 1; 2]\n[3; 5; 4; 2; 1]\n[4; 1; 2; 3; 5]\n[4; 1; 2; 5; 3]\n[4; 1; 3; 2; 5]\n[4; 1; 3; 5; 2]\n[4; 1; 5; 2; 3]\n[4; 1; 5; 3; 2]\n[4; 2; 1; 3; 5]\n[4; 2; 1; 5; 3]\n[4; 2; 3; 1; 5]\n[4; 2; 3; 5; 1]\n[4; 2; 5; 1; 3]\n[4; 2; 5; 3; 1]\n[4; 3; 1; 2; 5]\n[4; 3; 1; 5; 2]\n[4; 3; 2; 1; 5]\n[4; 3; 2; 5; 1]\n[4; 3; 5; 1; 2]\n[4; 3; 5; 2; 1]\n[4; 5; 1; 2; 3]\n[4; 5; 1; 3; 2]\n[4; 5; 2; 1; 3]\n[4; 5; 2; 3; 1]\n[4; 5; 3; 1; 2]\n[4; 5; 3; 2; 1]\n[5; 1; 2; 3; 4]\n[5; 1; 2; 4; 3]\n[5; 1; 3; 2; 4]\n[5; 1; 3; 4; 2]\n[5; 1; 4; 2; 3]\n[5; 1; 4; 3; 2]\n[5; 2; 1; 3; 4]\n[5; 2; 1; 4; 3]\n[5; 2; 3; 1; 4]\n[5; 2; 3; 4; 1]\n[5; 2; 4; 1; 3]\n[5; 2; 4; 3; 1]\n[5; 3; 1; 2; 4]\n[5; 3; 1; 4; 2]\n[5; 3; 2; 1; 4]\n[5; 3; 2; 4; 1]\n[5; 3; 4; 1; 2]\n[5; 3; 4; 2; 1]\n[5; 4; 1; 2; 3]\n[5; 4; 1; 3; 2]\n[5; 4; 2; 1; 3]\n[5; 4; 2; 3; 1]\n[5; 4; 3; 1; 2]\n[5; 4; 3; 2; 1]\n", + "stderr": "", + "flags": [] +} \ No newline at end of file diff --git a/examples/assigments-state-management.mq b/examples/assigments-state-management.mq new file mode 100644 index 0000000..9111ed1 --- /dev/null +++ b/examples/assigments-state-management.mq @@ -0,0 +1,27 @@ +---------------------------------- +Simple variable assigment +---------------------------------- +var x = 10; +var y = 20; +say x; +say y; + +x = 30; +say x; +say y; + +x = y; +say x; +say y; + +---------------------------------- +Array updates +---------------------------------- +var array = flat 1 2 3; +say array; + +array = update array 1 10; +say array; + +array = update array 1 (array.2); +say array; diff --git a/src/interpreter.cc b/src/interpreter.cc index 35aa26b..8ab35c1 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -14,6 +14,15 @@ static inline bool typecheck(std::vector const& args, auto const& ...expe } (std::make_index_sequence{}); } +/// Intrinsic implementation primitive providing a short way to move values based on matched type signature +static inline bool typecheck_front(std::vector const& args, auto const& ...expected_types) +{ + return (args.size() >= sizeof...(expected_types)) && + [&args, expected_types...](std::index_sequence) { + return ((expected_types == args[I].type) && ...); + } (std::make_index_sequence{}); +} + /// Intrinsic implementation primitive providing a short way to move values based on matched type signature template static inline auto move_from(std::vector& args) @@ -27,8 +36,9 @@ static inline auto move_from(std::vector& args) template struct Shape { - static inline auto move_from(std::vector& args) { return ::move_from(args); } - static inline auto typecheck(std::vector& args) { return ::typecheck(args, Types...); } + static inline auto move_from(std::vector& args) { return ::move_from(args); } + static inline auto typecheck(std::vector& args) { return ::typecheck(args, Types...); } + static inline auto typecheck_front(std::vector& args) { return ::typecheck_front(args, Types...); } }; /// Returns if type can be indexed @@ -295,6 +305,27 @@ Interpreter::Interpreter() register_note_length_constants(); + global.force_define("update", +[](Interpreter &i, std::vector args) -> Result { + assert(args.size() == 3, "Update requires 3 arguments"); // TODO(assert) + using Eager_And_Number = Shape; + using Lazy_And_Number = Shape; + + if (Eager_And_Number::typecheck_front(args)) { + auto [v, index] = Eager_And_Number::move_from(args); + v.elements[index.as_int()] = std::move(args.back()); + return Value::from(std::move(v)); + } + + if (Lazy_And_Number::typecheck_front(args)) { + auto [v, index] = Lazy_And_Number::move_from(args); + auto array = Try(into_flat_array(i, { Value::from(std::move(v)) })); + array.elements[index.as_int()] = std::move(args.back()); + return Value::from(std::move(array)); + } + + unimplemented("Wrong shape of update function"); + }); + global.force_define("typeof", +[](Interpreter&, std::vector args) -> Result { assert(args.size() == 1, "typeof expects only one argument"); return Value::from(std::string(type_name(args.front().type))); @@ -446,11 +477,20 @@ Result Interpreter::eval(Ast &&ast) case Ast::Type::Binary: { - std::vector values; - values.reserve(ast.arguments.size()); + assert(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long"); + + if (ast.token.source == "=") { + auto lhs = std::move(ast.arguments.front()); + auto rhs = std::move(ast.arguments.back()); + assert(lhs.type == Ast::Type::Literal && lhs.token.type == Token::Type::Symbol, + "Currently LHS of assigment must be an identifier"); // TODO(assert) + + Value *v = env->find(std::string(lhs.token.source)); + assert(v, "Cannot resolve variable: "s + std::string(lhs.token.source)); // TODO(assert) + return *v = Try(eval(std::move(rhs))); + } if (ast.token.source == "and" || ast.token.source == "or") { - assert(ast.arguments.size() == 2, "Expected arguments of binary operation to be 2 long"); auto lhs = std::move(ast.arguments.front()); auto rhs = std::move(ast.arguments.back()); @@ -463,7 +503,6 @@ Result Interpreter::eval(Ast &&ast) } auto op = operators.find(std::string(ast.token.source)); - if (op == operators.end()) { return Error { .details = errors::Undefined_Operator { .op = ast.token.source }, @@ -471,6 +510,8 @@ Result Interpreter::eval(Ast &&ast) }; } + std::vector values; + values.reserve(ast.arguments.size()); for (auto& a : ast.arguments) { values.push_back(Try(eval(std::move(a)))); }