88#include < lbd/intp/interpreter.h>
99#include < lbd/exceptions.h>
1010
11- // TODO: Add support for clicking enter during lambda expression definition
12-
1311#define on_off (val ) ((val) ? " on " : " off" )
1412
1513namespace repl {
@@ -46,91 +44,149 @@ namespace repl {
4644 options_v.logger .info (" info: file loaded " , filepath);
4745 }
4846
47+ static int compute_paren_depth (const std::string &s) {
48+ int depth = 0 ;
49+ for (const char c: s) {
50+ if (c == ' (' ) depth++;
51+ else if (c == ' )' ) depth = std::max (0 , depth - 1 );
52+ }
53+ return depth;
54+ }
55+
56+ static std::string trim (const std::string &s) {
57+ size_t start = 0 ;
58+ while (start < s.size () && std::isspace (static_cast <unsigned char >(s[start]))) start++;
59+ size_t end = s.size ();
60+ while (end > start && std::isspace (static_cast <unsigned char >(s[end - 1 ]))) end--;
61+ return s.substr (start, end - start);
62+ }
4963
5064 void loop (const bool debug) {
5165 enable_virtual_terminal ();
5266
5367 static logs::Logger logger (false , true , false );
5468 options_v = {.own_expr = true , .force_on_env_dump = false , .debug = debug, .logger = logger};
5569
56- std::string line;
70+ std::string line, buffer;
71+ size_t indent_level = 0 ;
5772 std::optional<std::shared_ptr<intp::interp::Env> > shared_global_env = std::nullopt ;
5873
5974 options_v.logger .info (" Welcome to lambda-discipline REPL.\n Type :quit to exit." );
6075
6176 while (true ) {
6277 try {
63- std::cout << colors::CYAN << " \n >> " << colors::RESET ;
64- if (!std::getline (std::cin, line)) {
65- break ; // EOF (C-d / C-z)
66- }
67- if (line == " :q" || line == " :quit" || line == " :exit" ) {
68- options_v.logger .info (" \n exiting REPL." );
69- break ;
78+ // Prompt depends on whether we are continuing a buffer
79+ if (buffer.empty ()) {
80+ std::cout << colors::CYAN << " \n >> " << colors::RESET ;
81+ } else {
82+ std::cout << colors::CYAN << " .. " << colors::RESET ;
7083 }
71- if (line == " :c" || line == " :clear" || line == " :cls" ) {
84+ for (size_t i = 0 ; i < indent_level; i++) std::cout << " " ;
85+
86+ if (!std::getline (std::cin, line)) break ; // EOF (C-d / C-z)
87+
88+ // REPL arguments parsing
89+ {
90+ if (line == " :q" || line == " :quit" || line == " :exit" ) {
91+ options_v.logger .info (" \n exiting REPL." );
92+ break ;
93+ }
94+
95+ if (line == " :c" || line == " :clear" || line == " :cls" ) {
7296#ifdef _WIN32
73- std::system (" cls" );
97+ std::system (" cls" );
7498#else
75- std::system (" clear" );
99+ std::system (" clear" );
76100#endif
77- continue ;
78- }
79- if (line == " :h" || line == " :help" || line == " :?" ) {
80- std::cout << std::endl;
81- print_table ({" General Commands" , " Argument" , " Description" }, {
82- {" :q, :quit, :exit" , " " , " Clear the screen" },
83- {" :c, :clear, :cls" , " " , " Exit the REPL" },
84- {" :h, :help, :?" , " " , " Display this help message" },
85- {" :l, :load" , " <filepath>" , " Load file into REPL" },
86- {" :r, :reset" , " " , " Reset environment" },
87- {" :d, :debug" , " " , " Toggle debug mode" }
88- }, colors::GREEN );
89- std::cout << std::endl;
90- print_table ({" Inspection Commands" , " Argument" , " Description" }, {
91- {" :e, :env" , " " , " Dump environment bindings" },
92- {" :force" , " " , " Force thunk evaluation on dump" }
93- }, colors::GREEN );
94- std::cout << std::endl;
95- print_table ({" Options" , " State" , " Help" }, {
96- {" debug" , on_off (options_v.debug ), " use :debug to toggle" },
97- {" force-on-env-dump" , on_off (options_v.force_on_env_dump ), " use :force to toggle" }
98- }, colors::GREEN );
99- continue ;
100- }
101- if (line == " :env" || line == " :e" ) {
102- std::cout << std::endl;
103- if (shared_global_env) {
104- print_table ({" Symbol" , " Thunk" }, (*shared_global_env)->to_vector (options_v.force_on_env_dump ),
105- colors::GREEN );
106- } else {
107- print_table ({" Symbol" , " Thunk" }, {{" Empty" }}, colors::GREEN );
101+ continue ;
102+ }
103+
104+ if (line == " :h" || line == " :help" || line == " :?" ) {
105+ std::cout << std::endl;
106+ print_table ({" General Commands" , " Argument" , " Description" }, {
107+ {" :q, :quit, :exit" , " " , " Clear the screen" },
108+ {" :c, :clear, :cls" , " " , " Exit the REPL" },
109+ {" :h, :help, :?" , " " , " Display this help message" },
110+ {" :l, :load" , " <filepath>" , " Load file into REPL" },
111+ {" :r, :reset" , " " , " Reset environment" },
112+ {" :d, :debug" , " " , " Toggle debug mode" }
113+ }, colors::GREEN );
114+ std::cout << std::endl;
115+ print_table ({" Inspection Commands" , " Argument" , " Description" }, {
116+ {" :e, :env" , " " , " Dump environment bindings" },
117+ {" :force" , " " , " Force thunk evaluation on dump" }
118+ }, colors::GREEN );
119+ std::cout << std::endl;
120+ print_table ({" Options" , " State" , " Help" }, {
121+ {" debug" , on_off (options_v.debug ), " use :debug to toggle" },
122+ {
123+ " force-on-env-dump" , on_off (options_v.force_on_env_dump ),
124+ " use :force to toggle"
125+ }
126+ }, colors::GREEN );
127+ continue ;
128+ }
129+
130+ if (line == " :env" || line == " :e" ) {
131+ std::cout << std::endl;
132+ if (shared_global_env) {
133+ print_table ({" Symbol" , " Thunk" },
134+ (*shared_global_env)->to_vector (options_v.force_on_env_dump ),
135+ colors::GREEN );
136+ } else {
137+ print_table ({" Symbol" , " Thunk" }, {{" Empty" }}, colors::GREEN );
138+ }
139+ continue ;
140+ }
141+
142+ if (line == " :reset" || line == " :r" ) {
143+ shared_global_env.reset ();
144+ continue ;
145+ }
146+
147+ if (line == " :debug" || line == " :d" ) {
148+ options_v.debug = !options_v.debug ;
149+ continue ;
150+ }
151+
152+ if (line == " :force" ) {
153+ options_v.force_on_env_dump = !options_v.force_on_env_dump ;
154+ continue ;
155+ }
156+
157+ if (line.rfind (" :load " , 0 ) == 0 ) {
158+ process_load_command (line.substr (6 ), shared_global_env);
159+ continue ;
160+ }
161+
162+ if (line.rfind (" :l " , 0 ) == 0 ) {
163+ process_load_command (line.substr (3 ), shared_global_env);
164+ continue ;
108165 }
109- continue ;
110- }
111- if (line == " :reset" || line == " :r" ) {
112- shared_global_env.reset ();
113- continue ;
114- }
115- if (line == " :debug" || line == " :d" ) {
116- options_v.debug = !options_v.debug ;
117- continue ;
118- }
119- if (line == " :force" ) {
120- options_v.force_on_env_dump = !options_v.force_on_env_dump ;
121- continue ;
122- }
123- if (line.rfind (" :load " , 0 ) == 0 ) {
124- process_load_command (line.substr (6 ), shared_global_env);
125- continue ;
126166 }
127- if (line.rfind (" :l " , 0 ) == 0 ) {
128- process_load_command (line.substr (3 ), shared_global_env);
129- continue ;
167+
168+ if (line.empty ()) continue ;
169+
170+ std::string trimmed = trim (line);
171+ // Continue the line with trailing '`'
172+ bool continuation = !trimmed.empty () && trimmed.back () == ' `' ;
173+ if (continuation) {
174+ trimmed.pop_back ();
130175 }
131- if (line.empty ()) {
176+ buffer += trimmed + " \n " ;
177+
178+ if (continuation) {
179+ // Indent level depends on whether the current line ends with '.' after trimming and removing trailing '`'.
180+ // Additionally, it also depends upon the current parenthesis depth.
181+ indent_level = compute_paren_depth (buffer);
182+ trimmed = trim (trimmed);
183+ if (!trimmed.empty () && trimmed.back () == ' .' ) indent_level++;
132184 continue ;
133185 }
186+ indent_level = 0 ;
187+ line = buffer;
188+ buffer.clear ();
189+
134190 // Lex
135191 fe::lexer::Lexer lexer_v (line, fe::lexer::FromRepl{}, options_v);
136192 const std::vector<fe::token::Token> tokens = lexer_v.lex_all ();
@@ -156,7 +212,7 @@ namespace repl {
156212 shared_global_env = global_env;
157213 } catch (const ControlledExit &) {
158214 } catch (const std::exception &ex) {
159- options_v.logger .error ({}, " error: " , ex.what ());;
215+ options_v.logger .error ({}, " error: " , ex.what ());
160216 }
161217 }
162218 }
0 commit comments