diff --git a/include/musique.hh b/include/musique.hh index 68e4b3e..daf6d88 100644 --- a/include/musique.hh +++ b/include/musique.hh @@ -297,6 +297,17 @@ struct [[nodiscard("This value may contain critical error, so it should NOT be i } } + /// Fill error location if it's empty and we have an error + inline Result with_location(Location location) && + { + if (!Storage::has_value()) { + if (auto& target = Storage::error().location; !target || target == Location{}) { + target = location; + } + } + return *this; + } + inline tl::expected to_expected() && { return *static_cast(this); diff --git a/src/errors.cc b/src/errors.cc index e94eaa8..9b69c2b 100644 --- a/src/errors.cc +++ b/src/errors.cc @@ -156,8 +156,9 @@ std::ostream& operator<<(std::ostream& os, Error const& err) auto const loc = err.location; auto const print_error_line = [&] { - if (loc->filename != "") { + if (loc) { Lines::the.print(os, std::string(loc->filename), loc->line, loc->line); + os << '\n'; } }; @@ -179,7 +180,6 @@ std::ostream& operator<<(std::ostream& os, Error const& err) print_error_line(); - os << "\n"; os << "Variables can only be references in scope (block) where they been created\n"; os << "or from parent blocks to variable block\n"; }, @@ -202,7 +202,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err) print_error_line(); if (err.reason == std::errc::result_out_of_range) { - os << "\nDeclared number is outside of valid range of numbers that can be represented.\n"; + os << "Declared number is outside of valid range of numbers that can be represented.\n"; os << "Only numbers in range [" << Min << ", " << Max << "] are supported\n"; } }, @@ -214,7 +214,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err) print_error_line(); - os << pretty::begin_comment << "\nThis error is considered an internal one. It should not be displayed to the end user.\n"; + os << pretty::begin_comment << "This error is considered an internal one. It should not be displayed to the end user.\n"; os << "\n"; os << "This error message is temporary and will be replaced by better one in the future\n"; os << pretty::end; @@ -226,7 +226,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err) print_error_line(); if (err.what == "var") { - os << "\nIf you want to create variable inside expression try wrapping them inside parentheses like this:\n"; + os << "If you want to create variable inside expression try wrapping them inside parentheses like this:\n"; os << " 10 + (var i = 20)\n"; } }, @@ -237,7 +237,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err) print_error_line(); if (err.type_name == "chord") { - os << "\nTry renaming to different name or appending with something that is not part of chord literal like 'x'\n"; + os << "Try renaming to different name or appending with something that is not part of chord literal like 'x'\n"; os << pretty::begin_comment << "\nMusical notation names are reserved for chord and note notations,\n" @@ -251,6 +251,9 @@ std::ostream& operator<<(std::ostream& os, Error const& err) case errors::Unsupported_Types_For::Function: { os << "I tried to call function '" << err.name << "' but you gave me wrong types for it!\n"; + + print_error_line(); + os << "Make sure that all values matches one of supported signatures listed below!\n"; os << '\n'; @@ -264,6 +267,8 @@ std::ostream& operator<<(std::ostream& os, Error const& err) os << "I tried and failed to evaluate operator '" << err.name << "' due to values with wrong types provided\n"; os << "Make sure that both values matches one of supported signatures listed below!\n"; os << '\n'; + print_error_line(); + if (err.name == "+") { os << "Addition only supports:\n"; } else { os << "Operator '" << err.name << "' only supports:\n"; } @@ -276,7 +281,24 @@ std::ostream& operator<<(std::ostream& os, Error const& err) } }, - [&](errors::Not_Callable const&) { unimplemented(); }, + [&](errors::Not_Callable const& err) { + os << "Value of type " << err.type << " cannot be called.\n"; + os << "\n"; + + print_error_line(); + + os << "Only values of this types can be called:\n"; + os << " - musical values like c, c47, (c&g) can be called to provide octave and duration\n"; + os << " - blocks can be called to compute their body like this: [i | i + 1] 3\n"; + os << " - builtin functions like if, par, floor\n"; + os << "\n"; + + os << pretty::begin_comment; + os << "Parhaps you forgot to include semicolon between successive elements?\n"; + os << "Common problem is writing [4 3] as list with elements 4 and 3,\n"; + os << "when correct code for such expression would be [4;3]\n"; + os << pretty::end; + }, [&](errors::Undefined_Operator const&) { unimplemented(); }, [&](errors::Unexpected_Keyword const&) { unimplemented(); }, [&](errors::Unexpected_Empty_Source const&) { unimplemented(); } diff --git a/src/interpreter.cc b/src/interpreter.cc index 583bf8e..b22c172 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -87,18 +87,20 @@ Result Interpreter::eval(Ast &&ast) 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))); + return *v = Try(eval(std::move(rhs)).with_location(ast.token.location)); } if (ast.token.source == "and" || ast.token.source == "or") { auto lhs = std::move(ast.arguments.front()); auto rhs = std::move(ast.arguments.back()); - auto result = Try(eval(std::move(lhs))); + auto lhs_loc = lhs.location, rhs_loc = rhs.location; + + auto result = Try(eval(std::move(lhs)).with_location(std::move(lhs_loc))); if (ast.token.source == "or" ? result.truthy() : result.falsy()) { return result; } else { - return eval(std::move(rhs)); + return eval(std::move(rhs)).with_location(rhs_loc); } } @@ -115,12 +117,15 @@ Result Interpreter::eval(Ast &&ast) auto lhs = std::move(ast.arguments.front()); auto rhs = std::move(ast.arguments.back()); + auto const rhs_loc = rhs.location; 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(op->second(*this, { *v, Try(eval(std::move(rhs))) })); + return *v = Try(op->second(*this, { + *v, Try(eval(std::move(rhs)).with_location(rhs_loc)) + }).with_location(ast.token.location)); } return Error { @@ -132,10 +137,11 @@ 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)))); + auto const a_loc = a.location; + values.push_back(Try(eval(std::move(a)).with_location(a_loc))); } - return op->second(*this, std::move(values)); + return op->second(*this, std::move(values)).with_location(ast.token.location); } break; @@ -153,6 +159,7 @@ Result Interpreter::eval(Ast &&ast) case Ast::Type::Call: { + auto call_location = ast.arguments.front().location; Value func = Try(eval(std::move(ast.arguments.front()))); std::vector values; @@ -160,7 +167,8 @@ Result Interpreter::eval(Ast &&ast) for (auto& a : std::span(ast.arguments).subspan(1)) { values.push_back(Try(eval(std::move(a)))); } - return std::move(func)(*this, std::move(values)); + return std::move(func)(*this, std::move(values)) + .with_location(std::move(call_location)); } case Ast::Type::Variable_Declaration: diff --git a/src/main.cc b/src/main.cc index a9cc15d..d460335 100644 --- a/src/main.cc +++ b/src/main.cc @@ -53,6 +53,17 @@ static std::string_view pop(std::span &span) std::exit(1); } +void print_repl_help() +{ + std::cout << + "List of all available commands of Musique interactive mode:\n" + ":exit - quit interactive mode\n" + ":help - prints this help message\n" + ":! - allows for execution of any shell command\n" + ":clear - clears screen\n" + ; +} + /// Trim spaces from left an right static void trim(std::string_view &s) { @@ -301,6 +312,7 @@ static Result Main(std::span args) if (command == "exit") { break; } if (command == "clear") { std::cout << "\x1b[1;1H\x1b[2J" << std::flush; continue; } if (command.starts_with('!')) { Ignore(system(command.data() + 1)); continue; } + if (command == "help") { print_repl_help(); continue; } std::cerr << "musique: error: unrecognized REPL command '" << command << '\'' << std::endl; continue; } diff --git a/src/value.cc b/src/value.cc index f4c3781..2fbc326 100644 --- a/src/value.cc +++ b/src/value.cc @@ -168,11 +168,8 @@ Result Value::operator()(Interpreter &i, std::vector args) return *this; } default: - // TODO Fill location return Error { - .details = errors::Not_Callable { - .type = type_name(type) - }, + .details = errors::Not_Callable { .type = type_name(type) }, .location = std::nullopt, }; }