Initial working version.

This commit is contained in:
Bruce Hill 2018-06-18 15:44:29 -07:00
parent fc71b0331b
commit 16d127abb5
19 changed files with 2158 additions and 2336 deletions

View File

@ -3,7 +3,7 @@ do
local _obj_0 = table local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end end
local Lua, Source local LuaCode, NomsuCode, Source
do do
local _class_0 local _class_0
local _base_0 = { local _base_0 = {
@ -242,7 +242,7 @@ do
if not (self.is_value) then if not (self.is_value) then
return self return self
end end
local statements = Lua(self.source) local statements = LuaCode(self.source)
if prefix ~= "" then if prefix ~= "" then
statements:append(prefix) statements:append(prefix)
end end
@ -272,7 +272,7 @@ do
local _list_1 = self.bits local _list_1 = self.bits
for _index_0 = 1, #_list_1 do for _index_0 = 1, #_list_1 do
local bit = _list_1[_index_0] local bit = _list_1[_index_0]
if bit.__class == Lua then if bit.__class == LuaCode then
gather_from(bit) gather_from(bit)
end end
end end
@ -357,7 +357,7 @@ do
self.__str = nil self.__str = nil
end, end,
__base = _base_0, __base = _base_0,
__name = "Lua", __name = "LuaCode",
__parent = _parent_0 __parent = _parent_0
}, { }, {
__index = function(cls, name) __index = function(cls, name)
@ -380,16 +380,15 @@ do
_base_0.__class = _class_0 _base_0.__class = _class_0
local self = _class_0 local self = _class_0
self.Value = function(...) self.Value = function(...)
local lua = Lua(...) local lua = LuaCode(...)
lua.is_value = true lua.is_value = true
return lua return lua
end end
if _parent_0.__inherited then if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0) _parent_0.__inherited(_parent_0, _class_0)
end end
Lua = _class_0 LuaCode = _class_0
end end
local Nomsu
do do
local _class_0 local _class_0
local _parent_0 = Code local _parent_0 = Code
@ -423,7 +422,7 @@ do
return _class_0.__parent.__init(self, ...) return _class_0.__parent.__init(self, ...)
end, end,
__base = _base_0, __base = _base_0,
__name = "Nomsu", __name = "NomsuCode",
__parent = _parent_0 __parent = _parent_0
}, { }, {
__index = function(cls, name) __index = function(cls, name)
@ -447,11 +446,11 @@ do
if _parent_0.__inherited then if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0) _parent_0.__inherited(_parent_0, _class_0)
end end
Nomsu = _class_0 NomsuCode = _class_0
end end
return { return {
Code = Code, Code = Code,
Nomsu = Nomsu, NomsuCode = NomsuCode,
Lua = Lua, LuaCode = LuaCode,
Source = Source Source = Source
} }

View File

@ -2,7 +2,7 @@
-- build up generated code, while keeping track of where it came from, and managing -- build up generated code, while keeping track of where it came from, and managing
-- indentation levels. -- indentation levels.
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
local Lua, Source local LuaCode, NomsuCode, Source
export LINE_STARTS export LINE_STARTS
class Source class Source
@ -99,7 +99,7 @@ class Code
else indents[i] = nil else indents[i] = nil
@__str = nil @__str = nil
class Lua extends Code class LuaCode extends Code
new: (...)=> new: (...)=>
super ... super ...
@free_vars = {} @free_vars = {}
@ -107,7 +107,7 @@ class Lua extends Code
@__str = nil @__str = nil
@Value = (...)-> @Value = (...)->
lua = Lua(...) lua = LuaCode(...)
lua.is_value = true lua.is_value = true
return lua return lua
@ -140,7 +140,7 @@ class Lua extends Code
as_statements: (prefix="", suffix=";")=> as_statements: (prefix="", suffix=";")=>
unless @is_value unless @is_value
return self return self
statements = Lua(@source) statements = LuaCode(@source)
if prefix != "" if prefix != ""
statements\append prefix statements\append prefix
statements\append self statements\append self
@ -157,7 +157,7 @@ class Lua extends Code
seen[var] = true seen[var] = true
to_declare[#to_declare+1] = var to_declare[#to_declare+1] = var
for bit in *@bits for bit in *@bits
if bit.__class == Lua if bit.__class == LuaCode
gather_from bit gather_from bit
gather_from self gather_from self
if #to_declare > 0 if #to_declare > 0
@ -205,7 +205,7 @@ class Lua extends Code
else else
error "Cannot parenthesize lua statements" error "Cannot parenthesize lua statements"
class Nomsu extends Code class NomsuCode extends Code
__tostring: => __tostring: =>
if @__str == nil if @__str == nil
buff, indents = {}, @indents buff, indents = {}, @indents
@ -224,4 +224,4 @@ class Nomsu extends Code
@prepend "(" @prepend "("
@append ")" @append ")"
return {:Code, :Nomsu, :Lua, :Source} return {:Code, :NomsuCode, :LuaCode, :Source}

View File

@ -9,125 +9,121 @@ use "core/operators.nom"
# List/dict functions: # List/dict functions:
# Indexing # Indexing
immediately compile [..]
compile [..]
%index st to last in %list, %index nd to last in %list, %index rd to last in %list %index st to last in %list, %index nd to last in %list, %index rd to last in %list
%index th to last in %list %index th to last in %list
..to: Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))" ..to: Lua value "utils.nth_to_last(\(%list as lua expr), \(%index as lua expr))"
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [last in %list] as: 1st to last in %list
parse [first in %list] as: %list.1 parse [last in %list] as: 1st to last in %list
parse [first in %list] as: %list.1
# Membership testing # Membership testing
immediately action [%item is in %list, %list contains %item, %list has %item]
action [%item is in %list, %list contains %item, %list has %item]
for %key = %value in %list for %key = %value in %list
if (%key is %item): return (yes) if (%key is %item): return (yes)
return (no) return (no)
action [..] action [..]
%item isn't in %list, %item is not in %list %item isn't in %list, %item is not in %list
%list doesn't contain %item, %list does not contain %item %list doesn't contain %item, %list does not contain %item
%list doesn't have %item, %list does not have %item %list doesn't have %item, %list does not have %item
.. ..
for %key = %value in %list for %key = %value in %list
if (%key is %item): return (no) if (%key is %item): return (no)
return (yes) return (yes)
immediately parse [%list has key %index, %list has index %index] as
parse [%list has key %index, %list has index %index] as
%list.%index != (nil) %list.%index != (nil)
parse [..] parse [..]
%list doesn't have key %index, %list does not have key %index %list doesn't have key %index, %list does not have key %index
%list doesn't have index %index, %list does not have index %index %list doesn't have index %index, %list does not have index %index
..as ..as
%list.%index = (nil) %list.%index = (nil)
compile [number of keys in %list] to compile [number of keys in %list] to
Lua value "utils.size(\(%list as lua expr))" Lua value "utils.size(\(%list as lua expr))"
compile [append %item to %list, add %item to %list, to %list add %item, to %list append %item] to compile [append %item to %list, add %item to %list, to %list add %item, to %list append %item] to
Lua "table.insert(\(%list as lua expr), \(%item as lua expr))" Lua "table.insert(\(%list as lua expr), \(%item as lua expr))"
compile [pop from %list, remove last from %list] to compile [pop from %list, remove last from %list] to
Lua "table.remove(\(%list as lua expr))" Lua "table.remove(\(%list as lua expr))"
compile [remove index %index from %list] to compile [remove index %index from %list] to
Lua "table.remove(\(%list as lua expr), \(%index as lua expr))" Lua "table.remove(\(%list as lua expr), \(%index as lua expr))"
# List Comprehension # List Comprehension
immediately parse [%expression for %item in %iterable] as
parse [%expression for %item in %iterable] as
result of result of
%comprehension <- [] %comprehension <- []
for %item in %iterable for %item in %iterable
add %expression to %comprehension add %expression to %comprehension
return %comprehension return %comprehension
parse [..] parse [..]
%expression for %index in %start to %stop via %step %expression for %index in %start to %stop via %step
%expression for %index in %start to %stop by %step %expression for %index in %start to %stop by %step
..as ..as
result of result of
%comprehension <- [] %comprehension <- []
for %index in %start to %stop via %step for %index in %start to %stop via %step
add %expression to %comprehension add %expression to %comprehension
return %comprehension return %comprehension
parse [%expression for %var in %start to %stop] as parse [%expression for %var in %start to %stop] as
%expression for %var in %start to %stop via 1 %expression for %var in %start to %stop via 1
parse [..] parse [..]
%expression for %key = %value in %iterable %expression for %key = %value in %iterable
%expression for (%key,%value) in %iterable %expression for (%key,%value) in %iterable
..as ..as
result of result of
%comprehension <- [] %comprehension <- []
for %key = %value in %iterable for %key = %value in %iterable
add %expression to %comprehension add %expression to %comprehension
return %comprehension return %comprehension
# Dict comprehensions # Dict comprehensions
parse [..] parse [..]
%key = %value for %item in %iterable %key = %value for %item in %iterable
(%key,%value) for %item in %iterable (%key,%value) for %item in %iterable
..as ..as
result of result of
%comprehension <- {} %comprehension <- {}
for %item in %iterable for %item in %iterable
%comprehension.%key <- %value %comprehension.%key <- %value
return %comprehension return %comprehension
parse [..] parse [..]
%key = %value for %src_key = %src_value in %iterable %key = %value for %src_key = %src_value in %iterable
(%key,%value) for (%src_key,%src_value) in %iterable (%key,%value) for (%src_key,%src_value) in %iterable
..as ..as
result of result of
%comprehension <- {} %comprehension <- {}
for %src_key = %src_value in %iterable for %src_key = %src_value in %iterable
%comprehension.%key <- %value %comprehension.%key <- %value
return %comprehension return %comprehension
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
action [%lists flattened] action [%lists flattened]
%flat <- [] %flat <- []
for %list in %lists for %list in %lists
for %item in %list for %item in %list
add %item to %flat add %item to %flat
return %flat return %flat
parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict parse [entries in %dict] as: {key:%k, value:%v} for %k = %v in %dict
parse [keys in %dict, keys of %dict] as: %k for %k = %v in %dict parse [keys in %dict, keys of %dict] as: %k for %k = %v in %dict
parse [values in %dict, values of %dict] as: %v for %k = %v in %dict parse [values in %dict, values of %dict] as: %v for %k = %v in %dict
# Metatable stuff # Metatable stuff
immediately compile [set %dict's metatable to %metatable] to
compile [set %dict's metatable to %metatable] to
Lua "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));" Lua "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
compile [%dict with fallback %key -> %value] to compile [%dict with fallback %key -> %value] to
Lua value ".." Lua value ".."
setmetatable(\(%dict as lua expr), {__index=function(self, \(%key as lua expr)) setmetatable(\(%dict as lua expr), {__index=function(self, \(%key as lua expr))
local value = \(%value as lua expr) local value = \(%value as lua expr)
@ -136,30 +132,30 @@ immediately
end}) end})
# Sorting # Sorting
immediately compile [sort %items] to: Lua "table.sort(\(%items as lua expr));"
compile [sort %items] to: Lua "table.sort(\(%items as lua expr));" parse [..]
parse [..]
sort %items by %item = %key_expr sort %items by %item = %key_expr
sort %items by %item -> %key_expr sort %items by %item -> %key_expr
..as ..as
do do
%keys <- ({} with fallback %item -> %key_expr) %keys <- ({} with fallback %item -> %key_expr)
lua> "table.sort(\%items, function(x,y) return \%keys[x] < \%keys[y] end)" lua> "table.sort(\%items, function(x,y) return \%keys[x] < \%keys[y] end)"
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
action [%items sorted, sorted %items]
action [%items sorted, sorted %items]
%copy <- (% for % in %items) %copy <- (% for % in %items)
sort %copy sort %copy
return %copy return %copy
action [..] action [..]
%items sorted by %item = %key %items sorted by %item = %key
%items sorted by %item -> %key %items sorted by %item -> %key
.. ..
%copy <- (% for % in %items) %copy <- (% for % in %items)
sort %copy by %item = %key sort %copy by %item = %key
return %copy return %copy
action [unique %items] action [unique %items]
%unique <- [] %unique <- []
%seen <- {} %seen <- {}
for % in %items for % in %items

View File

@ -8,19 +8,17 @@ use "core/operators.nom"
use "core/errors.nom" use "core/errors.nom"
# No-Op # No-Op
immediately compile [do nothing] to: Lua ""
compile [do nothing] to: Lua ""
# Conditionals # Conditionals
immediately compile [if %condition %if_body] to
compile [if %condition %if_body] to
Lua ".." Lua ".."
if \(%condition as lua expr) then if \(%condition as lua expr) then
\(%if_body as lua statements) \(%if_body as lua statements)
end end
parse [unless %condition %unless_body] as: if (not %condition) %unless_body parse [unless %condition %unless_body] as: if (not %condition) %unless_body
compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to compile [if %condition %if_body else %else_body, unless %condition %else_body else %if_body] to
Lua ".." Lua ".."
if \(%condition as lua expr) then if \(%condition as lua expr) then
\(%if_body as lua statements) \(%if_body as lua statements)
@ -28,16 +26,17 @@ immediately
\(%else_body as lua statements) \(%else_body as lua statements)
end end
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Conditional expression (ternary operator) # Conditional expression (ternary operator)
# Note: this uses a function instead of "(condition and if_expr or else_expr)" # Note: this uses a function instead of "(condition and if_expr or else_expr)"
because that breaks if %if_expr is falsey, e.g. "x < 5 and false or 99" because that breaks if %if_expr is falsey, e.g. "x < 5 and false or 99"
immediately compile [..]
compile [..]
%when_true_expr if %condition else %when_false_expr %when_true_expr if %condition else %when_false_expr
%when_true_expr if %condition otherwise %when_false_expr %when_true_expr if %condition otherwise %when_false_expr
%when_false_expr unless %condition else %when_true_expr %when_false_expr unless %condition else %when_true_expr
%when_false_expr unless %condition then %when_true_expr %when_false_expr unless %condition then %when_true_expr
..to ..to
# If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic # If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic
equivalent of a conditional expression: (cond and if_true or if_false) equivalent of a conditional expression: (cond and if_true or if_false)
if: {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type) if: {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type)
@ -59,20 +58,18 @@ immediately
end)()) end)())
# GOTOs # GOTOs
immediately compile [=== %label ===, --- %label ---, *** %label ***] to
compile [=== %label ===, --- %label ---, *** %label ***] to
Lua "::label_\(%label as lua identifier)::" Lua "::label_\(%label as lua identifier)::"
compile [go to %label] to compile [go to %label] to
Lua "goto label_\(%label as lua identifier)" Lua "goto label_\(%label as lua identifier)"
# Basic loop control # Basic loop control
immediately compile [do next] to: Lua "continue"
compile [do next] to: Lua "continue" compile [stop] to: Lua "break"
compile [stop] to: Lua "break"
# Helper function # Helper function
immediately #TODO: do "using % compile %" instead so it's actually a helper function
compile [%tree has subtree %subtree where %condition] to compile [%tree has subtree %subtree where %condition] to
Lua value ".." Lua value ".."
(function() (function()
for \(%subtree as lua expr) in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do for \(%subtree as lua expr) in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do
@ -83,11 +80,12 @@ immediately
return false return false
end)() end)()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# While loops # While loops
immediately compile [do next repeat] to: Lua "goto continue_repeat"
compile [do next repeat] to: Lua "goto continue_repeat" compile [stop repeating] to: Lua "goto stop_repeat"
compile [stop repeating] to: Lua "goto stop_repeat" compile [repeat while %condition %body] to
compile [repeat while %condition %body] to
%lua <- %lua <-
Lua ".." Lua ".."
while \(%condition as lua expr) do while \(%condition as lua expr) do
@ -106,12 +104,12 @@ immediately
::stop_repeat:: ::stop_repeat::
end -- end of "stop repeating" label scope end -- end of "stop repeating" label scope
return %lua return %lua
parse [repeat %body] as: repeat while (yes) %body parse [repeat %body] as: repeat while (yes) %body
parse [repeat until %condition %body] as: repeat while (not %condition) %body parse [repeat until %condition %body] as: repeat while (not %condition) %body
compile [..] compile [..]
repeat %n times %body repeat %n times %body
..to ..to
%lua <- %lua <-
Lua ".." Lua ".."
for i=1,\(%n as lua expr) do for i=1,\(%n as lua expr) do
@ -132,18 +130,18 @@ immediately
return %lua return %lua
# For loop control flow # For loop control flow
immediately compile [stop %var] to
compile [stop %var] to
Lua "goto stop_\(%var as lua identifier)" Lua "goto stop_\(%var as lua identifier)"
compile [do next %var] to compile [do next %var] to
Lua "goto continue_\(%var as lua identifier)" Lua "goto continue_\(%var as lua identifier)"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Numeric range for loops # Numeric range for loops
immediately compile [..]
compile [..]
for %var in %start to %stop by %step %body for %var in %start to %stop by %step %body
for %var in %start to %stop via %step %body for %var in %start to %stop via %step %body
..to ..to
# This uses Lua's approach of only allowing loop-scoped variables in a loop # This uses Lua's approach of only allowing loop-scoped variables in a loop
assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" assume (%var.type is "Var") or barf "Loop expected variable, not: \%var"
%lua <- %lua <-
@ -173,12 +171,12 @@ immediately
return %lua return %lua
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [for %var in %start to %stop %body] as: for %var in %start to %stop via 1 %body
parse [for %var in %start to %stop %body] as: for %var in %start to %stop via 1 %body
# For-each loop (lua's "ipairs()") # For-each loop (lua's "ipairs()")
immediately compile [for %var in %iterable %body] to
compile [for %var in %iterable %body] to
# This uses Lua's approach of only allowing loop-scoped variables in a loop # This uses Lua's approach of only allowing loop-scoped variables in a loop
assume (%var.type is "Var") or barf "Loop expected variable, not: \%var" assume (%var.type is "Var") or barf "Loop expected variable, not: \%var"
%lua <- %lua <-
@ -207,11 +205,10 @@ immediately
return %lua return %lua
# Dict iteration (lua's "pairs()") # Dict iteration (lua's "pairs()")
immediately compile [..]
compile [..]
for %key = %value in %iterable %body for %key = %value in %iterable %body
for (%key,%value) in %iterable %body for (%key,%value) in %iterable %body
..to ..to
# This uses Lua's approach of only allowing loop-scoped variables in a loop # This uses Lua's approach of only allowing loop-scoped variables in a loop
assume (%key.type is "Var") or barf "Loop expected variable, not: \%key" assume (%key.type is "Var") or barf "Loop expected variable, not: \%key"
assume (%value.type is "Var") or barf "Loop expected variable, not: \%value" assume (%value.type is "Var") or barf "Loop expected variable, not: \%value"
@ -258,9 +255,10 @@ immediately
end end
return %lua return %lua
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Switch statement/multi-branch if # Switch statement/multi-branch if
immediately compile [when %body] to
compile [when %body] to
%code <- (Lua "") %code <- (Lua "")
%fallthroughs <- [] %fallthroughs <- []
%is_first <- (yes) %is_first <- (yes)
@ -307,8 +305,7 @@ immediately
return %code return %code
# Switch statement # Switch statement
immediately compile [when %branch_value = ? %body, when %branch_value is ? %body] to
compile [when %branch_value = ? %body, when %branch_value is ? %body] to
%code <- (Lua "") %code <- (Lua "")
%fallthroughs <- [] %fallthroughs <- []
%is_first <- (yes) %is_first <- (yes)
@ -359,16 +356,14 @@ immediately
end --when % = ? end --when % = ?
return %code return %code
# Do/finally # Do/finally
immediately compile [do %action] to
compile [do %action] to
Lua ".." Lua ".."
do do
\(%action as lua statements) \(%action as lua statements)
end --do end --do
compile [do %action then always %final_action] to compile [do %action then always %final_action] to
Lua ".." Lua ".."
do do
local fell_through = false local fell_through = false
@ -382,8 +377,7 @@ immediately
end end
# Inline thunk: # Inline thunk:
immediately compile [result of %body] to
compile [result of %body] to
%body <- (%body as lua statements) %body <- (%body as lua statements)
declare locals in %body declare locals in %body
return return

View File

@ -22,11 +22,10 @@ compile [assume %condition or barf %message] to
end end
# Try/except # Try/except
immediately compile [..]
compile [..]
try %action and if it succeeds %success or if it barfs %msg %fallback try %action and if it succeeds %success or if it barfs %msg %fallback
try %action and if it barfs %msg %fallback or if it succeeds %success try %action and if it barfs %msg %fallback or if it succeeds %success
..to ..to
Lua ".." Lua ".."
do do
local fell_through = false local fell_through = false
@ -49,20 +48,21 @@ immediately
error(err, 0) error(err, 0)
end end
end end
immediately
parse [..] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [..]
try %action and if it succeeds %success or if it barfs %fallback try %action and if it succeeds %success or if it barfs %fallback
try %action and if it barfs %fallback or if it succeeds %success try %action and if it barfs %fallback or if it succeeds %success
..as: try %action and if it succeeds %success or if it barfs (=lua "") %fallback ..as: try %action and if it succeeds %success or if it barfs (=lua "") %fallback
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [try %action] as parse [try %action] as
try %action and if it succeeds: do nothing try %action and if it succeeds: do nothing
..or if it barfs: do nothing ..or if it barfs: do nothing
parse [try %action and if it barfs %fallback] as parse [try %action and if it barfs %fallback] as
try %action and if it succeeds: do nothing try %action and if it succeeds: do nothing
..or if it barfs %fallback ..or if it barfs %fallback
parse [try %action and if it barfs %msg %fallback] as parse [try %action and if it barfs %msg %fallback] as
try %action and if it succeeds: do nothing try %action and if it succeeds: do nothing
..or if it barfs %msg %fallback ..or if it barfs %msg %fallback
parse [try %action and if it succeeds %success] as parse [try %action and if it succeeds %success] as
try %action and if it succeeds %success or if it barfs: do nothing try %action and if it succeeds %success or if it barfs: do nothing

View File

@ -6,15 +6,15 @@ use "core/metaprogramming.nom"
compile [say %message] to compile [say %message] to
lua> ".." lua> ".."
if \%message.type == "Text" then if \%message.type == "Text" then
return Lua(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');"); return LuaCode(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');");
else else
return Lua(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');"); return LuaCode(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');");
end end
compile [ask %prompt] to compile [ask %prompt] to
lua> ".." lua> ".."
if \%prompt.type == "Text" then if \%prompt.type == "Text" then
return Lua.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())"); return LuaCode.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())");
else else
return Lua.Value(tree.source, "(io.write(tostring(", \(%prompt as lua expr), ")) and io.read())"); return LuaCode.Value(tree.source, "(io.write(tostring(", \(%prompt as lua expr), ")) and io.read())");
end end

View File

@ -3,34 +3,48 @@
functions to make that easier. functions to make that easier.
# Compile-time action to make compile-time actions: # Compile-time action to make compile-time actions:
immediately lua> ".."
lua> ".." nomsu.COMPILE_ACTIONS["give % nickname %"] = (function(nomsu, tree, \%action, \%nickname, is_compile_time)
A_give_1_nickname_2 = compile_time(function(tree, \%action, \%nickname, is_compile_time) local function arg_to_string(a) return tostring(nomsu:compile(a)) end
local function arg_to_string(a) return tostring(nomsu:tree_to_lua(a)) end
local action_args = table.map(\%action:get_args(), arg_to_string) local action_args = table.map(\%action:get_args(), arg_to_string)
local nickname_args = table.map(\%nickname:get_args(), arg_to_string) local nickname_args = table.map(\%nickname:get_args(), arg_to_string)
if utils.equivalent(action_args, nickname_args) then if utils.equivalent(action_args, nickname_args) then
return Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub)) if is_compile_time then
return LuaCode(tree.source, "nomsu.COMPILE_ACTIONS[", repr(\%nickname.stub), "] = nomsu.COMPILE_ACTIONS[", repr(\%action.stub), "]")
else
return LuaCode(tree.source, "A", string.as_lua_id(\%nickname.stub), " = A", string.as_lua_id(\%action.stub))
end end
local lua = Lua(tree.source, "A", string.as_lua_id(\%nickname.stub), " = ") end
if is_compile_time or COMPILE_TIME[_ENV["A"..string.as_lua_id(\%action.stub)]] then local lua = LuaCode(tree.source)
lua:append("compile_time") if is_compile_time then
table.insert(action_args, 1, "tree") lua:append("nomsu.COMPILE_ACTIONS[", repr(\%nickname.stub), "] = ")
table.insert(nickname_args, 1, "tree") table.insert(action_args, 1, "nomsu")
table.insert(nickname_args, 1, "nomsu")
table.insert(action_args, 2, "tree")
table.insert(nickname_args, 2, "tree")
else
lua:append("A", string.as_lua_id(\%nickname.stub), " = ")
end end
lua:append("(function(") lua:append("(function(")
lua:concat_append(nickname_args, ", ") lua:concat_append(nickname_args, ", ")
if is_compile_time then
lua:append(")\n return nomsu.COMPILE_ACTIONS[", repr(\%action.stub), "](")
else
lua:append(")\n return A", string.as_lua_id(\%action.stub), "(") lua:append(")\n return A", string.as_lua_id(\%action.stub), "(")
end
lua:concat_append(action_args, ", ") lua:concat_append(action_args, ", ")
lua:append(")\nend)") lua:append(")\nend)")
if not is_compile_time then
lua:add_free_vars({"A"..string.as_lua_id(\%nickname.stub)})
end
return lua return lua
end) end)
__MANGLE_INDEX = 0 __MANGLE_INDEX = 0
A_parse_1_as_2 = compile_time(function(tree, \%actions, \%body) nomsu.COMPILE_ACTIONS["parse % as %"] = (function(nomsu, tree, \%actions, \%body)
local replacements = {} local replacements = {}
for i,arg in ipairs(\%actions[1]:get_args()) do for i,arg in ipairs(\%actions[1]:get_args()) do
replacements[arg[1]] = tostring(nomsu:tree_to_lua(arg)) replacements[arg[1]] = tostring(nomsu:compile(arg))
end end
local function make_tree(t) local function make_tree(t)
if not AST.is_syntax_tree(t) then if not AST.is_syntax_tree(t) then
@ -45,13 +59,14 @@ immediately
return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))" return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX))"
end end
end end
local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = compile_time(function(tree") local lua = LuaCode(tree.source, "nomsu.COMPILE_ACTIONS[", repr(\%actions[1].stub), "] = (function(nomsu, tree")
lua:add_free_vars({"A"..string.as_lua_id(\%actions[1].stub)})
for _,arg in ipairs(\%actions[1]:get_args()) do for _,arg in ipairs(\%actions[1]:get_args()) do
lua:append(", ", nomsu:tree_to_lua(arg)) lua:append(", ", nomsu:compile(arg))
end end
lua:append(")\n __MANGLE_INDEX = __MANGLE_INDEX + 1", lua:append(")\n __MANGLE_INDEX = __MANGLE_INDEX + 1",
"\n local tree = ", make_tree(\%body), "\n local tree = ", make_tree(\%body),
"\n local lua = nomsu:tree_to_lua(tree)", "\n local lua = nomsu:compile(tree)",
"\n lua:remove_free_vars({") "\n lua:remove_free_vars({")
local vars = table.map(\%actions[1]:get_args(), function(a) local vars = table.map(\%actions[1]:get_args(), function(a)
return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")" return "Var("..repr(tostring(a.source))..", "..repr(a[1])..")"
@ -60,123 +75,138 @@ immediately
lua:append("})\n return lua\nend)") lua:append("})\n return lua\nend)")
for i=2,#\%actions do for i=2,#\%actions do
lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], true)) lua:append("\n", nomsu.COMPILE_ACTIONS["give % nickname %"](nomsu, \%actions[i], \%actions[1], \%actions[i], true))
end end
return lua return lua
end) end)
A_action_1_2 = compile_time(function(tree, \%actions, \%body, is_compile_time) nomsu.COMPILE_ACTIONS["local action % %"] = (function(nomsu, tree, \%actions, \%body, is_compile_time)
local lua = Lua(tree.source, "A", string.as_lua_id(\%actions[1].stub), " = ") local lua = LuaCode(tree.source)
if is_compile_time then lua:append("compile_time") end if is_compile_time then
lua:append("nomsu.COMPILE_ACTIONS[", repr(\%actions[1].stub), "] = ")
else
lua:append("A", string.as_lua_id(\%actions[1].stub), " = ")
lua:add_free_vars({"A"..string.as_lua_id(\%actions[1].stub)})
end
lua:append("(function(") lua:append("(function(")
local args = \%actions[1]:get_args() local args = \%actions[1]:get_args()
local lua_args = table.map(args, function(a) return nomsu:tree_to_lua(a) end) local lua_args = table.map(args, function(a) return nomsu:compile(a) end)
if is_compile_time then table.insert(lua_args, 1, "tree") end if is_compile_time then
table.insert(lua_args, 1, "nomsu")
table.insert(lua_args, 2, "tree")
end
lua:concat_append(lua_args, ", ") lua:concat_append(lua_args, ", ")
local body_lua = nomsu:tree_to_lua(\%body):as_statements("return ") local body_lua = nomsu:compile(\%body):as_statements("return ")
body_lua:remove_free_vars(args) body_lua:remove_free_vars(args)
body_lua:declare_locals() body_lua:declare_locals()
lua:append(")\n ", body_lua, "\nend)") lua:append(")\n ", body_lua, "\nend)")
for i=2,#\%actions do for i=2,#\%actions do
lua:append("\n", A_give_1_nickname_2(\%actions[i], \%actions[1], \%actions[i], is_compile_time)) lua:append("\n", nomsu.COMPILE_ACTIONS["give % nickname %"](nomsu, \%actions[i], \%actions[1], \%actions[i], is_compile_time))
end end
return lua return lua
end) end)
A_compile_1_to_2 = compile_time(function(tree, \%actions, \%body) -- Compile-time actions are always global, since they affect the state of the compiler
return A_action_1_2(tree, \%actions, \%body, true) nomsu.COMPILE_ACTIONS["compile % to %"] = (function(nomsu, tree, \%actions, \%body)
local lua = nomsu.COMPILE_ACTIONS["local action % %"](nomsu, tree, \%actions, \%body, true)
return lua
end) end)
nomsu.COMPILE_ACTIONS["action % %"] = (function(nomsu, tree, \%actions, \%body)
local lua = nomsu.COMPILE_ACTIONS["local action % %"](nomsu, tree, \%actions, \%body)
lua:remove_free_vars(table.map(\%actions, function(a) return "A"..a.stub:as_lua_id() end))
return lua
end)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compile [remove action %action] to compile [remove action %action] to
Lua ".." Lua ".."
A\(=lua "string.as_lua_id(\(%action.stub))") = nil A\(=lua "string.as_lua_id(\(%action.stub))") = nil
ARG_ORDERS[fn] = nil ARG_ORDERS[fn] = nil
COMPILE_TIME[fn] = nil COMPILE_TIME[fn] = nil
immediately action [%tree as nomsu]
action [%tree as nomsu]
=lua "nomsu:tree_to_nomsu(\%tree)" =lua "nomsu:tree_to_nomsu(\%tree)"
action [%tree as inline nomsu] action [%tree as inline nomsu]
=lua "nomsu:tree_to_nomsu(\%tree, true)" =lua "nomsu:tree_to_nomsu(\%tree, true)"
action [%tree as lua] action [%tree as lua]
=lua "nomsu:tree_to_lua(\%tree)" =lua "nomsu:compile(\%tree)"
action [%tree as lua expr] action [%tree as lua expr]
lua> ".." lua> ".."
local lua = nomsu:tree_to_lua(\%tree) local lua = nomsu:compile(\%tree)
if not lua.is_value then if not lua.is_value then
compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s") nomsu:compile_error(\%tree, "Invalid thing to convert to lua expr:\n%s")
end end
return lua return lua
action [%tree as lua statements] action [%tree as lua statements]
=lua "nomsu:tree_to_lua(\%tree):as_statements()" =lua "nomsu:compile(\%tree):as_statements()"
action [%tree as lua return] action [%tree as lua return]
=lua "nomsu:tree_to_lua(\%tree):as_statements('return ')" =lua "nomsu:compile(\%tree):as_statements('return ')"
action [%var as lua identifier, %var as lua id] action [%var as lua identifier, %var as lua id]
lua> ".." lua> ".."
if type(\%var) == 'string' then return string.as_lua_id(\%var) if type(\%var) == 'string' then return string.as_lua_id(\%var)
elseif \%var.type == 'Var' then return string.as_lua_id(\%var[1]) elseif \%var.type == 'Var' then return string.as_lua_id(\%var[1])
elseif \%var.type == 'Action' then return "A"..string.as_lua_id(\%var.stub) elseif \%var.type == 'Action' then return "A"..string.as_lua_id(\%var.stub)
end end
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compile [%tree with %t -> %replacement] to
compile [%tree with %t -> %replacement] to
Lua value ".." Lua value ".."
\(%tree as lua expr):map(function(\(%t as lua expr)) \(%tree as lua expr):map(function(\(%t as lua expr))
\(%replacement as lua return) \(%replacement as lua return)
end) end)
compile [declare locals in %code] to compile [declare locals in %code] to
Lua value "\(%code as lua expr):declare_locals()" Lua value "\(%code as lua expr):declare_locals()"
compile [declare locals %locals in %code] to compile [declare locals %locals in %code] to
Lua value "\(%code as lua expr):declare_locals(\(%locals as lua expr))" Lua value "\(%code as lua expr):declare_locals(\(%locals as lua expr))"
compile [remove free vars %vars from %code] to compile [remove free vars %vars from %code] to
Lua "\(%code as lua expr):remove_free_vars(\(%vars as lua expr));" Lua "\(%code as lua expr):remove_free_vars(\(%vars as lua expr));"
parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);" parse [%lua <-write %code, to %lua write %code] as: lua> "\%lua:append(\%code);"
immediately compile [quote %s] to
compile [quote %s] to
Lua value ".." Lua value ".."
('"'..\(%s as lua expr):gsub("\\\\", "\\\\\\\\"):gsub("\n","\\\\n"):gsub('"', '\\\\"')..'"') ('"'..\(%s as lua expr):gsub("\\\\", "\\\\\\\\"):gsub("\n","\\\\n"):gsub('"', '\\\\"')..'"')
compile [type of %obj] to: Lua value "type(\(%obj as lua expr))" compile [type of %obj] to: Lua value "type(\(%obj as lua expr))"
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compile [parse %text] to
compile [parse %text] to
Lua value ".." Lua value ".."
nomsu:parse(Nomsu("\("\(%text.source)")", \(%text as lua expr))) nomsu:parse(NomsuCode("\("\(%text.source)")", \(%text as lua expr)))
compile [run %nomsu_code] to compile [run %nomsu_code] to
Lua value "nomsu:run(Nomsu(\(quote "\(%nomsu_code.source)"), \(%nomsu_code as lua expr)))" Lua value "nomsu:run(NomsuCode(\(quote "\(%nomsu_code.source)"), \(%nomsu_code as lua expr)))"
action [run tree %tree, %tree as value] action [run tree %tree, %tree as value]
lua> ".." lua> ".."
if \%tree.type == 'Text' and #\%tree == 1 and type(\%tree[1]) == 'string' then if \%tree.type == 'Text' and #\%tree == 1 and type(\%tree[1]) == 'string' then
return \%tree[1] return \%tree[1]
end end
local lua = Lua(\%tree.source, "return ",nomsu:tree_to_lua(\%tree)) local lua = LuaCode(\%tree.source, "return ",nomsu:compile(\%tree))
return nomsu:run_lua(lua) return nomsu:run_lua(lua)
compile [compile %block, compiled %block, %block compiled] to compile [compile %block, compiled %block, %block compiled] to
Lua value "nomsu:tree_to_lua(\(%block as lua))" Lua value "nomsu:compile(\(%block as lua))"
# Return # Return statement is wrapped in a do..end block because Lua is unhappy if you
immediately
# Return statement is wrapped in a do..end block because Lua is unhappy if you
put code after a return statement, unless you wrap it in a block. put code after a return statement, unless you wrap it in a block.
compile [return] to: Lua "do return; end" compile [return] to: Lua "do return; end"
compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end" compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end"
# Literals # Literals
immediately compile [yes] to: Lua value "true"
compile [yes] to: Lua value "true" compile [no] to: Lua value "false"
compile [no] to: Lua value "false" compile [nothing, nil, null] to: Lua value "nil"
compile [nothing, nil, null] to: Lua value "nil"

View File

@ -5,21 +5,37 @@ use "core/metaprogramming.nom"
use "core/errors.nom" use "core/errors.nom"
# Comparison Operators # Comparison Operators
immediately compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))"
compile [%x < %y] to: Lua value "(\(%x as lua expr) < \(%y as lua expr))" compile [%x > %y] to: Lua value "(\(%x as lua expr) > \(%y as lua expr))"
compile [%x > %y] to: Lua value "(\(%x as lua expr) > \(%y as lua expr))" compile [%x <= %y] to: Lua value "(\(%x as lua expr) <= \(%y as lua expr))"
compile [%x <= %y] to: Lua value "(\(%x as lua expr) <= \(%y as lua expr))" compile [%x >= %y] to: Lua value "(\(%x as lua expr) >= \(%y as lua expr))"
compile [%x >= %y] to: Lua value "(\(%x as lua expr) >= \(%y as lua expr))" compile [%a is %b, %a = %b, %a == %b] to
compile [%a is %b, %a = %b, %a == %b] to
Lua value "(\(%a as lua expr) == \(%b as lua expr))" Lua value "(\(%a as lua expr) == \(%b as lua expr))"
compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to
Lua value "(\(%a as lua expr) ~= \(%b as lua expr))" Lua value "(\(%a as lua expr) ~= \(%b as lua expr))"
# For strict identity checking, use (%x's id) is (%y's id) # For strict identity checking, use (%x's id) is (%y's id)
compile [%'s id, id of %] to: Lua value "nomsu.ids[\(% as lua expr)]" lua> ".."
do
local new_uuid = require('uuid')
local NaN_surrogate = {}
local nil_surrogate = {}
IDS = setmetatable({}, {
__mode = "k",
__index = function(self, key)
if key == nil then return self[nil_surrogate]
elseif key ~= key then return self[NaN_surrogate] end
local id = new_uuid()
self[key] = id
return id
end
})
end
compile [%'s id, id of %] to: Lua value "IDS[\(% as lua expr)]"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Variable assignment operator # Variable assignment operator
immediately compile [%var <- %value] to
compile [%var <- %value] to
lua> "local \%var_lua = \(%var as lua)" lua> "local \%var_lua = \(%var as lua)"
assume %var_lua.is_value or barf "Invalid target for assignment: \%var" assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
lua> ".." lua> ".."
@ -32,19 +48,18 @@ immediately
assume %value_lua.is_value or barf "Invalid value for assignment: \%value" assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
lua> ".." lua> ".."
local lua = Lua(tree.source, \%var_lua, ' = ', \%value_lua, ';') local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
if \%var.type == 'Var' then if \%var.type == 'Var' then
lua:add_free_vars({\%var}) lua:add_free_vars({\%var})
end end
return lua; return lua
immediately # Simultaneous mutli-assignments like: x,y,z = 1,x,3;
# Simultaneous mutli-assignments like: x,y,z = 1,x,3; compile [<- %assignments, assign %assignments] to
compile [<- %assignments, assign %assignments] to
assume (%assignments.type is "Dict") or barf ".." assume (%assignments.type is "Dict") or barf ".."
Expected a Dict for the assignments part of '<- %' statement, not \%assignments Expected a Dict for the assignments part of '<- %' statement, not \%assignments
lua> ".." lua> ".."
local lhs, rhs = Lua(tree.source), Lua(tree.source) local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source)
for i, item in ipairs(\%assignments) do for i, item in ipairs(\%assignments) do
local \%target, \%value = item[1], item[2] local \%target, \%value = item[1], item[2]
\%value = \%value:map(function(t) \%value = \%value:map(function(t)
@ -66,25 +81,26 @@ immediately
lhs:append(target_lua) lhs:append(target_lua)
rhs:append(value_lua) rhs:append(value_lua)
end end
return Lua(tree.source, lhs, " = ", rhs, ";") return LuaCode(tree.source, lhs, " = ", rhs, ";")
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compile [external %var <- %value] to
compile [external %var <- %value] to
%var_lua <- (%var as lua) %var_lua <- (%var as lua)
assume %var_lua.is_value or barf "Invalid target for assignment: \%var" assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
%value_lua <- (%value as lua) %value_lua <- (%value as lua)
assume %value_lua.is_value or barf "Invalid value for assignment: \%value" assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
return: Lua "\%var_lua = \%value_lua;" return: Lua "\%var_lua = \%value_lua;"
compile [with external %externs %body] to compile [with external %externs %body] to
%body_lua <- (%body as lua statements) %body_lua <- (%body as lua statements)
lua> "\%body_lua:remove_free_vars(\%externs);" lua> "\%body_lua:remove_free_vars(\%externs);"
return %body_lua return %body_lua
compile [with %assignments %body] to compile [with %assignments %body] to
%lua <- (%body as lua statements) %lua <- (%body as lua statements)
lua> ".." lua> ".."
local lhs, rhs = Lua(tree.source), Lua(tree.source) local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source)
local vars = {} local vars = {}
for i, item in ipairs(\%assignments) do for i, item in ipairs(\%assignments) do
local \%target, \%value = item[1], item[2] local \%target, \%value = item[1], item[2]
@ -115,48 +131,49 @@ immediately
\%lua \%lua
end -- 'with' block end -- 'with' block
immediately # Math Operators
# Math Operators compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))"
compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))"
# 3-part chained comparisons # 3-part chained comparisons
# (uses a lambda to avoid re-evaluating middle value, while still being an expression) # (uses a lambda to avoid re-evaluating middle value, while still being an expression)
parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)" parse [%x < %y < %z] as: =lua "(function(x,y,z) return x < y and y < z; end)(\%x,\%y,\%z)"
parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)" parse [%x <= %y < %z] as: =lua "(function(x,y,z) return x <= y and y < z; end)(\%x,\%y,\%z)"
parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)" parse [%x < %y <= %z] as: =lua "(function(x,y,z) return x < y and y <= z; end)(\%x,\%y,\%z)"
parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)" parse [%x <= %y <= %z] as: =lua "(function(x,y,z) return x <= y and y <= z; end)(\%x,\%y,\%z)"
parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)" parse [%x > %y > %z] as: =lua "(function(x,y,z) return x > y and y > z; end)(\%x,\%y,\%z)"
parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)" parse [%x >= %y > %z] as: =lua "(function(x,y,z) return x >= y and y > z; end)(\%x,\%y,\%z)"
parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)" parse [%x > %y >= %z] as: =lua "(function(x,y,z) return x > y and y >= z; end)(\%x,\%y,\%z)"
parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)" parse [%x >= %y >= %z] as: =lua "(function(x,y,z) return x >= y and y >= z; end)(\%x,\%y,\%z)"
# TODO: optimize for common case where x,y,z are all either variables or number literals # TODO: optimize for common case where x,y,z are all either variables or number literals
# Boolean Operators # Boolean Operators
compile [%x and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))" compile [%x and %y] to: Lua value "(\(%x as lua expr) and \(%y as lua expr))"
compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))" compile [%x or %y] to: Lua value "(\(%x as lua expr) or \(%y as lua expr))"
# Bitwise Operators # Bitwise Operators
compile [%a OR %b, %a | %b] to: Lua value "bit32.bor(\(%a as lua expr), \(%b as lua expr))" # TODO: support bit.???() for luajit and bit32.??? for lua 5.2
compile [%a XOR %b] to: Lua value "bit32.bxor(\(%a as lua expr), \(%b as lua expr))" compile [%a OR %b, %a | %b] to: Lua value "(\(%a as lua expr) | \(%b as lua expr))"
compile [%a AND %b, %a & %b] to: Lua value "bit32.band(\(%a as lua expr), \(%b as lua expr))" compile [%a XOR %b] to: Lua value "(\(%a as lua expr) ^ \(%b as lua expr))"
compile [NOT %, ~ %] to: Lua value "bit32.bnot(\(% as lua expr))" compile [%a AND %b, %a & %b] to: Lua value "(\(%a as lua expr) & \(%b as lua expr))"
compile [%x LSHIFT %shift, %x << %shift] to: Lua value "bit32.lshift(\(%x as lua expr), \(%shift as lua expr))" compile [NOT %, ~ %] to: Lua value "~(\(% as lua expr))"
compile [%x RSHIFT %shift, %x >>> %shift] to: Lua value "bit32.rshift(\(%x as lua expr), \(%shift as lua expr))" compile [%x LSHIFT %shift, %x << %shift] to: Lua value "(\(%x as lua expr) << \(%shift as lua expr))"
compile [%x ARSHIFT %shift, %x >> %shift] to: Lua value "bit32.arshift(\(%x as lua expr), \(%shift as lua expr))" compile [%x RSHIFT %shift, %x >>> %shift] to: Lua value "(\(%x as lua expr) >>> \(%shift as lua expr))"
# TODO: implement OR, XOR, AND for multiple operands? compile [%x ARSHIFT %shift, %x >> %shift] to: Lua value "(\(%x as lua expr) >> \(%shift as lua expr))"
# TODO: implement OR, XOR, AND for multiple operands?
# Unary operators # Unary operators
compile [- %] to: Lua value "(- \(% as lua expr))" compile [- %] to: Lua value "(- \(% as lua expr))"
compile [not %] to: Lua value "(not \(% as lua expr))" compile [not %] to: Lua value "(not \(% as lua expr))"
compile [length of %list] to: Lua value "(#\(%list as lua expr))" compile [length of %list] to: Lua value "(#\(%list as lua expr))"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update operators # Update operators
immediately parse [%var + <- %, %var +<- %] as: %var <- (%var + %)
parse [%var + <- %, %var +<- %] as: %var <- (%var + %) parse [%var - <- %, %var -<- %] as: %var <- (%var - %)
parse [%var - <- %, %var -<- %] as: %var <- (%var - %) parse [%var * <- %, %var *<- %] as: %var <- (%var * %)
parse [%var * <- %, %var *<- %] as: %var <- (%var * %) parse [%var / <- %, %var /<- %] as: %var <- (%var / %)
parse [%var / <- %, %var /<- %] as: %var <- (%var / %) parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %)
parse [%var ^ <- %, %var ^<- %] as: %var <- (%var ^ %) parse [%var and <- %] as: %var <- (%var and %)
parse [%var and <- %] as: %var <- (%var and %) parse [%var or <- %] as: %var <- (%var or %)
parse [%var or <- %] as: %var <- (%var or %) parse [wrap %var around %] as: %var <- (%var wrapped around %)
parse [wrap %var around %] as: %var <- (%var wrapped around %)

View File

@ -35,7 +35,7 @@ lua> ".."
}; };
for name, e in pairs(escapes) do for name, e in pairs(escapes) do
local lua = "'"..e.."'" local lua = "'"..e.."'"
_ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, lua) end) nomsu.COMPILE_ACTIONS[name] = (function(nomsu, tree) return LuaCode.Value(tree.source, lua) end)
end end
local colors = { local colors = {
["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m", ["reset color"]="\\\\27[0m", bright="\\\\27[1m", dim="\\\\27[2m", underscore="\\\\27[4m",
@ -50,9 +50,9 @@ lua> ".."
for name, c in pairs(colors) do for name, c in pairs(colors) do
local color = "'"..c.."'"; local color = "'"..c.."'";
local reset = "'"..colors["reset color"].."'"; local reset = "'"..colors["reset color"].."'";
_ENV["A"..name:as_lua_id()] = compile_time(function(tree) return Lua.Value(tree.source, color) end) nomsu.COMPILE_ACTIONS[name] = (function(nomsu, tree) return LuaCode.Value(tree.source, color) end)
_ENV["A"..name:as_lua_id().."_1"] = compile_time(function(tree, text) nomsu.COMPILE_ACTIONS[name.." %"] = (function(nomsu, tree, text)
return Lua.Value(tree.source, color, "..", nomsu:tree_to_lua(text), "..", reset); return LuaCode.Value(tree.source, color, "..", nomsu:tree_to_lua(text), "..", reset);
end) end)
end end
end end

View File

@ -3,22 +3,22 @@
use "core" use "core"
immediately compile [@, me] to: Lua value "self"
compile [@, me] to: Lua value "self" compile [set methods %methods] to
compile [set methods %methods] to
%lua <- (Lua "") %lua <- (Lua "")
for %m in %methods for %m in %methods
to %lua write "\nclass.\(%m as lua id) = \(%m as lua id)" to %lua write "\nclass.\(%m as lua id) = \(%m as lua id)"
return %lua return %lua
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [method %actions %body] as
parse [method %actions %body] as
with local %actions with local %actions
action %actions %body action %actions %body
set methods %actions set methods %actions
immediately ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [as %instance %body] as parse [as %instance %body] as
result of result of
%old_self <- (me) %old_self <- (me)
(me) <- %instance (me) <- %instance
@ -30,7 +30,7 @@ immediately
..or if it succeeds ..or if it succeeds
(me) <- %old_self (me) <- %old_self
compile [object %classname extends %parent %class_body] to compile [object %classname extends %parent %class_body] to
Lua ".." Lua ".."
do do
local class = {name=\(%classname as lua expr)} local class = {name=\(%classname as lua expr)}
@ -56,4 +56,5 @@ immediately
class.__tostring = class["A"..string.as_lua_id("as text")] class.__tostring = class["A"..string.as_lua_id("as text")]
end end
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
parse [object %classname %class_body] as: object %classname extends (nil) %class_body parse [object %classname %class_body] as: object %classname extends (nil) %class_body

894
nomsu.lua

File diff suppressed because it is too large Load Diff

View File

@ -10,29 +10,20 @@
-- nomsu:run(your_nomsu_code) -- nomsu:run(your_nomsu_code)
-- Or from the command line: -- Or from the command line:
-- lua nomsu.lua your_file.nom -- lua nomsu.lua your_file.nom
export lpeg, re
_pairs, _ipairs = pairs, ipairs
if jit
package.cpath = "./luajit_lpeg/?.so;"..package.cpath
package.path = "./luajit_lpeg/?.lua;"..package.path
export bit32
bit32 = require('bit')
lpeg = require 'lpeg' lpeg = require 'lpeg'
re = require 're' re = require 're'
lpeg.setmaxstack 10000 lpeg.setmaxstack 10000
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg {:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg
utils = require 'utils' utils = require 'utils'
new_uuid = require 'uuid'
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils {:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""}) colors = setmetatable({}, {__index:->""})
export colored export colored
colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)}) colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
unpack or= table.unpack
{:match, :sub, :rep, :gsub, :format, :byte, :match, :find} = string {:match, :sub, :rep, :gsub, :format, :byte, :match, :find} = string
debug_getinfo = debug.getinfo debug_getinfo = debug.getinfo
{:Nomsu, :Lua, :Source} = require "code_obj" {:NomsuCode, :LuaCode, :Source} = require "code_obj"
STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2" STDIN, STDOUT, STDERR = "/dev/fd/0", "/dev/fd/1", "/dev/fd/2"
string.as_lua_id = (str)-> string.as_lua_id = (str)->
@ -138,103 +129,53 @@ do
AST = require "nomsu_tree" AST = require "nomsu_tree"
NOMSU_DEFS = with {} _list_mt =
-- Newline supports either windows-style CR+LF or unix-style LF __eq:utils.equivalent
.nl = P("\r")^-1 * P("\n") -- Could consider adding a __newindex to enforce list-ness, but would hurt performance
.ws = S(" \t") __tostring: =>
.tonumber = tonumber "["..concat([repr(b) for b in *@], ", ").."]"
string_escapes = n:"\n", t:"\t", b:"\b", a:"\a", v:"\v", f:"\f", r:"\r" list = (t)-> setmetatable(t, _list_mt)
digit, hex = R('09'), R('09','af','AF')
.escaped_char = (P("\\")*S("xX")*C(hex*hex)) / => string.char(tonumber(@, 16))
.escaped_char += (P("\\")*C(digit*(digit^-2))) / => string.char(tonumber @)
.escaped_char += (P("\\")*C(S("ntbavfr"))) / string_escapes
.operator_char = S("'~`!@$^&*-+=|<>?/")
.utf8_char = (
R("\194\223")*R("\128\191") +
R("\224\239")*R("\128\191")*R("\128\191") +
R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
.ident_char = R("az","AZ","09") + P("_") + .utf8_char
-- If the line begins with #indent+4 spaces, the pattern matches *those* spaces _dict_mt =
-- and adds them to the current indent (not any more). __eq:utils.equivalent
.indent = Cmt Carg(1), (start, userdata)=> __tostring: =>
indented = userdata.indent..' ' "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}"
if sub(@, start, start+#indented-1) == indented dict = (t)-> setmetatable(t, _dict_mt)
userdata.indent = indented
return start + #indented
-- If the number of leading space characters is <= the number of spaces in the current
-- indent minus 4, this pattern matches and decrements the current indent exactly once.
.dedent = Cmt Carg(1), (start, userdata)=>
dedented = sub(userdata.indent, 1, -5)
if #match(@, "^[ ]*", start) <= #dedented
userdata.indent = dedented
return start
-- If the number of leading space characters is >= the number of spaces in the current
-- indent, this pattern matches and does not modify the indent.
.nodent = Cmt Carg(1), (start, userdata)=>
if sub(@, start, start+#userdata.indent-1) == userdata.indent
return start + #userdata.indent
.userdata = Carg(1) MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
NomsuCompiler = setmetatable({}, {__index: (k)=> if _self = rawget(@, "self") then _self[k] else nil})
with NomsuCompiler
._ENV = NomsuCompiler
.nomsu = NomsuCompiler
parse = require("parser")
.parse = (...)=> parse(...)
.error = (src,end_pos,start_pos,err_msg,userdata)-> -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
seen_errors = userdata.errors -- from lua line number to nomsu line number
if seen_errors[start_pos] .source_map = {}
return true -- Discretionary/convenience stuff
if utils.size(seen_errors) >= 10 to_add = {
seen_errors[start_pos+1] = colored.bright colored.yellow colored.onred "Too many errors, canceling parsing..." repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re,
return #src+1 -- Lua stuff:
err_pos = start_pos :next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
line_no = pos_to_line(src, err_pos) :error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
src = FILE_CACHE[userdata.source.filename] :print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen,
line_starts = LINE_STARTS[src] :table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load,
prev_line = line_no == 1 and "" or src\sub(line_starts[line_no-1] or 1, line_starts[line_no]-2) :pairs, :ipairs,
err_line = src\sub(line_starts[line_no], (line_starts[line_no+1] or 0)-2) -- Nomsu types:
next_line = src\sub(line_starts[line_no+1] or -1, (line_starts[line_no+2] or 0)-2) :list, :dict,
i = err_pos-line_starts[line_no] }
pointer = ("-")\rep(i) .. "^" for k,v in pairs(to_add) do NomsuCompiler[k] = v
err_msg = colored.bright colored.yellow colored.onred (err_msg or "Parse error").." at #{userdata.source.filename}:#{line_no}:" for k,v in pairs(AST) do NomsuCompiler[k] = v
if #prev_line > 0 then err_msg ..= "\n"..colored.dim(prev_line) .LuaCode = LuaCode
err_line = colored.white(err_line\sub(1, i))..colored.bright(colored.red(err_line\sub(i+1,i+1)))..colored.dim(err_line\sub(i+2,-1)) .NomsuCode = NomsuCode
err_msg ..= "\n#{err_line}\n#{colored.red pointer}" .Source = Source
if #next_line > 0 then err_msg ..= "\n"..colored.dim(next_line) .ARG_ORDERS = setmetatable({}, {__mode:"k"})
--error(err_msg) .ALIASES = setmetatable({}, {__mode:"k"})
seen_errors[start_pos] = err_msg .LOADED = {}
return true .AST = AST
setmetatable(NOMSU_DEFS, {__index:(key)=> .compile_error = (tok, err_format_string, ...)=>
make_node = (start, value, stop, userdata)->
local source
with userdata.source
source = Source(.filename, .start + start-1, .start + stop-1)
value.source = source
setmetatable(value, AST[key])
if value.__init then value\__init!
for i=1,#value do assert(value[i])
return value
self[key] = make_node
return make_node
})
NOMSU_PATTERN = do
-- Just for cleanliness, I put the language spec in its own file using a slightly modified
-- version of the lpeg.re syntax.
peg_tidier = re.compile [[
file <- {~ %nl* (def/comment) (%nl+ (def/comment))* %nl* ~}
def <- anon_def / captured_def
anon_def <- ({ident} (" "*) ":"
{((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- %2"
captured_def <- ({ident} (" "*) "(" {ident} ")" (" "*) ":"
{((%nl " "+ [^%nl]*)+) / ([^%nl]*)}) -> "%1 <- (({} %3 {} %%userdata) -> %2)"
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
]]
nomsu_peg = peg_tidier\match(FILE_CACHE["nomsu.peg"])
re.compile(nomsu_peg, NOMSU_DEFS)
class NomsuCompiler
compile_error = (tok, err_format_string, ...)->
file = FILE_CACHE[tok.source.filename] file = FILE_CACHE[tok.source.filename]
line_no = pos_to_line(file, tok.source.start) line_no = pos_to_line(file, tok.source.start)
line_start = LINE_STARTS[file][line_no] line_start = LINE_STARTS[file][line_no]
@ -245,105 +186,132 @@ class NomsuCompiler
src = ' '..src\gsub('\n', '\n ') src = ' '..src\gsub('\n', '\n ')
err_msg = err_format_string\format(src, ...) err_msg = err_format_string\format(src, ...)
error("#{tok.source.filename}:#{line_no}: "..err_msg, 0) error("#{tok.source.filename}:#{line_no}: "..err_msg, 0)
new: =>
-- Weak-key mapping from objects to randomly generated unique IDs
NaN_surrogate = {}
nil_surrogate = {}
@ids = setmetatable({}, {
__mode: "k"
__index: (key)=>
if key == nil then return @[nil_surrogate]
elseif key != key then return @[NaN_surrogate]
id = new_uuid!
@[key] = id
return id
})
-- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
-- from lua line number to nomsu line number
@source_map = {}
_list_mt = -- This is a bit of a hack, but this code handles arbitrarily complex
__eq:utils.equivalent -- math expressions like 2*x + 3^2 without having to define a single
-- Could consider adding a __newindex to enforce list-ness, but would hurt performance -- action for every possibility.
__tostring: => math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
"["..concat([repr(b) for b in *@], ", ").."]" add_lua_bits = (lua, code)=>
list = (t)-> setmetatable(t, _list_mt) for bit in *code
_dict_mt = if type(bit) == "string"
__eq:utils.equivalent lua\append bit
__tostring: => else
"{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}" bit_lua = @compile(bit)
dict = (t)-> setmetatable(t, _dict_mt) unless bit_lua.is_value
@environment = { @compile_error bit,
-- Discretionary/convenience stuff "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
nomsu:self, repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re, lua\append bit_lua
:compile_error return lua
-- Lua stuff:
:next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall, add_lua_string_bits = (lua, code)=>
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module, line_len = 0
:print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :bit32, :rawlen, if code.type != "Text"
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :load, lua\append ", ", @compile(code)
:pairs, :ipairs, return
-- Nomsu types: for bit in *code
:list, :dict, bit_lua = if type(bit) == "string"
repr(bit)
else
bit_lua = @compile(bit)
unless bit_lua.is_value
@compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
bit_lua
line_len += #tostring(bit_lua)
if line_len > MAX_LINE
lua\append ",\n "
line_len = 4
else
lua\append ", "
lua\append bit_lua
.COMPILE_ACTIONS = setmetatable {
compile_math_expr: (tree, ...)=>
lua = LuaCode.Value(tree.source)
for i,tok in ipairs tree
if type(tok) == 'string'
lua\append tok
else
tok_lua = @compile(tok)
unless tok_lua.is_value
@compile_error tok,
"Non-expression value inside math expression:\n%s"
if tok.type == "Action"
tok_lua\parenthesize!
lua\append tok_lua
if i < #tree
lua\append " "
return lua
["Lua %"]: (tree, _code)=>
lua = LuaCode.Value(_code.source, "LuaCode(", repr(tostring _code.source))
add_lua_string_bits(@, lua, _code)
lua\append ")"
return lua
["Lua value %"]: (tree, _code)=>
lua = LuaCode.Value(_code.source, "LuaCode.Value(", repr(tostring _code.source))
add_lua_string_bits(@, lua, _code)
lua\append ")"
return lua
["lua > %"]: (tree, _code)=>
if _code.type != "Text"
return LuaCode tree.source, "nomsu:run_lua(", @compile(_code), ");"
return add_lua_bits(@, LuaCode(tree.source), _code)
["= lua %"]: (tree, _code)=>
if _code.type != "Text"
return LuaCode.Value tree.source, "nomsu:run_lua(", @compile(_code), ":as_statements('return '))"
return add_lua_bits(@, LuaCode.Value(tree.source), _code)
["use %"]: (tree, _path)=>
unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string'
return LuaCode(tree.source, "nomsu:run_file(#{@compile(_path)});")
path = _path[1]
@run_file(path)
return LuaCode(tree.source, "nomsu:run_file(#{repr path});")
}, {
__index: (stub)=>
if math_expression\match(stub)
return @compile_math_expr
} }
for k,v in pairs(AST) do @environment[k] = v
@environment.Lua = Lua
@environment.Nomsu = Nomsu
@environment.Source = Source
@environment.ARG_ORDERS = setmetatable({}, {__mode:"k"})
@environment.ALIASES = setmetatable({}, {__mode:"k"})
@environment.compile_time = (fn)->
@environment.COMPILE_TIME[fn] = true
return fn
@environment.COMPILE_TIME = {}
@environment.LOADED = {}
@environment.AST = AST
@environment._ENV = @environment
setmetatable @environment,
__index: (k)=>
if _self = rawget(@, "self")
return _self[k]
@initialize_core!
parse: (nomsu_code)=> .fork = =>
assert(type(nomsu_code) != 'string') setmetatable({COMPILE_ACTIONS:setmetatable({}, {__index:@COMPILE_ACTIONS})}, {__index:@})
userdata = {
source_code:nomsu_code, indent: "", errors: {},
source: nomsu_code.source,
}
tree = NOMSU_PATTERN\match(tostring(nomsu_code), nil, userdata)
unless tree
error "In file #{colored.blue filename} failed to parse:\n#{colored.onyellow colored.black nomsu_code}"
if next(userdata.errors) .run = (to_run, source=nil)=>
keys = utils.keys(userdata.errors) tree = if AST.is_syntax_tree(to_run) then tree else @parse(to_run, source or to_run.source)
table.sort(keys) if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string
errors = [userdata.errors[k] for k in *keys]
io.stderr\write(concat(errors, "\n\n").."\n")
os.exit!
return tree
run: (nomsu_code)=>
tree = assert(@parse(nomsu_code))
if type(tree) == 'number' -- Happens if pattern matches, but there are no captures, e.g. an empty string
return nil return nil
lua = @tree_to_lua(tree)\as_statements! if tree.type == "FileChunks"
ret = nil
all_lua = {}
for chunk in *tree
lua = @compile(chunk)\as_statements!
lua\declare_locals! lua\declare_locals!
lua\prepend "-- File: #{nomsu_code.source or ""}\n" lua\prepend "-- File: #{chunk.source or ""}\n"
if @compile_fn insert all_lua, tostring(lua)
self.compile_fn(lua, nomsu_code.source.filename) ret = @run_lua(lua)
if @on_compile
self.on_compile(concat(all_lua, "\n"), (source or to_run.source).filename)
return ret
else
lua = @compile(tree, compile_actions)\as_statements!
lua\declare_locals!
lua\prepend "-- File: #{source or to_run.source or ""}\n"
if @on_compile
self.on_compile(lua, (source or to_run.source).filename)
return @run_lua(lua) return @run_lua(lua)
_running_files = {} -- For detecting circular imports _running_files = {} -- For detecting circular imports
run_file: (filename)=> .run_file = (filename)=>
loaded = @environment.LOADED if @LOADED[filename]
if loaded[filename] return @LOADED[filename]
return loaded[filename]
ret = nil ret = nil
for filename in all_files(filename) for filename in all_files(filename)
if loaded[filename] if @LOADED[filename]
ret = loaded[filename] ret = @LOADED[filename]
continue continue
for i,running in ipairs _running_files for i,running in ipairs _running_files
@ -355,31 +323,31 @@ class NomsuCompiler
insert _running_files, filename insert _running_files, filename
if match(filename, "%.lua$") if match(filename, "%.lua$")
file = assert(FILE_CACHE[filename], "Could not find file: #{filename}") file = assert(FILE_CACHE[filename], "Could not find file: #{filename}")
ret = @run_lua(Lua(Source(filename, 1, #file), file)) ret = @run_lua file, Source(filename, 1, #file)
elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$")
if not @skip_precompiled -- Look for precompiled version if not @skip_precompiled -- Look for precompiled version
lua_filename = gsub(filename, "%.nom$", ".lua") lua_filename = gsub(filename, "%.nom$", ".lua")
file = FILE_CACHE[lua_filename] file = FILE_CACHE[lua_filename]
if file if file
ret = @run_lua(Lua(Source(filename, 1, #file), file)) ret = @run_lua file, Source(filename, 1, #file)
remove _running_files remove _running_files
continue continue
file = file or FILE_CACHE[filename] file = file or FILE_CACHE[filename]
if not file if not file
error("File does not exist: #{filename}", 0) error("File does not exist: #{filename}", 0)
ret = @run(Nomsu(Source(filename,1,#file), file)) ret = @run file, Source(filename,1,#file)
else else
error("Invalid filetype for #{filename}", 0) error("Invalid filetype for #{filename}", 0)
loaded[filename] = ret or true @LOADED[filename] = ret or true
remove _running_files remove _running_files
loaded[filename] = ret or true @LOADED[filename] = ret or true
return ret return ret
run_lua: (lua)=> .run_lua = (lua, source=nil)=>
assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)") assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)")
lua_string = tostring(lua) lua_string = tostring(lua)
run_lua_fn, err = load(lua_string, tostring(lua.source), "t", @environment) run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
if not run_lua_fn if not run_lua_fn
n = 1 n = 1
fn = -> fn = ->
@ -387,14 +355,14 @@ class NomsuCompiler
("\n%-3d|")\format(n) ("\n%-3d|")\format(n)
line_numbered_lua = "1 |"..lua_string\gsub("\n", fn) line_numbered_lua = "1 |"..lua_string\gsub("\n", fn)
error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0) error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0)
source_key = tostring(lua.source) source_key = tostring(source or lua.source)
unless @source_map[source_key] unless @source_map[source_key]
map = {} map = {}
offset = 1 offset = 1
source = lua.source source or= lua.source
nomsu_str = tostring(FILE_CACHE[source.filename]\sub(source.start, source.stop)) nomsu_str = tostring(FILE_CACHE[source.filename]\sub(source.start, source.stop))
lua_line = 1 lua_line = 1
nomsu_line = pos_to_line(nomsu_str, lua.source.start) nomsu_line = pos_to_line(nomsu_str, source.start)
fn = (s)-> fn = (s)->
if type(s) == 'string' if type(s) == 'string'
for nl in s\gmatch("\n") for nl in s\gmatch("\n")
@ -413,26 +381,26 @@ class NomsuCompiler
return run_lua_fn! return run_lua_fn!
MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value .compile = (tree)=>
math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]] assert(LuaCode)
tree_to_lua: (tree)=>
switch tree.type switch tree.type
when "Action" when "Action"
stub = tree.stub stub = tree.stub
action = @environment['A'..string.as_lua_id(stub)] if compile_action = @COMPILE_ACTIONS[stub]
if action and @environment.COMPILE_TIME[action]
args = [arg for arg in *tree when type(arg) != "string"] args = [arg for arg in *tree when type(arg) != "string"]
-- Force all compile-time actions to take a tree location if arg_orders = @ARG_ORDERS[stub]
if arg_orders = @environment.ARG_ORDERS[stub]
args = [args[p] for p in *arg_orders] args = [args[p] for p in *arg_orders]
-- Force Lua to avoid tail call optimization for debugging purposes -- Force Lua to avoid tail call optimization for debugging purposes
-- TODO: use tail call -- TODO: use tail call?
ret = action(tree, unpack(args)) ret = compile_action(@, tree, unpack(args))
if not ret if not ret
compile_error tree, @compile_error tree,
"Compile-time action:\n%s\nfailed to produce any Lua" "Compile-time action:\n%s\nfailed to produce any Lua"
return ret return ret
lua = Lua.Value(tree.source)
action = @['A'..string.as_lua_id(stub)]
lua = LuaCode.Value(tree.source)
if not action and math_expression\match(stub) if not action and math_expression\match(stub)
-- This is a bit of a hack, but this code handles arbitrarily complex -- This is a bit of a hack, but this code handles arbitrarily complex
-- math expressions like 2*x + 3^2 without having to define a single -- math expressions like 2*x + 3^2 without having to define a single
@ -441,9 +409,9 @@ class NomsuCompiler
if type(tok) == 'string' if type(tok) == 'string'
lua\append tok lua\append tok
else else
tok_lua = @tree_to_lua(tok) tok_lua = @compile(tok)
unless tok_lua.is_value unless tok_lua.is_value
compile_error tok, @compile_error tok,
"Non-expression value inside math expression:\n%s" "Non-expression value inside math expression:\n%s"
if tok.type == "Action" if tok.type == "Action"
tok_lua\parenthesize! tok_lua\parenthesize!
@ -455,15 +423,15 @@ class NomsuCompiler
args = {} args = {}
for i, tok in ipairs tree for i, tok in ipairs tree
if type(tok) == "string" then continue if type(tok) == "string" then continue
arg_lua = @tree_to_lua(tok) arg_lua = @compile(tok)
unless arg_lua.is_value unless arg_lua.is_value
compile_error tok, @compile_error tok,
"Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s", "Cannot use:\n%s\nas an argument to %s, since it's not an expression, it produces: %s",
stub, repr arg_lua stub, repr arg_lua
insert args, arg_lua insert args, arg_lua
if action if action
if arg_orders = @environment.ARG_ORDERS[stub] if arg_orders = @ARG_ORDERS[stub]
args = [args[p] for p in *arg_orders] args = [args[p] for p in *arg_orders]
lua\append "A",string.as_lua_id(stub),"(" lua\append "A",string.as_lua_id(stub),"("
@ -479,19 +447,19 @@ class NomsuCompiler
return repr(t) return repr(t)
bits = [make_tree(bit) for bit in *t] bits = [make_tree(bit) for bit in *t]
return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")" return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
Lua.Value tree.source, make_tree(tree[1]) LuaCode.Value tree.source, make_tree(tree[1])
when "Block" when "Block"
lua = Lua(tree.source) lua = LuaCode(tree.source)
for i,line in ipairs tree for i,line in ipairs tree
line_lua = @tree_to_lua(line) line_lua = @compile(line)
if i > 1 if i > 1
lua\append "\n" lua\append "\n"
lua\append line_lua\as_statements! lua\append line_lua\as_statements!
return lua return lua
when "Text" when "Text"
lua = Lua.Value(tree.source) lua = LuaCode.Value(tree.source)
string_buffer = "" string_buffer = ""
for i, bit in ipairs tree for i, bit in ipairs tree
if type(bit) == "string" if type(bit) == "string"
@ -501,15 +469,15 @@ class NomsuCompiler
if #lua.bits > 0 then lua\append ".." if #lua.bits > 0 then lua\append ".."
lua\append repr(string_buffer) lua\append repr(string_buffer)
string_buffer = "" string_buffer = ""
bit_lua = @tree_to_lua(bit) bit_lua = @compile(bit)
unless bit_lua.is_value unless bit_lua.is_value
src = ' '..gsub(tostring(@tree_to_nomsu(bit)), '\n','\n ') src = ' '..gsub(tostring(@tree_to_nomsu(bit)), '\n','\n ')
line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}" line = "#{bit.source.filename}:#{pos_to_line(FILE_CACHE[bit.source.filename], bit.source.start)}"
compile_error bit, @compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression." "Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
if #lua.bits > 0 then lua\append ".." if #lua.bits > 0 then lua\append ".."
if bit.type != "Text" if bit.type != "Text"
bit_lua = Lua.Value(bit.source, "stringify(",bit_lua,")") bit_lua = LuaCode.Value(bit.source, "stringify(",bit_lua,")")
lua\append bit_lua lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0 if string_buffer ~= "" or #lua.bits == 0
@ -521,12 +489,12 @@ class NomsuCompiler
return lua return lua
when "List" when "List"
lua = Lua.Value tree.source, "list{" lua = LuaCode.Value tree.source, "list{"
line_length = 0 line_length = 0
for i, item in ipairs tree for i, item in ipairs tree
item_lua = @tree_to_lua(item) item_lua = @compile(item)
unless item_lua.is_value unless item_lua.is_value
compile_error item, @compile_error item,
"Cannot use:\n%s\nas a list item, since it's not an expression." "Cannot use:\n%s\nas a list item, since it's not an expression."
lua\append item_lua lua\append item_lua
item_string = tostring(item_lua) item_string = tostring(item_lua)
@ -546,10 +514,10 @@ class NomsuCompiler
return lua return lua
when "Dict" when "Dict"
lua = Lua.Value tree.source, "dict{" lua = LuaCode.Value tree.source, "dict{"
line_length = 0 line_length = 0
for i, entry in ipairs tree for i, entry in ipairs tree
entry_lua = @tree_to_lua(entry) entry_lua = @compile(entry)
lua\append entry_lua lua\append entry_lua
entry_lua_str = tostring(entry_lua) entry_lua_str = tostring(entry_lua)
-- TODO: maybe make this more accurate? It's only a heuristic, so eh... -- TODO: maybe make this more accurate? It's only a heuristic, so eh...
@ -570,29 +538,29 @@ class NomsuCompiler
when "DictEntry" when "DictEntry"
key, value = tree[1], tree[2] key, value = tree[1], tree[2]
key_lua = @tree_to_lua(key) key_lua = @compile(key)
unless key_lua.is_value unless key_lua.is_value
compile_error tree[1], @compile_error tree[1],
"Cannot use:\n%s\nas a dict key, since it's not an expression." "Cannot use:\n%s\nas a dict key, since it's not an expression."
value_lua = value and @tree_to_lua(value) or Lua.Value(key.source, "true") value_lua = value and @compile(value) or LuaCode.Value(key.source, "true")
unless value_lua.is_value unless value_lua.is_value
compile_error tree[2], @compile_error tree[2],
"Cannot use:\n%s\nas a dict value, since it's not an expression." "Cannot use:\n%s\nas a dict value, since it's not an expression."
key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=]) key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
return if key_str return if key_str
Lua tree.source, key_str,"=",value_lua LuaCode tree.source, key_str,"=",value_lua
elseif sub(tostring(key_lua),1,1) == "[" elseif sub(tostring(key_lua),1,1) == "["
-- NOTE: this *must* use a space after the [ to avoid freaking out -- NOTE: this *must* use a space after the [ to avoid freaking out
-- Lua's parser if the inner expression is a long string. Lua -- Lua's parser if the inner expression is a long string. Lua
-- parses x[[[y]]] as x("[y]"), not as x["y"] -- parses x[[[y]]] as x("[y]"), not as x["y"]
Lua tree.source, "[ ",key_lua,"]=",value_lua LuaCode tree.source, "[ ",key_lua,"]=",value_lua
else else
Lua tree.source, "[",key_lua,"]=",value_lua LuaCode tree.source, "[",key_lua,"]=",value_lua
when "IndexChain" when "IndexChain"
lua = @tree_to_lua(tree[1]) lua = @compile(tree[1])
unless lua.is_value unless lua.is_value
compile_error tree[1], @compile_error tree[1],
"Cannot index:\n%s\nsince it's not an expression." "Cannot index:\n%s\nsince it's not an expression."
first_char = sub(tostring(lua),1,1) first_char = sub(tostring(lua),1,1)
if first_char == "{" or first_char == '"' or first_char == "[" if first_char == "{" or first_char == '"' or first_char == "["
@ -600,9 +568,9 @@ class NomsuCompiler
for i=2,#tree for i=2,#tree
key = tree[i] key = tree[i]
key_lua = @tree_to_lua(key) key_lua = @compile(key)
unless key_lua.is_value unless key_lua.is_value
compile_error key, @compile_error key,
"Cannot use:\n%s\nas an index, since it's not an expression." "Cannot use:\n%s\nas an index, since it's not an expression."
key_lua_str = tostring(key_lua) key_lua_str = tostring(key_lua)
if lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") if lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
@ -617,19 +585,19 @@ class NomsuCompiler
return lua return lua
when "Number" when "Number"
Lua.Value(tree.source, tostring(tree[1])) LuaCode.Value(tree.source, tostring(tree[1]))
when "Var" when "Var"
Lua.Value(tree.source, string.as_lua_id(tree[1])) LuaCode.Value(tree.source, string.as_lua_id(tree[1]))
else else
error("Unknown type: #{tree.type}") error("Unknown type: #{tree.type}")
tree_to_nomsu: (tree, inline=false, can_use_colon=false)=> .tree_to_nomsu = (tree, inline=false, can_use_colon=false)=>
switch tree.type switch tree.type
when "Action" when "Action"
if inline if inline
nomsu = Nomsu(tree.source) nomsu = NomsuCode(tree.source)
for i,bit in ipairs tree for i,bit in ipairs tree
if type(bit) == "string" if type(bit) == "string"
if i > 1 if i > 1
@ -645,7 +613,7 @@ class NomsuCompiler
nomsu\append arg_nomsu nomsu\append arg_nomsu
return nomsu return nomsu
else else
nomsu = Nomsu(tree.source) nomsu = NomsuCode(tree.source)
next_space = "" next_space = ""
line_len, last_colon = 0, nil line_len, last_colon = 0, nil
for i,bit in ipairs tree for i,bit in ipairs tree
@ -679,9 +647,9 @@ class NomsuCompiler
-- These types carry their own indentation -- These types carry their own indentation
if bit.type != "List" and bit.type != "Dict" and bit.type != "Text" if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
if i == 1 if i == 1
arg_nomsu = Nomsu(bit.source, "(..)\n ", arg_nomsu) arg_nomsu = NomsuCode(bit.source, "(..)\n ", arg_nomsu)
else else
arg_nomsu = Nomsu(bit.source, "\n ", arg_nomsu) arg_nomsu = NomsuCode(bit.source, "\n ", arg_nomsu)
if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block") if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
next_space = "" next_space = ""
@ -697,12 +665,12 @@ class NomsuCompiler
nomsu = @tree_to_nomsu(tree[1], true) nomsu = @tree_to_nomsu(tree[1], true)
if nomsu == nil and not inline if nomsu == nil and not inline
nomsu = @tree_to_nomsu(tree[1]) nomsu = @tree_to_nomsu(tree[1])
return nomsu and Nomsu tree.source, "\\:\n ", nomsu return nomsu and NomsuCode tree.source, "\\:\n ", nomsu
return nomsu and Nomsu tree.source, "\\(", nomsu, ")" return nomsu and NomsuCode tree.source, "\\(", nomsu, ")"
when "Block" when "Block"
if inline if inline
nomsu = Nomsu(tree.source) nomsu = NomsuCode(tree.source)
for i,line in ipairs tree for i,line in ipairs tree
if i > 1 if i > 1
nomsu\append "; " nomsu\append "; "
@ -710,7 +678,7 @@ class NomsuCompiler
return nil unless line_nomsu return nil unless line_nomsu
nomsu\append line_nomsu nomsu\append line_nomsu
return nomsu return nomsu
nomsu = Nomsu(tree.source) nomsu = NomsuCode(tree.source)
for i, line in ipairs tree for i, line in ipairs tree
line = assert(@tree_to_nomsu(line, nil, true), "Could not convert line to nomsu") line = assert(@tree_to_nomsu(line, nil, true), "Could not convert line to nomsu")
nomsu\append line nomsu\append line
@ -722,7 +690,7 @@ class NomsuCompiler
when "Text" when "Text"
if inline if inline
nomsu = Nomsu(tree.source, '"') nomsu = NomsuCode(tree.source, '"')
for bit in *tree for bit in *tree
if type(bit) == 'string' if type(bit) == 'string'
-- TODO: unescape better? -- TODO: unescape better?
@ -740,7 +708,7 @@ class NomsuCompiler
inline_version = @tree_to_nomsu(tree, true) inline_version = @tree_to_nomsu(tree, true)
if inline_version and #inline_version <= MAX_LINE if inline_version and #inline_version <= MAX_LINE
return inline_version return inline_version
nomsu = Nomsu(tree.source, '".."\n ') nomsu = NomsuCode(tree.source, '".."\n ')
for i, bit in ipairs tree for i, bit in ipairs tree
if type(bit) == 'string' if type(bit) == 'string'
bit_lines = get_lines\match(bit) bit_lines = get_lines\match(bit)
@ -779,7 +747,7 @@ class NomsuCompiler
when "List" when "List"
if inline if inline
nomsu = Nomsu(tree.source, "[") nomsu = NomsuCode(tree.source, "[")
for i, item in ipairs tree for i, item in ipairs tree
item_nomsu = @tree_to_nomsu(item, true) item_nomsu = @tree_to_nomsu(item, true)
return nil unless item_nomsu return nil unless item_nomsu
@ -792,8 +760,8 @@ class NomsuCompiler
inline_version = @tree_to_nomsu(tree, true) inline_version = @tree_to_nomsu(tree, true)
if inline_version and #inline_version <= MAX_LINE if inline_version and #inline_version <= MAX_LINE
return inline_version return inline_version
nomsu = Nomsu(tree.source, "[..]") nomsu = NomsuCode(tree.source, "[..]")
line = Nomsu(tree.source, "\n ") line = NomsuCode(tree.source, "\n ")
for item in *tree for item in *tree
item_nomsu = @tree_to_nomsu(item, true) item_nomsu = @tree_to_nomsu(item, true)
if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE if item_nomsu and #line + #", " + #item_nomsu <= MAX_LINE
@ -806,7 +774,7 @@ class NomsuCompiler
return nil unless item_nomsu return nil unless item_nomsu
if #line.bits > 1 if #line.bits > 1
nomsu\append line nomsu\append line
line = Nomsu(line.source, "\n ") line = NomsuCode(line.source, "\n ")
line\append item_nomsu line\append item_nomsu
if #line.bits > 1 if #line.bits > 1
nomsu\append line nomsu\append line
@ -814,7 +782,7 @@ class NomsuCompiler
when "Dict" when "Dict"
if inline if inline
nomsu = Nomsu(tree.source, "{") nomsu = NomsuCode(tree.source, "{")
for i, entry in ipairs tree for i, entry in ipairs tree
entry_nomsu = @tree_to_nomsu(entry, true) entry_nomsu = @tree_to_nomsu(entry, true)
return nil unless entry_nomsu return nil unless entry_nomsu
@ -826,8 +794,8 @@ class NomsuCompiler
else else
inline_version = @tree_to_nomsu(tree, true) inline_version = @tree_to_nomsu(tree, true)
if inline_version then return inline_version if inline_version then return inline_version
nomsu = Nomsu(tree.source, "{..}") nomsu = NomsuCode(tree.source, "{..}")
line = Nomsu(tree.source, "\n ") line = NomsuCode(tree.source, "\n ")
for entry in *tree for entry in *tree
entry_nomsu = @tree_to_nomsu(entry) entry_nomsu = @tree_to_nomsu(entry)
return nil unless entry_nomsu return nil unless entry_nomsu
@ -838,7 +806,7 @@ class NomsuCompiler
else else
if #line.bits > 1 if #line.bits > 1
nomsu\append line nomsu\append line
line = Nomsu(line.source, "\n ") line = NomsuCode(line.source, "\n ")
line\append entry_nomsu line\append entry_nomsu
if #line.bits > 1 if #line.bits > 1
nomsu\append line nomsu\append line
@ -852,22 +820,23 @@ class NomsuCompiler
key_nomsu\parenthesize! key_nomsu\parenthesize!
value_nomsu = if value value_nomsu = if value
@tree_to_nomsu(value, true) @tree_to_nomsu(value, true)
else Nomsu(tree.source, "") else NomsuCode(tree.source, "")
if inline and not value_nomsu then return nil if inline and not value_nomsu then return nil
if not value_nomsu if not value_nomsu
return nil if inline return nil if inline
value_nomsu = @tree_to_nomsu(value) value_nomsu = @tree_to_nomsu(value)
return nil unless value_nomsu return nil unless value_nomsu
return Nomsu tree.source, key_nomsu, ":", value_nomsu return NomsuCode tree.source, key_nomsu, ":", value_nomsu
when "IndexChain" when "IndexChain"
nomsu = Nomsu(tree.source) nomsu = NomsuCode(tree.source)
for i, bit in ipairs tree for i, bit in ipairs tree
if i > 1 if i > 1
nomsu\append "." nomsu\append "."
local bit_nomsu local bit_nomsu
if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string'
if (NOMSU_DEFS.ident_char^1)\match(bit[1]) -- TODO: support arbitrary words here, including operators and unicode
if bit[1]\match("[_a-zA-Z][_a-zA-Z0-9]*")
bit_nomsu = bit[1] bit_nomsu = bit[1]
unless bit_nomsu then bit_nomsu = @tree_to_nomsu(bit, true) unless bit_nomsu then bit_nomsu = @tree_to_nomsu(bit, true)
return nil unless bit_nomsu return nil unless bit_nomsu
@ -881,86 +850,22 @@ class NomsuCompiler
return nomsu return nomsu
when "Number" when "Number"
return Nomsu(tree.source, tostring(tree[1])) return NomsuCode(tree.source, tostring(tree[1]))
when "Var" when "Var"
return Nomsu(tree.source, "%", tree[1]) return NomsuCode(tree.source, "%", tree[1])
else else
error("Unknown type: #{tree.type}") error("Unknown type: #{tree.type}")
initialize_core: =>
-- Sets up some core functionality
nomsu = self
with nomsu.environment
.A_immediately_1 = .compile_time (_block)=>
lua = nomsu\tree_to_lua(_block)\as_statements!
lua\declare_locals!
nomsu\run_lua(lua)
return Lua(_block.source, "if IMMEDIATE then\n ", lua, "\nend")
add_lua_string_bits = (lua, code)->
line_len = 0
if code.type != "Text"
lua\append ", ", nomsu\tree_to_lua(code)
return
for bit in *code
bit_lua = if type(bit) == "string"
repr(bit)
else
bit_lua = nomsu\tree_to_lua(bit)
unless bit_lua.is_value
compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
bit_lua
line_len += #tostring(bit_lua)
if line_len > MAX_LINE
lua\append ",\n "
line_len = 4
else
lua\append ", "
lua\append bit_lua
.A_Lua_1 = .compile_time (_code)=>
lua = Lua.Value(_code.source, "Lua(", repr(tostring _code.source))
add_lua_string_bits(lua, _code)
lua\append ")"
return lua
.A_Lua_value_1 = .compile_time (_code)=>
lua = Lua.Value(_code.source, "Lua.Value(", repr(tostring _code.source))
add_lua_string_bits(lua, _code)
lua\append ")"
return lua
add_lua_bits = (lua, code)->
for bit in *code
if type(bit) == "string"
lua\append bit
else
bit_lua = nomsu\tree_to_lua(bit)
unless bit_lua.is_value
compile_error bit,
"Cannot use:\n%s\nas a string interpolation value, since it's not an expression."
lua\append bit_lua
return lua
nomsu.environment["A"..string.as_lua_id("lua > 1")] = .compile_time (_code)=> -- Command line interface:
if _code.type != "Text"
return Lua @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ");"
return add_lua_bits(Lua(@source), _code)
nomsu.environment["A"..string.as_lua_id("= lua 1")] = .compile_time (_code)=>
if _code.type != "Text"
return Lua.Value @source, "nomsu:run_lua(", nomsu\tree_to_lua(_code), ":as_statements('return '))"
return add_lua_bits(Lua.Value(@source), _code)
.A_use_1 = .compile_time (_path)=>
unless _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string'
return Lua(_path.source, "nomsu:run_file(#{nomsu\tree_to_lua(_path)});")
path = _path[1]
nomsu\run_file(path)
return Lua(_path.source, "nomsu:run_file(#{repr path});")
-- Only run this code if this file was run directly with command line arguments, and not require()'d: -- Only run this code if this file was run directly with command line arguments, and not require()'d:
if arg and debug_getinfo(2).func != require if arg and debug_getinfo(2).func != require
@ -1000,8 +905,8 @@ OPTIONS
]=] ]=]
os.exit! os.exit!
nomsu = NomsuCompiler! nomsu = NomsuCompiler
nomsu.environment.arg = args.nomsu_args nomsu.arg = args.nomsu_args
ok, to_lua = pcall -> require('moonscript.base').to_lua ok, to_lua = pcall -> require('moonscript.base').to_lua
if not ok then to_lua = nil if not ok then to_lua = nil
@ -1022,7 +927,7 @@ OPTIONS
else debug_getinfo(thread,f,what) else debug_getinfo(thread,f,what)
if not info or not info.func then return info if not info or not info.func then return info
if info.short_src or info.source or info.linedefine or info.currentline if info.short_src or info.source or info.linedefine or info.currentline
if arg_orders = nomsu.environment.ARG_ORDERS[info.func] if arg_orders = nomsu.ARG_ORDERS[info.func]
info.name = next(arg_orders) info.name = next(arg_orders)
if map = nomsu.source_map[info.source] if map = nomsu.source_map[info.source]
if info.currentline if info.currentline
@ -1077,7 +982,7 @@ OPTIONS
file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop)) file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop))
err_line = get_line(file, calling_fn.currentline)\sub(1,-2) err_line = get_line(file, calling_fn.currentline)\sub(1,-2)
offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)"))) offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)")))
if arg_orders = nomsu.environment.ARG_ORDERS[calling_fn.func] if arg_orders = nomsu.ARG_ORDERS[calling_fn.func]
name = "action '#{next(arg_orders)}'" name = "action '#{next(arg_orders)}'"
else else
name = "main chunk" name = "main chunk"
@ -1145,9 +1050,9 @@ OPTIONS
nomsu.skip_precompiled = not args.optimized nomsu.skip_precompiled = not args.optimized
if print_file == nil if print_file == nil
nomsu.environment.print = -> nomsu.print = ->
elseif print_file != io.stdout elseif print_file != io.stdout
nomsu.environment.print = (...)-> nomsu.print = (...)->
N = select("#",...) N = select("#",...)
if N > 0 if N > 0
print_file\write(tostring(select(1,...))) print_file\write(tostring(select(1,...)))
@ -1163,7 +1068,7 @@ OPTIONS
input_files[#input_files+1] = f input_files[#input_files+1] = f
to_run[f] = true to_run[f] = true
nomsu.compile_fn = if args.compile or args.verbose nomsu.on_compile = if args.compile or args.verbose
(code, from_file)-> (code, from_file)->
if to_run[from_file] if to_run[from_file]
if args.verbose if args.verbose
@ -1171,7 +1076,7 @@ OPTIONS
if args.compile and from_file\match("%.nom$") if args.compile and from_file\match("%.nom$")
output_filename = from_file\gsub("%.nom$", ".lua") output_filename = from_file\gsub("%.nom$", ".lua")
output_file = io.open(output_filename, 'w') output_file = io.open(output_filename, 'w')
output_file\write("local IMMEDIATE = true;\n", tostring(code)) output_file\write(tostring(code))
output_file\flush! output_file\flush!
print ("Compiled %-25s -> %s")\format(from_file, output_filename) print ("Compiled %-25s -> %s")\format(from_file, output_filename)
output_file\close! output_file\close!
@ -1181,7 +1086,8 @@ OPTIONS
for filename in *input_files for filename in *input_files
if args.syntax if args.syntax
-- Check syntax: -- Check syntax:
ok,err = pcall nomsu.parse, nomsu, Nomsu(filename, io.open(filename)\read("*a")) file_contents = io.open(filename)\read('*a')
ok,err = pcall nomsu.parse, nomsu, file_contents, Source(filename, 1, #file_contents)
if not ok if not ok
insert parse_errs, err insert parse_errs, err
elseif print_file elseif print_file
@ -1192,7 +1098,7 @@ OPTIONS
file = FILE_CACHE[filename] file = FILE_CACHE[filename]
if not file if not file
error("File does not exist: #{filename}", 0) error("File does not exist: #{filename}", 0)
tree = nomsu\parse(Nomsu(Source(filename,1,#file), file)) tree = nomsu\parse(file, Source(filename,1,#file))
formatted = tostring(nomsu\tree_to_nomsu(tree)) formatted = tostring(nomsu\tree_to_nomsu(tree))
if print_file if print_file
print_file\write(formatted, "\n") print_file\write(formatted, "\n")
@ -1200,7 +1106,7 @@ OPTIONS
elseif filename == STDIN elseif filename == STDIN
file = io.input!\read("*a") file = io.input!\read("*a")
FILE_CACHE.stdin = file FILE_CACHE.stdin = file
nomsu\run(Nomsu(Source('stdin',1,#file), file)) nomsu\run(file, Source('stdin',1,#file))
else else
nomsu\run_file(filename) nomsu\run_file(filename)
@ -1230,10 +1136,9 @@ OPTIONS
buff = concat(buff) buff = concat(buff)
FILE_CACHE["REPL#"..repl_line] = buff FILE_CACHE["REPL#"..repl_line] = buff
code = Nomsu(Source("REPL#"..repl_line, 1, #buff), buff)
err_hand = (error_message)-> err_hand = (error_message)->
print_err_msg error_message print_err_msg error_message
ok, ret = xpcall(nomsu.run, err_hand, nomsu, code) ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source("REPL#"..repl_line, 1, #buff))
if ok and ret != nil if ok and ret != nil
print "= "..repr(ret) print "= "..repr(ret)
elseif not ok elseif not ok

View File

@ -1,13 +1,17 @@
file: file:
(ignored_line %nl)* (ignored_line %nl)*
(block / action / expression)? (file_chunks / block / action / expression)?
(%nl ignored_line)* (%nl ignored_line)*
(!. / (({} (.* -> "Parse error") %userdata) => error)) (!. / (({} (.* -> "Parse error") %userdata) => error))
file_chunks (FileChunks):
{| (block/action/expression) (nodent chunk_delimeter nodent (block/action/expression))+ |}
chunk_delimeter: "~~~" (("~")*)
inline_block (Block): inline_block (Block):
{| inline_statement (%ws* ";" %ws* inline_statement)+ |} {| inline_statement (%ws* ";" %ws* inline_statement)+ |}
block (Block): block (Block):
{| statement (nodent (statement / (({} ([^%nl]* -> "Unexpected character while parsing block line") %userdata) => error)))+ |} {| statement (nodent !("~") (statement / (({} ([^%nl]* -> "Unexpected character while parsing block line") %userdata) => error)))+ |}
statement: (action / expression) (eol / (({} ([^%nl]* -> "Unexpected character while parsing line") %userdata) => error)) statement: (action / expression) (eol / (({} ([^%nl]* -> "Unexpected character while parsing line") %userdata) => error))
inline_statement: inline_action / inline_expression inline_statement: inline_action / inline_expression
@ -49,6 +53,7 @@ index_chain (IndexChain):
-- Actions need either at least 1 word, or at least 2 tokens -- Actions need either at least 1 word, or at least 2 tokens
inline_action (Action): inline_action (Action):
!chunk_delimeter
{| {|
( (inline_expression (%ws* (inline_expression / word))+) ( (inline_expression (%ws* (inline_expression / word))+)
/ (word (%ws* (inline_expression / word))*)) / (word (%ws* (inline_expression / word))*))
@ -56,6 +61,7 @@ inline_action (Action):
/ (({} ('' -> "Missing expression after the ':'") %userdata) => error)))? / (({} ('' -> "Missing expression after the ':'") %userdata) => error)))?
|} |}
action (Action): action (Action):
!chunk_delimeter
{| {|
(expression ((nodent "..")? %ws* (expression / word))+) (expression ((nodent "..")? %ws* (expression / word))+)
/ (word ((nodent "..")? %ws* (expression / word))*) / (word ((nodent "..")? %ws* (expression / word))*)

View File

@ -7,6 +7,7 @@ do
end end
local Source local Source
Source = require("code_obj").Source Source = require("code_obj").Source
local unpack = unpack or table.unpack
local AST = { } local AST = { }
AST.is_syntax_tree = function(n) AST.is_syntax_tree = function(n)
return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
@ -21,7 +22,8 @@ local types = {
"Dict", "Dict",
"DictEntry", "DictEntry",
"IndexChain", "IndexChain",
"Action" "Action",
"FileChunks"
} }
for _index_0 = 1, #types do for _index_0 = 1, #types do
local name = types[_index_0] local name = types[_index_0]

View File

@ -3,13 +3,14 @@
{:repr} = require 'utils' {:repr} = require 'utils'
{:insert, :remove, :concat} = table {:insert, :remove, :concat} = table
{:Source} = require "code_obj" {:Source} = require "code_obj"
unpack or= table.unpack
AST = {} AST = {}
AST.is_syntax_tree = (n)-> AST.is_syntax_tree = (n)->
type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry", types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
"IndexChain", "Action"} "IndexChain", "Action", "FileChunks"}
for name in *types for name in *types
cls = {} cls = {}
with cls with cls

View File

@ -3,22 +3,22 @@
use "core" use "core"
immediately compile [five] to: Lua value "5"
compile [five] to: Lua value "5" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assume ((five) = 5) or barf "Compile to expression failed." assume ((five) = 5) or barf "Compile to expression failed."
immediately compile [loc x] to: Lua "local _x = 99;"
compile [loc x] to: Lua "local _x = 99;" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
lua> "do" lua> "do"
loc x loc x
assume (%x is 99) or barf "Compile to statements with locals failed." assume (%x is 99) or barf "Compile to statements with locals failed."
lua> "end" lua> "end"
assume (%x is (nil)) or barf "Failed to properly localize a variable." assume (%x is (nil)) or barf "Failed to properly localize a variable."
immediately compile [asdf] to
compile [asdf] to
%tmp <- "" %tmp <- ""
return: Lua %tmp return: Lua %tmp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
asdf asdf
assume (%tmp is (nil)) or barf "compile to is leaking variables" assume (%tmp is (nil)) or barf "compile to is leaking variables"
@ -28,20 +28,20 @@ action [foo %x]
assume ((foo 10) = 11) or barf "Action didn't work." assume ((foo 10) = 11) or barf "Action didn't work."
assume (%y is (nil)) or barf "Action leaked a local into globals." assume (%y is (nil)) or barf "Action leaked a local into globals."
immediately parse [baz %] as: foo %
parse [baz %] as: foo % ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assume ((baz 10) = 11) or barf "Parse as action failed." assume ((baz 10) = 11) or barf "Parse as action failed."
immediately parse [V] as: five
parse [V] as: five ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assume ((V) = 5) or barf "Parse as compile action failed." assume ((V) = 5) or barf "Parse as compile action failed."
immediately parse [swap %x and %y] as
parse [swap %x and %y] as
do do
%tmp <- %x %tmp <- %x
%x <- %y %x <- %y
%y <- %tmp %y <- %tmp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<- {%1:1, %2:2} <- {%1:1, %2:2}
swap %1 and %2 swap %1 and %2
assume ((%1 = 2) and (%2 = 1)) or barf "'parse % as %' failed on 'swap % and %'" assume ((%1 = 2) and (%2 = 1)) or barf "'parse % as %' failed on 'swap % and %'"

View File

@ -51,13 +51,14 @@ assume (((no) and (barfer)) = (no))
assume ((no) or (yes)) assume ((no) or (yes))
assume ((yes) or (barfer)) assume ((yes) or (barfer))
assume ((1 OR 2) = 3) # Disabled because luajit doesn't have bitops
assume ((3 XOR 2) = 1) assume ((1 OR 2) = 3)
assume ((3 AND 2) = 2) assume ((3 XOR 2) = 1)
assume ((NOT (NOT 6)) = 6) assume ((3 AND 2) = 2)
assume ((1<<1) = 2) assume ((NOT (NOT 6)) = 6)
assume ((2>>1) = 1) assume ((1<<1) = 2)
assume ((2>>>1) = 1) assume ((2>>1) = 1)
assume ((2>>>1) = 1)
#.. Ugh, Lua is stupid when it comes to bitwise arithmetic on negative numbers, so I'm #.. Ugh, Lua is stupid when it comes to bitwise arithmetic on negative numbers, so I'm
skipping the tests for those. skipping the tests for those.

View File

@ -12,8 +12,9 @@ assume ("\n" = (newline)) or barf "Text literals failed."
assume (("x" + "y") = "xy") assume (("x" + "y") = "xy")
assume ((lines in "one\ntwo") = ["one", "two"]) assume ((lines in "one\ntwo") = ["one", "two"])
immediately parse [アクション %spec %body] as: action %spec %body
parse [アクション %spec %body] as: action %spec %body
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%こんにちは <- "こんにちは" %こんにちは <- "こんにちは"
アクション [% と言う] アクション [% と言う]

View File

@ -1,4 +1,5 @@
-- A simple UUID function based on RFC 4122: http://www.ietf.org/rfc/rfc4122.txt -- A simple UUID function based on RFC 4122: http://www.ietf.org/rfc/rfc4122.txt
local unpack = unpack or table.unpack
local function uuid() local function uuid()
local r = math.random local r = math.random
-- Set all the other bits to randomly (or pseudo-randomly) chosen values. -- Set all the other bits to randomly (or pseudo-randomly) chosen values.