💾 Archived View for soviet.circumlunar.space › oak › gmx.gmi captured on 2022-03-01 at 15:07:12. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
Experimental filter to process .gmi files for use with the gemini protocol.
Useful for static site generation or runtime server side templating / dynamic content inclusion (ymmv and experimental nature is stressed).
We use the alt text of gemtext preformatted blocks to generate inline expansion of arbitary code and script blocks for arbitary languages.
Best demonstrated by some examples..
The follow is a file named test.gmi (note the alt text near the ``` marker, this tells us we want to run the block using bash).
# Hello This is an example of using gmx. ``` bash:gmx cowsay "Hi from gmx!" ``` See you!
Now run gmx against test.gmi - the preformated block will be run using bash and its output inserted into the output in the expected location.
>./gmx test.gmi # Hello This is an example of using gmx. ``` ______________ < Hi from gmx! > -------------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || ``` See you!
You are not limited to using bash. For example, you can run Lua code snippets if you like (or python, or perl, or ..).
# Example 2 This is an example of using gmx and Lua. ``` lua:gmx opt = {} opt[ #opt + 1] = "one" opt[ #opt + 1] = "two" for i,v in ipairs(opt) do print(i,v) end ``` See you!
Gives
>./gmx test2.gmi # Example 2 This is an example of using gmx and Lua. ``` 1 one 2 two ``` See you!
If you prefer the output to not be within a preformatted blog, use the inline tag:
# Example 3 This is an example of using gmx and Lua. ``` lua:gmx:inline opt = {} opt[ #opt + 1] = "one" opt[ #opt + 1] = "two" for i,v in ipairs(opt) do print(i,v) end ``` See you!
Gives
>./gmx test2.gmi # Example 2 This is an example of using gmx and Lua. 1 one 2 two See you!
If process exits with an error then this gives a flavour of what happens (stderr is captured and inserted inline together with a line numbered output of the script/code block).
Error running inline script: exit 1 Command executed was: lua script.lua >output.tmp 2>error.tmp lua: script.lua:3: attempt to concatenate a nil value (global 'free') stack traceback: script.lua:3: in main chunk [C]: in ? Script was: 1: print( "hello world from inside a lua script" ) 2: 3: x = "" .. free 4: 5: tt = {} 6: tt[ #tt + 1 ] = "one" 7: tt[ #tt + 1 ] = "two" 8: 9: for i,v in ipairs(tt) do 10: print( i,v ) 11: end 12: 13: print( "thats all folks" ) 14:
Preformatted blocks without gmx blocks are processed in the normal way and not run as scripts.
text/gemini supports ``` preformated blocks.
The gemini specification says
Any text following the leading "```" of a preformat toggle line which toggles preformatted mode on MAY be interpreted by the client as "alt text" pertaining to the preformatted text lines which follow the toggle line. Use of alt text is at the client's discretion, and simple clients may ignore it. Alt text is recommended for ASCII art or similar non-textual content which, for example, cannot be meaningfully understood when rendered through a screen reader or usefully indexed by a search engine. Alt text may also be used for computer source code to identify the programming language which advanced clients may use for syntax highlighting.
gmx
Just a quick hack but works for me! ;-)
Keep smiling
#!/usr/local/bin/lua -- gmx 0.1 -- oak@soviet.circumlunar.space function file_exists(file) local f = io.open(file, "rb") if f then f:close() end return f ~= nil end -- get all lines from a file, returns an empty -- list/table if the file does not exist function lines_from(file) if not file_exists(file) then return {} end lines = {} for line in io.lines(file) do lines[#lines + 1] = line end return lines end function to_file( filename, lines ) local f = io.open( filename, "w+" ) if f then for i,v in ipairs(lines) do f:write( v .. "\n" ) end f:close() end end function show( gmi ) for i,v in ipairs(gmi) do print( v ) end end function append( output, new, withLineNumbers ) if new then if type(new) == "table" then for i,v in ipairs(new) do if withLineNumbers then output[ #output + 1] = string.format( "%3d: %s", i, v ) else output[ #output + 1] = v end end else output[ #output + 1 ] = "" .. new end end end function execute( lang, script, opts ) local script_name = "script.lua" local stdout = "output.tmp" local errout = "error.tmp" local cmdline = lang .. " " .. script_name .. " >" .. stdout .. " 2>" .. errout to_file( "script.lua", script ) local success, label, status = os.execute( cmdline ) local tmp = lines_from( stdout ) local output = {} if not success then append( output, "```" ) append( output, "Error running inline script: " .. label .. " " .. status ) append( output, "Command executed was: " .. cmdline ) append( output, "" ) append( output, lines_from( errout ) ) append( output, "" ) append( output, "Script was:" ) append( output, "" ) append( output, script, true ) append( output, "```" ) else if opts == "inline" then append( output, tmp ) else append( output, "```" ) append( output, tmp ) append( output, "```" ) end end return output end function filter( gmi ) local output = {} local script = {} local capturing = false local lang, opts for i,line in ipairs(gmi) do if string.sub(line,1,3) == "```" then capturing = not capturing if not capturing then if lang then local script_output = execute( lang, script, opts ) append( output, script_output ) else append( output, "```" ) append( output, script ) append( output, "```" ) end else lang = string.match( line, "```%s+([%w%._]+):gmx" ) opts = string.match( line, "```%s+[%w%._]+:gmx:(%g+)" ) script = {} end else if capturing then script[ #script + 1 ] = line else output[ #output + 1] = line end end end return output end gmi = lines_from( arg[1] ) gmix = filter( gmi ) show( gmix )