├── .gitignore ├── LICENSE ├── README.md ├── branding └── logo.png ├── bytecode.c ├── bytecode.h ├── compiler.c ├── compiler.h ├── debug ├── disassembler.c ├── disassembler.h ├── expr.c └── expr.h ├── embedding.c ├── embedding.h ├── engine.c ├── engine.h ├── gc.c ├── gc.h ├── include.h ├── object.c ├── object.h ├── package.c ├── package.h ├── parser.c ├── parser.h ├── scanner.c ├── scanner.h ├── stdlib ├── dll.c ├── file.c ├── io.c ├── math.c ├── os.c ├── picStdlib.h ├── random.c ├── str.c └── time.c ├── typecheck.c ├── typecheck.h ├── util ├── dynarray.h ├── file.c ├── file.h ├── hashmap.h ├── memory.c ├── memory.h ├── strutil.c └── strutil.h ├── value.c └── value.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 PiccoloLang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Piccolo programming language 5 | [![](https://discordapp.com/api/guilds/934984567411068948/widget.png?style=shield)](https://discord.gg/ZW8VwQCfGs) 6 | 7 | A fun, easy to embed high-level programming language. 8 | 9 | ### Credits 10 | 11 | Main programming - Nikos Plugachev, Emily Banerjee
12 | Logo Design - Oliver Huang -------------------------------------------------------------------------------- /branding/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PiccoloLang/piccolo/75f2da2a23097a3d126ccdf075b6594b77796ad9/branding/logo.png -------------------------------------------------------------------------------- /bytecode.c: -------------------------------------------------------------------------------- 1 | 2 | #include "bytecode.h" 3 | 4 | PICCOLO_DYNARRAY_IMPL(uint8_t, Byte) 5 | PICCOLO_DYNARRAY_IMPL(int, Int) 6 | 7 | void piccolo_initBytecode(struct piccolo_Bytecode* bytecode) { 8 | piccolo_initByteArray(&bytecode->code); 9 | piccolo_initIntArray(&bytecode->charIdxs); 10 | piccolo_initValueArray(&bytecode->constants); 11 | } 12 | 13 | void piccolo_freeBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode) { 14 | piccolo_freeByteArray(engine, &bytecode->code); 15 | piccolo_freeIntArray(engine, &bytecode->charIdxs); 16 | piccolo_freeValueArray(engine, &bytecode->constants); 17 | } 18 | 19 | void piccolo_writeBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, uint8_t byte, int charIdx) { 20 | piccolo_writeByteArray(engine, &bytecode->code, byte); 21 | piccolo_writeIntArray(engine, &bytecode->charIdxs, charIdx); 22 | } 23 | 24 | void piccolo_writeParameteredBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, uint8_t byte, uint16_t param, int charIdx) { 25 | piccolo_writeBytecode(engine, bytecode, byte, charIdx); 26 | piccolo_writeBytecode(engine, bytecode, (param & 0xFF00) >> 8, charIdx); 27 | piccolo_writeBytecode(engine, bytecode, (param & 0x00FF) >> 0, charIdx); 28 | } 29 | 30 | int piccolo_writeConst(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, piccolo_Value constant, int charIdx) { 31 | int constIdx = bytecode->constants.count; 32 | piccolo_writeValueArray(engine, &bytecode->constants, constant); 33 | piccolo_writeParameteredBytecode(engine, bytecode, PICCOLO_OP_CONST, constIdx, charIdx); 34 | return constIdx; 35 | } 36 | 37 | void piccolo_patchParam(struct piccolo_Bytecode* bytecode, int addr, uint16_t param) { 38 | bytecode->code.values[addr + 1] = (param & 0xFF00) >> 8; 39 | bytecode->code.values[addr + 2] = (param & 0x00FF) >> 0; 40 | } 41 | -------------------------------------------------------------------------------- /bytecode.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_BYTECODE_H 3 | #define PICCOLO_BYTECODE_H 4 | 5 | #include 6 | #include 7 | 8 | #include "util/dynarray.h" 9 | #include "value.h" 10 | 11 | enum piccolo_OpCode { 12 | PICCOLO_OP_RETURN, 13 | PICCOLO_OP_CONST, 14 | PICCOLO_OP_ADD, PICCOLO_OP_SUB, PICCOLO_OP_MUL, PICCOLO_OP_DIV, PICCOLO_OP_MOD, 15 | PICCOLO_OP_EQUAL, PICCOLO_OP_GREATER, PICCOLO_OP_LESS, 16 | PICCOLO_OP_NEGATE, PICCOLO_OP_NOT, 17 | 18 | PICCOLO_OP_POP_STACK, 19 | PICCOLO_OP_PEEK_STACK, 20 | PICCOLO_OP_SWAP_STACK, 21 | 22 | PICCOLO_OP_CREATE_ARRAY, 23 | PICCOLO_OP_CREATE_RANGE, 24 | PICCOLO_OP_GET_IDX, 25 | PICCOLO_OP_SET_IDX, 26 | 27 | PICCOLO_OP_HASHMAP, 28 | 29 | PICCOLO_OP_GET_GLOBAL, 30 | PICCOLO_OP_SET_GLOBAL, 31 | PICCOLO_OP_GET_LOCAL, 32 | PICCOLO_OP_SET_LOCAL, 33 | PICCOLO_OP_PUSH_LOCAL, 34 | PICCOLO_OP_POP_LOCALS, 35 | 36 | PICCOLO_OP_JUMP, 37 | PICCOLO_OP_JUMP_FALSE, 38 | PICCOLO_OP_REV_JUMP, 39 | PICCOLO_OP_REV_JUMP_FALSE, 40 | 41 | PICCOLO_OP_CALL, 42 | 43 | PICCOLO_OP_CLOSURE, 44 | PICCOLO_OP_GET_UPVAL, 45 | PICCOLO_OP_SET_UPVAL, 46 | PICCOLO_OP_CLOSE_UPVALS, 47 | 48 | PICCOLO_OP_GET_LEN, 49 | PICCOLO_OP_APPEND, 50 | PICCOLO_OP_IN, 51 | PICCOLO_OP_ITER_FIRST, 52 | PICCOLO_OP_ITER_CONT, 53 | PICCOLO_OP_ITER_NEXT, 54 | PICCOLO_OP_ITER_GET, 55 | 56 | PICCOLO_OP_EXECUTE_PACKAGE 57 | }; 58 | 59 | PICCOLO_DYNARRAY_HEADER(uint8_t, Byte) 60 | PICCOLO_DYNARRAY_HEADER(int, Int) 61 | 62 | struct piccolo_Bytecode { 63 | struct piccolo_ByteArray code; 64 | struct piccolo_IntArray charIdxs; 65 | struct piccolo_ValueArray constants; 66 | }; 67 | 68 | void piccolo_initBytecode(struct piccolo_Bytecode* bytecode); 69 | void piccolo_freeBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode); 70 | 71 | void piccolo_writeBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, uint8_t byte, int charIdx); 72 | void piccolo_writeParameteredBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, uint8_t byte, uint16_t param, int charIdx); 73 | int piccolo_writeConst(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode, piccolo_Value value, int charIdx); 74 | void piccolo_patchParam(struct piccolo_Bytecode* bytecode, int addr, uint16_t param); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /compiler.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_COMPILER_H 3 | #define PICCOLO_COMPILER_H 4 | 5 | #include 6 | 7 | #include "scanner.h" 8 | #include "util/dynarray.h" 9 | #include "typecheck.h" 10 | #include "bytecode.h" 11 | #include "parser.h" 12 | 13 | // Maximum length of a package name 14 | #define PICCOLO_MAX_PACKAGE 4096 15 | 16 | struct piccolo_Engine; 17 | struct piccolo_Package; 18 | 19 | struct piccolo_Variable { 20 | int slot; 21 | const char* nameStart; 22 | size_t nameLen; 23 | bool Mutable; 24 | struct puccolo_ExprNode* decl; 25 | }; 26 | 27 | struct piccolo_Upvalue { 28 | int slot; 29 | bool local; 30 | bool Mutable; 31 | struct piccolo_VarDeclNode* decl; 32 | }; 33 | 34 | struct piccolo_VarData { 35 | int slot; 36 | enum piccolo_OpCode setOp; 37 | enum piccolo_OpCode getOp; 38 | bool Mutable; 39 | struct piccolo_VarDeclNode* decl; 40 | }; 41 | 42 | PICCOLO_DYNARRAY_HEADER(struct piccolo_Variable, Variable) 43 | PICCOLO_DYNARRAY_HEADER(struct piccolo_Upvalue, Upvalue) 44 | 45 | struct piccolo_Compiler { 46 | struct piccolo_Package* package; 47 | struct piccolo_VariableArray* globals; 48 | struct piccolo_VariableArray locals; 49 | struct piccolo_UpvalueArray upvals; 50 | struct piccolo_Compiler* enclosing; 51 | bool hadError; 52 | }; 53 | 54 | struct piccolo_Package* piccolo_resolvePackage(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, const char* sourceFilepath, const char* name, size_t nameLen); 55 | struct piccolo_Package* piccolo_resolveImport(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, const char* sourceFilepath, struct piccolo_ImportNode* import); 56 | 57 | void piccolo_compilationError(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, int charIdx, const char* format, ...); 58 | struct piccolo_VarData piccolo_getVariable(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_Token name); 59 | bool piccolo_compilePackage(struct piccolo_Engine* engine, struct piccolo_Package* package); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /debug/disassembler.c: -------------------------------------------------------------------------------- 1 | 2 | #include "disassembler.h" 3 | #include 4 | 5 | static uint16_t getInstructionParam(struct piccolo_Bytecode* bytecode, int offset) { 6 | return (bytecode->code.values[offset + 1] << 8) + (bytecode->code.values[offset + 2] << 0); 7 | } 8 | 9 | int piccolo_disassembleInstruction(struct piccolo_Bytecode* bytecode, int offset) { 10 | #define SIMPLE_INSTRUCTION(opcode) \ 11 | case PICCOLO_ ## opcode: { \ 12 | printf(#opcode "\n"); \ 13 | return offset + 1; \ 14 | } 15 | #define PARAM_INSTRUCTION(opcode) \ 16 | case PICCOLO_ ## opcode: { \ 17 | printf(#opcode " %d\n", getInstructionParam(bytecode, offset)); \ 18 | return offset + 3; \ 19 | } 20 | 21 | printf("%4d | ", offset); 22 | switch(bytecode->code.values[offset]) { 23 | SIMPLE_INSTRUCTION(OP_RETURN) 24 | case PICCOLO_OP_CONST: { 25 | printf("OP_CONST { "); 26 | piccolo_printValue(bytecode->constants.values[getInstructionParam(bytecode, offset)]); 27 | printf(" }\n"); 28 | return offset + 3; 29 | } 30 | SIMPLE_INSTRUCTION(OP_ADD) 31 | SIMPLE_INSTRUCTION(OP_SUB) 32 | SIMPLE_INSTRUCTION(OP_MUL) 33 | SIMPLE_INSTRUCTION(OP_DIV) 34 | SIMPLE_INSTRUCTION(OP_MOD) 35 | 36 | SIMPLE_INSTRUCTION(OP_EQUAL) 37 | SIMPLE_INSTRUCTION(OP_GREATER) 38 | SIMPLE_INSTRUCTION(OP_LESS) 39 | SIMPLE_INSTRUCTION(OP_NEGATE) 40 | SIMPLE_INSTRUCTION(OP_NOT) 41 | 42 | SIMPLE_INSTRUCTION(OP_POP_STACK) 43 | PARAM_INSTRUCTION(OP_PEEK_STACK) 44 | SIMPLE_INSTRUCTION(OP_SWAP_STACK) 45 | 46 | PARAM_INSTRUCTION(OP_CREATE_ARRAY) 47 | SIMPLE_INSTRUCTION(OP_CREATE_RANGE) 48 | SIMPLE_INSTRUCTION(OP_GET_IDX) 49 | SIMPLE_INSTRUCTION(OP_SET_IDX) 50 | 51 | SIMPLE_INSTRUCTION(OP_HASHMAP) 52 | 53 | PARAM_INSTRUCTION(OP_GET_GLOBAL) 54 | PARAM_INSTRUCTION(OP_SET_GLOBAL) 55 | PARAM_INSTRUCTION(OP_GET_LOCAL) 56 | PARAM_INSTRUCTION(OP_SET_LOCAL) 57 | SIMPLE_INSTRUCTION(OP_PUSH_LOCAL) 58 | PARAM_INSTRUCTION(OP_POP_LOCALS) 59 | 60 | PARAM_INSTRUCTION(OP_JUMP) 61 | PARAM_INSTRUCTION(OP_JUMP_FALSE) 62 | PARAM_INSTRUCTION(OP_REV_JUMP) 63 | PARAM_INSTRUCTION(OP_REV_JUMP_FALSE) 64 | 65 | PARAM_INSTRUCTION(OP_CALL) 66 | 67 | case PICCOLO_OP_CLOSURE: { 68 | int upvals = getInstructionParam(bytecode, offset); 69 | printf("OP_CLOSURE [ "); 70 | for(int i = 0; i < upvals; i++) 71 | printf("%d ", getInstructionParam(bytecode, offset + 2 + 3 * i)); 72 | printf("]\n"); 73 | return offset + 3 + 3 * upvals; 74 | } 75 | PARAM_INSTRUCTION(OP_GET_UPVAL) 76 | PARAM_INSTRUCTION(OP_SET_UPVAL) 77 | SIMPLE_INSTRUCTION(OP_CLOSE_UPVALS) 78 | 79 | SIMPLE_INSTRUCTION(OP_GET_LEN) 80 | SIMPLE_INSTRUCTION(OP_APPEND) 81 | SIMPLE_INSTRUCTION(OP_IN) 82 | SIMPLE_INSTRUCTION(OP_ITER_CONT) 83 | PARAM_INSTRUCTION(OP_ITER_NEXT) 84 | SIMPLE_INSTRUCTION(OP_ITER_GET) 85 | 86 | SIMPLE_INSTRUCTION(OP_EXECUTE_PACKAGE) 87 | } 88 | printf("Unknown Opcode.\n"); 89 | return offset + 1; 90 | #undef SIMPLE_INSTRUCTION 91 | } 92 | 93 | void piccolo_disassembleBytecode(struct piccolo_Bytecode* bytecode) { 94 | int currOffset = 0; 95 | 96 | while(currOffset < bytecode->code.count) 97 | currOffset = piccolo_disassembleInstruction(bytecode, currOffset); 98 | } 99 | -------------------------------------------------------------------------------- /debug/disassembler.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_DISASSEMBLER_H 3 | #define PICCOLO_DISASSEMBLER_H 4 | 5 | #include "../bytecode.h" 6 | 7 | int piccolo_disassembleInstruction(struct piccolo_Bytecode* bytecode, int offset); 8 | void piccolo_disassembleBytecode(struct piccolo_Bytecode* bytecode); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /debug/expr.c: -------------------------------------------------------------------------------- 1 | 2 | #include "expr.h" 3 | #include "../typecheck.h" 4 | 5 | #include 6 | 7 | void piccolo_printExpr(struct piccolo_ExprNode* expr, int offset) { 8 | if(expr == NULL) 9 | return; 10 | 11 | char typeBuf[256]; 12 | piccolo_getTypename(expr->resultType, typeBuf); 13 | 14 | printf(expr->reqEval ? "[X] " : "[ ] "); 15 | for(int i = 0; i < offset; i++) 16 | printf(" "); 17 | switch(expr->type) { 18 | case PICCOLO_EXPR_LITERAL: { 19 | struct piccolo_LiteralNode* literal = (struct piccolo_LiteralNode*)expr; 20 | printf("LITERAL %.*s -> %s\n", (int)literal->token.length, literal->token.start, typeBuf); 21 | break; 22 | } 23 | case PICCOLO_EXPR_VAR: { 24 | struct piccolo_VarNode* var = (struct piccolo_VarNode*)expr; 25 | printf("VAR %.*s -> %s\n", (int)var->name.length, var->name.start, typeBuf); 26 | break; 27 | } 28 | case PICCOLO_EXPR_RANGE: { 29 | struct piccolo_RangeNode* range = (struct piccolo_RangeNode*)expr; 30 | printf("RANGE -> %s\n", typeBuf); 31 | piccolo_printExpr(range->left, offset + 1); 32 | piccolo_printExpr(range->right, offset + 1); 33 | break; 34 | } 35 | case PICCOLO_EXPR_ARRAY_LITERAL: { 36 | struct piccolo_ArrayLiteralNode* arrayLiteral = (struct piccolo_ArrayLiteralNode*)expr; 37 | printf("ARRAY LITERAL -> %s\n", typeBuf); 38 | piccolo_printExpr(arrayLiteral->first, offset + 1); 39 | break; 40 | } 41 | case PICCOLO_EXPR_HASHMAP_ENTRY: { 42 | struct piccolo_HashmapEntryNode* entry = (struct piccolo_HashmapEntryNode*)expr; 43 | printf("HASHMAP ENTRY\n"); 44 | for(int i = 0; i < offset + 2; i++) 45 | printf(" "); 46 | printf("KEY ->\n"); 47 | piccolo_printExpr(entry->key, offset + 2); 48 | for(int i = 0; i < offset + 2; i++) 49 | printf(" "); 50 | printf("VALUE ->\n"); 51 | piccolo_printExpr(entry->value, offset + 2); 52 | break; 53 | } 54 | case PICCOLO_EXPR_HASHMAP_LITERAL: { 55 | struct piccolo_HashmapLiteralNode* hashmap = (struct piccolo_HashmapLiteralNode*)expr; 56 | printf("HASHMAP -> %s\n", typeBuf); 57 | piccolo_printExpr((struct piccolo_ExprNode*)hashmap->first, offset + 1); 58 | break; 59 | } 60 | case PICCOLO_EXPR_SUBSCRIPT: { 61 | struct piccolo_SubscriptNode* subscript = (struct piccolo_SubscriptNode*)expr; 62 | printf("SUBSCRIPT %.*s -> %s\n", (int)subscript->subscript.length, subscript->subscript.start, typeBuf); 63 | piccolo_printExpr(subscript->value, offset + 1); 64 | break; 65 | } 66 | case PICCOLO_EXPR_INDEX: { 67 | struct piccolo_IndexNode* index = (struct piccolo_IndexNode*)expr; 68 | printf("INDEX -> %s\n", typeBuf); 69 | for(int i = 0; i < offset + 2; i++) 70 | printf(" "); 71 | printf("VALUE\n"); 72 | piccolo_printExpr(index->target, offset + 2); 73 | for(int i = 0; i < offset + 2; i++) 74 | printf(" "); 75 | printf("INDEX\n"); 76 | piccolo_printExpr(index->index, offset + 2); 77 | break; 78 | } 79 | case PICCOLO_EXPR_UNARY: { 80 | struct piccolo_UnaryNode* unary = (struct piccolo_UnaryNode*)expr; 81 | printf("UNARY %.*s -> %s\n", (int)unary->op.length, unary->op.start, typeBuf); 82 | piccolo_printExpr(unary->value, offset + 1); 83 | break; 84 | } 85 | case PICCOLO_EXPR_BINARY: { 86 | struct piccolo_BinaryNode* binary = (struct piccolo_BinaryNode*)expr; 87 | printf("BINARY %.*s -> %s\n", (int)binary->op.length, binary->op.start, typeBuf); 88 | piccolo_printExpr(binary->a, offset + 1); 89 | piccolo_printExpr(binary->b, offset + 1); 90 | break; 91 | } 92 | case PICCOLO_EXPR_BLOCK: { 93 | struct piccolo_BlockNode* block = (struct piccolo_BlockNode*)expr; 94 | printf("BLOCK -> %s\n", typeBuf); 95 | piccolo_printExpr(block->first, offset + 1); 96 | break; 97 | } 98 | case PICCOLO_EXPR_FN_LITERAL: { 99 | struct piccolo_FnLiteralNode* fnLiteral = (struct piccolo_FnLiteralNode*)expr; 100 | printf("FN_LITERAL "); 101 | for(int i = 0; i < fnLiteral->params.count; i++) 102 | printf("%.*s ", (int)fnLiteral->params.values[i].length, fnLiteral->params.values[i].start); 103 | printf(" -> %s\n", typeBuf); 104 | piccolo_printExpr(fnLiteral->value, offset + 1); 105 | break; 106 | } 107 | case PICCOLO_EXPR_VAR_DECL: { 108 | struct piccolo_VarDeclNode* varDecl = (struct piccolo_VarDeclNode*)expr; 109 | printf("VAR DECL %.*s -> %s\n", (int)varDecl->name.length, varDecl->name.start, typeBuf); 110 | piccolo_printExpr(varDecl->value, offset + 1); 111 | break; 112 | } 113 | case PICCOLO_EXPR_VAR_SET: { 114 | struct piccolo_VarSetNode* varSet = (struct piccolo_VarSetNode*)expr; 115 | printf("VAR SET %.*s -> %s\n", (int)varSet->name.length, varSet->name.start, typeBuf); 116 | piccolo_printExpr(varSet->value, offset + 1); 117 | break; 118 | } 119 | case PICCOLO_EXPR_SUBSCRIPT_SET: { 120 | struct piccolo_SubscriptSetNode* subscriptSet = (struct piccolo_SubscriptSetNode*)expr; 121 | printf("SUBSCRIPT SET %.*s -> %s\n", (int)subscriptSet->subscript.length, subscriptSet->subscript.start, typeBuf); 122 | for(int i = 0; i < offset + 2; i++) 123 | printf(" "); 124 | printf("TARGET\n"); 125 | piccolo_printExpr(subscriptSet->target, offset + 2); 126 | for(int i = 0; i < offset + 2; i++) 127 | printf(" "); 128 | printf("VALUE\n"); 129 | piccolo_printExpr(subscriptSet->value, offset + 2); 130 | break; 131 | } 132 | case PICCOLO_EXPR_INDEX_SET: { 133 | struct piccolo_IndexSetNode* indexSet = (struct piccolo_IndexSetNode*)expr; 134 | printf("INDEX SET -> %s\n", typeBuf); 135 | for(int i = 0; i < offset + 2; i++) 136 | printf(" "); 137 | printf("TARGET\n"); 138 | piccolo_printExpr(indexSet->target, offset + 2); 139 | for(int i = 0; i < offset + 2; i++) 140 | printf(" "); 141 | printf("INDEX\n"); 142 | piccolo_printExpr(indexSet->index, offset + 2); 143 | for(int i = 0; i < offset + 2; i++) 144 | printf(" "); 145 | printf("VALUE\n"); 146 | piccolo_printExpr(indexSet->value, offset + 2); 147 | break; 148 | } 149 | case PICCOLO_EXPR_IF: { 150 | struct piccolo_IfNode* ifNode = (struct piccolo_IfNode*)expr; 151 | printf("IF -> %s\n", typeBuf); 152 | piccolo_printExpr(ifNode->condition, offset + 1); 153 | for(int i = 0; i <= offset + 1; i++) 154 | printf(" "); 155 | printf("TRUE EXPR\n"); 156 | piccolo_printExpr(ifNode->trueVal, offset + 2); 157 | if(ifNode->falseVal != NULL) { 158 | for(int i = 0; i <= offset + 1; i++) 159 | printf(" "); 160 | printf("FALSE EXPR\n"); 161 | piccolo_printExpr(ifNode->falseVal, offset + 2); 162 | } 163 | break; 164 | } 165 | case PICCOLO_EXPR_WHILE: { 166 | struct piccolo_WhileNode* whileNode = (struct piccolo_WhileNode*)expr; 167 | printf("WHILE -> %s\n", typeBuf); 168 | for(int i = 0; i <= offset + 1; i++) 169 | printf(" "); 170 | printf("CONDITION\n"); 171 | piccolo_printExpr(whileNode->condition, offset + 2); 172 | for(int i = 0; i <= offset + 1; i++) 173 | printf(" "); 174 | printf("VALUE\n"); 175 | piccolo_printExpr(whileNode->value, offset + 2); 176 | break; 177 | } 178 | case PICCOLO_EXPR_FOR: { 179 | struct piccolo_ForNode* forNode = (struct piccolo_ForNode*)expr; 180 | printf("FOR %.*s -> %s\n", (int)forNode->name.length, forNode->name.start, typeBuf); 181 | for(int i = 0; i <= offset + 1; i++) 182 | printf(" "); 183 | printf("IN\n"); 184 | piccolo_printExpr(forNode->container, offset + 2); 185 | for(int i = 0; i <= offset + 1; i++) 186 | printf(" "); 187 | printf("VALUE\n"); 188 | piccolo_printExpr(forNode->value, offset + 2); 189 | break; 190 | } 191 | case PICCOLO_EXPR_CALL: { 192 | struct piccolo_CallNode* call = (struct piccolo_CallNode*)expr; 193 | printf("CALL -> %s\n", typeBuf); 194 | for(int i = 0; i < offset + 2; i++) 195 | printf(" "); 196 | printf("FUNC\n"); 197 | piccolo_printExpr(call->function, offset + 2); 198 | for(int i = 0; i < offset + 2; i++) 199 | printf(" "); 200 | printf("ARGS\n"); 201 | piccolo_printExpr(call->firstArg, offset + 2); 202 | break; 203 | } 204 | case PICCOLO_EXPR_IMPORT: { 205 | struct piccolo_ImportNode* import = (struct piccolo_ImportNode*)expr; 206 | printf("IMPORT %.*s -> %s\n", (int)import->packageName.length, import->packageName.start, typeBuf); 207 | break; 208 | } 209 | default: { 210 | printf("UNKNOWN AST TYPE\n"); 211 | } 212 | } 213 | 214 | if(expr->nextExpr != NULL) { 215 | piccolo_printExpr(expr->nextExpr, offset); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /debug/expr.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_EXPR_H 3 | #define PICCOLO_EXPR_H 4 | 5 | #include "../parser.h" 6 | #include "../typecheck.h" 7 | 8 | void piccolo_printExpr(struct piccolo_ExprNode* expr, int offset); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /embedding.c: -------------------------------------------------------------------------------- 1 | 2 | #include "embedding.h" 3 | 4 | #include 5 | 6 | void piccolo_addSearchPath(struct piccolo_Engine* engine, const char* path) { 7 | if(path[strlen(path) - 1] != '/') { 8 | // TODO: This should be fixed in the CLI once the package path changes are merged 9 | piccolo_enginePrintError(engine, "Incorrectly formatted package path '%s'\n", path); 10 | return; 11 | } 12 | piccolo_writeStringArray(engine, &engine->searchPaths, path); 13 | } 14 | 15 | void piccolo_defineGlobalWithNameSize(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, size_t nameLen, struct piccolo_Value value, struct piccolo_Type* type) { 16 | struct piccolo_ObjString* varName = piccolo_copyString(engine, name, nameLen); 17 | piccolo_setGlobalTable(engine, &package->globalIdxs, varName, package->globals.count); 18 | piccolo_writeValueArray(engine, &package->globals, value); 19 | piccolo_writeTypeArray(engine, &package->types, type); 20 | } 21 | 22 | void piccolo_defineGlobal(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, struct piccolo_Value value) { 23 | piccolo_defineGlobalWithNameSize(engine, package, name, strlen(name), value, piccolo_simpleType(engine, PICCOLO_TYPE_ANY)); 24 | } 25 | 26 | void piccolo_defineGlobalWithType(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, struct piccolo_Value value, struct piccolo_Type* type) { 27 | piccolo_defineGlobalWithNameSize(engine, package, name, strlen(name), value, type); 28 | } 29 | 30 | piccolo_Value piccolo_getGlobalWithNameSize(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, size_t nameLen) { 31 | struct piccolo_ObjString* varName = piccolo_copyString(engine, name, nameLen); 32 | int idx = piccolo_getGlobalTable(engine, &package->globalIdxs, varName); 33 | if(idx == -1) { 34 | return PICCOLO_NIL_VAL(); // TODO: There needs to be some way to dilliniate actual nil from errors 35 | } 36 | return package->globals.values[idx]; 37 | } 38 | 39 | piccolo_Value piccolo_getGlobal(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name) { 40 | return piccolo_getGlobalWithNameSize(engine, package, name, strlen(name)); 41 | } 42 | 43 | struct piccolo_Type* piccolo_makeFnType(struct piccolo_Engine* engine, struct piccolo_Type* resultType, int nParams, ...) { 44 | va_list args; 45 | va_start(args, nParams); 46 | struct piccolo_TypeArray paramTypes; 47 | piccolo_initTypeArray(¶mTypes); 48 | for(int i = 0; i < nParams; i++) 49 | piccolo_writeTypeArray(engine, ¶mTypes, va_arg(args, struct piccolo_Type*)); 50 | va_end(args); 51 | return piccolo_fnType(engine, ¶mTypes, resultType); 52 | } -------------------------------------------------------------------------------- /embedding.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_EMBEDDING_H 3 | #define PICCOLO_EMBEDDING_H 4 | 5 | #include "engine.h" 6 | #include "gc.h" 7 | #include "typecheck.h" 8 | 9 | void piccolo_addSearchPath(struct piccolo_Engine* engine, const char* path); 10 | void piccolo_defineGlobalWithNameSize(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, size_t nameLen, struct piccolo_Value value, struct piccolo_Type* type); 11 | void piccolo_defineGlobal(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, struct piccolo_Value value); 12 | void piccolo_defineGlobalWithType(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, struct piccolo_Value value, struct piccolo_Type* type); 13 | 14 | piccolo_Value piccolo_getGlobalWithNameSize(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name, size_t nameLen); 15 | piccolo_Value piccolo_getGlobal(struct piccolo_Engine* engine, struct piccolo_Package* package, const char* name); 16 | 17 | struct piccolo_Type* piccolo_makeFnType(struct piccolo_Engine* engine, struct piccolo_Type* resultType, int nParams, ...); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /engine.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_ENGINE_H 3 | #define PICCOLO_ENGINE_H 4 | 5 | #include "package.h" 6 | #include "object.h" 7 | #include "typecheck.h" 8 | #include 9 | #include 10 | 11 | struct piccolo_CallFrame { 12 | int localStart; 13 | int prevIp; 14 | int ip; 15 | struct piccolo_Bytecode* bytecode; 16 | struct piccolo_ObjClosure* closure; 17 | struct piccolo_Package* package; 18 | }; 19 | 20 | PICCOLO_DYNARRAY_HEADER(struct piccolo_Package*, Package) 21 | PICCOLO_DYNARRAY_HEADER(const char*, String) 22 | PICCOLO_DYNARRAY_HEADER(struct piccolo_CallFrame, CallFrame) 23 | 24 | struct piccolo_Engine { 25 | struct piccolo_PackageArray packages; 26 | 27 | struct piccolo_ValueArray locals; 28 | piccolo_Value stack[256]; 29 | piccolo_Value* stackTop; 30 | struct piccolo_CallFrameArray callFrames; 31 | bool hadError; 32 | 33 | size_t liveMemory; 34 | size_t gcThreshold; 35 | struct piccolo_Obj* objs; 36 | 37 | void (*printError)(const char* format, va_list); 38 | 39 | struct piccolo_Package* (*findPackage)(struct piccolo_Engine*, struct piccolo_Compiler* compiler, const char* sourceFilepath, const char* name, size_t nameLen); 40 | 41 | struct piccolo_ObjUpval* openUpvals; 42 | 43 | struct piccolo_StringArray searchPaths; 44 | struct piccolo_Type* types; 45 | #ifdef PICCOLO_ENABLE_MEMORY_TRACKER 46 | struct piccolo_MemoryTrack* track; 47 | #endif 48 | }; 49 | 50 | void piccolo_initEngine(struct piccolo_Engine* engine, void (*printError)(const char* format, va_list)); 51 | void piccolo_freeEngine(struct piccolo_Engine* engine); 52 | 53 | bool piccolo_executePackage(struct piccolo_Engine* engine, struct piccolo_Package* package); 54 | bool piccolo_executeBytecode(struct piccolo_Engine* engine, struct piccolo_Bytecode* bytecode); 55 | piccolo_Value piccolo_callFunction(struct piccolo_Engine* engine, struct piccolo_ObjClosure* closure, int argc, piccolo_Value* argv); 56 | 57 | void piccolo_enginePrintError(struct piccolo_Engine* engine, const char* format, ...); 58 | 59 | void piccolo_enginePushStack(struct piccolo_Engine* engine, piccolo_Value value); 60 | piccolo_Value piccolo_enginePopStack(struct piccolo_Engine* engine); 61 | piccolo_Value piccolo_enginePeekStack(struct piccolo_Engine* engine, int dist); 62 | 63 | void piccolo_runtimeError(struct piccolo_Engine* engine, const char* format, ...); 64 | 65 | #ifdef PICCOLO_ENABLE_MEMORY_TRACKER 66 | void piccolo_freeMemTracks(struct piccolo_Engine* engine); 67 | #endif 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /gc.c: -------------------------------------------------------------------------------- 1 | 2 | #include "gc.h" 3 | #include 4 | 5 | static void markObj(struct piccolo_Obj* obj) { 6 | if(obj == NULL) 7 | return; 8 | if(obj->marked) 9 | return; 10 | obj->marked = true; 11 | switch(obj->type) { 12 | case PICCOLO_OBJ_ARRAY: { 13 | struct piccolo_ObjArray* array = (struct piccolo_ObjArray*)obj; 14 | for(int i = 0; i < array->array.count; i++) 15 | piccolo_gcMarkValue(array->array.values[i]); 16 | break; 17 | } 18 | case PICCOLO_OBJ_HASHMAP: { 19 | struct piccolo_ObjHashmap* hashmap = (struct piccolo_ObjHashmap*)obj; 20 | for(int i = 0; i < hashmap->hashmap.capacity; i++) { 21 | if(!piccolo_HashmapIsBaseKey(hashmap->hashmap.entries[i].key)) { 22 | piccolo_gcMarkValue(hashmap->hashmap.entries[i].key); 23 | piccolo_gcMarkValue(hashmap->hashmap.entries[i].val.value); 24 | } 25 | } 26 | break; 27 | } 28 | case PICCOLO_OBJ_FUNC: { 29 | struct piccolo_ObjFunction* func = (struct piccolo_ObjFunction*)obj; 30 | for(int i = 0; i < func->bytecode.constants.count; i++) 31 | piccolo_gcMarkValue(func->bytecode.constants.values[i]); 32 | break; 33 | } 34 | case PICCOLO_OBJ_UPVAL: { 35 | struct piccolo_ObjUpval* upval = (struct piccolo_ObjUpval*)obj; 36 | if(!upval->open) 37 | piccolo_gcMarkValue(*upval->val.ptr); 38 | break; 39 | } 40 | case PICCOLO_OBJ_CLOSURE: { 41 | struct piccolo_ObjClosure* closure = (struct piccolo_ObjClosure*)obj; 42 | markObj((struct piccolo_Obj*) closure->prototype); 43 | for(int i = 0; i < closure->upvalCnt; i++) { 44 | markObj((struct piccolo_Obj*) closure->upvals[i]); 45 | } 46 | break; 47 | } 48 | case PICCOLO_OBJ_NATIVE_STRUCT: { 49 | struct piccolo_ObjNativeStruct* nativeStruct = (struct piccolo_ObjNativeStruct*)obj; 50 | if(nativeStruct->gcMark != NULL) 51 | nativeStruct->gcMark(PICCOLO_GET_PAYLOAD(obj, void)); 52 | break; 53 | } 54 | case PICCOLO_OBJ_STRING: break; 55 | case PICCOLO_OBJ_NATIVE_FN: break; 56 | case PICCOLO_OBJ_PACKAGE: break; 57 | } 58 | } 59 | 60 | void piccolo_gcMarkValue(piccolo_Value value) { 61 | if(PICCOLO_IS_OBJ(value)) { 62 | markObj(PICCOLO_AS_OBJ(value)); 63 | } 64 | } 65 | 66 | static void markPackage(struct piccolo_Package* package) { 67 | package->obj.marked = true; 68 | for(int i = 0; i < package->bytecode.constants.count; i++) 69 | piccolo_gcMarkValue(package->bytecode.constants.values[i]); 70 | for(int i = 0; i < package->globals.count; i++) 71 | piccolo_gcMarkValue(package->globals.values[i]); 72 | for(int i = 0; i < package->globalIdxs.capacity; i++) 73 | if(package->globalIdxs.entries[i].key != NULL) 74 | package->globalIdxs.entries[i].key->obj.marked = true; 75 | } 76 | 77 | static void markRoots(struct piccolo_Engine* engine) { 78 | for(piccolo_Value* iter = engine->stack; iter != engine->stackTop; iter++) 79 | piccolo_gcMarkValue(*iter); 80 | for(int i = 0; i < engine->callFrames.count; i++) { 81 | if(engine->callFrames.values[i].closure != NULL) 82 | markObj((struct piccolo_Obj*)engine->callFrames.values[i].closure); 83 | } 84 | for(int i = 0; i < engine->locals.count; i++) 85 | piccolo_gcMarkValue(engine->locals.values[i]); 86 | for(int i = 0; i < engine->packages.count; i++) 87 | markPackage(engine->packages.values[i]); 88 | } 89 | 90 | void piccolo_collectGarbage(struct piccolo_Engine* engine) { 91 | 92 | struct piccolo_Obj* currObj = engine->objs; 93 | while(currObj != NULL) { 94 | currObj->marked = false; 95 | currObj = currObj->next; 96 | } 97 | markRoots(engine); 98 | 99 | struct piccolo_Obj* newObjs = NULL; 100 | currObj = engine->objs; 101 | int liveObjs = 0; 102 | while(currObj != NULL) { 103 | struct piccolo_Obj* curr = currObj; 104 | currObj = currObj->next; 105 | if(curr->marked) { 106 | liveObjs++; 107 | curr->next = newObjs; 108 | newObjs = curr; 109 | } else { 110 | piccolo_freeObj(engine, curr); 111 | } 112 | } 113 | 114 | engine->objs = newObjs; 115 | } 116 | -------------------------------------------------------------------------------- /gc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_GC_H 3 | #define PICCOLO_GC_H 4 | 5 | #include "engine.h" 6 | 7 | void piccolo_gcMarkValue(piccolo_Value value); 8 | void piccolo_collectGarbage(struct piccolo_Engine* engine); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_INCLUDE_H 3 | #define PICCOLO_INCLUDE_H 4 | 5 | #include "engine.h" 6 | #include "package.h" 7 | #include "value.h" 8 | #include "embedding.h" 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /object.c: -------------------------------------------------------------------------------- 1 | 2 | #include "object.h" 3 | #include "util/memory.h" 4 | #include "engine.h" 5 | #include "package.h" 6 | #include "util/strutil.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | uint32_t piccolo_hashHashmapKey(piccolo_Value key) { 13 | if(PICCOLO_IS_NUM(key)) { 14 | double val = PICCOLO_AS_NUM(key); 15 | int32_t i; 16 | val = frexp(val, &i) * -(double)INT_MIN; 17 | int32_t ni = (int32_t)val; 18 | uint32_t u = (uint32_t)i + (uint32_t)ni; 19 | return u <= INT_MAX ? u : ~u; 20 | } 21 | if(PICCOLO_IS_BOOL(key)) { 22 | return 1000000007 * PICCOLO_AS_BOOL(key); 23 | } 24 | if(PICCOLO_IS_OBJ(key)) { 25 | struct piccolo_Obj* keyObj = PICCOLO_AS_OBJ(key); 26 | if(keyObj->type == PICCOLO_OBJ_STRING) { 27 | return ((struct piccolo_ObjString*)keyObj)->hash; 28 | } 29 | } 30 | return 0; 31 | } 32 | 33 | bool piccolo_compareHashmapKeys(piccolo_Value a, piccolo_Value b) { 34 | return piccolo_valuesEqual(a, b); 35 | } 36 | 37 | bool piccolo_HashmapIsBaseKey(piccolo_Value key) { 38 | return PICCOLO_IS_NIL(key); 39 | } 40 | 41 | static inline struct piccolo_HashmapValue getBase() { 42 | struct piccolo_HashmapValue result; 43 | result.exists = false; 44 | return result; 45 | } 46 | 47 | PICCOLO_HASHMAP_IMPL(piccolo_Value, struct piccolo_HashmapValue, Hashmap, PICCOLO_NIL_VAL(), (getBase())) 48 | 49 | bool piccolo_isObjOfType(piccolo_Value val, enum piccolo_ObjType type) { 50 | return PICCOLO_IS_OBJ(val) && PICCOLO_AS_OBJ(val)->type == type; 51 | } 52 | 53 | struct piccolo_Obj* allocateObj(struct piccolo_Engine* engine, enum piccolo_ObjType type, size_t size) { 54 | struct piccolo_Obj* obj = PICCOLO_REALLOCATE("obj", engine, NULL, 0, size); 55 | obj->next = engine->objs; 56 | engine->objs = obj; 57 | obj->type = type; 58 | obj->printed = false; 59 | return obj; 60 | } 61 | 62 | struct piccolo_ObjNativeStruct* piccolo_allocNativeStruct(struct piccolo_Engine* engine, size_t size, const char* Typename) { 63 | struct piccolo_ObjNativeStruct* nativeStruct = (struct piccolo_ObjNativeStruct*)allocateObj(engine, PICCOLO_OBJ_NATIVE_STRUCT, sizeof(struct piccolo_ObjNativeStruct) + size); 64 | nativeStruct->payloadSize = size; 65 | nativeStruct->free = NULL; 66 | nativeStruct->gcMark = NULL; 67 | nativeStruct->index = NULL; 68 | nativeStruct->Typename = Typename; 69 | return nativeStruct; 70 | } 71 | 72 | void piccolo_freeObj(struct piccolo_Engine* engine, struct piccolo_Obj* obj) { 73 | size_t objSize = 10; 74 | switch(obj->type) { 75 | case PICCOLO_OBJ_STRING: { 76 | objSize = sizeof(struct piccolo_ObjString); 77 | PICCOLO_REALLOCATE("free string", engine, ((struct piccolo_ObjString*)obj)->string, ((struct piccolo_ObjString*)obj)->len + 1, 0); 78 | break; 79 | } 80 | case PICCOLO_OBJ_ARRAY: { 81 | objSize = sizeof(struct piccolo_ObjArray); 82 | piccolo_freeValueArray(engine, &((struct piccolo_ObjArray*)obj)->array); 83 | break; 84 | } 85 | case PICCOLO_OBJ_HASHMAP: { 86 | objSize = sizeof(struct piccolo_ObjHashmap); 87 | piccolo_freeHashmap(engine, &((struct piccolo_ObjHashmap*)obj)->hashmap); 88 | break; 89 | } 90 | case PICCOLO_OBJ_FUNC: { 91 | objSize = sizeof(struct piccolo_ObjFunction); 92 | struct piccolo_ObjFunction* func = (struct piccolo_ObjFunction*)obj; 93 | piccolo_freeBytecode(engine, &func->bytecode); 94 | break; 95 | } 96 | case PICCOLO_OBJ_UPVAL: { 97 | objSize = sizeof(struct piccolo_ObjUpval); 98 | if(!((struct piccolo_ObjUpval*)obj)->open) 99 | PICCOLO_REALLOCATE("free heap upval", engine, ((struct piccolo_ObjUpval*)obj)->val.ptr, sizeof(struct piccolo_Value), 0); 100 | break; 101 | } 102 | case PICCOLO_OBJ_CLOSURE: { 103 | objSize = sizeof(struct piccolo_ObjClosure); 104 | struct piccolo_ObjClosure* closure = (struct piccolo_ObjClosure*)obj; 105 | PICCOLO_REALLOCATE("free upval array", engine, closure->upvals, sizeof(struct piccolo_ObjUpval*) * closure->upvalCnt, 0); 106 | break; 107 | } 108 | case PICCOLO_OBJ_NATIVE_FN: { 109 | objSize = sizeof(struct piccolo_ObjNativeFn); 110 | break; 111 | } 112 | case PICCOLO_OBJ_NATIVE_STRUCT: { 113 | struct piccolo_ObjNativeStruct* nativeStruct = (struct piccolo_ObjNativeStruct*)obj; 114 | objSize = sizeof(struct piccolo_ObjNativeStruct) + nativeStruct->payloadSize; 115 | if(nativeStruct->free != NULL) 116 | nativeStruct->free(PICCOLO_GET_PAYLOAD(obj, void)); 117 | break; 118 | } 119 | case PICCOLO_OBJ_PACKAGE: break; 120 | } 121 | PICCOLO_REALLOCATE("free obj", engine, obj, objSize, 0); 122 | } 123 | 124 | static uint32_t hashString(const char* string, int length) { 125 | uint32_t hash = 2166136261u; 126 | for (int i = 0; i < length; i++) { 127 | hash ^= (uint8_t)string[i]; 128 | hash *= 16777619; 129 | } 130 | return hash; 131 | } 132 | 133 | static struct piccolo_ObjString* newString(struct piccolo_Engine* engine, char* string, int len) { 134 | struct piccolo_ObjString* result = (struct piccolo_ObjString*) PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjString, PICCOLO_OBJ_STRING); 135 | result->string = string; 136 | result->len = len; 137 | result->utf8Len = 0; 138 | const char* curr = string; 139 | while(*curr != '\0') { 140 | result->utf8Len++; 141 | curr += piccolo_strutil_utf8Chars(*curr); 142 | } 143 | result->hash = hashString(string, len); 144 | return result; 145 | } 146 | 147 | struct piccolo_ObjString* piccolo_takeString(struct piccolo_Engine* engine, char* string) { 148 | return newString(engine, string, strlen(string)); 149 | } 150 | 151 | struct piccolo_ObjString* piccolo_copyString(struct piccolo_Engine* engine, const char* string, int len) { 152 | char* copy = PICCOLO_REALLOCATE("string copy", engine, NULL, 0, len + 1); 153 | memcpy(copy, string, len); 154 | copy[len] = '\0'; 155 | return newString(engine, copy, len); 156 | } 157 | 158 | struct piccolo_ObjArray* piccolo_newArray(struct piccolo_Engine* engine, int len) { 159 | struct piccolo_ObjArray* array = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjArray, PICCOLO_OBJ_ARRAY); 160 | piccolo_initValueArray(&array->array); 161 | for(int i = 0; i < len; i++) { 162 | piccolo_writeValueArray(engine, &array->array, PICCOLO_NIL_VAL()); 163 | if(!array->array.values) { 164 | piccolo_runtimeError(engine, "Failed to allocate for new array."); 165 | return NULL; 166 | } 167 | } 168 | return array; 169 | } 170 | 171 | struct piccolo_ObjHashmap* piccolo_newHashmap(struct piccolo_Engine* engine) { 172 | struct piccolo_ObjHashmap* hashmap = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjHashmap, PICCOLO_OBJ_HASHMAP); 173 | piccolo_initHashmap(&hashmap->hashmap); 174 | return hashmap; 175 | } 176 | 177 | struct piccolo_ObjFunction* piccolo_newFunction(struct piccolo_Engine* engine) { 178 | struct piccolo_ObjFunction* function = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjFunction, PICCOLO_OBJ_FUNC); 179 | piccolo_initBytecode(&function->bytecode); 180 | function->arity = 0; 181 | return function; 182 | } 183 | 184 | struct piccolo_ObjUpval* piccolo_newUpval(struct piccolo_Engine* engine, int idx) { 185 | struct piccolo_ObjUpval* upval = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjUpval, PICCOLO_OBJ_UPVAL); 186 | upval->val.idx = idx; 187 | upval->open = true; 188 | upval->next = engine->openUpvals; 189 | engine->openUpvals = upval; 190 | return upval; 191 | } 192 | 193 | struct piccolo_ObjClosure* piccolo_newClosure(struct piccolo_Engine* engine, struct piccolo_ObjFunction* function, int upvals) { 194 | struct piccolo_ObjClosure* closure = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjClosure, PICCOLO_OBJ_CLOSURE); 195 | closure->prototype = function; 196 | closure->upvals = PICCOLO_REALLOCATE("upval array", engine, NULL, 0, sizeof(struct piccolo_ObjUpval*) * upvals); 197 | closure->upvalCnt = upvals; 198 | return closure; 199 | } 200 | 201 | struct piccolo_ObjNativeFn* piccolo_makeNative(struct piccolo_Engine* engine, piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self)) { 202 | struct piccolo_ObjNativeFn* nativeFn = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_ObjNativeFn, PICCOLO_OBJ_NATIVE_FN); 203 | nativeFn->native = native; 204 | return nativeFn; 205 | } 206 | 207 | struct piccolo_ObjNativeFn* piccolo_makeBoundNative(struct piccolo_Engine* engine, piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self), piccolo_Value self) { 208 | struct piccolo_ObjNativeFn* nativeFn = piccolo_makeNative(engine, native); 209 | nativeFn->self = self; 210 | return nativeFn; 211 | } 212 | 213 | struct piccolo_Package* piccolo_newPackage(struct piccolo_Engine* engine) { 214 | struct piccolo_Package* package = PICCOLO_ALLOCATE_OBJ(engine, struct piccolo_Package, PICCOLO_OBJ_PACKAGE); 215 | return package; 216 | } 217 | -------------------------------------------------------------------------------- /object.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_OBJECT_H 3 | #define PICCOLO_OBJECT_H 4 | 5 | #include "value.h" 6 | #include "bytecode.h" 7 | 8 | enum piccolo_ObjType { 9 | PICCOLO_OBJ_STRING, 10 | PICCOLO_OBJ_ARRAY, 11 | PICCOLO_OBJ_HASHMAP, 12 | PICCOLO_OBJ_FUNC, 13 | PICCOLO_OBJ_UPVAL, 14 | PICCOLO_OBJ_CLOSURE, 15 | PICCOLO_OBJ_NATIVE_FN, 16 | PICCOLO_OBJ_NATIVE_STRUCT, 17 | PICCOLO_OBJ_PACKAGE, 18 | }; 19 | 20 | struct piccolo_Obj { 21 | enum piccolo_ObjType type; 22 | struct piccolo_Obj* next; 23 | bool marked; 24 | bool printed; 25 | }; 26 | 27 | struct piccolo_ObjString { 28 | struct piccolo_Obj obj; 29 | char* string; 30 | int len; // Number of bytes in memory(excluding null) 31 | int utf8Len; // Number of UTF8 chars 32 | uint32_t hash; 33 | }; 34 | 35 | struct piccolo_ObjArray { 36 | struct piccolo_Obj obj; 37 | struct piccolo_ValueArray array; 38 | }; 39 | 40 | #include "util/hashmap.h" 41 | struct piccolo_HashmapValue { 42 | piccolo_Value value; 43 | bool exists; 44 | }; 45 | 46 | PICCOLO_HASHMAP_HEADER(piccolo_Value, struct piccolo_HashmapValue, Hashmap) 47 | 48 | struct piccolo_ObjHashmap { 49 | struct piccolo_Obj obj; 50 | struct piccolo_Hashmap hashmap; 51 | }; 52 | 53 | struct piccolo_ObjFunction { 54 | struct piccolo_Obj obj; 55 | struct piccolo_Bytecode bytecode; 56 | int arity; 57 | }; 58 | 59 | struct piccolo_ObjUpval { 60 | struct piccolo_Obj obj; 61 | union { 62 | piccolo_Value *ptr; 63 | int idx; 64 | } val; 65 | bool open; 66 | struct piccolo_ObjUpval* next; 67 | }; 68 | 69 | struct piccolo_Package; 70 | 71 | struct piccolo_ObjClosure { 72 | struct piccolo_Obj obj; 73 | struct piccolo_ObjUpval** upvals; 74 | int upvalCnt; 75 | struct piccolo_ObjFunction* prototype; 76 | struct piccolo_Package* package; 77 | }; 78 | 79 | struct piccolo_ObjNativeFn { 80 | struct piccolo_Obj obj; 81 | piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self); 82 | piccolo_Value self; 83 | }; 84 | 85 | struct piccolo_ObjNativeStruct { 86 | struct piccolo_Obj obj; 87 | void (*free)(void* payload); 88 | void (*gcMark)(void* payload); 89 | piccolo_Value (*index)(void* payload, struct piccolo_Engine* engine, piccolo_Value key, bool set, piccolo_Value value); 90 | const char* Typename; 91 | size_t payloadSize; 92 | }; 93 | 94 | bool piccolo_isObjOfType(piccolo_Value val, enum piccolo_ObjType type); 95 | 96 | #define PICCOLO_IS_STRING(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_STRING)) 97 | #define PICCOLO_IS_ARRAY(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_ARRAY)) 98 | #define PICCOLO_IS_HASHMAP(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_HASHMAP)) 99 | #define PICCOLO_IS_FUNC(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_FUNC)) 100 | #define PICCOLO_IS_UPVAL(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_UPVAL)) 101 | #define PICCOLO_IS_CLOSURE(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_CLOSURE)) 102 | #define PICCOLO_IS_NATIVE_FN(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_NATIVE_FN)) 103 | #define PICCOLO_IS_NATIVE_STRUCT(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_NATIVE_STRUCT)) 104 | #define PICCOLO_IS_PACKAGE(val) (piccolo_isObjOfType(val, PICCOLO_OBJ_PACKAGE)) 105 | 106 | struct piccolo_Obj* allocateObj(struct piccolo_Engine* engine, enum piccolo_ObjType type, size_t size); 107 | #define PICCOLO_ALLOCATE_OBJ(engine, type, objType) ((type*)allocateObj(engine, objType, sizeof(type))) 108 | 109 | struct piccolo_ObjNativeStruct* piccolo_allocNativeStruct(struct piccolo_Engine* engine, size_t size, const char* name); 110 | 111 | #define PICCOLO_ALLOCATE_NATIVE_STRUCT(engine, type, name) ((struct piccolo_Obj*)piccolo_allocNativeStruct(engine, sizeof(type), name)) 112 | #define PICCOLO_GET_PAYLOAD(obj, type) ((type*)((uint8_t*)obj + sizeof(struct piccolo_ObjNativeStruct))) 113 | 114 | void piccolo_freeObj(struct piccolo_Engine* engine, struct piccolo_Obj* obj); 115 | 116 | struct piccolo_ObjString* piccolo_takeString(struct piccolo_Engine* engine, char* string); 117 | struct piccolo_ObjString* piccolo_copyString(struct piccolo_Engine* engine, const char* string, int len); 118 | struct piccolo_ObjArray* piccolo_newArray(struct piccolo_Engine* engine, int len); 119 | struct piccolo_ObjHashmap* piccolo_newHashmap(struct piccolo_Engine* engine); 120 | struct piccolo_ObjFunction* piccolo_newFunction(struct piccolo_Engine* engine); 121 | struct piccolo_ObjUpval* piccolo_newUpval(struct piccolo_Engine* engine, int idx); 122 | struct piccolo_ObjClosure* piccolo_newClosure(struct piccolo_Engine* engine, struct piccolo_ObjFunction* function, int upvals); 123 | struct piccolo_ObjNativeFn* piccolo_makeNative(struct piccolo_Engine* engine, piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self)); 124 | struct piccolo_ObjNativeFn* piccolo_makeBoundNative(struct piccolo_Engine* engine, piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self), piccolo_Value self); 125 | struct piccolo_Package* piccolo_newPackage(struct piccolo_Engine* engine); 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /package.c: -------------------------------------------------------------------------------- 1 | 2 | #include "package.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "engine.h" 8 | #include "compiler.h" 9 | #include "util/file.h" 10 | #include "object.h" 11 | #include "util/memory.h" 12 | 13 | uint32_t piccolo_hashGlobalTableKey(struct piccolo_ObjString* key) { 14 | return key->hash; 15 | } 16 | 17 | bool piccolo_compareGlobalTableKeys(struct piccolo_ObjString* a, struct piccolo_ObjString* b) { 18 | return strcmp(a->string, b->string) == 0; 19 | } 20 | 21 | bool piccolo_GlobalTableIsBaseKey(struct piccolo_ObjString* key) { 22 | return key == NULL; 23 | } 24 | 25 | PICCOLO_HASHMAP_IMPL(struct piccolo_ObjString*, int, GlobalTable, NULL, -1) 26 | 27 | static void initPackage(struct piccolo_Engine* engine, struct piccolo_Package* package) { 28 | piccolo_initValueArray(&package->globals); 29 | piccolo_initTypeArray(&package->types); 30 | piccolo_initGlobalTable(&package->globalIdxs); 31 | piccolo_initBytecode(&package->bytecode); 32 | package->compiled = false; 33 | package->executed = false; 34 | package->source = NULL; 35 | } 36 | 37 | struct piccolo_Package* piccolo_createPackage(struct piccolo_Engine* engine) { 38 | struct piccolo_Package* package = piccolo_newPackage(engine); 39 | piccolo_writePackageArray(engine, &engine->packages, package); 40 | initPackage(engine, package); 41 | return package; 42 | } 43 | 44 | struct piccolo_Package* piccolo_loadPackage(struct piccolo_Engine* engine, const char* filepath) { 45 | struct piccolo_Package* package = piccolo_createPackage(engine); 46 | package->compilationError = false; 47 | 48 | package->packageName = filepath; 49 | 50 | package->source = piccolo_readFile(filepath); 51 | if(package->source == NULL) { 52 | piccolo_enginePrintError(engine, "Could not load package %s\n", filepath); 53 | package->compilationError = true; 54 | return package; 55 | } 56 | 57 | if(!piccolo_compilePackage(engine, package)) { 58 | piccolo_enginePrintError(engine, "Compilation error.\n"); 59 | package->compilationError = true; 60 | return package; 61 | } 62 | 63 | return package; 64 | } 65 | 66 | void piccolo_freePackage(struct piccolo_Engine* engine, struct piccolo_Package* package) { 67 | piccolo_freeValueArray(engine, &package->globals); 68 | piccolo_freeGlobalTable(engine, &package->globalIdxs); 69 | piccolo_freeBytecode(engine, &package->bytecode); 70 | if(package->source != NULL) 71 | free(package->source); 72 | } 73 | -------------------------------------------------------------------------------- /package.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_PACKAGE_H 3 | #define PICCOLO_PACKAGE_H 4 | 5 | #include "compiler.h" 6 | #include "util/dynarray.h" 7 | #include "util/hashmap.h" 8 | #include "object.h" 9 | #include "bytecode.h" 10 | #include "typecheck.h" 11 | 12 | PICCOLO_HASHMAP_HEADER(struct piccolo_ObjString*, int, GlobalTable) 13 | #define PICCOLO_GLOBAL_SLOT_MUTABLE_BIT (1 << 14) 14 | 15 | struct piccolo_Package { 16 | struct piccolo_Obj obj; 17 | struct piccolo_Bytecode bytecode; 18 | char* source; 19 | struct piccolo_ValueArray globals; 20 | struct piccolo_TypeArray types; 21 | struct piccolo_GlobalTable globalIdxs; 22 | const char* packageName; 23 | bool compiled, executed, compilationError; 24 | }; 25 | 26 | struct piccolo_Package* piccolo_createPackage(struct piccolo_Engine* engine); 27 | struct piccolo_Package* piccolo_loadPackage(struct piccolo_Engine* engine, const char* filepath); 28 | void piccolo_freePackage(struct piccolo_Engine* engine, struct piccolo_Package* package); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | 2 | #include "parser.h" 3 | #include "engine.h" 4 | #include "util/strutil.h" 5 | 6 | #include 7 | #include 8 | 9 | PICCOLO_DYNARRAY_IMPL(struct piccolo_Token, Token) 10 | 11 | static struct piccolo_ExprNode* createNode(struct piccolo_Parser* parser, size_t size, enum piccolo_ExprNodeType type) { 12 | struct piccolo_ExprNode* node = malloc(size); 13 | node->nodes = parser->nodes; 14 | node->nextExpr = NULL; 15 | node->type = type; 16 | node->reqEval = false; 17 | node->resultType = NULL; 18 | parser->nodes = node; 19 | return node; 20 | } 21 | 22 | #define ALLOCATE_NODE(parser, name, type) \ 23 | ((struct piccolo_ ## name ## Node*)createNode(parser, sizeof(struct piccolo_ ## name ## Node), type)) 24 | 25 | static void parsingError(struct piccolo_Engine* engine, struct piccolo_Parser* parser, const char* format, ...) { 26 | va_list args; 27 | va_start(args, format); 28 | engine->printError(format, args); 29 | va_end(args); 30 | piccolo_enginePrintError(engine, " [%s]", parser->package->packageName); 31 | int charIdx = parser->currToken.charIdx; 32 | struct piccolo_strutil_LineInfo line = piccolo_strutil_getLine(parser->scanner->source, charIdx); 33 | piccolo_enginePrintError(engine, "\n[line %d] %.*s\n", line.line + 1, line.lineEnd - line.lineStart, line.lineStart); 34 | 35 | int lineNumberDigits = 0; 36 | int lineNumber = line.line + 1; 37 | while(lineNumber > 0) { 38 | lineNumberDigits++; 39 | lineNumber /= 10; 40 | } 41 | piccolo_enginePrintError(engine, "%*c ^", 7 + lineNumberDigits + parser->currToken.start - line.lineStart, ' '); 42 | piccolo_enginePrintError(engine, "\n"); 43 | 44 | parser->hadError = true; 45 | } 46 | 47 | static void advanceParser(struct piccolo_Engine* engine, struct piccolo_Parser* parser) { 48 | parser->currToken = piccolo_nextToken(parser->scanner); 49 | parser->cycled = false; 50 | while(parser->currToken.type == PICCOLO_TOKEN_ERROR) { 51 | parsingError(engine, parser, "Malformed token."); 52 | parser->currToken = piccolo_nextToken(parser->scanner); 53 | } 54 | } 55 | 56 | 57 | /* 58 | The reqExpr parameter is needed for determining whether newlines are significant. 59 | For example, in a case like this: 60 | 61 | 4 + 62 | 2 63 | 64 | The newline is not treated as significant, because an expression is required 65 | after the plus. 66 | 67 | In a case like this, however: 68 | 69 | 4 70 | -3 71 | 72 | The newline is treated as significant, because an expression is not needed 73 | after the 4. 74 | */ 75 | #define PARSER_PARAMS struct piccolo_Engine* engine, struct piccolo_Parser* parser, bool reqExpr 76 | #define PARSER_ARGS engine, parser, false 77 | #define PARSER_ARGS_REQ_VAL engine, parser, true 78 | 79 | #define SKIP_NEWLINES() \ 80 | if(reqExpr) { \ 81 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) \ 82 | advanceParser(engine, parser); \ 83 | } 84 | 85 | static struct piccolo_ExprNode* parseExpr(PARSER_PARAMS); 86 | 87 | struct piccolo_ExprNode* parseExprList(struct piccolo_Engine* engine, struct piccolo_Parser* parser, bool allowRightBrace) { 88 | struct piccolo_ExprNode* first = NULL; 89 | struct piccolo_ExprNode* curr = NULL; 90 | 91 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 92 | advanceParser(engine, parser); 93 | 94 | while(parser->currToken.type != PICCOLO_TOKEN_EOF && (!allowRightBrace || parser->currToken.type != PICCOLO_TOKEN_RIGHT_BRACE)) { 95 | struct piccolo_ExprNode* node = parseExpr(engine, parser, false); 96 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) { 97 | advanceParser(engine, parser); 98 | } 99 | 100 | if(node != NULL) { 101 | if (first == NULL) 102 | first = node; 103 | 104 | if (curr == NULL) { 105 | curr = node; 106 | } else { 107 | curr->nextExpr = node; 108 | curr = node; 109 | } 110 | } 111 | } 112 | return first; 113 | } 114 | 115 | static struct piccolo_ExprNode* parseBlock(PARSER_PARAMS) { 116 | advanceParser(engine, parser); 117 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 118 | advanceParser(engine, parser); 119 | 120 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 121 | advanceParser(engine, parser); 122 | struct piccolo_HashmapLiteralNode* hashmap = ALLOCATE_NODE(parser, HashmapLiteral, PICCOLO_EXPR_HASHMAP_LITERAL); 123 | hashmap->first = NULL; 124 | return (struct piccolo_ExprNode*)hashmap; 125 | } 126 | 127 | struct piccolo_ExprNode* firstExpr = parseExpr(PARSER_ARGS); 128 | if(firstExpr == NULL) return NULL; 129 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 130 | advanceParser(engine, parser); 131 | 132 | if(parser->currToken.type == PICCOLO_TOKEN_COLON) { 133 | advanceParser(engine, parser); 134 | struct piccolo_ExprNode* firstValue = parseExpr(PARSER_ARGS_REQ_VAL); 135 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 136 | advanceParser(engine, parser); 137 | if(parser->currToken.type == PICCOLO_TOKEN_COMMA) { 138 | advanceParser(engine, parser); 139 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 140 | advanceParser(engine, parser); 141 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 142 | parsingError(engine, parser, "Expected expression."); 143 | } 144 | } else if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 145 | 146 | } else { 147 | parsingError(engine, parser, "Expected comma."); 148 | } 149 | struct piccolo_HashmapEntryNode* firstEntry = ALLOCATE_NODE(parser, HashmapEntry, PICCOLO_EXPR_HASHMAP_ENTRY); 150 | firstEntry->key = firstExpr; 151 | firstEntry->value = firstValue; 152 | struct piccolo_HashmapEntryNode* curr = firstEntry; 153 | 154 | while(parser->currToken.type != PICCOLO_TOKEN_RIGHT_BRACE) { 155 | if(parser->currToken.type == PICCOLO_TOKEN_EOF) { 156 | parsingError(engine, parser, "Expected }."); 157 | break; 158 | } 159 | 160 | struct piccolo_ExprNode* key = parseExpr(PARSER_ARGS_REQ_VAL); 161 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 162 | advanceParser(engine, parser); 163 | if(parser->currToken.type == PICCOLO_TOKEN_COLON) { 164 | advanceParser(engine, parser); 165 | } else { 166 | parsingError(engine, parser, "Expected :."); 167 | } 168 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 169 | parsingError(engine, parser, "Expected expression."); 170 | break; 171 | } 172 | struct piccolo_ExprNode* value = parseExpr(PARSER_ARGS_REQ_VAL); 173 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 174 | advanceParser(engine, parser); 175 | 176 | struct piccolo_HashmapEntryNode* entry = ALLOCATE_NODE(parser, HashmapEntry, PICCOLO_EXPR_HASHMAP_ENTRY); 177 | entry->expr.nextExpr = NULL; 178 | entry->key = key; 179 | entry->value = value; 180 | curr->expr.nextExpr = (struct piccolo_ExprNode*)entry; 181 | curr = entry; 182 | 183 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 184 | advanceParser(engine, parser); 185 | if(parser->currToken.type == PICCOLO_TOKEN_COMMA) { 186 | advanceParser(engine, parser); 187 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 188 | advanceParser(engine, parser); 189 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 190 | parsingError(engine, parser, "Expected expression."); 191 | } 192 | } else if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 193 | 194 | } else { 195 | parsingError(engine, parser, "Expected comma."); 196 | } 197 | } 198 | advanceParser(engine, parser); 199 | 200 | struct piccolo_HashmapLiteralNode* hashmap = ALLOCATE_NODE(parser, HashmapLiteral, PICCOLO_EXPR_HASHMAP_LITERAL); 201 | hashmap->first = firstEntry; 202 | 203 | return (struct piccolo_ExprNode*)hashmap; 204 | 205 | } else { 206 | struct piccolo_ExprNode* exprs = parseExprList(engine, parser, true); 207 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_BRACE) { 208 | advanceParser(engine, parser); 209 | } else { 210 | parsingError(engine, parser, "Expected }."); 211 | } 212 | struct piccolo_BlockNode* block = ALLOCATE_NODE(parser, Block, PICCOLO_EXPR_BLOCK); 213 | firstExpr->nextExpr = exprs; 214 | block->first = firstExpr; 215 | return (struct piccolo_ExprNode*)block; 216 | } 217 | return NULL; 218 | } 219 | 220 | static struct piccolo_ExprNode* parseLiteral(PARSER_PARAMS) { 221 | SKIP_NEWLINES() 222 | if(parser->currToken.type == PICCOLO_TOKEN_NUM || 223 | parser->currToken.type == PICCOLO_TOKEN_STRING || 224 | parser->currToken.type == PICCOLO_TOKEN_TRUE || 225 | parser->currToken.type == PICCOLO_TOKEN_FALSE || 226 | parser->currToken.type == PICCOLO_TOKEN_NIL) { 227 | struct piccolo_LiteralNode* literal = ALLOCATE_NODE(parser, Literal, PICCOLO_EXPR_LITERAL); 228 | literal->token = parser->currToken; 229 | advanceParser(engine, parser); 230 | return (struct piccolo_ExprNode*)literal; 231 | } 232 | if(parser->currToken.type == PICCOLO_TOKEN_LEFT_PAREN) { 233 | advanceParser(engine, parser); 234 | struct piccolo_ExprNode* value = parseExpr(PARSER_ARGS_REQ_VAL); 235 | 236 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 237 | advanceParser(engine, parser); 238 | 239 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_PAREN) { 240 | advanceParser(engine, parser); 241 | } else { 242 | parsingError(engine, parser, "Expected )."); 243 | } 244 | return value; 245 | } 246 | if(parser->currToken.type == PICCOLO_TOKEN_LEFT_BRACE) { 247 | return parseBlock(PARSER_ARGS); 248 | } 249 | 250 | if(parser->cycled) { 251 | parsingError(engine, parser, "Expected expression."); 252 | advanceParser(engine, parser); 253 | } else { 254 | parser->cycled = true; 255 | return parseExpr(PARSER_ARGS); 256 | } 257 | return NULL; 258 | } 259 | 260 | static struct piccolo_ExprNode* parseVar(PARSER_PARAMS) { 261 | SKIP_NEWLINES() 262 | if(parser->currToken.type == PICCOLO_TOKEN_IDENTIFIER) { 263 | struct piccolo_Token varName = parser->currToken; 264 | advanceParser(engine, parser); 265 | if(parser->currToken.type == PICCOLO_TOKEN_EQ) { 266 | struct piccolo_VarSetNode* varSet = ALLOCATE_NODE(parser, VarSet, PICCOLO_EXPR_VAR_SET); 267 | varSet->name = varName; 268 | advanceParser(engine, parser); 269 | varSet->value = parseExpr(PARSER_ARGS_REQ_VAL); 270 | return (struct piccolo_ExprNode*)varSet; 271 | } else { 272 | struct piccolo_VarNode* var = ALLOCATE_NODE(parser, Var, PICCOLO_EXPR_VAR); 273 | var->name = varName; 274 | return (struct piccolo_ExprNode*)var; 275 | } 276 | } 277 | return parseLiteral(PARSER_ARGS); 278 | } 279 | 280 | 281 | static struct piccolo_ExprNode* parseArrayLiteral(PARSER_PARAMS) { 282 | SKIP_NEWLINES() 283 | if(parser->currToken.type == PICCOLO_TOKEN_LEFT_SQR_PAREN) { 284 | advanceParser(engine, parser); 285 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 286 | advanceParser(engine, parser); 287 | struct piccolo_ExprNode* first = NULL; 288 | struct piccolo_ExprNode* curr = NULL; 289 | while(parser->currToken.type != PICCOLO_TOKEN_RIGHT_SQR_PAREN) { 290 | if(parser->currToken.type == PICCOLO_TOKEN_EOF) { 291 | parsingError(engine, parser, "Expected ]."); 292 | break; 293 | } 294 | struct piccolo_ExprNode* next = parseExpr(PARSER_ARGS_REQ_VAL); 295 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 296 | advanceParser(engine, parser); 297 | if(first == NULL) 298 | first = next; 299 | if(curr != NULL) 300 | curr->nextExpr = next; 301 | curr = next; 302 | 303 | if(parser->currToken.type == PICCOLO_TOKEN_COMMA) { 304 | advanceParser(engine, parser); 305 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_SQR_PAREN) { 306 | parsingError(engine, parser, "Expected expression."); 307 | } 308 | } else if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_SQR_PAREN) { 309 | 310 | } else { 311 | parsingError(engine, parser, "Expected comma."); 312 | advanceParser(engine, parser); 313 | } 314 | } 315 | advanceParser(engine, parser); 316 | struct piccolo_ArrayLiteralNode* arrayLiteral = ALLOCATE_NODE(parser, ArrayLiteral, PICCOLO_EXPR_ARRAY_LITERAL); 317 | arrayLiteral->first = first; 318 | return (struct piccolo_ExprNode*)arrayLiteral; 319 | } 320 | return parseVar(PARSER_ARGS); 321 | } 322 | 323 | static struct piccolo_ExprNode* parseImport(PARSER_PARAMS) { 324 | SKIP_NEWLINES() 325 | if(parser->currToken.type == PICCOLO_TOKEN_IMPORT) { 326 | advanceParser(engine, parser); 327 | if(parser->currToken.type == PICCOLO_TOKEN_STRING) { 328 | if(parser->currToken.length > PICCOLO_MAX_PACKAGE) { 329 | parsingError(engine, parser, "Package import of length %d exceeded PICCOLO_MAX_PACKAGE (%d).", parser->currToken.length, PICCOLO_MAX_PACKAGE); 330 | } 331 | struct piccolo_Token packageName = parser->currToken; 332 | struct piccolo_ImportNode* import = ALLOCATE_NODE(parser, Import, PICCOLO_EXPR_IMPORT); 333 | import->packageName = packageName; 334 | import->package = NULL; 335 | import->resolved = false; 336 | advanceParser(engine, parser); 337 | if(parser->currToken.type == PICCOLO_TOKEN_AS) { 338 | advanceParser(engine, parser); 339 | struct piccolo_VarDeclNode* importAs = ALLOCATE_NODE(parser, VarDecl, PICCOLO_EXPR_VAR_DECL); 340 | importAs->typed = true; 341 | importAs->name = parser->currToken; 342 | if(parser->currToken.type != PICCOLO_TOKEN_IDENTIFIER) { 343 | parsingError(engine, parser, "Expected identifier."); 344 | } 345 | advanceParser(engine, parser); 346 | importAs->value = (struct piccolo_ExprNode*)import; 347 | importAs->Mutable = false; 348 | return (struct piccolo_ExprNode*)importAs; 349 | } 350 | return (struct piccolo_ExprNode*)import; 351 | } else { 352 | parsingError(engine, parser, "Expected package name."); 353 | return NULL; 354 | } 355 | } 356 | return parseArrayLiteral(PARSER_ARGS); 357 | } 358 | 359 | static struct piccolo_ExprNode* parseFnLiteral(PARSER_PARAMS) { 360 | SKIP_NEWLINES() 361 | if(parser->currToken.type == PICCOLO_TOKEN_FN) { 362 | advanceParser(engine, parser); 363 | struct piccolo_FnLiteralNode* fnLiteral = ALLOCATE_NODE(parser, FnLiteral, PICCOLO_EXPR_FN_LITERAL); 364 | piccolo_initTokenArray(&fnLiteral->params); 365 | while(parser->currToken.type != PICCOLO_TOKEN_ARROW) { 366 | if(parser->currToken.type != PICCOLO_TOKEN_IDENTIFIER) { 367 | parsingError(engine, parser, "Expected ->."); 368 | break; 369 | } else { 370 | piccolo_writeTokenArray(engine, &fnLiteral->params, parser->currToken); 371 | advanceParser(engine, parser); 372 | } 373 | 374 | if(parser->currToken.type == PICCOLO_TOKEN_COMMA) { 375 | advanceParser(engine, parser); 376 | if(parser->currToken.type != PICCOLO_TOKEN_IDENTIFIER) { 377 | parsingError(engine, parser, "Expected parameter name."); 378 | } 379 | } else if(parser->currToken.type != PICCOLO_TOKEN_ARROW) { 380 | parsingError(engine, parser, "Expected comma."); 381 | } 382 | } 383 | advanceParser(engine, parser); 384 | fnLiteral->value = parseExpr(PARSER_ARGS_REQ_VAL); 385 | return (struct piccolo_ExprNode*)fnLiteral; 386 | } 387 | return parseImport(PARSER_ARGS); 388 | } 389 | 390 | static struct piccolo_ExprNode* parseSubscript(PARSER_PARAMS, struct piccolo_ExprNode* value) { 391 | 392 | advanceParser(engine, parser); 393 | if (parser->currToken.type == PICCOLO_TOKEN_IDENTIFIER) { 394 | struct piccolo_Token subscript = parser->currToken; 395 | advanceParser(engine, parser); 396 | if (parser->currToken.type == PICCOLO_TOKEN_EQ) { 397 | advanceParser(engine, parser); 398 | struct piccolo_SubscriptSetNode *subscriptSet = ALLOCATE_NODE(parser, SubscriptSet, 399 | PICCOLO_EXPR_SUBSCRIPT_SET); 400 | subscriptSet->target = value; 401 | subscriptSet->subscript = subscript; 402 | subscriptSet->value = parseExpr(PARSER_ARGS_REQ_VAL); 403 | return (struct piccolo_ExprNode *) subscriptSet; 404 | } else { 405 | struct piccolo_SubscriptNode *subscriptNode = ALLOCATE_NODE(parser, Subscript, 406 | PICCOLO_EXPR_SUBSCRIPT); 407 | subscriptNode->value = value; 408 | subscriptNode->subscript = subscript; 409 | value = (struct piccolo_ExprNode *) subscriptNode; 410 | } 411 | } else { 412 | parsingError(engine, parser, "Expected name."); 413 | } 414 | 415 | return value; 416 | } 417 | 418 | static struct piccolo_ExprNode* parseIndex(PARSER_PARAMS, struct piccolo_ExprNode* value) { 419 | int charIdx = parser->currToken.charIdx; 420 | advanceParser(engine, parser); 421 | struct piccolo_ExprNode* index = parseExpr(PARSER_ARGS_REQ_VAL); 422 | 423 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_SQR_PAREN) { 424 | advanceParser(engine, parser); 425 | } else { 426 | parsingError(engine, parser, "Expected ]."); 427 | } 428 | 429 | if(parser->currToken.type == PICCOLO_TOKEN_EQ) { 430 | 431 | advanceParser(engine, parser); 432 | 433 | struct piccolo_IndexSetNode* indexSet = ALLOCATE_NODE(parser, IndexSet, PICCOLO_EXPR_INDEX_SET); 434 | indexSet->charIdx = charIdx; 435 | indexSet->target = value; 436 | indexSet->index = index; 437 | indexSet->value = parseExpr(PARSER_ARGS_REQ_VAL); 438 | 439 | return (struct piccolo_ExprNode*)indexSet; 440 | 441 | } else { 442 | struct piccolo_IndexNode* indexNote = ALLOCATE_NODE(parser, Index, PICCOLO_EXPR_INDEX); 443 | indexNote->charIdx = charIdx; 444 | indexNote->target = value; 445 | indexNote->index = index; 446 | value = (struct piccolo_ExprNode*)indexNote; 447 | } 448 | return value; 449 | } 450 | 451 | static struct piccolo_ExprNode* parseCall(PARSER_PARAMS, struct piccolo_ExprNode* function) { 452 | 453 | int charIdx = parser->currToken.charIdx; 454 | advanceParser(engine, parser); 455 | struct piccolo_ExprNode* firstArg = NULL; 456 | struct piccolo_ExprNode* curr = NULL; 457 | while(parser->currToken.type != PICCOLO_TOKEN_RIGHT_PAREN) { 458 | if(parser->currToken.type == PICCOLO_TOKEN_EOF) { 459 | parsingError(engine, parser, "Expected )."); 460 | return NULL; 461 | } 462 | struct piccolo_ExprNode* arg = parseExpr(PARSER_ARGS_REQ_VAL); 463 | if(curr == NULL) { 464 | firstArg = arg; 465 | curr = arg; 466 | } else { 467 | curr->nextExpr = arg; 468 | curr = arg; 469 | } 470 | 471 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 472 | advanceParser(engine, parser); 473 | 474 | if(parser->currToken.type == PICCOLO_TOKEN_COMMA) { 475 | advanceParser(engine, parser); 476 | if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_PAREN) { 477 | parsingError(engine, parser, "Expected argument."); 478 | advanceParser(engine, parser); 479 | return function; 480 | } 481 | } else if(parser->currToken.type == PICCOLO_TOKEN_RIGHT_PAREN) { 482 | 483 | } else { 484 | parsingError(engine, parser, "Expected comma."); 485 | return NULL; 486 | } 487 | } 488 | advanceParser(engine, parser); 489 | struct piccolo_CallNode* functionCall = ALLOCATE_NODE(parser, Call, PICCOLO_EXPR_CALL); 490 | functionCall->function = function; 491 | functionCall->firstArg = firstArg; 492 | functionCall->charIdx = charIdx; 493 | function = (struct piccolo_ExprNode*)functionCall; 494 | 495 | return function; 496 | } 497 | 498 | static struct piccolo_ExprNode* parsePostfix(PARSER_PARAMS) { 499 | struct piccolo_ExprNode* result = parseFnLiteral(PARSER_ARGS); 500 | while(parser->currToken.type == PICCOLO_TOKEN_DOT || parser->currToken.type == PICCOLO_TOKEN_LEFT_SQR_PAREN || parser->currToken.type == PICCOLO_TOKEN_LEFT_PAREN) { 501 | if(parser->currToken.type == PICCOLO_TOKEN_DOT) { 502 | result = parseSubscript(PARSER_ARGS, result); 503 | } else if(parser->currToken.type == PICCOLO_TOKEN_LEFT_SQR_PAREN) { 504 | result = parseIndex(PARSER_ARGS, result); 505 | } else if(parser->currToken.type == PICCOLO_TOKEN_LEFT_PAREN) { 506 | result = parseCall(PARSER_ARGS, result); 507 | } 508 | } 509 | return result; 510 | } 511 | 512 | static struct piccolo_ExprNode* parseUnary(PARSER_PARAMS) { 513 | SKIP_NEWLINES() 514 | if(parser->currToken.type == PICCOLO_TOKEN_MINUS || 515 | parser->currToken.type == PICCOLO_TOKEN_BANG) { 516 | struct piccolo_Token op = parser->currToken; 517 | advanceParser(engine, parser); 518 | struct piccolo_ExprNode* value = parseUnary(PARSER_ARGS_REQ_VAL); 519 | struct piccolo_UnaryNode* unary = ALLOCATE_NODE(parser, Unary, PICCOLO_EXPR_UNARY); 520 | unary->op = op; 521 | unary->value = value; 522 | return (struct piccolo_ExprNode*)unary; 523 | } 524 | return parsePostfix(PARSER_ARGS); 525 | } 526 | 527 | static struct piccolo_ExprNode* parseRange(PARSER_PARAMS) { 528 | SKIP_NEWLINES() 529 | struct piccolo_ExprNode* left = parseUnary(PARSER_ARGS); 530 | if(parser->currToken.type == PICCOLO_TOKEN_DOT_DOT) { 531 | int charIdx = parser->currToken.charIdx; 532 | advanceParser(engine, parser); 533 | struct piccolo_ExprNode* right = parseRange(PARSER_ARGS); 534 | struct piccolo_RangeNode* range = ALLOCATE_NODE(parser, Range, PICCOLO_EXPR_RANGE); 535 | range->left = left; 536 | range->right = right; 537 | range->charIdx = charIdx; 538 | return (struct piccolo_ExprNode*)range; 539 | } 540 | return left; 541 | } 542 | 543 | static struct piccolo_ExprNode* parseMultiplicative(PARSER_PARAMS) { 544 | SKIP_NEWLINES() 545 | struct piccolo_ExprNode* expr = parseRange(PARSER_ARGS); 546 | while(parser->currToken.type == PICCOLO_TOKEN_STAR || 547 | parser->currToken.type == PICCOLO_TOKEN_SLASH || 548 | parser->currToken.type == PICCOLO_TOKEN_PERCENT) { 549 | struct piccolo_Token op = parser->currToken; 550 | advanceParser(engine, parser); 551 | struct piccolo_ExprNode* rightHand = parseRange(PARSER_ARGS_REQ_VAL); 552 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 553 | binary->a = expr; 554 | binary->op = op; 555 | binary->b = rightHand; 556 | expr = (struct piccolo_ExprNode*)binary; 557 | } 558 | return expr; 559 | } 560 | 561 | static struct piccolo_ExprNode* parseAdditive(PARSER_PARAMS) { 562 | SKIP_NEWLINES() 563 | struct piccolo_ExprNode* expr = parseMultiplicative(PARSER_ARGS); 564 | while(parser->currToken.type == PICCOLO_TOKEN_PLUS || 565 | parser->currToken.type == PICCOLO_TOKEN_MINUS) { 566 | struct piccolo_Token op = parser->currToken; 567 | advanceParser(engine, parser); 568 | struct piccolo_ExprNode* rightHand = parseMultiplicative(PARSER_ARGS_REQ_VAL); 569 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 570 | binary->a = expr; 571 | binary->op = op; 572 | binary->b = rightHand; 573 | expr = (struct piccolo_ExprNode*)binary; 574 | } 575 | return expr; 576 | } 577 | 578 | static struct piccolo_ExprNode* parseIn(PARSER_PARAMS) { 579 | SKIP_NEWLINES() 580 | struct piccolo_ExprNode* expr = parseAdditive(PARSER_ARGS); 581 | while(parser->currToken.type == PICCOLO_TOKEN_IN) { 582 | struct piccolo_Token op = parser->currToken; 583 | advanceParser(engine, parser); 584 | struct piccolo_ExprNode* rightHand = parseAdditive(PARSER_ARGS_REQ_VAL); 585 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 586 | binary->a = expr; 587 | binary->b = rightHand; 588 | binary->op = op; 589 | expr = (struct piccolo_ExprNode*)binary; 590 | } 591 | return expr; 592 | } 593 | 594 | static struct piccolo_ExprNode* parseComparison(PARSER_PARAMS) { 595 | SKIP_NEWLINES() 596 | struct piccolo_ExprNode* expr = parseIn(PARSER_ARGS); 597 | while(parser->currToken.type == PICCOLO_TOKEN_GREATER || 598 | parser->currToken.type == PICCOLO_TOKEN_LESS || 599 | parser->currToken.type == PICCOLO_TOKEN_GREATER_EQ || 600 | parser->currToken.type == PICCOLO_TOKEN_LESS_EQ) { 601 | struct piccolo_Token op = parser->currToken; 602 | advanceParser(engine, parser); 603 | struct piccolo_ExprNode* rightHand = parseIn(PARSER_ARGS_REQ_VAL); 604 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 605 | binary->a = expr; 606 | binary->op = op; 607 | binary->b = rightHand; 608 | expr = (struct piccolo_ExprNode*)binary; 609 | } 610 | return expr; 611 | } 612 | 613 | static struct piccolo_ExprNode* parseEquality(PARSER_PARAMS) { 614 | SKIP_NEWLINES() 615 | struct piccolo_ExprNode* expr = parseComparison(PARSER_ARGS); 616 | while(parser->currToken.type == PICCOLO_TOKEN_EQ_EQ || 617 | parser->currToken.type == PICCOLO_TOKEN_BANG_EQ) { 618 | struct piccolo_Token op = parser->currToken; 619 | advanceParser(engine, parser); 620 | struct piccolo_ExprNode* rightHand = parseEquality(PARSER_ARGS_REQ_VAL); 621 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 622 | binary->a = expr; 623 | binary->op = op; 624 | binary->b = rightHand; 625 | expr = (struct piccolo_ExprNode*)binary; 626 | } 627 | return expr; 628 | } 629 | 630 | static struct piccolo_ExprNode* parseBoolean(PARSER_PARAMS) { 631 | SKIP_NEWLINES() 632 | struct piccolo_ExprNode* expr = parseEquality(PARSER_ARGS); 633 | while(parser->currToken.type == PICCOLO_TOKEN_AND || 634 | parser->currToken.type == PICCOLO_TOKEN_OR) { 635 | struct piccolo_Token op = parser->currToken; 636 | advanceParser(engine, parser); 637 | struct piccolo_ExprNode* rightHand = parseBoolean(PARSER_ARGS_REQ_VAL); 638 | struct piccolo_BinaryNode* binary = ALLOCATE_NODE(parser, Binary, PICCOLO_EXPR_BINARY); 639 | binary->a = expr; 640 | binary->op = op; 641 | binary->b = rightHand; 642 | expr = (struct piccolo_ExprNode*)binary; 643 | } 644 | return expr; 645 | } 646 | 647 | static struct piccolo_ExprNode* parseVarDecl(PARSER_PARAMS) { 648 | SKIP_NEWLINES() 649 | if(parser->currToken.type == PICCOLO_TOKEN_VAR || parser->currToken.type == PICCOLO_TOKEN_CONST) { 650 | struct piccolo_VarDeclNode* varDecl = ALLOCATE_NODE(parser, VarDecl, PICCOLO_EXPR_VAR_DECL); 651 | varDecl->Mutable = parser->currToken.type == PICCOLO_TOKEN_VAR; 652 | advanceParser(engine, parser); 653 | if(parser->currToken.type == PICCOLO_TOKEN_IDENTIFIER) { 654 | varDecl->name = parser->currToken; 655 | advanceParser(engine, parser); 656 | } else { 657 | parsingError(engine, parser, "Expected variable name."); 658 | } 659 | 660 | if(parser->currToken.type == PICCOLO_TOKEN_COLON) { 661 | varDecl->typed = true; 662 | advanceParser(engine, parser); 663 | } else { 664 | varDecl->typed = false; 665 | } 666 | 667 | if(parser->currToken.type == PICCOLO_TOKEN_EQ) { 668 | advanceParser(engine, parser); 669 | } else { 670 | parsingError(engine, parser, "Expected =."); 671 | } 672 | 673 | varDecl->value = parseExpr(PARSER_ARGS_REQ_VAL); 674 | 675 | return (struct piccolo_ExprNode*)varDecl; 676 | } 677 | return parseBoolean(PARSER_ARGS); 678 | } 679 | 680 | static struct piccolo_ExprNode* parseIf(PARSER_PARAMS) { 681 | SKIP_NEWLINES() 682 | if(parser->currToken.type == PICCOLO_TOKEN_IF) { 683 | advanceParser(engine, parser); 684 | int charIdx = parser->currToken.charIdx; 685 | struct piccolo_ExprNode* condition = parseExpr(PARSER_ARGS_REQ_VAL); 686 | struct piccolo_ExprNode* trueVal = parseExpr(PARSER_ARGS_REQ_VAL); 687 | struct piccolo_ExprNode* falseVal = NULL; 688 | while(parser->currToken.type == PICCOLO_TOKEN_NEWLINE) 689 | advanceParser(engine, parser); 690 | if(parser->currToken.type == PICCOLO_TOKEN_ELSE) { 691 | advanceParser(engine, parser); 692 | falseVal = parseExpr(PARSER_ARGS_REQ_VAL); 693 | } 694 | struct piccolo_IfNode* ifNode = ALLOCATE_NODE(parser, If, PICCOLO_EXPR_IF); 695 | ifNode->conditionCharIdx = charIdx; 696 | ifNode->condition = condition; 697 | ifNode->trueVal = trueVal; 698 | ifNode->falseVal = falseVal; 699 | return (struct piccolo_ExprNode*)ifNode; 700 | } 701 | return parseVarDecl(PARSER_ARGS); 702 | } 703 | 704 | static struct piccolo_ExprNode* parseWhile(PARSER_PARAMS) { 705 | SKIP_NEWLINES() 706 | if(parser->currToken.type == PICCOLO_TOKEN_WHILE) { 707 | advanceParser(engine, parser); 708 | struct piccolo_WhileNode* whileNode = ALLOCATE_NODE(parser, While, PICCOLO_EXPR_WHILE); 709 | whileNode->conditionCharIdx = parser->currToken.charIdx; 710 | whileNode->condition = parseExpr(PARSER_ARGS_REQ_VAL); 711 | whileNode->value = parseExpr(PARSER_ARGS_REQ_VAL); 712 | return (struct piccolo_ExprNode*)whileNode; 713 | } 714 | return parseIf(PARSER_ARGS); 715 | } 716 | 717 | static struct piccolo_ExprNode* parseFor(PARSER_PARAMS) { 718 | SKIP_NEWLINES() 719 | if(parser->currToken.type == PICCOLO_TOKEN_FOR) { 720 | advanceParser(engine, parser); 721 | struct piccolo_Token name = parser->currToken; 722 | if(parser->currToken.type != PICCOLO_TOKEN_IDENTIFIER) { 723 | parsingError(engine, parser, "Expected identifier."); 724 | } 725 | advanceParser(engine, parser); 726 | int charIdx = parser->currToken.charIdx; 727 | if(parser->currToken.type != PICCOLO_TOKEN_IN) { 728 | parsingError(engine, parser, "Expected in."); 729 | } else { 730 | advanceParser(engine, parser); 731 | } 732 | int containerCharIdx = parser->currToken.charIdx; 733 | struct piccolo_ExprNode* container = parseExpr(PARSER_ARGS_REQ_VAL); 734 | struct piccolo_ExprNode* value = parseExpr(PARSER_ARGS_REQ_VAL); 735 | 736 | struct piccolo_ForNode* forNode = ALLOCATE_NODE(parser, For, PICCOLO_EXPR_FOR); 737 | forNode->container = container; 738 | forNode->name = name; 739 | forNode->value = value; 740 | forNode->containerCharIdx = containerCharIdx; 741 | forNode->charIdx = charIdx; 742 | return (struct piccolo_ExprNode*)forNode; 743 | } 744 | return parseWhile(PARSER_ARGS); 745 | } 746 | 747 | static struct piccolo_ExprNode* parseExpr(PARSER_PARAMS) { 748 | SKIP_NEWLINES() 749 | return parseFor(PARSER_ARGS); 750 | } 751 | 752 | struct piccolo_ExprNode* piccolo_parse(struct piccolo_Engine* engine, struct piccolo_Parser* parser) { 753 | return parseExprList(engine, parser, false); 754 | } 755 | 756 | void piccolo_initParser(struct piccolo_Engine* engine, struct piccolo_Parser* parser, struct piccolo_Scanner* scanner, struct piccolo_Package* package) { 757 | parser->scanner = scanner; 758 | parser->nodes = NULL; 759 | parser->hadError = false; 760 | parser->cycled = false; 761 | parser->package = package; 762 | advanceParser(engine, parser); 763 | } 764 | 765 | void piccolo_freeParser(struct piccolo_Engine* engine, struct piccolo_Parser* parser) { 766 | struct piccolo_ExprNode* curr = parser->nodes; 767 | while(curr != NULL) { 768 | struct piccolo_ExprNode* next = curr->nodes; 769 | if(curr->type == PICCOLO_EXPR_FN_LITERAL) { 770 | struct piccolo_FnLiteralNode* fnLiteral = (struct piccolo_FnLiteralNode*)curr; 771 | piccolo_freeTokenArray(engine, &fnLiteral->params); 772 | } 773 | free(curr); 774 | curr = next; 775 | } 776 | } 777 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_PARSER_H 3 | #define PICCOLO_PARSER_H 4 | 5 | #include "scanner.h" 6 | #include "engine.h" 7 | #include "util/dynarray.h" 8 | 9 | enum piccolo_ExprNodeType { 10 | PICCOLO_EXPR_LITERAL, 11 | PICCOLO_EXPR_ARRAY_LITERAL, 12 | PICCOLO_EXPR_HASHMAP_ENTRY, 13 | PICCOLO_EXPR_HASHMAP_LITERAL, 14 | PICCOLO_EXPR_VAR, 15 | PICCOLO_EXPR_RANGE, 16 | PICCOLO_EXPR_SUBSCRIPT, 17 | PICCOLO_EXPR_INDEX, 18 | PICCOLO_EXPR_UNARY, 19 | PICCOLO_EXPR_BINARY, 20 | PICCOLO_EXPR_BLOCK, 21 | PICCOLO_EXPR_FN_LITERAL, 22 | 23 | PICCOLO_EXPR_VAR_DECL, 24 | PICCOLO_EXPR_VAR_SET, 25 | PICCOLO_EXPR_SUBSCRIPT_SET, 26 | PICCOLO_EXPR_INDEX_SET, 27 | 28 | PICCOLO_EXPR_IF, 29 | PICCOLO_EXPR_WHILE, 30 | PICCOLO_EXPR_FOR, 31 | 32 | PICCOLO_EXPR_CALL, 33 | 34 | PICCOLO_EXPR_IMPORT, 35 | }; 36 | 37 | struct piccolo_ExprNode { 38 | struct piccolo_ExprNode* nodes; 39 | struct piccolo_ExprNode* nextExpr; 40 | enum piccolo_ExprNodeType type; 41 | bool reqEval; 42 | struct piccolo_Type* resultType; 43 | }; 44 | 45 | struct piccolo_LiteralNode { 46 | struct piccolo_ExprNode expr; 47 | struct piccolo_Token token; 48 | }; 49 | 50 | struct piccolo_ArrayLiteralNode { 51 | struct piccolo_ExprNode expr; 52 | struct piccolo_ExprNode* first; 53 | }; 54 | 55 | struct piccolo_HashmapEntryNode { 56 | struct piccolo_ExprNode expr; 57 | struct piccolo_ExprNode* key; 58 | struct piccolo_ExprNode* value; 59 | }; 60 | 61 | struct piccolo_HashmapLiteralNode { 62 | struct piccolo_ExprNode expr; 63 | struct piccolo_HashmapEntryNode* first; 64 | }; 65 | 66 | struct piccolo_VarNode { 67 | struct piccolo_ExprNode expr; 68 | struct piccolo_Token name; 69 | struct piccolo_VarDeclNode* decl; 70 | }; 71 | 72 | struct piccolo_RangeNode { 73 | struct piccolo_ExprNode expr; 74 | struct piccolo_ExprNode* left; 75 | struct piccolo_ExprNode* right; 76 | int charIdx; 77 | }; 78 | 79 | struct piccolo_SubscriptNode { 80 | struct piccolo_ExprNode expr; 81 | struct piccolo_Token subscript; 82 | struct piccolo_ExprNode* value; 83 | }; 84 | 85 | struct piccolo_IndexNode { 86 | struct piccolo_ExprNode expr; 87 | struct piccolo_ExprNode* target; 88 | struct piccolo_ExprNode* index; 89 | int charIdx; 90 | }; 91 | 92 | struct piccolo_UnaryNode { 93 | struct piccolo_ExprNode expr; 94 | struct piccolo_Token op; 95 | struct piccolo_ExprNode* value; 96 | }; 97 | 98 | struct piccolo_BinaryNode { 99 | struct piccolo_ExprNode expr; 100 | struct piccolo_Token op; 101 | struct piccolo_ExprNode* a; 102 | struct piccolo_ExprNode* b; 103 | }; 104 | 105 | struct piccolo_BlockNode { 106 | struct piccolo_ExprNode expr; 107 | struct piccolo_ExprNode* first; 108 | }; 109 | 110 | struct piccolo_FnLiteralNode { 111 | struct piccolo_ExprNode expr; 112 | struct piccolo_ExprNode* value; 113 | struct piccolo_TokenArray params; 114 | }; 115 | 116 | struct piccolo_VarDeclNode { 117 | struct piccolo_ExprNode expr; 118 | struct piccolo_Token name; 119 | struct piccolo_ExprNode* value; 120 | bool typed; 121 | bool Mutable; 122 | }; 123 | 124 | struct piccolo_VarSetNode { 125 | struct piccolo_ExprNode expr; 126 | struct piccolo_Token name; 127 | struct piccolo_ExprNode* value; 128 | struct piccolo_VarDeclNode* decl; 129 | }; 130 | 131 | struct piccolo_SubscriptSetNode { 132 | struct piccolo_ExprNode expr; 133 | struct piccolo_ExprNode* target; 134 | struct piccolo_Token subscript; 135 | struct piccolo_ExprNode* value; 136 | }; 137 | 138 | struct piccolo_IndexSetNode { 139 | struct piccolo_ExprNode expr; 140 | struct piccolo_ExprNode* target; 141 | struct piccolo_ExprNode* index; 142 | struct piccolo_ExprNode* value; 143 | int charIdx; 144 | }; 145 | 146 | struct piccolo_IfNode { 147 | struct piccolo_ExprNode expr; 148 | struct piccolo_ExprNode* condition; 149 | struct piccolo_ExprNode* trueVal; 150 | struct piccolo_ExprNode* falseVal; 151 | int conditionCharIdx; 152 | }; 153 | 154 | struct piccolo_WhileNode { 155 | struct piccolo_ExprNode expr; 156 | struct piccolo_ExprNode* condition; 157 | struct piccolo_ExprNode* value; 158 | int conditionCharIdx; 159 | }; 160 | 161 | struct piccolo_ForNode { 162 | struct piccolo_ExprNode expr; 163 | struct piccolo_Token name; 164 | struct piccolo_ExprNode* container; 165 | struct piccolo_ExprNode* value; 166 | int charIdx; 167 | int containerCharIdx; 168 | }; 169 | 170 | struct piccolo_CallNode { 171 | struct piccolo_ExprNode expr; 172 | struct piccolo_ExprNode* function; 173 | struct piccolo_ExprNode* firstArg; 174 | int charIdx; 175 | }; 176 | 177 | struct piccolo_ImportNode { 178 | struct piccolo_ExprNode expr; 179 | struct piccolo_Token packageName; 180 | struct piccolo_Package* package; 181 | bool resolved; 182 | }; 183 | 184 | struct piccolo_Parser { 185 | struct piccolo_Scanner* scanner; 186 | struct piccolo_ExprNode* nodes; 187 | struct piccolo_Token currToken; 188 | bool cycled, hadError; 189 | struct piccolo_Package* package; 190 | }; 191 | 192 | void piccolo_initParser(struct piccolo_Engine* engine, struct piccolo_Parser* parser, struct piccolo_Scanner* scanner, struct piccolo_Package* package); 193 | void piccolo_freeParser(struct piccolo_Engine* engine, struct piccolo_Parser* parser); 194 | 195 | struct piccolo_ExprNode* piccolo_parse(struct piccolo_Engine* engine, struct piccolo_Parser* parser); 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /scanner.c: -------------------------------------------------------------------------------- 1 | 2 | #include "scanner.h" 3 | #include 4 | #include 5 | 6 | void piccolo_initScanner(struct piccolo_Scanner* scanner, const char* source) { 7 | scanner->source = source; 8 | scanner->start = source; 9 | scanner->current = source; 10 | } 11 | 12 | static void skipWhitespace(struct piccolo_Scanner* scanner) { 13 | while(*scanner->current == ' ' || *scanner->current == '\t' || *scanner->current == '\r') { 14 | scanner->current++; 15 | } 16 | scanner->start = scanner->current; 17 | } 18 | 19 | static bool numeric(char c) { 20 | return c >= '0' && c <= '9'; 21 | } 22 | 23 | static bool alpha(char c) { 24 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; 25 | } 26 | 27 | static bool alphanumeric(char c) { 28 | return alpha(c) || numeric(c); 29 | } 30 | 31 | static enum piccolo_TokenType getKeyword(const char* start, const char* end) { 32 | #define TOKEN_TYPE(token, keyword) \ 33 | if(end - start == strlen(keyword) && memcmp(start, keyword, strlen(keyword)) == 0) \ 34 | return PICCOLO_TOKEN_ ## token; 35 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 36 | 37 | TOKEN_TYPE(NIL, "nil") 38 | TOKEN_TYPE(TRUE, "true") 39 | TOKEN_TYPE(FALSE, "false") 40 | 41 | TOKEN_TYPE(VAR, "var") 42 | TOKEN_TYPE(CONST, "const") 43 | TOKEN_TYPE(FN, "fn") 44 | TOKEN_TYPE(AND, "and") 45 | TOKEN_TYPE(OR, "or") 46 | TOKEN_TYPE(IF, "if") 47 | TOKEN_TYPE(ELSE, "else") 48 | TOKEN_TYPE(WHILE, "while") 49 | TOKEN_TYPE(FOR, "for") 50 | TOKEN_TYPE(IN, "in") 51 | TOKEN_TYPE(IMPORT, "import") 52 | TOKEN_TYPE(AS, "as") 53 | 54 | return PICCOLO_TOKEN_IDENTIFIER; 55 | #undef TOKEN_TYPE 56 | } 57 | 58 | static struct piccolo_Token makeToken(struct piccolo_Scanner* scanner, enum piccolo_TokenType type) { 59 | struct piccolo_Token token; 60 | token.start = scanner->start; 61 | token.length = scanner->current - scanner->start; 62 | token.type = type; 63 | token.charIdx = scanner->start - scanner->source; 64 | scanner->start = scanner->current; 65 | return token; 66 | } 67 | 68 | struct piccolo_Token piccolo_nextToken(struct piccolo_Scanner* scanner) { 69 | skipWhitespace(scanner); 70 | 71 | switch(*scanner->current) { 72 | case '+': { 73 | scanner->current++; 74 | return makeToken(scanner, PICCOLO_TOKEN_PLUS); 75 | } 76 | case '-': { 77 | scanner->current++; 78 | if(*scanner->current == '>') { 79 | scanner->current++; 80 | return makeToken(scanner, PICCOLO_TOKEN_ARROW); 81 | } 82 | return makeToken(scanner, PICCOLO_TOKEN_MINUS); 83 | } 84 | case '*': { 85 | scanner->current++; 86 | return makeToken(scanner, PICCOLO_TOKEN_STAR); 87 | } 88 | case ',': { 89 | scanner->current++; 90 | return makeToken(scanner, PICCOLO_TOKEN_COMMA); 91 | } 92 | case '/': { 93 | scanner->current++; 94 | return makeToken(scanner, PICCOLO_TOKEN_SLASH); 95 | } 96 | case '%': { 97 | scanner->current++; 98 | return makeToken(scanner, PICCOLO_TOKEN_PERCENT); 99 | } 100 | case '=': { 101 | scanner->current++; 102 | if(*scanner->current == '=') { 103 | scanner->current++; 104 | return makeToken(scanner, PICCOLO_TOKEN_EQ_EQ); 105 | } 106 | return makeToken(scanner, PICCOLO_TOKEN_EQ); 107 | } 108 | case '>': { 109 | scanner->current++; 110 | if(*scanner->current == '=') { 111 | scanner->current++; 112 | return makeToken(scanner, PICCOLO_TOKEN_GREATER_EQ); 113 | } 114 | return makeToken(scanner, PICCOLO_TOKEN_GREATER); 115 | } 116 | case '<': { 117 | scanner->current++; 118 | if(*scanner->current == '=') { 119 | scanner->current++; 120 | return makeToken(scanner, PICCOLO_TOKEN_LESS_EQ); 121 | } 122 | return makeToken(scanner, PICCOLO_TOKEN_LESS); 123 | } 124 | case '!': { 125 | scanner->current++; 126 | if(*scanner->current == '=') { 127 | scanner->current++; 128 | return makeToken(scanner, PICCOLO_TOKEN_BANG_EQ); 129 | } 130 | return makeToken(scanner, PICCOLO_TOKEN_BANG); 131 | } 132 | case '(': { 133 | scanner->current++; 134 | return makeToken(scanner, PICCOLO_TOKEN_LEFT_PAREN); 135 | } 136 | case ')': { 137 | scanner->current++; 138 | return makeToken(scanner, PICCOLO_TOKEN_RIGHT_PAREN); 139 | } 140 | case '[': { 141 | scanner->current++; 142 | return makeToken(scanner, PICCOLO_TOKEN_LEFT_SQR_PAREN); 143 | } 144 | case ']': { 145 | scanner->current++; 146 | return makeToken(scanner, PICCOLO_TOKEN_RIGHT_SQR_PAREN); 147 | } 148 | case '{': { 149 | scanner->current++; 150 | return makeToken(scanner, PICCOLO_TOKEN_LEFT_BRACE); 151 | } 152 | case '}': { 153 | scanner->current++; 154 | return makeToken(scanner, PICCOLO_TOKEN_RIGHT_BRACE); 155 | } 156 | case '\'': { 157 | scanner->current++; 158 | while(*scanner->current != '\'') { 159 | if(*scanner->current == '\0') { 160 | scanner->source = scanner->current; 161 | return makeToken(scanner, PICCOLO_TOKEN_ERROR); 162 | } 163 | scanner->current++; 164 | } 165 | scanner->current++; 166 | return makeToken(scanner, PICCOLO_TOKEN_STRING); 167 | } 168 | case '.': { 169 | scanner->current++; 170 | if(*scanner->current == '.') { 171 | scanner->current++; 172 | return makeToken(scanner, PICCOLO_TOKEN_DOT_DOT); 173 | } 174 | return makeToken(scanner, PICCOLO_TOKEN_DOT); 175 | } 176 | case '#': { 177 | while(*scanner->current != '\n' && *scanner->current != '\0') 178 | scanner->current++; 179 | scanner->start = scanner->current; 180 | return piccolo_nextToken(scanner); 181 | } 182 | case ':': { 183 | scanner->current++; 184 | return makeToken(scanner, PICCOLO_TOKEN_COLON); 185 | } 186 | case '\n': { 187 | scanner->current++; 188 | return makeToken(scanner, PICCOLO_TOKEN_NEWLINE); 189 | } 190 | case '\0': { 191 | return makeToken(scanner, PICCOLO_TOKEN_EOF); 192 | } 193 | } 194 | 195 | if(numeric(*scanner->start)) { 196 | while(numeric(*scanner->current)) { 197 | scanner->current++; 198 | } 199 | 200 | bool hadPeriod = *scanner->current == '.'; 201 | if(*scanner->current == '.') 202 | scanner->current++; 203 | if(*scanner->current == '.') { 204 | scanner->current--; 205 | return makeToken(scanner, PICCOLO_TOKEN_NUM); 206 | } 207 | 208 | if(hadPeriod && !numeric(*scanner->current)) { 209 | scanner->current--; 210 | } else { 211 | while (numeric(*scanner->current)) { 212 | scanner->current++; 213 | } 214 | } 215 | return makeToken(scanner, PICCOLO_TOKEN_NUM); 216 | } 217 | 218 | if(alpha(*scanner->start)) { 219 | while(alphanumeric(*scanner->current)) { 220 | scanner->current++; 221 | } 222 | return makeToken(scanner, getKeyword(scanner->start, scanner->current)); 223 | } 224 | 225 | scanner->current++; 226 | return makeToken(scanner, PICCOLO_TOKEN_ERROR); 227 | } 228 | -------------------------------------------------------------------------------- /scanner.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_SCANNER_H 3 | #define PICCOLO_SCANNER_H 4 | 5 | #include 6 | #include 7 | 8 | enum piccolo_TokenType { 9 | // Single chars 10 | PICCOLO_TOKEN_PLUS, PICCOLO_TOKEN_MINUS, PICCOLO_TOKEN_STAR, PICCOLO_TOKEN_SLASH, PICCOLO_TOKEN_PERCENT, 11 | PICCOLO_TOKEN_COMMA, 12 | PICCOLO_TOKEN_EQ, 13 | PICCOLO_TOKEN_GREATER, PICCOLO_TOKEN_LESS, 14 | PICCOLO_TOKEN_BANG, 15 | PICCOLO_TOKEN_LEFT_PAREN, PICCOLO_TOKEN_RIGHT_PAREN, 16 | PICCOLO_TOKEN_LEFT_SQR_PAREN, PICCOLO_TOKEN_RIGHT_SQR_PAREN, 17 | PICCOLO_TOKEN_LEFT_BRACE, PICCOLO_TOKEN_RIGHT_BRACE, 18 | PICCOLO_TOKEN_DOT, 19 | PICCOLO_TOKEN_COLON, 20 | 21 | // 2 chars 22 | PICCOLO_TOKEN_EQ_EQ, PICCOLO_TOKEN_BANG_EQ, 23 | PICCOLO_TOKEN_ARROW, 24 | PICCOLO_TOKEN_GREATER_EQ, PICCOLO_TOKEN_LESS_EQ, 25 | PICCOLO_TOKEN_DOT_DOT, 26 | 27 | // Literals 28 | PICCOLO_TOKEN_NUM, PICCOLO_TOKEN_NIL, PICCOLO_TOKEN_TRUE, PICCOLO_TOKEN_FALSE, PICCOLO_TOKEN_STRING, 29 | 30 | // Keywords 31 | PICCOLO_TOKEN_VAR, PICCOLO_TOKEN_CONST, 32 | PICCOLO_TOKEN_FN, 33 | PICCOLO_TOKEN_AND, PICCOLO_TOKEN_OR, 34 | PICCOLO_TOKEN_IF, PICCOLO_TOKEN_ELSE, 35 | PICCOLO_TOKEN_WHILE, 36 | PICCOLO_TOKEN_FOR, PICCOLO_TOKEN_IN, 37 | PICCOLO_TOKEN_IMPORT, PICCOLO_TOKEN_AS, 38 | 39 | // Misc 40 | PICCOLO_TOKEN_IDENTIFIER, 41 | PICCOLO_TOKEN_ERROR, 42 | 43 | PICCOLO_TOKEN_NEWLINE, 44 | PICCOLO_TOKEN_EOF, 45 | }; 46 | 47 | struct piccolo_Scanner { 48 | const char* source; 49 | const char* start; 50 | const char* current; 51 | }; 52 | 53 | struct piccolo_Token { 54 | const char* start; 55 | uint32_t charIdx; 56 | size_t length; 57 | enum piccolo_TokenType type; 58 | }; 59 | 60 | void piccolo_initScanner(struct piccolo_Scanner* scanner, const char* source); 61 | struct piccolo_Token piccolo_nextToken(struct piccolo_Scanner* scanner); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /stdlib/dll.c: -------------------------------------------------------------------------------- 1 | 2 | #include "../embedding.h" 3 | #include "../util/memory.h" 4 | #include "picStdlib.h" 5 | #include "../gc.h" 6 | #include "../util/file.h" 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | #include 15 | 16 | struct dll { 17 | 18 | #ifdef _WIN32 19 | HMODULE library; 20 | #else 21 | void* handle; 22 | #endif 23 | 24 | piccolo_Value close, get; 25 | 26 | }; 27 | 28 | static piccolo_Value dllCloseNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 29 | if(argc != 0) { 30 | piccolo_runtimeError(engine, "Wrong argument count."); 31 | return PICCOLO_NIL_VAL(); 32 | } 33 | struct piccolo_Obj* obj = PICCOLO_AS_OBJ(self); 34 | struct dll* dll = PICCOLO_GET_PAYLOAD(obj, struct dll); 35 | #ifdef _WIN32 36 | FreeLibrary(dll->library); 37 | #else 38 | dlclose(dll->handle); 39 | #endif 40 | return PICCOLO_NIL_VAL(); 41 | } 42 | 43 | static piccolo_Value dllGetNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 44 | if(argc != 1) { 45 | piccolo_runtimeError(engine, "Wrong argument count."); 46 | return PICCOLO_NIL_VAL(); 47 | } 48 | piccolo_Value symbolNameVal = argv[0]; 49 | if(!PICCOLO_IS_STRING(symbolNameVal)) { 50 | piccolo_runtimeError(engine, "Function name must be a string."); 51 | return PICCOLO_NIL_VAL(); 52 | } 53 | struct piccolo_ObjString* symbolName = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(symbolNameVal); 54 | struct piccolo_Obj* obj = PICCOLO_AS_OBJ(self); 55 | struct dll* dll = PICCOLO_GET_PAYLOAD(obj, struct dll); 56 | piccolo_Value (*native)(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self); 57 | #ifdef _WIN32 58 | void* dllObj = GetProcAddress(dll->library, symbolName->string); 59 | if(dllObj == NULL) { 60 | piccolo_runtimeError(engine, "Invalid DLL function."); 61 | return PICCOLO_NIL_VAL(); 62 | } 63 | native = dllObj; 64 | #else 65 | void* dllObj = dlsym(dll->handle, symbolName->string); 66 | if(dllObj == NULL) { 67 | piccolo_runtimeError(engine, "Invalid DLL function."); 68 | return PICCOLO_NIL_VAL(); 69 | } 70 | native = (piccolo_Value(*)(struct piccolo_Engine *, int, piccolo_Value *, piccolo_Value))dllObj; 71 | #endif 72 | return PICCOLO_OBJ_VAL(piccolo_makeNative(engine, native)); 73 | } 74 | 75 | static void gcMarkDll(void* payload) { 76 | struct dll* dll = (struct dll*)payload; 77 | piccolo_gcMarkValue(dll->close); 78 | piccolo_gcMarkValue(dll->get); 79 | } 80 | 81 | static piccolo_Value indexDll(void* payload, struct piccolo_Engine* engine, piccolo_Value key, bool set, piccolo_Value value) { 82 | struct dll* dll = (struct dll*)payload; 83 | if(!PICCOLO_IS_STRING(key)) { 84 | piccolo_runtimeError(engine, "Property must be a string."); 85 | return PICCOLO_NIL_VAL(); 86 | } 87 | struct piccolo_ObjString* keyStr = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(key); 88 | if(strcmp(keyStr->string, "close") == 0) { 89 | if(set) { 90 | piccolo_runtimeError(engine, "Cannot set close."); 91 | return PICCOLO_NIL_VAL(); 92 | } 93 | return dll->close; 94 | } 95 | if(strcmp(keyStr->string, "get") == 0) { 96 | if(set) { 97 | piccolo_runtimeError(engine, "Cannot set get."); 98 | return PICCOLO_NIL_VAL(); 99 | } 100 | return dll->get; 101 | } 102 | piccolo_runtimeError(engine, "Invalid property."); 103 | return PICCOLO_NIL_VAL(); 104 | } 105 | 106 | static piccolo_Value openNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 107 | if(argc != 1) { 108 | piccolo_runtimeError(engine, "Wrong argument count."); 109 | return PICCOLO_NIL_VAL(); 110 | } 111 | piccolo_Value pathVal = argv[0]; 112 | if(!PICCOLO_IS_STRING(pathVal)) { 113 | piccolo_runtimeError(engine, "Argument must be a string."); 114 | return PICCOLO_NIL_VAL(); 115 | } 116 | char* path = ((struct piccolo_ObjString*)PICCOLO_AS_OBJ(pathVal))->string; 117 | struct piccolo_ObjNativeStruct* dllNativeStruct = (struct piccolo_ObjNativeStruct*)PICCOLO_ALLOCATE_NATIVE_STRUCT(engine, struct dll, "dll"); 118 | dllNativeStruct->gcMark = gcMarkDll; 119 | dllNativeStruct->index = indexDll; 120 | struct dll* dll = PICCOLO_GET_PAYLOAD(dllNativeStruct, struct dll); 121 | 122 | #ifdef _WIN32 123 | dll->library = LoadLibrary(path); 124 | if(dll->library == NULL) { 125 | size_t pathLen = strlen(path); 126 | char buf[4096]; 127 | for(int i = 0; i < engine->searchPaths.count; i++) { 128 | piccolo_applyRelativePathToFilePath(buf, path, pathLen, engine->searchPaths.values[i]); 129 | dll->library = LoadLibrary(buf); 130 | if(dll->library != NULL) 131 | break; 132 | } 133 | if(dll->library == NULL) { 134 | piccolo_runtimeError(engine, "Could not open DLL."); 135 | return PICCOLO_NIL_VAL(); 136 | } 137 | } 138 | #else 139 | dll->handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); 140 | if(dll->handle == NULL) { 141 | size_t pathLen = strlen(path); 142 | char buf[4096]; 143 | for(int i = 0; i < engine->searchPaths.count; i++) { 144 | piccolo_applyRelativePathToFilePath(buf, path, pathLen, engine->searchPaths.values[i]); 145 | dll->handle = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL); 146 | if(dll->handle != NULL) 147 | break; 148 | } 149 | if(dll->handle == NULL) { 150 | piccolo_runtimeError(engine, "Could not open DLL."); 151 | return PICCOLO_NIL_VAL(); 152 | } 153 | } 154 | #endif 155 | dll->close = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, dllCloseNative, PICCOLO_OBJ_VAL(dllNativeStruct))); 156 | dll->get = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, dllGetNative, PICCOLO_OBJ_VAL(dllNativeStruct))); 157 | return PICCOLO_OBJ_VAL(dllNativeStruct); 158 | } 159 | 160 | void piccolo_addDLLLib(struct piccolo_Engine* engine) { 161 | struct piccolo_Package* dll = piccolo_createPackage(engine); 162 | dll->packageName = "dll"; 163 | struct piccolo_Type* str = piccolo_simpleType(engine, PICCOLO_TYPE_STR); 164 | struct piccolo_Type* any = piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 165 | piccolo_defineGlobalWithType(engine, dll, "open", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, openNative)), piccolo_makeFnType(engine, any, 1, str)); 166 | #ifdef __APPLE__ 167 | const char* extension = "dylib"; 168 | #endif 169 | #ifdef _WIN32 170 | const char* extension = "dll"; 171 | #endif 172 | #ifdef __linux__ 173 | const char* extension = "so"; 174 | #endif 175 | piccolo_defineGlobalWithType(engine, dll, "extension", PICCOLO_OBJ_VAL(piccolo_copyString(engine, extension, strlen(extension))), piccolo_simpleType(engine, PICCOLO_TYPE_STR)); 176 | } 177 | -------------------------------------------------------------------------------- /stdlib/file.c: -------------------------------------------------------------------------------- 1 | 2 | #include "picStdlib.h" 3 | #include "../util/file.h" 4 | #include 5 | #include 6 | #include "../embedding.h" 7 | #include "../gc.h" 8 | #include "../util/strutil.h" 9 | 10 | static piccolo_Value readNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 11 | if(argc != 1) { 12 | piccolo_runtimeError(engine, "Wrong argument count."); 13 | return PICCOLO_NIL_VAL(); 14 | } 15 | piccolo_Value pathVal = argv[0]; 16 | if(!PICCOLO_IS_STRING(pathVal)) { 17 | piccolo_runtimeError(engine, "Path must be a string."); 18 | return PICCOLO_NIL_VAL(); 19 | } 20 | char* path = ((struct piccolo_ObjString*)PICCOLO_AS_OBJ(pathVal))->string; 21 | char* contents = piccolo_readFile(path); 22 | if(contents == NULL) { 23 | piccolo_runtimeError(engine, "Could not read file."); 24 | return PICCOLO_NIL_VAL(); 25 | } 26 | struct piccolo_ObjString* stringObj = piccolo_takeString(engine, contents); 27 | return PICCOLO_OBJ_VAL(stringObj); 28 | } 29 | 30 | static piccolo_Value writeNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 31 | if(argc != 2) { 32 | piccolo_runtimeError(engine, "Wrong argument count."); 33 | return PICCOLO_NIL_VAL(); 34 | } 35 | piccolo_Value pathVal = argv[0]; 36 | if(!PICCOLO_IS_STRING(pathVal)) { 37 | piccolo_runtimeError(engine, "Path must be a string."); 38 | return PICCOLO_NIL_VAL(); 39 | } 40 | piccolo_Value data = argv[1]; 41 | if(!PICCOLO_IS_STRING(data)) { 42 | piccolo_runtimeError(engine, "Data must be a string."); 43 | return PICCOLO_NIL_VAL(); 44 | } 45 | FILE* file = fopen(((struct piccolo_ObjString*)PICCOLO_AS_OBJ(pathVal))->string, "w"); 46 | if(file == NULL) { 47 | piccolo_runtimeError(engine, "Could not open file."); 48 | return PICCOLO_NIL_VAL(); 49 | } 50 | fprintf(file, "%s", ((struct piccolo_ObjString*)PICCOLO_AS_OBJ(data))->string); 51 | fclose(file); 52 | return PICCOLO_NIL_VAL(); 53 | } 54 | 55 | struct file { 56 | FILE* file; 57 | piccolo_Value path, mode; 58 | piccolo_Value write, writeByte, readChar, close; 59 | }; 60 | 61 | static void gcMarkFile(void* payload) { 62 | struct file* file = (struct file*)payload; 63 | piccolo_gcMarkValue(file->path); 64 | piccolo_gcMarkValue(file->mode); 65 | 66 | piccolo_gcMarkValue(file->write); 67 | piccolo_gcMarkValue(file->writeByte); 68 | piccolo_gcMarkValue(file->readChar); 69 | piccolo_gcMarkValue(file->close); 70 | } 71 | 72 | static piccolo_Value indexFile(void* payload, struct piccolo_Engine* engine, piccolo_Value key, bool set, piccolo_Value value) { 73 | struct file* file = (struct file*)payload; 74 | if(!PICCOLO_IS_STRING(key)) { 75 | piccolo_runtimeError(engine, "Property must be a string."); 76 | return PICCOLO_NIL_VAL(); 77 | } 78 | struct piccolo_ObjString* keyStr = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(key); 79 | if(strcmp(keyStr->string, "path") == 0) { 80 | if(set) { 81 | piccolo_runtimeError(engine, "Cannot set path."); 82 | return PICCOLO_NIL_VAL(); 83 | } 84 | return file->path; 85 | } 86 | if(strcmp(keyStr->string, "mode") == 0) { 87 | if(set) { 88 | piccolo_runtimeError(engine, "Cannot set mode."); 89 | return PICCOLO_NIL_VAL(); 90 | } 91 | return file->mode; 92 | } 93 | 94 | if(strcmp(keyStr->string, "write") == 0) { 95 | if(set) { 96 | piccolo_runtimeError(engine, "Cannot set write."); 97 | return PICCOLO_NIL_VAL(); 98 | } 99 | return file->write; 100 | } 101 | if(strcmp(keyStr->string, "writeByte") == 0) { 102 | if(set) { 103 | piccolo_runtimeError(engine, "Cannot set write."); 104 | return PICCOLO_NIL_VAL(); 105 | } 106 | return file->writeByte; 107 | } 108 | if(strcmp(keyStr->string, "readChar") == 0) { 109 | if(set) { 110 | piccolo_runtimeError(engine, "Cannot set readChar."); 111 | return PICCOLO_NIL_VAL(); 112 | } 113 | return file->readChar; 114 | } 115 | if(strcmp(keyStr->string, "close") == 0) { 116 | if(set) { 117 | piccolo_runtimeError(engine, "Cannot set close."); 118 | return PICCOLO_NIL_VAL(); 119 | } 120 | return file->close; 121 | } 122 | 123 | piccolo_runtimeError(engine, "Invalid property."); 124 | return PICCOLO_NIL_VAL(); 125 | } 126 | 127 | static piccolo_Value fileWriteNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 128 | if(argc != 1) { 129 | piccolo_runtimeError(engine, "Wrong argument count."); 130 | return PICCOLO_NIL_VAL(); 131 | } 132 | piccolo_Value data = argv[0]; 133 | if(!PICCOLO_IS_STRING(data)) { 134 | piccolo_runtimeError(engine, "Data must be a string."); 135 | return PICCOLO_NIL_VAL(); 136 | } 137 | struct file* file = PICCOLO_GET_PAYLOAD(PICCOLO_AS_OBJ(self), struct file); 138 | struct piccolo_ObjString* str = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(data); 139 | fprintf(file->file, "%s", str->string); 140 | return PICCOLO_NIL_VAL(); 141 | } 142 | 143 | static piccolo_Value fileWriteByteNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 144 | if(argc != 1) { 145 | piccolo_runtimeError(engine, "Wrong argument count."); 146 | return PICCOLO_NIL_VAL(); 147 | } 148 | piccolo_Value byte = argv[0]; 149 | if(!PICCOLO_IS_NUM(byte)) { 150 | piccolo_runtimeError(engine, "Byte must be a number."); 151 | return PICCOLO_NIL_VAL(); 152 | } 153 | struct file* file = PICCOLO_GET_PAYLOAD(PICCOLO_AS_OBJ(self), struct file); 154 | fprintf(file->file, "%c", (int)PICCOLO_AS_NUM(byte)); 155 | return PICCOLO_NIL_VAL(); 156 | } 157 | 158 | static piccolo_Value fileReadCharNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 159 | if(argc != 0) { 160 | piccolo_runtimeError(engine, "Wrong argument count."); 161 | return PICCOLO_NIL_VAL(); 162 | } 163 | struct file* file = PICCOLO_GET_PAYLOAD(PICCOLO_AS_OBJ(self), struct file); 164 | char c = fgetc(file->file); 165 | if(c == EOF) { 166 | return PICCOLO_NIL_VAL(); 167 | } 168 | char str[5]; 169 | str[0] = c; 170 | int chars = piccolo_strutil_utf8Chars(c); 171 | for(int i = 1; i < chars; i++) { 172 | str[i] = fgetc(file->file); 173 | } 174 | str[chars] = '\0'; 175 | return PICCOLO_OBJ_VAL(piccolo_copyString(engine, str, chars)); 176 | } 177 | 178 | static piccolo_Value fileCloseNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 179 | if(argc != 0) { 180 | piccolo_runtimeError(engine, "Wrong argument count."); 181 | return PICCOLO_NIL_VAL(); 182 | } 183 | struct file* file = PICCOLO_GET_PAYLOAD(PICCOLO_AS_OBJ(self), struct file); 184 | fclose(file->file); 185 | return PICCOLO_NIL_VAL(); 186 | } 187 | 188 | static piccolo_Value openNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 189 | if(argc != 2) { 190 | piccolo_runtimeError(engine, "Wrong argument count."); 191 | return PICCOLO_NIL_VAL(); 192 | } 193 | piccolo_Value pathVal = argv[0]; 194 | if(!PICCOLO_IS_STRING(pathVal)) { 195 | piccolo_runtimeError(engine, "Path must be a string."); 196 | return PICCOLO_NIL_VAL(); 197 | } 198 | piccolo_Value modeVal = argv[1]; 199 | if(!PICCOLO_IS_STRING(modeVal)) { 200 | piccolo_runtimeError(engine, "Invalid file mode."); 201 | return PICCOLO_NIL_VAL(); 202 | } 203 | struct piccolo_ObjString* mode = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(modeVal); 204 | if(mode->len != 1) { 205 | piccolo_runtimeError(engine, "Invalid file mode."); 206 | return PICCOLO_NIL_VAL(); 207 | } 208 | char modeChar = mode->string[0]; 209 | if(modeChar != 'r' && modeChar != 'w') { 210 | piccolo_runtimeError(engine, "Invalid file mode."); 211 | return PICCOLO_NIL_VAL(); 212 | } 213 | char* path = ((struct piccolo_ObjString*)PICCOLO_AS_OBJ(pathVal))->string; 214 | FILE* file = fopen(path, modeChar == 'r' ? "r" : "w"); 215 | if(file == NULL) { 216 | piccolo_runtimeError(engine, "Could not open file."); 217 | return PICCOLO_NIL_VAL(); 218 | } 219 | struct piccolo_ObjNativeStruct* fileObj = (struct piccolo_ObjNativeStruct*)PICCOLO_ALLOCATE_NATIVE_STRUCT(engine, struct file, "file"); 220 | fileObj->gcMark = gcMarkFile; 221 | fileObj->index = indexFile; 222 | struct file* payload = PICCOLO_GET_PAYLOAD(fileObj, struct file); 223 | payload->file = file; 224 | payload->path = pathVal; 225 | payload->mode = modeVal; 226 | 227 | payload->write = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, fileWriteNative, PICCOLO_OBJ_VAL(fileObj))); 228 | payload->writeByte = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, fileWriteByteNative, PICCOLO_OBJ_VAL(fileObj))); 229 | payload->readChar = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, fileReadCharNative, PICCOLO_OBJ_VAL(fileObj))); 230 | payload->close = PICCOLO_OBJ_VAL(piccolo_makeBoundNative(engine, fileCloseNative, PICCOLO_OBJ_VAL(fileObj))); 231 | 232 | return PICCOLO_OBJ_VAL(fileObj); 233 | } 234 | 235 | void piccolo_addFileLib(struct piccolo_Engine* engine) { 236 | struct piccolo_Package* file = piccolo_createPackage(engine); 237 | file->packageName = "file"; 238 | struct piccolo_Type* str = piccolo_simpleType(engine, PICCOLO_TYPE_STR); 239 | piccolo_defineGlobalWithType(engine, file, "read", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, readNative)), piccolo_makeFnType(engine, str, 1, str)); 240 | piccolo_defineGlobalWithType(engine, file, "write", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, writeNative)), piccolo_makeFnType(engine, piccolo_simpleType(engine, PICCOLO_TYPE_NIL), 2, str, str)); 241 | piccolo_defineGlobalWithType(engine, file, "open", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, openNative)), piccolo_makeFnType(engine, piccolo_simpleType(engine, PICCOLO_TYPE_ANY), 2, str, str)); 242 | } 243 | -------------------------------------------------------------------------------- /stdlib/io.c: -------------------------------------------------------------------------------- 1 | 2 | #include "picStdlib.h" 3 | #include "../embedding.h" 4 | #include "../util/memory.h" 5 | #include 6 | 7 | static piccolo_Value printNative(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self) { 8 | for(int i = 0; i < argc; i++) { 9 | piccolo_printValue(args[i]); 10 | printf(" "); 11 | } 12 | printf("\n"); 13 | return PICCOLO_NIL_VAL(); 14 | } 15 | 16 | static piccolo_Value inputNative(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self) { 17 | if(argc != 0) { 18 | piccolo_runtimeError(engine, "Wrong argument count."); 19 | return PICCOLO_NIL_VAL(); 20 | } 21 | 22 | size_t lenMax = 64; 23 | size_t len = 0; 24 | char* line = (char*)PICCOLO_REALLOCATE("input string", engine, NULL, 0, lenMax); 25 | char* curr = line; 26 | for(;;) { 27 | if(line == NULL) { 28 | piccolo_runtimeError(engine, "Could not read input."); 29 | return PICCOLO_NIL_VAL(); 30 | } 31 | int c = fgetc(stdin); 32 | if(c == EOF || c == '\n') { 33 | break; 34 | } 35 | *curr = c; 36 | curr++; 37 | len++; 38 | if(len >= lenMax) { 39 | lenMax *= 2; 40 | line = (char*)PICCOLO_REALLOCATE("input string", engine, line, lenMax / 2, lenMax); 41 | } 42 | } 43 | *curr = '\0'; 44 | return PICCOLO_OBJ_VAL(piccolo_takeString(engine, line)); 45 | } 46 | 47 | void piccolo_addIOLib(struct piccolo_Engine* engine) { 48 | struct piccolo_Package* io = piccolo_createPackage(engine); 49 | io->packageName = "io"; 50 | struct piccolo_Type* str = piccolo_simpleType(engine, PICCOLO_TYPE_STR); 51 | piccolo_defineGlobal(engine, io, "print", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, printNative))); 52 | piccolo_defineGlobalWithType(engine, io, "input", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, inputNative)), piccolo_makeFnType(engine, str, 0)); 53 | } 54 | -------------------------------------------------------------------------------- /stdlib/math.c: -------------------------------------------------------------------------------- 1 | 2 | #include "picStdlib.h" 3 | #include "../embedding.h" 4 | #include "../util/memory.h" 5 | #include 6 | 7 | static piccolo_Value minNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 8 | if(argc != 2) { 9 | piccolo_runtimeError(engine, "Wrong argument count."); 10 | return PICCOLO_NIL_VAL(); 11 | } 12 | piccolo_Value aVal = argv[0]; 13 | piccolo_Value bVal = argv[1]; 14 | if(!PICCOLO_IS_NUM(aVal) || !PICCOLO_IS_NUM(bVal)) { 15 | piccolo_runtimeError(engine, "Both arguments must be numbers."); 16 | return PICCOLO_NIL_VAL(); 17 | } 18 | 19 | double a = PICCOLO_AS_NUM(aVal); 20 | double b = PICCOLO_AS_NUM(bVal); 21 | return PICCOLO_NUM_VAL(a < b ? a : b); 22 | } 23 | 24 | static piccolo_Value maxNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 25 | if(argc != 2) { 26 | piccolo_runtimeError(engine, "Wrong argument count."); 27 | return PICCOLO_NIL_VAL(); 28 | } 29 | piccolo_Value aVal = argv[0]; 30 | piccolo_Value bVal = argv[1]; 31 | if(!PICCOLO_IS_NUM(aVal) || !PICCOLO_IS_NUM(bVal)) { 32 | piccolo_runtimeError(engine, "Both arguments must be numbers."); 33 | return PICCOLO_NIL_VAL(); 34 | } 35 | 36 | double a = PICCOLO_AS_NUM(aVal); 37 | double b = PICCOLO_AS_NUM(bVal); 38 | return PICCOLO_NUM_VAL(a > b ? a : b); 39 | } 40 | 41 | static piccolo_Value mapNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 42 | if(argc != 5) { 43 | piccolo_runtimeError(engine, "Wrong argument count."); 44 | return PICCOLO_NIL_VAL(); 45 | } else { 46 | for(int i = 0; i < 5; i++) { 47 | if(!PICCOLO_IS_NUM(argv[i])) { 48 | piccolo_runtimeError(engine, "All arguments must be numbers."); 49 | return PICCOLO_NIL_VAL(); 50 | } 51 | } 52 | double value = PICCOLO_AS_NUM(argv[0]); 53 | double fromL = PICCOLO_AS_NUM(argv[1]); 54 | double fromR = PICCOLO_AS_NUM(argv[2]); 55 | double toL = PICCOLO_AS_NUM(argv[3]); 56 | double toR = PICCOLO_AS_NUM(argv[4]); 57 | double result = ((value - fromL) / (fromR - fromL)) * (toR - toL) + toL; 58 | return PICCOLO_NUM_VAL(result); 59 | } 60 | } 61 | 62 | #include 63 | 64 | static piccolo_Value sinNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 65 | if(argc != 1) { 66 | piccolo_runtimeError(engine, "Wrong argument count."); 67 | } else { 68 | if(!PICCOLO_IS_NUM(argv[0])) { 69 | piccolo_runtimeError(engine, "Angle must be a number."); 70 | } else { 71 | double angle = PICCOLO_AS_NUM(argv[0]); 72 | double result = sin(angle); 73 | return PICCOLO_NUM_VAL(result); 74 | } 75 | } 76 | return PICCOLO_NIL_VAL(); 77 | } 78 | 79 | static piccolo_Value cosNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 80 | if(argc != 1) { 81 | piccolo_runtimeError(engine, "Wrong argument count."); 82 | } else { 83 | if(!PICCOLO_IS_NUM(argv[0])) { 84 | piccolo_runtimeError(engine, "Angle must be a number."); 85 | } else { 86 | double angle = PICCOLO_AS_NUM(argv[0]); 87 | double result = cos(angle); 88 | return PICCOLO_NUM_VAL(result); 89 | } 90 | } 91 | return PICCOLO_NIL_VAL(); 92 | } 93 | 94 | static piccolo_Value tanNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 95 | if(argc != 1) { 96 | piccolo_runtimeError(engine, "Wrong argument count."); 97 | } else { 98 | if(!PICCOLO_IS_NUM(argv[0])) { 99 | piccolo_runtimeError(engine, "Angle must be a number."); 100 | } else { 101 | double angle = PICCOLO_AS_NUM(argv[0]); 102 | double result = tan(angle); 103 | return PICCOLO_NUM_VAL(result); 104 | } 105 | } 106 | return PICCOLO_NIL_VAL(); 107 | } 108 | 109 | static piccolo_Value floorNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 110 | if(argc != 1) { 111 | piccolo_runtimeError(engine, "Wrong argument count."); 112 | } else { 113 | if(!PICCOLO_IS_NUM(argv[0])) { 114 | piccolo_runtimeError(engine, "Value must be a number."); 115 | } else { 116 | double val = PICCOLO_AS_NUM(argv[0]); 117 | int64_t valFloored = val; 118 | return PICCOLO_NUM_VAL(valFloored); 119 | } 120 | } 121 | return PICCOLO_NIL_VAL(); 122 | } 123 | 124 | static piccolo_Value sqrtNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 125 | if(argc != 1) { 126 | piccolo_runtimeError(engine, "Wrong argument count."); 127 | } else { 128 | if(!PICCOLO_IS_NUM(argv[0])) { 129 | piccolo_runtimeError(engine, "Value must be a number."); 130 | } else { 131 | double val = PICCOLO_AS_NUM(argv[0]); 132 | return PICCOLO_NUM_VAL(sqrt(val)); 133 | } 134 | } 135 | return PICCOLO_NIL_VAL(); 136 | } 137 | 138 | void piccolo_addMathLib(struct piccolo_Engine* engine) { 139 | struct piccolo_Package* math = piccolo_createPackage(engine); 140 | math->packageName = "math"; 141 | 142 | struct piccolo_Type* num = piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 143 | struct piccolo_Type* numToNum = piccolo_makeFnType(engine, num, 1, num); 144 | struct piccolo_Type* twoNumToNum = piccolo_makeFnType(engine, num, 2, num, num); 145 | 146 | piccolo_defineGlobalWithType(engine, math, "min", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, minNative)), twoNumToNum); 147 | piccolo_defineGlobalWithType(engine, math, "max", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, maxNative)), twoNumToNum); 148 | piccolo_defineGlobalWithType(engine, math, "map", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, mapNative)), piccolo_makeFnType(engine, num, 5, num, num, num, num, num)); 149 | piccolo_defineGlobalWithType(engine, math, "pi", PICCOLO_NUM_VAL(3.14159265359), num); 150 | piccolo_defineGlobalWithType(engine, math, "sin", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, sinNative)), numToNum); 151 | piccolo_defineGlobalWithType(engine, math, "cos", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, cosNative)), numToNum); 152 | piccolo_defineGlobalWithType(engine, math, "tan", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, tanNative)), numToNum); 153 | piccolo_defineGlobalWithType(engine, math, "floor", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, floorNative)), numToNum); 154 | piccolo_defineGlobalWithType(engine, math, "sqrt", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, sqrtNative)), numToNum); 155 | } 156 | -------------------------------------------------------------------------------- /stdlib/os.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "../embedding.h" 4 | #include "../util/memory.h" 5 | #include "picStdlib.h" 6 | #include 7 | #include 8 | 9 | static piccolo_Value shellNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 10 | if(argc != 1) { 11 | piccolo_runtimeError(engine, "Wrong argument count."); 12 | return PICCOLO_NIL_VAL(); 13 | } 14 | piccolo_Value cmdVal = argv[0]; 15 | if(!PICCOLO_IS_STRING(cmdVal)) { 16 | piccolo_runtimeError(engine, "Command must be a string."); 17 | return PICCOLO_NIL_VAL(); 18 | } 19 | struct piccolo_ObjString* cmd = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(cmdVal); 20 | system(cmd->string); 21 | return PICCOLO_NIL_VAL(); 22 | } 23 | 24 | void piccolo_addOSLib(struct piccolo_Engine* engine) { 25 | struct piccolo_Package* os = piccolo_createPackage(engine); 26 | os->packageName = "os"; 27 | struct piccolo_Type* str = piccolo_simpleType(engine, PICCOLO_TYPE_STR); 28 | struct piccolo_Type* nil = piccolo_simpleType(engine, PICCOLO_TYPE_NIL); 29 | piccolo_defineGlobalWithType(engine, os, "shell", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, shellNative)), piccolo_makeFnType(engine, nil, 1, str)); 30 | } 31 | -------------------------------------------------------------------------------- /stdlib/picStdlib.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_STDLIB_H 3 | #define PICCOLO_STDLIB_H 4 | 5 | #include "../engine.h" 6 | 7 | void piccolo_addIOLib(struct piccolo_Engine* engine); 8 | void piccolo_addTimeLib(struct piccolo_Engine* engine); 9 | void piccolo_addMathLib(struct piccolo_Engine* engine); 10 | void piccolo_addRandomLib(struct piccolo_Engine* engine); 11 | void piccolo_addStrLib(struct piccolo_Engine* engine); 12 | void piccolo_addFileLib(struct piccolo_Engine* engine); 13 | void piccolo_addDLLLib(struct piccolo_Engine* engine); 14 | void piccolo_addOSLib(struct piccolo_Engine* engine); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /stdlib/random.c: -------------------------------------------------------------------------------- 1 | 2 | #include "../embedding.h" 3 | #include "../util/memory.h" 4 | #include "picStdlib.h" 5 | #include 6 | #include 7 | 8 | static piccolo_Value randomValNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 9 | if(argc != 0) { 10 | piccolo_runtimeError(engine, "Wrong argument count."); 11 | return PICCOLO_NIL_VAL(); 12 | } 13 | double val = (double)rand() / RAND_MAX; 14 | return PICCOLO_NUM_VAL(val); 15 | } 16 | 17 | void piccolo_addRandomLib(struct piccolo_Engine* engine) { 18 | struct piccolo_Package* random = piccolo_createPackage(engine); 19 | random->packageName = "random"; 20 | struct piccolo_Type* num = piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 21 | piccolo_defineGlobalWithType(engine, random, "val", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, randomValNative)), piccolo_makeFnType(engine, num, 0)); 22 | } 23 | -------------------------------------------------------------------------------- /stdlib/str.c: -------------------------------------------------------------------------------- 1 | 2 | #include "picStdlib.h" 3 | #include "../embedding.h" 4 | #include "../util/memory.h" 5 | #include "../util/strutil.h" 6 | 7 | #include 8 | #include 9 | 10 | static piccolo_Value getCodeNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 11 | if(argc != 1) { 12 | piccolo_runtimeError(engine, "Wrong argument count."); 13 | return PICCOLO_NIL_VAL(); 14 | } 15 | piccolo_Value val = argv[0]; 16 | if(!PICCOLO_IS_STRING(val)) { 17 | piccolo_runtimeError(engine, "Argument must be a string."); 18 | return PICCOLO_NIL_VAL(); 19 | } 20 | struct piccolo_ObjString* str = (struct piccolo_ObjString*)PICCOLO_AS_OBJ(val); 21 | if(str->utf8Len != 1) { 22 | piccolo_runtimeError(engine, "Argument must be a single character string."); 23 | return PICCOLO_NIL_VAL(); 24 | } 25 | int charSize = piccolo_strutil_utf8Chars(*str->string); 26 | int code = 0; 27 | if(charSize == 1) { 28 | code = *str->string; 29 | } 30 | if(charSize == 2) { 31 | code = ((*str->string & 0x1F) << 6) + (*(str->string + 1) & 0x3F); 32 | } 33 | if(charSize == 3) { 34 | code = ((*str->string & 0x0F) << 12) + ((*(str->string + 1) & 0x3F) << 6) + (*(str->string + 2) & 0x3F); 35 | } 36 | if(charSize == 4) { 37 | code = ((*str->string & 0x07) << 18) + ((*(str->string + 1) & 0x3F) << 12) + ((*(str->string + 2) & 0x3F) << 6) + (*(str->string + 3) & 0x3F); 38 | } 39 | return PICCOLO_NUM_VAL(code); 40 | } 41 | 42 | static piccolo_Value numToStrNative(struct piccolo_Engine* engine, int argc, piccolo_Value* argv, piccolo_Value self) { 43 | if(argc != 1) { 44 | piccolo_runtimeError(engine, "Wrong argument count."); 45 | return PICCOLO_NIL_VAL(); 46 | } 47 | piccolo_Value val = argv[0]; 48 | if(!PICCOLO_IS_NUM(val)) { 49 | piccolo_runtimeError(engine, "Argument must be a number."); 50 | return PICCOLO_NIL_VAL(); 51 | } 52 | char buf[32]; 53 | sprintf(buf, "%g", PICCOLO_AS_NUM(val)); 54 | return PICCOLO_OBJ_VAL(piccolo_copyString(engine, buf, strlen(buf))); 55 | } 56 | 57 | void piccolo_addStrLib(struct piccolo_Engine* engine) { 58 | struct piccolo_Package* str = piccolo_createPackage(engine); 59 | str->packageName = "str"; 60 | struct piccolo_Type* strType = piccolo_simpleType(engine, PICCOLO_TYPE_STR); 61 | struct piccolo_Type* num = piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 62 | piccolo_defineGlobalWithType(engine, str, "utfCode", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, getCodeNative)), piccolo_makeFnType(engine, num, 1, strType)); 63 | piccolo_defineGlobalWithType(engine, str, "numToStr", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, numToStrNative)), piccolo_makeFnType(engine, strType, 1, num)); 64 | } 65 | -------------------------------------------------------------------------------- /stdlib/time.c: -------------------------------------------------------------------------------- 1 | 2 | #include "picStdlib.h" 3 | #include "../embedding.h" 4 | #include "../util/memory.h" 5 | #include 6 | 7 | static piccolo_Value clockNative(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self) { 8 | double time = (double)clock() / (double)CLOCKS_PER_SEC; 9 | if(argc != 0) { 10 | piccolo_runtimeError(engine, "Wrong argument count."); 11 | } 12 | return PICCOLO_NUM_VAL(time); 13 | } 14 | 15 | static piccolo_Value sleepNative(struct piccolo_Engine* engine, int argc, struct piccolo_Value* args, piccolo_Value self) { 16 | if(argc != 1) { 17 | piccolo_runtimeError(engine, "Wrong argument count."); 18 | } else { 19 | if(!PICCOLO_IS_NUM(args[0])) { 20 | piccolo_runtimeError(engine, "Sleep time must be a number."); 21 | } else { 22 | double time = PICCOLO_AS_NUM(args[0]); 23 | clock_t startTime = clock(); 24 | while(clock() - startTime < time * CLOCKS_PER_SEC) {} 25 | } 26 | } 27 | return PICCOLO_NIL_VAL(); 28 | } 29 | 30 | void piccolo_addTimeLib(struct piccolo_Engine* engine) { 31 | struct piccolo_Package* time = piccolo_createPackage(engine); 32 | time->packageName = "time"; 33 | struct piccolo_Type* num = piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 34 | struct piccolo_Type* nil = piccolo_simpleType(engine, PICCOLO_TYPE_NIL); 35 | piccolo_defineGlobalWithType(engine, time, "clock", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, clockNative)), piccolo_makeFnType(engine, num, 0)); 36 | piccolo_defineGlobalWithType(engine, time, "sleep", PICCOLO_OBJ_VAL(piccolo_makeNative(engine, sleepNative)), piccolo_makeFnType(engine, nil, 1, num)); 37 | } 38 | -------------------------------------------------------------------------------- /typecheck.c: -------------------------------------------------------------------------------- 1 | 2 | #include "typecheck.h" 3 | #include "compiler.h" 4 | #include "package.h" 5 | #include "parser.h" 6 | #include "object.h" 7 | #include 8 | #include 9 | #include 10 | 11 | PICCOLO_DYNARRAY_IMPL(struct piccolo_Type*, Type) 12 | 13 | void piccolo_getTypename(struct piccolo_Type* type, char* dest) { 14 | char* originalDest = dest; 15 | if(type == NULL) { 16 | sprintf(dest, "NULL"); 17 | return; 18 | } 19 | switch(type->type) { 20 | case PICCOLO_TYPE_ANY: { 21 | sprintf(dest, "any"); 22 | break; 23 | } 24 | case PICCOLO_TYPE_NUM: { 25 | sprintf(dest, "num"); 26 | break; 27 | } 28 | case PICCOLO_TYPE_STR: { 29 | sprintf(dest, "str"); 30 | break; 31 | } 32 | case PICCOLO_TYPE_BOOL: { 33 | sprintf(dest, "bool"); 34 | break; 35 | } 36 | case PICCOLO_TYPE_NIL: { 37 | sprintf(dest, "nil"); 38 | break; 39 | } 40 | case PICCOLO_TYPE_ARRAY: { 41 | char buf[256]; 42 | piccolo_getTypename(type->subtypes.listElem, buf); 43 | snprintf(dest, 256, "[%s]", buf); 44 | break; 45 | } 46 | case PICCOLO_TYPE_HASHMAP: { 47 | char keyBuf[256]; 48 | piccolo_getTypename(type->subtypes.hashmap.key, keyBuf); 49 | char valBuf[256]; 50 | piccolo_getTypename(type->subtypes.hashmap.val, valBuf); 51 | char strKeyBuf[256]; 52 | strKeyBuf[0] = '\0'; 53 | char* ptr = strKeyBuf; 54 | size_t sizeLeft = 256; 55 | for(int i = 0; i < type->subtypes.hashmap.strKeys.count; i++) { 56 | char strValBuf[256]; 57 | piccolo_getTypename(type->subtypes.hashmap.strTypes.values[i], strValBuf); 58 | struct piccolo_Token* strKey = &type->subtypes.hashmap.strKeys.values[i]; 59 | size_t taken = snprintf(ptr, sizeLeft, "'%.*s' : %s, ", strKey->length - 2, strKey->start + 1, strValBuf); 60 | ptr += taken; 61 | sizeLeft -= taken; 62 | } 63 | snprintf(dest, 256, "{%s%s : %s}", strKeyBuf, keyBuf, valBuf); 64 | break; 65 | } 66 | case PICCOLO_TYPE_FN: { 67 | dest += sprintf(dest, "fn "); 68 | size_t sizeLeft = 256 - 3; 69 | size_t taken; 70 | for(int i = 0; i < type->subtypes.fn.params.count; i++) { 71 | char buf[256]; 72 | piccolo_getTypename(type->subtypes.fn.params.values[i], buf); 73 | taken = snprintf(dest, sizeLeft, i == 0 ? "%s" : ", %s", buf); 74 | dest += taken; 75 | sizeLeft -= taken; 76 | } 77 | taken = snprintf(dest, sizeLeft, type->subtypes.fn.params.count == 0 ? "-> " : " -> "); 78 | dest += taken; 79 | sizeLeft -= taken; 80 | char buf[256]; 81 | piccolo_getTypename(type->subtypes.fn.result, buf); 82 | dest += snprintf(dest, sizeLeft, "%s", buf); 83 | break; 84 | } 85 | case PICCOLO_TYPE_PKG: { 86 | sprintf(dest, "pkg"); 87 | break; 88 | } 89 | case PICCOLO_TYPE_UNION: { 90 | size_t sizeLeft = 256; 91 | for(int i = 0; i < type->subtypes.unionTypes.types.count; i++) { 92 | char buf[256]; 93 | piccolo_getTypename(type->subtypes.unionTypes.types.values[i], buf); 94 | size_t taken = snprintf(dest, sizeLeft, i == 0 ? "%s" : " | %s", buf); 95 | dest += taken; 96 | sizeLeft -= taken; 97 | } 98 | break; 99 | } 100 | } 101 | dest = originalDest; 102 | dest[252] = '.'; 103 | dest[253] = '.'; 104 | dest[254] = '.'; 105 | dest[255] = '\0'; 106 | } 107 | 108 | static uint64_t typeToNum(struct piccolo_Type* type) { // TODO: make this more intelligent lol 109 | union { 110 | struct piccolo_Type* type; 111 | uint64_t ptr; 112 | } x; 113 | x.type = type; 114 | return x.ptr; 115 | } 116 | 117 | static void sortTypeArray(struct piccolo_TypeArray* types, int start, int size) { // TODO: perhaps make this part of the dynarr macro madness? 118 | if(size == 1 || size == 0) 119 | return; 120 | sortTypeArray(types, start, size / 2); 121 | sortTypeArray(types, start + size / 2, size - size / 2); 122 | struct picolo_Type* temp[size]; 123 | int a = 0, b = 0; 124 | for(int i = 0; i < size; i++) { 125 | if(b == size - size / 2 || typeToNum(types->values[start + a]) < typeToNum(types->values[start + size / 2 + b])) { 126 | temp[i] = types->values[start + a]; 127 | a++; 128 | } else { 129 | temp[i] = types->values[start + size / 2 + b]; 130 | b++; 131 | } 132 | } 133 | memcpy(&types->values[start], temp, size * sizeof(struct piccolo_Type*)); 134 | } 135 | 136 | void piccolo_freeType(struct piccolo_Engine* engine, struct piccolo_Type* type) { 137 | if(type->type == PICCOLO_TYPE_FN) { 138 | piccolo_freeTypeArray(engine, &type->subtypes.fn.params); 139 | } 140 | if(type->type == PICCOLO_TYPE_HASHMAP) { 141 | piccolo_freeTokenArray(engine, &type->subtypes.hashmap.strKeys); 142 | piccolo_freeTypeArray(engine, &type->subtypes.hashmap.strTypes); 143 | } 144 | if(type->type == PICCOLO_TYPE_UNION) { 145 | piccolo_freeTypeArray(engine, &type->subtypes.unionTypes.types); 146 | } 147 | } 148 | 149 | static bool isAny(struct piccolo_Type* type) { 150 | return type->type == PICCOLO_TYPE_ANY; 151 | } 152 | 153 | static bool isNum(struct piccolo_Type* type) { 154 | return type->type == PICCOLO_TYPE_NUM || type->type == PICCOLO_TYPE_ANY; 155 | } 156 | 157 | static bool isStr(struct piccolo_Type* type) { 158 | return type->type == PICCOLO_TYPE_STR || type->type == PICCOLO_TYPE_ANY; 159 | } 160 | 161 | static bool isBool(struct piccolo_Type* type) { 162 | return type->type == PICCOLO_TYPE_BOOL || type->type == PICCOLO_TYPE_ANY; 163 | } 164 | 165 | static bool isFn(struct piccolo_Type* type) { 166 | return type->type == PICCOLO_TYPE_FN || type->type == PICCOLO_TYPE_ANY; 167 | } 168 | 169 | static bool isPkg(struct piccolo_Type* type) { 170 | return type->type == PICCOLO_TYPE_PKG || type->type == PICCOLO_TYPE_ANY; 171 | } 172 | 173 | static bool isArr(struct piccolo_Type* type) { 174 | return type->type == PICCOLO_TYPE_ARRAY || type->type == PICCOLO_TYPE_ANY; 175 | } 176 | 177 | static bool isHashmap(struct piccolo_Type* type) { 178 | return type->type == PICCOLO_TYPE_HASHMAP || type->type == PICCOLO_TYPE_ANY; 179 | } 180 | 181 | static struct piccolo_Type* allocType(struct piccolo_Engine* engine, enum piccolo_TypeType type) { 182 | struct piccolo_Type* newType = PICCOLO_REALLOCATE("type", engine, NULL, 0, sizeof(struct piccolo_Type)); 183 | newType->type = type; 184 | newType->next = engine->types; 185 | engine->types = newType; 186 | return newType; 187 | } 188 | 189 | struct piccolo_Type* piccolo_simpleType(struct piccolo_Engine* engine, enum piccolo_TypeType type) { 190 | struct piccolo_Type* curr = engine->types; 191 | while(curr != NULL) { 192 | if(curr->type == type) 193 | return curr; 194 | curr = curr->next; 195 | } 196 | return allocType(engine, type); 197 | } 198 | 199 | struct piccolo_Type* piccolo_arrayType(struct piccolo_Engine* engine, struct piccolo_Type* elemType) { 200 | struct piccolo_Type* curr = engine->types; 201 | while(curr != NULL) { 202 | if(curr->type == PICCOLO_TYPE_ARRAY && curr->subtypes.listElem == elemType) 203 | return curr; 204 | curr = curr->next; 205 | } 206 | struct piccolo_Type* result = allocType(engine, PICCOLO_TYPE_ARRAY); 207 | result->subtypes.listElem = elemType; 208 | return result; 209 | } 210 | 211 | struct piccolo_Type* piccolo_hashmapType(struct piccolo_Engine* engine, struct piccolo_Type* keyType, struct piccolo_Type* valType, struct piccolo_TokenArray* strKeys, struct piccolo_TypeArray* strTypes) { 212 | struct piccolo_Type* curr = engine->types; 213 | while(curr != NULL) { 214 | if(curr->type == PICCOLO_TYPE_HASHMAP) { 215 | bool allStrKeysMatch = true; 216 | for(int i = 0; i < strKeys->count; i++) { 217 | bool found = false; 218 | for(int j = 0; j < curr->subtypes.hashmap.strKeys.count; j++) { 219 | struct piccolo_Token* otherToken = &curr->subtypes.hashmap.strKeys.values[j]; 220 | if(strKeys->values[i].length == otherToken->length && 221 | memcmp(strKeys->values[i].start, otherToken->start, strKeys->values[i].length) == 0) { 222 | if(strTypes->values[i] == curr->subtypes.hashmap.strTypes.values[j]) { 223 | found = true; 224 | break; 225 | } 226 | } 227 | } 228 | if(!found) 229 | allStrKeysMatch = false; 230 | } 231 | if(curr->subtypes.hashmap.key == keyType && 232 | curr->subtypes.hashmap.val == valType && allStrKeysMatch) { 233 | piccolo_freeTokenArray(engine, strKeys); 234 | piccolo_freeTypeArray(engine, strTypes); 235 | return curr; 236 | } 237 | } 238 | curr = curr->next; 239 | } 240 | struct piccolo_Type* result = allocType(engine, PICCOLO_TYPE_HASHMAP); 241 | result->subtypes.hashmap.key = keyType; 242 | result->subtypes.hashmap.val = valType; 243 | result->subtypes.hashmap.strKeys = *strKeys; 244 | result->subtypes.hashmap.strTypes = *strTypes; 245 | return result; 246 | } 247 | 248 | static bool typeArrEq(struct piccolo_TypeArray* a, struct piccolo_TypeArray* b) { 249 | if(a->count != b->count) 250 | return false; 251 | for(int i = 0; i < a->count; i++) 252 | if(a->values[i] != b->values[i]) 253 | return false; 254 | return true; 255 | } 256 | 257 | struct piccolo_Type* piccolo_fnType(struct piccolo_Engine* engine, struct piccolo_TypeArray* params, struct piccolo_Type* res) { 258 | struct piccolo_Type* curr = engine->types; 259 | while(curr != NULL) { 260 | if(curr->type == PICCOLO_TYPE_FN && curr->subtypes.fn.result == res && typeArrEq(params, &curr->subtypes.fn.params)) { 261 | piccolo_freeTypeArray(engine, params); 262 | return curr; 263 | } 264 | curr = curr->next; 265 | } 266 | struct piccolo_Type* fnType = allocType(engine, PICCOLO_TYPE_FN); 267 | fnType->subtypes.fn.params = *params; 268 | fnType->subtypes.fn.result = res; 269 | return fnType; 270 | } 271 | 272 | struct piccolo_Type* piccolo_pkgType(struct piccolo_Engine* engine, struct piccolo_Package* pkg) { 273 | struct piccolo_Type* curr = engine->types; 274 | while(curr != NULL) { 275 | if(curr->type == PICCOLO_TYPE_PKG && curr->subtypes.pkg == pkg) 276 | return curr; 277 | curr = curr->next; 278 | } 279 | struct piccolo_Type* pkgType = allocType(engine, PICCOLO_TYPE_PKG); 280 | pkgType->subtypes.pkg = pkg; 281 | return pkgType; 282 | } 283 | 284 | struct piccolo_Type* piccolo_unionType(struct piccolo_Engine* engine, struct piccolo_TypeArray* types) { 285 | sortTypeArray(types, 0, types->count); 286 | struct piccolo_Type* curr = engine->types; 287 | while(curr != NULL) { 288 | if(curr->type == PICCOLO_TYPE_UNION && typeArrEq(&curr->subtypes.unionTypes.types, types)) { // TODO: dedupe permutations of types. Atm, num | str and str | num are considered different and memory is wasted 289 | piccolo_freeTypeArray(engine, types); 290 | return curr; 291 | } 292 | curr = curr->next; 293 | } 294 | struct piccolo_Type* unionType = allocType(engine, PICCOLO_TYPE_UNION); 295 | unionType->subtypes.unionTypes.types = *types; 296 | return unionType; 297 | } 298 | 299 | static bool isSubtype(struct piccolo_Type* super, struct piccolo_Type* sub) { 300 | if(isAny(super) || isAny(sub)) 301 | return true; 302 | if(super == sub) 303 | return true; 304 | if(super->type == PICCOLO_TYPE_UNION) { 305 | if(sub->type == PICCOLO_TYPE_UNION) { 306 | for(int i = 0; i < sub->subtypes.unionTypes.types.count; i++) { 307 | bool found = false; 308 | for(int j = 0; j < super->subtypes.unionTypes.types.count; j++) { 309 | if(isSubtype(super->subtypes.unionTypes.types.values[j], sub->subtypes.unionTypes.types.values[i])) { 310 | found = true; 311 | break; 312 | } 313 | } 314 | if(!found) 315 | return false; 316 | } 317 | return true; 318 | } else { 319 | for(int i = 0; i < super->subtypes.unionTypes.types.count; i++) { 320 | if(isSubtype(super->subtypes.unionTypes.types.values[i], sub)) 321 | return true; 322 | } 323 | } 324 | } 325 | if(super->type == PICCOLO_TYPE_ARRAY) { 326 | if(sub->type == PICCOLO_TYPE_ARRAY) { 327 | return isSubtype(super->subtypes.listElem, sub->subtypes.listElem); 328 | } 329 | } 330 | if(super->type == PICCOLO_TYPE_HASHMAP) { 331 | if(sub->type == PICCOLO_TYPE_HASHMAP) { 332 | if(!isSubtype(super->subtypes.hashmap.key, sub->subtypes.hashmap.key)) 333 | return false; 334 | if(!isSubtype(super->subtypes.hashmap.val, sub->subtypes.hashmap.val)) 335 | return false; 336 | for(int i = 0; i < sub->subtypes.hashmap.strKeys.count; i++) { 337 | bool found = false; 338 | for(int j = 0; j < super->subtypes.hashmap.strKeys.count; j++) { 339 | struct piccolo_Token* a = &sub->subtypes.hashmap.strKeys.values[i]; 340 | struct piccolo_Token* b = &super->subtypes.hashmap.strKeys.values[j]; 341 | if(a->length == b->length && memcmp(a->start, b->start, a->length) == 0) { 342 | if(isSubtype(super->subtypes.hashmap.strTypes.values[j], sub->subtypes.hashmap.strTypes.values[i])) { 343 | found = true; 344 | break; 345 | } 346 | } 347 | } 348 | if(!found) 349 | return false; 350 | } 351 | return true; 352 | } 353 | } 354 | return false; 355 | } 356 | 357 | static struct piccolo_Type* mergeTypes(struct piccolo_Engine* engine, struct piccolo_Type* a, struct piccolo_Type* b) { 358 | if(a == NULL) 359 | return b; 360 | if(b == NULL) 361 | return a; 362 | if(a == b) 363 | return a; 364 | if(isSubtype(a, b)) 365 | return a; 366 | if(isSubtype(b, a)) 367 | return b; 368 | struct piccolo_TypeArray res; 369 | piccolo_initTypeArray(&res); 370 | if(a->type == PICCOLO_TYPE_UNION) { 371 | for(int i = 0; i < a->subtypes.unionTypes.types.count; i++) 372 | piccolo_writeTypeArray(engine, &res, a->subtypes.unionTypes.types.values[i]); 373 | } else { 374 | piccolo_writeTypeArray(engine, &res, a); 375 | } 376 | if(b->type == PICCOLO_TYPE_UNION) { 377 | for(int i = 0; i < b->subtypes.unionTypes.types.count; i++) { 378 | struct piccolo_Type* t = b->subtypes.unionTypes.types.values[i]; 379 | bool found = false; 380 | for(int i = 0; i < res.count; i++) { 381 | if(isSubtype(res.values[i], t)) { 382 | found = true; 383 | break; 384 | } 385 | } 386 | if(!found) 387 | piccolo_writeTypeArray(engine, &res, t); 388 | } 389 | } else { 390 | bool found = false; 391 | for(int i = 0; i < res.count; i++) { 392 | if(isSubtype(res.values[i], b)) { 393 | found = true; 394 | break; 395 | } 396 | } 397 | if(!found) 398 | piccolo_writeTypeArray(engine, &res, b); 399 | } 400 | return piccolo_unionType(engine, &res); // TODO: add union type 401 | } 402 | 403 | static struct piccolo_Type* getSubscriptType(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_Type* valType, struct piccolo_Token* subscript) { 404 | #define IS_SUBSCRIPT(str) (subscript->length == strlen(str) && memcmp(str, subscript->start, subscript->length) == 0) 405 | if(isAny(valType)) { 406 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 407 | } else if(isArr(valType) || isStr(valType)) { 408 | if(IS_SUBSCRIPT("length")) { 409 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 410 | } 411 | } else if(isHashmap(valType)) { 412 | for(int i = 0; i < valType->subtypes.hashmap.strKeys.count; i++) { 413 | struct piccolo_Token* key = &valType->subtypes.hashmap.strKeys.values[i]; 414 | if(key->length - 2 == subscript->length && memcmp(key->start + 1, subscript->start, subscript->length) == 0) { 415 | return valType->subtypes.hashmap.strTypes.values[i]; 416 | } 417 | } 418 | 419 | struct piccolo_Type* keyType = valType->subtypes.hashmap.key; 420 | if(isStr(keyType)) { 421 | return valType->subtypes.hashmap.val; 422 | } 423 | } else if(isPkg(valType)) { 424 | struct piccolo_Package* pkg = valType->subtypes.pkg; 425 | if(pkg == NULL) 426 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 427 | struct piccolo_ObjString* indexStr = piccolo_copyString(engine, subscript->start, subscript->length); 428 | int globalIdx = piccolo_getGlobalTable(engine, &pkg->globalIdxs, indexStr); 429 | if(globalIdx != -1) { 430 | struct piccolo_Type* varType = pkg->types.values[globalIdx & ~PICCOLO_GLOBAL_SLOT_MUTABLE_BIT]; 431 | return varType == NULL ? piccolo_simpleType(engine, PICCOLO_TYPE_ANY) : varType; 432 | } 433 | } 434 | char buf[256]; 435 | piccolo_getTypename(valType, buf); 436 | piccolo_compilationError(engine, compiler, subscript->charIdx, "Property '%.*s' does not exsist on %s.", subscript->length, subscript->start, buf); 437 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 438 | #undef IS_SUBSCRIPT 439 | } 440 | 441 | static struct piccolo_Type* getIndexType(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_Type* targetType, struct piccolo_Type* indexType, int charIdx) { 442 | if(isAny(targetType)) 443 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 444 | if(isArr(targetType) && isNum(indexType)) { 445 | return targetType->subtypes.listElem; 446 | } 447 | if(isStr(targetType) && isNum(indexType)) 448 | return piccolo_simpleType(engine, PICCOLO_TYPE_STR); 449 | if(isHashmap(targetType)) { 450 | struct piccolo_Type* keyType = targetType->subtypes.hashmap.key; 451 | if(isSubtype(keyType, indexType)) 452 | return targetType->subtypes.hashmap.val; 453 | } 454 | if(isPkg(targetType)) { 455 | if(isStr(indexType)) 456 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 457 | } 458 | 459 | char targetBuf[256]; 460 | piccolo_getTypename(targetType, targetBuf); 461 | char indexBuf[256]; 462 | piccolo_getTypename(indexType, indexBuf); 463 | piccolo_compilationError(engine, compiler, charIdx, "Cannot index %s with %s.", targetBuf, indexBuf); 464 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 465 | } 466 | 467 | static struct piccolo_Type* getType(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_ExprNode* expr) { 468 | switch(expr->type) { 469 | case PICCOLO_EXPR_LITERAL: { 470 | struct piccolo_LiteralNode* literal = (struct piccolo_LiteralNode*)expr; 471 | switch(literal->token.type) { 472 | case PICCOLO_TOKEN_NUM: 473 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 474 | case PICCOLO_TOKEN_STRING: 475 | return piccolo_simpleType(engine, PICCOLO_TYPE_STR); 476 | case PICCOLO_TOKEN_FALSE: 477 | case PICCOLO_TOKEN_TRUE: 478 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 479 | case PICCOLO_TOKEN_NIL: 480 | return piccolo_simpleType(engine, PICCOLO_TYPE_NIL); 481 | default: 482 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 483 | } 484 | } 485 | case PICCOLO_EXPR_ARRAY_LITERAL: { 486 | struct piccolo_ArrayLiteralNode* arrayLiteral = (struct piccolo_ArrayLiteralNode*)expr; 487 | struct piccolo_Type* elemType = NULL; 488 | struct piccolo_ExprNode* curr = arrayLiteral->first; 489 | while(curr != NULL) { 490 | elemType = mergeTypes(engine, elemType, piccolo_getType(engine, compiler, curr)); 491 | curr = curr->nextExpr; 492 | } 493 | if(elemType == NULL) 494 | elemType = piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 495 | return piccolo_arrayType(engine, elemType); 496 | } 497 | case PICCOLO_EXPR_HASHMAP_ENTRY: { 498 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 499 | } 500 | case PICCOLO_EXPR_HASHMAP_LITERAL: { 501 | struct piccolo_HashmapLiteralNode* hashmapLiteral = (struct piccolo_HashmapLiteralNode*)expr; 502 | struct piccolo_HashmapEntryNode* currEntry = hashmapLiteral->first; 503 | 504 | struct piccolo_Type* keyType = NULL; 505 | struct piccolo_Type* valType = NULL; 506 | 507 | struct piccolo_TokenArray strKeys; 508 | struct piccolo_TypeArray strTypes; 509 | piccolo_initTokenArray(&strKeys); 510 | piccolo_initTypeArray(&strTypes); 511 | 512 | while(currEntry != NULL) { 513 | struct piccolo_ExprNode* keyExpr = currEntry->key; 514 | struct piccolo_Type* currValType = piccolo_getType(engine, compiler, currEntry->value); 515 | 516 | if(keyExpr->type == PICCOLO_EXPR_LITERAL) { 517 | struct piccolo_LiteralNode* keyLiteral = keyExpr; 518 | if(keyLiteral->token.type == PICCOLO_TOKEN_STRING) { 519 | piccolo_writeTokenArray(engine, &strKeys, keyLiteral->token); 520 | piccolo_writeTypeArray(engine, &strTypes, currValType); 521 | } 522 | } 523 | keyType = mergeTypes(engine, keyType, piccolo_getType(engine, compiler, keyExpr)); 524 | valType = mergeTypes(engine, valType, currValType); 525 | currEntry = currEntry->expr.nextExpr; 526 | } 527 | if(keyType == NULL) 528 | keyType = piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 529 | if(valType == NULL) 530 | valType = piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 531 | 532 | struct piccolo_Type* result = piccolo_hashmapType(engine, keyType, valType, &strKeys, &strTypes); 533 | return result; 534 | } 535 | case PICCOLO_EXPR_VAR: { 536 | struct piccolo_VarNode* var = (struct piccolo_VarNode*)expr; 537 | if(var->decl == NULL) 538 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 539 | return var->decl->typed ? piccolo_getType(engine, compiler, var->decl->value) : piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 540 | } 541 | case PICCOLO_EXPR_RANGE: { 542 | struct piccolo_RangeNode* range = (struct piccolo_RangeNode*)expr; 543 | struct piccolo_Type* lType = piccolo_getType(engine, compiler, range->left); 544 | struct piccolo_Type* rType = piccolo_getType(engine, compiler, range->right); 545 | if(!(isNum(lType) && isNum(rType))) { 546 | char lBuf[256]; 547 | piccolo_getTypename(lType, lBuf); 548 | char rBuf[256]; 549 | piccolo_getTypename(rType, rBuf); 550 | piccolo_compilationError(engine, compiler, range->charIdx, "Cannot create range between %s and %s.", lBuf, rBuf); 551 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 552 | } 553 | return piccolo_arrayType(engine, piccolo_simpleType(engine, PICCOLO_TYPE_NUM)); 554 | } 555 | case PICCOLO_EXPR_SUBSCRIPT: { 556 | struct piccolo_SubscriptNode* subscript = (struct piccolo_SubscriptNode*)expr; 557 | return getSubscriptType(engine, compiler, piccolo_getType(engine, compiler, subscript->value), &subscript->subscript); 558 | } 559 | case PICCOLO_EXPR_INDEX: { 560 | struct piccolo_IndexNode* index = (struct piccolo_IndexNode*)expr; 561 | struct piccolo_Type* targetType = piccolo_getType(engine, compiler, index->target); 562 | struct piccolo_Type* indexType = piccolo_getType(engine, compiler, index->index); 563 | return getIndexType(engine, compiler, targetType, indexType, index->charIdx); 564 | } 565 | case PICCOLO_EXPR_UNARY: { 566 | struct piccolo_UnaryNode* unary = (struct piccolo_UnaryNode*)expr; 567 | struct piccolo_Type* valType = piccolo_getType(engine, compiler, unary->value); 568 | switch(unary->op.type) { 569 | case PICCOLO_TOKEN_MINUS: { 570 | if(isNum(valType)) { 571 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 572 | } else { 573 | char buf[256]; 574 | piccolo_getTypename(valType, buf); 575 | piccolo_compilationError(engine, compiler, unary->op.charIdx, "Cannot negate %s.", buf); 576 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 577 | } 578 | } 579 | case PICCOLO_TOKEN_BANG: { 580 | if(isBool(valType)) { 581 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 582 | } else { 583 | char buf[256]; 584 | piccolo_getTypename(valType, buf); 585 | piccolo_compilationError(engine, compiler, unary->op.charIdx, "Cannot invert %s.", buf); 586 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 587 | } 588 | } 589 | } 590 | } 591 | case PICCOLO_EXPR_BINARY: { 592 | struct piccolo_BinaryNode* binary = (struct piccolo_BinaryNode*)expr; 593 | struct piccolo_Type* aType = piccolo_getType(engine, compiler, binary->a); 594 | struct piccolo_Type* bType = piccolo_getType(engine, compiler, binary->b); 595 | if(isAny(aType) && isAny(bType)) 596 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 597 | char aBuf[256]; 598 | piccolo_getTypename(aType, aBuf); 599 | char bBuf[256]; 600 | piccolo_getTypename(bType, bBuf); 601 | switch(binary->op.type) { 602 | case PICCOLO_TOKEN_PLUS: { 603 | if(isNum(aType) && isNum(bType)) { 604 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 605 | } 606 | if(isStr(aType) && isStr(bType)) { 607 | return piccolo_simpleType(engine, PICCOLO_TYPE_STR); 608 | } 609 | if(isArr(aType) && isArr(bType)) { 610 | return piccolo_arrayType(engine, mergeTypes(engine, aType->subtypes.listElem, bType->subtypes.listElem)); 611 | } 612 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot add %s and %s.", aBuf, bBuf); 613 | break; 614 | } 615 | case PICCOLO_TOKEN_MINUS: { 616 | if(isNum(aType) && isNum(bType)) { 617 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 618 | } 619 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot subtract %s from %s.", bBuf, aBuf); 620 | break; 621 | } 622 | case PICCOLO_TOKEN_SLASH: { 623 | if(isNum(aType) && isNum(bType)) { 624 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 625 | } 626 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot divide %s by %s.", aBuf, bBuf); 627 | break; 628 | } 629 | case PICCOLO_TOKEN_PERCENT: { 630 | if(isNum(aType) && isNum(bType)) { 631 | return piccolo_simpleType(engine, PICCOLO_TYPE_NUM); 632 | } 633 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot modulo %s by %s.", aBuf, bBuf); 634 | break; 635 | } 636 | case PICCOLO_TOKEN_STAR: { 637 | if(isNum(aType) && (isNum(bType) || isStr(bType) || isArr(bType))) { 638 | return bType; 639 | } 640 | if(isNum(bType) && (isNum(aType) || isStr(aType) || isArr(aType))) { 641 | return aType; 642 | } 643 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot multiply %s by %s.", aBuf, bBuf); 644 | break; 645 | } 646 | case PICCOLO_TOKEN_AND: { 647 | if(isBool(aType) && isBool(bType)) { 648 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 649 | } 650 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot and %s and %s.", aBuf, bBuf); 651 | break; 652 | } 653 | case PICCOLO_TOKEN_OR: { 654 | if(isBool(aType) && isBool(bType)) { 655 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 656 | } 657 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot or %s and %s.", aBuf, bBuf); 658 | break; 659 | } 660 | case PICCOLO_TOKEN_LESS: 661 | case PICCOLO_TOKEN_GREATER: 662 | case PICCOLO_TOKEN_LESS_EQ: 663 | case PICCOLO_TOKEN_GREATER_EQ: { 664 | if(isNum(aType) && isNum(bType)) 665 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 666 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot compare %s and %s.", aBuf, bBuf); 667 | break; 668 | } 669 | case PICCOLO_TOKEN_EQ_EQ: 670 | case PICCOLO_TOKEN_BANG_EQ: 671 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 672 | case PICCOLO_TOKEN_IN: { 673 | if(isAny(bType) || (bType->type == PICCOLO_TYPE_HASHMAP && isSubtype(bType->subtypes.hashmap.key, aType))) { 674 | return piccolo_simpleType(engine, PICCOLO_TYPE_BOOL); 675 | } 676 | piccolo_compilationError(engine, compiler, binary->op.charIdx, "Cannot check if %s is in %s.", aBuf, bBuf); 677 | } 678 | } 679 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 680 | } 681 | case PICCOLO_EXPR_BLOCK: { 682 | struct piccolo_BlockNode* block = (struct piccolo_BlockNode*)expr; 683 | struct piccolo_Type* res = piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 684 | struct piccolo_ExprNode* curr = block->first; 685 | while(curr != NULL) { 686 | res = piccolo_getType(engine, compiler, curr); 687 | curr = curr->nextExpr; 688 | } 689 | return res; 690 | } 691 | case PICCOLO_EXPR_FN_LITERAL: { 692 | struct piccolo_FnLiteralNode* fn = (struct piccolo_FnLiteralNode*)expr; 693 | struct piccolo_TypeArray paramTypes; 694 | piccolo_initTypeArray(¶mTypes); 695 | for(int i = 0; i < fn->params.count; i++) 696 | piccolo_writeTypeArray(engine, ¶mTypes, piccolo_simpleType(engine, PICCOLO_TYPE_ANY)); 697 | struct piccolo_Type* resType = piccolo_getType(engine, compiler, fn->value); 698 | return piccolo_fnType(engine, ¶mTypes, resType); 699 | } 700 | case PICCOLO_EXPR_VAR_DECL: { 701 | struct piccolo_VarDeclNode* varDecl = (struct piccolo_VarDeclNode*)expr; 702 | return piccolo_getType(engine, compiler, varDecl->value); 703 | } 704 | case PICCOLO_EXPR_VAR_SET: { 705 | struct piccolo_VarSetNode* varSet = (struct piccolo_VarSetNode*)expr; 706 | struct piccolo_Type* valType = piccolo_getType(engine, compiler, varSet->value); 707 | if(varSet->decl == NULL || !varSet->decl->typed) 708 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 709 | struct piccolo_Type* targetType = piccolo_getType(engine, compiler, varSet->decl->value); 710 | if(!isSubtype(targetType, valType)) { 711 | char varTypeBuf[256]; 712 | piccolo_getTypename(targetType, varTypeBuf); 713 | char valTypeBuf[256]; 714 | piccolo_getTypename(valType, valTypeBuf); 715 | piccolo_compilationError(engine, compiler, varSet->name.charIdx, "Cannot assign %s to variable of type %s.", valTypeBuf, varTypeBuf); 716 | } 717 | return valType; 718 | } 719 | case PICCOLO_EXPR_SUBSCRIPT_SET: { 720 | struct piccolo_SubscriptSetNode* subscriptSet = (struct piccolo_SubscriptSetNode*)expr; 721 | struct piccolo_Type* newValType = piccolo_getType(engine, compiler, subscriptSet->value); 722 | struct piccolo_Type* targetType = getSubscriptType(engine, compiler, piccolo_getType(engine, compiler, subscriptSet->target), &subscriptSet->subscript); 723 | if(isSubtype(targetType, newValType)) { 724 | return newValType; 725 | } 726 | char newValBuf[256]; 727 | piccolo_getTypename(newValType, newValBuf); 728 | char targetBuf[256]; 729 | piccolo_getTypename(targetType, targetBuf); 730 | piccolo_compilationError(engine, compiler, subscriptSet->subscript.charIdx, "Cannot assign %s to %s.", newValBuf, targetBuf); 731 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 732 | } 733 | case PICCOLO_EXPR_INDEX_SET: { 734 | struct piccolo_IndexSetNode* indexSet = (struct piccolo_IndexSetNode*)expr; 735 | struct piccolo_Type* targetType = piccolo_getType(engine, compiler, indexSet->target); 736 | struct piccolo_Type* indexType = piccolo_getType(engine, compiler, indexSet->index); 737 | struct piccolo_Type* valType = getIndexType(engine, compiler, targetType, indexType, indexSet->charIdx); 738 | struct piccolo_Type* newValType = piccolo_getType(engine, compiler, indexSet->value); 739 | if(isSubtype(valType, newValType)) { 740 | return newValType; 741 | } 742 | char newValBuf[256]; 743 | piccolo_getTypename(newValType, newValBuf); 744 | char valBuf[256]; 745 | piccolo_getTypename(valType, valBuf); 746 | piccolo_compilationError(engine, compiler, indexSet->charIdx, "Cannot assign %s to %s.", newValBuf, valBuf); 747 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 748 | } 749 | case PICCOLO_EXPR_IF: { 750 | struct piccolo_IfNode* ifNode = (struct piccolo_IfNode*)expr; 751 | struct piccolo_Type* conditionType = piccolo_getType(engine, compiler, ifNode->condition); 752 | struct piccolo_Type* aType = piccolo_getType(engine, compiler, ifNode->trueVal); 753 | struct piccolo_Type* bType = ifNode->falseVal == NULL ? NULL : piccolo_getType(engine, compiler, ifNode->falseVal); 754 | if(!isBool(conditionType)) { 755 | piccolo_compilationError(engine, compiler, ifNode->conditionCharIdx, "Condition must be a bool."); 756 | } 757 | return mergeTypes(engine, aType, bType); 758 | } 759 | case PICCOLO_EXPR_WHILE: { 760 | struct piccolo_WhileNode* whileNode = (struct piccolo_WhileNode*)expr; 761 | struct piccolo_Type* conditionType = piccolo_getType(engine, compiler, whileNode->condition); 762 | struct piccolo_Type* valType = piccolo_getType(engine, compiler, whileNode->value); 763 | if(!isBool(conditionType)) { 764 | piccolo_compilationError(engine, compiler, whileNode->conditionCharIdx, "Condition must be a bool."); 765 | } 766 | return piccolo_arrayType(engine, valType); 767 | } 768 | case PICCOLO_EXPR_FOR: { 769 | struct piccolo_ForNode* forNode = (struct piccolo_ForNode*)expr; 770 | struct piccolo_Type* containerType = piccolo_getType(engine, compiler, forNode->container); 771 | struct piccolo_Type* valType = piccolo_getType(engine, compiler, forNode->value); 772 | if(!isArr(containerType) && !isHashmap(containerType) && !isStr(containerType)) { 773 | char buf[256]; 774 | piccolo_getTypename(containerType, buf); 775 | piccolo_compilationError(engine, compiler, forNode->containerCharIdx, "Cannot iterate over %s.", buf); 776 | } 777 | return piccolo_arrayType(engine, valType); 778 | } 779 | 780 | case PICCOLO_EXPR_CALL: { 781 | struct piccolo_CallNode* call = (struct piccolo_CallNode*)expr; 782 | struct piccolo_Type* fnType = piccolo_getType(engine, compiler, call->function); 783 | if(isAny(fnType)) { 784 | struct piccolo_ExprNode* arg = call->firstArg; 785 | while(arg != NULL) { 786 | piccolo_getType(engine, compiler, arg); 787 | arg = arg->nextExpr; 788 | } 789 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 790 | } 791 | if(!isFn(fnType)) 792 | goto callError; 793 | struct piccolo_ExprNode* arg = call->firstArg; 794 | int currArg = 0; 795 | while(arg != NULL) { 796 | struct piccolo_Type* argType = piccolo_getType(engine, compiler, arg); 797 | if(!isAny(fnType) && (currArg >= fnType->subtypes.fn.params.count || !isSubtype(fnType->subtypes.fn.params.values[currArg], argType))) { 798 | goto callError; 799 | } 800 | arg = arg->nextExpr; 801 | currArg++; 802 | } 803 | if(currArg < fnType->subtypes.fn.params.count) 804 | goto callError; 805 | if(isAny(fnType)) 806 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 807 | return fnType->subtypes.fn.result; 808 | 809 | callError: 810 | currArg = 0; 811 | char fnBuf[256]; 812 | piccolo_getTypename(fnType, fnBuf); 813 | char paramBuf[256]; 814 | paramBuf[0] = 0; 815 | char* paramDest = paramBuf; 816 | arg = call->firstArg; 817 | while(arg != NULL) { 818 | char typeBuf[256]; 819 | piccolo_getTypename(piccolo_getType(engine, compiler, arg), typeBuf); 820 | paramDest += sprintf(paramDest, arg->nextExpr == NULL ? "%s" : "%s, ", typeBuf); 821 | arg = arg->nextExpr; 822 | } 823 | piccolo_compilationError(engine, compiler, call->charIdx, "Cannot call %s with params ( %s )", fnBuf, paramBuf); 824 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 825 | } 826 | case PICCOLO_EXPR_IMPORT: { 827 | struct piccolo_ImportNode* import = (struct piccolo_ImportNode*)expr; 828 | struct piccolo_Package* package = piccolo_resolveImport(engine, compiler, compiler->package->packageName, import); 829 | return piccolo_pkgType(engine, package); 830 | } 831 | } 832 | return piccolo_simpleType(engine, PICCOLO_TYPE_ANY); 833 | } 834 | 835 | struct piccolo_Type* piccolo_getType(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_ExprNode* expr) { 836 | if(expr->resultType != NULL) 837 | return expr->resultType; 838 | struct piccolo_Type* type = getType(engine, compiler, expr); 839 | expr->resultType = type; 840 | return type; 841 | } -------------------------------------------------------------------------------- /typecheck.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_TYPECHECK_H 3 | #define PICCOLO_TYPECHECK_H 4 | 5 | #include "scanner.h" 6 | #include "util/dynarray.h" 7 | 8 | PICCOLO_DYNARRAY_HEADER(struct piccolo_Token, Token) 9 | 10 | enum piccolo_TypeType { 11 | PICCOLO_TYPE_ANY, 12 | PICCOLO_TYPE_NUM, 13 | PICCOLO_TYPE_STR, 14 | PICCOLO_TYPE_BOOL, 15 | PICCOLO_TYPE_NIL, 16 | 17 | PICCOLO_TYPE_ARRAY, 18 | PICCOLO_TYPE_HASHMAP, 19 | 20 | PICCOLO_TYPE_FN, 21 | 22 | PICCOLO_TYPE_PKG, 23 | 24 | PICCOLO_TYPE_UNION 25 | }; 26 | 27 | PICCOLO_DYNARRAY_HEADER(struct piccolo_Type*, Type) 28 | 29 | struct piccolo_Type { 30 | enum piccolo_TypeType type; 31 | union { 32 | struct piccolo_Type* listElem; 33 | struct { 34 | struct piccolo_Type* key; 35 | struct piccolo_Type* val; 36 | 37 | struct piccolo_TokenArray strKeys; 38 | struct piccolo_TypeArray strTypes; 39 | } hashmap; 40 | struct { 41 | struct piccolo_Type* result; 42 | struct piccolo_TypeArray params; 43 | } fn; 44 | struct { 45 | struct piccolo_TypeArray types; 46 | } unionTypes; 47 | struct piccolo_Package* pkg; 48 | } subtypes; 49 | struct piccolo_Type* next; 50 | }; 51 | 52 | void piccolo_freeType(struct piccolo_Engine* engine, struct piccolo_Type* type); 53 | void piccolo_getTypename(struct piccolo_Type* type, char* dest); 54 | 55 | struct piccolo_Compiler; 56 | struct piccolo_ExprNode; 57 | 58 | struct piccolo_Type* piccolo_simpleType(struct piccolo_Engine* engine, enum piccolo_TypeType type); 59 | struct piccolo_Type* piccolo_arrayType(struct piccolo_Engine* engine, struct piccolo_Type* elemType); 60 | struct piccolo_Type* piccolo_hashmapType(struct piccolo_Engine* engine, struct piccolo_Type* keyType, struct piccolo_Type* valType, struct piccolo_TokenArray* strKeys, struct piccolo_TypeArray* strTypes); 61 | struct piccolo_Type* piccolo_fnType(struct piccolo_Engine* engine, struct piccolo_TypeArray* params, struct piccolo_Type* res); 62 | struct piccolo_Type* piccolo_pkgType(struct piccolo_Engine* engine, struct piccolo_Package* pkg); 63 | struct piccolo_Type* piccolo_unionType(struct piccolo_Engine* engine, struct piccolo_TypeArray* types); 64 | 65 | struct piccolo_Type* piccolo_getType(struct piccolo_Engine* engine, struct piccolo_Compiler* compiler, struct piccolo_ExprNode* expr); 66 | 67 | #endif -------------------------------------------------------------------------------- /util/dynarray.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_DYNARRAY_H 3 | #define PICCOLO_DYNARRAY_H 4 | 5 | #include "memory.h" 6 | 7 | struct piccolo_Engine; 8 | 9 | #define PICCOLO_ARRAY_NAME(typename) piccolo_ ## typename ## Array 10 | #define PICCOLO_GROW_CAPACITY(oldCapacity) ((oldCapacity < 8) ? 8 : 2 * oldCapacity) 11 | #define PICCOLO_GROW_ARRAY(engine, type, pointer, oldCount, newCount) \ 12 | ((type*)PICCOLO_REALLOCATE("array grow", engine, pointer, sizeof(type) * oldCount, sizeof(type) * newCount)) 13 | #define PICCOLO_FREE_ARRAY(engine, type, pointer, oldCount) \ 14 | ((type*)PICCOLO_REALLOCATE("array free", engine, pointer, sizeof(type)* oldCount, 0)) 15 | 16 | #define PICCOLO_DYNARRAY_HEADER(type, typename) \ 17 | struct PICCOLO_ARRAY_NAME(typename) { \ 18 | int count; \ 19 | int capacity; \ 20 | type* values; \ 21 | }; \ 22 | \ 23 | void piccolo_init ## typename ## Array(struct PICCOLO_ARRAY_NAME(typename)* array); \ 24 | void piccolo_write ## typename ## Array(struct piccolo_Engine* engine, struct PICCOLO_ARRAY_NAME(typename)* array, type value); \ 25 | void piccolo_free ## typename ## Array(struct piccolo_Engine* engine, struct PICCOLO_ARRAY_NAME(typename)* array); \ 26 | type piccolo_pop ## typename ## Array(struct PICCOLO_ARRAY_NAME(typename)* array); 27 | 28 | #define PICCOLO_DYNARRAY_IMPL(type, typename) \ 29 | void piccolo_init ## typename ## Array(struct PICCOLO_ARRAY_NAME(typename)* array) { \ 30 | array->count = 0; \ 31 | array->capacity = 0; \ 32 | array->values = NULL; \ 33 | } \ 34 | \ 35 | void piccolo_write ## typename ## Array(struct piccolo_Engine* engine, struct PICCOLO_ARRAY_NAME(typename)* array, type value) { \ 36 | if(array->capacity < array->count + 1) { \ 37 | int oldCapacity = array->capacity; \ 38 | array->capacity = PICCOLO_GROW_CAPACITY(oldCapacity); \ 39 | array->values = PICCOLO_GROW_ARRAY(engine, type, array->values, oldCapacity, array->capacity); \ 40 | } \ 41 | array->values[array->count] = value; \ 42 | array->count++; \ 43 | } \ 44 | \ 45 | void piccolo_free ## typename ## Array(struct piccolo_Engine* engine, struct PICCOLO_ARRAY_NAME(typename)* array) { \ 46 | array->values = PICCOLO_FREE_ARRAY(engine, type, array->values, array->capacity); \ 47 | } \ 48 | \ 49 | type piccolo_pop ## typename ## Array(struct PICCOLO_ARRAY_NAME(typename)* array) { \ 50 | array->count--; \ 51 | return array->values[array->count]; \ 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /util/file.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "file.h" 7 | 8 | char* piccolo_readFile(const char* path) { 9 | FILE* file = fopen(path, "r"); 10 | if(file == NULL) 11 | return NULL; 12 | 13 | fseek(file, 0L, SEEK_END); 14 | size_t fileSize = ftell(file); 15 | rewind(file); 16 | 17 | /* need to calloc to guarantee the appending of the null term */ 18 | char* buffer = (char*)calloc(fileSize + 1, sizeof(char)); 19 | if(buffer == NULL) 20 | return NULL; 21 | size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); 22 | if(bytesRead < fileSize) 23 | return NULL; 24 | 25 | fclose(file); 26 | return buffer; 27 | } 28 | 29 | void piccolo_applyRelativePathToFilePath(char* dest, const char* relativePath, size_t relPathLen, const char* filepath) { 30 | strcpy(dest, filepath); 31 | int i = 0; 32 | int topNamed = 0; 33 | while(filepath[i] != 0) { 34 | if(filepath[i] == '/' && (i < 2 || !(filepath[i - 1] == '.' && filepath[i - 2] == '.'))) { 35 | topNamed++; 36 | } 37 | i++; 38 | } 39 | size_t currLen = strlen(dest); 40 | while(currLen > 0 && filepath[currLen - 1] != '/') { 41 | dest[currLen - 1] = 0; 42 | currLen--; 43 | } 44 | 45 | size_t start = 0; 46 | for(i = 0; i <= relPathLen; i++) { 47 | if (i == relPathLen || relativePath[i] == '/') { 48 | size_t len = i - start; 49 | if (len == 2 && relativePath[start] == '.' && relativePath[start + 1] == '.') { 50 | if (topNamed == 0) { 51 | dest[currLen] = '.'; 52 | dest[currLen + 1] = '.'; 53 | dest[currLen + 2] = '/'; 54 | currLen += 3; 55 | topNamed = 0; 56 | } else { 57 | currLen--; 58 | dest[currLen] = 0; 59 | while(currLen > 0 && filepath[currLen - 1] != '/') { 60 | dest[currLen - 1] = 0; 61 | currLen--; 62 | } 63 | topNamed--; 64 | } 65 | } else { 66 | for (int j = start; j < i; j++) { 67 | dest[currLen] = relativePath[j]; 68 | currLen++; 69 | dest[currLen] = 0; 70 | } 71 | topNamed++; 72 | if (i != relPathLen) { 73 | dest[currLen] = '/'; 74 | currLen++; 75 | dest[currLen] = 0; 76 | } 77 | } 78 | start = i + 1; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /util/file.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_FILE_H 3 | #define PICCOLO_FILE_H 4 | 5 | char* piccolo_readFile(const char* path); 6 | 7 | void piccolo_applyRelativePathToFilePath(char* dest, const char* relativePath, size_t relPathLen, const char* filepath); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /util/hashmap.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_HASHMAP_H 3 | #define PICCOLO_HASHMAP_H 4 | 5 | #include "dynarray.h" 6 | 7 | #define PICCOLO_MAX_LOAD_FACTOR 0.75 8 | 9 | #define PICCOLO_ALLOCATE(engine, type, count) ((type*)PICCOLO_REALLOCATE("hashmap alloc", engine, NULL, 0, sizeof(type) * count)) 10 | 11 | #define PICCOLO_HASHMAP_HEADER(keyType, valType, name) \ 12 | struct piccolo_ ## name ## Entry { \ 13 | keyType key; \ 14 | valType val; \ 15 | }; \ 16 | \ 17 | struct piccolo_ ## name { \ 18 | int count; \ 19 | int capacity; \ 20 | struct piccolo_ ## name ## Entry* entries; \ 21 | \ 22 | }; \ 23 | \ 24 | void piccolo_init ## name(struct piccolo_ ## name * hashmap); \ 25 | void piccolo_free ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap); \ 26 | void piccolo_set ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap, keyType key, valType val); \ 27 | valType piccolo_get ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap, keyType key); \ 28 | uint32_t piccolo_hash ## name ## Key(keyType key); \ 29 | bool piccolo_compare ## name ## Keys(keyType a, keyType b); \ 30 | bool piccolo_ ## name ## IsBaseKey(keyType key); 31 | 32 | #define PICCOLO_HASHMAP_IMPL(keyType, valType, name, baseKey, baseVal) \ 33 | void piccolo_init ## name(struct piccolo_ ## name * hashmap) { \ 34 | hashmap->count = 0; \ 35 | hashmap->capacity = 0; \ 36 | hashmap->entries = NULL;\ 37 | } \ 38 | \ 39 | void piccolo_free ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap) { \ 40 | hashmap->entries = PICCOLO_FREE_ARRAY(engine, struct piccolo_ ## name ## Entry, hashmap->entries, hashmap->capacity); \ 41 | piccolo_init ## name(hashmap);\ 42 | } \ 43 | \ 44 | static struct piccolo_ ## name ## Entry* find ## name ## Entry(struct piccolo_ ## name ## Entry * entries, int capacity, keyType key) { \ 45 | uint32_t index = piccolo_hash ## name ## Key(key) & (capacity - 1); \ 46 | for(;;) { \ 47 | struct piccolo_ ## name ## Entry* entry = &entries[index]; \ 48 | if(piccolo_ ## name ## IsBaseKey(entry->key) || piccolo_compare ## name ## Keys(key, entry->key)) { \ 49 | return entry; \ 50 | } \ 51 | index = (index + 1) & (capacity - 1);\ 52 | }\ 53 | } \ 54 | \ 55 | static void adjust ## name ## Capacity(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap, int capacity) { \ 56 | struct piccolo_ ## name ## Entry* entries = PICCOLO_ALLOCATE(engine, struct piccolo_ ## name ## Entry, capacity); \ 57 | for(int i = 0; i < capacity; i++) { \ 58 | entries[i].key = baseKey; \ 59 | entries[i].val = (baseVal); \ 60 | } \ 61 | \ 62 | for(int i = 0; i < hashmap->capacity; i++) { \ 63 | struct piccolo_ ## name ## Entry* entry = &hashmap->entries[i]; \ 64 | if(piccolo_ ## name ## IsBaseKey(entry->key)) continue; \ 65 | \ 66 | struct piccolo_ ## name ## Entry* dest = find ## name ## Entry(entries, capacity, entry->key); \ 67 | dest->key = entry->key; \ 68 | dest->val = entry->val;\ 69 | } \ 70 | \ 71 | PICCOLO_FREE_ARRAY(engine, struct piccolo_ ## name ## Entry, hashmap->entries, hashmap->capacity);\ 72 | \ 73 | hashmap->entries = entries; \ 74 | hashmap->capacity = capacity;\ 75 | }\ 76 | \ 77 | void piccolo_set ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap, keyType key, valType val) { \ 78 | if(hashmap->count + 1 > hashmap->capacity * PICCOLO_MAX_LOAD_FACTOR) { \ 79 | int capacity = PICCOLO_GROW_CAPACITY(hashmap->capacity); \ 80 | adjust ## name ## Capacity(engine, hashmap, capacity);\ 81 | }\ 82 | struct piccolo_ ## name ## Entry* entry = find ## name ## Entry(hashmap->entries, hashmap->capacity, key); \ 83 | if(piccolo_ ## name ## IsBaseKey(entry->key)) \ 84 | hashmap->count++;\ 85 | entry->key = key; \ 86 | entry->val = val;\ 87 | } \ 88 | \ 89 | valType piccolo_get ## name(struct piccolo_Engine* engine, struct piccolo_ ## name * hashmap, keyType key) { \ 90 | if(hashmap->count == 0) \ 91 | return (baseVal); \ 92 | struct piccolo_ ## name ## Entry* entry = find ## name ## Entry(hashmap->entries, hashmap->capacity, key); \ 93 | if(piccolo_ ## name ## IsBaseKey(entry->key)) \ 94 | return (baseVal); \ 95 | return entry->val;\ 96 | }\ 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /util/memory.c: -------------------------------------------------------------------------------- 1 | 2 | #include "memory.h" 3 | #include "../engine.h" 4 | #include 5 | #include 6 | #include 7 | 8 | void* piccolo_reallocate(struct piccolo_Engine* engine, void* data, size_t oldSize, size_t newSize) { 9 | engine->liveMemory += newSize - oldSize; 10 | if(newSize == 0) { 11 | free(data); 12 | return NULL; 13 | } 14 | void* const retval = realloc(data, newSize); 15 | memset((uint8_t*)retval + oldSize, 0, newSize - oldSize); 16 | return retval; 17 | } 18 | 19 | #ifdef PICCOLO_ENABLE_MEMORY_TRACKER 20 | 21 | #include 22 | 23 | void* piccolo_reallocateTrack(const char* name, struct piccolo_Engine* engine, void* data, size_t oldSize, size_t newSize) { 24 | struct piccolo_MemoryTrack* track = engine->track; 25 | while(track != NULL) { 26 | if(track->ptr == data) { 27 | break; 28 | } 29 | track = track->next; 30 | } 31 | if(track == NULL) { 32 | track = malloc(sizeof(struct piccolo_MemoryTrack)); 33 | track->next = engine->track; 34 | engine->track = track; 35 | track->name = name; 36 | } 37 | void* newPtr = piccolo_reallocate(engine, data, oldSize, newSize); 38 | track->ptr = newPtr; 39 | track->size = newSize; 40 | return newPtr; 41 | } 42 | 43 | void piccolo_printMemAllocs(struct piccolo_Engine* engine) { 44 | printf("live memory: %lu\n", engine->liveMemory); 45 | struct piccolo_MemoryTrack* track = engine->track; 46 | while(track != NULL) { 47 | if(track->size > 0) { 48 | printf("%s\n", track->name); 49 | printf("\tsize: %lu\n", track->size); 50 | } 51 | track = track->next; 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /util/memory.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_MEMORY_H 3 | #define PICCOLO_MEMORY_H 4 | 5 | #include 6 | 7 | struct piccolo_Engine; 8 | 9 | // #define PICCOLO_ENABLE_MEMORY_TRACKER 10 | 11 | #ifdef PICCOLO_ENABLE_MEMORY_TRACKER 12 | 13 | #define PICCOLO_S(x) #x 14 | #define PICCOLO_S_(x) PICCOLO_S(x) 15 | #define PICCOLO_S__LINE__ PICCOLO_S_(__LINE__) 16 | 17 | #define PICCOLO_REALLOCATE(allocName, engine, data, oldSize, newSize) \ 18 | piccolo_reallocateTrack(__FILE__ ":" PICCOLO_S__LINE__ ":" allocName, engine, data, oldSize, newSize) 19 | 20 | struct piccolo_MemoryTrack { 21 | void* ptr; 22 | size_t size; 23 | const char* name; 24 | struct piccolo_MemoryTrack* next; 25 | }; 26 | 27 | void* piccolo_reallocateTrack(const char* name, struct piccolo_Engine* engine, void* data, size_t oldSize, size_t newSize); 28 | void piccolo_printMemAllocs(struct piccolo_Engine* engine); 29 | #else 30 | #define PICCOLO_REALLOCATE(allocName, engine, data, oldSize, newSize) \ 31 | piccolo_reallocate(engine, data, oldSize, newSize) 32 | #endif 33 | 34 | void* piccolo_reallocate(struct piccolo_Engine* engine, void* data, size_t oldSize, size_t newSize); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /util/strutil.c: -------------------------------------------------------------------------------- 1 | 2 | #include "strutil.h" 3 | 4 | struct piccolo_strutil_LineInfo piccolo_strutil_getLine(const char* source, uint32_t charIdx) { 5 | struct piccolo_strutil_LineInfo lineInfo; 6 | if(!source) return (struct piccolo_strutil_LineInfo) {0}; 7 | lineInfo.lineStart = source; 8 | lineInfo.line = 0; 9 | for(uint32_t i = 0; i < charIdx; i++) { 10 | if(source[i] == '\0') { 11 | break; 12 | } 13 | if(source[i] == '\n') { 14 | lineInfo.line++; 15 | lineInfo.lineStart = source + i + 1; 16 | } 17 | } 18 | lineInfo.lineEnd = lineInfo.lineStart; 19 | while(*lineInfo.lineEnd != '\0' && *lineInfo.lineEnd != '\n') 20 | lineInfo.lineEnd++; 21 | return lineInfo; 22 | } 23 | 24 | int piccolo_strutil_utf8Chars(char c) { 25 | if((c & 0xF8) == 0xF0) 26 | return 4; 27 | if((c & 0xF0) == 0xE0) 28 | return 3; 29 | if((c & 0xE0) == 0xC0) 30 | return 2; 31 | return 1; 32 | } 33 | -------------------------------------------------------------------------------- /util/strutil.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_STRUTIL_H 3 | #define PICCOLO_STRUTIL_H 4 | 5 | #include 6 | 7 | struct piccolo_strutil_LineInfo { 8 | // These are assigned with const values in several places and so should be marked const 9 | const char* lineStart; 10 | const char* lineEnd; 11 | int line; 12 | }; 13 | 14 | struct piccolo_strutil_LineInfo piccolo_strutil_getLine(const char* source, uint32_t charIdx); 15 | int piccolo_strutil_utf8Chars(char c); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /value.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "value.h" 4 | #include "object.h" 5 | #include "package.h" 6 | #include "debug/disassembler.h" 7 | 8 | #include 9 | 10 | PICCOLO_DYNARRAY_IMPL(piccolo_Value, Value) 11 | 12 | static void printObject(struct piccolo_Obj* obj) { 13 | bool printed = obj->printed; 14 | obj->printed = true; 15 | if(obj->type == PICCOLO_OBJ_STRING) { 16 | struct piccolo_ObjString* string = (struct piccolo_ObjString*)obj; 17 | printf("%.*s", string->len, string->string); 18 | } 19 | if(obj->type == PICCOLO_OBJ_ARRAY) { 20 | struct piccolo_ObjArray* array = (struct piccolo_ObjArray*)obj; 21 | printf("["); 22 | if(!printed) { 23 | for(int i = 0; i < array->array.count; i++) { 24 | piccolo_printValue(array->array.values[i]); 25 | if(i < array->array.count - 1) 26 | printf(", "); 27 | } 28 | } else { 29 | printf("..."); 30 | } 31 | printf("]"); 32 | } 33 | if(obj->type == PICCOLO_OBJ_HASHMAP) { 34 | struct piccolo_ObjHashmap* hashmap = (struct piccolo_ObjHashmap*)obj; 35 | printf("{"); 36 | if(!printed) { 37 | bool first = true; 38 | for(int i = 0; i < hashmap->hashmap.capacity; i++) { 39 | if(!piccolo_HashmapIsBaseKey(hashmap->hashmap.entries[i].key)) { 40 | if(!first) 41 | printf(", "); 42 | piccolo_printValue(hashmap->hashmap.entries[i].key); 43 | printf(": "); 44 | piccolo_printValue(hashmap->hashmap.entries[i].val.value); 45 | first = false; 46 | } 47 | } 48 | } else { 49 | printf("..."); 50 | } 51 | printf("}"); 52 | } 53 | if(obj->type == PICCOLO_OBJ_FUNC) { 54 | printf(""); 55 | } 56 | if(obj->type == PICCOLO_OBJ_CLOSURE) { 57 | printf(""); 58 | } 59 | if(obj->type == PICCOLO_OBJ_NATIVE_FN) { 60 | printf(""); 61 | } 62 | if(obj->type == PICCOLO_OBJ_PACKAGE) { 63 | struct piccolo_Package* package = ((struct piccolo_Package*)obj); 64 | printf("", package->packageName); 65 | } 66 | if(obj->type == PICCOLO_OBJ_NATIVE_STRUCT) { 67 | struct piccolo_ObjNativeStruct* nativeStruct = (struct piccolo_ObjNativeStruct*)obj; 68 | printf("<%s>", nativeStruct->Typename); 69 | } 70 | obj->printed = false; 71 | } 72 | 73 | void piccolo_printValue(piccolo_Value value) { 74 | if(PICCOLO_IS_NIL(value)) { 75 | printf("nil"); 76 | return; 77 | } 78 | if(PICCOLO_IS_NUM(value)) { 79 | printf("%g", PICCOLO_AS_NUM(value)); 80 | return; 81 | } 82 | if(PICCOLO_IS_BOOL(value)) { 83 | printf(PICCOLO_AS_BOOL(value) ? "true" : "false"); 84 | return; 85 | } 86 | if(PICCOLO_IS_OBJ(value)) { 87 | printObject(PICCOLO_AS_OBJ(value)); 88 | } 89 | } 90 | 91 | char* piccolo_getTypeName(piccolo_Value value) { 92 | if(PICCOLO_IS_NIL(value)) { 93 | return "nil"; 94 | } 95 | if(PICCOLO_IS_NUM(value)) { 96 | return "num"; 97 | } 98 | if(PICCOLO_IS_BOOL(value)) { 99 | return "bool"; 100 | } 101 | if(PICCOLO_IS_OBJ(value)) { 102 | enum piccolo_ObjType type = PICCOLO_AS_OBJ(value)->type; 103 | if(type == PICCOLO_OBJ_STRING) 104 | return "str"; 105 | if(type == PICCOLO_OBJ_ARRAY) 106 | return "array"; 107 | if(type == PICCOLO_OBJ_HASHMAP) 108 | return "hashmap"; 109 | if(type == PICCOLO_OBJ_FUNC) 110 | return "raw fn"; 111 | if(type == PICCOLO_OBJ_CLOSURE) 112 | return "fn"; 113 | if(type == PICCOLO_OBJ_NATIVE_FN) 114 | return "native fn"; 115 | if(type == PICCOLO_OBJ_PACKAGE) 116 | return "package"; 117 | } 118 | return "Unknown"; 119 | } 120 | 121 | bool piccolo_valuesEqual(struct piccolo_Value a, struct piccolo_Value b) { 122 | if(PICCOLO_IS_NUM(a) && PICCOLO_IS_NUM(b)) { 123 | return PICCOLO_AS_NUM(a) == PICCOLO_AS_NUM(b); 124 | } 125 | if(PICCOLO_IS_BOOL(a) && PICCOLO_IS_BOOL(b)) { 126 | return PICCOLO_AS_BOOL(a) == PICCOLO_AS_BOOL(b); 127 | } 128 | if(PICCOLO_IS_NIL(a) && PICCOLO_IS_NIL(b)) { 129 | return true; 130 | } 131 | if(PICCOLO_IS_OBJ(a) && PICCOLO_IS_OBJ(b)) { 132 | struct piccolo_Obj* aObj = PICCOLO_AS_OBJ(a); 133 | struct piccolo_Obj* bObj = PICCOLO_AS_OBJ(b); 134 | if(aObj->type == PICCOLO_OBJ_STRING && bObj->type == PICCOLO_OBJ_STRING) { 135 | struct piccolo_ObjString* aStr = (struct piccolo_ObjString*)aObj; 136 | struct piccolo_ObjString* bStr = (struct piccolo_ObjString*)bObj; 137 | if(aStr->len == bStr->len && strncmp(aStr->string, bStr->string, aStr->len) == 0) { 138 | return true; 139 | } 140 | } 141 | } 142 | return false; 143 | } 144 | -------------------------------------------------------------------------------- /value.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PICCOLO_VALUE_H 3 | #define PICCOLO_VALUE_H 4 | 5 | #include 6 | 7 | #include "util/dynarray.h" 8 | 9 | enum piccolo_ValueType { 10 | PICCOLO_VALUE_NIL, 11 | PICCOLO_VALUE_NUMBER, 12 | PICCOLO_VALUE_BOOL, 13 | PICCOLO_VALUE_OBJ, 14 | }; 15 | 16 | struct piccolo_Value { 17 | enum piccolo_ValueType type; 18 | union { 19 | double number; 20 | bool boolean; 21 | struct piccolo_Obj* obj; 22 | } as; 23 | }; 24 | 25 | typedef struct piccolo_Value piccolo_Value; 26 | 27 | #define PICCOLO_IS_NIL(value) (value.type == PICCOLO_VALUE_NIL) 28 | #define PICCOLO_IS_NUM(value) (value.type == PICCOLO_VALUE_NUMBER) 29 | #define PICCOLO_IS_BOOL(value) (value.type == PICCOLO_VALUE_BOOL) 30 | #define PICCOLO_IS_OBJ(value) (value.type == PICCOLO_VALUE_OBJ) 31 | 32 | #define PICCOLO_AS_NUM(value) (value.as.number) 33 | #define PICCOLO_AS_BOOL(value) (value.as.boolean) 34 | #define PICCOLO_AS_OBJ(value) (value.as.obj) 35 | 36 | #define PICCOLO_NIL_VAL() ((piccolo_Value){PICCOLO_VALUE_NIL, {.number = 0}}) 37 | #define PICCOLO_NUM_VAL(num) ((piccolo_Value){PICCOLO_VALUE_NUMBER, {.number = (num)}}) 38 | #define PICCOLO_BOOL_VAL(bool) ((piccolo_Value){PICCOLO_VALUE_BOOL, {.boolean = (bool)}}) 39 | #define PICCOLO_OBJ_VAL(object)((piccolo_Value){PICCOLO_VALUE_OBJ, {.obj = ((struct piccolo_Obj*)object)}}) 40 | 41 | PICCOLO_DYNARRAY_HEADER(piccolo_Value, Value) 42 | 43 | void piccolo_printValue(piccolo_Value value); 44 | char* piccolo_getTypeName(piccolo_Value value); 45 | 46 | bool piccolo_valuesEqual(struct piccolo_Value a, struct piccolo_Value b); 47 | 48 | #endif 49 | --------------------------------------------------------------------------------