Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

Commit 7b03e2b

Browse files
committed
Implement REPL
1 parent 1a9238d commit 7b03e2b

8 files changed

Lines changed: 97 additions & 29 deletions

File tree

‎CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_library(intp STATIC
2424
add_executable(lbd
2525
${CMAKE_SOURCE_DIR}/src/main.cpp
2626
${CMAKE_SOURCE_DIR}/src/cmd.cpp
27+
${CMAKE_SOURCE_DIR}/src/repl.cpp
2728
)
2829

2930
target_link_libraries(lbd PRIVATE fe intp)

‎include/lbd/fe/lexer.h‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
#include <vector>
77

88
namespace fe::lexer {
9+
struct FromFile {
10+
};
11+
12+
struct FromRepl {
13+
};
14+
915
struct Lexer {
1016
std::string source;
1117
size_t pos = 0;
1218
size_t row = 1;
1319
size_t col = 1;
1420
std::string filepath;
1521

16-
explicit Lexer(const std::string &filepath);
22+
explicit Lexer(const std::string &filepath, FromFile);
23+
24+
explicit Lexer(const std::string &str, FromRepl);
1725

1826
token::Token next_token();
1927

‎include/lbd/intp/interpreter.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ namespace intp::interp {
7777
/// Program Driver
7878
struct Result {
7979
std::shared_ptr<Env> global_env;
80+
Value value;
8081
};
8182

82-
Result interpret(const fe::ast::Program &program);
83+
Result interpret(const fe::ast::Program &program, std::optional<std::shared_ptr<Env> > global_env = std::nullopt);
8384

8485
/// Add builtins Native Functions into Environment
8586
void install_builtins(const std::shared_ptr<Env> &env);

‎include/lbd/repl.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
namespace repl {
4+
void loop(bool debug = false);
5+
}

‎src/fe/lexer.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace fe::lexer {
2929
return {row, col, filepath};
3030
}
3131

32-
Lexer::Lexer(const std::string &filepath) : filepath(filepath) {
32+
Lexer::Lexer(const std::string &filepath, FromFile) : filepath(filepath) {
3333
std::ifstream ifs(filepath);
3434
if (!ifs) {
3535
std::cerr << "error: could not open file "
@@ -42,6 +42,9 @@ namespace fe::lexer {
4242
source = ss.str();
4343
}
4444

45+
Lexer::Lexer(const std::string &str, FromRepl) : source(str) {
46+
}
47+
4548
token::Token Lexer::next_token() {
4649
char c = peek();
4750
// Skip whitespace

‎src/intp/interpreter.cpp‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,23 +243,25 @@ namespace intp::interp {
243243
thunk->set(&def_ast_node.expr, env, def_ast_node.expr.get_loc());
244244
}
245245

246-
Result interpret(const fe::ast::Program &program) {
247-
const auto global_env = std::make_shared<Env>();
248-
install_builtins(global_env);
246+
Result interpret(const fe::ast::Program &program, std::optional<std::shared_ptr<Env> > global_env) {
247+
if (!global_env) {
248+
global_env = std::make_shared<Env>();
249+
}
250+
Value result_value;
251+
install_builtins(*global_env);
249252
for (const auto &[value]: program.nodes) {
250253
std::visit([&]<typename T0>(T0 &&arg) {
251254
using T = std::decay_t<T0>;
252255
if constexpr (std::is_same_v<T, fe::ast::Expression>) {
253-
eval_expr(arg, global_env);
254-
// The top level Value returned by this is not needed
256+
result_value = eval_expr(arg, *global_env);
255257
} else if constexpr (std::is_same_v<T, fe::ast::DefAstNode>) {
256-
bind_def_ast_node_lazy(arg, global_env);
258+
bind_def_ast_node_lazy(arg, *global_env);
257259
} else {
258260
STATIC_ASSERT_UNREACHABLE_T(T, "unhandled program node");
259261
}
260262
}, value);
261263
}
262-
return {global_env};
264+
return {*global_env, result_value};
263265
}
264266

265267
void install_builtins(const std::shared_ptr<Env> &env) {

‎src/main.cpp‎

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <lbd/fe/parser.h>
55
#include <lbd/intp/interpreter.h>
66
#include <lbd/cmd.h>
7+
#include <lbd/repl.h>
78
#include <string>
89
#include <vector>
910

@@ -13,27 +14,26 @@ int main(const int argc, char **argv) {
1314
const auto &[filepath, show_help, repl, debug] = cmd::parse_args(argc, argv, program_name);
1415
if (show_help) {
1516
cmd::print_help(std::cout, argv[0]);
16-
return 0;
17+
return EXIT_SUCCESS;
1718
}
1819
if (repl) {
19-
std::cout << "entering REPL mode (not yet implemented)..." << std::endl;
20-
// TODO: implement REPL
21-
return 0;
22-
}
23-
// Lex
24-
auto lexer_v = fe::lexer::Lexer(*filepath);
25-
const std::vector<fe::token::Token> tokens = lexer_v.lex_all();
26-
if (debug) {
27-
for (const fe::token::Token &token: tokens) {
28-
std::cout << token << std::endl;
20+
repl::loop(debug);
21+
} else {
22+
// Lex
23+
auto lexer_v = fe::lexer::Lexer(*filepath, fe::lexer::FromFile{});
24+
const std::vector<fe::token::Token> tokens = lexer_v.lex_all();
25+
if (debug) {
26+
for (const fe::token::Token &token: tokens) {
27+
std::cout << token << std::endl;
28+
}
2929
}
30+
// Parse
31+
const auto parser = fe::parser::Parser(tokens);
32+
if (debug) {
33+
std::cout << parser.program << std::endl;
34+
}
35+
// Interpret
36+
auto result = intp::interp::interpret(parser.program);
37+
return EXIT_SUCCESS;
3038
}
31-
// Parse
32-
const auto parser = fe::parser::Parser(tokens);
33-
if (debug) {
34-
std::cout << parser.program << std::endl;
35-
}
36-
// Interpret
37-
auto result = intp::interp::interpret(parser.program);
38-
return EXIT_SUCCESS;
3939
}

‎src/repl.cpp‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <iostream>
2+
#include <lbd/repl.h>
3+
4+
#include "lbd/fe/lexer.h"
5+
#include "lbd/fe/parser.h"
6+
#include "lbd/intp/interpreter.h"
7+
8+
namespace repl {
9+
void loop(const bool debug) {
10+
std::cout << "Lambda Discipline REPL. Type :quit to exit." << std::endl;
11+
std::string line;
12+
std::optional<std::shared_ptr<intp::interp::Env> > shared_global_env = std::nullopt;
13+
while (true) {
14+
std::cout << "lbd> ";
15+
if (!std::getline(std::cin, line)) {
16+
break; // EOF (C-d / C-z)
17+
}
18+
if (line == ":quit" || line == ":q" || line == ":exit") {
19+
std::cout << "Exiting REPL...\n";
20+
break;
21+
}
22+
if (line.empty()) {
23+
continue;
24+
}
25+
try {
26+
// Lex
27+
fe::lexer::Lexer lexer(line, fe::lexer::FromRepl{});
28+
const std::vector<fe::token::Token> tokens = lexer.lex_all();
29+
if (debug) {
30+
for (const auto &tok: tokens) {
31+
std::cout << tok << "\n";
32+
}
33+
}
34+
// Parse
35+
fe::parser::Parser parser(tokens);
36+
if (debug) {
37+
std::cout << parser.program << "\n";
38+
}
39+
// Interpret
40+
const auto [global_env, value] = intp::interp::interpret(parser.program, shared_global_env);
41+
std::cout << "=> " << value << "\n";
42+
shared_global_env = global_env;
43+
} catch (const std::exception &ex) {
44+
std::cerr << "error: " << ex.what() << "\n";
45+
}
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)