1 | // MIT License |
2 | |
3 | // Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com |
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 | |
23 | #include "core/core.h" |
24 | |
25 | // Fennel requires Lua |
26 | #if defined(TIC_BUILD_WITH_LUA) |
27 | |
28 | #include "lua_api.h" |
29 | |
30 | #if defined(TIC_BUILD_WITH_FENNEL) |
31 | |
32 | #include "fennel.h" |
33 | |
34 | #define FENNEL_CODE(...) #__VA_ARGS__ |
35 | |
36 | static const char* execute_fennel_src = FENNEL_CODE( |
37 | local fennel = require("fennel" ) |
38 | debug.traceback = fennel.traceback |
39 | local opts = {filename="game" , allowedGlobals = false} |
40 | local src = ... |
41 | if(src:find("\n;; strict: true" )) then opts.allowedGlobals = nil end |
42 | local ok, msg = pcall(fennel.eval, src, opts) |
43 | if(not ok) then return msg end |
44 | ); |
45 | |
46 | static bool initFennel(tic_mem* tic, const char* code) |
47 | { |
48 | tic_core* core = (tic_core*)tic; |
49 | closeLua(tic); |
50 | |
51 | lua_State* lua = core->currentVM = luaL_newstate(); |
52 | lua_open_builtins(lua); |
53 | |
54 | initLuaAPI(core); |
55 | |
56 | { |
57 | lua_State* fennel = core->currentVM; |
58 | |
59 | lua_settop(fennel, 0); |
60 | |
61 | if (luaL_loadbuffer(fennel, (const char *)loadfennel_lua, |
62 | loadfennel_lua_len, "fennel.lua" ) != LUA_OK) |
63 | { |
64 | core->data->error(core->data->data, "failed to load fennel compiler" ); |
65 | return false; |
66 | } |
67 | |
68 | lua_call(fennel, 0, 0); |
69 | |
70 | if (luaL_loadbuffer(fennel, execute_fennel_src, strlen(execute_fennel_src), "execute_fennel" ) != LUA_OK) |
71 | { |
72 | core->data->error(core->data->data, "failed to load fennel compiler" ); |
73 | return false; |
74 | } |
75 | |
76 | lua_pushstring(fennel, code); |
77 | lua_call(fennel, 1, 1); |
78 | const char* err = lua_tostring(fennel, -1); |
79 | |
80 | if (err) |
81 | { |
82 | core->data->error(core->data->data, err); |
83 | return false; |
84 | } |
85 | } |
86 | |
87 | return true; |
88 | } |
89 | |
90 | static const char* const FennelKeywords [] = |
91 | { |
92 | "lua" , "hashfn" ,"macro" , "macros" , "macroexpand" , "macrodebug" , |
93 | "do" , "values" , "if" , "when" , "each" , "for" , "fn" , "lambda" , "partial" , |
94 | "while" , "set" , "global" , "var" , "local" , "let" , "tset" , "doto" , "match" , |
95 | "or" , "and" , "true" , "false" , "nil" , "not" , "not=" , "length" , "set-forcibly!" , |
96 | "rshift" , "lshift" , "bor" , "band" , "bnot" , "bxor" , "pick-values" , "pick-args" , |
97 | "." , ".." , "#" , "..." , ":" , "->" , "->>" , "-?>" , "-?>>" , "$" , "with-open" |
98 | }; |
99 | |
100 | static const tic_outline_item* getFennelOutline(const char* code, s32* size) |
101 | { |
102 | enum{Size = sizeof(tic_outline_item)}; |
103 | |
104 | *size = 0; |
105 | |
106 | static tic_outline_item* items = NULL; |
107 | |
108 | if(items) |
109 | { |
110 | free(items); |
111 | items = NULL; |
112 | } |
113 | |
114 | const char* ptr = code; |
115 | |
116 | while(true) |
117 | { |
118 | static const char FuncString[] = "(fn " ; |
119 | |
120 | ptr = strstr(ptr, FuncString); |
121 | |
122 | if(ptr) |
123 | { |
124 | ptr += sizeof FuncString - 1; |
125 | |
126 | const char* start = ptr; |
127 | const char* end = start; |
128 | |
129 | while(*ptr) |
130 | { |
131 | char c = *ptr; |
132 | |
133 | if(c == ' ' || c == '\t' || c == '\n' || c == '[') |
134 | { |
135 | end = ptr; |
136 | break; |
137 | } |
138 | |
139 | ptr++; |
140 | } |
141 | |
142 | if(end > start) |
143 | { |
144 | items = realloc(items, (*size + 1) * Size); |
145 | |
146 | items[*size].pos = start; |
147 | items[*size].size = (s32)(end - start); |
148 | |
149 | (*size)++; |
150 | } |
151 | } |
152 | else break; |
153 | } |
154 | |
155 | return items; |
156 | } |
157 | |
158 | static void evalFennel(tic_mem* tic, const char* code) { |
159 | tic_core* core = (tic_core*)tic; |
160 | lua_State* fennel = core->currentVM; |
161 | |
162 | lua_settop(fennel, 0); |
163 | |
164 | if (luaL_loadbuffer(fennel, execute_fennel_src, strlen(execute_fennel_src), "execute_fennel" ) != LUA_OK) |
165 | { |
166 | core->data->error(core->data->data, "failed to load fennel compiler" ); |
167 | } |
168 | |
169 | lua_pushstring(fennel, code); |
170 | lua_call(fennel, 1, 1); |
171 | const char* err = lua_tostring(fennel, -1); |
172 | |
173 | if (err) |
174 | { |
175 | core->data->error(core->data->data, err); |
176 | } |
177 | } |
178 | |
179 | tic_script_config FennelSyntaxConfig = |
180 | { |
181 | .id = 14, |
182 | .name = "fennel" , |
183 | .fileExtension = ".fnl" , |
184 | .projectComment = ";;" , |
185 | { |
186 | .init = initFennel, |
187 | .close = closeLua, |
188 | .tick = callLuaTick, |
189 | .boot = callLuaBoot, |
190 | |
191 | .callback = |
192 | { |
193 | .scanline = callLuaScanline, |
194 | .border = callLuaBorder, |
195 | .menu = callLuaMenu, |
196 | }, |
197 | }, |
198 | |
199 | .getOutline = getFennelOutline, |
200 | .eval = evalFennel, |
201 | |
202 | .blockCommentStart = NULL, |
203 | .blockCommentEnd = NULL, |
204 | .blockCommentStart2 = NULL, |
205 | .blockCommentEnd2 = NULL, |
206 | .blockStringStart = NULL, |
207 | .blockStringEnd = NULL, |
208 | .singleComment = ";" , |
209 | .blockEnd = NULL, |
210 | |
211 | .keywords = FennelKeywords, |
212 | .keywordsCount = COUNT_OF(FennelKeywords), |
213 | }; |
214 | |
215 | #endif /* defined(TIC_BUILD_WITH_FENNEL) */ |
216 | |
217 | #endif |
218 | |