aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/collections.nom4
-rw-r--r--core/control_flow.nom21
-rw-r--r--core/io.nom4
-rw-r--r--core/metaprogramming.nom4
-rw-r--r--core/operators.nom10
-rw-r--r--error_handling.lua2
-rw-r--r--error_handling.moon3
-rw-r--r--examples/how_do_i.nom202
-rw-r--r--nomsu_compiler.lua2
-rw-r--r--nomsu_compiler.moon2
10 files changed, 174 insertions, 80 deletions
diff --git a/core/collections.nom b/core/collections.nom
index 6ba806a..abd4a3a 100644
--- a/core/collections.nom
+++ b/core/collections.nom
@@ -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))"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/core/control_flow.nom b/core/control_flow.nom
index 4a007df..9aae937 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -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 <-
diff --git a/core/io.nom b/core/io.nom
index 751650d..48db71f 100644
--- a/core/io.nom
+++ b/core/io.nom
@@ -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
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 8911b59..1e167d0 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -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 ".."
diff --git a/core/operators.nom b/core/operators.nom
index 0756751..c81760d 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -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))"
diff --git a/error_handling.lua b/error_handling.lua
index 3ead904..5e7d715 100644
--- a/error_handling.lua
+++ b/error_handling.lua
@@ -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
diff --git a/error_handling.moon b/error_handling.moon
index c95556b..8aa0a82 100644
--- a/error_handling.moon
+++ b/error_handling.moon
@@ -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
diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index 107b987..867405b 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -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
-
- # 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
+# 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
+
+# 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 (% * %)
-
-# 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)
-
-# 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
-
-%double-nums <- ((2 * %num) for %num in %nums BACKWARDS!)
-say %double-nums
+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)
+
+# The first example generates code that looks like:
+
+ 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))
+
+ 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
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index 5c7fb91..d093ff5 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -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 = { }
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index 4d7b4cf..8226db4 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -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)],