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

Commit 1704ef1

Browse files
committed
Implement use and :load language feature
1 parent 99210cb commit 1704ef1

5 files changed

Lines changed: 116 additions & 49 deletions

File tree

‎examples/std.lbd‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
-- TODO: Support for `use`
2-
31
-- booleans and conditions
42
true: Bool = \x: Any. \y: Any. x
53
false: Bool = \x: Any. \y: Any. y

‎examples/test.lbd‎

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
-- Define Pi as constant
2-
pi: Float = 3.14
1+
use "examples/std.lbd"
32

4-
-- Define function to calculate square
5-
square: Float -> Float = \x: Float. (mul x x)
6-
7-
-- Define function to calculate area of circle
8-
area_of_circle: Float -> Float = \r: Float.
9-
(mul pi (square r))
10-
11-
-- Define function to calculate volume of cylinder
12-
volume_of_cylinder: Float -> Float -> Float = \r: Float.
13-
\h: Float. (mul (area_of_circle r) h)
14-
15-
-- Calculate the volume of a cylinder
16-
(print "Volume of cylinder is: ")
17-
(print (volume_of_cylinder 5.0 10.0))
3+
(print (eq 1 0))

‎src/cmd.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace cmd {
44
void print_help(std::ostream &os, const std::string &program_name) {
55
os << "usage: " << program_name << " [options]\n\n"
66
<< "options:\n"
7-
<< " -f, --file <filename> Specify input source file to run\n"
7+
<< " -f, --file <filepath> Specify input source filepath to run\n"
88
<< " -h, --help Show this help message and exit\n"
99
<< " -d, --debug Enable debug mode\n"
1010
<< " -r, --repl Run in interactive REPL node" << std::endl;

‎src/fe/parser.cpp‎

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
#include <lbd/fe/loc.h>
2+
#include <lbd/fe/lexer.h>
23
#include <lbd/fe/parser.h>
34
#include <lbd/string_escape.h>
5+
#include <unordered_set>
6+
#include <filesystem>
47

58
namespace fe::parser {
9+
static std::unordered_set<std::string> loaded_files;
10+
611
Parser::Parser(const std::vector<token::Token> &tokens) {
712
size_t i = 0;
813
program = ast::Program{build_ast(tokens, i)};
@@ -137,26 +142,53 @@ namespace fe::parser {
137142
return ast::DefAstNode{def_name, typ, std::move(expr), loc};
138143
}
139144

145+
static std::string get_abs_path(const std::string &path) {
146+
return std::filesystem::absolute(path).string();
147+
}
148+
149+
static void process_use_file(std::vector<ast::AstNode> &nodes, const std::string &filepath) {
150+
const std::string abs_path = get_abs_path(filepath);
151+
if (loaded_files.contains(abs_path)) {
152+
// Circular Dependency or Duplicate Load
153+
return;
154+
}
155+
loaded_files.insert(abs_path);
156+
auto lexer_v = lexer::Lexer(filepath, lexer::FromFile{});
157+
const auto tokens = lexer_v.lex_all();
158+
// TODO: Accept Options and Add a debug flag for printing Lexed Tokens
159+
for (Parser parser(tokens); auto &node: parser.program.nodes) {
160+
nodes.emplace_back(std::move(node));
161+
}
162+
}
163+
140164
std::vector<ast::AstNode> Parser::build_ast(const std::vector<token::Token> &tokens, size_t &i) {
141165
std::vector<ast::AstNode> nodes;
142166
while (!std::holds_alternative<token::Eof>(tokens[i].typ)) {
143167
const token::Token &tok = tokens[i];
144-
nodes.push_back(std::visit([&]<typename T0>(T0 &&) {
168+
std::visit([&]<typename T0>(T0 &&) {
145169
using T = std::decay_t<T0>;
146-
if (std::is_same_v<T, token::Iden>) {
147-
return ast::AstNode{std::move(parse_def_ast_node(tokens, i))};
148-
}
149-
if (std::is_same_v<T, token::String>
150-
|| std::is_same_v<T, token::Float>
151-
|| std::is_same_v<T, token::BackwardSlash>
152-
|| std::is_same_v<T, token::OpenParen>) {
153-
return ast::AstNode{std::move(parse_expression(tokens, i))};
170+
if constexpr (std::is_same_v<T, token::Iden>) {
171+
if (const auto iden_value = std::get<token::Iden>(tokens[i].typ).value; iden_value == "use") {
172+
++i; // eat "use"
173+
assert_token<token::String>(tokens, i);
174+
const std::string filepath = unescape_string(std::get<token::String>(tokens[i].typ).value);
175+
++i; // eat <filepath>
176+
process_use_file(nodes, filepath);
177+
} else {
178+
nodes.push_back(ast::AstNode{std::move(parse_def_ast_node(tokens, i))});
179+
}
180+
} else if constexpr (std::is_same_v<T, token::String> ||
181+
std::is_same_v<T, token::Float> ||
182+
std::is_same_v<T, token::BackwardSlash> ||
183+
std::is_same_v<T, token::OpenParen>) {
184+
nodes.push_back(ast::AstNode{std::move(parse_expression(tokens, i))});
185+
} else {
186+
std::cerr << tok.loc << ": syntax error: unexpected token "
187+
<< tok.to_string()
188+
<< std::endl;
189+
std::exit(EXIT_FAILURE);
154190
}
155-
std::cerr << tok.loc << ": syntax error: unexpected token "
156-
<< tok.to_string()
157-
<< std::endl;
158-
std::exit(EXIT_FAILURE);
159-
}, tok.typ));
191+
}, tok.typ);
160192
}
161193
return nodes;
162194
}

‎src/repl.cpp‎

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,75 @@
1+
#include <filesystem>
2+
#include <fstream>
13
#include <iostream>
24
#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-
// TODO: Implement loading a file to repl
9-
// syntax: :load <filepath> or :l <filepath>
5+
#include <lbd/fe/lexer.h>
6+
#include <lbd/fe/parser.h>
7+
#include <lbd/intp/interpreter.h>
108

119
namespace repl {
1210
static bool force_on_env_dump = false;
1311
static bool debug_enabled = false;
1412

13+
static std::optional<std::string> read_file(const std::string &filepath) {
14+
std::ifstream file(filepath);
15+
if (!file) {
16+
// Could not open file
17+
return std::nullopt;
18+
}
19+
return std::string((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
20+
}
21+
22+
static void process_load_command(const std::string &arg,
23+
std::optional<std::shared_ptr<intp::interp::Env> > &shared_env) {
24+
try {
25+
const std::string filepath = arg;
26+
if (!std::filesystem::exists(filepath)) {
27+
std::cerr << "error: filepath "
28+
<< filepath << " does not exist"
29+
<< std::endl;
30+
return;
31+
}
32+
fe::lexer::Lexer lexer(filepath, fe::lexer::FromFile{});
33+
const auto tokens = lexer.lex_all();
34+
// TODO: Accept Options and Add a debug flag for printing Lexed Tokens
35+
fe::parser::Parser parser(tokens);
36+
const std::optional<std::shared_ptr<intp::interp::Env> > temp_env = shared_env;
37+
// Merge loaded_env into shared_env
38+
if (const auto [loaded_env, _] = intp::interp::interpret(parser.program, temp_env, {.own_expr = true});
39+
loaded_env) {
40+
if (!shared_env) {
41+
shared_env = loaded_env;
42+
} else {
43+
// Merge all Bindings from loaded_env into shared_env
44+
for (const auto &[fst, snd]: loaded_env->table) {
45+
(*shared_env)->bind(fst, snd);
46+
}
47+
}
48+
}
49+
std::cout << "info: file loaded: " << filepath << std::endl;
50+
} catch (const std::exception &ex) {
51+
std::cerr << "error: could not load file: " << ex.what() << std::endl;
52+
}
53+
}
54+
1555
static void help() {
1656
std::cout
1757
<< "available REPL commands:\n"
1858
<< " general:\n"
19-
<< " :q, :quit, :exit Exit the REPL\n"
20-
<< " :c, :clear, :cls Clear the screen\n"
21-
<< " :h, :help, :? Show this help message\n"
22-
<< " :r, :reload Reset environment (forget all bindings)\n"
23-
<< " :d, :debug Toggle debug mode\n"
59+
<< " :q, :quit, :exit Exit the REPL\n"
60+
<< " :c, :clear, :cls Clear the screen\n"
61+
<< " :h, :help, :? Show this help message\n"
62+
<< " :l <filepath>, :load <filepath> Load file into REPL\n"
63+
<< " :r, :reload Reset environment (forget all bindings)\n"
64+
<< " :d, :debug Toggle debug mode\n"
2465
<< " inspection:\n"
25-
<< " :e, :env Show all variables in current environment\n"
26-
<< " :force Toggle forcing evaluation when dumping env\n"
66+
<< " :e, :env Show all variables in current environment\n"
67+
<< " :force Toggle forcing evaluation when dumping env\n"
2768
<< "current options:\n"
28-
<< " debug [" << (debug_enabled ? "on " : "off") << "] (use :debug to toggle)\n"
29-
<< " force-on-env-dump [" << (force_on_env_dump ? "on " : "off") << "] (use :force to toggle)"
69+
<< " debug [" << (debug_enabled ? "on " : "off") <<
70+
"] (use :debug to toggle)\n"
71+
<< " force-on-env-dump [" << (force_on_env_dump ? "on " : "off") <<
72+
"] (use :force to toggle)"
3073
<< std::endl;
3174
}
3275

@@ -78,6 +121,14 @@ namespace repl {
78121
force_on_env_dump = !force_on_env_dump;
79122
continue;
80123
}
124+
if (line.rfind(":load ", 0) == 0) {
125+
process_load_command(line.substr(6), shared_global_env);
126+
continue;
127+
}
128+
if (line.rfind(":l ", 0) == 0) {
129+
process_load_command(line.substr(3), shared_global_env);
130+
continue;
131+
}
81132
if (line.empty()) {
82133
continue;
83134
}

0 commit comments

Comments
 (0)