]> gitweb.ps.run Git - iftint/blob - main3.c
proof of concept
[iftint] / main3.c
1 #include<stdint.h>
2 #include<stdbool.h>
3 #include<stdio.h>
4
5 // Global defines
6
7 #define STR_SIZE 128
8 #define BUF_SIZE 1024*1024
9
10 // Memory
11
12 static char g_memory[1024*1024];
13 static int g_memory_index = 0;
14
15 void * alloc(int num, int size) {
16     void * result = g_memory + g_memory_index;
17     for (int i = 0; i < num*size; i++)
18         g_memory[g_memory_index+i] = 0;
19     g_memory_index += num*size;
20     return result;
21 }
22
23 #define NEW(TYPE) ((TYPE *)alloc(1, sizeof(TYPE)))
24 #define NEWARR(TYPE, NUM) ((TYPE *)alloc(NUM, sizeof(TYPE)))
25
26 // getch
27
28 #ifdef _WIN32
29 #include <windows.h>
30 #include <conio.h>
31 #else
32 #include <sys/ioctl.h>
33 #include <termios.h>
34 #include <unistd.h>
35 #include <stdio.h>
36
37 /* reads from keypress, doesn't echo */
38 int getch(void)
39 {
40     struct termios oldattr, newattr;
41     int ch;
42     tcgetattr( STDIN_FILENO, &oldattr );
43     newattr = oldattr;
44     newattr.c_lflag &= ~( ICANON | ECHO ); // no ECHO for echo(?)
45     tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
46     ch = getchar();
47     tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
48     return ch;
49 }
50
51 /* ungets keypress */
52 void ungetch(int ch)
53 {
54     struct termios oldattr, newattr;
55     tcgetattr( STDIN_FILENO, &oldattr );
56     newattr = oldattr;
57     newattr.c_lflag &= ~( ICANON | ECHO );
58     tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
59     ungetc(ch, stdin);
60     tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
61 }
62 #endif
63
64 int
65 peekch() {
66     int c = getch();
67     ungetch(c);
68     return c;
69 }
70
71 // VT100
72
73 #define ASCII_ESC 27
74
75 void vt100Escape(const char * str, ...) {
76     va_list args;
77     va_start(args, str);
78
79     printf("%c", ASCII_ESC);
80     vprintf(str, args);
81 }
82
83 void vt100ClearScreen() { vt100Escape("[2J"); }
84 void vt100CursorHome() { vt100Escape("[H"); }
85 void vt100CursorPos(int v, int h) { vt100Escape("[%d;%dH", v, h); }
86 void vt100SaveCursor() { vt100Escape("7"); }
87 void vt100RestoreCursor() { vt100Escape("8"); }
88 void vt100EnableAlternateBuffer() { vt100Escape("[?1049h"); }
89 void vt100DisableAlternateBuffer() { vt100Escape("[?1049l"); }
90 void vt100EnableNegative() { vt100Escape("[7m"); }
91 void vt100DisableNegative() { vt100Escape("[27m"); }
92 void vt100ShowCursor() { vt100Escape("[?25h"); }
93 void vt100HideCursor() { vt100Escape("[?25l"); }
94 void vt100GetScreenSize(int * v, int * h) {
95 #ifdef _WIN32
96     CONSOLE_SCREEN_BUFFER_INFO csbi;
97     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
98     *h = csbi.srWindow.Right - csbi.srWindow.Left + 1;
99     *v = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
100 #else
101     struct winsize w;
102     ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
103     *h = w.ws_row;
104     *v = w.ws_col;
105 #endif
106 }
107
108 // Node
109
110 typedef enum NodeKind {
111     NK_Func,
112     NK_ArgList,
113     NK_ExprList,
114     NK_Var,
115     NK_Body,
116     NK_If,
117     NK_Num,
118     NK_Str,
119     NK_Call,
120     NK_Op,
121
122     NK_COUNT
123 } NodeKind;
124
125 const char *NK_STRINGS[NK_COUNT] = {
126     "NK_Func",
127     "NK_ArgList",
128     "NK_ExprList",
129     "NK_Var",
130     "NK_Body",
131     "NK_If",
132     "NK_Num",
133     "NK_Str",
134     "NK_Call",
135     "NK_Op",
136 };
137
138 typedef struct Node Node;
139 struct Node {
140     NodeKind kind;
141     void * data;
142
143     int childCount;
144     Node *next, *prev, *parent, *child;
145 };
146
147 Node *NodeGetChild(Node *n, unsigned int index) {
148     if (index >= n->childCount)
149         return NULL;
150     
151     Node *result = n->child;
152     for (int i = 0; i < index; i++)
153         result = result->next;
154     return result;
155 }
156
157 void NodeAppend(Node *n1, Node *n2) {
158     if (n1->childCount == 0) {
159         n1->child = n2;
160     }
161     else {
162         Node *lastChild = NodeGetChild(n1, n1->childCount-1);
163
164         lastChild->next = n2;
165         n2->prev = lastChild;
166         n2->next = NULL;
167     }
168     n2->parent = n1;
169     n1->childCount += 1;
170 }
171
172 void NodeInsert(Node *n1, Node *n2, unsigned int index) {
173     if (index >= n1->childCount-1) {
174         NodeAppend(n1, n2);
175         return;
176     }
177
178     Node *insertAfter = NodeGetChild(n1, index);
179     Node *insertBefore = NodeGetChild(n1, index+1);
180
181     insertAfter->next = n2;
182     insertBefore->prev = n2;
183     n2->prev = insertAfter;
184     n2->next = insertBefore;
185
186     n1->childCount += 1;
187 }
188
189 Node *NodeRemove(Node *n1, Node *n2) {
190     if (n1 == NULL)
191         return n2;
192     
193     Node *prev = n2->prev;
194     Node *next = n2->next;
195
196     if (prev != NULL)
197         prev->next = next;
198     if (next != NULL)
199         next->prev = prev;
200     
201     n2->prev = n2->next = n2->parent = NULL;
202
203     if (n2 == n1->child)
204         n1->child = next;
205
206     n1->childCount -= 1;
207     
208     if (prev == NULL)
209         return n1;
210     else
211         return prev;
212 }
213
214 void NodeDraw(Node *n, Node *selected) {
215     static int indent = 0;
216     #define INDENT for (int i = 0; i < indent; i++) printf("  ");
217     
218     vt100DisableNegative();
219     if (n == selected) vt100EnableNegative();
220     
221     switch (n->kind) {
222     case NK_Func:    { printf("fn %s ", n->data); NodeDraw(NodeGetChild(n, 0), selected); printf("\n"); INDENT NodeDraw(NodeGetChild(n, 1), selected); break;         }
223     case NK_ExprList:
224     case NK_ArgList: { printf("("); for (int i = 0; i < n->childCount; i++) { if (i != 0) printf(" "); NodeDraw(NodeGetChild(n, i), selected); } printf(")"); break; }
225     case NK_Var:     { printf("var %s", n->data); break;                                                                                                       }
226     case NK_Body:    { printf("{\n"); indent++; for (int i = 0; i < n->childCount; i++) { INDENT NodeDraw(NodeGetChild(n, i), selected); printf("\n"); } indent--; INDENT printf("}\n"); break;  }
227     case NK_If:      { printf("if "); NodeDraw(NodeGetChild(n, 0), selected); printf("\n"); INDENT NodeDraw(NodeGetChild(n, 1), selected); break;                     }
228     case NK_Num:     { printf("%s", n->data); break;                                                                                                     }
229     case NK_Str:     { printf("'%s'", n->data); break;                                                                                                     }
230     case NK_Call:    { printf("call %s ", n->data); NodeDraw(NodeGetChild(n, 0), selected); break;                                                             }
231     case NK_Op:      { if (n->childCount <= 1) printf("%s ", n->data); for (int i = 0; i < n->childCount; i++) { if (i != 0) printf(" %s ", n->data); NodeDraw(NodeGetChild(n, i), selected); } break;         }
232     }
233
234     vt100DisableNegative();
235 }
236
237 // Input
238
239 #define KEY_CTRL_C 3
240 #define KEY_BACKSPACE1 8
241 #define KEY_BACKSPACE2 127
242
243 typedef enum Input {
244     IN_None,
245
246     IN_SPACE,
247     IN_D,
248
249     IN_H,
250     IN_J,
251     IN_K,
252     IN_L,
253
254     IN_F,
255     IN_V,
256     IN_I,
257     IN_N,
258     IN_S,
259     IN_C,
260     IN_O,
261
262     IN_Quit,
263
264     IN_COUNT
265 } Input;
266
267 const char *IN_STRINGS[IN_COUNT] = {
268     "IN_None",
269
270     "IN_SPACE",
271     "IN_D",
272
273     "IN_H",
274     "IN_J",
275     "IN_K",
276     "IN_L",
277
278     "IN_F",
279     "IN_V",
280     "IN_I",
281     "IN_N",
282     "IN_S",
283     "IN_C",
284     "IN_O",
285
286     "IN_Quit",
287 };
288
289 Input InputGet() {
290     int c;
291
292     while (true) {
293         c = getch();
294         switch (c) {
295             case ' ': return IN_SPACE;
296             case 'd': return IN_D;
297             
298             case 'h': return IN_H;
299             case 'j': return IN_J;
300             case 'k': return IN_K;
301             case 'l': return IN_L;
302
303             case 'f': return IN_F;
304             case 'v': return IN_V;
305             case 'i': return IN_I;
306             case 'n': return IN_N;
307             case 's': return IN_S;
308             case 'c': return IN_C;
309             case 'o': return IN_O;
310
311             case 'q': return IN_Quit;
312         }
313     }
314 }
315
316 typedef enum InputAction {
317     IA_None,
318
319     IA_StartEditing,
320     IA_Delete,
321
322     IA_MoveLeft,
323     IA_MoveUp,
324     IA_MoveDown,
325     IA_MoveRight,
326
327     IA_AddFunc,
328     IA_AddVar,
329     IA_AddIf,
330     IA_AddNum,
331     IA_AddStr,
332     IA_AddCall,
333     IA_AddOp,
334
335     IA_COUNT
336 } InputAction;
337
338 const char *IA_STRINGS[IA_COUNT] = {
339     "IA_None",
340
341     "IA_StartEditing",
342     "IA_Delete",
343
344     "IA_MoveLeft",
345     "IA_MoveUp",
346     "IA_MoveDown",
347     "IA_MoveRight",
348
349     "IA_AddFunc",
350     "IA_AddVar",
351     "IA_AddIf",
352     "IA_AddNum",
353     "IA_AddStr",
354     "IA_AddCall",
355     "IA_AddOp",
356 };
357
358 void DrawInfo(InputAction actions[NK_COUNT][IN_COUNT], NodeKind nk) {
359     int v, h;
360     vt100GetScreenSize(&v, &h);
361
362     int line = 2;
363
364     vt100CursorPos(line++, h-30);
365     printf("%s:", NK_STRINGS[nk]);
366
367     for (int i = 0; i < IN_COUNT; i++) {
368         InputAction action = actions[nk][i];
369         if (action != IA_None) {
370             vt100CursorPos(line++, h-30);
371             printf("%s %s", IN_STRINGS[i], IA_STRINGS[action]);
372         }
373     }
374 }
375
376 Node *GetNode(InputAction actions[NK_COUNT][IN_COUNT]) {
377     Node *result = NEW(Node); result->kind = NK_Body;
378
379     Node * n = result;
380
381     Input in;
382     InputAction action;
383
384     enum {
385         Mode_Normal,
386         Mode_Editing,
387     } mode = Mode_Normal;
388
389     while (true) {
390         vt100ClearScreen();
391         vt100CursorHome();
392         NodeDraw(result, n);
393         DrawInfo(actions, n->kind);
394
395         if (mode == Mode_Editing) {
396             int c = getch();
397             char *s = (char*)n->data;
398             int slen = strlen(s);
399             
400             if (c == KEY_BACKSPACE1 || c == KEY_BACKSPACE2) {
401                 s[slen-1] = '\0';
402             }
403             else if (c == 13) {
404                 mode = Mode_Normal;
405             }
406             else if (slen < STR_SIZE) {
407                 s[slen++] = (char)c;
408             }
409         }
410         else if (mode == Mode_Normal) {
411             in = InputGet();
412
413             if (in == IN_Quit)
414                 break;
415
416             action = actions[n->kind][in];
417
418             #define NA(NAME, PARENT, KIND) Node *NAME = NEW(Node); NAME->kind = KIND; NodeAppend(PARENT, NAME);
419             #define NS(NAME, PARENT, KIND) Node *NAME = NEW(Node); NAME->kind = KIND; NAME->data = NEWARR(char, STR_SIZE); NodeAppend(PARENT, NAME);
420
421             switch (action) {
422             case IA_StartEditing: { mode = Mode_Editing; break; }
423             case IA_Delete: { n = NodeRemove(n->parent, n); break; }
424
425             case IA_MoveLeft: { if (n->prev != NULL) n = n->prev; break; }
426             case IA_MoveDown: { if (n->child != NULL) n = n->child; break; }
427             case IA_MoveUp: { if (n->parent != NULL) n = n->parent; break; }
428             case IA_MoveRight: { if (n->next != NULL) n = n->next; break; }
429             
430             case IA_AddFunc: { NS(n1, n, NK_Func) NA(n2, n1, NK_ArgList)  NA(n3, n1, NK_Body) n = n1; mode = Mode_Editing; break; }
431             case IA_AddVar:  { NS(n1, n, NK_Var)                                              n = n1; mode = Mode_Editing; break; }
432             case IA_AddIf:   { NA(n1, n, NK_If)   NA(n2, n1, NK_ExprList) NA(n3, n1, NK_Body) n = n1;                      break; }
433             case IA_AddNum:  { NS(n1, n, NK_Num)                                              n = n1; mode = Mode_Editing; break; }
434             case IA_AddStr:  { NS(n1, n, NK_Str)                                              n = n1; mode = Mode_Editing; break; }
435             case IA_AddCall: { NS(n1, n, NK_Call) NA(n2, n1, NK_ExprList)                     n = n1; mode = Mode_Editing; break; }
436             case IA_AddOp:   { NS(n1, n, NK_Op)                                               n = n1; mode = Mode_Editing; break; }
437             }
438
439             #undef NA
440             #undef NS
441         }
442     }
443
444     return result;
445 }
446
447
448 int main() {
449     // Setup
450     static InputAction actions[NK_COUNT][IN_COUNT];
451
452     for (int i = 0; i < NK_COUNT; i++) {
453         actions[i][IN_H] = IA_MoveLeft;
454         actions[i][IN_J] = IA_MoveUp;
455         actions[i][IN_K] = IA_MoveDown;
456         actions[i][IN_L] = IA_MoveRight;
457     }
458
459     actions[NK_Func][IN_D] = IA_Delete;
460     actions[NK_Func][IN_SPACE] = IA_StartEditing;
461
462     actions[NK_ArgList][IN_V] = IA_AddVar;
463     
464     actions[NK_ExprList][IN_V] = IA_AddVar;
465     actions[NK_ExprList][IN_I] = IA_AddIf;
466     actions[NK_ExprList][IN_N] = IA_AddNum;
467     actions[NK_ExprList][IN_S] = IA_AddStr;
468     actions[NK_ExprList][IN_C] = IA_AddCall;
469     actions[NK_ExprList][IN_O] = IA_AddOp;
470
471     actions[NK_Var][IN_D] = IA_Delete;
472     actions[NK_Var][IN_SPACE] = IA_StartEditing;
473
474     actions[NK_Body][IN_F] = IA_AddFunc;
475     actions[NK_Body][IN_V] = IA_AddVar;
476     actions[NK_Body][IN_I] = IA_AddIf;
477     actions[NK_Body][IN_N] = IA_AddNum;
478     actions[NK_Body][IN_S] = IA_AddStr;
479     actions[NK_Body][IN_C] = IA_AddCall;
480     actions[NK_Body][IN_O] = IA_AddOp;
481
482     actions[NK_If][IN_D] = IA_Delete;
483
484     actions[NK_Num][IN_D] = IA_Delete;
485     actions[NK_Num][IN_SPACE] = IA_StartEditing;
486
487     actions[NK_Call][IN_D] = IA_Delete;
488     actions[NK_Call][IN_SPACE] = IA_StartEditing;
489
490     actions[NK_Op][IN_D] = IA_Delete;
491     actions[NK_Op][IN_SPACE] = IA_StartEditing;
492     actions[NK_Op][IN_V] = IA_AddVar;
493     actions[NK_Op][IN_I] = IA_AddIf;
494     actions[NK_Op][IN_N] = IA_AddNum;
495     actions[NK_Op][IN_S] = IA_AddStr;
496     actions[NK_Op][IN_C] = IA_AddCall;
497     actions[NK_Op][IN_O] = IA_AddOp;
498
499     // Main
500     vt100EnableAlternateBuffer();
501     vt100HideCursor();
502
503     Node *n = GetNode(actions);
504
505     vt100DisableAlternateBuffer();
506     vt100ShowCursor();
507
508     int v, h;
509     vt100GetScreenSize(&v, &h);
510     printf("Screen Size: %d | %d\n", v, h);
511
512     NodeDraw(n, NULL);
513
514     return 0;
515 }