Cleanups.
This commit is contained in:
parent
c1cec2ac84
commit
d73cbf0aa5
@ -50,10 +50,10 @@ compile [append %item to %list, add %item to %list, to %list add %item, to %list
|
||||
Lua "table.insert(\(%list as lua expr), \(%item as lua expr))"
|
||||
|
||||
compile [pop from %list, remove last from %list] to
|
||||
Lua "table.remove(\(%list as lua expr))"
|
||||
Lua value "table.remove(\(%list as lua expr))"
|
||||
|
||||
compile [remove index %index from %list] to
|
||||
Lua "table.remove(\(%list as lua expr), \(%index as lua expr))"
|
||||
Lua value "table.remove(\(%list as lua expr), \(%index as lua expr))"
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -135,6 +135,11 @@ compile [stop %var] to
|
||||
compile [do next %var] to
|
||||
Lua "goto continue_\(%var as lua identifier)"
|
||||
|
||||
compile [=== stop %var ===, --- stop %var ---, *** stop %var ***] to
|
||||
Lua "::stop_\(%var as lua identifier)::"
|
||||
compile [=== next %var ===, --- next %var ---, *** next %var ***] to
|
||||
Lua "::continue_\(%var as lua identifier)::"
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# Numeric range for loops
|
||||
@ -153,7 +158,7 @@ compile [..]
|
||||
(%.type = "Action") and
|
||||
(%.stub is "do next %") and
|
||||
%.(3).1 = %var.1
|
||||
..: to %lua write "\n ::continue_\(%var as lua identifier)::"
|
||||
..: to %lua write "\n \(compile as: === next %var ===)"
|
||||
to %lua write "\nend --numeric for-loop"
|
||||
|
||||
if
|
||||
@ -166,7 +171,7 @@ compile [..]
|
||||
Lua ".."
|
||||
do -- scope for stopping for-loop
|
||||
\%lua
|
||||
::stop_\(%var as lua identifier)::
|
||||
\(compile as: === stop %var ===)
|
||||
end -- end of scope for stopping for-loop
|
||||
|
||||
return %lua
|
||||
@ -188,7 +193,7 @@ compile [for %var in %iterable %body] to
|
||||
(%.type = "Action") and
|
||||
(%.stub is "do next %") and
|
||||
%.3.(1) = %var.(1)
|
||||
..: to %lua write (Lua "\n::continue_\(%var as lua identifier)::")
|
||||
..: to %lua write (Lua "\n\(compile as: === next %var ===)")
|
||||
to %lua write "\nend --foreach-loop"
|
||||
if
|
||||
%body has subtree % where
|
||||
@ -200,7 +205,7 @@ compile [for %var in %iterable %body] to
|
||||
Lua ".."
|
||||
do -- scope for stopping for-loop
|
||||
\%lua
|
||||
::stop_\(%var as lua identifier)::
|
||||
\(compile as: === stop %var ===)
|
||||
end -- end of scope for stopping for-loop
|
||||
return %lua
|
||||
|
||||
@ -221,14 +226,14 @@ compile [..]
|
||||
(%.type = "Action") and
|
||||
(%.stub is "do next %") and
|
||||
%.3.(1) = %key.(1)
|
||||
..: to %lua write (Lua "\n::continue_\(%key as lua identifier)::")
|
||||
..: to %lua write (Lua "\n\(compile as: === next %key ===)")
|
||||
|
||||
if
|
||||
%body has subtree % where
|
||||
(%.type = "Action") and
|
||||
(%.stub is "do next %") and
|
||||
%.3.(1) = %value.(1)
|
||||
..: to %lua write (Lua "\n::continue_\(%value as lua identifier)::")
|
||||
..: to %lua write (Lua "\n\(compile as: === next %value ===)")
|
||||
to %lua write "\nend --foreach-loop"
|
||||
|
||||
%stop_labels <- (Lua "")
|
||||
@ -237,14 +242,14 @@ compile [..]
|
||||
(%.type = "Action") and
|
||||
(%.stub is "stop %") and
|
||||
%.2.(1) = %key.(1)
|
||||
..: to %stop_labels write "\n::stop_\(%key as lua identifier)::"
|
||||
..: to %stop_labels write "\n\(compile as: === stop %key ===)"
|
||||
|
||||
if
|
||||
%body has subtree % where
|
||||
(%.type = "Action") and
|
||||
(%.stub is "stop %") and
|
||||
%.2.(1) = %value.(1)
|
||||
..: to %stop_labels write "\n::stop_\(%value as lua identifier)::"
|
||||
..: to %stop_labels write "\n\(compile as: === stop %value ===)"
|
||||
|
||||
if: (length of "\%stop_labels") > 0
|
||||
%lua <-
|
||||
|
@ -6,9 +6,9 @@ use "core/metaprogramming.nom"
|
||||
compile [say %message] to
|
||||
lua> ".."
|
||||
if \%message.type == "Text" then
|
||||
return LuaCode(tree.source, "io.write(", \(%message as lua expr), ", '\\\\n');");
|
||||
return LuaCode(tree.source, "print(", \(%message as lua expr), ");");
|
||||
else
|
||||
return LuaCode(tree.source, "io.write(tostring(", \(%message as lua expr), "), '\\\\n');");
|
||||
return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));");
|
||||
end
|
||||
|
||||
compile [ask %prompt] to
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
lua> ".."
|
||||
nomsu.COMPILE_ACTIONS["% -> %"] = function(nomsu, tree, \%args, \%body)
|
||||
local lua = LuaCode(tree.source, "function(")
|
||||
local lua = LuaCode.Value(tree.source, "function(")
|
||||
if AST.is_syntax_tree(\%args, "Action") then \%args = \%args:get_args() end
|
||||
local lua_args = table.map(\%args, function(a) return AST.is_syntax_tree(a) and tostring(nomsu:compile(a)) or a end)
|
||||
lua:concat_append(lua_args, ", ")
|
||||
@ -163,7 +163,7 @@ compile [declare locals %locals in %code] to
|
||||
compile [remove free vars %vars from %code] to
|
||||
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);"
|
||||
compile [%lua <-write %code, to %lua write %code] to: Lua "\(%lua as lua expr):append(\(%code as lua expr));"
|
||||
|
||||
compile [quote %s] to
|
||||
Lua value ".."
|
||||
|
@ -130,6 +130,16 @@ compile [with %assignments %body] to
|
||||
\%lua
|
||||
end -- 'with' block
|
||||
|
||||
compile [local %var_or_vars] to
|
||||
%lua <- (Lua "")
|
||||
lua> ".."
|
||||
if \%var_or_vars.type == "List" then
|
||||
\%lua:add_free_vars(table.map(\%var_or_vars, function(v) return tostring(nomsu:compile(v)) end))
|
||||
else
|
||||
\%lua:add_free_vars({tostring(nomsu:compile(\%var_or_vars))})
|
||||
end
|
||||
return %lua
|
||||
|
||||
# Math Operators
|
||||
compile [%x wrapped around %y, %x mod %y] to: Lua value "(\(%x as lua expr) % \(%y as lua expr))"
|
||||
|
||||
|
@ -160,7 +160,7 @@ print_error = function(error_message, stack_offset)
|
||||
end
|
||||
end
|
||||
end
|
||||
if file and calling_fn.short_src:match("%.moon$") and type(MOON_SOURCE_MAP[file]) == 'table' then
|
||||
if file and (calling_fn.short_src:match("%.moon$") or file:match("^#![^\n]*moon\n")) and type(MOON_SOURCE_MAP[file]) == 'table' then
|
||||
local char = MOON_SOURCE_MAP[file][calling_fn.currentline]
|
||||
line_num = 1
|
||||
for _ in file:sub(1, char):gmatch("\n") do
|
||||
|
@ -98,7 +98,8 @@ print_error = (error_message, stack_offset=3)->
|
||||
name = "upvalue '#{varname}'"
|
||||
if not varname\match("%(")
|
||||
break
|
||||
if file and calling_fn.short_src\match("%.moon$") and type(MOON_SOURCE_MAP[file]) == 'table'
|
||||
|
||||
if file and (calling_fn.short_src\match("%.moon$") or file\match("^#![^\n]*moon\n")) and type(MOON_SOURCE_MAP[file]) == 'table'
|
||||
char = MOON_SOURCE_MAP[file][calling_fn.currentline]
|
||||
line_num = 1
|
||||
for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
|
||||
|
@ -273,24 +273,26 @@ action [square root of %n]
|
||||
=lua "math.sqrt(\%n)"
|
||||
say "The square root of 2 is \(square root of 2)"
|
||||
|
||||
# The "immediately %" macro forces the indented code below it to run before the rest of
|
||||
the file finishes compiling, so it's often useful for writing your own macros.
|
||||
immediately
|
||||
# Macros can be defined to transform one bit of nomsu code into another using "parse % as %":
|
||||
parse [if %condition is untrue %body] as
|
||||
if (not %condition) %body
|
||||
# Macros can be defined to transform one bit of nomsu code into another using "parse % as %":
|
||||
parse [if %condition is untrue %body] as
|
||||
if (not %condition) %body
|
||||
|
||||
# Or to transform nomsu code into custom lua code using "compile % to %"
|
||||
compile [if %condition on opposite day %body] to
|
||||
Lua ".."
|
||||
if not \(%condition as lua expr) then
|
||||
\(%body as lua statements)
|
||||
end
|
||||
# Or to transform nomsu code into custom lua code using "compile % to %"
|
||||
compile [if %condition on opposite day %body] to
|
||||
Lua ".."
|
||||
if not \(%condition as lua expr) then
|
||||
\(%body as lua statements)
|
||||
end
|
||||
|
||||
# Constants can be defined as macros
|
||||
parse [TWENTY] as: 20
|
||||
# When they're invoked, they'll need parentheses just like a function call
|
||||
parse [TWENTY ONE] as: 21
|
||||
# Constants can be defined as macros
|
||||
parse [TWENTY] as: 20
|
||||
# When they're invoked, they'll need parentheses just like a function call
|
||||
parse [TWENTY ONE] as: 21
|
||||
|
||||
# If you need to use compile-time actions in the same file that they're defined in, you
|
||||
can add a line of tildes (3 or more), and the file will be split into chunks, and each
|
||||
chunk will run before the next one compiles. (note that each chunk has its own scope)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
if (1 > (TWENTY)) is untrue
|
||||
say "Nomsu parsing macros work!"
|
||||
@ -305,55 +307,131 @@ if (1 > (TWENTY)) on opposite day
|
||||
# Well... it's always *possible* to fall back to Lua behavior for something like this:
|
||||
action [best of %items according to %key-fn]
|
||||
<- {%best:nil, %best-key:nil}
|
||||
for % in %items
|
||||
%key <- (=lua "\%key-fn(\%)")
|
||||
for %item in %items
|
||||
%key <- (=lua "\%key-fn(\%item)")
|
||||
if: (%best is (nil)) or (%key > %best-key)
|
||||
<- {%best:%, %best-key:%key}
|
||||
<- {%best:%item, %best-key:%key}
|
||||
return %best
|
||||
|
||||
immediately
|
||||
compile [function %var %body] to
|
||||
Lua value ".."
|
||||
(function(\(%var as lua expr))
|
||||
return \(%body as lua expr)
|
||||
end)
|
||||
|
||||
say: best of [2,-3,4,-8] according to (function %: % * %)
|
||||
say: best of [2,-3,4,-8] according to ([%x] -> (%x * %x))
|
||||
|
||||
# But nomsu was mostly designed so that you don't *need* to. Instead of creating a
|
||||
one-off function to pass to another function and get called a bunch of times, you
|
||||
could use a macro to generate a single block of code that inlines the expression you
|
||||
want to use:
|
||||
immediately
|
||||
parse [best of %items according to %key-var in %key-expr] as
|
||||
result of
|
||||
<- {%best:nil, %best-key:nil}
|
||||
for %key-var in %items
|
||||
%key <- %key-expr
|
||||
if: (%best is (nil)) or (%key > %best-key)
|
||||
<- {%best:%, %best-key:%key}
|
||||
return %best
|
||||
# This results in generated code that is more efficient (no function calls in the
|
||||
inner loop)
|
||||
say: best of [2,-3,4,-8] according to % in (% * %)
|
||||
parse [best of %items where %item-var has score %key-expr] as
|
||||
result of
|
||||
<- {%best:nil, %best-key:nil}
|
||||
for %item-var in %items
|
||||
%key <- %key-expr
|
||||
if: (%best is (nil)) or (%key > %best-key)
|
||||
<- {%best:%item-var, %best-key:%key}
|
||||
return %best
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
say: best of [2,-3,4,-8] where %x has score (%x * %x)
|
||||
|
||||
# In a functional programming language, you might do something like:
|
||||
doubled = map(list, function(x) return 2 * x end)
|
||||
to get a new list with every entry multiplied by 2, but it's *much* more readable to
|
||||
do something like:
|
||||
%nums <- [1,2,3,4,5]
|
||||
%double-nums <- ((2 * %num) for %num in %nums)
|
||||
# The first example generates code that looks like:
|
||||
|
||||
# Nomsu comes with built-in list comprehensions, but the flexible macro system makes it
|
||||
incredibly easy to make similar constructs.
|
||||
immediately
|
||||
parse [%expr for %key in %list BACKWARDS!] as
|
||||
result of
|
||||
%result <- []
|
||||
%N <- (length of %list)
|
||||
for %i = %key in %list
|
||||
%result.(%N - %i + 1) <- %expr
|
||||
return %result
|
||||
A_best_of_1_according_to_2 = function(items, key_fn)
|
||||
local best, best_key = nil, nil
|
||||
for _, item in ipairs(items) do
|
||||
local key = key_fn(item)
|
||||
if best == nil or key > best_key then
|
||||
best, best_key = item, key
|
||||
end
|
||||
end
|
||||
return best
|
||||
end
|
||||
print(A_best_of_1_according_to_2({2,-3,4,-8}, function(x)
|
||||
return x * x
|
||||
end))
|
||||
|
||||
%double-nums <- ((2 * %num) for %num in %nums BACKWARDS!)
|
||||
say %double-nums
|
||||
But the second example produces something more like:
|
||||
|
||||
print((function()
|
||||
local best, best_key = nil, nil
|
||||
for _, x in ipairs({1,-2,3,-4}) do
|
||||
local key = x * x
|
||||
if best == nil or key > best_key then
|
||||
best, best_key = x, key
|
||||
end
|
||||
end
|
||||
return best
|
||||
end)())
|
||||
|
||||
Notice that the second example has inlined the key function, so that in the inner loop,
|
||||
the code only has to do a multiplication, instead of calling a function that does the
|
||||
multiplication. In addition to being more efficient, it's also more powerful and
|
||||
flexible for the language to allow you to define syntax that manipulates expressions.
|
||||
|
||||
For example, list comprehensions can (and are) easily defined in Nomsu as:
|
||||
|
||||
parse [%expr for %key-var in %list] as
|
||||
result of
|
||||
%result <- []
|
||||
for %key-var in %list
|
||||
add %expr to %result
|
||||
return %result
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
%double-nums <- ((2 * %num) for %num in [1,2,3,4,5])
|
||||
|
||||
|
||||
# Naturally, Nomsu's macros are hygienic, so even though "%result" is used as a local
|
||||
variable in the macro, there won't be any namespace collisions or confusion if you
|
||||
use a different variable named "%result" as any (or all) of the arguments to the macro.
|
||||
|
||||
%result <- [1,2,3,4]
|
||||
%result <- ((2*%result) for %result in %result)
|
||||
assume: %result = [2,4,6,8]
|
||||
|
||||
# You'll find that in a lot places where a functional programmer would think to use a
|
||||
lambda function, what they actually want to do is some metaprogramming. If you embrace
|
||||
the metaprogramming from the start, then you can do a lot more powerful stuff.
|
||||
|
||||
# For example, you can define a loop that operates on recursive datastructures, but still
|
||||
has all of the normal imperative control structures:
|
||||
|
||||
compile [stack for %var] to
|
||||
Lua value "stack\(%var as lua id)"
|
||||
|
||||
parse [recurse on %sub as %super] as
|
||||
add %sub to (stack for %super)
|
||||
|
||||
parse [for %var in recursive %iterable %body] as
|
||||
with {(stack for %var): [%iterable]}
|
||||
repeat while: (length of (stack for %var)) > 0
|
||||
%var <- (remove index 1 from (stack for %var))
|
||||
with {%val: %expr}
|
||||
if: %val != (nil)
|
||||
add %val to %result
|
||||
%body
|
||||
=== next %var ===
|
||||
=== stop %var ===
|
||||
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
# This action iterates over a recursive structure, but it's written imperatively and it
|
||||
can return early without traversing the whole tree.
|
||||
action [tree %tree contains %val]
|
||||
for %subtree in recursive %tree
|
||||
if: %subtree = %val
|
||||
return: yes
|
||||
if: (type of %subtree) = "table"
|
||||
for % in %subtree: recurse on % as %subtree
|
||||
return: no
|
||||
|
||||
%t <- [1,[2,[3,4,[]],5,[6]]]
|
||||
say: tree %t contains 3
|
||||
say: tree %t contains 99
|
||||
|
||||
|
||||
action [linked list from %list]
|
||||
%head <- (nil)
|
||||
for %i in (length of %list) to 1 via -1
|
||||
%head <- {value:%list.%i, next:%head}
|
||||
return %head
|
||||
|
||||
for %node in recursive (linked list from [3,4,5,6,7])
|
||||
say "NODE: \(%node.value)"
|
||||
recurse on %node.next as %node
|
||||
|
@ -501,7 +501,7 @@ do
|
||||
source = nil
|
||||
end
|
||||
local lua_string = tostring(lua)
|
||||
local run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self)
|
||||
local run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
|
||||
if not run_lua_fn then
|
||||
local line_numbered_lua = concat((function()
|
||||
local _accum_0 = { }
|
||||
|
@ -339,7 +339,7 @@ with NomsuCompiler
|
||||
|
||||
.run_lua = (lua, source=nil)=>
|
||||
lua_string = tostring(lua)
|
||||
run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self)
|
||||
run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
|
||||
if not run_lua_fn
|
||||
line_numbered_lua = concat(
|
||||
[format("%3d|%s",i,line) for i, line in ipairs get_lines\match(lua_string)],
|
||||
|
Loading…
Reference in New Issue
Block a user