]> git.bts.cx Git - benzene.git/blob - src/bz/scripting/script.c
Sprites
[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 }