├── .gitignore ├── Makefile ├── README.md ├── ast.c ├── ast.h ├── color.c ├── compiler.c ├── compiler.h ├── debug.c ├── debug.h ├── emit.c ├── emit.h ├── examples ├── array.dt ├── assignables.dt ├── branch_types.dt ├── conds.dt ├── date_time.dt ├── fill.dt ├── fizzbuzz.dt ├── hello.dt ├── helloworld.dt ├── life.dt ├── match.dt ├── math.dt ├── mouse.dt ├── piano.dt ├── print_digit.dt ├── pulser.dt ├── recursion.dt ├── sound.dt ├── sprite.dt ├── test.dt ├── type_determined.dt └── vars.dt ├── lang.h ├── lemon.c ├── lempar.c ├── lib.c ├── lut.c ├── lut.h ├── main.c ├── out.png ├── parser.y ├── scanner.l ├── test └── test.c └── todo.org /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | lang 3 | lemon 4 | parser.out 5 | parser.c 6 | parser.h 7 | scanner.c 8 | scanner.h -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FLEX = /usr/bin/flex 2 | CC := /usr/bin/gcc 3 | CFLAGS := $(CFLAGS) -Wextra 4 | 5 | lang: main.o parser.o scanner.o compiler.o debug.o emit.o ast.o lut.o 6 | $(CC) -o lang main.o parser.o scanner.o compiler.o debug.o emit.o ast.o lut.o 7 | 8 | color: color.o scanner.o compiler.o debug.o 9 | $(CC) -o color color.o scanner.o compiler.o debug.o 10 | 11 | color.o: color.c parser.h scanner.h lang.h 12 | 13 | main.o: main.c parser.h scanner.h lang.h emit.c 14 | 15 | compiler.o: compiler.c compiler.h parser.h scanner.h lang.h 16 | 17 | parser.o: parser.h parser.c lang.h 18 | 19 | scanner.o: scanner.h 20 | 21 | debug.o: debug.c compiler.h parser.h scanner.h lang.h 22 | 23 | emit.o: emit.c lang.h debug.h compiler.o lut.o debug.o 24 | 25 | ast.o: ast.c ast.h 26 | 27 | lut.o: lut.c lut.h 28 | 29 | # Parser 30 | parser.h parser.c: parser.y lemon 31 | ./lemon parser.y 32 | 33 | # Lexer 34 | scanner.c scanner.h: scanner.l 35 | $(FLEX) scanner.l 36 | 37 | lemon: lemon.c 38 | $(CC) -o lemon lemon.c 39 | 40 | format: 41 | clang-format -i color.c compiler.c debug.c emit.c main.c 42 | clang-format -i debug.h emit.h lang.h compiler.h 43 | 44 | check: parser.o scanner.o compiler.o debug.o emit.o ast.o lut.o 45 | $(CC) -o test/run test/test.c parser.o scanner.o compiler.o debug.o emit.o ast.o lut.o `pkg-config --cflags --libs check` 46 | ./test/run 47 | 48 | .PHONY: clean 49 | clean: 50 | rm -f *.o 51 | rm -f scanner.c scanner.h 52 | rm -f parser.c parser.h parser.out 53 | rm -f lang lemon 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dotal 2 | 3 | A small language designed to compile to the Uxn virtual machine. 4 | 5 | This goal is to provide joyful way to write programs that has features tailored to the features of Uxn. 6 | 7 | ## Status 8 | 9 | This is a pet/sideproject. Not everything will work! I don't expect the existing syntax to change, but I do expect that some changes around lexical scope and veriable handling and how the program is assembled will need to change to support larger projects and more interesting programming constructs. There are also still some things that are less expressive then I would like. There are a lot of magic numbers you need to know to write programs. You can see some of my thinking in the `todo.org` file. 10 | 11 | I wrote this as a part of a bigger "visual compiler explorers" project that in ongoing. You can see some evidence of it in some of the artifacts that the compiler is able to export. 12 | 13 | Example program: 14 | 15 | ``` 16 | sprite: square 17 | 11111111 18 | 10000001 19 | 10000001 20 | 10000001 21 | 10000001 22 | 10000001 23 | 10000001 24 | 11111111 25 | 26 | fn main() do 27 | theme #202 #c1c #ece #905 28 | 29 | send(.Screen/x, 100) 30 | send(.Screen/y, 100) 31 | 32 | send(.Screen/addr, :sprite_square) 33 | send(.Screen/sprite, 67) 34 | end 35 | ``` 36 | 37 | Simple program which draws a square to the screen. 38 | 39 | ## Install 40 | 41 | 1. clone the project 42 | 2. `make` (make sure you have relatively recent flex and a C compiler) 43 | 44 | ## Compile and Run a program 45 | 46 | ``` 47 | ./lang examples/sprite.dt > test.tal 48 | ~/Downloads/uxn/uxnasm test.tal out.rom 49 | ~/Downloads/uxn/uxnemu out.rom 50 | ``` 51 | 52 | To learn more about the language, look at the examples in the `examples/` directory. 53 | 54 | ## Some notable syntax 55 | 56 | ### Hello World 57 | 58 | A hello world program in dotal looks like 59 | 60 | ``` 61 | fn main() do 62 | put("hello world") 63 | end 64 | ``` 65 | 66 | programs begin at the `main` function. 67 | 68 | ### Sprite literals 69 | 70 | You can specify your sprite literals with a basic "ascii art" syntax 71 | 72 | A simple mouse cursor might look like, 73 | 74 | ``` 75 | sprite: cursor 76 | 10000000 77 | 11000000 78 | 11100000 79 | 11110000 80 | 11111000 81 | 11111100 82 | 00000000 83 | 00000000 84 | ``` 85 | 86 | ### Variables 87 | 88 | Variables are defined using `::` followed by their type. so creating a global variable named `hi` that is 8 bits looks like, 89 | 90 | ``` 91 | $hi :: i8 92 | ``` 93 | 94 | ### access memory identifiers 95 | 96 | Getting the location of the mouse might look like, 97 | 98 | ``` 99 | $x :: i16 100 | $y :: i16 101 | 102 | fn main() do 103 | $x = get(.Mouse/x) 104 | $y = get(.Mouse/y) 105 | end 106 | ``` 107 | 108 | There are a number of memory identifiers that you can access this way, 109 | 110 | | Name | Size | 111 | |--------------------|------| 112 | | .System/vector | i16 | 113 | | .System/r | i16 | 114 | | .System/g | i16 | 115 | | .System/b | i16 | 116 | | .Console/vector | i16 | 117 | | .Console/write | i8 | 118 | | .Screen/vector | i16 | 119 | | .Screen/width | i16 | 120 | | .Screen/height | i16 | 121 | | .Screen/x | i16 | 122 | | .Screen/y | i16 | 123 | | .Screen/addr | i16 | 124 | | .Screen/pixel | i8 | 125 | | .Screen/sprite | i8 | 126 | | .Audio{0,3}/addr | i16 | 127 | | .Audio{0,3}/length | i16 | 128 | | .Audio{0,3}/adsr | i16 | 129 | | .Audio{0,3}/volume | i16 | 130 | | .Audio{0,3}/pitch | i16 | 131 | | .Mouse/vector | i16 | 132 | | .Mouse/x | i16 | 133 | | .Mouse/y | i16 | 134 | | .Mouse/state | i8 | 135 | | .DateTime/year | i16 | 136 | | .DateTime/month | i8 | 137 | | .DateTime/hour | i8 | 138 | | .DateTime/minute | i8 | 139 | | .DateTime/second | i8 | 140 | 141 | If any are missing, we can add them. 142 | 143 | ### Do something when something happens 144 | 145 | ``` 146 | fn main() do 147 | send(.Mouse/vector, :mouse) 148 | end 149 | 150 | fn mouse() do 151 | put("mouse moved!") 152 | end 153 | ``` 154 | 155 | 156 | The send method lets you do something whenever the mouse moves. 157 | 158 | ## Future work 159 | 160 | The compiler outputs suboptimal code in a number of ways. 161 | -------------------------------------------------------------------------------- /ast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "compiler.h" 4 | #include "ast.h" 5 | 6 | Node *progNode(int id, Token *tok, InstructionType type, ReturnType ret) { 7 | Node *n = malloc(sizeof(Node)); 8 | n->type = type; 9 | n->ret = ret; 10 | n->children_index = 0; 11 | n->children_capacity = 20; 12 | 13 | n->memory_address = -1; 14 | 15 | n->id = id; 16 | n->tok = tok; 17 | 18 | n->children = malloc(sizeof(Node *) * n->children_capacity); 19 | return n; 20 | } 21 | 22 | Node *progEmptyNode(int id, InstructionType type, ReturnType ret) { 23 | Node *n = malloc(sizeof(Node)); 24 | n->children_index = 0; 25 | n->children_capacity = 20; 26 | 27 | n->id = id; 28 | n->type = type; 29 | n->ret = ret; 30 | 31 | n->tok = malloc(sizeof(Token)); 32 | n->tok->str = "Group"; 33 | n->tok->line = 0; 34 | n->tok->column = 0; 35 | n->tok->size = 0; 36 | n->tok->filename = ""; 37 | n->tok->lexCode = 0; 38 | 39 | n->memory_address = -1; 40 | 41 | n->children = malloc(sizeof(Node *) * n->children_capacity); 42 | return n; 43 | } 44 | 45 | void progAddChild(Node *n, Node *child) { 46 | if (n->children_capacity >= n->children_index + 1) { 47 | n->children_capacity += 10; 48 | n->children = realloc(n->children, sizeof(Node *) * n->children_capacity); 49 | } 50 | n->children[n->children_index++] = child; 51 | } 52 | -------------------------------------------------------------------------------- /ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST 2 | #define AST 3 | 4 | typedef struct Token { 5 | int lexCode; 6 | char *filename; 7 | int line; 8 | int column; 9 | int size; 10 | char *str; 11 | } Token; 12 | 13 | 14 | typedef enum InstructionType { 15 | // TYPES 16 | INSTR_I8, 17 | INSTR_I16, 18 | INSTR_I8S, 19 | INSTR_I16S, 20 | INSTR_CHAR, 21 | INSTR_CHARS, 22 | INSTR_LITERAL_INT, 23 | INSTR_LITERAL_CHAR, 24 | 25 | // PARTS 26 | INSTR_ROOT, 27 | INSTR_BREAK, 28 | INSTR_MEM_AREA, 29 | INSTR_MEM_LOCATION, 30 | INSTR_LABEL, 31 | INSTR_PAD, 32 | INSTR_FUNCTION, 33 | INSTR_FUNCTION_CALL, 34 | ISNTR_ARGUMENTS, 35 | INSTR_STRING_LITERAL, 36 | INSTR_SPRITE, 37 | INSTR_SPRITE_ROW, 38 | 39 | // COLORS 40 | INSTR_THEME, 41 | INSTR_COLOR_BACKGROUND, 42 | INSTR_COLOR0, 43 | INSTR_COLOR1, 44 | INSTR_COLOR2, 45 | 46 | INSTR_LOCAL_DEFINE, 47 | INSTR_LOCAL_READ, 48 | INSTR_LOCAL_ASSIGN, 49 | INSTR_LOCAL_ARRAY_ASSIGN, 50 | INSTR_LOCAL_ARRAY_READ, 51 | 52 | INSTR_GLOBAL_DEFINE, 53 | INSTR_GLOBAL_READ, 54 | INSTR_GLOBAL_ASSIGN, 55 | INSTR_GLOBAL_ARRAY_ASSIGN, 56 | INSTR_GLOBAL_ARRAY_READ, 57 | 58 | // MATH 59 | INSTR_TIMES, 60 | INSTR_DIVIDE, 61 | INSTR_PLUS, 62 | INSTR_MINUS, 63 | INSTR_MODULO, 64 | 65 | // LOGIC 66 | INSTR_EQUALITY, 67 | INSTR_NOT_EQUALITY, 68 | INSTR_AND, 69 | INSTR_OR, 70 | INSTR_GREATER_THAN, 71 | INSTR_LESS_THAN, 72 | 73 | // CONTROL FLOW 74 | INSTR_WHILE, 75 | INSTR_CONDITION, 76 | INSTR_RETURN, 77 | 78 | // IO 79 | INSTR_GET, 80 | INSTR_SEND, 81 | 82 | // TO AXE 83 | INSTR_PUT, 84 | INSTR_PRINT 85 | } InstructionType; 86 | 87 | typedef enum ReturnType { 88 | RET_I8, 89 | RET_I16, 90 | RET_I, 91 | RET_I8S, 92 | RET_I16S, 93 | RET_CHAR, 94 | RET_CHARS, 95 | RET_STRING, 96 | RET_VOID 97 | } ReturnType; 98 | 99 | typedef struct Node { 100 | int id; 101 | int children_index; 102 | int children_capacity; 103 | InstructionType type; 104 | ReturnType ret; 105 | 106 | // Track if this code can leave 107 | // int leaves; 108 | 109 | int memory_address; 110 | 111 | int loc; 112 | Token *tok; 113 | struct Node **children; 114 | } Node; 115 | 116 | extern Node *progNode(int id, Token *tok, InstructionType type, ReturnType ret); 117 | extern Node *progEmptyNode(int id, InstructionType type, ReturnType ret); 118 | extern void progAddChild(Node *n, Node *child); 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /color.c: -------------------------------------------------------------------------------- 1 | #include "lang.h" 2 | #include "parser.h" 3 | #include 4 | // I don't like including the C file here, 5 | // but it gives us access too yyTokenName[] 6 | #include "compiler.h" 7 | #include "parser.c" 8 | #include "scanner.h" 9 | 10 | // Black 0;30 Dark Gray 1;30 11 | // Red 0;31 Light Red 1;31 12 | // Green 0;32 Light Green 1;32 13 | // Brown/Orange 0;33 Yellow 1;33 14 | // Blue 0;34 Light Blue 1;34 15 | // Purple 0;35 Light Purple 1;35 16 | // Cyan 0;36 Light Cyan 1;36 17 | // Light Gray 0;37 White 1;37 18 | 19 | void printTokens(FILE *file) { 20 | char *no_color = "\033[0m"; 21 | 22 | char *colors[] = {"\033[1;31m", "\033[1;32m", "\033[1;33m", "\033[1;34m", 23 | "\033[1;35m", "\033[1;36m", "\033[1;33m", "\033[1;37m"}; 24 | 25 | ExtraData datas[10000]; 26 | int lexCodes[10000]; 27 | int maxLexCode = 0; 28 | 29 | ExtraData data; 30 | data.line = 1; 31 | data.col = 1; 32 | data.size = 1; 33 | 34 | yyscan_t scanner; 35 | yylex_init_extra(&data, &scanner); 36 | 37 | yyset_in(file, scanner); 38 | 39 | int index = 0; 40 | int lexCode; 41 | do { 42 | lexCode = yylex(scanner); 43 | lexCodes[index] = lexCode; 44 | datas[index].line = data.line; 45 | datas[index].col = data.col; 46 | datas[index].size = data.size; 47 | index++; 48 | 49 | if (lexCode > maxLexCode) { 50 | maxLexCode = lexCode; 51 | } 52 | } while (lexCode > 0); 53 | 54 | rewind(file); 55 | 56 | int charIndex = 1; 57 | int lineIndex = 1; 58 | int dataIndex = 0; 59 | 60 | int c; 61 | while ((c = fgetc(file)) != EOF) { 62 | if (dataIndex > 0 && (datas[dataIndex - 1].col) == charIndex && 63 | datas[dataIndex - 1].line == lineIndex) { 64 | printf("%s", no_color); 65 | } 66 | 67 | if ((datas[dataIndex].col - datas[dataIndex].size) == charIndex && 68 | datas[dataIndex].line == lineIndex) { 69 | printf("%s", colors[lexCodes[dataIndex] % 8]); 70 | dataIndex++; 71 | } 72 | 73 | printf("%c", c); 74 | charIndex++; 75 | 76 | if (c == '\n') { 77 | lineIndex++; 78 | charIndex = 1; 79 | } 80 | } 81 | 82 | printf("\n"); 83 | for (int i = 0; i <= maxLexCode; i++) { 84 | printf("%s█ - %s%s\n", colors[i % 8], yyTokenName[i], no_color); 85 | } 86 | } 87 | 88 | int main(int argc, char **argv) { 89 | FILE *file; 90 | if (argc > 1) { 91 | file = fopen(argv[1], "r"); 92 | if (!file) { 93 | fprintf(stderr, "could not open %s\n", argv[1]); 94 | return 1; 95 | } 96 | } else { 97 | fprintf(stderr, "Please provide a file\n"); 98 | return 1; 99 | } 100 | 101 | printTokens(file); 102 | } 103 | -------------------------------------------------------------------------------- /compiler.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "lang.h" 3 | #include "lut.h" 4 | #include "parser.h" 5 | #include "scanner.h" 6 | #include 7 | 8 | void *ParseAlloc(void *(*allocProc)(unsigned long)); 9 | void *ParseFree(void *, void (*freeProc)(void *)); 10 | void *Parse(void *, int lexCode, Token *tok, Program *data); 11 | 12 | int id; 13 | 14 | ReturnType InstrToRet(InstructionType instr) { 15 | switch (instr) { 16 | case INSTR_I8: 17 | return RET_I8; 18 | case INSTR_I16: 19 | return RET_I16; 20 | case INSTR_I8S: 21 | return RET_I8S; 22 | case INSTR_I16S: 23 | return RET_I16S; 24 | case INSTR_CHARS: 25 | return RET_CHARS; 26 | case INSTR_CHAR: 27 | return RET_CHAR; 28 | case INSTR_ROOT: 29 | return RET_VOID; 30 | default: 31 | fprintf(stderr, "failed on instr type %s\n", typeType(instr)); 32 | return 0; 33 | } 34 | } 35 | 36 | ReturnType memoryIdentifierType(Program *prog, Node *node, char *loc1, char *loc2) { 37 | if (strcmp(loc1, "System") == 0) { 38 | prog->used_system = 1; 39 | if (strcmp(loc2, "vector") == 0) { 40 | return RET_I16; 41 | } else if (strcmp(loc2, "r") == 0) { 42 | return RET_I16; 43 | } else if (strcmp(loc2, "g") == 0) { 44 | return RET_I16; 45 | } else if (strcmp(loc2, "b") == 0) { 46 | return RET_I16; 47 | } else { 48 | printError(prog, node, "unknown aspect of System"); 49 | fprintf(stderr, "%s\n", loc2); 50 | return RET_VOID; 51 | } 52 | } else if (strcmp(loc1, "Console") == 0) { 53 | prog->used_console = 1; 54 | if (strcmp(loc2, "vector") == 0) { 55 | return RET_I16; 56 | } else if (strcmp(loc2, "write") == 0) { 57 | return RET_I8; 58 | } else { 59 | printError(prog, node, "unknown aspect of Console"); 60 | fprintf(stderr, "%s\n", loc2); 61 | return RET_VOID; 62 | } 63 | } else if (strcmp(loc1, "Screen") == 0) { 64 | prog->used_screen = 1; 65 | if (strcmp(loc2, "vector") == 0) { 66 | return RET_I16; 67 | } else if (strcmp(loc2, "width") == 0) { 68 | return RET_I16; 69 | } else if (strcmp(loc2, "height") == 0) { 70 | return RET_I16; 71 | } else if (strcmp(loc2, "x") == 0) { 72 | return RET_I16; 73 | } else if (strcmp(loc2, "y") == 0) { 74 | return RET_I16; 75 | } else if (strcmp(loc2, "addr") == 0) { 76 | return RET_I16; 77 | } else if (strcmp(loc2, "pixel") == 0) { 78 | return RET_I8; 79 | } else if (strcmp(loc2, "sprite") == 0) { 80 | return RET_I8; 81 | } else { 82 | printError(prog, node, "unknown aspect of Screen"); 83 | fprintf(stderr, "%s\n", loc2); 84 | return RET_VOID; 85 | } 86 | } else if (strcmp(loc1, "Audio0") == 0 || 87 | strcmp(loc1, "Audio1") == 0 || 88 | strcmp(loc1, "Audio2") == 0 || 89 | strcmp(loc1, "Audio3") == 0 90 | ) { 91 | prog->used_audio = 1; 92 | if (strcmp(loc2, "addr") == 0) { 93 | return RET_I16; 94 | } else if (strcmp(loc2, "length") == 0) { 95 | return RET_I16; 96 | } else if (strcmp(loc2, "adsr") == 0) { 97 | return RET_I16; 98 | } else if (strcmp(loc2, "volume") == 0) { 99 | return RET_I8; 100 | } else if (strcmp(loc2, "pitch") == 0) { 101 | return RET_I8; 102 | } 103 | } else if (strcmp(loc1, "Mouse") == 0) { 104 | prog->used_mouse = 1; 105 | if (strcmp(loc2, "vector") == 0) { 106 | return RET_I16; 107 | } else if (strcmp(loc2, "x") == 0) { 108 | return RET_I16; 109 | } else if (strcmp(loc2, "y") == 0) { 110 | return RET_I16; 111 | } else if (strcmp(loc2, "state") == 0) { 112 | return RET_I8; 113 | } else { 114 | printError(prog, node, "unknown aspect of Mouse"); 115 | fprintf(stderr, "%s\n", loc2); 116 | return RET_VOID; 117 | } 118 | } else if (strcmp(loc1, "DateTime") == 0) { 119 | prog->used_datetime = 1; 120 | if (strcmp(loc2, "year") == 0) { 121 | return RET_I16; 122 | } else if (strcmp(loc2, "month") == 0) { 123 | return RET_I8; 124 | } else if (strcmp(loc2, "hour") == 0) { 125 | return RET_I8; 126 | } else if (strcmp(loc2, "minute") == 0) { 127 | return RET_I8; 128 | } else if (strcmp(loc2, "second") == 0) { 129 | return RET_I8; 130 | } else { 131 | printError(prog, node, "unknown aspect of DateTime"); 132 | fprintf(stderr, "%s\n", loc2); 133 | return RET_VOID; 134 | } 135 | } else { 136 | printError(prog, node, "unknown label"); 137 | return RET_VOID; 138 | } 139 | return RET_VOID; 140 | } 141 | 142 | void verifyTypes(Program * prog, Node * node, Node * lhs, Node * rhs) { 143 | if (rhs->ret == RET_I && lhs->ret == RET_I) { 144 | fprintf(stderr, "Warn: Inferring i8 type due to lack of local clearity\n"); 145 | lhs->ret = RET_I8; 146 | rhs->ret = RET_I8; 147 | } 148 | 149 | if (lhs->ret == RET_I && rhs->ret != RET_I) { 150 | lhs->ret = rhs->ret; 151 | } 152 | 153 | if (rhs->ret == RET_I && lhs->ret != RET_I) { 154 | rhs->ret = lhs->ret; 155 | } 156 | 157 | if (lhs->ret != rhs->ret) { 158 | printError(prog, node, "expression is operating on different types"); 159 | fprintf(stderr, "%i != %i\n", lhs->ret, rhs->ret); 160 | } 161 | } 162 | 163 | Node *progImplicitPad(Program * prog, Node * node) { 164 | return progNode(prog->id++, node->tok, INSTR_PAD, RET_VOID); 165 | } 166 | 167 | 168 | void parse(Program *prog) { 169 | // TODO: we can inline these variables 170 | 171 | yyscan_t scanner = prog->scanner; 172 | void *parser = prog->parser; 173 | ExtraData *data = &prog->data; 174 | int lexCode; 175 | Token *tok = malloc(sizeof(Token)); 176 | do { 177 | lexCode = yylex(scanner); 178 | 179 | tok->filename = data->filename; 180 | // TODO: What the heck is going on here. 181 | // I think a string I am getting from yyget_text is not null terminated? 182 | // This is casuing some really weird behavior, but it seems super rare? 183 | // right now I can only reproduce it using piano.dt for 184 | // send(.Screen/addr, :sprite_key_right) 185 | // that label includes the closing paren some how. 186 | tok->str = malloc(sizeof(char) * yyget_leng(scanner) + 1); 187 | tok->str[yyget_leng(scanner)] = '\0'; 188 | tok->line = data->line; 189 | tok->column = data->col; 190 | tok->size = data->size; 191 | tok->lexCode = lexCode; 192 | 193 | strcpy(tok->str, yyget_text(scanner)); 194 | 195 | prog->tok = tok; 196 | /* fprintf(stderr, "CODE: %i tok: \"%s\" :%i\n", lexCode, tok->str, tok->line); */ 197 | Parse(parser, lexCode, tok, prog); 198 | tok = malloc(sizeof(Token)); 199 | } while (lexCode > 0); 200 | 201 | // resolve defered lookups 202 | for (int i = 0; i < prog->function_lut.defer_index; i++) { 203 | Node * node = prog->function_lut.defered_lookups[i]; 204 | int functionId = lutFindFnCall(&prog->function_lut, node); 205 | 206 | if (functionId == -1) { 207 | printError(prog, node, "function not found"); 208 | } else { 209 | node->ret = prog->function_lut.types[functionId]; 210 | } 211 | } 212 | } 213 | 214 | void setupCompiler(Program * prog) { 215 | prog->id = 0; 216 | prog->errored = 0; 217 | prog->strings.capacity = 100; 218 | prog->strings.index = 0; 219 | prog->strings.nodes = malloc(sizeof(Node *) * prog->strings.capacity); 220 | 221 | setupLut(&prog->local_lut, 10); 222 | prog->local_lut.stack_slots = MAX_STACK_SLOTS; 223 | setupLut(&prog->global_lut, 10); 224 | setupLut(&prog->function_lut, 10); 225 | 226 | prog->used_system = 0; 227 | prog->used_console = 0; 228 | prog->used_screen = 0; 229 | prog->used_mouse = 0; 230 | prog->used_datetime = 0; 231 | prog->used_print = 0; 232 | prog->used_audio = 0; 233 | prog->used_sine = 0; 234 | 235 | prog->root = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 236 | 237 | prog->data.line = 1; 238 | prog->data.col = 1; 239 | prog->data.size = 1; 240 | 241 | yylex_init_extra(&prog->data, &prog->scanner); 242 | 243 | prog->parser = ParseAlloc(malloc); 244 | prog->output = stdout; 245 | } 246 | 247 | void resetCompiler(Program * prog) { 248 | prog->id = 0; 249 | prog->errored = 0; 250 | prog->strings.capacity = 100; 251 | prog->strings.index = 0; 252 | 253 | free(prog->strings.nodes); 254 | prog->strings.nodes = malloc(sizeof(Node *) * prog->strings.capacity); 255 | 256 | resetLut(&prog->local_lut, 10); 257 | resetLut(&prog->global_lut, 10); 258 | resetLut(&prog->function_lut, 10); 259 | 260 | 261 | prog->used_system = 0; 262 | prog->used_console = 0; 263 | prog->used_screen = 0; 264 | prog->used_mouse = 0; 265 | prog->used_datetime = 0; 266 | prog->used_print = 0; 267 | prog->used_audio = 0; 268 | prog->used_sine = 0; 269 | 270 | prog->data.line = 1; 271 | prog->data.col = 1; 272 | prog->data.size = 1; 273 | 274 | } 275 | 276 | void fromString(Program * prog, char * str) { 277 | yy_scan_string(str, prog->scanner); 278 | } 279 | 280 | void fromStdin(Program * prog) { 281 | yyset_in(stdin, prog->scanner); 282 | } 283 | 284 | void toFile(Program * prog, char * file) { 285 | FILE * f = fopen(file, "w"); 286 | 287 | if (!f) { 288 | fprintf(stderr, "could not open %s\n", file); 289 | } 290 | 291 | prog->output = f; 292 | } 293 | 294 | void fromFile(Program * prog, char * file) { 295 | FILE * f = fopen(file, "r"); 296 | 297 | if (!f) { 298 | fprintf(stderr, "could not open %s\n", file); 299 | } 300 | 301 | prog->data.filename = file; 302 | yyset_in(f, prog->scanner); 303 | } 304 | -------------------------------------------------------------------------------- /compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER 2 | #define COMPILER 3 | 4 | #include 5 | #include "lang.h" 6 | 7 | extern void setupCompiler(Program * prog); 8 | extern void resetCompiler(Program * prog); 9 | extern void fromString(Program * prog, char * str); 10 | extern void fromStdin(Program * prog); 11 | extern void fromFile(Program * prog, char * file); 12 | extern void toFile(Program * prog, char * file); 13 | extern void parse(Program * prog); 14 | 15 | extern ReturnType InstrToRet(InstructionType instr); 16 | extern ReturnType memoryIdentifierType(Program *prog, Node *node, char *loc1, char *loc2); 17 | extern void verifyTypes(Program * prog, Node * node, Node * lhs, Node * rhs); 18 | extern Node *progImplicitPad(Program * prog, Node * node); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | #include "lang.h" 2 | #include 3 | #include 4 | 5 | const char *retType(ReturnType ret) { 6 | switch (ret) { 7 | case RET_I8: 8 | return "i8"; 9 | case RET_I16: 10 | return "i16"; 11 | case RET_I: 12 | return "i"; 13 | case RET_I8S: 14 | return "i8s"; 15 | case RET_I16S: 16 | return "i16s"; 17 | case RET_CHAR: 18 | return "char"; 19 | case RET_CHARS: 20 | return "chars"; 21 | case RET_STRING: 22 | return "string"; 23 | case RET_VOID: 24 | return "void"; 25 | } 26 | } 27 | 28 | // TODO: this name really sucks 29 | const char *typeType(InstructionType type) { 30 | switch (type) { 31 | case INSTR_BREAK: 32 | return "Break"; 33 | case INSTR_I8: 34 | return "i8"; 35 | case INSTR_I16: 36 | return "i16"; 37 | case INSTR_I8S: 38 | return "i8s"; 39 | case INSTR_I16S: 40 | return "i16s"; 41 | case INSTR_CHAR: 42 | return "Char"; 43 | case INSTR_CHARS: 44 | return "Chars"; 45 | case INSTR_LITERAL_INT: 46 | return "INT"; 47 | case INSTR_MEM_AREA: 48 | return "memA"; 49 | case INSTR_MEM_LOCATION: 50 | return "memL"; 51 | case INSTR_THEME: 52 | return "Theme"; 53 | case INSTR_RETURN: 54 | return "Return"; 55 | case INSTR_PAD: 56 | return "Pad"; 57 | case INSTR_FUNCTION: 58 | return "fn"; 59 | case INSTR_FUNCTION_CALL: 60 | return "fcall"; 61 | case ISNTR_ARGUMENTS: 62 | return "Arg"; 63 | case INSTR_LABEL: 64 | return "Label"; 65 | case INSTR_PLUS: 66 | return "Plus"; 67 | case INSTR_MINUS: 68 | return "Minus"; 69 | case INSTR_TIMES: 70 | return "Times"; 71 | case INSTR_DIVIDE: 72 | return "Divide"; 73 | case INSTR_MODULO: 74 | return "Mod"; 75 | case INSTR_GLOBAL_DEFINE: 76 | return "$Define"; 77 | case INSTR_GLOBAL_ASSIGN: 78 | return "$Assign"; 79 | case INSTR_GLOBAL_READ: 80 | return "$Read"; 81 | case INSTR_ROOT: 82 | return "Node"; 83 | case INSTR_LOCAL_DEFINE: 84 | return "Define"; 85 | case INSTR_LOCAL_ASSIGN: 86 | return "Assign"; 87 | case INSTR_LOCAL_ARRAY_ASSIGN: 88 | return "[]Assign"; 89 | case INSTR_LOCAL_ARRAY_READ: 90 | return "[]Read"; 91 | case INSTR_GLOBAL_ARRAY_ASSIGN: 92 | return "$[]Assign"; 93 | case INSTR_GLOBAL_ARRAY_READ: 94 | return "$[]Read"; 95 | case INSTR_LOCAL_READ: 96 | return "Read"; 97 | case INSTR_PUT: 98 | return "Put"; 99 | case INSTR_GET: 100 | return "Get"; 101 | case INSTR_SEND: 102 | return "Send"; 103 | case INSTR_PRINT: 104 | return "Print"; 105 | case INSTR_CONDITION: 106 | return "Cond"; 107 | case INSTR_WHILE: 108 | return "While"; 109 | case INSTR_EQUALITY: 110 | return "EQ"; 111 | case INSTR_NOT_EQUALITY: 112 | return "NOT EQ"; 113 | case INSTR_AND: 114 | return "And"; 115 | case INSTR_OR: 116 | return "Or"; 117 | case INSTR_GREATER_THAN: 118 | return "Greater Than"; 119 | case INSTR_LESS_THAN: 120 | return "Less Than"; 121 | case INSTR_STRING_LITERAL: 122 | return "String"; 123 | case INSTR_LITERAL_CHAR: 124 | return "Char"; 125 | case INSTR_COLOR_BACKGROUND: 126 | return "Background"; 127 | case INSTR_COLOR0: 128 | return "C0"; 129 | case INSTR_COLOR1: 130 | return "C1"; 131 | case INSTR_COLOR2: 132 | return "C2"; 133 | case INSTR_SPRITE: 134 | return "Sprite"; 135 | case INSTR_SPRITE_ROW: 136 | return "Sprite Row"; 137 | } 138 | } 139 | 140 | void printNode(Node *node) { 141 | fprintf(stderr, "- <%s: \"%s\">\n", typeType(node->type), node->tok); 142 | } 143 | 144 | void print_helper(Node *n) { 145 | printf(" \"%i\" [label=\"%s: %s\" shape=\"box\"];\n", n->id, typeType(n->type), n->tok->str); 146 | 147 | for (int i = 0; i < n->children_index; i++) { 148 | print_helper(n->children[i]); 149 | printf(" \"%i\" -> \"%i\" [label=\"%s\"];\n", n->id, n->children[i]->id, retType(n->children[i]->ret)); 150 | } 151 | } 152 | 153 | void printGraph(Node *s) { 154 | printf("digraph G {\n"); 155 | 156 | print_helper(s); 157 | 158 | printf("}\n"); 159 | } 160 | 161 | void printTree(Node *root) { 162 | printNode(root); 163 | for (int i = 0; i < root->children_index; i++) { 164 | printTree(root->children[i]); 165 | } 166 | } 167 | 168 | char *programLine(Program *prog, int num) { 169 | size_t len; 170 | char *line = NULL; 171 | FILE *file; 172 | file = fopen(prog->tok->filename, "r"); 173 | if (!file) { 174 | fprintf(stderr, "could not open '%s'\n", prog->tok->filename); 175 | return ""; 176 | } else { 177 | for (int i = 0; i < num; i++) { 178 | getline(&line, &len, file); 179 | } 180 | fclose(file); 181 | } 182 | return line; 183 | } 184 | 185 | void printUnderline(Token *start) { 186 | for (int i = 1; i < start->column - start->size; i++) { 187 | fprintf(stderr, " "); 188 | } 189 | 190 | fprintf(stderr, "└"); 191 | for (int i = 1; i < start->size; i++) { 192 | fprintf(stderr, "─"); 193 | } 194 | fprintf(stderr, "\n"); 195 | } 196 | 197 | void printError(Program *prog, Node *node, const char *message) { 198 | prog->errored = 1; 199 | fprintf(stderr, "ERROR: %s -%s- :%s: - %s:%i:%i-%i\n", message, 200 | typeType(node->type), node->tok->str, node->tok->filename, node->tok->line, 201 | node->tok->column - node->tok->size, node->tok->column); 202 | fprintf(stderr, "%s", programLine(prog, node->tok->line)); 203 | printUnderline(node->tok); 204 | } 205 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG 2 | #define DEBUG 3 | 4 | #include "lang.h" 5 | 6 | extern const char *printInstruction(Token *tok); 7 | extern const char *printGraph(Node *tok); 8 | extern const char *printTree(Node *tok); 9 | extern void printNode(Node *node); 10 | extern const char *retType(ReturnType ret); 11 | extern const char *typeType(InstructionType type); 12 | 13 | extern char *programLine(Program *prog, int num); 14 | extern void printUnderline(Token *tok); 15 | extern void printError(Program *prog, Node *node, const char *message); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /emit.c: -------------------------------------------------------------------------------- 1 | #include "compiler.h" 2 | #include "lut.h" 3 | #include "debug.h" 4 | #include "lang.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #define EMIT(str, ...) fprintf(prog->output, str, ##__VA_ARGS__) 10 | 11 | void emit_condition(Program *prog, Node *node) { 12 | if (node->children_index > 2) { 13 | EMIT(" ;default-%i JCN2\n", node->id); 14 | EMIT(" ;else-%i JMP2\n", node->id); 15 | } else if (node->children_index == 2) { 16 | EMIT(" ;default-%i JCN2\n", node->id); 17 | EMIT(" ;after-%i JMP2\n", node->id); 18 | } else { 19 | printError(prog, node, "condition has no true or false paths"); 20 | } 21 | } 22 | 23 | int add_string(Program *prog, Node *node) { 24 | if (prog->strings.index == prog->strings.capacity) { 25 | prog->strings.capacity += 10; 26 | prog->strings.nodes = realloc(prog->strings.nodes, prog->strings.capacity); 27 | } 28 | 29 | for (int i = 0; i < prog->strings.index; i++) { 30 | if (strcmp(prog->strings.nodes[i]->tok->str, node->tok->str) == 0) { 31 | return i; 32 | } 33 | } 34 | prog->strings.nodes[prog->strings.index++] = node; 35 | return prog->strings.index - 1; 36 | } 37 | 38 | void emit_self(Program *prog, Node *node) { 39 | /* fprintf(stderr, "EMITTING %s\n", tokType(node->tok->str)); */ 40 | switch (node->type) { 41 | case INSTR_BREAK: 42 | EMIT(" BREAKPOINT\n"); 43 | break; 44 | case INSTR_CONDITION: 45 | emit_condition(prog, node); 46 | break; 47 | case INSTR_WHILE: 48 | EMIT(" ;while-block-%i JCN2\n", node->id); 49 | EMIT(" ;while-end-%i JMP2\n", node->id); 50 | break; 51 | case INSTR_PAD: 52 | EMIT(" #00 SWP ( i8 -> i16 )\n"); 53 | break; 54 | case INSTR_FUNCTION: { 55 | int id = lutFindFn(&prog->function_lut, node); 56 | if (id == -1) { 57 | printError(prog, node, "Function not found - in definition! how does this happen!"); 58 | } else { 59 | EMIT("@%s-%i\n", prog->function_lut.names[id], id); 60 | } 61 | break; 62 | } 63 | case INSTR_LABEL: { 64 | // This has no args or returns so, we look it up by name only 65 | int id = lutFind(&prog->function_lut, node); 66 | if (id == -1) { 67 | // TODO: it might be good to register implicit labels, 68 | // but for now we can just emit them 69 | EMIT(" ;%s\n", node->tok->str); 70 | } else { 71 | EMIT(" ;%s-%i\n", prog->function_lut.names[id], id); 72 | } 73 | break; 74 | } 75 | case INSTR_GLOBAL_ASSIGN: 76 | switch (prog->global_lut.types[lutFind(&prog->global_lut, node)]) { 77 | case RET_I8: 78 | EMIT(" ;global-heap #%04x ADD2 STA ( assign global %s )\n", node->loc, node->tok->str); 79 | break; 80 | case RET_I16: 81 | EMIT(" ;global-heap #%04x ADD2 STA2 ( assign global %s )\n", node->loc, node->tok->str); 82 | break; 83 | default: 84 | printError(prog, node, "local assign only supports i8 and i16 implement it!"); 85 | break; 86 | } 87 | break; 88 | case INSTR_GLOBAL_DEFINE: 89 | // skip 90 | break; 91 | case INSTR_GLOBAL_READ: 92 | if (node->ret == RET_I8) { 93 | EMIT(" ;global-heap #%04x ADD2 LDA ( load global %s )\n", node->loc, node->tok->str); 94 | } else if (node->ret == RET_I16) { 95 | EMIT(" ;global-heap #%04x ADD2 LDA2 ( load global %s )\n", node->loc, node->tok->str); 96 | } 97 | break; 98 | case INSTR_DIVIDE: 99 | if (node->children_index > 2) { 100 | printError(prog, node, "plus has wrong number of children"); 101 | } 102 | 103 | if (node->ret == RET_I8) { 104 | EMIT(" DIV\n"); 105 | } else if (node->ret == RET_I16) { 106 | EMIT(" DIV2\n"); 107 | } 108 | 109 | break; 110 | case INSTR_FUNCTION_CALL: { 111 | int id = lutFindFnCall(&prog->function_lut, node); 112 | if (id == -1) { 113 | printError(prog, node, "Function not found"); 114 | } else { 115 | EMIT(" ;%s-%i JSR2\n", prog->function_lut.names[id], id); 116 | 117 | for (int i = 0; i < prog->function_lut.arg_count[id] && i < MAX_STACK_SLOTS; i++) { 118 | if (prog->function_lut.args[id][i] == RET_I16) { 119 | EMIT("POP2 ( removing i16 stack arg )\n"); 120 | } else { 121 | EMIT("POP ( removing i8 stack arg )\n"); 122 | } 123 | } 124 | } 125 | break; 126 | } 127 | 128 | case ISNTR_ARGUMENTS: 129 | // Ignore arguments. 130 | // TODO: we might not actually need to keep these in the tree? 131 | break; 132 | case INSTR_LOCAL_READ: { 133 | if (node->loc < 0) { 134 | if (node->loc == -1) { 135 | if (node->ret == RET_I8 || node->ret == RET_CHAR) { 136 | EMIT(" DUP ( read local from stack i8 %s )\n", node->tok->str); 137 | } else if (node->ret == RET_I16) { 138 | EMIT(" DUP2 ( read local from stack %s )\n", node->tok->str); 139 | } else { 140 | fprintf(stderr, "************************* error failed to emit local read\n"); 141 | } 142 | } else if (node->loc == -2) { 143 | if (node->ret == RET_I8 || node->ret == RET_CHAR) { 144 | EMIT(" OVR ( read local from stack i8 %s )\n", node->tok->str); 145 | } else if (node->ret == RET_I16) { 146 | EMIT(" OVR2 ( read local from stack %s )\n", node->tok->str); 147 | } else { 148 | fprintf(stderr, "************************* error failed to emit local read\n"); 149 | } 150 | } 151 | break; 152 | } 153 | 154 | if (node->ret == RET_I8 || node->ret == RET_CHAR) { 155 | EMIT(" ;local-heap #%04x ADD2 LDA ( read local %s )\n", node->loc, node->tok->str); 156 | } else if (node->ret == RET_I16) { 157 | EMIT(" ;local-heap #%04x ADD2 LDA2 ( read local %s )\n", node->loc, node->tok->str); 158 | } else { 159 | fprintf(stderr, "************************* error failed to emit local read\n"); 160 | } 161 | break; 162 | } 163 | case INSTR_LOCAL_ASSIGN: { 164 | if (node->loc < 0) { 165 | // Skip when we saved the local on the stack only 166 | break; 167 | } 168 | switch (node->ret) { 169 | case RET_CHAR: 170 | case RET_I8: 171 | EMIT(" ;local-heap #%04x ADD2 STA ( assign local i8 %s )\n", node->loc, node->tok->str); 172 | break; 173 | case RET_I16: 174 | EMIT(" ;local-heap #%04x ADD2 STA2 ( assign local %s )\n", node->loc, node->tok->str); 175 | break; 176 | default: 177 | printError(prog, node, "********* local assign only supports i8 and i16 implement it!"); 178 | fprintf(stderr, "XXXXXXXXXX %s\n", retType(node->ret)); 179 | break; 180 | } 181 | 182 | /* if (node->children[0]->ret == RET_I8) { */ 183 | /* EMIT(" ;local-heap #%04x ADD2 STA ( assign local %s )\n", node->loc, node->tok->str); */ 184 | /* } else if (node->children[0]->ret == RET_I16) { */ 185 | /* EMIT(" ;local-heap #%04x ADD2 STA2 ( assign local %s )\n", node->loc,node->tok->str); */ 186 | /* } else { */ 187 | /* // TODO: add support for char and string types */ 188 | /* printError(prog, node, "local assign only supports i8 and i16 implement it!"); */ 189 | /* } */ 190 | break; 191 | } 192 | case INSTR_LOCAL_ARRAY_ASSIGN: { 193 | if (node->children[0]->ret == RET_I8 || node->children[0]->ret == RET_CHAR) { 194 | // The second ADD2 is the array index 195 | EMIT(" ;local-heap #%04x ADD2 ADD2 STA ( assign local %s )\n", node->loc, node->tok->str); 196 | } else if (node->children[0]->ret == RET_I16) { 197 | // TODO 198 | EMIT(" #0002 MUL2 ;local-heap #%04x ADD2 ADD2 STA2 ( assign local %s )\n", node->loc, node->tok->str); 199 | } else { 200 | printError(prog, node, "local array assign only supports i8 and i16 implement it!"); 201 | fprintf(stderr, "type: %i\n", node->children[0]->ret); 202 | } 203 | break; 204 | } 205 | case INSTR_LOCAL_ARRAY_READ: { 206 | if (node->ret == RET_I8 || node->ret == RET_CHAR) { 207 | // The second ADD2 is the array index 208 | EMIT(" ;local-heap #%04x ADD2 ADD2 LDA ( read local %s )\n", node->loc, node->tok->str); 209 | } else if (node->ret == RET_I16) { 210 | EMIT(" #0002 MUL2 ;local-heap #%04x ADD2 ADD2 LDA2 ( assign local %s )\n", node->loc, node->tok->str); 211 | } else { 212 | printError(prog, node, "local array read only supports i8 and i16 implement it!"); 213 | fprintf(stderr, "type: %i\n", node->ret); 214 | } 215 | break; 216 | } 217 | case INSTR_LITERAL_INT: 218 | if (node->ret == RET_I8) { 219 | EMIT(" LIT %02x ( literal %s )\n", atoi(node->tok->str), node->tok->str); 220 | } else if (node->ret == RET_I16) { 221 | EMIT(" LIT2 %04x ( literal %s )\n", atoi(node->tok->str), node->tok->str); 222 | } else { 223 | EMIT(" LIT2 %04x ( literal %s )\n", atoi(node->tok->str), node->tok->str); 224 | } 225 | break; 226 | case INSTR_LITERAL_CHAR: 227 | EMIT(" LIT '%c ( character literal %s )\n", node->tok->str[0], node->tok->str); 228 | break; 229 | case INSTR_GLOBAL_ARRAY_ASSIGN: { 230 | if (node->children[0]->ret == RET_I8) { 231 | // The second ADD2 is the array index 232 | EMIT( 233 | " ;global-heap #%04x ADD2 ADD2 STA ( global array assign %s )\n", node->loc, node->tok->str); 234 | } else if (node->children[0]->ret == RET_I16) { 235 | // TODO 236 | EMIT(" #0002 MUL2 ;global-heap #%04x ADD2 ADD2 STA2 ( global array assign %s )\n", node->loc, node->tok->str); 237 | } else { 238 | printError(prog, node, "global array assign only supports i8 and i16 implement it!"); 239 | } 240 | break; 241 | } 242 | case INSTR_GLOBAL_ARRAY_READ: { 243 | if (node->ret == RET_I8) { 244 | // The second ADD2 is the array index 245 | EMIT(" ;global-heap #%04x ADD2 ADD2 LDA ( read array global %s )\n", node->loc, node->tok->str); 246 | } else if (node->ret == RET_I16) { 247 | EMIT(" #0002 MUL2 ;global-heap #%04x ADD2 ADD2 LDA2 ( read array global %s )\n", node->loc, node->tok->str); 248 | } else { 249 | printError(prog, node, "global array read only supports i8 and i16 implement it!"); 250 | } 251 | break; 252 | } 253 | case INSTR_LOCAL_DEFINE: { 254 | /* localLutInsert(prog, node, node->children[0]->ret); */ 255 | break; 256 | } 257 | case INSTR_CHAR: 258 | EMIT(" LIT %02x ( literal %s )\n", node->tok->str[0], node->tok->str); 259 | break; 260 | case INSTR_MINUS: 261 | if (node->children_index > 2) { 262 | printError(prog, node, "plus has wrong number of children"); 263 | } 264 | 265 | if (node->children[0]->ret == RET_I8) { 266 | EMIT(" SUB\n"); 267 | } else if (node->children[0]->ret == RET_I16) { 268 | EMIT(" SUB2\n"); 269 | } else { 270 | printError(prog, node, "what type is this?"); 271 | } 272 | break; 273 | case INSTR_PLUS: 274 | if (node->children_index > 2) { 275 | printError(prog, node, "plus has wrong number of children"); 276 | } 277 | 278 | if (node->children[0]->ret == RET_I8) { 279 | EMIT(" ADD\n"); 280 | } else if (node->children[0]->ret == RET_I16) { 281 | EMIT(" ADD2\n"); 282 | } else { 283 | printError(prog, node, "what type is this?"); 284 | } 285 | break; 286 | case INSTR_EQUALITY: 287 | if (node->children_index > 2) { 288 | printError(prog, node, "equality has wrong number of children"); 289 | } 290 | 291 | if (node->children[0]->ret == RET_I8) { 292 | EMIT(" EQU\n"); 293 | } else if (node->children[0]->ret == RET_I16) { 294 | EMIT(" EQU2\n"); 295 | } 296 | break; 297 | case INSTR_NOT_EQUALITY: 298 | if (node->children_index > 2) { 299 | printError(prog, node, "not equals has wrong number of children"); 300 | } 301 | 302 | if (node->children[0]->ret == RET_I8) { 303 | EMIT(" NEQ\n"); 304 | } else if (node->children[0]->ret == RET_I16) { 305 | EMIT(" NEQ2\n"); 306 | } 307 | break; 308 | case INSTR_GREATER_THAN: 309 | if (node->children[0]->ret == RET_I8) { 310 | EMIT(" GTH\n"); 311 | } else if (node->children[0]->ret == RET_I16) { 312 | EMIT(" GTH2\n"); 313 | } 314 | break; 315 | case INSTR_LESS_THAN: 316 | if (node->children[0]->ret == RET_I8) { 317 | EMIT(" LTH\n"); 318 | } else if (node->children[0]->ret == RET_I16) { 319 | EMIT(" LTH2\n"); 320 | } 321 | break; 322 | case INSTR_MODULO: 323 | if (node->children[0]->ret == RET_I8) { 324 | EMIT(" MOD ( mod )\n"); 325 | } else if (node->children[0]->ret == RET_I16) { 326 | EMIT(" MOD2 ( mod )\n"); 327 | } 328 | break; 329 | case INSTR_AND: 330 | if (node->children[0]->ret == RET_I8) { 331 | EMIT(" AND\n"); 332 | } else if (node->children[0]->ret == RET_I16) { 333 | EMIT(" AND2\n"); 334 | } 335 | break; 336 | case INSTR_OR: 337 | if (node->children[0]->ret == RET_I8) { 338 | EMIT(" ORA\n"); 339 | } else if (node->children[0]->ret == RET_I16) { 340 | EMIT(" ORA2\n"); 341 | } 342 | break; 343 | case INSTR_I16: 344 | /* EMIT(" $2\n"); */ 345 | break; 346 | case INSTR_I8: 347 | /* EMIT(" $1\n"); */ 348 | break; 349 | case INSTR_I16S: 350 | /* EMIT(" $2\n"); */ 351 | break; 352 | case INSTR_I8S: 353 | /* EMIT(" $1\n"); */ 354 | break; 355 | case INSTR_MEM_AREA: 356 | EMIT(" .%s/%s\n", node->tok->str, node->children[0]->tok->str); 357 | break; 358 | case INSTR_MEM_LOCATION: 359 | // skip 360 | break; 361 | case INSTR_STRING_LITERAL: 362 | EMIT(" ;str-%i ( String address )\n", add_string(prog, node)); 363 | break; 364 | case INSTR_PUT: { 365 | switch (node->children[0]->ret) { 366 | case RET_STRING: 367 | EMIT(" ;print-str JSR2 ( Print string )\n"); 368 | break; 369 | case RET_I8: 370 | EMIT(" PRINT_I8 ( Print number )\n"); 371 | break; 372 | case RET_I16: 373 | EMIT(" PRINT_I16 ( Print number )\n"); 374 | break; 375 | case RET_I: 376 | // TODO: ret is not yet defined... 377 | // maybe I can do this better in another way... 378 | // Might resolve itself if we move this out of 379 | EMIT(" PRINT_I8 ( Print number )\n"); 380 | break; 381 | case RET_CHAR: 382 | EMIT(" EMIT ( Print Char )\n"); 383 | break; 384 | case RET_VOID: 385 | fprintf(stderr, "ERROR: can't print void\n"); 386 | break; 387 | default: 388 | fprintf(stderr, "WHAt arE YOU PRINtING????????? %i\n", 389 | node->children[0]->ret); 390 | // TODO: this is used for arrays temp... 391 | EMIT(" PRINT_I8 ( Print number )\n"); 392 | } 393 | EMIT(" #0a EMIT ( Finish with a newline )\n"); 394 | break; 395 | } 396 | 397 | case INSTR_GET: 398 | if (node->ret == RET_I8) { 399 | EMIT(" DEI\n"); 400 | } else { 401 | EMIT(" DEI2\n"); 402 | } 403 | break; 404 | case INSTR_SEND: 405 | // First child is the locationsv 406 | if (node->children[1]->ret == RET_I8) { 407 | EMIT(" DEO\n"); 408 | } else { 409 | EMIT(" DEO2\n"); 410 | } 411 | break; 412 | case INSTR_PRINT: { 413 | switch (node->children[0]->ret) { 414 | case RET_STRING: 415 | EMIT(" ;print-str JSR2 ( Print string )\n"); 416 | break; 417 | case RET_I8: 418 | EMIT(" PRINT_I8 ( Print number )\n"); 419 | break; 420 | case RET_I16: 421 | EMIT(" PRINT_I16 ( Print number )\n"); 422 | break; 423 | case RET_I8S: 424 | EMIT(" PRINT_I16 ( WARNING no idea how to print arrays yet )\n"); 425 | break; 426 | case RET_I16S: 427 | EMIT(" PRINT_I16 ( WARNING CANNOT PRINT ARRAYS YET )\n"); 428 | break; 429 | case RET_CHAR: 430 | EMIT(" EMIT ( Print Char )\n"); 431 | break; 432 | case RET_VOID: 433 | printError(prog, node, "can't print void"); 434 | break; 435 | case RET_I: 436 | printError(prog, node, "can't print unspecified type"); 437 | break; 438 | } 439 | break; 440 | } 441 | case INSTR_RETURN: 442 | EMIT(" JMP2r\n"); 443 | break; 444 | case INSTR_ROOT: 445 | // SKIP 446 | break; 447 | case INSTR_THEME: 448 | EMIT(" #%c%c%c%c .System/r DEO2\n", node->children[0]->tok->str[1], 449 | node->children[1]->tok->str[1], node->children[2]->tok->str[1], 450 | node->children[3]->tok->str[1]); 451 | EMIT(" #%c%c%c%c .System/g DEO2\n", node->children[0]->tok->str[2], 452 | node->children[1]->tok->str[2], node->children[2]->tok->str[2], 453 | node->children[3]->tok->str[2]); 454 | EMIT(" #%c%c%c%c .System/b DEO2\n", node->children[0]->tok->str[3], 455 | node->children[1]->tok->str[3], node->children[2]->tok->str[3], 456 | node->children[3]->tok->str[3]); 457 | break; 458 | case INSTR_TIMES: 459 | if (node->children_index > 2) { 460 | printError(prog, node, "plus has wrong number of children"); 461 | } 462 | 463 | if (node->ret == RET_I8) { 464 | EMIT(" MUL\n"); 465 | } else if (node->ret == RET_I16) { 466 | EMIT(" MUL2\n"); 467 | } 468 | break; 469 | case INSTR_COLOR_BACKGROUND: 470 | break; 471 | case INSTR_COLOR0: 472 | break; 473 | case INSTR_COLOR1: 474 | break; 475 | case INSTR_COLOR2: 476 | break; 477 | case INSTR_SPRITE: 478 | EMIT("@sprite_%s", node->tok->str); 479 | break; 480 | case INSTR_SPRITE_ROW: { 481 | EMIT(" "); 482 | int x; 483 | x = 0; 484 | for (int i = 0; i < 4; i++) { 485 | if (node->tok->str[3 - i] == '1') { 486 | x |= (1 << i); 487 | } 488 | } 489 | EMIT("%x", x); 490 | x = 0; 491 | for (int i = 0; i < 4; i++) { 492 | if (node->tok->str[7 - i] == '1') { 493 | x |= (1 << i); 494 | } 495 | } 496 | EMIT("%x", x); 497 | break; 498 | } 499 | } 500 | } 501 | 502 | void build_helper(Program *prog, Node *node) { 503 | /* fprintf(stderr, "doing node: %s %i\n", node->tok->str->str, node->type); */ 504 | if (node->type == INSTR_LOCAL_DEFINE) { 505 | emit_self(prog, node); 506 | return; 507 | } 508 | 509 | if (node->type == INSTR_CONDITION) { 510 | if (node->children_index > 2) { 511 | if (node->children[0]->ret != RET_I8) { 512 | printError(prog, node->children[0], "conditions must return i8 to prevent eventual stack overflow"); 513 | } 514 | 515 | build_helper(prog, node->children[0]); // CONDITION 516 | 517 | emit_self(prog, node); // CONDITIONAL JUMPS 518 | 519 | EMIT(" @default-%i ( true case )\n", node->id); 520 | build_helper(prog, node->children[1]); // TRUE PATH 521 | 522 | EMIT(" ;after-%i JMP2 ( true case done )\n", node->id); 523 | EMIT(" @else-%i ( else )\n", node->id); 524 | build_helper(prog, node->children[2]); // ELSE PATH 525 | EMIT(" @after-%i ( done )\n", node->id); 526 | } else if (node->children_index == 2) { 527 | if (node->children[0]->ret != RET_I8) { 528 | printError(prog, node->children[0], "conditions must return i8 to prevent eventual stack overflow"); 529 | } 530 | 531 | build_helper(prog, node->children[0]); // CONDITION 532 | 533 | emit_self(prog, node); // CONDITIONAL JUMPS 534 | 535 | EMIT(" @default-%i ( true case )\n", node->id); 536 | 537 | build_helper(prog, node->children[1]); // TRUE PATH 538 | 539 | EMIT(" @after-%i ( else )\n", node->id); 540 | 541 | } else { 542 | printError(prog, node, 543 | "conditional had a hard to understand number of conditions"); 544 | } 545 | return; 546 | } 547 | 548 | if (node->type == INSTR_GLOBAL_DEFINE) { 549 | emit_self(prog, node); 550 | return; 551 | } 552 | 553 | if (node->type == INSTR_SPRITE) { 554 | emit_self(prog, node); 555 | for (int i = 0; i < node->children_index; i++) { 556 | build_helper(prog, node->children[i]); 557 | } 558 | EMIT("\n"); 559 | return; 560 | } 561 | 562 | if (node->type == INSTR_WHILE) { 563 | EMIT(" @while-cond-%i ( while condition )\n", node->id); 564 | build_helper(prog, node->children[0]); // CONDITION 565 | 566 | emit_self(prog, node); // CHECK... 567 | 568 | EMIT(" @while-block-%i ( while block )\n", node->id); 569 | 570 | build_helper(prog, node->children[1]); // work 571 | EMIT(" ;while-cond-%i JMP2 ( Skip to condition case )\n", node->id); 572 | EMIT(" @while-end-%i ( end while )\n", node->id); 573 | return; 574 | } 575 | 576 | if (node->type == INSTR_FUNCTION) { 577 | emit_self(prog, node); 578 | 579 | // arguments need to pull off the stack in reverse order 580 | for (int i = node->children[0]->children_index - 1; i >= 0; i--) { 581 | build_helper(prog, node->children[0]->children[i]); 582 | } 583 | 584 | int returned = 0; 585 | for (int i = 1; i < node->children_index; i++) { 586 | if (node->children[i]->type == INSTR_RETURN) { 587 | returned = 1; 588 | } 589 | build_helper(prog, node->children[i]); 590 | } 591 | 592 | if (returned == 0) { 593 | EMIT("BRK ( end of function )\n"); // TODO: skip BRK if we always return 594 | } 595 | return; 596 | } 597 | 598 | for (int i = 0; i < node->children_index; i++) { 599 | build_helper(prog, node->children[i]); 600 | } 601 | 602 | emit_self(prog, node); 603 | } 604 | 605 | void build_instruction_list(Program *prog, Node *node) { 606 | // prelude 607 | EMIT("%%BREAKPOINT { #0101 #0e DEO2 }\n"); 608 | EMIT("%%EMIT { #18 DEO }\n"); 609 | EMIT("%%EMIT2 { #18 DEO2 }\n"); 610 | EMIT("%%PRINT_I8 {\n"); 611 | EMIT(" DUP #64 DIV DUP #30 ADD EMIT #64 MUL SUB\n"); 612 | EMIT(" DUP #0a DIV DUP #30 ADD EMIT #0a MUL SUB\n"); 613 | EMIT(" #30 ADD EMIT\n"); 614 | EMIT("}\n"); 615 | 616 | EMIT("%%PRINT_I16 {\n"); 617 | EMIT(" DUP2 #03e8 DIV2 DUP2 #0030 ADD2 EMIT2 #03e8 MUL2 SUB2\n"); 618 | EMIT(" DUP2 #0064 DIV2 DUP2 #0030 ADD2 EMIT2 #0064 MUL2 SUB2\n"); 619 | EMIT(" DUP2 #000a DIV2 DUP2 #0030 ADD2 EMIT2 #000a MUL2 SUB2\n"); 620 | EMIT(" LIT2 0030 ADD2 EMIT2\n"); 621 | EMIT("}\n"); 622 | EMIT("%%MOD { DUP2 DIV MUL SUB }\n"); 623 | EMIT("%%MOD2 { DIV2k MUL2 SUB2 }\n"); 624 | EMIT("\n"); 625 | if (prog->used_system) 626 | EMIT("|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]\n"); 627 | if (prog->used_console) 628 | EMIT("|10 @Console [ &vector $2 &pad $6 &write $1 ]\n"); 629 | if (prog->used_screen) 630 | EMIT("|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1 ]\n"); 631 | if (prog->used_audio) { 632 | EMIT("|30 @Audio0 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ]\n"); 633 | EMIT("|40 @Audio1 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ]\n"); 634 | EMIT("|50 @Audio2 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ]\n"); 635 | EMIT("|60 @Audio3 [ &vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1 ]\n"); 636 | // All 4 chanels 637 | } 638 | 639 | 640 | if (prog->used_mouse) 641 | EMIT("|90 @Mouse [ &vector $2 &x $2 &y $2 &state $1 &wheel $1 ]\n"); 642 | /* EMIT("|a0 @File [ &vector $2 &success $2 &offset-hs $2 &offset-ls $2 &name $2 &length $2 &load $2 &save $2 ]\n"); */ 643 | if (prog->used_datetime) 644 | EMIT("|b0 @DateTime [ &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1 ]\n"); 645 | 646 | int mainFound = 0; 647 | for (int i = 0; i < prog->function_lut.index; i++) { 648 | if (strcmp(prog->function_lut.names[i], "main") == 0 && 649 | prog->function_lut.arg_count[i] == 0) { 650 | mainFound = 1; 651 | EMIT("|0100 ;%s-%i JSR2\nBRK\n", prog->function_lut.names[i], i); 652 | } 653 | } 654 | 655 | if (mainFound == 0) { 656 | printError(prog, prog->root, "No main method found"); 657 | exit(1); 658 | } 659 | 660 | build_helper(prog, prog->root); 661 | 662 | if (prog->strings.index > 0 && prog->used_print) { 663 | EMIT("( Strings )\n"); 664 | EMIT("@print-str\n"); 665 | EMIT(" &loop\n"); 666 | EMIT(" LDAk #18 DEO\n"); 667 | EMIT(" INC2 LDAk ,&loop JCN\n"); 668 | EMIT(" POP2\n"); 669 | EMIT("JMP2r\n"); 670 | for (int i = 0; i < prog->strings.index; i++) { 671 | EMIT("@str-%i \"", i); 672 | int strIndex = 0; 673 | while (prog->strings.nodes[i]->tok->str[strIndex] != '\0') { 674 | if (prog->strings.nodes[i]->tok->str[strIndex] == ' ') { 675 | EMIT(" 20 \""); 676 | } else { 677 | EMIT("%c", prog->strings.nodes[i]->tok->str[strIndex]); 678 | } 679 | strIndex++; 680 | } 681 | EMIT(" $1\n"); 682 | } 683 | } 684 | 685 | if (prog->global_lut.max > 0) { 686 | EMIT("( global heap )\n"); 687 | EMIT("@global-heap $%i\n", prog->global_lut.max); 688 | } 689 | 690 | if (prog->local_lut.all_time_max > 0) { 691 | EMIT("( local heap )\n"); 692 | EMIT("@local-heap $%i\n", prog->local_lut.all_time_max); 693 | } 694 | 695 | if (prog->used_sine) { 696 | EMIT("@sine\n"); 697 | EMIT(" 8083 8689 8c8f 9295 989b 9ea1 a4a7 aaad\n"); 698 | EMIT(" b0b3 b6b9 bbbe c1c3 c6c9 cbce d0d2 d5d7\n"); 699 | EMIT(" d9db dee0 e2e4 e6e7 e9eb ecee f0f1 f2f4\n"); 700 | EMIT(" f5f6 f7f8 f9fa fbfb fcfd fdfe fefe fefe\n"); 701 | EMIT(" fffe fefe fefe fdfd fcfb fbfa f9f8 f7f6\n"); 702 | EMIT(" f5f4 f2f1 f0ee eceb e9e7 e6e4 e2e0 dedb\n"); 703 | EMIT(" d9d7 d5d2 d0ce cbc9 c6c3 c1be bbb9 b6b3\n"); 704 | EMIT(" b0ad aaa7 a4a1 9e9b 9895 928f 8c89 8683\n"); 705 | EMIT(" 807d 7a77 7471 6e6b 6865 625f 5c59 5653\n"); 706 | EMIT(" 504d 4a47 4542 3f3d 3a37 3532 302e 2b29\n"); 707 | EMIT(" 2725 2220 1e1c 1a19 1715 1412 100f 0e0c\n"); 708 | EMIT(" 0b0a 0908 0706 0505 0403 0302 0202 0202\n"); 709 | EMIT(" 0102 0202 0202 0303 0405 0506 0708 090a\n"); 710 | EMIT(" 0b0c 0e0f 1012 1415 1719 1a1c 1e20 2225\n"); 711 | EMIT(" 2729 2b2e 3032 3537 3a3d 3f42 4547 4a4d\n"); 712 | EMIT(" 5053 5659 5c5f 6265 686b 6e71 7477 7a7d\n"); 713 | } 714 | } 715 | -------------------------------------------------------------------------------- /emit.h: -------------------------------------------------------------------------------- 1 | #include "lang.h" 2 | 3 | extern void build_instruction_list(Program *prog, Node *node); 4 | -------------------------------------------------------------------------------- /examples/array.dt: -------------------------------------------------------------------------------- 1 | $g_array :: 100 i16s 2 | 3 | fn main() do 4 | i :: i16 5 | i = 0 6 | a :: 3 i16s 7 | 8 | a[i] = i 9 | put(a[i]) 10 | i = i + 1 11 | 12 | a[i] = i 13 | put(a[i]) 14 | i = i + 1 15 | 16 | a[i] = i 17 | put(a[i]) 18 | i = i + 1 19 | 20 | put("global") 21 | 22 | $g_array[50] = 123 23 | put($g_array[50]) 24 | end 25 | -------------------------------------------------------------------------------- /examples/assignables.dt: -------------------------------------------------------------------------------- 1 | fn main() do 2 | theme #202 #c1c #ece #905 3 | a :: i8 4 | b :: i16 5 | c :: 3 i8s 6 | d :: 3 i16s 7 | e :: Char 8 | f :: 2 Chars 9 | a = 1 10 | b = 2 11 | c[0] = 3 12 | d[1] = 4 13 | e = 'a 14 | put(e) 15 | 16 | f[0] = 'h 17 | f[1] = 'i 18 | put(f[0]) 19 | put(f[1]) 20 | end 21 | -------------------------------------------------------------------------------- /examples/branch_types.dt: -------------------------------------------------------------------------------- 1 | fn foo():i8 do 2 | x :: i8 3 | y :: i16 4 | x = 1 5 | y = 2 6 | 7 | if 1 == 1 do 8 | return : x 9 | end 10 | -- This should fail here since it must return an i8 and doesn't 11 | end 12 | 13 | fn main() do 14 | x :: i8 15 | x = foo() 16 | end 17 | -------------------------------------------------------------------------------- /examples/conds.dt: -------------------------------------------------------------------------------- 1 | fn main() do 2 | print("- test only if: ") 3 | if 1 == 1 do 4 | put("pass") 5 | end 6 | 7 | print("- test only if false: ") 8 | if 0 == 1 do 9 | put("fail: shouldn't end up in false single if") 10 | end 11 | put("pass") 12 | 13 | print("- test single else true path: ") 14 | if 1 == 1 do 15 | put("pass") 16 | else 17 | put("fail: fell into else case") 18 | end 19 | 20 | print("- test single else false path: ") 21 | if 0 == 1 do 22 | put("fail: fell into else case") 23 | else 24 | put("pass") 25 | end 26 | 27 | print("- test else: ") 28 | if 0 == 1 do 29 | put("fail: fell into true path") 30 | elseif 0 == 1 do 31 | put("fail: fell into elseif") 32 | else 33 | put("pass") 34 | end 35 | 36 | print("- test elseif: ") 37 | if 0 == 1 do 38 | put("fail: fell into true path") 39 | elseif 1 == 1 do 40 | put("pass") 41 | else 42 | put("fail: fell into else") 43 | end 44 | 45 | print("- test else: ") 46 | if 1 == 1 do 47 | put("pass") 48 | elseif 0 == 1 do 49 | put("fail: fell into elseif") 50 | else 51 | put("fail: fell into else") 52 | end 53 | 54 | print("- test second elseif: ") 55 | if 0 == 1 do 56 | put("pass") 57 | elseif 0 == 1 do 58 | put("fail: fell into elseif") 59 | elseif 1 == 1 do 60 | put("pass") 61 | else 62 | put("fail: fell into else") 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /examples/date_time.dt: -------------------------------------------------------------------------------- 1 | sprite: square 2 | 11111111 3 | 11111111 4 | 11111111 5 | 11111111 6 | 11111111 7 | 11111111 8 | 11111111 9 | 11111111 10 | 11 | 12 | $x :: i16 13 | $y :: i16 14 | $width :: i16 15 | $height :: i16 16 | $second :: i8 17 | $minute :: i8 18 | $hour :: i8 19 | 20 | fn main() do 21 | theme #202 #c1c #ece #905 22 | send(.Screen/addr, :sprite_square) 23 | 24 | $x = 0 25 | $y = 0 26 | 27 | $width = get(.Screen/width) 28 | $height = get(.Screen/height) 29 | 30 | $second = get(.DateTime/second) 31 | $minute = get(.DateTime/minute) 32 | $hour = get(.DateTime/hour) 33 | 34 | send(.Screen/vector, :on_frame) 35 | end 36 | 37 | fn on_frame() do 38 | cur :: i8 39 | cur = get(.DateTime/second) 40 | if $second != cur do 41 | $second = cur 42 | $minute = get(.DateTime/minute) 43 | $hour = get(.DateTime/hour) 44 | draw_seconds() 45 | draw_minutes() 46 | draw_hours() 47 | end 48 | end 49 | 50 | fn draw_seconds() do 51 | x :: i16 52 | x = 0 53 | 54 | y :: i16 55 | y = 0 56 | 57 | send(.Screen/y, y) 58 | 59 | while x < 60 do 60 | send(.Screen/x, (x % 15) * 10) 61 | 62 | if (x % 15) == 0 do 63 | y = y + 10 64 | send(.Screen/y, y) 65 | end 66 | 67 | if x < pad($second) do 68 | send(.Screen/sprite, 65) 69 | else 70 | send(.Screen/sprite, 66) 71 | end 72 | x = x + 1 73 | end 74 | return 75 | end 76 | 77 | fn draw_minutes() do 78 | x :: i16 79 | x = 0 80 | 81 | y :: i16 82 | y = 64 83 | 84 | send(.Screen/y, y) 85 | 86 | while x < 60 do 87 | send(.Screen/x, (x % 15) * 10) 88 | 89 | if (x % 15) == 0 do 90 | y = y + 10 91 | send(.Screen/y, y) 92 | end 93 | 94 | if x < pad($minute) do 95 | send(.Screen/sprite, 65) 96 | else 97 | send(.Screen/sprite, 66) 98 | end 99 | x = x + 1 100 | end 101 | return 102 | end 103 | 104 | fn draw_hours() do 105 | x :: i16 106 | x = 0 107 | 108 | y :: i16 109 | y = 120 110 | 111 | send(.Screen/y, y) 112 | 113 | while x < 24 do 114 | send(.Screen/x, (x % 12) * 10) 115 | 116 | if (x % 12) == 0 do 117 | y = y + 10 118 | send(.Screen/y, y) 119 | end 120 | 121 | if x < pad($hour) do 122 | send(.Screen/sprite, 65) 123 | else 124 | send(.Screen/sprite, 66) 125 | end 126 | x = x + 1 127 | end 128 | return 129 | end 130 | -------------------------------------------------------------------------------- /examples/fill.dt: -------------------------------------------------------------------------------- 1 | sprite: square 2 | 11111111 3 | 11111111 4 | 11111111 5 | 11111111 6 | 11111111 7 | 11111111 8 | 11111111 9 | 11111111 10 | 11 | $x :: i16 12 | $y :: i16 13 | $width :: i16 14 | $height :: i16 15 | $color :: i8 16 | 17 | fn main() do 18 | theme #202 #c1c #ece #905 19 | $x = 4 20 | $y = 4 21 | $color = 65 22 | 23 | $width = get(.Screen/width) 24 | $height = get(.Screen/height) 25 | 26 | send(.Screen/vector, :on_frame) 27 | end 28 | 29 | fn on_frame() do 30 | send(.Screen/x, $x) 31 | send(.Screen/y, $y) 32 | send(.Screen/addr, :sprite_square) 33 | send(.Screen/sprite, $color) 34 | if $x > $width && $y > $height do 35 | $x = 4 36 | $y = 4 37 | if $color == 65 do 38 | $color = 66 39 | else 40 | $color = 65 41 | end 42 | elseif $x > $width do 43 | $x = 4 44 | $y = $y + 16 45 | else 46 | $x = $x + 16 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /examples/fizzbuzz.dt: -------------------------------------------------------------------------------- 1 | -- for all the numbers between 1 and 99 2 | -- print out "Fizz" if the number is divisible by 3 3 | -- print out "Buzz" if the number is divisible by 5 4 | -- if the number is both divisible by 3 and 5 print "fizzbuzz" 5 | -- in all other cases print the number 6 | 7 | fn main() do 8 | x :: i16 9 | x = 0 10 | while x < 101 do 11 | x = x + 1 12 | if x % 3 == 0 && x % 5 == 0 do 13 | put("FizzBuzz") 14 | elseif x % 3 == 0 do 15 | put("Fizz") 16 | elseif x % 5 == 0 do 17 | put("Buzz") 18 | else 19 | put(x) 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /examples/hello.dt: -------------------------------------------------------------------------------- 1 | fn main() do 2 | put("hello world") 3 | end -------------------------------------------------------------------------------- /examples/helloworld.dt: -------------------------------------------------------------------------------- 1 | fn hi(a: i16, b: i16, c: i16) do 2 | put(a) 3 | put(b) 4 | put(c) 5 | return 6 | end 7 | 8 | fn frame() do 9 | x :: i16 10 | x = 1 11 | if x == 1 do 12 | put("WHATUP") 13 | end 14 | end 15 | 16 | fn main() do 17 | theme #202 #c1c #ece #905 18 | hi(1,2,3) 19 | 20 | x :: i16 21 | x = 1 22 | if x == 1 do 23 | put("WHATUP") 24 | end 25 | 26 | put("hello world") 27 | send(.Screen/vector, :frame) 28 | end 29 | -------------------------------------------------------------------------------- /examples/life.dt: -------------------------------------------------------------------------------- 1 | -- Game Of Life 2 | -- Any live cell with fewer than two live neighbours dies, as if by underpopulation. 3 | -- Any live cell with two or three live neighbours lives on to the next generation. 4 | -- Any live cell with more than three live neighbours dies, as if by overpopulation. 5 | -- Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. 6 | 7 | sprite: square 8 | 11111111 9 | 11111111 10 | 11111111 11 | 11111111 12 | 11111111 13 | 11111111 14 | 11111111 15 | 11111111 16 | 17 | -- TODO: assign whole array 18 | $board :: 100 i16s 19 | $new_board :: 100 i16s 20 | 21 | $timer :: i16 22 | 23 | fn draw_square(dix: i16) do 24 | send(.Screen/addr, :sprite_square) 25 | send(.Screen/x, dix%10 * 10) 26 | send(.Screen/y, dix/10 * 10) 27 | 28 | send(.Screen/sprite, 67) 29 | return 30 | end 31 | 32 | fn erase_square(eix: i16) do 33 | send(.Screen/addr, :sprite_square) 34 | send(.Screen/x, eix%10 * 10) 35 | send(.Screen/y, eix/10 * 10) 36 | 37 | send(.Screen/sprite, 64) 38 | return 39 | end 40 | 41 | fn apply_rules() do 42 | i :: i16 43 | neighbours :: i16 44 | 45 | i = 0 46 | while i < 100 do 47 | -- neighbours = 0 48 | -- if i > 0 && $board[i - 1] == 1 do 49 | -- neighbours = neighbours + 1 50 | -- end 51 | -- if i < 100 && $board[i + 1] == 1 do 52 | -- neighbours = neighbours + 1 53 | -- end 54 | -- if i - 10 > 0 && $board[i - 10] == 1 do 55 | -- neighbours = neighbours + 1 56 | -- end 57 | -- if i - 11 > 0 && $board[i - 11] == 1 do 58 | -- neighbours = neighbours + 1 59 | -- end 60 | -- if i - 9 > 0 && $board[i - 9] == 1 do 61 | -- neighbours = neighbours + 1 62 | -- end 63 | -- if i + 10 < 100 && $board[i + 10] == 1 do 64 | -- neighbours = neighbours + 1 65 | -- end 66 | -- if i + 11 < 100 && $board[i + 11] == 1 do 67 | -- neighbours = neighbours + 1 68 | -- end 69 | -- if i + 9 < 100 && $board[i + 9] == 1 do 70 | -- neighbours = neighbours + 1 71 | -- end 72 | -- if neighbours < 2 do 73 | -- -- death due to underpopulation 74 | -- $new_board[i] = 0 75 | -- elseif $board[i] == 1 && (neighbours == 2 || neighbours == 3) do 76 | -- -- continued life due to normal population 77 | -- $new_board[i] = 1 78 | -- elseif $board[i] == 0 && neighbours == 3 do 79 | -- -- new life due to perfect popuation 80 | -- $new_board[i] = 1 81 | -- else 82 | -- $new_board[i] = 0 83 | -- end 84 | i = i + 1 85 | end 86 | 87 | -- i = 0 88 | -- -- TODO: whole array copy 89 | -- while i < 100 do 90 | -- $board[i] = $new_board[i] 91 | -- $new_board[i] = 0 92 | -- i = i + 1 93 | -- end 94 | return 95 | end 96 | 97 | -- fn cell(x: i16, y: i16) do 98 | -- index :: i16 99 | -- index = (y%10)*10 + x%10 100 | -- return : $board[index] 101 | -- end 102 | 103 | fn on_frame() do 104 | if $timer == 0 do 105 | apply_rules() 106 | -- $timer = 30 107 | end 108 | -- $timer = $timer - 1 109 | -- s :: i16 110 | -- s = 0 111 | -- while s < 100 do 112 | -- if $board[s] == 1 do 113 | -- draw_square(s) 114 | -- else 115 | -- erase_square(s) 116 | -- end 117 | -- s = s + 1 118 | -- end 119 | end 120 | 121 | fn main() do 122 | theme #202 #c1c #ece #905 123 | 124 | -- glider 125 | $board[13] = 1 126 | $board[24] = 1 127 | $board[32] = 1 128 | $board[33] = 1 129 | $board[34] = 1 130 | 131 | $timer = 30 132 | send(.Screen/vector, :on_frame) 133 | end 134 | -------------------------------------------------------------------------------- /examples/match.dt: -------------------------------------------------------------------------------- 1 | -- Cool patern matching concepts 2 | -- - best match / all matchs / first match 3 | -- - really fast string matchers by making a graph like how lex works 4 | 5 | fn main() do 6 | x :: i8 7 | x = 1 8 | 9 | when x do 10 | is 1 do 11 | put "hi" 12 | end 13 | is > 1 do 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /examples/math.dt: -------------------------------------------------------------------------------- 1 | fn main() do 2 | x :: i16 3 | x = 1234 4 | print("x = ") 5 | put(x + 10) 6 | end 7 | -------------------------------------------------------------------------------- /examples/mouse.dt: -------------------------------------------------------------------------------- 1 | sprite: cursor 2 | 10000000 3 | 11000000 4 | 11100000 5 | 11110000 6 | 11111000 7 | 11111100 8 | 00000000 9 | 00000000 10 | 11 | $x :: i16 12 | $y :: i16 13 | $lastx :: i16 14 | $lasty :: i16 15 | $curx :: i16 16 | $cury :: i16 17 | 18 | fn main() do 19 | theme #202 #c1c #ece #905 20 | send(.Mouse/vector, :mouse) 21 | end 22 | 23 | fn point(x: i16, y: i16) do 24 | send(.Screen/x, x) 25 | send(.Screen/y, y) 26 | send(.Screen/pixel, 2) 27 | 28 | send(.Screen/x, x + 10) 29 | send(.Screen/y, y + 10) 30 | send(.Screen/pixel, 1) 31 | 32 | return 33 | end 34 | 35 | fn cursor_erase(x: i16, y: i16) do 36 | send(.Screen/x, x) 37 | send(.Screen/y, y) 38 | send(.Screen/addr, :sprite_cursor) 39 | send(.Screen/sprite, 64) 40 | return 41 | end 42 | 43 | fn cursor_draw(x: i16, y: i16) do 44 | send(.Screen/addr, :sprite_cursor) 45 | send(.Screen/x, x) 46 | send(.Screen/y, y) 47 | 48 | send(.Screen/sprite, 67) 49 | return 50 | end 51 | 52 | fn mouse() do 53 | $x = get(.Mouse/x) 54 | $y = get(.Mouse/y) 55 | cursor_erase($lastx, $lasty) 56 | cursor_draw($x, $y) 57 | 58 | if get(.Mouse/state) == 1 do 59 | $curx = $lastx 60 | $cury = $lasty 61 | while $curx != $x || $cury != $y do 62 | point($curx, $cury) 63 | 64 | if $curx != $x do 65 | if $curx < $x do 66 | $curx = $curx + 1 67 | else 68 | $curx = $curx - 1 69 | end 70 | end 71 | 72 | if $cury != $y do 73 | if $cury < $y do 74 | $cury = $cury + 1 75 | else 76 | $cury = $cury - 1 77 | end 78 | end 79 | end 80 | end 81 | $lastx = $x 82 | $lasty = $y 83 | end 84 | -------------------------------------------------------------------------------- /examples/piano.dt: -------------------------------------------------------------------------------- 1 | sprite: cursor 2 | 10000000 3 | 11000000 4 | 11100000 5 | 11110000 6 | 11111000 7 | 11111100 8 | 00000000 9 | 00000000 10 | 11 | sprite: key_corner 12 | 10000000 13 | 10000000 14 | 10000000 15 | 10000000 16 | 10000000 17 | 10000000 18 | 11000000 19 | 01111111 20 | 21 | $pitch :: i8 22 | 23 | fn mouse() do 24 | if get(.Mouse/state) == 1 do 25 | $pitch = $pitch + 1 26 | send(.Audio0/pitch, $pitch) 27 | elseif get(.Mouse/state) == 16 do 28 | $pitch = $pitch - 1 29 | send(.Audio0/pitch, $pitch) 30 | end 31 | end 32 | 33 | fn draw_key() do 34 | 35 | end 36 | 37 | fn draw_keys() do 38 | send(.Screen/y, 10) 39 | 40 | send(.Screen/x, 10) 41 | send(.Screen/addr, :sprite_key_corner) 42 | send(.Screen/sprite, 33) 43 | 44 | send(.Screen/x, 18) 45 | send(.Screen/addr, :sprite_key_corner) 46 | send(.Screen/sprite, 49) 47 | 48 | send(.Screen/y, 18) 49 | 50 | send(.Screen/x, 10) 51 | send(.Screen/addr, :sprite_key_corner) 52 | send(.Screen/sprite, 1) 53 | 54 | send(.Screen/x, 18) 55 | send(.Screen/addr, :sprite_key_corner) 56 | send(.Screen/sprite, 17) 57 | 58 | 59 | return 60 | end 61 | 62 | fn main() do 63 | theme #202 #c1c #ece #905 64 | $pitch = 60 65 | send(.Mouse/vector, :mouse) 66 | 67 | send(.Audio0/addr, :sine) 68 | send(.Audio0/length, 20) 69 | send(.Audio0/adsr, 65535) 70 | send(.Audio0/volume, 255) 71 | send(.Audio0/pitch, $pitch) 72 | 73 | draw_keys() 74 | end -------------------------------------------------------------------------------- /examples/print_digit.dt: -------------------------------------------------------------------------------- 1 | fn main() do 2 | x :: i8 3 | x = 86 4 | 5 | print_i8(x) 6 | end 7 | 8 | fn print_i8(x: i8) do 9 | found :: i8 10 | digit :: i8 11 | 12 | digit = x / 100 13 | if digit > 0 || found do 14 | found = 1 15 | send(.Console/write, digit + 48) 16 | end 17 | x = x - digit * 100 18 | 19 | digit = x / 10 20 | if digit > 0 || found do 21 | found = 1 22 | send(.Console/write, digit + 48) 23 | end 24 | x = x - digit * 10 25 | 26 | send(.Console/write, x + 48) 27 | end 28 | -------------------------------------------------------------------------------- /examples/pulser.dt: -------------------------------------------------------------------------------- 1 | 2 | sprite: square 3 | 11111111 4 | 11111111 5 | 11111111 6 | 11111111 7 | 11111111 8 | 11111111 9 | 11111111 10 | 11111111 11 | 12 | $board :: 100 i16s 13 | -- TODO: assign whole array 14 | 15 | $toggle :: i16 16 | $timer :: i16 17 | 18 | fn draw_square(i: i16) do 19 | send(.Screen/addr, :sprite_square) 20 | send(.Screen/x, i%10 * 10) 21 | send(.Screen/y, i/10 * 10) 22 | 23 | send(.Screen/sprite, 67) 24 | return 25 | end 26 | 27 | fn erase_square(i: i16) do 28 | send(.Screen/addr, :sprite_square) 29 | send(.Screen/x, i%10 * 10) 30 | send(.Screen/y, i/10 * 10) 31 | 32 | send(.Screen/sprite, 64) 33 | return 34 | end 35 | 36 | fn main() do 37 | theme #202 #c1c #ece #905 38 | 39 | $toggle = 0 40 | $timer = 0 41 | send(.Screen/vector, :on_frame) 42 | end 43 | 44 | fn on_frame() do 45 | i :: i16 46 | i = 0 47 | 48 | if $timer == 0 do 49 | while i < 100 do 50 | if i%2 == 1 do 51 | $board[i] = $toggle 52 | else 53 | if $toggle == 0 do 54 | $board[i] = 1 55 | else 56 | $board[i] = 0 57 | end 58 | end 59 | 60 | i = i + 1 61 | end 62 | 63 | if $toggle == 1 do 64 | $toggle = 0 65 | else 66 | $toggle = 1 67 | end 68 | $timer = 60 69 | end 70 | 71 | $timer = $timer - 1 72 | 73 | i = 0 74 | while i < 100 do 75 | -- TODO: this causes a cryptic error if you write 76 | -- if $board[i] do 77 | -- Since $board[i] returns an i16, if doesn't clean up its stack. 78 | if $board[i] == 1 do 79 | draw_square(i) 80 | else 81 | erase_square(i) 82 | end 83 | i = i + 1 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /examples/recursion.dt: -------------------------------------------------------------------------------- 1 | fn recurse(i: i8) do 2 | if i > 0 do 3 | put(i) 4 | recurse(i - 1) 5 | end 6 | end 7 | 8 | fn main() do 9 | recurse(10) 10 | end 11 | -------------------------------------------------------------------------------- /examples/sound.dt: -------------------------------------------------------------------------------- 1 | $pitch :: i8 2 | 3 | fn mouse() do 4 | if get(.Mouse/state) == 1 do 5 | $pitch = $pitch + 1 6 | send(.Audio0/pitch, $pitch) 7 | elseif get(.Mouse/state) == 16 do 8 | $pitch = $pitch - 1 9 | send(.Audio0/pitch, $pitch) 10 | end 11 | end 12 | 13 | fn main() do 14 | $pitch = 60 15 | send(.Mouse/vector, :mouse) 16 | 17 | send(.Audio0/addr, :sine) 18 | send(.Audio0/length, 20) 19 | send(.Audio0/adsr, 65535) 20 | send(.Audio0/volume, 255) 21 | send(.Audio0/pitch, $pitch) 22 | end -------------------------------------------------------------------------------- /examples/sprite.dt: -------------------------------------------------------------------------------- 1 | sprite: square 2 | 11111111 3 | 10000001 4 | 10000001 5 | 10000001 6 | 10000001 7 | 10000001 8 | 10000001 9 | 11111111 10 | 11 | fn main() do 12 | theme #202 #c1c #ece #905 13 | 14 | send(.Screen/x, 100) 15 | send(.Screen/y, 100) 16 | 17 | send(.Screen/addr, :sprite_square) 18 | send(.Screen/sprite, 67) 19 | end 20 | -------------------------------------------------------------------------------- /examples/test.dt: -------------------------------------------------------------------------------- 1 | fn arg_stress(a: i8, b: i8, c: i8, d: i8, e: i8, f: i8) do 2 | put(f) 3 | put(e) 4 | put(d) 5 | put(c) 6 | put(b) 7 | put(a) 8 | -- again backwards 9 | put(a) 10 | put(b) 11 | put(c) 12 | put(d) 13 | put(e) 14 | put(f) 15 | return 16 | end 17 | 18 | fn arg_relax(a: i8, b: i8, c: i8) do 19 | put(c) 20 | put(b) 21 | put(a) 22 | -- again backwards 23 | put(a) 24 | put(b) 25 | put(c) 26 | return 27 | end 28 | 29 | fn arg_smellaxe(a: i8, b: i8, c: i8) do 30 | put(c) 31 | put(b) 32 | put(a) 33 | -- again backwards 34 | put(a) 35 | put(b) 36 | put(c) 37 | return 38 | end 39 | 40 | 41 | fn main() do 42 | theme #202 #c1c #ece #905 43 | arg_relax(1,2,3) 44 | arg_smellaxe(1,2,3) 45 | arg_stress(1,2,3,4,5,6) 46 | end 47 | -------------------------------------------------------------------------------- /examples/type_determined.dt: -------------------------------------------------------------------------------- 1 | fn hi(i: i8) do 2 | put("i8 hi") 3 | return 4 | end 5 | 6 | fn hi(i: i16) do 7 | put("i16 hi") 8 | return 9 | end 10 | 11 | fn bye(): i8 do 12 | return : 123 13 | end 14 | 15 | fn bye(): i16 do 16 | return : 1 17 | end 18 | 19 | 20 | fn main() do 21 | x :: i8 22 | y :: i16 23 | x = 1 24 | y = 2 25 | 26 | hi(x) 27 | hi(y) 28 | 29 | x = bye() 30 | put(x) 31 | y = bye() 32 | put(y) 33 | 34 | -- TODO: test types that can't be determined 35 | -- put(1) 36 | end 37 | -------------------------------------------------------------------------------- /examples/vars.dt: -------------------------------------------------------------------------------- 1 | $x :: i16 2 | $y :: i8 3 | $z :: i16 4 | 5 | fn main() do 6 | x :: i8 7 | y :: i16 8 | z :: i8 9 | 10 | x = 1 11 | y = 2 12 | z = 3 13 | 14 | put(x) 15 | put(y) 16 | put(z) 17 | 18 | $x = 4 19 | $y = 5 20 | $z = 6 21 | 22 | put($x) 23 | put($y) 24 | put($z) 25 | end 26 | -------------------------------------------------------------------------------- /lang.h: -------------------------------------------------------------------------------- 1 | #ifndef LANG 2 | #define LANG 3 | 4 | #include 5 | #include "ast.h" 6 | #include "lut.h" 7 | 8 | #define MAX_STACK_SLOTS 2 9 | 10 | typedef struct ExtraData { 11 | char *filename; 12 | int line; 13 | int col; 14 | int size; 15 | } ExtraData; 16 | 17 | typedef struct Instruction { 18 | InstructionType type; 19 | Token *tok; 20 | } Instruction; 21 | 22 | typedef struct StringNode { 23 | int capacity; 24 | int index; 25 | Node **nodes; 26 | } StringNode; 27 | 28 | typedef struct Program { 29 | int id; 30 | int errored; 31 | Node *root; 32 | 33 | Lut global_lut; 34 | Lut local_lut; 35 | Lut function_lut; 36 | 37 | StringNode strings; 38 | Token *tok; 39 | int used_system; 40 | int used_console; 41 | int used_screen; 42 | int used_mouse; 43 | int used_datetime; 44 | int used_print; 45 | int used_audio; 46 | int used_sine; 47 | 48 | void *scanner; 49 | void *parser; 50 | ExtraData data; 51 | 52 | FILE * output; 53 | } Program; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /lempar.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** 2000-05-29 3 | ** 4 | ** The author disclaims copyright to this source code. In place of 5 | ** a legal notice, here is a blessing: 6 | ** 7 | ** May you do good and not evil. 8 | ** May you find forgiveness for yourself and forgive others. 9 | ** May you share freely, never taking more than you give. 10 | ** 11 | ************************************************************************* 12 | ** Driver template for the LEMON parser generator. 13 | ** 14 | ** The "lemon" program processes an LALR(1) input grammar file, then uses 15 | ** this template to construct a parser. The "lemon" program inserts text 16 | ** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the 17 | ** interstitial "-" characters) contained in this template is changed into 18 | ** the value of the %name directive from the grammar. Otherwise, the content 19 | ** of this template is copied straight through into the generate parser 20 | ** source file. 21 | ** 22 | ** The following is the concatenation of all %include directives from the 23 | ** input grammar file: 24 | */ 25 | /************ Begin %include sections from the grammar ************************/ 26 | %% 27 | /**************** End of %include directives **********************************/ 28 | /* These constants specify the various numeric values for terminal symbols. 29 | ***************** Begin token definitions *************************************/ 30 | %% 31 | /**************** End token definitions ***************************************/ 32 | 33 | /* The next sections is a series of control #defines. 34 | ** various aspects of the generated parser. 35 | ** YYCODETYPE is the data type used to store the integer codes 36 | ** that represent terminal and non-terminal symbols. 37 | ** "unsigned char" is used if there are fewer than 38 | ** 256 symbols. Larger types otherwise. 39 | ** YYNOCODE is a number of type YYCODETYPE that is not used for 40 | ** any terminal or nonterminal symbol. 41 | ** YYFALLBACK If defined, this indicates that one or more tokens 42 | ** (also known as: "terminal symbols") have fall-back 43 | ** values which should be used if the original symbol 44 | ** would not parse. This permits keywords to sometimes 45 | ** be used as identifiers, for example. 46 | ** YYACTIONTYPE is the data type used for "action codes" - numbers 47 | ** that indicate what to do in response to the next 48 | ** token. 49 | ** ParseTOKENTYPE is the data type used for minor type for terminal 50 | ** symbols. Background: A "minor type" is a semantic 51 | ** value associated with a terminal or non-terminal 52 | ** symbols. For example, for an "ID" terminal symbol, 53 | ** the minor type might be the name of the identifier. 54 | ** Each non-terminal can have a different minor type. 55 | ** Terminal symbols all have the same minor type, though. 56 | ** This macros defines the minor type for terminal 57 | ** symbols. 58 | ** YYMINORTYPE is the data type used for all minor types. 59 | ** This is typically a union of many types, one of 60 | ** which is ParseTOKENTYPE. The entry in the union 61 | ** for terminal symbols is called "yy0". 62 | ** YYSTACKDEPTH is the maximum depth of the parser's stack. If 63 | ** zero the stack is dynamically sized using realloc() 64 | ** ParseARG_SDECL A static variable declaration for the %extra_argument 65 | ** ParseARG_PDECL A parameter declaration for the %extra_argument 66 | ** ParseARG_PARAM Code to pass %extra_argument as a subroutine parameter 67 | ** ParseARG_STORE Code to store %extra_argument into yypParser 68 | ** ParseARG_FETCH Code to extract %extra_argument from yypParser 69 | ** ParseCTX_* As ParseARG_ except for %extra_context 70 | ** YYERRORSYMBOL is the code number of the error symbol. If not 71 | ** defined, then do no error processing. 72 | ** YYNSTATE the combined number of states. 73 | ** YYNRULE the number of rules in the grammar 74 | ** YYNTOKEN Number of terminal symbols 75 | ** YY_MAX_SHIFT Maximum value for shift actions 76 | ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions 77 | ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions 78 | ** YY_ERROR_ACTION The yy_action[] code for syntax error 79 | ** YY_ACCEPT_ACTION The yy_action[] code for accept 80 | ** YY_NO_ACTION The yy_action[] code for no-op 81 | ** YY_MIN_REDUCE Minimum value for reduce actions 82 | ** YY_MAX_REDUCE Maximum value for reduce actions 83 | */ 84 | #ifndef INTERFACE 85 | # define INTERFACE 1 86 | #endif 87 | /************* Begin control #defines *****************************************/ 88 | %% 89 | /************* End control #defines *******************************************/ 90 | #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) 91 | 92 | /* Define the yytestcase() macro to be a no-op if is not already defined 93 | ** otherwise. 94 | ** 95 | ** Applications can choose to define yytestcase() in the %include section 96 | ** to a macro that can assist in verifying code coverage. For production 97 | ** code the yytestcase() macro should be turned off. But it is useful 98 | ** for testing. 99 | */ 100 | #ifndef yytestcase 101 | # define yytestcase(X) 102 | #endif 103 | 104 | 105 | /* Next are the tables used to determine what action to take based on the 106 | ** current state and lookahead token. These tables are used to implement 107 | ** functions that take a state number and lookahead value and return an 108 | ** action integer. 109 | ** 110 | ** Suppose the action integer is N. Then the action is determined as 111 | ** follows 112 | ** 113 | ** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead 114 | ** token onto the stack and goto state N. 115 | ** 116 | ** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then 117 | ** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE. 118 | ** 119 | ** N == YY_ERROR_ACTION A syntax error has occurred. 120 | ** 121 | ** N == YY_ACCEPT_ACTION The parser accepts its input. 122 | ** 123 | ** N == YY_NO_ACTION No such action. Denotes unused 124 | ** slots in the yy_action[] table. 125 | ** 126 | ** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE 127 | ** and YY_MAX_REDUCE 128 | ** 129 | ** The action table is constructed as a single large table named yy_action[]. 130 | ** Given state S and lookahead X, the action is computed as either: 131 | ** 132 | ** (A) N = yy_action[ yy_shift_ofst[S] + X ] 133 | ** (B) N = yy_default[S] 134 | ** 135 | ** The (A) formula is preferred. The B formula is used instead if 136 | ** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X. 137 | ** 138 | ** The formulas above are for computing the action when the lookahead is 139 | ** a terminal symbol. If the lookahead is a non-terminal (as occurs after 140 | ** a reduce action) then the yy_reduce_ofst[] array is used in place of 141 | ** the yy_shift_ofst[] array. 142 | ** 143 | ** The following are the tables generated in this section: 144 | ** 145 | ** yy_action[] A single table containing all actions. 146 | ** yy_lookahead[] A table containing the lookahead for each entry in 147 | ** yy_action. Used to detect hash collisions. 148 | ** yy_shift_ofst[] For each state, the offset into yy_action for 149 | ** shifting terminals. 150 | ** yy_reduce_ofst[] For each state, the offset into yy_action for 151 | ** shifting non-terminals after a reduce. 152 | ** yy_default[] Default action for each state. 153 | ** 154 | *********** Begin parsing tables **********************************************/ 155 | %% 156 | /********** End of lemon-generated parsing tables *****************************/ 157 | 158 | /* The next table maps tokens (terminal symbols) into fallback tokens. 159 | ** If a construct like the following: 160 | ** 161 | ** %fallback ID X Y Z. 162 | ** 163 | ** appears in the grammar, then ID becomes a fallback token for X, Y, 164 | ** and Z. Whenever one of the tokens X, Y, or Z is input to the parser 165 | ** but it does not parse, the type of the token is changed to ID and 166 | ** the parse is retried before an error is thrown. 167 | ** 168 | ** This feature can be used, for example, to cause some keywords in a language 169 | ** to revert to identifiers if they keyword does not apply in the context where 170 | ** it appears. 171 | */ 172 | #ifdef YYFALLBACK 173 | static const YYCODETYPE yyFallback[] = { 174 | %% 175 | }; 176 | #endif /* YYFALLBACK */ 177 | 178 | /* The following structure represents a single element of the 179 | ** parser's stack. Information stored includes: 180 | ** 181 | ** + The state number for the parser at this level of the stack. 182 | ** 183 | ** + The value of the token stored at this level of the stack. 184 | ** (In other words, the "major" token.) 185 | ** 186 | ** + The semantic value stored at this level of the stack. This is 187 | ** the information used by the action routines in the grammar. 188 | ** It is sometimes called the "minor" token. 189 | ** 190 | ** After the "shift" half of a SHIFTREDUCE action, the stateno field 191 | ** actually contains the reduce action for the second half of the 192 | ** SHIFTREDUCE. 193 | */ 194 | struct yyStackEntry { 195 | YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */ 196 | YYCODETYPE major; /* The major token value. This is the code 197 | ** number for the token at this stack level */ 198 | YYMINORTYPE minor; /* The user-supplied minor token value. This 199 | ** is the value of the token */ 200 | }; 201 | typedef struct yyStackEntry yyStackEntry; 202 | 203 | /* The state of the parser is completely contained in an instance of 204 | ** the following structure */ 205 | struct yyParser { 206 | yyStackEntry *yytos; /* Pointer to top element of the stack */ 207 | #ifdef YYTRACKMAXSTACKDEPTH 208 | int yyhwm; /* High-water mark of the stack */ 209 | #endif 210 | #ifndef YYNOERRORRECOVERY 211 | int yyerrcnt; /* Shifts left before out of the error */ 212 | #endif 213 | ParseARG_SDECL /* A place to hold %extra_argument */ 214 | ParseCTX_SDECL /* A place to hold %extra_context */ 215 | #if YYSTACKDEPTH<=0 216 | int yystksz; /* Current side of the stack */ 217 | yyStackEntry *yystack; /* The parser's stack */ 218 | yyStackEntry yystk0; /* First stack entry */ 219 | #else 220 | yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ 221 | yyStackEntry *yystackEnd; /* Last entry in the stack */ 222 | #endif 223 | }; 224 | typedef struct yyParser yyParser; 225 | 226 | #ifndef NDEBUG 227 | #include 228 | #include 229 | static FILE *yyTraceFILE = 0; 230 | static char *yyTracePrompt = 0; 231 | #endif /* NDEBUG */ 232 | 233 | #ifndef NDEBUG 234 | /* 235 | ** Turn parser tracing on by giving a stream to which to write the trace 236 | ** and a prompt to preface each trace message. Tracing is turned off 237 | ** by making either argument NULL 238 | ** 239 | ** Inputs: 240 | **
    241 | **
  • A FILE* to which trace output should be written. 242 | ** If NULL, then tracing is turned off. 243 | **
  • A prefix string written at the beginning of every 244 | ** line of trace output. If NULL, then tracing is 245 | ** turned off. 246 | **
247 | ** 248 | ** Outputs: 249 | ** None. 250 | */ 251 | void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ 252 | yyTraceFILE = TraceFILE; 253 | yyTracePrompt = zTracePrompt; 254 | if( yyTraceFILE==0 ) yyTracePrompt = 0; 255 | else if( yyTracePrompt==0 ) yyTraceFILE = 0; 256 | } 257 | #endif /* NDEBUG */ 258 | 259 | #if defined(YYCOVERAGE) || !defined(NDEBUG) 260 | /* For tracing shifts, the names of all terminals and nonterminals 261 | ** are required. The following table supplies these names */ 262 | static const char *const yyTokenName[] = { 263 | %% 264 | }; 265 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ 266 | 267 | #ifndef NDEBUG 268 | /* For tracing reduce actions, the names of all rules are required. 269 | */ 270 | static const char *const yyRuleName[] = { 271 | %% 272 | }; 273 | #endif /* NDEBUG */ 274 | 275 | 276 | #if YYSTACKDEPTH<=0 277 | /* 278 | ** Try to increase the size of the parser stack. Return the number 279 | ** of errors. Return 0 on success. 280 | */ 281 | static int yyGrowStack(yyParser *p){ 282 | int newSize; 283 | int idx; 284 | yyStackEntry *pNew; 285 | 286 | newSize = p->yystksz*2 + 100; 287 | idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; 288 | if( p->yystack==&p->yystk0 ){ 289 | pNew = malloc(newSize*sizeof(pNew[0])); 290 | if( pNew ) pNew[0] = p->yystk0; 291 | }else{ 292 | pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); 293 | } 294 | if( pNew ){ 295 | p->yystack = pNew; 296 | p->yytos = &p->yystack[idx]; 297 | #ifndef NDEBUG 298 | if( yyTraceFILE ){ 299 | fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", 300 | yyTracePrompt, p->yystksz, newSize); 301 | } 302 | #endif 303 | p->yystksz = newSize; 304 | } 305 | return pNew==0; 306 | } 307 | #endif 308 | 309 | /* Datatype of the argument to the memory allocated passed as the 310 | ** second argument to ParseAlloc() below. This can be changed by 311 | ** putting an appropriate #define in the %include section of the input 312 | ** grammar. 313 | */ 314 | #ifndef YYMALLOCARGTYPE 315 | # define YYMALLOCARGTYPE size_t 316 | #endif 317 | 318 | /* Initialize a new parser that has already been allocated. 319 | */ 320 | void ParseInit(void *yypRawParser ParseCTX_PDECL){ 321 | yyParser *yypParser = (yyParser*)yypRawParser; 322 | ParseCTX_STORE 323 | #ifdef YYTRACKMAXSTACKDEPTH 324 | yypParser->yyhwm = 0; 325 | #endif 326 | #if YYSTACKDEPTH<=0 327 | yypParser->yytos = NULL; 328 | yypParser->yystack = NULL; 329 | yypParser->yystksz = 0; 330 | if( yyGrowStack(yypParser) ){ 331 | yypParser->yystack = &yypParser->yystk0; 332 | yypParser->yystksz = 1; 333 | } 334 | #endif 335 | #ifndef YYNOERRORRECOVERY 336 | yypParser->yyerrcnt = -1; 337 | #endif 338 | yypParser->yytos = yypParser->yystack; 339 | yypParser->yystack[0].stateno = 0; 340 | yypParser->yystack[0].major = 0; 341 | #if YYSTACKDEPTH>0 342 | yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; 343 | #endif 344 | } 345 | 346 | #ifndef Parse_ENGINEALWAYSONSTACK 347 | /* 348 | ** This function allocates a new parser. 349 | ** The only argument is a pointer to a function which works like 350 | ** malloc. 351 | ** 352 | ** Inputs: 353 | ** A pointer to the function used to allocate memory. 354 | ** 355 | ** Outputs: 356 | ** A pointer to a parser. This pointer is used in subsequent calls 357 | ** to Parse and ParseFree. 358 | */ 359 | void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE) ParseCTX_PDECL){ 360 | yyParser *yypParser; 361 | yypParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) ); 362 | if( yypParser ){ 363 | ParseCTX_STORE 364 | ParseInit(yypParser ParseCTX_PARAM); 365 | } 366 | return (void*)yypParser; 367 | } 368 | #endif /* Parse_ENGINEALWAYSONSTACK */ 369 | 370 | 371 | /* The following function deletes the "minor type" or semantic value 372 | ** associated with a symbol. The symbol can be either a terminal 373 | ** or nonterminal. "yymajor" is the symbol code, and "yypminor" is 374 | ** a pointer to the value to be deleted. The code used to do the 375 | ** deletions is derived from the %destructor and/or %token_destructor 376 | ** directives of the input grammar. 377 | */ 378 | static void yy_destructor( 379 | yyParser *yypParser, /* The parser */ 380 | YYCODETYPE yymajor, /* Type code for object to destroy */ 381 | YYMINORTYPE *yypminor /* The object to be destroyed */ 382 | ){ 383 | ParseARG_FETCH 384 | ParseCTX_FETCH 385 | switch( yymajor ){ 386 | /* Here is inserted the actions which take place when a 387 | ** terminal or non-terminal is destroyed. This can happen 388 | ** when the symbol is popped from the stack during a 389 | ** reduce or during error processing or when a parser is 390 | ** being destroyed before it is finished parsing. 391 | ** 392 | ** Note: during a reduce, the only symbols destroyed are those 393 | ** which appear on the RHS of the rule, but which are *not* used 394 | ** inside the C code. 395 | */ 396 | /********* Begin destructor definitions ***************************************/ 397 | %% 398 | /********* End destructor definitions *****************************************/ 399 | default: break; /* If no destructor action specified: do nothing */ 400 | } 401 | } 402 | 403 | /* 404 | ** Pop the parser's stack once. 405 | ** 406 | ** If there is a destructor routine associated with the token which 407 | ** is popped from the stack, then call it. 408 | */ 409 | static void yy_pop_parser_stack(yyParser *pParser){ 410 | yyStackEntry *yytos; 411 | assert( pParser->yytos!=0 ); 412 | assert( pParser->yytos > pParser->yystack ); 413 | yytos = pParser->yytos--; 414 | #ifndef NDEBUG 415 | if( yyTraceFILE ){ 416 | fprintf(yyTraceFILE,"%sPopping %s\n", 417 | yyTracePrompt, 418 | yyTokenName[yytos->major]); 419 | } 420 | #endif 421 | yy_destructor(pParser, yytos->major, &yytos->minor); 422 | } 423 | 424 | /* 425 | ** Clear all secondary memory allocations from the parser 426 | */ 427 | void ParseFinalize(void *p){ 428 | yyParser *pParser = (yyParser*)p; 429 | while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); 430 | #if YYSTACKDEPTH<=0 431 | if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); 432 | #endif 433 | } 434 | 435 | #ifndef Parse_ENGINEALWAYSONSTACK 436 | /* 437 | ** Deallocate and destroy a parser. Destructors are called for 438 | ** all stack elements before shutting the parser down. 439 | ** 440 | ** If the YYPARSEFREENEVERNULL macro exists (for example because it 441 | ** is defined in a %include section of the input grammar) then it is 442 | ** assumed that the input pointer is never NULL. 443 | */ 444 | void ParseFree( 445 | void *p, /* The parser to be deleted */ 446 | void (*freeProc)(void*) /* Function used to reclaim memory */ 447 | ){ 448 | #ifndef YYPARSEFREENEVERNULL 449 | if( p==0 ) return; 450 | #endif 451 | ParseFinalize(p); 452 | (*freeProc)(p); 453 | } 454 | #endif /* Parse_ENGINEALWAYSONSTACK */ 455 | 456 | /* 457 | ** Return the peak depth of the stack for a parser. 458 | */ 459 | #ifdef YYTRACKMAXSTACKDEPTH 460 | int ParseStackPeak(void *p){ 461 | yyParser *pParser = (yyParser*)p; 462 | return pParser->yyhwm; 463 | } 464 | #endif 465 | 466 | /* This array of booleans keeps track of the parser statement 467 | ** coverage. The element yycoverage[X][Y] is set when the parser 468 | ** is in state X and has a lookahead token Y. In a well-tested 469 | ** systems, every element of this matrix should end up being set. 470 | */ 471 | #if defined(YYCOVERAGE) 472 | static unsigned char yycoverage[YYNSTATE][YYNTOKEN]; 473 | #endif 474 | 475 | /* 476 | ** Write into out a description of every state/lookahead combination that 477 | ** 478 | ** (1) has not been used by the parser, and 479 | ** (2) is not a syntax error. 480 | ** 481 | ** Return the number of missed state/lookahead combinations. 482 | */ 483 | #if defined(YYCOVERAGE) 484 | int ParseCoverage(FILE *out){ 485 | int stateno, iLookAhead, i; 486 | int nMissed = 0; 487 | for(stateno=0; statenoYY_MAX_SHIFT ) return stateno; 514 | assert( stateno <= YY_SHIFT_COUNT ); 515 | #if defined(YYCOVERAGE) 516 | yycoverage[stateno][iLookAhead] = 1; 517 | #endif 518 | do{ 519 | i = yy_shift_ofst[stateno]; 520 | assert( i>=0 ); 521 | assert( i<=YY_ACTTAB_COUNT ); 522 | assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); 523 | assert( iLookAhead!=YYNOCODE ); 524 | assert( iLookAhead < YYNTOKEN ); 525 | i += iLookAhead; 526 | assert( i<(int)YY_NLOOKAHEAD ); 527 | if( yy_lookahead[i]!=iLookAhead ){ 528 | #ifdef YYFALLBACK 529 | YYCODETYPE iFallback; /* Fallback token */ 530 | assert( iLookAhead %s\n", 536 | yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); 537 | } 538 | #endif 539 | assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */ 540 | iLookAhead = iFallback; 541 | continue; 542 | } 543 | #endif 544 | #ifdef YYWILDCARD 545 | { 546 | int j = i - iLookAhead + YYWILDCARD; 547 | assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) ); 548 | if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){ 549 | #ifndef NDEBUG 550 | if( yyTraceFILE ){ 551 | fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n", 552 | yyTracePrompt, yyTokenName[iLookAhead], 553 | yyTokenName[YYWILDCARD]); 554 | } 555 | #endif /* NDEBUG */ 556 | return yy_action[j]; 557 | } 558 | } 559 | #endif /* YYWILDCARD */ 560 | return yy_default[stateno]; 561 | }else{ 562 | assert( i>=0 && i<(int)(sizeof(yy_action)/sizeof(yy_action[0])) ); 563 | return yy_action[i]; 564 | } 565 | }while(1); 566 | } 567 | 568 | /* 569 | ** Find the appropriate action for a parser given the non-terminal 570 | ** look-ahead token iLookAhead. 571 | */ 572 | static YYACTIONTYPE yy_find_reduce_action( 573 | YYACTIONTYPE stateno, /* Current state number */ 574 | YYCODETYPE iLookAhead /* The look-ahead token */ 575 | ){ 576 | int i; 577 | #ifdef YYERRORSYMBOL 578 | if( stateno>YY_REDUCE_COUNT ){ 579 | return yy_default[stateno]; 580 | } 581 | #else 582 | assert( stateno<=YY_REDUCE_COUNT ); 583 | #endif 584 | i = yy_reduce_ofst[stateno]; 585 | assert( iLookAhead!=YYNOCODE ); 586 | i += iLookAhead; 587 | #ifdef YYERRORSYMBOL 588 | if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ 589 | return yy_default[stateno]; 590 | } 591 | #else 592 | assert( i>=0 && iyytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); 610 | /* Here code is inserted which will execute if the parser 611 | ** stack every overflows */ 612 | /******** Begin %stack_overflow code ******************************************/ 613 | %% 614 | /******** End %stack_overflow code ********************************************/ 615 | ParseARG_STORE /* Suppress warning about unused %extra_argument var */ 616 | ParseCTX_STORE 617 | } 618 | 619 | /* 620 | ** Print tracing information for a SHIFT action 621 | */ 622 | #ifndef NDEBUG 623 | static void yyTraceShift(yyParser *yypParser, int yyNewState, const char *zTag){ 624 | if( yyTraceFILE ){ 625 | if( yyNewStateyytos->major], 628 | yyNewState); 629 | }else{ 630 | fprintf(yyTraceFILE,"%s%s '%s', pending reduce %d\n", 631 | yyTracePrompt, zTag, yyTokenName[yypParser->yytos->major], 632 | yyNewState - YY_MIN_REDUCE); 633 | } 634 | } 635 | } 636 | #else 637 | # define yyTraceShift(X,Y,Z) 638 | #endif 639 | 640 | /* 641 | ** Perform a shift action. 642 | */ 643 | static void yy_shift( 644 | yyParser *yypParser, /* The parser to be shifted */ 645 | YYACTIONTYPE yyNewState, /* The new state to shift in */ 646 | YYCODETYPE yyMajor, /* The major token to shift in */ 647 | ParseTOKENTYPE yyMinor /* The minor token to shift in */ 648 | ){ 649 | yyStackEntry *yytos; 650 | yypParser->yytos++; 651 | #ifdef YYTRACKMAXSTACKDEPTH 652 | if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ 653 | yypParser->yyhwm++; 654 | assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); 655 | } 656 | #endif 657 | #if YYSTACKDEPTH>0 658 | if( yypParser->yytos>yypParser->yystackEnd ){ 659 | yypParser->yytos--; 660 | yyStackOverflow(yypParser); 661 | return; 662 | } 663 | #else 664 | if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ 665 | if( yyGrowStack(yypParser) ){ 666 | yypParser->yytos--; 667 | yyStackOverflow(yypParser); 668 | return; 669 | } 670 | } 671 | #endif 672 | if( yyNewState > YY_MAX_SHIFT ){ 673 | yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; 674 | } 675 | yytos = yypParser->yytos; 676 | yytos->stateno = yyNewState; 677 | yytos->major = yyMajor; 678 | yytos->minor.yy0 = yyMinor; 679 | yyTraceShift(yypParser, yyNewState, "Shift"); 680 | } 681 | 682 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side 683 | ** of that rule */ 684 | static const YYCODETYPE yyRuleInfoLhs[] = { 685 | %% 686 | }; 687 | 688 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number 689 | ** of symbols on the right-hand side of that rule. */ 690 | static const signed char yyRuleInfoNRhs[] = { 691 | %% 692 | }; 693 | 694 | static void yy_accept(yyParser*); /* Forward Declaration */ 695 | 696 | /* 697 | ** Perform a reduce action and the shift that must immediately 698 | ** follow the reduce. 699 | ** 700 | ** The yyLookahead and yyLookaheadToken parameters provide reduce actions 701 | ** access to the lookahead token (if any). The yyLookahead will be YYNOCODE 702 | ** if the lookahead token has already been consumed. As this procedure is 703 | ** only called from one place, optimizing compilers will in-line it, which 704 | ** means that the extra parameters have no performance impact. 705 | */ 706 | static YYACTIONTYPE yy_reduce( 707 | yyParser *yypParser, /* The parser */ 708 | unsigned int yyruleno, /* Number of the rule by which to reduce */ 709 | int yyLookahead, /* Lookahead token, or YYNOCODE if none */ 710 | ParseTOKENTYPE yyLookaheadToken /* Value of the lookahead token */ 711 | ParseCTX_PDECL /* %extra_context */ 712 | ){ 713 | int yygoto; /* The next state */ 714 | YYACTIONTYPE yyact; /* The next action */ 715 | yyStackEntry *yymsp; /* The top of the parser's stack */ 716 | int yysize; /* Amount to pop the stack */ 717 | ParseARG_FETCH 718 | (void)yyLookahead; 719 | (void)yyLookaheadToken; 720 | yymsp = yypParser->yytos; 721 | 722 | switch( yyruleno ){ 723 | /* Beginning here are the reduction cases. A typical example 724 | ** follows: 725 | ** case 0: 726 | ** #line 727 | ** { ... } // User supplied code 728 | ** #line 729 | ** break; 730 | */ 731 | /********** Begin reduce actions **********************************************/ 732 | %% 733 | /********** End reduce actions ************************************************/ 734 | }; 735 | assert( yyrulenoYY_MAX_SHIFT && yyact<=YY_MAX_SHIFTREDUCE) ); 743 | 744 | /* It is not possible for a REDUCE to be followed by an error */ 745 | assert( yyact!=YY_ERROR_ACTION ); 746 | 747 | yymsp += yysize+1; 748 | yypParser->yytos = yymsp; 749 | yymsp->stateno = (YYACTIONTYPE)yyact; 750 | yymsp->major = (YYCODETYPE)yygoto; 751 | yyTraceShift(yypParser, yyact, "... then shift"); 752 | return yyact; 753 | } 754 | 755 | /* 756 | ** The following code executes when the parse fails 757 | */ 758 | #ifndef YYNOERRORRECOVERY 759 | static void yy_parse_failed( 760 | yyParser *yypParser /* The parser */ 761 | ){ 762 | ParseARG_FETCH 763 | ParseCTX_FETCH 764 | #ifndef NDEBUG 765 | if( yyTraceFILE ){ 766 | fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); 767 | } 768 | #endif 769 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); 770 | /* Here code is inserted which will be executed whenever the 771 | ** parser fails */ 772 | /************ Begin %parse_failure code ***************************************/ 773 | %% 774 | /************ End %parse_failure code *****************************************/ 775 | ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ 776 | ParseCTX_STORE 777 | } 778 | #endif /* YYNOERRORRECOVERY */ 779 | 780 | /* 781 | ** The following code executes when a syntax error first occurs. 782 | */ 783 | static void yy_syntax_error( 784 | yyParser *yypParser, /* The parser */ 785 | int yymajor, /* The major type of the error token */ 786 | ParseTOKENTYPE yyminor /* The minor type of the error token */ 787 | ){ 788 | ParseARG_FETCH 789 | ParseCTX_FETCH 790 | #define TOKEN yyminor 791 | /************ Begin %syntax_error code ****************************************/ 792 | %% 793 | /************ End %syntax_error code ******************************************/ 794 | ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ 795 | ParseCTX_STORE 796 | } 797 | 798 | /* 799 | ** The following is executed when the parser accepts 800 | */ 801 | static void yy_accept( 802 | yyParser *yypParser /* The parser */ 803 | ){ 804 | ParseARG_FETCH 805 | ParseCTX_FETCH 806 | #ifndef NDEBUG 807 | if( yyTraceFILE ){ 808 | fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); 809 | } 810 | #endif 811 | #ifndef YYNOERRORRECOVERY 812 | yypParser->yyerrcnt = -1; 813 | #endif 814 | assert( yypParser->yytos==yypParser->yystack ); 815 | /* Here code is inserted which will be executed whenever the 816 | ** parser accepts */ 817 | /*********** Begin %parse_accept code *****************************************/ 818 | %% 819 | /*********** End %parse_accept code *******************************************/ 820 | ParseARG_STORE /* Suppress warning about unused %extra_argument variable */ 821 | ParseCTX_STORE 822 | } 823 | 824 | /* The main parser program. 825 | ** The first argument is a pointer to a structure obtained from 826 | ** "ParseAlloc" which describes the current state of the parser. 827 | ** The second argument is the major token number. The third is 828 | ** the minor token. The fourth optional argument is whatever the 829 | ** user wants (and specified in the grammar) and is available for 830 | ** use by the action routines. 831 | ** 832 | ** Inputs: 833 | **
    834 | **
  • A pointer to the parser (an opaque structure.) 835 | **
  • The major token number. 836 | **
  • The minor token number. 837 | **
  • An option argument of a grammar-specified type. 838 | **
839 | ** 840 | ** Outputs: 841 | ** None. 842 | */ 843 | void Parse( 844 | void *yyp, /* The parser */ 845 | int yymajor, /* The major token code number */ 846 | ParseTOKENTYPE yyminor /* The value for the token */ 847 | ParseARG_PDECL /* Optional %extra_argument parameter */ 848 | ){ 849 | YYMINORTYPE yyminorunion; 850 | YYACTIONTYPE yyact; /* The parser action. */ 851 | #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) 852 | int yyendofinput; /* True if we are at the end of input */ 853 | #endif 854 | #ifdef YYERRORSYMBOL 855 | int yyerrorhit = 0; /* True if yymajor has invoked an error */ 856 | #endif 857 | yyParser *yypParser = (yyParser*)yyp; /* The parser */ 858 | ParseCTX_FETCH 859 | ParseARG_STORE 860 | 861 | assert( yypParser->yytos!=0 ); 862 | #if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY) 863 | yyendofinput = (yymajor==0); 864 | #endif 865 | 866 | yyact = yypParser->yytos->stateno; 867 | #ifndef NDEBUG 868 | if( yyTraceFILE ){ 869 | if( yyact < YY_MIN_REDUCE ){ 870 | fprintf(yyTraceFILE,"%sInput '%s' in state %d\n", 871 | yyTracePrompt,yyTokenName[yymajor],yyact); 872 | }else{ 873 | fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", 874 | yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); 875 | } 876 | } 877 | #endif 878 | 879 | while(1){ /* Exit by "break" */ 880 | assert( yypParser->yytos>=yypParser->yystack ); 881 | assert( yyact==yypParser->yytos->stateno ); 882 | yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact); 883 | if( yyact >= YY_MIN_REDUCE ){ 884 | unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */ 885 | assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ); 886 | #ifndef NDEBUG 887 | if( yyTraceFILE ){ 888 | int yysize = yyRuleInfoNRhs[yyruleno]; 889 | if( yysize ){ 890 | fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n", 891 | yyTracePrompt, 892 | yyruleno, yyRuleName[yyruleno], 893 | yyrulenoyytos[yysize].stateno); 895 | }else{ 896 | fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n", 897 | yyTracePrompt, yyruleno, yyRuleName[yyruleno], 898 | yyrulenoyytos - yypParser->yystack)>yypParser->yyhwm ){ 909 | yypParser->yyhwm++; 910 | assert( yypParser->yyhwm == 911 | (int)(yypParser->yytos - yypParser->yystack)); 912 | } 913 | #endif 914 | #if YYSTACKDEPTH>0 915 | if( yypParser->yytos>=yypParser->yystackEnd ){ 916 | yyStackOverflow(yypParser); 917 | break; 918 | } 919 | #else 920 | if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ 921 | if( yyGrowStack(yypParser) ){ 922 | yyStackOverflow(yypParser); 923 | break; 924 | } 925 | } 926 | #endif 927 | } 928 | yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor ParseCTX_PARAM); 929 | }else if( yyact <= YY_MAX_SHIFTREDUCE ){ 930 | yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor); 931 | #ifndef YYNOERRORRECOVERY 932 | yypParser->yyerrcnt--; 933 | #endif 934 | break; 935 | }else if( yyact==YY_ACCEPT_ACTION ){ 936 | yypParser->yytos--; 937 | yy_accept(yypParser); 938 | return; 939 | }else{ 940 | assert( yyact == YY_ERROR_ACTION ); 941 | yyminorunion.yy0 = yyminor; 942 | #ifdef YYERRORSYMBOL 943 | int yymx; 944 | #endif 945 | #ifndef NDEBUG 946 | if( yyTraceFILE ){ 947 | fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); 948 | } 949 | #endif 950 | #ifdef YYERRORSYMBOL 951 | /* A syntax error has occurred. 952 | ** The response to an error depends upon whether or not the 953 | ** grammar defines an error token "ERROR". 954 | ** 955 | ** This is what we do if the grammar does define ERROR: 956 | ** 957 | ** * Call the %syntax_error function. 958 | ** 959 | ** * Begin popping the stack until we enter a state where 960 | ** it is legal to shift the error symbol, then shift 961 | ** the error symbol. 962 | ** 963 | ** * Set the error count to three. 964 | ** 965 | ** * Begin accepting and shifting new tokens. No new error 966 | ** processing will occur until three tokens have been 967 | ** shifted successfully. 968 | ** 969 | */ 970 | if( yypParser->yyerrcnt<0 ){ 971 | yy_syntax_error(yypParser,yymajor,yyminor); 972 | } 973 | yymx = yypParser->yytos->major; 974 | if( yymx==YYERRORSYMBOL || yyerrorhit ){ 975 | #ifndef NDEBUG 976 | if( yyTraceFILE ){ 977 | fprintf(yyTraceFILE,"%sDiscard input token %s\n", 978 | yyTracePrompt,yyTokenName[yymajor]); 979 | } 980 | #endif 981 | yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion); 982 | yymajor = YYNOCODE; 983 | }else{ 984 | while( yypParser->yytos >= yypParser->yystack 985 | && (yyact = yy_find_reduce_action( 986 | yypParser->yytos->stateno, 987 | YYERRORSYMBOL)) > YY_MAX_SHIFTREDUCE 988 | ){ 989 | yy_pop_parser_stack(yypParser); 990 | } 991 | if( yypParser->yytos < yypParser->yystack || yymajor==0 ){ 992 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 993 | yy_parse_failed(yypParser); 994 | #ifndef YYNOERRORRECOVERY 995 | yypParser->yyerrcnt = -1; 996 | #endif 997 | yymajor = YYNOCODE; 998 | }else if( yymx!=YYERRORSYMBOL ){ 999 | yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor); 1000 | } 1001 | } 1002 | yypParser->yyerrcnt = 3; 1003 | yyerrorhit = 1; 1004 | if( yymajor==YYNOCODE ) break; 1005 | yyact = yypParser->yytos->stateno; 1006 | #elif defined(YYNOERRORRECOVERY) 1007 | /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to 1008 | ** do any kind of error recovery. Instead, simply invoke the syntax 1009 | ** error routine and continue going as if nothing had happened. 1010 | ** 1011 | ** Applications can set this macro (for example inside %include) if 1012 | ** they intend to abandon the parse upon the first syntax error seen. 1013 | */ 1014 | yy_syntax_error(yypParser,yymajor, yyminor); 1015 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 1016 | break; 1017 | #else /* YYERRORSYMBOL is not defined */ 1018 | /* This is what we do if the grammar does not define ERROR: 1019 | ** 1020 | ** * Report an error message, and throw away the input token. 1021 | ** 1022 | ** * If the input token is $, then fail the parse. 1023 | ** 1024 | ** As before, subsequent error messages are suppressed until 1025 | ** three input tokens have been successfully shifted. 1026 | */ 1027 | if( yypParser->yyerrcnt<=0 ){ 1028 | yy_syntax_error(yypParser,yymajor, yyminor); 1029 | } 1030 | yypParser->yyerrcnt = 3; 1031 | yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion); 1032 | if( yyendofinput ){ 1033 | yy_parse_failed(yypParser); 1034 | #ifndef YYNOERRORRECOVERY 1035 | yypParser->yyerrcnt = -1; 1036 | #endif 1037 | } 1038 | break; 1039 | #endif 1040 | } 1041 | } 1042 | #ifndef NDEBUG 1043 | if( yyTraceFILE ){ 1044 | yyStackEntry *i; 1045 | char cDiv = '['; 1046 | fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt); 1047 | for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){ 1048 | fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]); 1049 | cDiv = ' '; 1050 | } 1051 | fprintf(yyTraceFILE,"]\n"); 1052 | } 1053 | #endif 1054 | return; 1055 | } 1056 | 1057 | /* 1058 | ** Return the fallback token corresponding to canonical token iToken, or 1059 | ** 0 if iToken has no fallback. 1060 | */ 1061 | int ParseFallback(int iToken){ 1062 | #ifdef YYFALLBACK 1063 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); 1064 | return yyFallback[iToken]; 1065 | #else 1066 | (void)iToken; 1067 | return 0; 1068 | #endif 1069 | } 1070 | -------------------------------------------------------------------------------- /lib.c: -------------------------------------------------------------------------------- 1 | const char *prelude = "fn print_i8(x: i8) do\n" 2 | " found := i8\n" 3 | " digit := i8\n" 4 | "\n" 5 | " digit = x / 100\n" 6 | " if digit > 0 || found do\n" 7 | " found = 1\n" 8 | " send(.Console/write, digit + 48)\n" 9 | " end\n" 10 | " x = x - digit * 100\n" 11 | "\n" 12 | " digit = x / 10\n" 13 | " if digit > 0 || found do\n" 14 | " found = 1\n" 15 | " send(.Console/write, digit + 48)\n" 16 | " end\n" 17 | " x = x - digit * 10\n" 18 | "\n" 19 | " send(.Console/write, x + 48)\n" 20 | "end\n"; 21 | -------------------------------------------------------------------------------- /lut.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lang.h" 5 | #include "lut.h" 6 | #include "ast.h" 7 | #include "compiler.h" 8 | #include "debug.h" 9 | 10 | void setupLut(Lut *lut, int size) { 11 | lut->index = 0; 12 | lut->max = 0; 13 | lut->stack_slots = 0; 14 | lut->all_time_max = 0; 15 | lut->search_offset = 0; 16 | lut->size = size; 17 | 18 | lut->names = malloc(sizeof(char *) * lut->size); 19 | lut->types = malloc(sizeof(ReturnType) * lut->size); 20 | lut->args = malloc(sizeof(ReturnType *) * lut->size); 21 | lut->arg_count = malloc(sizeof(int) * lut->size); 22 | lut->locs = malloc(sizeof(int) * lut->size); 23 | 24 | lut->defer_index = 0; 25 | lut->defer_size = lut->size; 26 | 27 | lut->defered_lookups = malloc(sizeof(Node*) * lut->defer_size); 28 | } 29 | 30 | void resetLut(Lut *lut, int size) { 31 | lut->index = 0; 32 | lut->max = 0; 33 | lut->all_time_max = 0; 34 | lut->search_offset = 0; 35 | lut->size = size; 36 | 37 | free(lut->names); 38 | free(lut->types); 39 | free(lut->args); 40 | free(lut->arg_count); 41 | free(lut->locs); 42 | 43 | lut->names = malloc(sizeof(char *) * lut->size); 44 | lut->types = malloc(sizeof(ReturnType) * lut->size); 45 | lut->args = malloc(sizeof(ReturnType *) * lut->size); 46 | lut->arg_count = malloc(sizeof(int) * lut->size); 47 | lut->locs = malloc(sizeof(int) * lut->size); 48 | 49 | lut->defer_index = 0; 50 | lut->defer_size = lut->size; 51 | 52 | free(lut->defered_lookups); 53 | lut->defered_lookups = malloc(sizeof(Node*) * lut->defer_size); 54 | } 55 | 56 | 57 | int lutFind(Lut *lut, Node *node) { 58 | if (node->memory_address != -1) { 59 | return node->memory_address; 60 | } 61 | 62 | int start = 0; 63 | if (lut->search_offset > 0) { 64 | start = lut->search_offset; 65 | } 66 | 67 | int found = -1; 68 | 69 | for (int i = start; i < lut->index; i++) { 70 | if (strcmp(lut->names[i], node->tok->str) == 0) { 71 | node->memory_address = i; 72 | return i; 73 | } 74 | } 75 | 76 | 77 | return found; 78 | } 79 | 80 | int lutInsert(Lut *lut, Node *node, ReturnType t) { 81 | int id = lutFind(lut, node); 82 | if (id != -1) { 83 | fprintf(stderr, "Warn inserting already initialized variable\n"); 84 | return id; 85 | } 86 | 87 | if (lut->stack_slots > 0) { 88 | lut->names[lut->index] = node->tok->str; 89 | lut->types[lut->index] = t; 90 | lut->locs[lut->index] = lut->stack_slots * -1; 91 | lut->stack_slots--; 92 | 93 | id = lut->index++; 94 | return id; 95 | } 96 | 97 | if (lut->index == lut->size) { 98 | lut->size += 10; 99 | lut->names = realloc(lut->names, sizeof(char *) * lut->size); 100 | lut->types = realloc(lut->types, sizeof(ReturnType) * lut->size); 101 | lut->args = realloc(lut->args, sizeof(ReturnType *) * lut->size); 102 | lut->arg_count = realloc(lut->args, sizeof(int) * lut->size); 103 | lut->locs = realloc(lut->locs, sizeof(int) * lut->size); 104 | } 105 | 106 | lut->names[lut->index] = node->tok->str; 107 | lut->types[lut->index] = t; 108 | 109 | if (t == RET_I8 || t == RET_CHAR) { 110 | lut->locs[lut->index] = lut->max; 111 | lut->max += 1; 112 | } else if (t == RET_I16 || t == RET_CHARS) { 113 | lut->locs[lut->index] = lut->max; 114 | lut->max += 2; 115 | } else if (t == RET_I8S) { 116 | lut->locs[lut->index] = lut->max; 117 | lut->max += atoi(node->children[0]->children[0]->tok->str); 118 | } else if (t == RET_I16S) { 119 | lut->locs[lut->index] = lut->max; 120 | lut->max += 2 * atoi(node->children[0]->children[0]->tok->str); 121 | } else if (t == RET_I) { 122 | fprintf(stderr, "unresolved type in lut.\n"); 123 | } else if (t == RET_VOID) { 124 | // TODO: this probably is a problem, 125 | // but we need this for inserting function definitions 126 | fprintf(stderr, "bad type in lut void\n"); 127 | } else { 128 | fprintf(stderr, "bad type in lut %s\n", retType(t)); 129 | } 130 | node->memory_address = id; 131 | id = lut->index++; 132 | return id; 133 | } 134 | 135 | int lutFindFn(Lut * lut, Node *node) { 136 | int found = 0; 137 | for (int i = 0; i < lut->index ; i++) { 138 | if (strcmp(lut->names[i], node->tok->str) != 0) { 139 | continue; 140 | } 141 | if (node->ret != lut->types[i]) { 142 | continue; 143 | } 144 | 145 | if (lut->arg_count[i] != node->children[0]->children_index) { 146 | continue; 147 | } 148 | 149 | found = 1; 150 | for (int j = 0; j < node->children[0]->children_index; j++) { 151 | if (InstrToRet(node->children[0]->children[j]->children[0]->type) != lut->args[i][j]) { 152 | found = 0; 153 | } 154 | } 155 | if (found) { 156 | return i; 157 | } 158 | } 159 | return -1; 160 | } 161 | 162 | int lutFindFnCall(Lut * lut, Node *node) { 163 | int found = 0; 164 | for (int i = 0; i < lut->index ; i++) { 165 | if (strcmp(lut->names[i], node->tok->str) != 0) { 166 | continue; 167 | } 168 | 169 | if (node->ret != lut->types[i]) { 170 | continue; 171 | } 172 | 173 | if (lut->arg_count[i] != node->children[0]->children_index) { 174 | continue; 175 | } 176 | 177 | found = 1; 178 | for (int j = 0; j < node->children[0]->children_index; j++) { 179 | if (node->children[0]->children[j]->ret != lut->args[i][j] && 180 | node->children[0]->children[j]->ret != RET_I) { 181 | found = 0; 182 | } 183 | } 184 | 185 | if (found) { 186 | // Finalize the types for any RET_I 187 | for (int j = 0; j < node->children[0]->children_index; j++) { 188 | if (node->children[0]->children[j]->ret == RET_I) { 189 | node->children[0]->children[j]->ret = lut->args[i][j]; 190 | } 191 | } 192 | return i; 193 | } 194 | } 195 | return -1; 196 | } 197 | 198 | int lutInsertFn(Lut *lut, Node *node, ReturnType t) { 199 | int id = lutFindFn(lut, node); 200 | if (id != -1) { 201 | fprintf(stderr, "Warning: function insert already found %s\n", node->tok->str); 202 | return id; 203 | } 204 | 205 | if (lut->index == lut->size) { 206 | lut->size += 10; 207 | lut->names = realloc(lut->names, sizeof(char *) * lut->size); 208 | lut->types = realloc(lut->types, sizeof(ReturnType) * lut->size); 209 | lut->args = realloc(lut->args, sizeof(ReturnType *) * lut->size); 210 | lut->arg_count = realloc(lut->args, sizeof(int) * lut->size); 211 | lut->locs = realloc(lut->locs, sizeof(int) * lut->size); 212 | } 213 | 214 | lut->names[lut->index] = node->tok->str; 215 | lut->types[lut->index] = t; 216 | lut->index++; 217 | return id; 218 | } 219 | 220 | void deferLookup(Lut * lut, Node * node) { 221 | if (lut->defer_index == lut->defer_size) { 222 | lut->defer_size += 10; 223 | lut->defered_lookups = realloc(lut->defered_lookups, sizeof(Node*) * lut->defer_size); 224 | } 225 | lut->defered_lookups[lut->defer_index++] = node; 226 | } 227 | -------------------------------------------------------------------------------- /lut.h: -------------------------------------------------------------------------------- 1 | #ifndef LUT 2 | #define LUT 3 | 4 | #include "ast.h" 5 | 6 | typedef struct Lut { 7 | int size; 8 | int index; 9 | int max; 10 | int stack_slots; 11 | int all_time_max; 12 | int search_offset; 13 | ReturnType *types; // TODO: rename to rets 14 | ReturnType **args; 15 | int *arg_count; 16 | char **names; 17 | int *locs; 18 | int defer_index; 19 | int defer_size; 20 | Node **defered_lookups; 21 | } Lut; 22 | 23 | extern void setupLut(Lut *lut, int size); 24 | extern void resetLut(Lut *lut, int size); 25 | 26 | extern int lutInsert(Lut *lut, Node *node, ReturnType t); 27 | extern int lutFind(Lut *lut, Node *node); 28 | 29 | extern int lutInsertFn(Lut *lut, Node *node, ReturnType t); 30 | extern int lutFindFn(Lut *lut, Node *node); 31 | extern int lutFindFnCall(Lut *lut, Node *node); 32 | 33 | extern void deferLookup(Lut * lut, Node * node); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "compiler.h" 2 | #include "debug.h" 3 | #include "emit.h" 4 | #include "lang.h" 5 | #include "parser.h" 6 | #include "scanner.h" 7 | #include 8 | 9 | int main(int argc, char ** argv) { 10 | Program prog; 11 | setupCompiler(&prog); 12 | 13 | if (argc == 1) { 14 | fromStdin(&prog); 15 | } else if (argc == 2) { 16 | fromFile(&prog, argv[1]); 17 | prog.data.filename = argv[1]; 18 | } else if (strcmp(argv[1], "-e") == 0) { 19 | fromString(&prog, argv[2]); 20 | } else { 21 | fprintf(stderr, "Can't understand command line arguments\n"); 22 | return 1; 23 | } 24 | 25 | parse(&prog); 26 | build_instruction_list(&prog, prog.root); 27 | /* printGraph(prog.root); */ 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HParker/dotal/2c23f301de4e3cfef46b8f801d0bb6dc3e3eb0dd/out.png -------------------------------------------------------------------------------- /parser.y: -------------------------------------------------------------------------------- 1 | %include { 2 | #include 3 | #include 4 | #include 5 | #include "lang.h" 6 | #include "compiler.h" 7 | #include "lut.h" 8 | #include "debug.h" 9 | } 10 | 11 | %token_type { Token* } 12 | %type function { Node* } 13 | %type functions { Node* } 14 | %type type { Node* } 15 | %type function_body { Node* } 16 | %type conditional_elses { Node* } 17 | %type conditional_else { Node* } 18 | %type argument { Node* } 19 | %type arguments { Node* } 20 | %type argument_definition { Node* } 21 | %type argument_definitions { Node* } 22 | %type statements { Node* } 23 | %type statement { Node* } 24 | %type fcall { Node* } 25 | %type return_type { Node* } 26 | %type expression { Node* } 27 | %type operator { Node* } 28 | %type location { Node* } 29 | %extra_argument { Program *prog } 30 | 31 | %left EQUALS EXPR_AND EXPR_OR. 32 | %left EQUALITY NOT_EQUALITY GREATER_THAN LESS_THAN. 33 | %left PLUS MINUS. 34 | %left TIMES SLASH. 35 | %left MODULO. 36 | 37 | %syntax_error 38 | { 39 | fprintf(stderr, "Syntax error: Unexpected token of lexcode %i - %s - %s:%i:%i-%i\n%s", 40 | prog->tok->lexCode, 41 | prog->tok->str, 42 | prog->tok->filename, 43 | prog->tok->line, 44 | prog->tok->column, 45 | prog->tok->column + prog->tok->size, 46 | programLine(prog, prog->tok->line)); 47 | printUnderline(prog->tok); 48 | prog->errored = 1; 49 | } 50 | 51 | program ::= functions(funcs). { 52 | progAddChild(prog->root, funcs); 53 | /* prog->root = funcs; */ 54 | } 55 | 56 | functions(node) ::= functions(funcs) function(func). { 57 | node = funcs; 58 | progAddChild(funcs, func); 59 | } 60 | 61 | functions(node) ::= . { 62 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 63 | } 64 | 65 | function(node) ::= SPRITE IDENTIFIER(sprite_name) INT_LITERAL(row1) INT_LITERAL(row2) INT_LITERAL(row3) INT_LITERAL(row4) INT_LITERAL(row5) INT_LITERAL(row6) INT_LITERAL(row7) INT_LITERAL(row8). { 66 | node = progNode(prog->id++, sprite_name, INSTR_SPRITE, RET_VOID); 67 | progAddChild(node, progNode(prog->id++, row1, INSTR_SPRITE_ROW, RET_VOID)); 68 | progAddChild(node, progNode(prog->id++, row2, INSTR_SPRITE_ROW, RET_VOID)); 69 | progAddChild(node, progNode(prog->id++, row3, INSTR_SPRITE_ROW, RET_VOID)); 70 | progAddChild(node, progNode(prog->id++, row4, INSTR_SPRITE_ROW, RET_VOID)); 71 | progAddChild(node, progNode(prog->id++, row5, INSTR_SPRITE_ROW, RET_VOID)); 72 | progAddChild(node, progNode(prog->id++, row6, INSTR_SPRITE_ROW, RET_VOID)); 73 | progAddChild(node, progNode(prog->id++, row7, INSTR_SPRITE_ROW, RET_VOID)); 74 | progAddChild(node, progNode(prog->id++, row8, INSTR_SPRITE_ROW, RET_VOID)); 75 | } 76 | 77 | // Type definition 78 | // Global 79 | function(node) ::= GLOBAL IDENTIFIER(IDENT) DEFINE type(t). { 80 | node = progNode(prog->id++, IDENT, INSTR_GLOBAL_DEFINE, RET_VOID); 81 | // TODO: is this child required? 82 | // if I get rid of it, I need to clean up the nodes 83 | progAddChild(node, t); 84 | 85 | lutInsert(&prog->global_lut, node, InstrToRet(t->type)); 86 | } 87 | 88 | // local 89 | statement(node) ::= IDENTIFIER(IDENT) DEFINE type(t). { 90 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_DEFINE, RET_VOID); 91 | // TODO: is the child needed here? 92 | // if I get rid of it, I need to clean up the nodes 93 | // Alternatively type can just stay a token? 94 | // I'll have to think 95 | progAddChild(node, t); 96 | lutInsert(&prog->local_lut, node, InstrToRet(t->type)); 97 | } 98 | 99 | type(node) ::= I16(t). { 100 | node = progNode(prog->id++, t, INSTR_I16, RET_VOID); 101 | } 102 | 103 | type(node) ::= I8(t). { 104 | node = progNode(prog->id++, t, INSTR_I8, RET_VOID); 105 | } 106 | 107 | type(node) ::= CHAR(t). { 108 | node = progNode(prog->id++, t, INSTR_CHAR, RET_VOID); 109 | } 110 | 111 | type(node) ::= CHARS(t). { 112 | node = progNode(prog->id++, t, INSTR_CHARS, RET_VOID); 113 | } 114 | 115 | type(node) ::= INT_LITERAL(index) I8S(t). { 116 | node = progNode(prog->id++, t, INSTR_I8S, RET_VOID); 117 | progAddChild(node, progNode(prog->id++, index, INSTR_LITERAL_INT, RET_I16)); 118 | } 119 | 120 | type(node) ::= INT_LITERAL(index) CHARS(t). { 121 | node = progNode(prog->id++, t, INSTR_CHARS, RET_VOID); 122 | progAddChild(node, progNode(prog->id++, index, INSTR_LITERAL_INT, RET_I16)); 123 | } 124 | 125 | type(node) ::= INT_LITERAL(index) I16S(t). { 126 | node = progNode(prog->id++, t, INSTR_I16S, RET_VOID); 127 | progAddChild(node, progNode(prog->id++, index, INSTR_LITERAL_INT, RET_I16)); 128 | } 129 | 130 | // Function 131 | function(node) ::= FN IDENTIFIER(fn_name) OPEN_PAREN argument_definitions(arg) CLOSE_PAREN return_type(RT) DO_BLOCK statements(body) END_BLOCK. { 132 | node = progNode(prog->id++, fn_name, INSTR_FUNCTION, InstrToRet(RT->type)); 133 | progAddChild(node, arg); 134 | progAddChild(node, body); 135 | 136 | for (int i = 0; i < body->children_index; i++) { 137 | if (body->children[i]->ret != RET_VOID && body->children[i]->ret != InstrToRet(RT->type)) { 138 | // TODO: re-enable this after fixing LOCAL READ and assign use of RET 139 | /* printError(prog, body->children[i], "function returns a type that is not the return type of the function"); */ 140 | } 141 | } 142 | 143 | // TODO: this should be handled in the LUT for us. 144 | prog->function_lut.args[prog->function_lut.index] = malloc(sizeof(ReturnType) * arg->children_index); 145 | prog->function_lut.arg_count[prog->function_lut.index] = arg->children_index; 146 | for (int i = 0; i < arg->children_index; i++) { 147 | prog->function_lut.args[prog->function_lut.index][i] = InstrToRet(arg->children[i]->children[0]->type); 148 | } 149 | 150 | lutInsertFn(&prog->function_lut, node, InstrToRet(RT->type)); 151 | 152 | // Update search offset so that local functions are scoped to the function 153 | if (prog->local_lut.max > prog->local_lut.all_time_max) { 154 | prog->local_lut.all_time_max = prog->local_lut.max; 155 | } 156 | prog->local_lut.search_offset = prog->local_lut.index; 157 | prog->local_lut.stack_slots = MAX_STACK_SLOTS; 158 | } 159 | 160 | 161 | argument_definitions(node) ::= . { 162 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 163 | } 164 | 165 | argument_definitions(node) ::= argument_definition(arg). { 166 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 167 | progAddChild(node, arg); 168 | } 169 | 170 | argument_definitions(node) ::= argument_definitions(nodes) COMMA argument_definition(arg). { 171 | progAddChild(nodes, arg); 172 | node = nodes; 173 | } 174 | 175 | argument_definition(node) ::= IDENTIFIER(IDENT) COLON type(t). { 176 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_ASSIGN, InstrToRet(t->type)); 177 | 178 | // TODO: The type node is not really needed 179 | // but if we get rid of it we need to clean it up. 180 | progAddChild(node, t); 181 | 182 | int ivarId = lutInsert(&prog->local_lut, node, InstrToRet(t->type)); 183 | if (ivarId == -1) { 184 | // TODO: this should be impossible... we just inserted it right? 185 | printError(prog, node, "cannot assign to argument? why: this is a compiler bug"); 186 | } else { 187 | node->loc = prog->local_lut.locs[ivarId]; 188 | } 189 | } 190 | 191 | return_type(node) ::= COLON type(return_type). { 192 | node = return_type; 193 | } 194 | 195 | return_type(node) ::= . { 196 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 197 | } 198 | 199 | 200 | fcall(node) ::= IDENTIFIER(func) OPEN_PAREN arguments(args_node) CLOSE_PAREN. { 201 | node = progNode(prog->id++, func, INSTR_FUNCTION_CALL, RET_VOID); 202 | progAddChild(node, args_node); 203 | 204 | int functionId = lutFindFnCall(&prog->function_lut, node); 205 | 206 | if (functionId == -1) { 207 | deferLookup(&prog->function_lut, node); 208 | } else { 209 | if (args_node->children_index != prog->function_lut.arg_count[functionId]) { 210 | printError(prog, node, "wrong number of arguments"); 211 | fprintf(stderr, "%i != %i\n", args_node->children_index, prog->function_lut.arg_count[functionId]); 212 | } else { 213 | if (prog->function_lut.types[functionId] != RET_VOID) { 214 | printError(prog, node, "calling function with a return value that isn't used. This is now allowed"); 215 | } else { 216 | node->ret = prog->function_lut.types[functionId]; 217 | } 218 | } 219 | } 220 | } 221 | 222 | statements(node) ::= statements(states) statement(state). { 223 | if (state->type == INSTR_RETURN) { 224 | states->ret = state->ret; 225 | } 226 | progAddChild(states, state); 227 | node = states; 228 | } 229 | 230 | statements(node) ::= . { 231 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 232 | } 233 | 234 | statement(node) ::= BREAK(b). { 235 | node = progNode(prog->id++, b, INSTR_BREAK, RET_VOID); 236 | } 237 | 238 | statement(node) ::= PUT(p) OPEN_PAREN expression(expr) CLOSE_PAREN. { 239 | // TODO: this function is on the axe 240 | prog->used_print = 1; 241 | 242 | node = progNode(prog->id++, p, INSTR_PUT, RET_VOID); 243 | progAddChild(node, expr); 244 | } 245 | 246 | statement(node) ::= SEND(p) OPEN_PAREN location(loc) COMMA expression(expr) CLOSE_PAREN. { 247 | // TODO: ret is wrong here 248 | node = progNode(prog->id++, p, INSTR_SEND, RET_VOID); 249 | 250 | if (expr->ret == RET_I) { 251 | expr->ret = loc->ret; 252 | } 253 | 254 | if (strcmp(expr->tok->str, "sine") == 0) { 255 | prog->used_sine = 1; 256 | } 257 | progAddChild(node, expr); 258 | progAddChild(node, loc); 259 | } 260 | 261 | expression(node) ::= COLON IDENTIFIER(label). { 262 | node = progNode(prog->id++, label, INSTR_LABEL, RET_I16); 263 | } 264 | 265 | 266 | expression(node) ::= GET(p) OPEN_PAREN location(loc) CLOSE_PAREN. { 267 | node = progNode(prog->id++, p, INSTR_GET, loc->ret); 268 | progAddChild(node, loc); 269 | } 270 | 271 | location(node) ::= DOT IDENTIFIER(loc1) SLASH IDENTIFIER(loc2). { 272 | node = progNode(prog->id++, loc1, INSTR_MEM_AREA, memoryIdentifierType(prog, node, loc1->str, loc2->str)); 273 | // TODO: do I need this child node? 274 | progAddChild(node, progNode(prog->id++, loc2, INSTR_MEM_LOCATION, RET_VOID)); 275 | } 276 | 277 | statement(node) ::= PRINT(p) OPEN_PAREN expression(expr) CLOSE_PAREN. { 278 | // TODO: this function is on the axe 279 | node = progNode(prog->id++, p, INSTR_PRINT, RET_VOID); 280 | progAddChild(node, expr); 281 | } 282 | 283 | statement(node) ::= IDENTIFIER(IDENT) EQUALS expression(expr). { 284 | // TODO: this is incorrect usage of ret, but emit needs this. 285 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_ASSIGN, expr->ret); 286 | progAddChild(node, expr); 287 | 288 | int ivarId = lutFind(&prog->local_lut, node); 289 | if (ivarId == -1) { 290 | printError(prog, node, "cannot assign to undefined local variable"); 291 | } else { 292 | if (expr->ret == RET_I) { 293 | expr->ret = prog->local_lut.types[ivarId]; 294 | node->ret = expr->ret; 295 | } else if (prog->local_lut.types[ivarId] != expr->ret) { 296 | if (expr->type == INSTR_FUNCTION_CALL) { 297 | // TODO: only do this if the function is unresolved 298 | expr->ret = prog->local_lut.types[ivarId]; 299 | } 300 | } 301 | node->loc = prog->local_lut.locs[ivarId]; 302 | } 303 | } 304 | 305 | statement(node) ::= GLOBAL IDENTIFIER(IDENT) EQUALS expression(expr). { 306 | node = progNode(prog->id++, IDENT, INSTR_GLOBAL_ASSIGN, RET_VOID); 307 | progAddChild(node, expr); 308 | 309 | int ivarId = lutFind(&prog->global_lut, node); 310 | if (ivarId == -1) { 311 | printError(prog, node, "cannot assign to undefined global variable"); 312 | } else { 313 | if (expr->ret == RET_I) { 314 | expr->ret = prog->global_lut.types[ivarId]; 315 | } else if (prog->global_lut.types[ivarId] != expr->ret) { 316 | printError(prog, node, "global assigning incompatible types"); 317 | fprintf(stderr, "%i != %i\n", prog->local_lut.types[ivarId], expr->ret); 318 | } 319 | // TODO: is this intended to be expr->ret? 320 | // changing the ret on an assign seems super wrong... 321 | // node->ret = prog->global_lut.types[ivarId]; 322 | node->loc = prog->global_lut.locs[ivarId]; 323 | } 324 | } 325 | 326 | statement(node) ::= fcall(fc). { 327 | node = fc; 328 | } 329 | 330 | statement(node) ::= THEME(th) COLOR_CODE(background) COLOR_CODE(c0) COLOR_CODE(c1) COLOR_CODE(c2). { 331 | prog->used_system = 1; 332 | 333 | node = progNode(prog->id++, th, INSTR_THEME, RET_VOID); 334 | 335 | progAddChild(node, progNode(prog->id++, background, INSTR_COLOR_BACKGROUND, RET_VOID)); 336 | progAddChild(node, progNode(prog->id++, c0, INSTR_COLOR0, RET_VOID)); 337 | progAddChild(node, progNode(prog->id++, c1, INSTR_COLOR1, RET_VOID)); 338 | progAddChild(node, progNode(prog->id++, c2, INSTR_COLOR2, RET_VOID)); 339 | } 340 | 341 | statement(node) ::= RETURN(RET) COLON expression(expr). { 342 | node = progNode(prog->id++, RET, INSTR_RETURN, expr->ret); 343 | progAddChild(node, expr); 344 | } 345 | 346 | statement(node) ::= RETURN(ret). { 347 | node = progNode(prog->id++, ret, INSTR_RETURN, RET_VOID); 348 | } 349 | 350 | statement(node) ::= WHILE(cond) expression(expr) DO_BLOCK statements(states) END_BLOCK. { 351 | node = progNode(prog->id++, cond, INSTR_WHILE, RET_VOID); 352 | progAddChild(node, expr); 353 | progAddChild(node, states); 354 | node->ret = RET_VOID; 355 | } 356 | 357 | statement(node) ::= IF(cond) expression(expr) DO_BLOCK statements(true_block) END_BLOCK. { 358 | node = progNode(prog->id++, cond, INSTR_CONDITION, true_block->ret); 359 | if (expr->ret != RET_I8 && expr->ret != RET_I16) { 360 | printError(prog, node, "Conditionals must return a number"); 361 | fprintf(stderr, "type %i\n", expr->ret); 362 | } 363 | 364 | progAddChild(node, expr); 365 | progAddChild(node, true_block); 366 | } 367 | 368 | statement(node) ::= IF(cond) expression(expr) DO_BLOCK statements(true_block) conditional_elses(ces) END_BLOCK. { 369 | node = progNode(prog->id++, cond, INSTR_CONDITION, true_block->ret); 370 | 371 | // TODO: check true_block->ret == ces->ret 372 | 373 | progAddChild(node, expr); 374 | progAddChild(node, true_block); 375 | progAddChild(node, ces); 376 | } 377 | 378 | statement(node) ::= IF(cond) expression(expr) DO_BLOCK statements(true_block) conditional_elses(ces) ELSE statements(else_block) END_BLOCK. { 379 | node = progNode(prog->id++, cond, INSTR_CONDITION, true_block->ret); 380 | progAddChild(node, expr); 381 | progAddChild(node, true_block); 382 | progAddChild(node, ces); 383 | 384 | progAddChild(ces->children[ces->children_index - 1], else_block); 385 | } 386 | 387 | statement(node) ::= IF(cond) expression(expr) DO_BLOCK statements(true_block) ELSE statements(else_block) END_BLOCK. { 388 | node = progNode(prog->id++, cond, INSTR_CONDITION, true_block->ret); 389 | if ((true_block->ret != RET_VOID || else_block->ret != RET_VOID) && true_block->ret != else_block->ret) { 390 | printError(prog, node, "true and else block return different types"); 391 | } 392 | progAddChild(node, expr); 393 | progAddChild(node, true_block); 394 | progAddChild(node, else_block); 395 | } 396 | 397 | conditional_elses(node) ::= conditional_elses(ces) conditional_else(ce). { 398 | progAddChild(ces, ce); 399 | node = ces; 400 | } 401 | 402 | conditional_elses(node) ::= conditional_else(ce). { 403 | node = ce; 404 | } 405 | 406 | conditional_else(node) ::= ELSE_IF(cond) expression(expr) DO_BLOCK statements(states). { 407 | node = progNode(prog->id++, cond, INSTR_CONDITION, RET_VOID); 408 | progAddChild(node, expr); 409 | progAddChild(node, states); 410 | } 411 | 412 | expression(node) ::= PAD(p) OPEN_PAREN arguments(args) CLOSE_PAREN. { 413 | node = progNode(prog->id++, p, INSTR_PAD, RET_I16); 414 | progAddChild(node, args); 415 | } 416 | 417 | expression(node) ::= fcall(fc). { 418 | node = fc; 419 | } 420 | 421 | arguments(node) ::= . { 422 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 423 | } 424 | 425 | arguments(node) ::= expression(arg). { 426 | node = progEmptyNode(prog->id++, INSTR_ROOT, RET_VOID); 427 | progAddChild(node, arg); 428 | } 429 | 430 | arguments(node) ::= arguments(nodes) COMMA expression(arg). { 431 | progAddChild(nodes, arg); 432 | node = nodes; 433 | } 434 | 435 | expression(node) ::= IDENTIFIER(IDENT). { 436 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_READ, RET_VOID); 437 | 438 | int ivarId = lutFind(&prog->local_lut, node); 439 | if (ivarId == -1) { 440 | printError(prog, node, "cannot find local variable"); 441 | } else { 442 | node->ret = prog->local_lut.types[ivarId]; 443 | node->loc = prog->local_lut.locs[ivarId]; 444 | } 445 | } 446 | 447 | expression(node) ::= IDENTIFIER(IDENT) OPEN_SQUARE_BRACKET expression(index) CLOSE_SQUARE_BRACKET. { 448 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_ARRAY_READ, RET_VOID); 449 | 450 | // TODO: 451 | int ivarId = lutFind(&prog->local_lut, node); 452 | if (ivarId == -1) { 453 | printError(prog, node, "cannot find local variable"); 454 | } else { 455 | if (prog->local_lut.types[ivarId] == RET_I8S) { 456 | node->ret = RET_I8; 457 | } else if (prog->local_lut.types[ivarId] == RET_CHARS) { 458 | node->ret = RET_CHAR; 459 | } else if (prog->local_lut.types[ivarId] == RET_I16S) { 460 | node->ret = RET_I16; 461 | } else { 462 | printError(prog, node, "Attempted to index into non-array variable"); 463 | fprintf(stderr, "lut: %i\n", prog->local_lut.types[ivarId]); 464 | } 465 | node->loc = prog->local_lut.locs[ivarId]; 466 | } 467 | 468 | if (index->ret == RET_I8) { 469 | // insert a pad to upgrade the i8 to a i16 just for this operation 470 | Node * pad = progImplicitPad(prog, node); 471 | progAddChild(node, pad); 472 | progAddChild(pad, index); 473 | } else { 474 | progAddChild(node, index); 475 | } 476 | } 477 | 478 | statement(node) ::= IDENTIFIER(IDENT) OPEN_SQUARE_BRACKET expression(index) CLOSE_SQUARE_BRACKET EQUALS expression(expr). { 479 | node = progNode(prog->id++, IDENT, INSTR_LOCAL_ARRAY_ASSIGN, RET_VOID); 480 | progAddChild(node, expr); 481 | 482 | int ivarId = lutFind(&prog->local_lut, node); 483 | if (ivarId == -1) { 484 | printError(prog, node, "cannot assign to undefined local variable"); 485 | } else { 486 | if (expr->ret == RET_I) { 487 | if (prog->local_lut.types[ivarId] == RET_I8S) { 488 | expr->ret = RET_I8; 489 | } else if (prog->local_lut.types[ivarId] == RET_CHARS) { 490 | // TODO: not sure we can reach this one since char is never ambiguous. 491 | expr->ret = RET_CHAR; 492 | } else if (prog->local_lut.types[ivarId] == RET_I16S) { 493 | expr->ret = RET_I16; 494 | } else { 495 | // TODO: We can also just add this to the loc 496 | // That saves a node, which is nice. 497 | printError(prog, node, "cannot assign this type to a local variable IMPLEMENT ME"); 498 | } 499 | // TODO: why did I write this this way??? 500 | } else if (prog->local_lut.types[ivarId] == RET_I8S && expr->ret == RET_I8) { 501 | } else if (prog->local_lut.types[ivarId] == RET_I16S && expr->ret == RET_I16) { 502 | } else if (prog->local_lut.types[ivarId] == RET_CHARS && expr->ret == RET_CHAR) { 503 | } else if (prog->local_lut.types[ivarId] != expr->ret) { 504 | printError(prog, expr, "array assigning incompatible types"); 505 | fprintf(stderr, "%i != %i\n", prog->local_lut.types[ivarId], expr->ret); 506 | } 507 | node->loc = prog->local_lut.locs[ivarId]; 508 | } 509 | 510 | if (index->ret == RET_I8) { 511 | // insert a pad to upgrade the i8 to a i16 just for this operation 512 | Node * pad = progImplicitPad(prog, node); 513 | progAddChild(node, pad); 514 | progAddChild(pad, index); 515 | } else { 516 | progAddChild(node, index); 517 | } 518 | 519 | node->ret = RET_VOID; 520 | } 521 | 522 | expression(node) ::= GLOBAL IDENTIFIER(IDENT) OPEN_SQUARE_BRACKET expression(index) CLOSE_SQUARE_BRACKET. { 523 | node = progNode(prog->id++, IDENT, INSTR_GLOBAL_ARRAY_READ, RET_VOID); 524 | 525 | int ivarId = lutFind(&prog->global_lut, node); 526 | if (ivarId == -1) { 527 | printError(prog, node, "cannot find global variable"); 528 | } else { 529 | if (prog->global_lut.types[ivarId] == RET_I8S) { 530 | node->ret = RET_I8; 531 | } else if (prog->global_lut.types[ivarId] == RET_I16S) { 532 | node->ret = RET_I16; 533 | } else { 534 | // TODO: We can also just add this to the loc 535 | // That saves a node, which is nice. 536 | node->ret = prog->global_lut.types[ivarId]; 537 | } 538 | node->loc = prog->global_lut.locs[ivarId]; 539 | } 540 | 541 | if (index->ret == RET_I8) { 542 | // insert a pad to upgrade the i8 to a i16 just for this operation 543 | Node * pad = progImplicitPad(prog, node); 544 | progAddChild(node, pad); 545 | progAddChild(pad, index); 546 | } else { 547 | progAddChild(node, index); 548 | } 549 | } 550 | 551 | statement(node) ::= GLOBAL IDENTIFIER(IDENT) OPEN_SQUARE_BRACKET expression(index) CLOSE_SQUARE_BRACKET EQUALS expression(expr). { 552 | node = progNode(prog->id++, IDENT, INSTR_GLOBAL_ARRAY_ASSIGN, RET_VOID); 553 | progAddChild(node, expr); 554 | 555 | int ivarId = lutFind(&prog->global_lut, node); 556 | if (ivarId == -1) { 557 | printError(prog, node, "cannot assign to undefined global variable"); 558 | } else { 559 | if (expr->ret == RET_I) { 560 | if (prog->global_lut.types[ivarId] == RET_I8S) { 561 | expr->ret = RET_I8; 562 | } else if (prog->global_lut.types[ivarId] == RET_I16S) { 563 | expr->ret = RET_I16; 564 | } else { 565 | // TODO: We can also just add this to the loc 566 | // That saves a node, which is nice. 567 | fprintf(stderr,"UH OH\n"); 568 | } 569 | } else if (prog->global_lut.types[ivarId] == RET_I8S && expr->ret == RET_I8) { 570 | } else if (prog->global_lut.types[ivarId] == RET_I16S && expr->ret == RET_I16) { 571 | } else if (prog->global_lut.types[ivarId] != expr->ret) { 572 | printError(prog, expr, "array assigning incompatible types"); 573 | fprintf(stderr, "%i != %i\n", prog->global_lut.types[ivarId], expr->ret); 574 | } 575 | node->loc = prog->global_lut.locs[ivarId]; 576 | } 577 | 578 | if (index->ret == RET_I8) { 579 | // insert a pad to upgrade the i8 to a i16 just for this operation 580 | Node * pad = progImplicitPad(prog, node); 581 | progAddChild(node, pad); 582 | progAddChild(pad, index); 583 | } else { 584 | progAddChild(node, index); 585 | } 586 | 587 | node->ret = RET_VOID; 588 | } 589 | 590 | expression(node) ::= GLOBAL IDENTIFIER(IDENT). { 591 | node = progNode(prog->id++, IDENT, INSTR_GLOBAL_READ, RET_VOID); 592 | 593 | int ivarId = lutFind(&prog->global_lut, node); 594 | if (ivarId == -1) { 595 | printError(prog, node, "cannot find global variable"); 596 | } else { 597 | node->ret = prog->global_lut.types[ivarId]; 598 | node->loc = prog->global_lut.locs[ivarId]; 599 | } 600 | } 601 | 602 | expression(node) ::= OPEN_PAREN expression(expr) CLOSE_PAREN. { 603 | node = expr; 604 | } 605 | 606 | 607 | expression(node) ::= expression(lhs) TIMES(op) expression(rhs). { 608 | node = progNode(prog->id++, op, INSTR_TIMES, lhs->ret); 609 | 610 | progAddChild(node, lhs); 611 | progAddChild(node, rhs); 612 | 613 | verifyTypes(prog, node, lhs, rhs); 614 | } 615 | 616 | expression(node) ::= expression(lhs) SLASH(op) expression(rhs). { 617 | node = progNode(prog->id++, op, INSTR_DIVIDE, lhs->ret); 618 | 619 | progAddChild(node, lhs); 620 | progAddChild(node, rhs); 621 | 622 | verifyTypes(prog, node, lhs, rhs); 623 | } 624 | 625 | expression(node) ::= expression(lhs) PLUS(op) expression(rhs). { 626 | node = progNode(prog->id++, op, INSTR_PLUS, lhs->ret); 627 | 628 | progAddChild(node, lhs); 629 | progAddChild(node, rhs); 630 | 631 | verifyTypes(prog, node, lhs, rhs); 632 | } 633 | 634 | expression(node) ::= expression(lhs) MINUS(op) expression(rhs). { 635 | node = progNode(prog->id++, op, INSTR_MINUS, lhs->ret); 636 | 637 | progAddChild(node, lhs); 638 | progAddChild(node, rhs); 639 | 640 | verifyTypes(prog, node, lhs, rhs); 641 | } 642 | 643 | expression(node) ::= expression(lhs) MODULO(op) expression(rhs). { 644 | node = progNode(prog->id++, op, INSTR_MODULO, lhs->ret); 645 | 646 | progAddChild(node, lhs); 647 | progAddChild(node, rhs); 648 | 649 | verifyTypes(prog, node, lhs, rhs); 650 | } 651 | 652 | expression(node) ::= expression(lhs) EQUALITY(op) expression(rhs). { 653 | node = progNode(prog->id++, op, INSTR_EQUALITY, RET_I8); 654 | 655 | progAddChild(node, lhs); 656 | progAddChild(node, rhs); 657 | 658 | verifyTypes(prog, node, lhs, rhs); 659 | } 660 | 661 | expression(node) ::= expression(lhs) NOT_EQUALITY(op) expression(rhs). { 662 | node = progNode(prog->id++, op, INSTR_NOT_EQUALITY, RET_I8); 663 | 664 | progAddChild(node, lhs); 665 | progAddChild(node, rhs); 666 | 667 | verifyTypes(prog, node, lhs, rhs); 668 | } 669 | 670 | 671 | expression(node) ::= expression(lhs) GREATER_THAN(op) expression(rhs). { 672 | node = progNode(prog->id++, op, INSTR_GREATER_THAN, RET_I8); 673 | 674 | progAddChild(node, lhs); 675 | progAddChild(node, rhs); 676 | 677 | verifyTypes(prog, node, lhs, rhs); 678 | } 679 | 680 | expression(node) ::= expression(lhs) LESS_THAN(op) expression(rhs). { 681 | node = progNode(prog->id++, op, INSTR_LESS_THAN, RET_I8); 682 | 683 | progAddChild(node, lhs); 684 | progAddChild(node, rhs); 685 | 686 | verifyTypes(prog, node, lhs, rhs); 687 | } 688 | 689 | expression(node) ::= expression(lhs) EXPR_AND(op) expression(rhs). { 690 | node = progNode(prog->id++, op, INSTR_AND, RET_I8); 691 | 692 | progAddChild(node, lhs); 693 | progAddChild(node, rhs); 694 | 695 | verifyTypes(prog, node, lhs, rhs); 696 | } 697 | 698 | expression(node) ::= expression(lhs) EXPR_OR(op) expression(rhs). { 699 | node = progNode(prog->id++, op, INSTR_OR, RET_I8); 700 | 701 | progAddChild(node, lhs); 702 | progAddChild(node, rhs); 703 | 704 | verifyTypes(prog, node, lhs, rhs); 705 | } 706 | 707 | expression(node) ::= INT_LITERAL(LIT). { 708 | node = progNode(prog->id++, LIT, INSTR_LITERAL_INT, RET_I); 709 | } 710 | 711 | expression(node) ::= SINGLE_QUOTE IDENTIFIER(tok). { 712 | node = progNode(prog->id++, tok, INSTR_LITERAL_CHAR, RET_CHAR); 713 | } 714 | 715 | expression(node) ::= STRING(tok). { 716 | node = progNode(prog->id++, tok, INSTR_STRING_LITERAL, RET_STRING); 717 | } 718 | 719 | expression(node) ::= EMPTY_STRING(tok). { 720 | tok->str = ""; 721 | node = progNode(prog->id++, tok, INSTR_STRING_LITERAL, RET_STRING); 722 | } 723 | -------------------------------------------------------------------------------- /scanner.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include "parser.h" 3 | #include "lang.h" 4 | #include "compiler.h" 5 | 6 | #define YY_USER_ACTION yyextra->size = yyleng; yyextra->col += yyleng; 7 | %} 8 | 9 | %option outfile="scanner.c" header-file="scanner.h" 10 | 11 | %option warn 12 | %option reentrant 13 | %option noyywrap 14 | %option yylineno 15 | %option outfile="scanner.c" header-file="scanner.h" 16 | %option extra-type="ExtraData*" 17 | 18 | %x STR 19 | 20 | %% 21 | 22 | \-\-.* { /* munch up the comments */ } 23 | 24 | "do" { return DO_BLOCK; } 25 | "end" { return END_BLOCK; } 26 | "if" { return IF; } 27 | "else" { return ELSE; } 28 | "elseif" { return ELSE_IF; } 29 | "break" { return BREAK; } 30 | "put" { return PUT; } 31 | "get" { return GET; } 32 | "send" { return SEND; } 33 | "print" { return PRINT; } 34 | "return" { return RETURN; } 35 | "while" { return WHILE; } 36 | "&&" { return EXPR_AND; } 37 | "||" { return EXPR_OR; } 38 | "theme" { return THEME; } 39 | "sprite:" { return SPRITE; } 40 | "pad" { return PAD; } 41 | 42 | "i16" { return I16; } 43 | "i8" { return I8; } 44 | "i16s" { return I16S; } 45 | "i8s" { return I8S; } 46 | "Char" { return CHAR; } 47 | "Chars" { return CHARS; } 48 | 49 | "#"([0-9a-f]){3} { return COLOR_CODE; } 50 | "$" { return GLOBAL; } 51 | 52 | "(" { return OPEN_PAREN; } 53 | ")" { return CLOSE_PAREN; } 54 | "[" { return OPEN_SQUARE_BRACKET; } 55 | "]" { return CLOSE_SQUARE_BRACKET; } 56 | 57 | 58 | "'" { return SINGLE_QUOTE; } 59 | "fn" { return FN; } 60 | 61 | "+" { return PLUS; } 62 | "-" { return MINUS; } 63 | "*" { return TIMES; } 64 | "." { return DOT; } 65 | "/" { return SLASH; } 66 | "%" { return MODULO; } 67 | "::" { return DEFINE; } 68 | ":" { return COLON; } 69 | "=" { return EQUALS; } 70 | "==" { return EQUALITY; } 71 | "!=" { return NOT_EQUALITY; } 72 | ">" { return GREATER_THAN; } 73 | "<" { return LESS_THAN; } 74 | "," { return COMMA; } 75 | 76 | \"\" { return EMPTY_STRING; } 77 | 78 | \" { BEGIN(STR); } 79 | [^\"]+ { return STRING; } 80 | \" { BEGIN(INITIAL); } 81 | 82 | [0-9]+ { return INT_LITERAL; } 83 | [a-zA-Z][a-zA-Z0-9_]* { return IDENTIFIER; } 84 | 85 | . 86 | \n { yyextra->line++; yyextra->col = 1; } 87 | 88 | %% 89 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../emit.h" 4 | #include "../compiler.h" 5 | 6 | START_TEST (test_from_file) 7 | { 8 | Program prog; 9 | setupCompiler(&prog); 10 | 11 | char * fileNames[] = { 12 | "examples/array.dt", 13 | "examples/assignables.dt", 14 | "examples/conds.dt", 15 | "examples/date_time.dt", 16 | "examples/fill.dt", 17 | "examples/fizzbuzz.dt", 18 | "examples/helloworld.dt", 19 | "examples/life.dt", 20 | "examples/math.dt", 21 | "examples/mouse.dt", 22 | "examples/print_digit.dt", 23 | "examples/pulser.dt", 24 | "examples/recursion.dt", 25 | "examples/sprite.dt", 26 | "examples/test.dt", 27 | "examples/type_determined.dt", 28 | "examples/vars.dt" 29 | }; 30 | 31 | char * str = "test/outX.txt"; 32 | 33 | for (int i = 0; i < 17; i++) { 34 | fromFile(&prog, fileNames[i]); 35 | str[8] = i + 48; 36 | toFile(&prog, str); 37 | 38 | parse(&prog); 39 | build_instruction_list(&prog, prog.root); 40 | ck_assert_int_eq(prog.errored, 0); 41 | 42 | resetCompiler(&prog); 43 | } 44 | } 45 | END_TEST 46 | 47 | START_TEST (test_from_string) 48 | { 49 | Program prog; 50 | setupCompiler(&prog); 51 | 52 | fromString(&prog, "fn main() do x :: i8 x = 1 + 1 end"); 53 | 54 | parse(&prog); 55 | 56 | ck_assert_int_eq(prog.errored, 0); 57 | } 58 | END_TEST 59 | 60 | 61 | Suite * lang_suite(void) 62 | { 63 | Suite * s; 64 | TCase * test_Parser; 65 | 66 | s = suite_create("parser tests"); 67 | 68 | test_Parser = tcase_create("parsing"); 69 | tcase_set_timeout(test_Parser, 100); 70 | tcase_add_test(test_Parser, test_from_file); 71 | tcase_add_test(test_Parser, test_from_string); 72 | 73 | suite_add_tcase(s, test_Parser); 74 | return s; 75 | } 76 | 77 | 78 | int main(void) 79 | { 80 | int number_failed; 81 | Suite *s; 82 | SRunner *sr; 83 | 84 | s = lang_suite(); 85 | sr = srunner_create(s); 86 | 87 | srunner_run_all(sr, CK_NORMAL); 88 | number_failed = srunner_ntests_failed(sr); 89 | srunner_free(sr); 90 | return (number_failed == 0) ? 0 : 1; 91 | } 92 | -------------------------------------------------------------------------------- /todo.org: -------------------------------------------------------------------------------- 1 | DONE 2 | 3 | Right now we define them all uniquely as labels 4 | 5 | Instead we can save space by storing them as indexes into a space 6 | in memory equal to the largest number of local variables used in a single function 7 | 8 | something like 9 | 10 | @local-heap $100 11 | 12 | @global-heap $100 13 | 14 | 15 | ------ 16 | 17 | DONE 18 | 19 | Because of how the stack works, we need to do a test that when a function ends it balances the stack. 20 | maybe it is easier to just disallow full statements that end with an unbalanced stack? 21 | 22 | :start do 23 | 10 -- +1 eventual overflow 24 | end 25 | 26 | ------ 27 | 28 | DONE 29 | 30 | I think we can get a lot of utility out of allowing functions with the same name, but different types 31 | 32 | i.e. i can define 33 | 34 | fn foo(x: i8) do 35 | return x + 10 36 | end 37 | 38 | fn foo(x: i16) do 39 | return x + 1000 40 | end 41 | 42 | but i think type `i` is probably more useful... thinking 43 | 44 | ------ 45 | 46 | DONE 47 | 48 | Eventually we should move the bits of the tokens we care about to the instruction 49 | that should help reduce the nasty node->tok->str code that looks a little weird. 50 | 51 | ------ 52 | 53 | DONE 54 | 55 | We could use a function LUT to give us used/unused errors. 56 | 57 | ------ 58 | 59 | DONE 60 | 61 | Implicit nodes should report their parents location rather then 0, 0 62 | 63 | ------ 64 | 65 | DONE - this now errors 66 | 67 | if $board[i] do 68 | Since $board[i] returns an i16, if doesn't clean up its stack. 69 | 70 | ------ 71 | 72 | DONE in template :sweaty-smile: 73 | 74 | setup clang 75 | setup test suite 76 | 77 | ------ 78 | 79 | check branches all return the same type of thing 80 | 81 | fn foo(): i8 do 82 | if 1 do 83 | return 0 84 | else 85 | -- error: failed to return a i8 here or at the end of the function 86 | end 87 | end 88 | 89 | ------- 90 | 91 | IDEA 92 | 93 | Feels a little more consistent to make all functions 94 | 95 | literally replace types with variables/constants when calling... 96 | 97 | fn foo(x: i8): i8 do... 98 | and 99 | foo(x: 123): y 100 | 101 | Maybe too different? 102 | 103 | ----- 104 | 105 | DONE - validate args and prevent calling methods with returns that are unused 106 | 107 | what can i do to make stack underflow and overflow easier to debug? 108 | can I do static analysis to tell when we are going to get one? 109 | 110 | Validating arguments got us a long way already 111 | 112 | ----- 113 | 114 | DONE 115 | 116 | right now all local variables need to be globally unique... 117 | That is really bad design. i should look up a good way to manage local luts, 118 | and also 119 | 120 | maybe functions need to track a `lut_start point` to track how far up a function body can look. 121 | 122 | ----- 123 | 124 | DONE 125 | 126 | recursion and reentry are impossible right now since it will clobber previous entries 127 | 128 | make this work: 129 | 130 | fn foo(i: i8): i8 do 131 | if i > 0 do 132 | return : i + foo(i - 1) 133 | else 134 | return : 0 135 | end 136 | end 137 | 138 | or this: 139 | 140 | fn bar(i: i8): i8 do 141 | if i == 1 do 142 | return : foo(10 + 1) 143 | else 144 | return : 0 145 | end 146 | end 147 | 148 | fn foo(i: i8) do 149 | return : bar(i) + i 150 | end 151 | 152 | fn main() do 153 | foo(1) 154 | end 155 | 156 | 157 | -------------- 158 | 159 | Alright, there are a lot more things I want, but I want to start looking at things I can prove. 160 | Basic things are good first. 161 | 162 | - When does a variable enter and exit usage? 163 | - How do I make sure that all return sites return the same thing within a function? 164 | --------------------------------------------------------------------------------