Skip to content

Commit

Permalink
feat: great user and handle exceptions in repl (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
hrzlgnm authored Dec 15, 2024
1 parent 57b0bd2 commit d73a9f9
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 66 deletions.
5 changes: 0 additions & 5 deletions .codespellignore

This file was deleted.

7 changes: 0 additions & 7 deletions .codespellrc

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM ghcr.io/void-linux/void-musl
RUN xbps-install -Syu || xbps-install -yu xbps \
&& xbps-install -yu \
&& xbps-install -y bash git cmake clang python3-codespell ninja clang clang-tools-extra cppcheck \
&& xbps-install -y bash git cmake clang ninja clang clang-tools-extra cppcheck wget tar gzip jq \
&& xbps-remove -Ooy


6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ jobs:
- name: Checkout
uses: classabbyamp/treeless-checkout-action@v1

- name: Lint
- name: 📐 Check clang-format
run: cmake -D FORMAT_COMMAND=clang-format -P cmake/lint.cmake

- name: Spell check
- name: 📝 Check spelling using typos-action
if: always()
run: cmake -P cmake/spell.cmake
uses: crate-ci/typos@d1c850b2b5d502763520c25fb4a6a1128ad99bd9 # v1.28.3

Build-And-Test:
needs: [Lint]
Expand Down
2 changes: 1 addition & 1 deletion HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Runs the executable target `monkey_exe`.

#### `spell-check` and `spell-fix`

These targets run the codespell tool on the codebase to check errors and to fix
These targets run the typos tool on the codebase to check errors and to fix
them respectively. Customization available using the `SPELL_COMMAND` cache
variable.

Expand Down
2 changes: 1 addition & 1 deletion cmake/lint-targets.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
set(FORMAT_PATTERNS
source/**/*.cpp source/**/*.hpp test/**/*.cpp test/**/*.hpp
source/*.cpp source/*.hpp test/*.cpp test/*.hpp
CACHE STRING
"; separated patterns relative to the project source dir to format")

Expand Down
36 changes: 18 additions & 18 deletions cmake/lint.cmake
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
cmake_minimum_required(VERSION 3.14)

macro(default name)
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
endmacro()

default(FORMAT_COMMAND clang-format)
default(PATTERNS source/**/*.cpp source/**/*.hpp test/**/*.cpp test/**/*.hpp)
default(PATTERNS source/*.cpp source/*.hpp test/*.cpp test/*.hpp)
default(FIX NO)

set(flag --output-replacements-xml)
set(args OUTPUT_VARIABLE output)
if(FIX)
set(flag -i)
set(args "")
set(flag -i)
set(args "")
endif()

file(GLOB_RECURSE files ${PATTERNS})
Expand All @@ -23,22 +23,22 @@ set(output "")
string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length)

foreach(file IN LISTS files)
execute_process(
execute_process(
COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE result ${args})
if(NOT result EQUAL "0")
message(FATAL_ERROR "'${file}': formatter returned with ${result}")
endif()
if(NOT FIX AND output MATCHES "\n<replacement offset")
string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
list(APPEND badly_formatted "${relative_file}")
endif()
set(output "")
if(NOT result EQUAL "0")
message(FATAL_ERROR "'${file}': formatter returned with ${result}")
endif()
if(NOT FIX AND output MATCHES "\n<replacement offset")
string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
list(APPEND badly_formatted "${relative_file}")
endif()
set(output "")
endforeach()

if(NOT badly_formatted STREQUAL "")
list(JOIN badly_formatted "\n" bad_list)
message("The following files are badly formatted:\n\n${bad_list}\n")
message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
list(JOIN badly_formatted "\n" bad_list)
message("The following files are badly formatted:\n\n${bad_list}\n")
message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
endif()
2 changes: 1 addition & 1 deletion cmake/spell-targets.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(SPELL_COMMAND codespell CACHE STRING "Spell checker to use")
set(SPELL_COMMAND typos CACHE STRING "Spell checker to use")

add_custom_target(
spell-check
Expand Down
16 changes: 8 additions & 8 deletions cmake/spell.cmake
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
cmake_minimum_required(VERSION 3.14)

macro(default name)
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
if(NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif()
endmacro()

default(SPELL_COMMAND codespell)
default(SPELL_COMMAND typos)
default(FIX NO)

set(flag "")
if(FIX)
set(flag -w)
set(flag --write-changes)
endif()

execute_process(
Expand All @@ -21,9 +21,9 @@ execute_process(
)

if(result EQUAL "65")
message(FATAL_ERROR "Run again with FIX=YES to fix these errors.")
message(FATAL_ERROR "Run again with FIX=YES to fix these errors.")
elseif(result EQUAL "64")
message(FATAL_ERROR "Spell checker printed the usage info. Bad arguments?")
message(FATAL_ERROR "Spell checker printed the usage info. Bad arguments?")
elseif(NOT result EQUAL "0")
message(FATAL_ERROR "Spell checker returned with ${result}")
message(FATAL_ERROR "Spell checker returned with ${result}")
endif()
96 changes: 76 additions & 20 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
#include <vector>

#include <ast/builtin_function_expression.hpp>
#include <gc.hpp>
#include <compiler/compiler.hpp>
#include <compiler/symbol_table.hpp>
#include <eval/environment.hpp>
#include <fmt/base.h>
#include <fmt/format.h>
#include <gc.hpp>
#include <lexer/lexer.hpp>
#include <parser/parser.hpp>
#include <vm/vm.hpp>
Expand Down Expand Up @@ -44,24 +44,50 @@ constexpr auto monkey_face = R"r(
auto monkey_business()
{
std::cerr << monkey_face;
std::cerr << "Woops! We ran into some monkey business here!\n";
std::cerr << "Woops! We ran into some monkey business here!\n ";
}

auto print_parse_errors(const std::vector<std::string>& errors)
{
monkey_business();
std::cerr << " parser errors: \n";
for (const auto& error : errors) {
std::cerr << "\t" << error << "\n";
std::cerr << " " << error << '\n';
}
}

auto show_error(std::string_view error_kind, std::string_view error_message)
{
std::cerr << "Whoops! We ran into some " << error_kind << " error: \n " << error_message << '\n';
}

auto print_compile_error(std::string_view error)
{
show_error("compiler", error);
}

auto print_eval_error(std::string_view error)
{
show_error("evaluation", error);
}

enum class engine : std::uint8_t
{
vm,
eval,
};

auto operator<<(std::ostream& strm, engine en) -> std::ostream&
{
switch (en) {
case engine::vm:
return strm << "vm";
case engine::eval:
return strm << "eval";
}
return strm << "unknown";
}

struct command_line_args
{
bool help {};
Expand All @@ -70,6 +96,19 @@ struct command_line_args
std::string_view file;
};

auto get_logged_in_user() -> std::string
{
// NOLINTBEGIN(concurrency-mt-unsafe)
const char* username = getenv("USER");

if (username == nullptr) {
username = getenv("USERNAME");
}
// NOLINTEND(concurrency-mt-unsafe)

return (username != nullptr) ? std::string(username) : "Unknown";
}

[[noreturn]] auto show_usage(std::string_view program, std::string_view error_msg = {})
{
auto exit_code = EXIT_SUCCESS;
Expand Down Expand Up @@ -120,7 +159,7 @@ auto run_file(const command_line_args& opts) -> int
{
std::ifstream ifs(std::string {opts.file});
if (!ifs) {
std::cerr << "ERROR: could not open file: " << opts.file << "\n";
std::cerr << "ERROR: could not open file: " << opts.file << '\n';
return 1;
}
const std::string contents {(std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())};
Expand All @@ -138,7 +177,7 @@ auto run_file(const command_line_args& opts) -> int
machine.run();
const auto* result = machine.last_popped();
if (!result->is(object::object_type::null)) {
std::cout << result->inspect() << "\n";
std::cout << result->inspect() << '\n';
}
} else {
auto* global_env = make<environment>();
Expand All @@ -147,52 +186,69 @@ auto run_file(const command_line_args& opts) -> int
}
const auto* result = prgrm->eval(global_env);
if (!result->is(object::object_type::null)) {
std::cout << result->inspect() << "\n";
std::cout << result->inspect() << '\n';
}
}
return 0;
}

auto run_repl(const command_line_args& opts) -> int
{
std::cout << "Hello " << get_logged_in_user() << ". This is the Monkey programming language using engine "
<< opts.mode << ".\n";
std::cout << "Feel free to type in commands\n";
auto* global_env = make<environment>();
auto* symbols = symbol_table::create();
constants consts;
constants globals(globals_size);
for (auto idx = 0UL; const auto& builtin : builtin_function_expression::builtins) {
global_env->set(builtin->name, make<function_object>(builtin, nullptr));
global_env->set(builtin->name, make<builtin_object>(builtin));
symbols->define_builtin(idx, builtin->name);
idx++;
}
auto show_prompt = []() { std::cout << prompt; };
auto input = std::string {};
std::cout << prompt;
show_prompt();
while (getline(std::cin, input)) {
auto lxr = lexer {input};
auto prsr = parser {lxr};
auto* prgrm = prsr.parse_program();
if (!prsr.errors().empty()) {
print_parse_errors(prsr.errors());
show_prompt();
continue;
}
if (opts.mode == engine::vm) {
auto cmplr = compiler::create_with_state(&consts, symbols);
cmplr.compile(prgrm);
auto machine = vm::create_with_state(cmplr.byte_code(), &globals);
machine.run();
const auto* result = machine.last_popped();
if (result != nullptr && !result->is(object::object_type::null)) {
std::cout << result->inspect() << "\n";
try {
auto cmplr = compiler::create_with_state(&consts, symbols);
cmplr.compile(prgrm);
auto machine = vm::create_with_state(cmplr.byte_code(), &globals);
machine.run();
const auto* result = machine.last_popped();
if (result != nullptr && !result->is(object::object_type::null)) {
std::cout << result->inspect() << '\n';
}
} catch (const std::exception& e) {
print_compile_error(e.what());
show_prompt();
continue;
}
} else {
const auto* result = prgrm->eval(global_env);
if (result != nullptr && !result->is(object::object_type::null)) {
std::cout << result->inspect() << "\n";
try {
const auto* result = prgrm->eval(global_env);
if (result != nullptr && !result->is(object::object_type::null)) {
std::cout << result->inspect() << '\n';
}
} catch (const std::exception& e) {
print_eval_error(e.what());
show_prompt();
continue;
}
}
if (opts.debug_env) {
global_env->debug();
}
std::cout << prompt;
show_prompt();
}
return 0;
}
Expand All @@ -213,7 +269,7 @@ auto main(int argc, char* argv[]) -> int

} catch (const std::exception& e) {
monkey_business();
std::cerr << "Caught an exception: " << e.what() << "\n";
std::cerr << "Caught an exception: " << e.what() << '\n';
return 1;
}
}
4 changes: 3 additions & 1 deletion typos.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[files]
extend-exclude = ["test/doctest/doctest/doctest.h"]

[default.extend-words]
SEH = "SEH"
fals = "fals"
tru = "tru"
thr = "thr"

0 comments on commit d73a9f9

Please sign in to comment.