| 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 | |