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
36static 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
46static 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
90static 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
100static 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
158static 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
179tic_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