simple-repl.h
1 #ifdef SIMPLE_REPL_ENABLE_RLINE
2 #include "vendor/rline.h"
3 static int enableRline = 1;
4 #endif
5 
6 #define PROMPT_MAIN ">>> "
7 #define PROMPT_BLOCK " > "
8 
9 static int runSimpleRepl(void) {
10 #ifdef _WIN32
11  SetConsoleOutputCP(65001);
12  SetConsoleCP(65001);
13 #endif
14  int exitRepl = 0;
15  while (!exitRepl) {
16  size_t lineCapacity = 8;
17  size_t lineCount = 0;
18  char ** lines = KRK_ALLOCATE(char *, lineCapacity);
19  size_t totalData = 0;
20  int valid = 1;
21  char * allData = NULL;
22  int inBlock = 0;
23  int blockWidth = 0;
24 #ifdef SIMPLE_REPL_ENABLE_RLINE
25  rline_exp_set_prompts(PROMPT_MAIN, "", 4, 0);
26  rline_exit_string="";
27  rline_exp_set_syntax("krk");
28  rline_exp_set_tab_complete_func(NULL);
29 #endif
30  while (1) {
31  /* This would be a nice place for line editing */
32  char buf[4096] = {0};
33 #ifdef SIMPLE_REPL_ENABLE_RLINE
34  if (inBlock) {
35  /* When entering multiple lines, the additional lines
36  * will show a single > (and keep the left side aligned) */
37  rline_exp_set_prompts(PROMPT_BLOCK, "", 4, 0);
38  /* Also add indentation as necessary */
39  rline_preload = malloc(blockWidth + 1);
40  for (int i = 0; i < blockWidth; ++i) {
41  rline_preload[i] = ' ';
42  }
43  rline_preload[blockWidth] = '\0';
44  }
45 
46  if (!enableRline) {
47 #else
48  if (1) {
49 #endif
50  fprintf(stdout, "%s", inBlock ? PROMPT_BLOCK : PROMPT_MAIN);
51  fflush(stdout);
52  }
53 
54 #ifdef SIMPLE_REPL_ENABLE_RLINE
55  rline_scroll = 0;
56  if (enableRline) {
57  if (rline(buf, 4096) == 0) {
58  valid = 0;
59  exitRepl = 1;
60  break;
61  }
62  } else {
63 #endif
64  char * out = fgets(buf, 4096, stdin);
65  if (!out || !strlen(buf)) {
66  fprintf(stdout, "^D\n");
67  valid = 0;
68  exitRepl = 1;
69  break;
70  }
71 #ifdef SIMPLE_REPL_ENABLE_RLINE
72  }
73 #endif
74 
75  if (buf[strlen(buf)-1] != '\n') {
76  valid = 0;
77  break;
78  }
79 
80  if (lineCapacity < lineCount + 1) {
81  /* If we need more space, grow as needed... */
82  size_t old = lineCapacity;
83  lineCapacity = KRK_GROW_CAPACITY(old);
84  lines = KRK_GROW_ARRAY(char *,lines,old,lineCapacity);
85  }
86 
87  int i = lineCount++;
88  lines[i] = strdup(buf);
89 
90  size_t lineLength = strlen(lines[i]);
91  totalData += lineLength;
92  /* Figure out indentation */
93  int isSpaces = 1;
94  int countSpaces = 0;
95  for (size_t j = 0; j < lineLength; ++j) {
96  if (lines[i][j] != ' ' && lines[i][j] != '\n') {
97  isSpaces = 0;
98  break;
99  }
100  countSpaces += 1;
101  }
102 
103  /* Naively detect the start of a new block so we can
104  * continue to accept input. Our compiler isn't really
105  * set up to let us compile "on the fly" so we can't just
106  * run lines through it and see if it wants more... */
107  if (lineLength > 1 && lines[i][lineLength-2] == ':') {
108  inBlock = 1;
109  blockWidth = countSpaces + 4;
110  continue;
111  } else if (lineLength > 1 && lines[i][lineLength-2] == '\\') {
112  inBlock = 1;
113  continue;
114  } else if (inBlock && lineLength != 1) {
115  if (isSpaces) {
116  free(lines[i]);
117  totalData -= lineLength;
118  lineCount--;
119  break;
120  }
121  blockWidth = countSpaces;
122  continue;
123  } else if (lineLength > 1 && lines[i][countSpaces] == '@') {
124  inBlock = 1;
125  blockWidth = countSpaces;
126  continue;
127  }
128 
129  /* Ignore blank lines. */
130  if (isSpaces && !i) valid = 0;
131 
132  /* If we're not in a block, or have entered a blank line,
133  * we can stop reading new lines and jump to execution. */
134  break;
135  }
136 
137  if (valid) {
138  allData = malloc(totalData + 1);
139  allData[0] = '\0';
140  }
141 
142  for (size_t i = 0; i < lineCount; ++i) {
143  if (valid) strcat(allData, lines[i]);
144 #ifdef SIMPLE_REPL_ENABLE_RLINE
145  if (enableRline) {
146  rline_history_insert(strdup(lines[i]));
147  rline_scroll = 0;
148  }
149 #endif
150  free(lines[i]);
151  }
152  KRK_FREE_ARRAY(char *, lines, lineCapacity);
153  if (valid) {
154  KrkValue result = krk_interpret(allData, "<stdin>");
155  if (!IS_NONE(result)) {
156  krk_attachNamedValue(&vm.builtins->fields, "_", result);
157  KrkClass * type = krk_getType(result);
158  const char * formatStr = " \033[1;90m=> %s\033[0m\n";
159  if (type->_reprer) {
160  krk_push(result);
161  result = krk_callDirect(type->_reprer, 1);
162  } else if (type->_tostr) {
163  krk_push(result);
164  result = krk_callDirect(type->_tostr, 1);
165  }
166  if (!IS_STRING(result)) {
167  fprintf(stdout, " \033[1;91m=> Unable to produce representation for value.\033[0m\n");
168  } else {
169  fprintf(stdout, formatStr, AS_CSTRING(result));
170  }
171  }
172  krk_resetStack();
173  free(allData);
174  }
175 
176  (void)blockWidth;
177  }
178 
179  return 0;
180 }
181 #undef PROMPT_MAIN
182 #undef PROMPT_BLOCK
Type object.
Definition: object.h:215
KrkObj * _tostr
__str__ Called to produce a string from an instance
Definition: object.h:230
KrkObj * _reprer
__repr__ Called to create a reproducible string representation of an instance
Definition: object.h:229
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:794
Stack reference or primative value.
KrkClass * krk_getType(KrkValue value)
Get the class representing a value.
Definition: vm.c:240
void krk_resetStack(void)
Reset the current thread's stack state to the top level.
Definition: vm.c:85
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
KrkValue krk_interpret(const char *src, const char *fromFile)
Compile and execute a source code input.
Definition: vm.c:3219
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118
KrkValue krk_callDirect(KrkObj *callable, int argCount)
Call a closure or native function with argCount arguments.
Definition: vm.c:740