From d114930dbed917b8f79c6f71c20769cfff374cb8 Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Thu, 10 May 2018 19:42:55 -0700 Subject: [PATCH] Add support for fennel as a supported language for games. Does not include changes to the 3rd-party submodule; you'll need to do this manually: $ mkdir 3rd-party/fennel && cd 3rd-party/fennel $ wget https://p.hagelb.org/fennel_original.lua $ wget -O Makefile https://p.hagelb.org/tic-fennel-Makefile $ make --- Makefile | 3 +- include/tic80_config.h | 5 +- src/console.c | 48 +++++++++++- src/luaapi.c | 161 +++++++++++++++++++++++++++++++++++++++++ src/machine.h | 6 +- src/tic.c | 9 +++ 6 files changed, 227 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index e1524ae96..d82101d14 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ INCLUDES= \ -I$(3RD_PARTY)/sdl-gpu/include \ -I$(3RD_PARTY)/wren-0.1.0/src/include \ -I$(3RD_PARTY)/moonscript \ + -I$(3RD_PARTY)/fennel \ -I$(BLIPBUF_LIB) \ -I$(DUKTAPE_LIB) \ -I$(SDL_NET_LIB) \ @@ -448,4 +449,4 @@ bin/assets/benchmark.tic.dat: demos/benchmark.tic $(BIN2TXT) $< $@ -z clean: $(TIC_O) $(TIC80_O) - del bin\*.o + rm bin/*.o diff --git a/include/tic80_config.h b/include/tic80_config.h index 6dbe52a90..058e697ff 100644 --- a/include/tic80_config.h +++ b/include/tic80_config.h @@ -24,8 +24,9 @@ #define TIC_BUILD_WITH_LUA 1 #define TIC_BUILD_WITH_MOON 1 +#define TIC_BUILD_WITH_FENNEL 1 #define TIC_BUILD_WITH_JS 1 -#define TIC_BUILD_WITH_WREN 1 +#define TIC_BUILD_WITH_WREN 0 #if defined(__APPLE__) # include "AvailabilityMacros.h" @@ -77,4 +78,4 @@ # else # define TIC80_API # endif -#endif \ No newline at end of file +#endif diff --git a/src/console.c b/src/console.c index aefdf825d..4ae4a1553 100644 --- a/src/console.c +++ b/src/console.c @@ -56,6 +56,10 @@ typedef enum MoonScript, # endif +# if defined(TIC_BUILD_WITH_FENNEL) + Fennel, +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -97,6 +101,10 @@ static const char DefaultLuaTicPath[] = TIC_LOCAL "default.tic"; static const char DefaultMoonTicPath[] = TIC_LOCAL "default_moon.tic"; # endif +# if defined(TIC_BUILD_WITH_FENNEL) +static const char DefaultFennelTicPath[] = TIC_LOCAL "default_fennel.tic"; +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -470,6 +478,10 @@ static void* getDemoCart(Console* console, ScriptLang script, s32* size) case MoonScript: strcpy(path, DefaultMoonTicPath); break; # endif +# if defined(TIC_BUILD_WITH_MOON) + case Fennel: strcpy(path, DefaultFennelTicPath); break; +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -519,6 +531,20 @@ static void* getDemoCart(Console* console, ScriptLang script, s32* size) break; # endif +# if defined(TIC_BUILD_WITH_FENNEL) + case Fennel: + { + static const u8 FennelDemoRom[] = + { + #include "../bin/assets/luademo.tic.dat" /* TODO: fennel version */ + }; + + demo = FennelDemoRom; + romSize = sizeof FennelDemoRom; + } + break; +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ @@ -584,6 +610,11 @@ static void onConsoleLoadDemoCommandConfirmed(Console* console, const char* para data = getDemoCart(console, MoonScript, &size); # endif +# if defined(TIC_BUILD_WITH_FENNEL) + if(strcmp(param, DefaultFennelTicPath) == 0) + data = getDemoCart(console, Fennel, &size); +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -1166,6 +1197,14 @@ static void onConsoleNewCommandConfirmed(Console* console, const char* param) } # endif +# if defined(TIC_BUILD_WITH_FENNEL) + if(strcmp(param, "fennel") == 0) + { + loadDemo(console, Fennel); + done = true; + } +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -1460,6 +1499,13 @@ static void onConsoleConfigCommand(Console* console, const char* param) } # endif +# if defined(TIC_BUILD_WITH_FENNEL) + else if(strcmp(param, "default fennel") == 0) + { + onConsoleLoadDemoCommand(console, DefaultFennelTicPath); + } +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ #if defined(TIC_BUILD_WITH_JS) @@ -3264,4 +3310,4 @@ void initConsole(Console* console, tic_mem* tic, FileSystem* fs, Config* config, #endif console->active = !console->embed.yes; -} \ No newline at end of file +} diff --git a/src/luaapi.c b/src/luaapi.c index 15d33a781..aa575f839 100644 --- a/src/luaapi.c +++ b/src/luaapi.c @@ -1613,4 +1613,165 @@ const tic_script_config* getMoonScriptConfig() #endif /* defined(TIC_BUILD_WITH_MOON) */ +#if defined(TIC_BUILD_WITH_FENNEL) + +#include "fennel.h" + +#define FENNEL_CODE(...) #__VA_ARGS__ + +static const char* execute_fennel_src = FENNEL_CODE( + local ok, val = pcall(require('fennel').eval, ...) + return val +); + +static bool initFennel(tic_mem* tic, const char* code) +{ + tic_machine* machine = (tic_machine*)tic; + closeLua(tic); + + lua_State* lua = machine->lua = luaL_newstate(); + + static const luaL_Reg loadedlibs[] = + { + { "_G", luaopen_base }, + { LUA_LOADLIBNAME, luaopen_package }, + { LUA_COLIBNAME, luaopen_coroutine }, + { LUA_TABLIBNAME, luaopen_table }, + { LUA_STRLIBNAME, luaopen_string }, + { LUA_MATHLIBNAME, luaopen_math }, + { LUA_DBLIBNAME, luaopen_debug }, + { NULL, NULL } + }; + + for (const luaL_Reg *lib = loadedlibs; lib->func; lib++) + { + luaL_requiref(lua, lib->name, lib->func, 1); + lua_pop(lua, 1); + } + + initAPI(machine); + + { + lua_State* fennel = machine->lua; + + lua_settop(fennel, 0); + + if (luaL_loadbuffer(fennel, (const char *)fennel_lua, fennel_lua_len, "fennel.lua") != LUA_OK) + { + machine->data->error(machine->data->data, "failed to load fennel.lua"); + return false; + } + + lua_call(fennel, 0, 0); + + if (luaL_loadbuffer(fennel, execute_fennel_src, strlen(execute_fennel_src), "execute_fennel") != LUA_OK) + { + machine->data->error(machine->data->data, "failed to load fennel compiler"); + return false; + } + + lua_pushstring(fennel, code); + if (lua_pcall(fennel, 1, 1, 0) != LUA_OK) + { + const char* msg = lua_tostring(fennel, -1); + + if (msg) + { + machine->data->error(machine->data->data, msg); + return false; + } + } + } + + return true; +} + +static const char* const FennelKeywords [] = +{ + "do", "values", "if", "when", "each", "for", "fn", "lambda", "partial", + "while", "set", "global", "var", "local", "let", "tset", + "or", "and", "true", "false", "nil", "#", ":", "->", "->>" +}; + +static const tic_outline_item* getFennelOutline(const char* code, s32* size) +{ + enum{Size = sizeof(tic_outline_item)}; + + *size = 0; + + static tic_outline_item* items = NULL; + + if(items) + { + free(items); + items = NULL; + } + + const char* ptr = code; + + while(true) + { + static const char FuncString[] = "fn"; /* this probably doesn't work */ + + ptr = strstr(ptr, FuncString); + + if(ptr) + { + const char* end = ptr; + + ptr += sizeof FuncString - 1; + + while(end >= code && !isalnum_(*end)) end--; + + const char* start = end; + + for (const char* val = start-1; val >= code && (isalnum_(*val)); val--, start--); + + if(end > start) + { + items = items ? realloc(items, (*size + 1) * Size) : malloc(Size); + + items[*size].pos = start - code; + items[*size].size = end - start + 1; + + (*size)++; + } + } + else break; + } + + return items; +} + +static const tic_script_config FennelSyntaxConfig = +{ + .init = initFennel, + .close = closeLua, + .tick = callLuaTick, + .scanline = callLuaScanline, + .overline = callLuaOverline, + + .getOutline = getFennelOutline, + .parse = parseCode, + + .blockCommentStart = NULL, + .blockCommentEnd = NULL, + .blockStringStart = NULL, + .blockStringEnd = NULL, + .singleComment = ";", + + .keywords = FennelKeywords, + .keywordsCount = COUNT_OF(FennelKeywords), + + .api = ApiKeywords, + .apiCount = COUNT_OF(ApiKeywords), +}; + +const tic_script_config* getFennelConfig() +{ + return &FennelSyntaxConfig; +} + +#endif /* defined(TIC_BUILD_WITH_FENNEL) */ + #endif /* defined(TIC_BUILD_WITH_LUA) */ diff --git a/src/machine.h b/src/machine.h index ec05c669a..56297d8d7 100644 --- a/src/machine.h +++ b/src/machine.h @@ -123,7 +123,7 @@ typedef struct struct { -#if defined(TIC_BUILD_WITH_LUA) || defined(TIC_BUILD_WITH_MOON) +#if defined(TIC_BUILD_WITH_LUA) || defined(TIC_BUILD_WITH_MOON) || defined(TIC_BUILD_WITH_FENNEL) struct lua_State* lua; #endif @@ -176,6 +176,10 @@ const tic_script_config* getLuaScriptConfig(); const tic_script_config* getMoonScriptConfig(); # endif +# if defined(TIC_BUILD_WITH_FENNEL) +const tic_script_config* getFennelConfig(); +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ diff --git a/src/tic.c b/src/tic.c index 0da3dada1..9f09675bd 100644 --- a/src/tic.c +++ b/src/tic.c @@ -566,6 +566,10 @@ void tic_close(tic_mem* tic) getMoonScriptConfig()->close(tic); # endif +# if defined(TIC_BUILD_WITH_FENNEL) + getFennelConfig()->close(tic); +# endif + #endif /* defined(TIC_BUILD_WITH_LUA) */ @@ -1566,6 +1570,11 @@ static const tic_script_config* getScriptConfig(const char* code) return getMoonScriptConfig(); #endif +#if defined(TIC_BUILD_WITH_FENNEL) + if(compareMetatag(code, "script", "fennel", getFennelConfig()->singleComment)) + return getFennelConfig(); +#endif + #if defined(TIC_BUILD_WITH_JS) if(compareMetatag(code, "script", "js", getJsScriptConfig()->singleComment) || compareMetatag(code, "script", "javascript", getJsScriptConfig()->singleComment))