]> git.bts.cx Git - benzene.git/blob - src/bz/scripting/script.c
Initial version
[benzene.git] / src / bz / scripting / script.c
1 #include <bz/scripting/script_internal.h>
2
3 #include <bz/memory/allocator.h>
4 #include <bz/resources/resource.h>
5 //#include <bz/scripting/bindings_internal.h>
6 //#include <bz/scripting/environment_internal.h>
7 #include <bz/types/identifier_internal.h>
8 #include <sun/compiler/compiler.h>
9 #include <string.h>
10
11 #define kMaxIncludeDepth 8
12
13 struct BZScriptReadContext {
14         size_t currentHandleIdx;
15         BZResourceID handles[kMaxIncludeDepth];
16         bool handlePreprocessor;
17         bool pastFirstCharacter;
18 };
19 typedef struct BZScriptReadContext BZScriptReadContext;
20
21 static size_t readSource(char *out, size_t outSize, void *userParam) {
22         BZScriptReadContext *context = userParam;
23
24         BZResourceID currentHandle = context->handles[context->currentHandleIdx];
25         size_t readStartPosition = bzResourcesTell(currentHandle);
26
27 process:
28         if (context->handlePreprocessor) {
29                 // Extract the current line...
30
31                 size_t preprocessorLength = 0;
32                 while (bzResourcesReadBytes(currentHandle, out, 1) > 0) {
33                         if (*out == '\n') {
34                                 break;
35                         }
36                         preprocessorLength += 1;
37                 }
38
39                 char *preprocessorInstruction = alloca(preprocessorLength + 1);
40                 preprocessorInstruction[preprocessorLength] = '\0';
41                 bzResourcesSeek(currentHandle, readStartPosition);
42                 bzResourcesReadBytes(currentHandle, preprocessorInstruction, preprocessorLength);
43
44                 if (strncmp(preprocessorInstruction, "#include ", 9) == 0) {
45                         context->currentHandleIdx++;
46                         bzAssert(context->currentHandleIdx < kMaxIncludeDepth);
47                         preprocessorInstruction += 9;
48                         context->handles[context->currentHandleIdx] = bzResourcesOpenResource("script", "assets/scripts/%s", preprocessorInstruction);
49                         bzAssertMessage(context->handles[context->currentHandleIdx] != NULL, "Invalid include '%s'", preprocessorInstruction);
50                 }
51         }
52
53         size_t readSize = 0;
54         while (readSize == 0) {
55                 currentHandle = context->handles[context->currentHandleIdx];
56                 readStartPosition = bzResourcesTell(currentHandle);
57                 readSize = bzResourcesReadBytes(currentHandle, out, outSize);
58
59                 if (readSize == 0) {
60                         if (context->currentHandleIdx > 0) {
61                                 bzResourcesCloseResource(currentHandle);
62                                 --context->currentHandleIdx;
63                         } else {
64                                 // 0th level is closed in main code
65                                 break;
66                         }
67                 }
68         }
69
70         for (size_t i = 0; i < readSize; ++i) {
71                 switch (out[i]) {
72                         case '#':
73                                 if (context->pastFirstCharacter == false) {
74                                         // need to handle preprocessor, set flag and return only the read characters
75                                         bzResourcesSeek(currentHandle, readStartPosition + i);
76                                         context->handlePreprocessor = true;
77                                         
78                                         if (i > 0) {
79                                                 return i; // don't return the '#' character
80                                         } else {
81                                                 goto process; // I know this is horrible, don't @ me.
82                                         }
83                                 }
84                                 break;
85                         
86                         case '\n':
87                                 context->pastFirstCharacter = false;
88                                 break;
89
90                         case ' ':
91                         case '\t':
92                                 // These still count as 'first character' techically...
93                                 break;
94
95                         default:
96                                 context->pastFirstCharacter = true;
97                                 break;
98                 }
99         }
100
101         return readSize;
102 }
103
104 static void *sourceMalloc(void *memory, size_t size) {
105         return alloca(size);
106 }
107
108 SVMModule *bzScriptingLoadScriptModule(BZMemoryArenaID arena, const char *identifierFmt, ...) {
109         bzMakeIdentifier(identifier, identifierFmt);
110
111         BZScriptReadContext context = {
112                 .handles = { bzResourcesOpenResource("script", "assets/scripts/%s.sun", identifier), },
113                 .currentHandleIdx = 0,
114                 .handlePreprocessor = false,
115                 .pastFirstCharacter = false,
116         };
117         bzAssertMessage(context.handles[0] != NULL, "Invalid script '%s'", identifier);
118
119         SLCCompileConfiguration config = {
120                 .sourceReadCallback = readSource,
121                 .sourceUserParam = &context,
122                 .realloc = sourceMalloc, // fixme
123                 .free = NULL, // fixme
124         };
125
126         SVMModule *module = slcCompileSource(&config);
127         bzResourcesCloseResource(context.handles[0]);
128
129         if (module == NULL) {
130                 bzError("Could not load script %s", identifier);
131         }
132
133         return module;
134 }