nomsu/nomnom/compile.nom
Bruce Hill 652c29bdef Major overhaul, splitting nomsu_compiler into nomsu_environment,
nomsu_compiler, and nomsu_decompiler. Also added comprehensions.
2018-11-08 15:24:15 -08:00

279 lines
12 KiB
Plaintext

#!/usr/bin/env nomsu -V4.8.10
# This file contains the code to convert syntax trees to Lua code
use "nomnom/code_obj.nom"
use "nomnom/parser.nom"
use "nomnom/pretty_errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
externally (report compile error at %tree %err) means:
barf (pretty "Compile Error" error at %tree %err)
externally (report compile error at %pos %err hint %hint) means:
barf (pretty "Compile Error" error at %tree %err hint %hint)
externally (barf any errors in %t) means:
assume (%t is a "Syntax Tree")
%errs = []
for % in recursive %t:
if (%.type == "Error"): %errs::add %
for %k = %v in %:
if (%v is a "Syntax Tree"): recurse % on %v
sort %errs by % -> %.source
%errs = ((% as a pretty error) for % in %errs)
if ((size of %errs) > 0):
if ((size of %errs) > 3):
%n = ((size of %errs) - 3)
for %i in 4 to (size of %errs): %errs.%i = (nil)
%errs::add "\027[31;1m +\%n additional errors...\027[0m\n"
barf (%errs::joined with "\n\n")
externally (%tree compiled with %compile_actions) means:
assume (%tree is a "Syntax Tree")
if all of [..]
%tree.version, ((Nomsu version)'s meaning) != (nil), %tree.version != (Nomsu version)
((1 upgraded from 2 to 3)'s meaning) != (nil)
..then:
%tree = (upgrade %tree from %tree.version to (Nomsu version))
if %tree.type is:
"Action":
%stub = %tree.stub
%compile_action = %compile_actions.%stub
# Don't apply compiler actions to methods
if (%compile_action and (not %tree.target)):
# TODO: restore this:
#%args = [%tree, %compile_actions]
%args = [%nomsu, %tree]
for % in (%tree::arguments): %args::add %
%result = (call %compile_action with %args)
if (%result == (nil)):
report compile error at %tree "\
..The compile-time action here (\(%tree.stub)) failed to return any value."
..hint "\
..Look at the implementation of (\(%tree.stub)) and make sure it's returning something."
if (%result is a "Syntax Tree"):
if (%result == %tree):
report compile error at %tree "\
..The compile-time action here (\(%tree.stub)) is producing an endless loop."
..hint "\
..Look at the implementation of (\(%tree.stub)) and make sure it's not just returning the original tree."
return (%result compiled with %compile_actions)
return %result
%lua = (a Lua Buffer with {source:%tree})
if %tree.target:
# Method call
%target_lua = (%tree.target compiled with %compile_actions)
if (..)
((%target_lua::text)::matches "^%(.*%)$") or (..)
(%target_lua::text)::matches "^[_a-zA-Z][_a-zA-Z0-9]*$"
..:
%lua::add [%target_lua, ":"]
..else:
%lua::add ["(", %target_lua, "):"]
%lua::add [%stub as lua id, "("]
%args = []
for %tok in %tree at %i:
if (%tok is text): do next %tok
# TODO: maybe don't translate Lua comments
#if (%tok.type == "Comment"): do next %tok
if (%tok.type == "Block"):
%values = []
for %line in %tok:
#unless (%line.type == "Comment"):
%values::add (%line compiled with %compile_actions)
if all of (%.is_value for % in %values):
if ((size of %values) == 1):
%arg_lua = %values.1
..else:
%arg_lua = (a Lua Buffer with {source:%tok, is_value:yes, bits:["("]})
%arg_lua::add %values joined with " and nil or "
%arg_lua::add ")"
..else:
%arg_lua = (a Lua Buffer with {source:%tok, is_value:yes, bits:["((function()"]})
for %v in %values at %i:
if %v.is_value:
%v = (%v::as statements with ("return " if (%i == (size of %values) else "")))
%arg_lua::add ["\n ", %v]
%arg_lua::add "\nend)())"
..else:
%arg_lua = (%tok compiled with %compile_actions)
unless %arg_lua.is_value:
if (%tok.type == "Action"):
report compile error at %tok "\
..Can't use this as an argument to (\%stub), since it's not an expression, it produces: \%arg_lua"
..hint "\
..Check the implementation of (\(%tok.stub)) to see if it is actually meant to produce an expression."
..else:
report compile error at %tok "\
..Can't use this as an argument to (\%stub), since it's not an expression, it produces: \%arg_lua"
assume (%arg_lua != %lua) or barf "Huh? \%tree .\%i = \%tok -> \%arg_lua"
%args::add %arg_lua
%lua::add %args joined with ", "
%lua::add ")"
return %lua
"EscapedNomsu":
%lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["a_Syntax_Tree_with{type=", quote %tree.(1).type]})
set {%needs_comma:no, %i:1}
(% as shmua) means:
if (% is a "Lua number"): return "\%"
if (% is a "Syntax Tree"):
return (% compiled with %compile_actions)
if (% is text): return (quote %)
return (%::as lua)
for %k = %v in (((%tree.(1).type == "EscapedNomsu") and %tree) or %tree.1):
%lua::add ", "
if:
(%k == %i): %i += 1
((%k is text) and (%k::is a lua identifier)):
%lua::add [%k, "= "]
else:
%lua::add ["[", % as shmua, "]= "]
if (%k == "source"):
%lua::add (quote "\%v")
..else:
%lua::add (%v as shmua)
%lua::add "}"
return %lua
"Block":
%lua = (a Lua Buffer with {source:%tree})
%lua::add (..)
((%line compiled with %compile_actions)::as statements) for %line in %tree
..joined with "\n"
return %lua
"Text":
%lua = (a Lua Buffer with {source:%tree})
%lua_bits = []
%string_buffer = ""
for % in %tree:
if (% is text):
%string_buffer = "\%string_buffer\%"
do next %
if (%string_buffer != ""):
%lua_bits::add (%string_buffer::as lua)
%string_buffer = ""
%bit_lua = (% compiled with %compile_actions)
unless %bit_lua.is_value:
report compile error at % "\
..Can't use this as a string interpolation value, since it doesn't have a value."
if (%.type != "Text"):
%bit_lua = (a Lua Buffer with {source:%, is_value:yes, bits:["tostring(", %bit_lua, ")"]})
%lua_bits::add %bit_lua
if ((%string_buffer != "") or ((size of %lua_bits) == 0)):
%lua_bits::add (%string_buffer::as lua)
%lua::add %lua_bits joined with ".."
if ((size of %lua_bits) > 1): %lua::parenthesize
return %lua
"List":
%lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["List{"]})
%lua::add ((% compiled with %compile_actions) for % in %tree) joined with ", " or ",\n "
%lua::add "}"
return %lua
"Dict":
%lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["Dict{"]})
%lua::add ((% compiled with %compile_actions) for % in %tree) joined with ", " or ",\n "
%lua::add "}"
return %lua
"DictEntry":
set {%key:%tree.1, %value:%tree.2}
%key_lua = (%key compiled with %compile_actions)
unless %key_lua.is_value:
report compile error at %tree.1 "\
..Can't use this as a dict key, since it's not an expression."
%value_lua = (..)
(%value compiled with %compile_actions) if %value else (..)
a Lua Buffer with {source:%key, is_value:yes, bits:["true"]}
unless %value_lua.is_value:
report compile error at %tree.2 "\
..Can't use this as a dict value, since it's not an expression."
%key_str = ((%key_lua::text)::matching "^[\"']([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
if:
%key_str:
return (a Lua Buffer with {source:%tree, bits:[%key_str, "=", %value_lua]})
((%key_lua::text).1 == "["):
# NOTE: this *must* use a space after the [ to avoid freaking out
Lua's parser if the inner expression is a long string. Lua
parses x[[[y]]] as x("[y]"), not as x["y"]
return (a Lua Buffer with {source:%tree, bits:["[ ", %key_lua, "]=", %value_lua]})
else:
return (a Lua Buffer with {source:%tree, bits:["[", %key_lua, "]=", %value_lua]})
"IndexChain":
%lua = (%tree.1 compiled with %compile_actions)
unless %lua.is_value:
report compile error at %tree.1 "\
..Can't index into this, since it's not an expression."
%first_char = (%lua::text).1
if (any of [%first_char == "{", %first_char == "\"", %first_char == "["]):
%lua::parenthesize
for %i in 2 to (size of %tree):
%key = %tree.%i
%key_lua = (%key compiled with %compile_actions)
unless %key_lua.is_value:
report compile error at %key "\
..Can't use this as an index, since it's not an expression."
%key_lua_str = (%key_lua::text)
%lua_id = (%key_lua_str::matching "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
if:
%lua_id:
%lua::add [".", %lua_id]
(%key_lua_str.1 == "["):
# NOTE: this *must* use a space after the [ to avoid freaking out
Lua's parser if the inner expression is a long string. Lua
parses x[[[y]]] as x("[y]"), not as x["y"]
%lua::add ["[ ", %key_lua, " ]"]
else:
%lua::add ["[", %key_lua, "]"]
return %lua
"Number":
return (a Lua Buffer with {source:%tree, is_value:yes, bits:["\(%tree.1)"]})
"Var":
return (a Lua Buffer with {source:%tree, is_value:yes, is_variable:yes, bits:[%tree.1::as lua id]})
"FileChunks":
barf "\
..Can't convert FileChunks to a single block of lua, since each chunk's compilation depends on the earlier chunks"
"Comment":
# TODO: de-implement?
return (a Lua Buffer with {source:%tree, bits:["-- \(%tree.1::with "\n" -> "\n-- ")"]})
"Error":
barf (%tree as a pretty error)
else:
barf "Unknown type: \(%tree.type)"