As you may know from
Post #535321, I've been experimenting with writing scripts for BizHawk in
Fennel, a lisp programming language based on Lua.
Fennel code has to be compiled to Lua before BizHawk can run it. To do that, I have been dividing programs into two files: a main ".fnl", written in Fennel, that has the actual code; and a stub ".lua" file that compiles and runs the .fnl file. You load the .lua file in BizHawk, then the .lua file runs the .fnl file.
For example, I might have a "script.fnl" that contains the code of the script:
Language: fennel
(console.log "Hello Fennel")
(console.log (string.format "ROM name: %q" (gameinfo.getromname)))
(local fennel (require :fennel)) ;; for fennel.view
(console.log "joypad" (fennel.view (joypad.get) {:one-line? true}))
And then a "script.lua" stub that executes the main Fennel file:
Language: lua
require("fennel").install().dofile("script.fnl")
The "fennel" module comes from
fennel.lua, which you just have to install in the directory alongside your scripts.
The two-file approach works fine. But I found a way to get the same effect with just one file. The file is simultaneously a Lua program and a Fennel program; i.e., a
polyglot. It looks like this:
Language: lua
;; return require("fennel").install().eval([==[
(console.log "Hello Fennel")
(console.log (string.format "ROM name: %q" (gameinfo.getromname)))
(local fennel (require :fennel)) ;; for fennel.view
(console.log "joypad" (fennel.view (joypad.get) {:one-line? true}))
;; ]==])
The
;; marks a comment in Fennel. So from the point of view of a Fennel interpreter, the first and last lines of the file disappear, leaving only the lisp code in between. In Lua,
; is an
empty statement, a no-op separator. So the Lua interpreter sees and executes the
require("fennel").install().eval(...). The
[==[ marks the beginning of a
long literal string that lasts until the
]==] on the last line—encompassing the entire text of the Fennel program. To the Lua interpreter, the Fennel part of the program looks like one long string, which gets passed to the
fennel.eval function.
It would be nice if only required adding something to the top of the file, not both top and bottom, but I couldn't think of a way to do that.
I've found just one problem with the polyglot approach, which is that BizHawk won't let you load a file with a ".fnl" filename extension with the
--lua option on the command line. I want to name the program file "script.fnl", so that when I edit it, I get syntax highlighting and automatic indentation appropriate for Fennel. But BizHawk's
--lua option
only allows ".lua" and ".txt" as filename extensions, and silently ignores files with any other extension. (Except for ".luases", which I don't know what that is.) You can load .fnl files from the "Open Script" UI in the Lua Console; it's just the
--lua option that doesn't work. So I have to name the file "script.lua", which adds inconvenience when editing it as Fennel code. So I might stick with the two-file method anyway.
If you want to see how the Fennel code compiles to Lua,
you can paste it in here.