├── .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://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 |
--------------------------------------------------------------------------------