Cleanup: removed "File" type trees (now just Block), overhauled
how_do_i.nom, added "result of %" macro, which allowed comprehensions to be way more concisely defined. Moved len() operator into the nomsu environment.
This commit is contained in:
parent
77c11a2443
commit
4126589afe
@ -36,16 +36,14 @@ immediately
|
|||||||
return (yes)
|
return (yes)
|
||||||
|
|
||||||
immediately
|
immediately
|
||||||
# Note: it's important to have the space after "[" to prevent confusion if %index is a string
|
parse [%list has key %index, %list has index %index] as
|
||||||
compile [%list has key %index, %list has index %index] to
|
%list.%index != (nil)
|
||||||
Lua value ".."
|
|
||||||
((\(%list as lua expr))[ \(%index as lua expr)] ~= nil)
|
|
||||||
|
|
||||||
# Note: it's important to have the space after "[" to prevent confusion if %index is a string
|
parse [..]
|
||||||
compile [..]
|
|
||||||
%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
|
||||||
..to: Lua value "((\(%list as lua expr))[ \(%index as lua expr)] == nil)"
|
..as
|
||||||
|
%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))"
|
||||||
@ -61,90 +59,65 @@ immediately
|
|||||||
|
|
||||||
# List Comprehension
|
# List Comprehension
|
||||||
immediately
|
immediately
|
||||||
compile [%expression for %item in %iterable] to
|
parse [%expression for %item in %iterable] as
|
||||||
assume (%item.type is "Var") or barf ".."
|
result of
|
||||||
List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item.type)
|
%comprehension <- []
|
||||||
return
|
for %i = %item in %iterable
|
||||||
Lua value ".."
|
%comprehension.%i <- %expression
|
||||||
(function()
|
return %comprehension
|
||||||
local comprehension = {};
|
|
||||||
for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
|
|
||||||
comprehension[i] = \(%expression as lua expr);
|
|
||||||
end
|
|
||||||
return comprehension;
|
|
||||||
end)()
|
|
||||||
parse [%expression for all %iterable] as: %expression for % in %iterable
|
|
||||||
|
|
||||||
compile [..]
|
parse [%expression for all %iterable] as
|
||||||
|
%expression for % in %iterable
|
||||||
|
|
||||||
|
parse [..]
|
||||||
%expression for %index from %start to %stop via %step
|
%expression for %index from %start to %stop via %step
|
||||||
%expression for %index from %start to %stop by %step
|
%expression for %index from %start to %stop by %step
|
||||||
..to
|
..as
|
||||||
assume (%index.type is "Var") or barf ".."
|
result of
|
||||||
List comprehension has the wrong type for the loop variable. Expected Var, but got: \(%index.type)
|
%comprehension <- []
|
||||||
return
|
for %index from %start to %stop via %step
|
||||||
Lua value ".."
|
add %expression to %comprehension
|
||||||
(function()
|
return %comprehension
|
||||||
local comprehension = {};
|
|
||||||
for \(%index as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(%step as lua expr) do
|
parse [%expression for all %iterable] as
|
||||||
comprehension[#comprehension+1] = \(%expression as lua expr);
|
%expression for % in %iterable
|
||||||
end
|
|
||||||
return comprehension;
|
parse [%expression for %var from %start to %stop] as
|
||||||
end)()
|
%expression for %var from %start to %stop via 1
|
||||||
parse [%expression for all ] as: %expression for % in %iterable
|
|
||||||
parse [%expression for %var from %start to %stop] as: %expression for %var from %start to %stop via 1
|
|
||||||
parse [..]
|
parse [..]
|
||||||
%expression for all %start to %stop by %step
|
%expression for all %start to %stop by %step
|
||||||
%expression for all %start to %stop via %step
|
%expression for all %start to %stop via %step
|
||||||
..as: %expression for % from %start to %stop via %step
|
..as
|
||||||
parse [%expression for all %start to %stop] as: %expression for all %start to %stop via 1
|
%expression for % from %start to %stop via %step
|
||||||
|
|
||||||
compile [%expression for %key = %value in %iterable] to
|
parse [%expression for all %start to %stop] as
|
||||||
assume (%key.type is "Var") or barf ".."
|
%expression for all %start to %stop via 1
|
||||||
List comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%key.type)
|
|
||||||
assume (%value.type is "Var") or barf ".."
|
|
||||||
List comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%value.type)
|
|
||||||
return
|
|
||||||
Lua value ".."
|
|
||||||
(function()
|
|
||||||
local comprehension = {};
|
|
||||||
for \(%key as lua expr), \(%value as lua expr) in pairs(\(%iterable as lua expr)) do
|
|
||||||
table.insert(comprehension, \(%expression as lua expr));
|
|
||||||
end
|
|
||||||
return comprehension;
|
|
||||||
end)()
|
|
||||||
|
|
||||||
# Dict comprehensions
|
parse [%expression for %key = %value in %iterable] as
|
||||||
immediately
|
result of
|
||||||
compile [%key = %value for %item in %iterable] to
|
%comprehension <- []
|
||||||
assume (%item.type is "Var") or barf ".."
|
for %key = %value in %iterable
|
||||||
Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \(%item.type)
|
add %expression to %comprehension
|
||||||
# Note: it's important to have the space after "[" to prevent confusion if %key is a string
|
return %comprehension
|
||||||
return
|
|
||||||
Lua value ".."
|
|
||||||
(function()
|
|
||||||
local comprehension = {};
|
|
||||||
for i,\(%item as lua expr) in ipairs(\(%iterable as lua expr)) do
|
|
||||||
comprehension[ \(%key as lua expr)] = \(%value as lua expr)
|
|
||||||
end
|
|
||||||
return comprehension;
|
|
||||||
end)()
|
|
||||||
parse [%key = %value for all %iterable] as: %key = %value for % in %iterable
|
|
||||||
|
|
||||||
compile [%key = %value for %src_key = %src_value in %iterable] to
|
# Dict comprehensions
|
||||||
assume (%src_key.type is "Var") or barf ".."
|
parse [%key = %value for %item in %iterable] as
|
||||||
Dict comprehension has the wrong type for the key loop variable. Expected Var, but got: \(%src_key.type)
|
result of
|
||||||
assume (%src_value.type is "Var") or barf ".."
|
%comprehension <- {}
|
||||||
Dict comprehension has the wrong type for the value loop variable. Expected Var, but got: \(%src_value.type)
|
for %item in %iterable
|
||||||
# Note: it's important to have the space after "[" to prevent confusion if %key is a string
|
%comprehension.%key <- %value
|
||||||
return
|
return %comprehension
|
||||||
Lua value ".."
|
|
||||||
(function()
|
parse [%key = %value for all %iterable] as
|
||||||
local comprehension = {};
|
%key = %value for % in %iterable
|
||||||
for \(%src_key as lua expr), \(%src_value as lua expr) in pairs(\(%iterable as lua expr)) do
|
|
||||||
comprehension[ \(%key as lua expr)] = \(%value as lua expr);
|
parse [%key = %value for %src_key = %src_value in %iterable] as
|
||||||
end
|
result of
|
||||||
return comprehension;
|
%comprehension <- {}
|
||||||
end)()
|
for %src_key = %src_value in %iterable
|
||||||
|
%comprehension.%key <- %value
|
||||||
|
return %comprehension
|
||||||
|
|
||||||
immediately
|
immediately
|
||||||
action [%lists flattened]
|
action [%lists flattened]
|
||||||
|
@ -423,3 +423,11 @@ immediately
|
|||||||
end
|
end
|
||||||
end --do-then-always
|
end --do-then-always
|
||||||
|
|
||||||
|
# Inline thunk:
|
||||||
|
immediately
|
||||||
|
compile [result of %body] to
|
||||||
|
Lua value ".."
|
||||||
|
(function()
|
||||||
|
\(%body as lua statements)
|
||||||
|
end)()
|
||||||
|
|
||||||
|
@ -127,6 +127,9 @@ immediately
|
|||||||
lua:convert_to_statements();
|
lua:convert_to_statements();
|
||||||
return lua;
|
return lua;
|
||||||
|
|
||||||
|
action [%tree with vars %vars]
|
||||||
|
=lua "nomsu:tree_with_replaced_vars(\%tree, \%vars)"
|
||||||
|
|
||||||
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()"
|
||||||
|
|
||||||
|
@ -163,14 +163,8 @@ immediately
|
|||||||
# 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
|
# Using custom "len()" instead of Lua's "#" operator for compatibility with luajit.
|
||||||
# A bit of a hack so that luajit works properly.
|
compile [length of %list] to: Lua value "len(\(%list as lua expr))"
|
||||||
Lua value ".."
|
|
||||||
(function(l)
|
|
||||||
local mt = getmetatable(l);
|
|
||||||
if mt and mt.__len then return mt.__len(l) end
|
|
||||||
return #l
|
|
||||||
end)(\(%list as lua expr))
|
|
||||||
|
|
||||||
# Update operators
|
# Update operators
|
||||||
immediately
|
immediately
|
||||||
|
@ -13,29 +13,27 @@ use "core/control_flow.nom"
|
|||||||
# How do I import all the files in a directory?
|
# How do I import all the files in a directory?
|
||||||
use "core"
|
use "core"
|
||||||
|
|
||||||
# Set a variable?
|
# How do I print stuff?
|
||||||
|
say "Hello world!"
|
||||||
|
|
||||||
|
# How do I set a variable?
|
||||||
%x <- 1
|
%x <- 1
|
||||||
%str <- "Hello world"
|
%str <- "Hello world"
|
||||||
# Expressions that are more than just literal values require parentheses:
|
# Expressions that are more than just literal values require parentheses:
|
||||||
%x <- (2 + 3)
|
%x <- (2 + 3)
|
||||||
|
|
||||||
# Modify a variable?
|
# How do I modify a variable?
|
||||||
%foobar <- 100
|
%x <- (%x + 1)
|
||||||
# As a shorthand, you can type:
|
# Or, as a shorthand, you can do this to increment a variable:
|
||||||
%foobar +<- 1
|
%x +<- 1
|
||||||
# which does the same thing as:
|
|
||||||
%foobar <- (%foobar + 1)
|
|
||||||
|
|
||||||
# Print stuff?
|
# How do I define a mutli-line string?
|
||||||
say "Hello world!"
|
|
||||||
|
|
||||||
# Define a mutli-line string?
|
|
||||||
%mutli_str <- ".."
|
%mutli_str <- ".."
|
||||||
Start with "..", then put lines below it
|
Start with "..", then put lines below it
|
||||||
that are indented one level.
|
that are indented one level.
|
||||||
The string will continue until the indentation ends.
|
The string will continue until the indentation ends.
|
||||||
|
|
||||||
# Format a string?
|
# How do I put values inside a string?
|
||||||
%format_str <- ".."
|
%format_str <- ".."
|
||||||
Strings can contain a backslash followed by a variable, list, dict, or parenthesized
|
Strings can contain a backslash followed by a variable, list, dict, or parenthesized
|
||||||
expression. This escaped value will be converted to a readable string, like so:
|
expression. This escaped value will be converted to a readable string, like so:
|
||||||
@ -44,7 +42,7 @@ say "Hello world!"
|
|||||||
If you need to use a plain ol' backslash, you can do \\ <-- that
|
If you need to use a plain ol' backslash, you can do \\ <-- that
|
||||||
%format_str2 <- "Single-line strings can contain escape sequences like \", \\, \n, \065, and \x0A"
|
%format_str2 <- "Single-line strings can contain escape sequences like \", \\, \n, \065, and \x0A"
|
||||||
|
|
||||||
# Define a list?
|
# How do I define a list?
|
||||||
%my_list <- [1,2,"hello"]
|
%my_list <- [1,2,"hello"]
|
||||||
#.. Really long lists can use [..] followed by a bunch of indented values delimited
|
#.. Really long lists can use [..] followed by a bunch of indented values delimited
|
||||||
by commas and/or newlines
|
by commas and/or newlines
|
||||||
@ -54,7 +52,7 @@ say "Hello world!"
|
|||||||
7
|
7
|
||||||
8,9,10
|
8,9,10
|
||||||
|
|
||||||
# Use a list?
|
# How do I use a list?
|
||||||
%my_list <- ["first item", "second item", "third item"]
|
%my_list <- ["first item", "second item", "third item"]
|
||||||
# Lists are 1-indexed because they're implemented as Lua tables, so this prints "first item"
|
# Lists are 1-indexed because they're implemented as Lua tables, so this prints "first item"
|
||||||
say %my_list.1
|
say %my_list.1
|
||||||
@ -62,19 +60,19 @@ say %my_list.1
|
|||||||
%my_list.1 <- "ONE!!!"
|
%my_list.1 <- "ONE!!!"
|
||||||
say (length of %my_list)
|
say (length of %my_list)
|
||||||
|
|
||||||
# Define a dictionary/hash map?
|
# How do I define a dictionary/hash map?
|
||||||
%my_dict <- {x: 99, y: 101}
|
%my_dict <- {x: 99, y: 101}
|
||||||
%my_dict <- {..}
|
%my_dict <- {..}
|
||||||
x: 101, y: 2
|
x: 101, y: 2
|
||||||
"99 bottles": 99
|
"99 bottles": 99
|
||||||
653: 292
|
653: 292
|
||||||
|
|
||||||
# Use a dict?
|
# How do I use a dict?
|
||||||
# Dicts are also implemented as Lua tables, so they're accessed and modified the same way as lists
|
# Dicts are also implemented as Lua tables, so they're accessed and modified the same way as lists
|
||||||
say %my_dict.x
|
say %my_dict.x
|
||||||
%my_dict.x <- 9999
|
%my_dict.x <- 9999
|
||||||
|
|
||||||
# Do conditional branching?
|
# How do I do conditional branching?
|
||||||
if: 1 < 10
|
if: 1 < 10
|
||||||
say "1 is indeed < 10"
|
say "1 is indeed < 10"
|
||||||
|
|
||||||
@ -96,7 +94,7 @@ when
|
|||||||
* else
|
* else
|
||||||
say "this is the default case"
|
say "this is the default case"
|
||||||
|
|
||||||
# Do a switch statement?
|
# How do I do a switch statement?
|
||||||
when 3 = ?
|
when 3 = ?
|
||||||
* 0
|
* 0
|
||||||
* 1
|
* 1
|
||||||
@ -107,7 +105,7 @@ when 3 = ?
|
|||||||
* else
|
* else
|
||||||
say "this won't print"
|
say "this won't print"
|
||||||
|
|
||||||
# Loop over a list (a foreach loop)?
|
# How do I loop over a list (a foreach loop)?
|
||||||
%list <- [1,2,3]
|
%list <- [1,2,3]
|
||||||
for %x in %list
|
for %x in %list
|
||||||
say "For %x loop #\%x"
|
say "For %x loop #\%x"
|
||||||
@ -115,7 +113,7 @@ for %x in %list
|
|||||||
for all %list
|
for all %list
|
||||||
say "For all loop #\%"
|
say "For all loop #\%"
|
||||||
|
|
||||||
# Loop over a number range?
|
# How do I loop over a number range?
|
||||||
# This is inclusive, so it will loop over 1,2, and 3
|
# This is inclusive, so it will loop over 1,2, and 3
|
||||||
for %i from 1 to 3
|
for %i from 1 to 3
|
||||||
say "For %i from 1 to 3 loop #\%i"
|
say "For %i from 1 to 3 loop #\%i"
|
||||||
@ -127,7 +125,7 @@ for %even from 0 to 5 by 2
|
|||||||
for %backwards from 3 to 1 by -1
|
for %backwards from 3 to 1 by -1
|
||||||
say "Backwards #\%backwards"
|
say "Backwards #\%backwards"
|
||||||
|
|
||||||
# While loops:
|
# How do I do a 'while' loop?
|
||||||
%x <- 1
|
%x <- 1
|
||||||
repeat while: %x <= 3
|
repeat while: %x <= 3
|
||||||
say "repeat while loop #\%x"
|
say "repeat while loop #\%x"
|
||||||
@ -138,7 +136,7 @@ repeat until: %x > 3
|
|||||||
say "repeat until loop #\%x"
|
say "repeat until loop #\%x"
|
||||||
%x +<- 1
|
%x +<- 1
|
||||||
|
|
||||||
# Infinite loop:
|
# How do I do an infinite loop?
|
||||||
%x <- 1
|
%x <- 1
|
||||||
repeat
|
repeat
|
||||||
say "repeat loop #\%x"
|
say "repeat loop #\%x"
|
||||||
@ -146,7 +144,7 @@ repeat
|
|||||||
if: %x > 3
|
if: %x > 3
|
||||||
stop repeating
|
stop repeating
|
||||||
|
|
||||||
# GOTOs:
|
# How do I do a 'goto'?
|
||||||
do
|
do
|
||||||
%x <- 1
|
%x <- 1
|
||||||
=== %again ===
|
=== %again ===
|
||||||
@ -157,13 +155,14 @@ do
|
|||||||
say "finished going to"
|
say "finished going to"
|
||||||
|
|
||||||
|
|
||||||
# Function definition:
|
# How do I define a function/method/procedure?
|
||||||
|
# In nomsu, they're called "action"s, and they can be declared like this:
|
||||||
action [say both %first and also %second]
|
action [say both %first and also %second]
|
||||||
say %first
|
say %first
|
||||||
# Function arguments are accessed just like variables
|
# Function arguments are accessed just like variables
|
||||||
say %second
|
say %second
|
||||||
|
|
||||||
# Functions can use "return" to return a value early
|
# Actions can use "return" to return a value early
|
||||||
action [first fibonacci above %n]
|
action [first fibonacci above %n]
|
||||||
%f1 <- 0
|
%f1 <- 0
|
||||||
%f2 <- 1
|
%f2 <- 1
|
||||||
@ -176,7 +175,7 @@ action [first fibonacci above %n]
|
|||||||
|
|
||||||
say (first fibonacci above 10)
|
say (first fibonacci above 10)
|
||||||
|
|
||||||
# Functions can have aliases, which may or may not have the arguments in different order
|
# Actions can have aliases, which may or may not have the arguments in different order
|
||||||
action [..]
|
action [..]
|
||||||
I hate %worse_things more than %better_things
|
I hate %worse_things more than %better_things
|
||||||
I think %worse_things are worse than %better_things
|
I think %worse_things are worse than %better_things
|
||||||
@ -188,18 +187,18 @@ I like "dogs" more than "cats"
|
|||||||
I think "chihuahuas" are worse than "corgis"
|
I think "chihuahuas" are worse than "corgis"
|
||||||
|
|
||||||
|
|
||||||
#.. Function calls can have parts of the function's name spread throughout.
|
#.. Actions can have parts of the action's name spread throughout.
|
||||||
Everything that's not a literal value is treated as part of the function's name
|
Everything that's not a literal value is treated as part of the action's name
|
||||||
say both "Hello" and also "again!"
|
say both "Hello" and also "again!"
|
||||||
|
|
||||||
# Functions can even have their name at the end:
|
# Actions can even start with a parameter
|
||||||
action [%what_she_said is what she said]
|
action [%what_she_said is what she said]
|
||||||
say %what_she_said
|
say %what_she_said
|
||||||
say "-- she said"
|
say "-- she said"
|
||||||
|
|
||||||
"Howdy pardner" is what she said
|
"Howdy pardner" is what she said
|
||||||
|
|
||||||
#.. The language only reserves []{}().,:;% as special characters, so functions
|
#.. The language only reserves []{}().,:;% as special characters, so actions
|
||||||
can have really funky names!
|
can have really funky names!
|
||||||
action [>> %foo_bar $$$^ --> % @& _~-^-~_~-^ %1 !]
|
action [>> %foo_bar $$$^ --> % @& _~-^-~_~-^ %1 !]
|
||||||
say %foo_bar
|
say %foo_bar
|
||||||
@ -214,7 +213,7 @@ action [% と言う]
|
|||||||
"\(%)世界"
|
"\(%)世界"
|
||||||
say (%こんにちは と言う)
|
say (%こんにちは と言う)
|
||||||
|
|
||||||
# Math and logic operations are just treated the same as function calls in the syntax
|
# Math and logic operations are just treated the same as actions in the syntax
|
||||||
say (2 + 3)
|
say (2 + 3)
|
||||||
#.. So you can define your own operators, although they will need to be parenthesized to
|
#.. So you can define your own operators, although they will need to be parenthesized to
|
||||||
play nicely with other operators
|
play nicely with other operators
|
||||||
@ -222,6 +221,7 @@ action [%a ++ %b]
|
|||||||
2 * (%a + %b)
|
2 * (%a + %b)
|
||||||
say (1 ++ (2 * 3))
|
say (1 ++ (2 * 3))
|
||||||
|
|
||||||
|
|
||||||
# How do I do grouping?
|
# How do I do grouping?
|
||||||
# Expressions can be grouped by enclosing parentheses:
|
# Expressions can be grouped by enclosing parentheses:
|
||||||
say (2 + 3)
|
say (2 + 3)
|
||||||
@ -282,3 +282,60 @@ if (1 > (TWENTY)) on opposite day
|
|||||||
say "Lua compiling macros work!"
|
say "Lua compiling macros work!"
|
||||||
say "It looks like a keyword, but there's no magic here!"
|
say "It looks like a keyword, but there's no magic here!"
|
||||||
|
|
||||||
|
|
||||||
|
# How do I use an action as a value?
|
||||||
|
#.. 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 all %items
|
||||||
|
%key <- (=lua "\%key_fn(\%)")
|
||||||
|
if: (%best is (nil)) or (%key > %best_key)
|
||||||
|
<- {%best:%, %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 %: % * %)
|
||||||
|
|
||||||
|
#.. 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_expr] as
|
||||||
|
result of
|
||||||
|
<- {%best:nil, %best_key:nil}
|
||||||
|
for all %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 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
|
||||||
|
32
nomsu.lua
32
nomsu.lua
@ -375,7 +375,6 @@ do
|
|||||||
end
|
end
|
||||||
local tree = self:parse(nomsu_code)
|
local tree = self:parse(nomsu_code)
|
||||||
assert(tree, "Failed to parse: " .. tostring(nomsu_code))
|
assert(tree, "Failed to parse: " .. tostring(nomsu_code))
|
||||||
assert(tree.type == "File", "Attempt to run non-file: " .. tostring(tree.type))
|
|
||||||
local lua = tree:as_lua(self)
|
local lua = tree:as_lua(self)
|
||||||
lua:convert_to_statements()
|
lua:convert_to_statements()
|
||||||
lua:declare_locals()
|
lua:declare_locals()
|
||||||
@ -492,7 +491,7 @@ do
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
local _exp_0 = tree.type
|
local _exp_0 = tree.type
|
||||||
if "List" == _exp_0 or "File" == _exp_0 or "Block" == _exp_0 or "Action" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then
|
if "List" == _exp_0 or "Block" == _exp_0 or "Action" == _exp_0 or "Text" == _exp_0 or "IndexChain" == _exp_0 then
|
||||||
local _list_0 = tree.value
|
local _list_0 = tree.value
|
||||||
for _index_0 = 1, #_list_0 do
|
for _index_0 = 1, #_list_0 do
|
||||||
local v = _list_0[_index_0]
|
local v = _list_0[_index_0]
|
||||||
@ -543,6 +542,18 @@ do
|
|||||||
return tree:map(fn)
|
return tree:map(fn)
|
||||||
end,
|
end,
|
||||||
tree_with_replaced_vars = function(self, tree, replacements)
|
tree_with_replaced_vars = function(self, tree, replacements)
|
||||||
|
if not (next(replacements)) then
|
||||||
|
return tree
|
||||||
|
end
|
||||||
|
if next(replacements).type == "Var" then
|
||||||
|
do
|
||||||
|
local _tbl_0 = { }
|
||||||
|
for k, v in pairs(replacements) do
|
||||||
|
_tbl_0[self:var_to_lua_identifier(k)] = v
|
||||||
|
end
|
||||||
|
replacements = _tbl_0
|
||||||
|
end
|
||||||
|
end
|
||||||
return tree:map(function(t)
|
return tree:map(function(t)
|
||||||
if t.type == "Var" then
|
if t.type == "Var" then
|
||||||
local id = tostring(t:as_lua(self))
|
local id = tostring(t:as_lua(self))
|
||||||
@ -737,6 +748,23 @@ do
|
|||||||
load = load,
|
load = load,
|
||||||
ipairs = ipairs
|
ipairs = ipairs
|
||||||
}
|
}
|
||||||
|
if jit then
|
||||||
|
self.environment.len = function(x)
|
||||||
|
do
|
||||||
|
local mt = getmetatable(x)
|
||||||
|
if mt then
|
||||||
|
if mt.__len then
|
||||||
|
return mt.__len(x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #x
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.environment.len = (function(x)
|
||||||
|
return #x
|
||||||
|
end)
|
||||||
|
end
|
||||||
for k, v in pairs(Types) do
|
for k, v in pairs(Types) do
|
||||||
self.environment[k] = v
|
self.environment[k] = v
|
||||||
end
|
end
|
||||||
|
13
nomsu.moon
13
nomsu.moon
@ -254,6 +254,13 @@ class NomsuCompiler
|
|||||||
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :pairs,
|
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :pairs,
|
||||||
:load, :ipairs,
|
:load, :ipairs,
|
||||||
}
|
}
|
||||||
|
@environment.len = if jit
|
||||||
|
(x)->
|
||||||
|
if mt = getmetatable(x)
|
||||||
|
if mt.__len
|
||||||
|
return mt.__len(x)
|
||||||
|
return #x
|
||||||
|
else ((x) -> #x)
|
||||||
for k,v in pairs(Types) do @environment[k] = v
|
for k,v in pairs(Types) do @environment[k] = v
|
||||||
@environment.Tuple = Tuple
|
@environment.Tuple = Tuple
|
||||||
@environment.Lua = Lua
|
@environment.Lua = Lua
|
||||||
@ -331,7 +338,6 @@ class NomsuCompiler
|
|||||||
if #tostring(nomsu_code) == 0 then return nil
|
if #tostring(nomsu_code) == 0 then return nil
|
||||||
tree = @parse(nomsu_code)
|
tree = @parse(nomsu_code)
|
||||||
assert tree, "Failed to parse: #{nomsu_code}"
|
assert tree, "Failed to parse: #{nomsu_code}"
|
||||||
assert tree.type == "File", "Attempt to run non-file: #{tree.type}"
|
|
||||||
lua = tree\as_lua(@)
|
lua = tree\as_lua(@)
|
||||||
lua\convert_to_statements!
|
lua\convert_to_statements!
|
||||||
lua\declare_locals!
|
lua\declare_locals!
|
||||||
@ -410,7 +416,7 @@ class NomsuCompiler
|
|||||||
coroutine.yield(tree, depth)
|
coroutine.yield(tree, depth)
|
||||||
return unless Types.is_node(tree)
|
return unless Types.is_node(tree)
|
||||||
switch tree.type
|
switch tree.type
|
||||||
when "List", "File", "Block", "Action", "Text", "IndexChain"
|
when "List", "Block", "Action", "Text", "IndexChain"
|
||||||
for v in *tree.value
|
for v in *tree.value
|
||||||
@walk_tree(v, depth+1)
|
@walk_tree(v, depth+1)
|
||||||
when "Dict"
|
when "Dict"
|
||||||
@ -445,6 +451,9 @@ class NomsuCompiler
|
|||||||
return tree\map(fn)
|
return tree\map(fn)
|
||||||
|
|
||||||
tree_with_replaced_vars: (tree, replacements)=>
|
tree_with_replaced_vars: (tree, replacements)=>
|
||||||
|
return tree unless next(replacements)
|
||||||
|
if next(replacements).type == "Var"
|
||||||
|
replacements = {@var_to_lua_identifier(k),v for k,v in pairs(replacements)}
|
||||||
tree\map (t)->
|
tree\map (t)->
|
||||||
if t.type == "Var"
|
if t.type == "Var"
|
||||||
id = tostring(t\as_lua(self))
|
id = tostring(t\as_lua(self))
|
||||||
|
33
nomsu.peg
33
nomsu.peg
@ -1,10 +1,8 @@
|
|||||||
file (File):
|
file:
|
||||||
{| shebang?
|
shebang?
|
||||||
(ignored_line %nl)*
|
(ignored_line %nl)*
|
||||||
statement ((nodent (statement / (({} ([^%nl]* -> "Error while parsing line")) => error)))
|
(block / action / expression)?
|
||||||
/ (({} ((%nl %dedent) ->"Indentation error")) => error))*
|
(%nl ignored_line)*
|
||||||
(%nl ignored_line)*
|
|
||||||
|} -> Tuple
|
|
||||||
(!. / (({} (.* -> "Parse error")) => error))
|
(!. / (({} (.* -> "Parse error")) => error))
|
||||||
|
|
||||||
shebang: "#!" [^%nl]* (!. / %nl)
|
shebang: "#!" [^%nl]* (!. / %nl)
|
||||||
@ -15,23 +13,26 @@ inline_statement: inline_action / inline_expression
|
|||||||
inline_block (Block):
|
inline_block (Block):
|
||||||
{| inline_statement (%ws* ";" %ws* inline_statement)+ |} -> Tuple
|
{| inline_statement (%ws* ";" %ws* inline_statement)+ |} -> Tuple
|
||||||
block (Block):
|
block (Block):
|
||||||
{| statement (nodent statement)+ |} -> Tuple
|
{| statement (nodent (statement / (({} ([^%nl]* -> "Error while parsing block line")) => error)))+ |} -> Tuple
|
||||||
|
|
||||||
inline_nomsu (Nomsu): "\" noindex_inline_expression
|
inline_nomsu (Nomsu): "\" noindex_inline_expression
|
||||||
indented_nomsu (Nomsu):
|
indented_nomsu (Nomsu):
|
||||||
"\" (noindex_inline_expression / (":" %ws* (inline_block / inline_action / inline_expression) eol) / indented_expression)
|
"\" (noindex_inline_expression / (":" %ws* (inline_block / inline_action / inline_expression) eol) / indented_expression)
|
||||||
|
|
||||||
index_chain (IndexChain):
|
index_chain (IndexChain):
|
||||||
{| noindex_inline_expression ("." ((({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / noindex_inline_expression))+ |} -> Tuple
|
{|
|
||||||
|
noindex_inline_expression ("." (text_word / noindex_inline_expression))+
|
||||||
|
|} -> Tuple
|
||||||
|
|
||||||
noindex_inline_expression:
|
noindex_inline_expression:
|
||||||
number / variable / inline_text / inline_list / inline_dict / inline_nomsu
|
number / variable / inline_text / inline_list / inline_dict / inline_nomsu
|
||||||
/ (
|
/ ( "("
|
||||||
"(" %ws* (inline_block / inline_action / inline_expression) %ws*
|
%ws* (inline_block / inline_action / inline_expression) %ws*
|
||||||
(")"
|
(")"
|
||||||
/ (({} ((!. / &%nl) -> 'Expected to find a ) before the end of the line')) => error)
|
/ (({} ((!. / &%nl) -> 'Expected to find a ) before the end of the line')) => error)
|
||||||
/ (({} ([^%nl]* -> 'Error while parsing subexpression')) => error))
|
/ (({} ([^%nl]* -> 'Error while parsing subexpression')) => error)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
inline_expression:
|
inline_expression:
|
||||||
index_chain / noindex_inline_expression
|
index_chain / noindex_inline_expression
|
||||||
@ -49,10 +50,12 @@ inline_action (Action):
|
|||||||
{| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
|
{| (inline_expression %ws*)* word (%ws* (inline_expression / word))*
|
||||||
(%ws* ":" %ws* (inline_block / inline_action / inline_expression))?|} -> Tuple
|
(%ws* ":" %ws* (inline_block / inline_action / inline_expression))?|} -> Tuple
|
||||||
action (Action):
|
action (Action):
|
||||||
{| (expression (dotdot / %ws*))* word ((dotdot / %ws*) (expression / word))* |} -> Tuple
|
{| (expression (dotdot? %ws*))* word ((dotdot? %ws*) (expression / word))* |} -> Tuple
|
||||||
|
|
||||||
word (Word): { %operator / (!number plain_word) }
|
word (Word): { %operator / (!number plain_word) }
|
||||||
|
|
||||||
|
text_word (Text): {| {%operator / (!number plain_word)} |} -> Tuple
|
||||||
|
|
||||||
inline_text (Text):
|
inline_text (Text):
|
||||||
!('".."' eol)
|
!('".."' eol)
|
||||||
'"' ({|
|
'"' ({|
|
||||||
@ -119,7 +122,7 @@ dict_line:
|
|||||||
inline_dict_item:
|
inline_dict_item:
|
||||||
((dict_key %ws* (":" %ws* (inline_block / inline_action / inline_expression)?)?)-> DictEntry)
|
((dict_key %ws* (":" %ws* (inline_block / inline_action / inline_expression)?)?)-> DictEntry)
|
||||||
dict_key:
|
dict_key:
|
||||||
(({} ({|{%operator / (!number plain_word)}|} -> Tuple) {}) -> Text) / inline_expression
|
text_word / inline_expression
|
||||||
|
|
||||||
block_comment: "#.." [^%nl]* (%nl+ %indent [^%nl]* (%nl+ %nodent [^%nl]*)* %dedent)?
|
block_comment: "#.." [^%nl]* (%nl+ %indent [^%nl]* (%nl+ %nodent [^%nl]*)* %dedent)?
|
||||||
line_comment: "#" [^%nl]*
|
line_comment: "#" [^%nl]*
|
||||||
@ -131,5 +134,5 @@ nodent: eol (%nl ignored_line)* %nl %nodent
|
|||||||
dedent: eol (%nl ignored_line)* (((!.) &%dedent) / (&(%nl %dedent)))
|
dedent: eol (%nl ignored_line)* (((!.) &%dedent) / (&(%nl %dedent)))
|
||||||
non_dedent_error: (!dedent .)* eol (%nl ignored_line)* (!. / &%nl)
|
non_dedent_error: (!dedent .)* eol (%nl ignored_line)* (!. / &%nl)
|
||||||
comma: %ws* "," %ws*
|
comma: %ws* "," %ws*
|
||||||
dotdot: nodent ".." %ws*
|
dotdot: nodent ".."
|
||||||
plain_word: ([a-zA-Z0-9_] / %utf8_char)+
|
plain_word: ([a-zA-Z0-9_] / %utf8_char)+
|
||||||
|
@ -52,63 +52,9 @@ Tree = function(name, methods)
|
|||||||
"source"
|
"source"
|
||||||
}, methods)
|
}, methods)
|
||||||
end
|
end
|
||||||
Tree("File", {
|
|
||||||
as_lua = function(self, nomsu)
|
|
||||||
if #self.value == 1 then
|
|
||||||
return self.value[1]:as_lua(nomsu)
|
|
||||||
end
|
|
||||||
local lua = Lua(self.source)
|
|
||||||
for i, line in ipairs(self.value) do
|
|
||||||
local line_lua = line:as_lua(nomsu)
|
|
||||||
if not line_lua then
|
|
||||||
error("No lua produced by " .. tostring(repr(line)), 0)
|
|
||||||
end
|
|
||||||
if i > 1 then
|
|
||||||
lua:append("\n")
|
|
||||||
end
|
|
||||||
lua:convert_to_statements()
|
|
||||||
lua:append(line_lua)
|
|
||||||
end
|
|
||||||
lua:declare_locals()
|
|
||||||
return lua
|
|
||||||
end,
|
|
||||||
as_nomsu = function(self, inline)
|
|
||||||
if inline == nil then
|
|
||||||
inline = false
|
|
||||||
end
|
|
||||||
if inline then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
local nomsu = Nomsu(self.source)
|
|
||||||
for i, line in ipairs(self.value) do
|
|
||||||
line = assert(line:as_nomsu(nil, true), "Could not convert line to nomsu")
|
|
||||||
nomsu:append(line)
|
|
||||||
if i < #self.value then
|
|
||||||
if tostring(line):match("\n") then
|
|
||||||
nomsu:append("\n")
|
|
||||||
end
|
|
||||||
nomsu:append("\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nomsu
|
|
||||||
end,
|
|
||||||
map = function(self, fn)
|
|
||||||
return fn(self) or self:with_value(Tuple(unpack((function()
|
|
||||||
local _accum_0 = { }
|
|
||||||
local _len_0 = 1
|
|
||||||
local _list_0 = self.value
|
|
||||||
for _index_0 = 1, #_list_0 do
|
|
||||||
local v = _list_0[_index_0]
|
|
||||||
_accum_0[_len_0] = v:map(fn)
|
|
||||||
_len_0 = _len_0 + 1
|
|
||||||
end
|
|
||||||
return _accum_0
|
|
||||||
end)())))
|
|
||||||
end
|
|
||||||
})
|
|
||||||
Tree("Nomsu", {
|
Tree("Nomsu", {
|
||||||
as_lua = function(self, nomsu)
|
as_lua = function(self, nomsu)
|
||||||
return Lua.Value(self.source, "nomsu:parse(Nomsu(", repr(self.value.source), ", ", repr(tostring(self.value.source:get_text())), ")).value[1]")
|
return Lua.Value(self.source, "nomsu:parse(Nomsu(", repr(self.value.source), ", ", repr(tostring(self.value:as_nomsu())), "))")
|
||||||
end,
|
end,
|
||||||
as_nomsu = function(self, inline)
|
as_nomsu = function(self, inline)
|
||||||
if inline == nil then
|
if inline == nil then
|
||||||
@ -162,6 +108,9 @@ Tree("Block", {
|
|||||||
nomsu:append(line)
|
nomsu:append(line)
|
||||||
if i < #self.value then
|
if i < #self.value then
|
||||||
nomsu:append("\n")
|
nomsu:append("\n")
|
||||||
|
if tostring(line):match("\n") then
|
||||||
|
nomsu:append("\n")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nomsu
|
return nomsu
|
||||||
@ -212,6 +161,9 @@ Tree("Action", {
|
|||||||
args = _accum_0
|
args = _accum_0
|
||||||
end
|
end
|
||||||
local ret = compile_action(self, unpack(args))
|
local ret = compile_action(self, unpack(args))
|
||||||
|
if not ret then
|
||||||
|
error("Failed to produce any Lua")
|
||||||
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
local action = rawget(nomsu.environment.ACTIONS, stub)
|
local action = rawget(nomsu.environment.ACTIONS, stub)
|
||||||
@ -814,6 +766,9 @@ Tree("IndexChain", {
|
|||||||
if not (bit_nomsu) then
|
if not (bit_nomsu) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
if bit.type == "Action" or bit.type == "Block" then
|
||||||
|
bit_nomsu:parenthesize()
|
||||||
|
end
|
||||||
nomsu:append(bit_nomsu)
|
nomsu:append(bit_nomsu)
|
||||||
end
|
end
|
||||||
return nomsu
|
return nomsu
|
||||||
|
@ -33,40 +33,9 @@ Tree = (name, methods)->
|
|||||||
Types[name] = immutable {"value","source"}, methods
|
Types[name] = immutable {"value","source"}, methods
|
||||||
|
|
||||||
|
|
||||||
Tree "File",
|
|
||||||
as_lua: (nomsu)=>
|
|
||||||
if #@value == 1
|
|
||||||
return @value[1]\as_lua(nomsu)
|
|
||||||
lua = Lua(@source)
|
|
||||||
for i, line in ipairs @value
|
|
||||||
line_lua = line\as_lua(nomsu)
|
|
||||||
if not line_lua
|
|
||||||
error("No lua produced by #{repr line}", 0)
|
|
||||||
if i > 1
|
|
||||||
lua\append "\n"
|
|
||||||
lua\convert_to_statements!
|
|
||||||
lua\append line_lua
|
|
||||||
lua\declare_locals!
|
|
||||||
return lua
|
|
||||||
|
|
||||||
as_nomsu: (inline=false)=>
|
|
||||||
return nil if inline
|
|
||||||
nomsu = Nomsu(@source)
|
|
||||||
for i, line in ipairs @value
|
|
||||||
line = assert(line\as_nomsu(nil,true), "Could not convert line to nomsu")
|
|
||||||
nomsu\append line
|
|
||||||
if i < #@value
|
|
||||||
if tostring(line)\match("\n")
|
|
||||||
nomsu\append "\n"
|
|
||||||
nomsu\append "\n"
|
|
||||||
return nomsu
|
|
||||||
|
|
||||||
map: (fn)=>
|
|
||||||
fn(self) or @with_value(Tuple(unpack([v\map(fn) for v in *@value])))
|
|
||||||
|
|
||||||
Tree "Nomsu",
|
Tree "Nomsu",
|
||||||
as_lua: (nomsu)=>
|
as_lua: (nomsu)=>
|
||||||
Lua.Value(@source, "nomsu:parse(Nomsu(",repr(@value.source),", ",repr(tostring(@value.source\get_text!)),")).value[1]")
|
Lua.Value(@source, "nomsu:parse(Nomsu(",repr(@value.source),", ",repr(tostring(@value\as_nomsu!)),"))")
|
||||||
|
|
||||||
as_nomsu: (inline=false)=>
|
as_nomsu: (inline=false)=>
|
||||||
nomsu = @value\as_nomsu(true)
|
nomsu = @value\as_nomsu(true)
|
||||||
@ -105,6 +74,8 @@ Tree "Block",
|
|||||||
nomsu\append line
|
nomsu\append line
|
||||||
if i < #@value
|
if i < #@value
|
||||||
nomsu\append "\n"
|
nomsu\append "\n"
|
||||||
|
if tostring(line)\match("\n")
|
||||||
|
nomsu\append "\n"
|
||||||
return nomsu
|
return nomsu
|
||||||
|
|
||||||
map: (fn)=>
|
map: (fn)=>
|
||||||
@ -121,6 +92,7 @@ Tree "Action",
|
|||||||
args = [args[p-1] for p in *nomsu.environment.ARG_ORDERS[compile_action][stub]]
|
args = [args[p-1] for p in *nomsu.environment.ARG_ORDERS[compile_action][stub]]
|
||||||
-- Force Lua to avoid tail call optimization for debugging purposes
|
-- Force Lua to avoid tail call optimization for debugging purposes
|
||||||
ret = compile_action(self, unpack(args))
|
ret = compile_action(self, unpack(args))
|
||||||
|
if not ret then error("Failed to produce any Lua")
|
||||||
return ret
|
return ret
|
||||||
action = rawget(nomsu.environment.ACTIONS, stub)
|
action = rawget(nomsu.environment.ACTIONS, stub)
|
||||||
lua = Lua.Value(@source)
|
lua = Lua.Value(@source)
|
||||||
@ -492,6 +464,8 @@ Tree "IndexChain",
|
|||||||
nomsu\append "."
|
nomsu\append "."
|
||||||
bit_nomsu = bit\as_nomsu(true)
|
bit_nomsu = bit\as_nomsu(true)
|
||||||
return nil unless bit_nomsu
|
return nil unless bit_nomsu
|
||||||
|
if bit.type == "Action" or bit.type == "Block"
|
||||||
|
bit_nomsu\parenthesize!
|
||||||
nomsu\append bit_nomsu
|
nomsu\append bit_nomsu
|
||||||
return nomsu
|
return nomsu
|
||||||
|
|
||||||
|
@ -192,3 +192,11 @@ try
|
|||||||
assume (%x = 1) or barf "do/then always failed"
|
assume (%x = 1) or barf "do/then always failed"
|
||||||
|
|
||||||
say "Control flow test passed."
|
say "Control flow test passed."
|
||||||
|
|
||||||
|
assume
|
||||||
|
(..)
|
||||||
|
result of
|
||||||
|
%n <- 0
|
||||||
|
for all [1,2,3]: %n +<- %
|
||||||
|
return %n
|
||||||
|
..= 6
|
||||||
|
Loading…
Reference in New Issue
Block a user