1 #include "ml_console.h"
2 #include "ml_runtime.h"
3 #include "ml_debugger.h"
4 #include "minilang.h"
5 #include "ml_macros.h"
6 #include "ml_compiler.h"
7 #include "stringmap.h"
8 #ifndef __MINGW32__
9 #include "linenoise.h"
10 #endif
11 #include <gc.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 typedef struct {
16 	ml_state_t Base;
17 	ml_parser_t *Parser;
18 	ml_compiler_t *Compiler;
19 	const char *Prompt;
20 	const char *DefaultPrompt, *ContinuePrompt;
21 	ml_debugger_t *Debugger;
22 } ml_console_t;
23 
24 #ifdef __MINGW32__
ml_read_line(FILE * File,ssize_t Offset,char ** Result)25 static ssize_t ml_read_line(FILE *File, ssize_t Offset, char **Result) {
26 	char Buffer[129];
27 	if (fgets(Buffer, 129, File) == NULL) return -1;
28 	int Length = strlen(Buffer);
29 	if (Length == 128) {
30 		ssize_t Total = ml_read_line(File, Offset + 128, Result);
31 		memcpy(*Result + Offset, Buffer, 128);
32 		return Total;
33 	} else {
34 		*Result = GC_MALLOC_ATOMIC(Offset + Length + 1);
35 		strcpy(*Result + Offset, Buffer);
36 		return Offset + Length;
37 	}
38 }
39 #endif
40 
ml_console_line_read(ml_console_t * Console)41 static const char *ml_console_line_read(ml_console_t *Console) {
42 #ifdef __MINGW32__
43 	fputs(Console->Prompt, stdout);
44 	char *Line;
45 	if (!ml_read_line(stdin, 0, &Line)) return NULL;
46 #else
47 	const char *Line = linenoise(Console->Prompt);
48 	if (!Line) return NULL;
49 	linenoiseHistoryAdd(Line);
50 #endif
51 	int Length = strlen(Line);
52 	char *Buffer = snew(Length + 2);
53 	memcpy(Buffer, Line, Length);
54 	Buffer[Length] = '\n';
55 	Buffer[Length + 1] = 0;
56 	Console->Prompt = Console->ContinuePrompt;
57 	return Buffer;
58 }
59 
ml_stringbuffer_print(FILE * File,const char * String,size_t Length)60 static int ml_stringbuffer_print(FILE *File, const char *String, size_t Length) {
61 	fwrite(String, 1, Length, File);
62 	return 0;
63 }
64 
ml_console_log(void * Data,ml_value_t * Value)65 static void ml_console_log(void *Data, ml_value_t *Value) {
66 	if (ml_is_error(Value)) {
67 	error:
68 		printf("%s: %s\n", ml_error_type(Value), ml_error_message(Value));
69 		ml_source_t Source;
70 		int Level = 0;
71 		while (ml_error_source(Value, Level++, &Source)) {
72 			printf("\t%s:%d\n", Source.Name, Source.Line);
73 		}
74 	} else {
75 		ml_stringbuffer_t Buffer[1] = {ML_STRINGBUFFER_INIT};
76 		Value = ml_stringbuffer_append(Buffer, Value);
77 		if (ml_is_error(Value)) goto error;
78 		ml_stringbuffer_foreach(Buffer, stdout, (void *)ml_stringbuffer_print);
79 		puts("");
80 		fflush(stdout);
81 	}
82 }
83 
ml_console_repl_run(ml_console_t * Console,ml_value_t * Result)84 static void ml_console_repl_run(ml_console_t *Console, ml_value_t *Result) {
85 	if (Result == MLEndOfInput) return;
86 	Console->Prompt = Console->DefaultPrompt;
87 	Result = ml_deref(Result);
88 	ml_console_log(NULL, Result);
89 	if (ml_is_error(Result)) ml_parser_reset(Console->Parser);
90 	return ml_command_evaluate((ml_state_t *)Console, Console->Parser, Console->Compiler);
91 }
92 
93 typedef struct {
94 	ml_console_t *Console;
95 	interactive_debugger_t *Debugger;
96 } ml_console_debugger_t;
97 
ml_console_debugger_get(ml_console_debugger_t * ConsoleDebugger,const char * Name)98 static ml_value_t *ml_console_debugger_get(ml_console_debugger_t *ConsoleDebugger, const char *Name) {
99 	ml_value_t *Value = interactive_debugger_get(ConsoleDebugger->Debugger, Name);
100 	if (Value) return Value;
101 	return ml_compiler_lookup(ConsoleDebugger->Console->Compiler, Name);
102 }
103 
ml_console_debug_enter(ml_console_t * Console,interactive_debugger_t * Debugger,ml_source_t Source,int Index)104 static void ml_console_debug_enter(ml_console_t *Console, interactive_debugger_t *Debugger, ml_source_t Source, int Index) {
105 	ml_console_debugger_t *ConsoleDebugger = new(ml_console_debugger_t);
106 	ConsoleDebugger->Console = Console;
107 	ConsoleDebugger->Debugger = Debugger;
108 	printf("Debug break [%d]: %s:%d\n", Index, Source.Name, Source.Line);
109 	ml_console(Console->Base.Context, (void *)ml_console_debugger_get, ConsoleDebugger, "\e[34m>>>\e[0m ", "\e[34m...\e[0m ");
110 	interactive_debugger_resume(Debugger);
111 }
112 
ml_console_debug_exit(void * Data,interactive_debugger_t * Debugger,ml_state_t * Caller,int Index)113 static void ml_console_debug_exit(void *Data, interactive_debugger_t *Debugger, ml_state_t *Caller, int Index) {
114 	ML_RETURN(MLEndOfInput);
115 }
116 
ml_console(ml_context_t * Context,ml_getter_t GlobalGet,void * Globals,const char * DefaultPrompt,const char * ContinuePrompt)117 void ml_console(ml_context_t *Context, ml_getter_t GlobalGet, void *Globals, const char *DefaultPrompt, const char *ContinuePrompt) {
118 	ml_console_t Console[1];
119 	Console->Base.run = (void *)ml_console_repl_run;
120 	Console->Base.Context = Context;
121 	Console->Prompt = Console->DefaultPrompt = DefaultPrompt;
122 	Console->ContinuePrompt = ContinuePrompt;
123 	Console->Debugger = NULL;
124 	ml_parser_t *Parser = ml_parser((void *)ml_console_line_read, Console);
125 	ml_compiler_t *Compiler = ml_compiler(GlobalGet, Globals);
126 	ml_compiler_define(Compiler, "debugger", interactive_debugger(
127 		(void *)ml_console_debug_enter,
128 		(void *)ml_console_debug_exit,
129 		ml_console_log,
130 		Console,
131 		GlobalGet,
132 		Globals
133 	));
134 
135 	ml_parser_source(Parser, (ml_source_t){"<console>", 1});
136 	Console->Parser = Parser;
137 	Console->Compiler = Compiler;
138 	ml_command_evaluate((ml_state_t *)Console, Parser, Compiler);
139 }
140