Mostly working overhaul again that strips out ending lines with "..",
and simplifies a lot of the code. Also stripped out Expression, which was just a useless level of indirection.
This commit is contained in:
parent
a6cd8907c8
commit
6e46f042fd
@ -7,13 +7,11 @@ require "lib/core.nom"
|
||||
|
||||
say "foo"
|
||||
|
||||
say (4)
|
||||
say (-4 + (1.5 - .25))
|
||||
|
||||
#.. "rule" is just a function that takes a function call spec and a block of code to run,
|
||||
#.. "rule" is just a macro that takes a function call spec and a block of code to run,
|
||||
and stores the function definition
|
||||
rule: fart ..=: say "poot"
|
||||
|
||||
fart
|
||||
rule [fart] =: say "poot"
|
||||
|
||||
# multi-line strings:
|
||||
say ".."
|
||||
@ -25,21 +23,26 @@ say ".."
|
||||
|
|
||||
| with
|
||||
| rather
|
||||
| silly
|
||||
| silly # fake comments and
|
||||
# Real comments look like this
|
||||
| indentation
|
||||
|
||||
| and "quotes"
|
||||
|.." (even fakeouts like that) "
|
||||
|(done)
|
||||
|
|
||||
|^and trailing spaces
|
||||
|
|
||||
|
||||
rule: doublefart ..=: # this farts twice
|
||||
say ".."|Longstrings have interpolation: \1 + 2\ <- like that
|
||||
|
||||
rule [doublefart] =: # this farts twice
|
||||
say "poot"
|
||||
say "poot"
|
||||
|
||||
doublefart
|
||||
|
||||
rule: subex work ..=: "subexpressions work"
|
||||
rule [subex work] =: "subexpressions work"
|
||||
|
||||
say (subex work)
|
||||
|
||||
@ -54,9 +57,9 @@ say [..]
|
||||
1, 2
|
||||
3
|
||||
|
||||
rule: say both %one and %two ..=:
|
||||
say %one
|
||||
say %two
|
||||
rule [say both %1 and %2] =:
|
||||
say %1
|
||||
say %2
|
||||
|
||||
say both [..]
|
||||
1,2
|
||||
@ -64,24 +67,21 @@ say both [..]
|
||||
3,4
|
||||
|
||||
|
||||
say both..
|
||||
"hello"
|
||||
and "world"
|
||||
|
||||
rule: three ..=: 3
|
||||
say both ..
|
||||
"a list:"
|
||||
and [..]
|
||||
1,2,(three),(4)
|
||||
rule [three] =: 3
|
||||
say both
|
||||
.."a list:"
|
||||
..and [..]
|
||||
1,2,three,4
|
||||
|
||||
if 1: yes
|
||||
..else: no
|
||||
|
||||
if 1: yes ..else: no
|
||||
if 1 (:yes) else (:no)
|
||||
|
||||
say (do: return: 5)
|
||||
|
||||
rule: do %one also %two ..=:
|
||||
# Some variables
|
||||
rule [do %one also %two] =:
|
||||
do %one
|
||||
do %two
|
||||
|
||||
@ -92,9 +92,38 @@ say (do: return: "wow")
|
||||
|
||||
say (1 + (-(2 * 3)))
|
||||
|
||||
say (2 + (..)
|
||||
3 * 4
|
||||
..)
|
||||
say (..)
|
||||
2 + (..)
|
||||
3 * 4
|
||||
|
||||
when:
|
||||
when %x == 1:
|
||||
say "one"
|
||||
when %x == 2:
|
||||
say "two"
|
||||
when %x:
|
||||
say "nonzero"
|
||||
else:
|
||||
say "???"
|
||||
|
||||
when:
|
||||
* %x == 1:
|
||||
say "one"
|
||||
* %x == 2:
|
||||
say "two"
|
||||
* %x:
|
||||
say "nonzero"
|
||||
*:
|
||||
say "???"
|
||||
|
||||
when %x ==:
|
||||
* 1:
|
||||
say "one"
|
||||
* 2:
|
||||
say "two"
|
||||
*:
|
||||
say "???"
|
||||
|
||||
|
||||
if %x:
|
||||
say "one"
|
||||
@ -110,7 +139,7 @@ say ".."
|
||||
| with multiple lines
|
||||
| and an interpolated expression: \2 + 5\
|
||||
|
||||
rule: %n bottles ..=:
|
||||
rule [%n bottles] =:
|
||||
lua block ".."
|
||||
|do
|
||||
| print("running raw lua code...")
|
||||
@ -122,7 +151,7 @@ rule: %n bottles ..=:
|
||||
nil
|
||||
9 bottles
|
||||
|
||||
rule: dumsum %nums ..=:
|
||||
rule [dumsum %nums] =:
|
||||
%sum =: 0
|
||||
for %n in %nums:
|
||||
%sum +=: %n
|
||||
|
@ -10,21 +10,18 @@ with secrets:
|
||||
| {by_name=function(self, key) return "User<"..key..">" end,
|
||||
| add=function(self, key) self[key] = self:by_name(key) end}
|
||||
|})
|
||||
rule: find user %name ..=:
|
||||
rule [find user %name] =:
|
||||
lua expr "secrets.users:by_name(vars.name) or compiler:error('Failed to find user: '..tostring(vars.name))"
|
||||
rule: add user %name ..=:
|
||||
rule [add user %name] =:
|
||||
lua expr "secrets.users:add(vars.name)"
|
||||
macro: @ %name_block ..=:
|
||||
macro [@%name_block] =:
|
||||
%name_str =: lua expr "vars.name_block.value.value.src"
|
||||
".."
|
||||
|compiler:call("find user %", \repr %name_str\)
|
||||
rule:
|
||||
everybody
|
||||
everyone
|
||||
..=:
|
||||
rule [everybody, everyone] =:
|
||||
(%entry's "key") for %entry in (entries in (secret %users))
|
||||
|
||||
rule: rules that change users ..=: ["add user %"]
|
||||
rule [rules that change users] =: ["add user %"]
|
||||
|
||||
# Inventory:
|
||||
with secrets:
|
||||
@ -35,29 +32,29 @@ with secrets:
|
||||
| return t
|
||||
|end})
|
||||
|
||||
rule: inventory ..=:
|
||||
rule [inventory] =:
|
||||
dict (..)
|
||||
[%inv's "key", dict (%entry for %entry in (entries in (%inv's "value")))]
|
||||
for %inv in (entries in (secret %inventory))
|
||||
..for %inv in (entries in (secret %inventory))
|
||||
|
||||
rule: %person's inventory ..=:
|
||||
rule [%person's inventory] =:
|
||||
dict (%entry for %entry in (entries in ((secret %inventory)->%person)))
|
||||
|
||||
rule: %person's stock of %item ..=:
|
||||
rule [%person's stock of %item] =:
|
||||
%item =: canonicalize %item
|
||||
((%person's inventory)->%item) or 0
|
||||
|
||||
rule: %person's stock of %item as str ..=:
|
||||
rule [%person's stock of %item as str] =:
|
||||
%item =: canonicalize %item
|
||||
%count =: %person's stock of %item
|
||||
".."
|
||||
|\%count\ \(singular %item) if (%count == 1) else (plural %item)\
|
||||
|
||||
rule: give %person %count %item ..=:
|
||||
rule [give %person %count %item] =:
|
||||
%item =: canonicalize %item
|
||||
(%person's inventory)-> %item =: (%person's stock of %item) + %count
|
||||
|
||||
rule: give %person %count %item from %donor ..=:
|
||||
rule [give %person %count %item from %donor] =:
|
||||
%item =: canonicalize %item
|
||||
if ((%donor's stock of %item) < %count):
|
||||
say ".."
|
||||
@ -66,26 +63,19 @@ with secrets:
|
||||
(%person's inventory)->%item =: (%person's stock of %item) + %count
|
||||
(%donor's inventory)->%item =: (%donor's stock of %item) - %count
|
||||
|
||||
rule:
|
||||
%person has %item
|
||||
%person has a %item
|
||||
%person has an %item
|
||||
..=:
|
||||
rule [%person has %item, %person has a %item, %person has an %item] =:
|
||||
(%person's stock of %item) > 0
|
||||
|
||||
rule: rules that change the inventory ..=: [..]
|
||||
rule [rules that change the inventory] =: [..]
|
||||
"give % % %", "give % % % from %"
|
||||
|
||||
rule: rules that view the inventory ..=: [..]
|
||||
rule [rules that view the inventory] =: [..]
|
||||
"inventory", "%'s inventory", "%'s stock of %", "%'s stock of % as str"
|
||||
|
||||
rule: you ..=:
|
||||
rule [you] =:
|
||||
lua expr "(compiler.you or 'Anonymous')"
|
||||
|
||||
rule:
|
||||
make %person %action
|
||||
make %person do %action
|
||||
..=:
|
||||
rule [make %person %action, make %person do %action] =:
|
||||
lua block ".."
|
||||
|do
|
||||
| local old_you = compiler.you
|
||||
@ -100,10 +90,10 @@ say "===================================================="
|
||||
|
||||
# Unanimity for proposals
|
||||
with secrets:
|
||||
rule: pending proposal ..=:
|
||||
rule [pending proposal] =:
|
||||
secret %pending
|
||||
|
||||
rule: propose source %src ..=:
|
||||
rule [propose source %src] =:
|
||||
if (secret %pending):
|
||||
error "A proposal is already pending."
|
||||
secret %pending =: %src
|
||||
@ -111,16 +101,18 @@ with secrets:
|
||||
say ".."
|
||||
|Proposal:
|
||||
|\%src\
|
||||
say "foo"
|
||||
|
||||
macro block: propose %action ..=:
|
||||
|
||||
macro block [propose %action] =:
|
||||
%source =: source code from tree %action
|
||||
".."
|
||||
|compiler:call("propose source %", \repr %source\)
|
||||
|
||||
rule: with everyone's approval do %action ..=:
|
||||
rule [with everyone's approval do %action] =:
|
||||
run %action
|
||||
|
||||
rule: mark %who as approving ..=:
|
||||
rule [mark %who as approving] =:
|
||||
if (not (pending proposal)):
|
||||
say "No action pending"
|
||||
return
|
||||
@ -137,31 +129,21 @@ with secrets:
|
||||
secret %pending =: nil
|
||||
secret %approvals =: nil
|
||||
|
||||
rule: mark %who as rejecting ..=:
|
||||
rule [mark %who as rejecting] =:
|
||||
secret %pending =: nil
|
||||
secret %approvals =: nil
|
||||
|
||||
rule:
|
||||
approve
|
||||
vote yes
|
||||
vote yea
|
||||
..=:
|
||||
rule [approve, vote yes, vote yea] =:
|
||||
mark (you) as approving
|
||||
|
||||
rule:
|
||||
reject
|
||||
vote no
|
||||
vote nay
|
||||
veto
|
||||
disapprove
|
||||
..=:
|
||||
rule [reject, vote no, vote nay, veto, disapprove] =:
|
||||
mark (you) as rejecting
|
||||
|
||||
restrict "with everyone's approval do %" to within "mark % as approving"
|
||||
restrict "mark % as approving" to within "approve"
|
||||
restrict "mark % as rejecting" to within "reject"
|
||||
|
||||
rule: join ..=:
|
||||
rule [join] =:
|
||||
add user (you)
|
||||
say ".."
|
||||
|Welcome to the game, \you\!
|
||||
@ -195,7 +177,7 @@ propose:
|
||||
approve
|
||||
|
||||
propose:
|
||||
rule: fart ..=:
|
||||
rule [fart] =:
|
||||
say "poot"
|
||||
say "fart should have been defined"
|
||||
approve
|
||||
@ -204,7 +186,7 @@ fart
|
||||
|
||||
propose:
|
||||
with secrets:
|
||||
rule: open election %candidates %action ..=:
|
||||
rule [open election %candidates %action] =:
|
||||
if (secret %candidates):
|
||||
error "An election is already in progress."
|
||||
..else:
|
||||
@ -213,25 +195,25 @@ propose:
|
||||
secret %action =: %action
|
||||
secret %winner =: nil
|
||||
|
||||
rule: close the election ..=:
|
||||
rule [close the election] =:
|
||||
secret %candidates =: nil
|
||||
secret %votes =: nil
|
||||
secret %action =: nil
|
||||
secret %winner =: nil
|
||||
|
||||
rule: votes for %candidate ..=:
|
||||
rule [votes for %candidate] =:
|
||||
%votes =: []
|
||||
for %entry in (entries in (secret %votes)):
|
||||
if ((%entry's "value") == %candidate):
|
||||
add (%entry's "key") to %votes
|
||||
%votes
|
||||
|
||||
rule: after winning a fair election do %action ..=:
|
||||
rule [after winning a fair election do %action] =:
|
||||
do %action
|
||||
|
||||
rule: the winner ..=: secret %winner
|
||||
rule [the winner] =: secret %winner
|
||||
|
||||
rule: vote for %candidate ..=:
|
||||
rule [vote for %candidate] =:
|
||||
for %c in (secret %candidates):
|
||||
if (%c == %candidate):
|
||||
go to %candidate-is-legit
|
||||
@ -248,16 +230,16 @@ propose:
|
||||
after winning a fair election do (secret %action)
|
||||
close the election
|
||||
|
||||
rule: rules that change the election ..=: [..]
|
||||
rule [rules that change the election] =: [..]
|
||||
"open election %", "close the election", "vote for %"
|
||||
|
||||
rule: rules that view the election ..=: [..]
|
||||
rule [rules that view the election] =: [..]
|
||||
"votes for %"
|
||||
|
||||
approve
|
||||
|
||||
propose:
|
||||
rule: as bill %action ..=:
|
||||
rule [as bill %action] =:
|
||||
if ((you) == "Anonymous"):
|
||||
make "bill" do %action
|
||||
..else:
|
||||
@ -268,7 +250,7 @@ approve
|
||||
as bill: join
|
||||
|
||||
propose:
|
||||
rule: as dave %action ..=:
|
||||
rule [as dave %action] =:
|
||||
if ((you) == "Anonymous"):
|
||||
make "dave" do %action
|
||||
..else:
|
||||
@ -280,15 +262,15 @@ as bill: approve
|
||||
as dave: join
|
||||
|
||||
propose:
|
||||
rule: declare war ..=: say "WAR!!!"
|
||||
rule [declare war] =: say "WAR!!!"
|
||||
with secrets:
|
||||
secret %president =: nil
|
||||
rule: elect president from %candidates ..=:
|
||||
rule [elect president from %candidates] =:
|
||||
open election %candidates:
|
||||
say ".."|Hail to the chief: \the winner\
|
||||
secret %president =: the winner
|
||||
|
||||
rule: as the president do %action ..=: do %action
|
||||
rule [as the president do %action] =: do %action
|
||||
|
||||
restrict "declare war" to within "as the president do %"
|
||||
approve
|
||||
@ -303,7 +285,7 @@ as dave:
|
||||
declare war
|
||||
|
||||
propose:
|
||||
rule: take a shit ..=: say "shit taken."
|
||||
rule [take a shit] =: say "shit taken."
|
||||
|
||||
approve
|
||||
as bill: approve
|
||||
|
@ -98,24 +98,18 @@ do:
|
||||
|
||||
|
||||
# Function definition:
|
||||
rule:
|
||||
say both %first and also %second
|
||||
..=:
|
||||
rule [say both %first and also %second] =:
|
||||
# Function arguments are accessed just like variables
|
||||
say %first
|
||||
say %second
|
||||
|
||||
# The last line of a function is the return value
|
||||
rule:
|
||||
add %x and %y
|
||||
..=:
|
||||
rule [add %x and %y] =:
|
||||
%result =: %x + %y
|
||||
%result
|
||||
|
||||
# Functions can use "return" to return a value early
|
||||
rule:
|
||||
first fibonacci above %n
|
||||
..=:
|
||||
rule [first fibonacci above %n] =:
|
||||
%f1 =: 0
|
||||
%f2 =: 1
|
||||
repeat:
|
||||
@ -128,7 +122,7 @@ rule:
|
||||
say (first fibonacci above 10)
|
||||
|
||||
# Functions can have aliases, which may or may not have the arguments in different order
|
||||
rule:
|
||||
rule [..]
|
||||
I hate %worse-things more than %better-things
|
||||
I think %worse-things are worse than %better-things
|
||||
I like %better-things more than %worse-things
|
||||
@ -144,9 +138,7 @@ I think "chihuahuas" are worse than "corgis"
|
||||
say both "Hello" and also "again!"
|
||||
|
||||
# Functions can even have their name at the end:
|
||||
rule:
|
||||
%what-she-said is what she said
|
||||
..=:
|
||||
rule [%what-she-said is what she said] =:
|
||||
say %what-she-said
|
||||
say "-- she said"
|
||||
|
||||
@ -154,9 +146,7 @@ rule:
|
||||
|
||||
#.. The language only reserves []{}().,:;% as special characters, so functions and variables
|
||||
can have really funky names!
|
||||
rule:
|
||||
>> %foo-bar$$$^ --> %@@& _~-^-~_~-^ %1 !
|
||||
..=:
|
||||
rule [>> %foo-bar$$$^ --> %@@& _~-^-~_~-^ %1 !] =:
|
||||
say %foo-bar$$$^
|
||||
say %@@&
|
||||
say %1
|
||||
@ -166,9 +156,7 @@ rule:
|
||||
# Math and logic operations are just treated the same as function calls in the syntax
|
||||
say (2 + 3)
|
||||
# So it's easy to define your own operators
|
||||
rule:
|
||||
%a ++ %b
|
||||
..=:
|
||||
rule [%a ++ %b] =:
|
||||
2 * (%a + %b)
|
||||
|
||||
say (2 ++ 3)
|
||||
@ -213,7 +201,7 @@ say both ".."
|
||||
..and also..
|
||||
"-- Abraham Lincoln"
|
||||
|
||||
rule: my favorite number ..=: 21 + 2
|
||||
rule [my favorite number] =: 21 + 2
|
||||
|
||||
# Subexpressions are wrapped in parentheses:
|
||||
say (my favorite number)
|
||||
@ -231,9 +219,7 @@ say ".."
|
||||
number
|
||||
..\, but this time it uses an indented subexpression!
|
||||
|
||||
rule:
|
||||
sing %starting-bottles bottles of beer
|
||||
..=:
|
||||
rule [sing %starting-bottles bottles of beer] =:
|
||||
for %n in (%starting-bottles down through 0):
|
||||
say ".."
|
||||
|\%n if (%n > 0) else "No more"\ \"bottle" if (%n == 1) else "bottles"\ of beer on the wall.
|
||||
@ -260,7 +246,7 @@ any of [0,0,0,0,1,0,0]
|
||||
|
||||
# Macros:
|
||||
# The "lua block %" and "lua expr %" macros can be used to write raw lua code:
|
||||
rule: say the time ..=:
|
||||
rule [say the time] =:
|
||||
lua block ".."
|
||||
|io.write("The OS time is: ")
|
||||
|io.write(tostring(os.time()).."\\n")
|
||||
@ -269,13 +255,13 @@ say ".."|Math expression result is: \lua expr "(1 + 2*3 + 3*4)^2"\
|
||||
|
||||
#.. In the lua environment, "vars" can be used to get local variables/function args, and
|
||||
"compiler" can be used to access the compiler, function defs, and other things
|
||||
rule: square root of %n ..=:
|
||||
rule [square root of %n] =:
|
||||
lua expr "math.sqrt(vars.n)"
|
||||
say ".."|The square root of 2 is \square root of 2\
|
||||
|
||||
# Macros can be defined as functions that take unprocessed syntax trees and return lua code
|
||||
# "macro block %" is for defining macros that produce blocks of code, not values
|
||||
macro block: unless %condition %body ..=: ".."
|
||||
macro block [unless %condition %body] =: ".."
|
||||
|if not (\%condition as lua expr\) then
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
|end
|
||||
@ -285,7 +271,7 @@ unless (1 > 10):
|
||||
say "It looks like a keyword, but there's no magic here!"
|
||||
|
||||
# and "macro %" is for defining macros that produce an expression
|
||||
macro: %value as a boolean ..=: ".."
|
||||
macro [%value as a boolean] =: ".."
|
||||
|(not not (\%value as lua expr\))
|
||||
macro: yep ..=: "true"
|
||||
macro [yep] =: "true"
|
||||
|
||||
|
378
lib/core.nom
378
lib/core.nom
@ -4,11 +4,11 @@ lua block ".."
|
||||
| if kind == "Expression" then
|
||||
| compiler:error("Cannot use rule definitions as an expression.")
|
||||
| end
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec.value, vars)
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec, vars)
|
||||
| return ("compiler:def("..compiler.utils.repr(spec,true)..", "..compiler:tree_to_lua(vars.body, vars)..")"), true
|
||||
|end)
|
||||
|
||||
rule: help %invocation ..=:
|
||||
rule [help %invocation] =:
|
||||
lua block ".."
|
||||
|local fn_info = compiler.defs[vars.invocation]
|
||||
|if not fn_info then
|
||||
@ -20,7 +20,7 @@ rule: help %invocation ..=:
|
||||
# Macros
|
||||
lua block ".."
|
||||
|local add_macro = function(compiler, vars, kind)
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec.value, vars)
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec, vars)
|
||||
| local fn = compiler:tree_to_value(vars.body, vars)
|
||||
| compiler:defmacro(spec, fn, vars.body.src)
|
||||
| return "", true
|
||||
@ -28,7 +28,7 @@ lua block ".."
|
||||
|compiler:defmacro("macro %spec = %body", add_macro)
|
||||
|
|
||||
|local add_macro_block = function(compiler, vars, kind)
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec.value, vars)
|
||||
| local spec = compiler:get_invocations_from_definition(vars.spec, vars)
|
||||
| local fn = compiler:tree_to_value(vars.body, vars)
|
||||
| local wrapper = function(compiler, vars, kind)
|
||||
| if kind == "Expression" then
|
||||
@ -42,13 +42,10 @@ lua block ".."
|
||||
|compiler:defmacro("macro block %spec = %body", add_macro_block)
|
||||
|
||||
# Compiler tools
|
||||
rule:
|
||||
eval %code
|
||||
run %code
|
||||
..=:
|
||||
rule [eval %code, run %code] =:
|
||||
lua expr "compiler:run(vars.code)"
|
||||
|
||||
rule: source code from tree %tree ..=:
|
||||
rule [source code from tree %tree] =:
|
||||
lua block ".."
|
||||
|local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S")
|
||||
|if leading_space then
|
||||
@ -61,47 +58,48 @@ rule: source code from tree %tree ..=:
|
||||
|
||||
%source
|
||||
|
||||
macro: source code %body ..=:
|
||||
repr (source code from tree %body)
|
||||
macro [source code %body] =:
|
||||
lua expr ".."
|
||||
|compiler.utils.repr(compiler:call("source code from tree %", vars.body), true)
|
||||
|
||||
rule: run file %filename ..=:
|
||||
rule [run file %filename] =:
|
||||
lua block ".."
|
||||
|local file = io.open(vars.filename)
|
||||
|return compiler:run(file:read("*a"))
|
||||
|
||||
# Error functions
|
||||
rule: error! ..=:
|
||||
rule [error!] =:
|
||||
lua block ".."
|
||||
|table.remove(compiler.callstack)
|
||||
|compiler:error()
|
||||
|
||||
rule: error %msg ..=:
|
||||
rule [error %msg] =:
|
||||
lua block ".."
|
||||
|table.remove(compiler.callstack)
|
||||
|compiler:error(vars.msg)
|
||||
|
||||
# TODO: Make useful
|
||||
macro: as lua %block ..=:
|
||||
lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, 'Statement'), true)"
|
||||
macro [as lua %block] =:
|
||||
lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value, 'Statement'), true)"
|
||||
|
||||
macro block: show generated lua %block ..=:
|
||||
".."|compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value.value, 'Statement'), true)"\)
|
||||
macro block [show generated lua %block] =:
|
||||
".."|compiler:writeln(\lua expr "compiler.utils.repr(compiler:tree_to_lua(vars.block.value, 'Statement'), true)"\)
|
||||
|
||||
# Macro helper functions
|
||||
rule: %tree as value ..=:
|
||||
rule [%tree as value] =:
|
||||
lua expr ".."
|
||||
|compiler:tree_to_value(vars.tree, vars)
|
||||
|
||||
rule: %tree as lua block ..=:
|
||||
rule [%tree as lua block] =:
|
||||
lua block ".."
|
||||
|return compiler:tree_to_lua(vars.tree, 'Statement'), true
|
||||
|
||||
rule: %tree as lua expr ..=:
|
||||
rule [%tree as lua expr] =:
|
||||
lua expr ".."
|
||||
|compiler:tree_to_lua(vars.tree, 'Expression')
|
||||
|
||||
# Moonscript!
|
||||
macro block: moonscript block %moonscript_code ..=:
|
||||
macro block [moonscript block %moonscript_code] =:
|
||||
lua block ".."
|
||||
|local parse, compile = require('moonscript.parse'), require('moonscript.compile')
|
||||
|local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
|
||||
@ -115,7 +113,7 @@ macro block: moonscript block %moonscript_code ..=:
|
||||
|end
|
||||
|return "do\\n"..lua_code.."\\nend"
|
||||
|
||||
macro: moonscript %moonscript_code ..=:
|
||||
macro [moonscript %moonscript_code] =:
|
||||
lua block ".."
|
||||
|local parse, compile = require('moonscript.parse'), require('moonscript.compile')
|
||||
|local moon_code = compiler:tree_to_value(vars.moonscript_code, vars)
|
||||
@ -130,31 +128,28 @@ macro: moonscript %moonscript_code ..=:
|
||||
|return "(function(compiler, vars)\\n"..lua_code.."\\nend)(compiler, vars)"
|
||||
|
||||
# String functions
|
||||
rule: join %strs ..=:
|
||||
rule [join %strs] =:
|
||||
lua block ".."
|
||||
|local str_bits = {}
|
||||
|for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
|
||||
|return table.concat(str_bits)
|
||||
|
||||
rule: join %strs with glue %glue ..=:
|
||||
rule [join %strs with glue %glue] =:
|
||||
lua block ".."
|
||||
|local str_bits = {}
|
||||
|for i,bit in ipairs(vars.strs) do str_bits[i] = compiler.utils.repr(bit) end
|
||||
|return table.concat(str_bits, vars.glue)
|
||||
|
||||
rule:
|
||||
capitalize %str
|
||||
%str capitalized
|
||||
..=:
|
||||
rule [capitalize %str, %str capitalized] =:
|
||||
lua expr ".."|vars.str:gsub("%l", string.upper, 1)
|
||||
|
||||
# Variable assignment
|
||||
#..
|
||||
macro block: %var = %value ..=:
|
||||
macro block [%var = %value] =:
|
||||
lua block ".."
|
||||
|if vars.var.value.type ~= "Var" then
|
||||
|if vars.var.type ~= "Var" then
|
||||
| compiler:error("Assignment operation has the wrong type for the left hand side. "
|
||||
| .."Expected Var, but got: "..vars.var.value.type)
|
||||
| .."Expected Var, but got: "..vars.var.type)
|
||||
|end
|
||||
".."|\%var as lua expr\ = \%value as lua expr\
|
||||
|
||||
@ -164,130 +159,118 @@ lua block ".."
|
||||
| if kind == "Expression" then
|
||||
| compiler:error("Cannot use an assignment operation as an expression value.")
|
||||
| end
|
||||
| if vars.var.value.type ~= "Var" then
|
||||
| if vars.var.type ~= "Var" then
|
||||
| compiler:error("Assignment operation has the wrong type for the left hand side. "
|
||||
| .."Expected Var, but got: "..vars.var.value.type)
|
||||
| .."Expected Var, but got: "..vars.var.type)
|
||||
| end
|
||||
| if vars.value.value.type ~= "Thunk" then
|
||||
| if vars.rhs.type ~= "Thunk" then
|
||||
| compiler:error("Assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| .."Expected Thunk, but got: "..vars.rhs.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| end
|
||||
| local ret = "do\\n local ret"
|
||||
| ret = ret .. "\\n "..compiler:tree_to_lua(vars.value.value.value, "Statement")
|
||||
| ret = ret .. "\\n "..compiler:tree_to_lua(vars.rhs.value, "Statement")
|
||||
| ret = ret .. "\\n "..callback(compiler:tree_to_lua(vars.var, "Expression"))
|
||||
| return (ret.."\\nend"), true
|
||||
| end
|
||||
|end
|
||||
|compiler:defmacro("%var = %value", helper(function(var) return var.." = ret" end))
|
||||
|compiler:defmacro("%var += %value", helper(function(var) return var.." = "..var.." + ret" end))
|
||||
|compiler:defmacro("%var -= %value", helper(function(var) return var.." = "..var.." - ret" end))
|
||||
|compiler:defmacro("%var *= %value", helper(function(var) return var.." = "..var.." * ret" end))
|
||||
|compiler:defmacro("%var /= %value", helper(function(var) return var.." = "..var.." / ret" end))
|
||||
|compiler:defmacro("%var ^= %value", helper(function(var) return var.." = "..var.." ^ ret" end))
|
||||
|compiler:defmacro("%var and= %value", helper(function(var) return var.." = "..var.." and ret" end))
|
||||
|compiler:defmacro("%var or= %value", helper(function(var) return var.." = "..var.." or ret" end))
|
||||
|compiler:defmacro("%var concat= %value", helper(function(var) return var.." = "..var.." .. ret" end))
|
||||
|compiler:defmacro("%var mod= %value", helper(function(var) return var.." = "..var.." % ret" end))
|
||||
|compiler:defmacro("%var = %rhs", helper(function(var) return var.." = ret" end))
|
||||
|compiler:defmacro("%var += %rhs", helper(function(var) return var.." = "..var.." + ret" end))
|
||||
|compiler:defmacro("%var -= %rhs", helper(function(var) return var.." = "..var.." - ret" end))
|
||||
|compiler:defmacro("%var *= %rhs", helper(function(var) return var.." = "..var.." * ret" end))
|
||||
|compiler:defmacro("%var /= %rhs", helper(function(var) return var.." = "..var.." / ret" end))
|
||||
|compiler:defmacro("%var ^= %rhs", helper(function(var) return var.." = "..var.." ^ ret" end))
|
||||
|compiler:defmacro("%var and= %rhs", helper(function(var) return var.." = "..var.." and ret" end))
|
||||
|compiler:defmacro("%var or= %rhs", helper(function(var) return var.." = "..var.." or ret" end))
|
||||
|compiler:defmacro("%var concat= %rhs", helper(function(var) return var.." = "..var.." .. ret" end))
|
||||
|compiler:defmacro("%var mod= %rhs", helper(function(var) return var.." = "..var.." % ret" end))
|
||||
|
||||
# Operators
|
||||
macro:
|
||||
true
|
||||
yes
|
||||
..=: "true"
|
||||
macro:
|
||||
false
|
||||
no
|
||||
..=: "false"
|
||||
macro:
|
||||
nil
|
||||
null
|
||||
..=: "nil"
|
||||
macro block:
|
||||
nop
|
||||
pass
|
||||
..=: ""
|
||||
macro: %a + %b ..=: ".."|(\%a as lua expr\ + \%b as lua expr\)
|
||||
macro: %a + %b + %c ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\)
|
||||
macro: %a + %b + %c + %d ..=: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\ + \%d as lua expr\)
|
||||
macro: %a - %b ..=: ".."|(\%a as lua expr\ - \%b as lua expr\)
|
||||
macro: %a * %b ..=: ".."|(\%a as lua expr\ * \%b as lua expr\)
|
||||
macro: %a * %b * %c ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\)
|
||||
macro: %a * %b * %c * %d ..=: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\ * \%d as lua expr\)
|
||||
macro: %a / %b ..=: ".."|(\%a as lua expr\ / \%b as lua expr\)
|
||||
macro: %a === %b ..=: ".."|(\%a as lua expr\ == \%b as lua expr\)
|
||||
macro: %a !== %b ..=: ".."|(\%a as lua expr\ ~= \%b as lua expr\)
|
||||
macro: %a < %b ..=: ".."|(\%a as lua expr\ < \%b as lua expr\)
|
||||
macro: %a < %b < %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
|
||||
macro: %a <= %b < %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
|
||||
macro: %a <= %b <= %c ..=: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
|
||||
macro: %a < %b <= %c ..=: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
|
||||
macro: %a > %b > %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
|
||||
macro: %a >= %b > %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
|
||||
macro: %a >= %b >= %c ..=: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
|
||||
macro: %a > %b >= %c ..=: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
|
||||
macro: %a <= %b ..=: ".."|(\%a as lua expr\ <= \%b as lua expr\)
|
||||
macro: %a > %b ..=: ".."|(\%a as lua expr\ > \%b as lua expr\)
|
||||
macro: %a >= %b ..=: ".."|(\%a as lua expr\ >= \%b as lua expr\)
|
||||
macro: %a ^ %b ..=: ".."|(\%a as lua expr\ ^ \%b as lua expr\)
|
||||
macro: %a and %b ..=: ".."|(\%a as lua expr\ and \%b as lua expr\)
|
||||
macro: %a and %b and %c ..=:
|
||||
macro [true, yes] =: "true"
|
||||
macro [false, no] =: "false"
|
||||
macro [nil, null] =: "nil"
|
||||
macro block [nop, pass] =: ""
|
||||
macro [%a + %b] =: ".."|(\%a as lua expr\ + \%b as lua expr\)
|
||||
macro [%a + %b + %c] =: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\)
|
||||
macro [%a + %b + %c + %d] =: ".."|(\%a as lua expr\ + \%b as lua expr\ + \%c as lua expr\ + \%d as lua expr\)
|
||||
macro [%a - %b] =: ".."|(\%a as lua expr\ - \%b as lua expr\)
|
||||
macro [%a * %b] =: ".."|(\%a as lua expr\ * \%b as lua expr\)
|
||||
macro [%a * %b * %c] =: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\)
|
||||
macro [%a * %b * %c * %d] =: ".."|(\%a as lua expr\ * \%b as lua expr\ * \%c as lua expr\ * \%d as lua expr\)
|
||||
macro [%a / %b] =: ".."|(\%a as lua expr\ / \%b as lua expr\)
|
||||
macro [%a === %b] =: ".."|(\%a as lua expr\ == \%b as lua expr\)
|
||||
macro [%a !== %b] =: ".."|(\%a as lua expr\ ~= \%b as lua expr\)
|
||||
macro [%a < %b] =: ".."|(\%a as lua expr\ < \%b as lua expr\)
|
||||
macro [%a < %b < %c] =: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
|
||||
macro [%a <= %b < %c] =: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ < \%c as lua expr\))
|
||||
macro [%a <= %b <= %c] =: ".."|((\%a as lua expr\ <= \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
|
||||
macro [%a < %b <= %c] =: ".."|((\%a as lua expr\ < \%b as lua expr\) and (\%b as lua expr\ <= \%c as lua expr\))
|
||||
macro [%a > %b > %c] =: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
|
||||
macro [%a >= %b > %c] =: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ > \%c as lua expr\))
|
||||
macro [%a >= %b >= %c] =: ".."|((\%a as lua expr\ >= \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
|
||||
macro [%a > %b >= %c] =: ".."|((\%a as lua expr\ > \%b as lua expr\) and (\%b as lua expr\ >= \%c as lua expr\))
|
||||
macro [%a <= %b] =: ".."|(\%a as lua expr\ <= \%b as lua expr\)
|
||||
macro [%a > %b] =: ".."|(\%a as lua expr\ > \%b as lua expr\)
|
||||
macro [%a >= %b] =: ".."|(\%a as lua expr\ >= \%b as lua expr\)
|
||||
macro [%a ^ %b] =: ".."|(\%a as lua expr\ ^ \%b as lua expr\)
|
||||
macro [%a and %b] =: ".."|(\%a as lua expr\ and \%b as lua expr\)
|
||||
macro [%a and %b and %c] =:
|
||||
".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\)
|
||||
macro: %a and %b and %c and %d ..=:
|
||||
macro [%a and %b and %c and %d] =:
|
||||
".."|(\%a as lua expr\ and \%b as lua expr\ and \%c as lua expr\ and \%d as lua expr\)
|
||||
macro: %a or %b ..=: ".."|(\%a as lua expr\ or \%b as lua expr\)
|
||||
macro: %a or %b or %c ..=:
|
||||
macro [%a or %b] =: ".."|(\%a as lua expr\ or \%b as lua expr\)
|
||||
macro [%a or %b or %c] =:
|
||||
".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\)
|
||||
macro: %a or %b or %c or %d ..=:
|
||||
macro [%a or %b or %c or %d] =:
|
||||
".."|(\%a as lua expr\ or \%b as lua expr\ or \%c as lua expr\ or \%d as lua expr\)
|
||||
macro: %a mod %b ..=: ".."|(\%a as lua expr\ mod \%b as lua expr\)
|
||||
macro: - %a ..=: ".."|-(\%a as lua expr\)
|
||||
macro: not %a ..=: ".."|not (\%a as lua expr\)
|
||||
macro [%a mod %b] =: ".."|(\%a as lua expr\ mod \%b as lua expr\)
|
||||
macro [- %a] =: ".."|-(\%a as lua expr\)
|
||||
macro [not %a] =: ".."|not (\%a as lua expr\)
|
||||
|
||||
rule: %a == %b ..=:
|
||||
rule [%a == %b] =:
|
||||
lua expr "((vars.a == vars.b) or compiler.utils.equivalent(vars.a, vars.b))"
|
||||
rule: %a != %b ..=:
|
||||
rule [%a != %b] =:
|
||||
lua expr "((vars.a ~= vars.b) or not compiler.utils.equivalent(vars.a, vars.b))"
|
||||
|
||||
macro: repr %obj ..=:
|
||||
macro [repr %obj] =:
|
||||
".."|compiler.utils.repr(\%obj as lua expr\, true)
|
||||
|
||||
macro: say %str ..=:
|
||||
macro [say %str] =:
|
||||
".."|compiler:writeln(compiler.utils.repr(\%str as lua expr\))
|
||||
|
||||
# Control flow
|
||||
rule: do %action ..=:
|
||||
rule [do %action] =:
|
||||
lua expr "vars.action(compiler, setmetatable({}, {__index=vars}))"
|
||||
|
||||
macro block: return %return_value ..=:
|
||||
macro block [return %return_value] =:
|
||||
lua block ".."
|
||||
|if vars.return_value.value.type ~= "Thunk" then
|
||||
|if vars.return_value.type ~= "Thunk" then
|
||||
| compiler:error("Assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.return_value.value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| .."Expected Thunk, but got: "..vars.return_value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
|end
|
||||
".."|do
|
||||
| local ret
|
||||
| \lua expr "compiler:tree_to_lua(vars.return_value.value.value, 'Statement')"\
|
||||
| \lua expr "compiler:tree_to_lua(vars.return_value.value, 'Statement')"\
|
||||
| return ret
|
||||
|end
|
||||
|
||||
macro block: return ..=:
|
||||
macro block [return] =:
|
||||
"return nil"
|
||||
|
||||
# Conditionals
|
||||
macro block: if %condition %if_body ..=:
|
||||
macro block [if %condition %if_body] =:
|
||||
".."|if \%condition as lua expr\ then
|
||||
| \(lua expr "vars.if_body.value.value") as lua block\
|
||||
| \(lua expr "vars.if_body.value") as lua block\
|
||||
|end
|
||||
|
||||
macro block: if %condition %if_body else %else_body ..=:
|
||||
macro block [if %condition %if_body else %else_body] =:
|
||||
".."|if \%condition as lua expr\ then
|
||||
| \(lua expr "vars.if_body.value.value") as lua block\
|
||||
| \(lua expr "vars.if_body.value") as lua block\
|
||||
|else
|
||||
| \(lua expr "vars.else_body.value.value") as lua block\
|
||||
| \(lua expr "vars.else_body.value") as lua block\
|
||||
|end
|
||||
|
||||
# Ternary operator
|
||||
macro: %if_expr if %condition else %else_expr ..=:
|
||||
macro [%if_expr if %condition else %else_expr] =:
|
||||
".."|(function(compiler, vars)
|
||||
| if \%condition as lua expr\ then
|
||||
| return \%if_expr as lua expr\
|
||||
@ -296,9 +279,15 @@ macro: %if_expr if %condition else %else_expr ..=:
|
||||
| end
|
||||
|end)(compiler, vars)
|
||||
|
||||
# Switch statement/multi-branch if
|
||||
macro block [when %body] =:
|
||||
%result =: ""
|
||||
%result
|
||||
|
||||
|
||||
# Loop control flow
|
||||
macro block: break ..=: "break"
|
||||
macro block: continue ..=: "continue"
|
||||
macro block [break] =: "break"
|
||||
macro block [continue] =: "continue"
|
||||
# TODO: add labeled break/continue?
|
||||
|
||||
# GOTOs
|
||||
@ -319,29 +308,29 @@ lua block ".."
|
||||
|end
|
||||
|
|
||||
|compiler:defmacro("-> %label", function(compiler, vars, kind)
|
||||
| return "::"..lua_label(vars.label.value).."::", true
|
||||
| return "::"..lua_label(vars.label).."::", true
|
||||
|end)
|
||||
|compiler:defmacro("go to %label", function(compiler, vars, kind)
|
||||
| return "goto "..lua_label(vars.label.value), true
|
||||
| return "goto "..lua_label(vars.label), true
|
||||
|end)
|
||||
|
||||
# While loops
|
||||
macro block: repeat %body ..=:
|
||||
macro block [repeat %body] =:
|
||||
".."|while true do
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
| \(lua expr "vars.body.value") as lua block\
|
||||
|end
|
||||
macro block: repeat while %condition %body ..=:
|
||||
macro block [repeat while %condition %body] =:
|
||||
".."|while \%condition as lua expr\ do
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
| \(lua expr "vars.body.value") as lua block\
|
||||
|end
|
||||
macro block: repeat until %condition %body ..=:
|
||||
macro block [repeat until %condition %body] =:
|
||||
".."|while not (\%condition as lua expr\) do
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
| \(lua expr "vars.body.value") as lua block\
|
||||
|end
|
||||
|
||||
# For loops
|
||||
macro block: for %var in %iterable %body ..=:
|
||||
%var-type =: lua expr "vars.var.value.type"
|
||||
macro block [for %var in %iterable %body] =:
|
||||
%var-type =: lua expr "vars.var.type"
|
||||
if (%var-type != "Var"):
|
||||
error ".."
|
||||
|For loop has the wrong type for the loop variable. Expected Var, but got: \%var-type\
|
||||
@ -349,22 +338,22 @@ macro block: for %var in %iterable %body ..=:
|
||||
".."|local old_loopval = \%var-code\
|
||||
|for i,value in ipairs(\%iterable as lua expr\) do
|
||||
| \%var-code\ = value
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
| \(lua expr "vars.body.value") as lua block\
|
||||
|end
|
||||
|\%var-code\ = old_loopval
|
||||
|
||||
macro block: for all %iterable %body ..=:
|
||||
macro block [for all %iterable %body] =:
|
||||
".."|local old_loopval = vars.it
|
||||
|for i,value in ipairs(\%iterable as lua expr\) do
|
||||
| vars.it = value
|
||||
| \(lua expr "vars.body.value.value") as lua block\
|
||||
| \(lua expr "vars.body.value") as lua block\
|
||||
|end
|
||||
|vars.it = old_loopval
|
||||
|
||||
# List Comprehension
|
||||
# TODO: maybe make this lazy, or a lazy version?
|
||||
macro: %expression for %var in %iterable ..=:
|
||||
%var-type =: lua expr "vars.var.value.type"
|
||||
macro [%expression for %var in %iterable] =:
|
||||
%var-type =: lua expr "vars.var.type"
|
||||
if (%var-type != "Var"):
|
||||
error ".."
|
||||
|List comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\
|
||||
@ -378,7 +367,7 @@ macro: %expression for %var in %iterable ..=:
|
||||
| return comprehension
|
||||
|end)(game, setmetatable({}, {__index=vars}))
|
||||
|
||||
macro: %expression for all %iterable ..=:
|
||||
macro [%expression for all %iterable] =:
|
||||
".."|(function(game, vars)
|
||||
| local comprehension = {}
|
||||
| for i,value in ipairs(\%iterable as lua expr\) do
|
||||
@ -389,8 +378,8 @@ macro: %expression for all %iterable ..=:
|
||||
|end)(game, setmetatable({}, {__index=vars}))
|
||||
|
||||
# Dict comprehension
|
||||
macro: %key -> %value for %var in %iterable ..=:
|
||||
%var-type =: lua expr "vars.var.value.type"
|
||||
macro [%key -> %value for %var in %iterable] =:
|
||||
%var-type =: lua expr "vars.var.type"
|
||||
if (%var-type != "Var"):
|
||||
error ".."
|
||||
|Dict comprehension has the wrong type for the loop variable. Expected Var, but got: \%var-type\
|
||||
@ -404,7 +393,7 @@ macro: %key -> %value for %var in %iterable ..=:
|
||||
| return comprehension
|
||||
|end)(game, setmetatable({}, {__index=vars}))
|
||||
|
||||
macro: %key -> %value for all %iterable ..=:
|
||||
macro [%key -> %value for all %iterable] =:
|
||||
".."|(function(game, vars)
|
||||
| local comprehension = {}
|
||||
| for i,value in ipairs(\%iterable as lua expr\) do
|
||||
@ -415,136 +404,94 @@ macro: %key -> %value for all %iterable ..=:
|
||||
|end)(game, setmetatable({}, {__index=vars}))
|
||||
|
||||
# Number ranges
|
||||
rule: %start up to %stop ..=:
|
||||
rule [%start up to %stop] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop-1)"
|
||||
|
||||
rule:
|
||||
%start thru %stop
|
||||
%start through %stop
|
||||
..=:
|
||||
rule [%start thru %stop, %start through %stop] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop)"
|
||||
|
||||
rule: %start down to %stop ..=:
|
||||
rule [%start down to %stop] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop+1,-1)"
|
||||
|
||||
rule:
|
||||
%start down thru %stop
|
||||
%start down through %stop
|
||||
..=:
|
||||
rule [%start down thru %stop, %start down through %stop] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop,-1)"
|
||||
|
||||
rule: %start up to %stop via %step ..=:
|
||||
rule [%start up to %stop via %step] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop-1,vars.step)"
|
||||
|
||||
rule:
|
||||
%start thru %stop via %step
|
||||
%start through %stop via %step
|
||||
..=:
|
||||
rule [%start thru %stop via %step, %start through %stop via %step] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop,vars.step)"
|
||||
|
||||
rule: %start down to %stop via %step ..=:
|
||||
rule [%start down to %stop via %step] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop+1,-vars.step)"
|
||||
|
||||
rule:
|
||||
%start down thru %stop via %step
|
||||
%start down through %stop via %step
|
||||
..=:
|
||||
rule [%start down thru %stop via %step, %start down through %stop via %step] =:
|
||||
lua expr "compiler.utils.range(vars.start,vars.stop,-vars.step)"
|
||||
|
||||
# Common utility functions
|
||||
rule: random number ..=: lua expr "math.random()"
|
||||
rule: sum of %items ..=: lua expr "compiler.utils.sum(vars.items)"
|
||||
rule: product of %items ..=: lua expr "compiler.utils.product(vars.items)"
|
||||
rule: all of %items ..=: lua expr "compiler.utils.all(vars.items)"
|
||||
rule: any of %items ..=: lua expr "compiler.utils.any(vars.items)"
|
||||
rule:
|
||||
avg of %items
|
||||
average of %items
|
||||
..=: lua expr "(compiler.utils.sum(vars.items)/#vars.items)"
|
||||
rule:
|
||||
min of %items
|
||||
smallest of %items
|
||||
lowest of %items
|
||||
..=:
|
||||
rule [random number] =: lua expr "math.random()"
|
||||
rule [sum of %items] =: lua expr "compiler.utils.sum(vars.items)"
|
||||
rule [product of %items] =: lua expr "compiler.utils.product(vars.items)"
|
||||
rule [all of %items] =: lua expr "compiler.utils.all(vars.items)"
|
||||
rule [any of %items] =: lua expr "compiler.utils.any(vars.items)"
|
||||
rule [avg of %items, average of %items] =:
|
||||
lua expr "(compiler.utils.sum(vars.items)/#vars.items)"
|
||||
rule [min of %items, smallest of %items, lowest of %items] =:
|
||||
lua expr "compiler.utils.min(vars.items)"
|
||||
rule:
|
||||
max of %items
|
||||
biggest of %items
|
||||
largest of %items
|
||||
highest of %items
|
||||
..=:
|
||||
rule [max of %items, biggest of %items, largest of %items, highest of %items] =:
|
||||
lua expr "compiler.utils.min(vars.items)"
|
||||
|
||||
rule: min of %items with respect to %keys ..=:
|
||||
rule [min of %items with respect to %keys] =:
|
||||
lua expr "compiler.utils.min(vars.items, vars.keys)"
|
||||
rule: max of %items with respect to %keys ..=:
|
||||
rule [max of %items with respect to %keys] =:
|
||||
lua expr "compiler.utils.max(vars.items, vars.keys)"
|
||||
|
||||
# List/dict functions
|
||||
macro:
|
||||
%list's %index
|
||||
%index st in %list
|
||||
%index nd in %list
|
||||
%index rd in %list
|
||||
%index th in %list
|
||||
%index in %list
|
||||
%list -> %index
|
||||
macro [..]
|
||||
%list's %index, %index st in %list, %index nd in %list, %index rd in %list
|
||||
%index th in %list, %index in %list, %list -> %index
|
||||
..=:
|
||||
".."|\%list as lua expr\[\%index as lua expr\]
|
||||
|
||||
macro block:
|
||||
%list's %index = %value
|
||||
%index st in %list = %value
|
||||
%index nd in %list = %value
|
||||
%index rd in %list = %value
|
||||
%index th in %list = %value
|
||||
%index in %list = %value
|
||||
%list -> %index = %value
|
||||
macro block [..]
|
||||
%list's %index = %new_value, %index st in %list = %new_value, %index nd in %list = %new_value
|
||||
%index rd in %list = %new_value, %index th in %list = %new_value, %index in %list = %new_value
|
||||
%list -> %index = %new_value
|
||||
..=:
|
||||
lua block ".."
|
||||
|if vars.value.value.type ~= "Thunk" then
|
||||
|if vars.new_value.type ~= "Thunk" then
|
||||
| compiler:error("Dict assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| .."Expected Thunk, but got: "..vars.new_value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
|end
|
||||
".."|do
|
||||
| local ret
|
||||
| \lua expr "compiler:tree_to_lua(vars.value.value.value, 'Statement')"\
|
||||
| \lua expr "compiler:tree_to_lua(vars.new_value.value, 'Statement')"\
|
||||
| \%list as lua expr\[\%index as lua expr\] = ret
|
||||
|end
|
||||
|
||||
macro:
|
||||
append %item to %list
|
||||
add %item to %list
|
||||
..=:
|
||||
macro [append %item to %list, add %item to %list] =:
|
||||
".."|table.insert(\%list as lua expr\, \%item as lua expr\)
|
||||
|
||||
rule: flatten %lists ..=:
|
||||
rule [flatten %lists] =:
|
||||
%flat =: []
|
||||
for %list in %lists:
|
||||
for %item in %list:
|
||||
add %item to %flat
|
||||
%flat
|
||||
|
||||
macro:
|
||||
%item is in %list
|
||||
%list contains %item
|
||||
..=:
|
||||
macro [%item is in %list, %list contains %item] =:
|
||||
".."|(\%list as lua expr\[\%index as lua expr\] ~= nil)
|
||||
|
||||
macro:
|
||||
length of %list
|
||||
size of %list
|
||||
number of %list
|
||||
..=:
|
||||
macro [length of %list, size of %list, number of %list] =:
|
||||
".."|#(\%list as lua expr\)
|
||||
|
||||
rule: dict %items ..=:
|
||||
rule [dict %items] =:
|
||||
%dict =: []
|
||||
for %pair in %items:
|
||||
lua block "vars.dict[vars.pair[1]] = vars.pair[2]"
|
||||
return: %dict
|
||||
|
||||
rule: entries in %dict ..=:
|
||||
rule [entries in %dict] =:
|
||||
lua block ".."
|
||||
|local items = {}
|
||||
|for k,v in pairs(vars.dict) do
|
||||
@ -553,7 +500,7 @@ rule: entries in %dict ..=:
|
||||
|return items
|
||||
|
||||
# Permission functions
|
||||
rule: restrict %fn to within %whitelist ..=:
|
||||
rule [restrict %fn to within %whitelist] =:
|
||||
lua block ".."
|
||||
|local fns = compiler:get_invocations(vars.fn)
|
||||
|local whitelist = compiler:get_invocations(vars.whitelist)
|
||||
@ -575,7 +522,7 @@ rule: restrict %fn to within %whitelist ..=:
|
||||
| end
|
||||
|end
|
||||
|
||||
rule: allow %whitelist to use %fn ..=:
|
||||
rule [allow %whitelist to use %fn] =:
|
||||
lua block ".."
|
||||
|local fns = compiler:get_invocations(vars.fn)
|
||||
|local whitelist = compiler:get_invocations(vars.whitelist)
|
||||
@ -599,7 +546,7 @@ rule: allow %whitelist to use %fn ..=:
|
||||
| end
|
||||
|end
|
||||
|
||||
rule: forbid %blacklist to use %fn ..=:
|
||||
rule [forbid %blacklist to use %fn] =:
|
||||
lua block ".."
|
||||
|local fns = compiler:get_invocations(vars.fn)
|
||||
|local blacklist = compiler:get_invocations(vars.blacklist)
|
||||
@ -622,8 +569,11 @@ rule: forbid %blacklist to use %fn ..=:
|
||||
|end
|
||||
|
||||
# For unit testing
|
||||
macro block: test %code yields %expected ..=:
|
||||
%generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value.value), true)"
|
||||
macro [parse tree %code] =:
|
||||
lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value), true)"
|
||||
|
||||
macro block [test %code yields %expected] =:
|
||||
%generated =: lua expr "compiler.utils.repr(compiler:stringify_tree(vars.code.value), true)"
|
||||
%expected =: %expected as lua expr
|
||||
if (%generated != %expected):
|
||||
say "Test failed!"
|
||||
|
@ -18,18 +18,18 @@ with secrets:
|
||||
| return key
|
||||
|end})
|
||||
|
||||
rule: the plural of %singular is %plural ..=:
|
||||
rule [the plural of %singular is %plural] =:
|
||||
(secret %plurals)->%singular =: %plural
|
||||
(secret %singulars)->%plural =: %singular
|
||||
(secret %canonicals)->%plural =: %singular
|
||||
|
||||
rule: singular %plural ..=:
|
||||
rule [singular %plural] =:
|
||||
%plural in (secret %singulars)
|
||||
|
||||
rule: plural %singular ..=:
|
||||
rule [plural %singular] =:
|
||||
%singular in (secret %plurals)
|
||||
|
||||
rule: canonicalize %item-name ..=:
|
||||
rule [canonicalize %item-name] =:
|
||||
%item-name in (secret %canonicals)
|
||||
|
||||
rule: rules that change plurals ..=: ["the plural of % is %"]
|
||||
rule [rules that change plurals] =: ["the plural of % is %"]
|
||||
|
@ -1,41 +1,34 @@
|
||||
require "lib/core.nom"
|
||||
|
||||
macro block: with secrets %block
|
||||
..=: ".."
|
||||
macro block [with secrets %block] =: ".."
|
||||
|local secrets = {}
|
||||
|\((%block's "value")'s "value") as lua block\
|
||||
|\(%block's "value") as lua block\
|
||||
|
||||
macro block: with secrets as %secret_name %block
|
||||
..=: ".."
|
||||
macro block [with secrets as %secret_name %block] =: ".."
|
||||
|local \%secret_name as value\ = {}
|
||||
|\((%block's "value")'s "value") as lua block\
|
||||
|\(%block's "value") as lua block\
|
||||
|
||||
macro: secrets
|
||||
..=: "secrets"
|
||||
# Access the lua variable that should be within scope
|
||||
macro [secrets] =: "secrets"
|
||||
|
||||
macro:
|
||||
secret %key
|
||||
secret value of %key
|
||||
secret value for %key
|
||||
..=:
|
||||
if (((%key ->"value")->"type") != "Var"):
|
||||
macro [secret %key, secret value of %key, secret value for %key] =:
|
||||
if ((%key's "type") != "Var"):
|
||||
error ".."
|
||||
|Wrong type, expected Var, but got: \(%key ->"value")->"type"\
|
||||
".."|secrets[\repr ((%key -> "value")->"value")\]
|
||||
|Wrong type, expected Var, but got: \%key's "type"\
|
||||
".."|secrets[\repr (%key's "value")\]
|
||||
|
||||
macro block: secret %key = %value
|
||||
..=:
|
||||
macro block [secret %key = %new_value] =:
|
||||
lua block ".."
|
||||
|if vars.key.value.type ~= "Var" then
|
||||
|if vars.key.type ~= "Var" then
|
||||
| compiler:error("Assignment operation has the wrong type for the left hand side. "
|
||||
| .."Expected Var, but got: "..vars.key.value.type)
|
||||
| .."Expected Var, but got: "..vars.key.type)
|
||||
|end
|
||||
|if vars.value.value.type ~= "Thunk" then
|
||||
|if vars.new_value.type ~= "Thunk" then
|
||||
| compiler:error("Assignment operation has the wrong type for the right hand side. "
|
||||
| .."Expected Thunk, but got: "..vars.value.value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
| .."Expected Thunk, but got: "..vars.new_value.type.."\\nMaybe you used '=' instead of '=:'?")
|
||||
|end
|
||||
".."|do
|
||||
| local ret
|
||||
| \lua expr "compiler:tree_to_lua(vars.value.value.value, 'Statement')"\
|
||||
| secrets[\repr ((%key -> "value")->"value")\] = ret
|
||||
| \lua expr "compiler:tree_to_lua(vars.new_value.value, 'Statement')"\
|
||||
| secrets[\repr (%key's "value")\] = ret
|
||||
|end
|
||||
|
307
nomsu.moon
307
nomsu.moon
@ -16,88 +16,14 @@ INDENT = " "
|
||||
lpeg.setmaxstack 10000 -- whoa
|
||||
{:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
|
||||
|
||||
get_line_indentation = (line)->
|
||||
indent_amounts = {[" "]:1, ["\t"]:4}
|
||||
with sum = 0
|
||||
leading_space = line\match("[\t ]*")
|
||||
for c in leading_space\gmatch "[\t ]"
|
||||
sum += indent_amounts[c]
|
||||
|
||||
make_parser = (lingo, extra_definitions)->
|
||||
indent_stack = {0}
|
||||
push = (n)-> table.insert indent_stack, n
|
||||
pop = ()-> table.remove indent_stack
|
||||
check_indent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces <= indent_stack[#indent_stack] then return nil
|
||||
push num_spaces
|
||||
return end_pos
|
||||
check_dedent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces >= indent_stack[#indent_stack] then return nil
|
||||
pop!
|
||||
return end_pos
|
||||
check_nodent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces != indent_stack[#indent_stack] then return nil
|
||||
return end_pos
|
||||
|
||||
wordchar = P(1)-S(' \t\n\r%#:;,.{}[]()"\\')
|
||||
nl = P("\n")
|
||||
whitespace = S(" \t")^1
|
||||
blank_line = whitespace^-1 * nl
|
||||
line_comment = re.compile([=[ "#" [^%nl]* ]=], {:nl})
|
||||
block_comment = re.compile([=[
|
||||
"#.." (!%nl .)* (%indent (!%dedent %nl [^%nl]*)*)
|
||||
]=], {:nl, :whitespace,
|
||||
indent:#(nl * blank_line^0 * Cmt(S(" \t")^0, check_indent)),
|
||||
dedent:#(nl * blank_line^0 * Cmt(S(" \t")^0, check_dedent)),
|
||||
new_line:nl * blank_line^0 * Cmt(S(" \t")^0, check_nodent)})
|
||||
blank_line = ((Cmt(whitespace^-1, check_nodent) * (block_comment + line_comment))^-1 + whitespace^-1) * nl
|
||||
defs =
|
||||
:wordchar, :nl, ws:whitespace, :blank_line, :block_comment, :line_comment
|
||||
eol: #nl + (P("")-P(1))
|
||||
word_boundary: whitespace^-1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl)^0 * P(".."))
|
||||
indent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_indent))
|
||||
dedent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_dedent))
|
||||
new_line: nl * blank_line^0 * Cmt(whitespace^-1, check_nodent)
|
||||
error_handler: (src,pos,errors)->
|
||||
line_no = 1
|
||||
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
||||
err_pos = #src - #errors + 1
|
||||
if errors\sub(1,1) == "\n"
|
||||
-- Indentation error
|
||||
err_pos += #errors\match("[ \t]*", 2)
|
||||
start_of_err_line = err_pos
|
||||
while src\sub(start_of_err_line, start_of_err_line) != "\n" do start_of_err_line -= 1
|
||||
start_of_prev_line = start_of_err_line - 1
|
||||
while src\sub(start_of_prev_line, start_of_prev_line) != "\n" do start_of_prev_line -= 1
|
||||
|
||||
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
||||
|
||||
pointer = ("-")\rep(err_pos - start_of_err_line + 0) .. "^"
|
||||
error("\nParse error on line #{line_no}:\n\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n")
|
||||
|
||||
if extra_definitions
|
||||
for k,v in pairs(extra_definitions) do defs[k] = v
|
||||
|
||||
setmetatable(defs, {
|
||||
__index: (t,key)->
|
||||
fn = (src, value, errors)->
|
||||
token = {type: key, :src, :value, :errors}
|
||||
return token
|
||||
t[key] = fn
|
||||
return fn
|
||||
})
|
||||
return re.compile lingo, defs
|
||||
|
||||
class NomsuCompiler
|
||||
new:(parent)=>
|
||||
@write = (...)=> io.write(...)
|
||||
@defs = setmetatable({}, {__index:parent and parent.defs})
|
||||
@callstack = {}
|
||||
@debug = false
|
||||
@initialize_core!
|
||||
@write = (...)=> io.write(...)
|
||||
@utils = utils
|
||||
@loaded_files = {}
|
||||
|
||||
@ -141,23 +67,28 @@ class NomsuCompiler
|
||||
@defs[invocation] = fn_info
|
||||
|
||||
get_invocations_from_definition:(def, vars)=>
|
||||
if def.type == "String" or def.type == "List"
|
||||
if def.type == "String"
|
||||
return @tree_to_value(def, vars)
|
||||
|
||||
if def.type != "Thunk"
|
||||
@error "Trying to get invocations from #{def.type}, but expected Thunk."
|
||||
if def.type != "List"
|
||||
error "DEF IS: #{utils.repr(def)}"
|
||||
@error "Trying to get invocations from #{def.type}, but expected List or String."
|
||||
|
||||
invocations = {}
|
||||
for statement in *def.value.value
|
||||
if statement.value.type != "FunctionCall"
|
||||
@error "Invalid statement type: #{statement.value.type}, expected FunctionCall"
|
||||
for item in *def.value
|
||||
if item.type == "String"
|
||||
table.insert invocations, @tree_to_value(item, vars)
|
||||
continue
|
||||
if item.type != "FunctionCall"
|
||||
@error "Invalid list item: #{item.type}, expected FunctionCall or String"
|
||||
name_bits = {}
|
||||
for token in *statement.value.value
|
||||
for token in *item.value
|
||||
if token.type == "Word"
|
||||
table.insert name_bits, token.value
|
||||
elseif token.value.type == "Var"
|
||||
table.insert name_bits, token.value.src
|
||||
elseif token.type == "Var"
|
||||
table.insert name_bits, token.src
|
||||
else
|
||||
@error "Unexpected token type in definition: #{token.value.type} (expected Word or Var)"
|
||||
@error "Unexpected token type in definition: #{token.type} (expected Word or Var)"
|
||||
table.insert invocations, table.concat(name_bits, " ")
|
||||
return invocations
|
||||
|
||||
@ -189,11 +120,11 @@ class NomsuCompiler
|
||||
for invocation in *invocations
|
||||
@defs[invocation] = fn_info
|
||||
|
||||
run: (text)=>
|
||||
run: (text, filename)=>
|
||||
if @debug
|
||||
@writeln "RUNNING TEXT:\n#{text}"
|
||||
-- This will execute each chunk as it goes along
|
||||
code, retval = @compile(text)
|
||||
code, retval = @compile(text, filename)
|
||||
if @debug
|
||||
@writeln "\nGENERATED LUA CODE:\n#{code}"
|
||||
@writeln "\nPRODUCED RETURN VALUE:\n#{retval}"
|
||||
@ -224,70 +155,145 @@ class NomsuCompiler
|
||||
error("Failed to compile generated code:\n#{str}\n\n#{err}")
|
||||
return (lua_thunk!)(self, {})
|
||||
|
||||
parse: (str)=>
|
||||
parse: (str, filename)=>
|
||||
if @debug
|
||||
@writeln("PARSING:\n#{str}")
|
||||
|
||||
get_line_indentation = (line)->
|
||||
indent_amounts = {[" "]:1, ["\t"]:4}
|
||||
with sum = 0
|
||||
leading_space = line\match("[\t ]*")
|
||||
for c in leading_space\gmatch "[\t ]"
|
||||
sum += indent_amounts[c]
|
||||
|
||||
indent_stack = {0}
|
||||
check_indent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces > indent_stack[#indent_stack]
|
||||
table.insert(indent_stack, num_spaces)
|
||||
return end_pos
|
||||
check_dedent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces < indent_stack[#indent_stack]
|
||||
table.remove(indent_stack)
|
||||
return end_pos
|
||||
check_nodent = (subject,end_pos,spaces)->
|
||||
num_spaces = get_line_indentation(spaces)
|
||||
if num_spaces == indent_stack[#indent_stack]
|
||||
return end_pos
|
||||
|
||||
lingo = [=[
|
||||
file <- ({ {| %blank_line* {:body: block :} %blank_line* (errors)? |} }) -> File
|
||||
errors <- (({.+}) => error_handler)
|
||||
block <- ({ {| statement (%new_line statement)* |} }) -> Block
|
||||
statement <- ({ (functioncall / expression) }) -> Statement
|
||||
one_liner <- ({ {|
|
||||
(({
|
||||
(({ {|
|
||||
(expression (%word_boundary fn_bit)+) / (word (%word_boundary fn_bit)*)
|
||||
|} }) -> FunctionCall)
|
||||
/ (expression)
|
||||
}) -> Statement)
|
||||
|} }) -> Block
|
||||
file <- ({ {| shebang? {:body: block :} %nl* (({.+} ("" -> "Unexpected end of file")) => error)? |} }) -> File
|
||||
|
||||
functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall
|
||||
fn_bit <- (expression / word)
|
||||
fn_bits <-
|
||||
((".." %ws? %line_comment? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?)
|
||||
/ (%new_line ".." fn_bit (%word_boundary fn_bits)?)
|
||||
/ (fn_bit (%word_boundary fn_bits)?))
|
||||
indented_fn_bits <-
|
||||
fn_bit ((%new_line / %word_boundary) indented_fn_bits)?
|
||||
shebang <- "#!" [^%nl]* %nl
|
||||
|
||||
thunk <-
|
||||
({ ":" %ws? %line_comment?
|
||||
((%indent %new_line block ((%dedent (%new_line "..")?) / errors))
|
||||
/ (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk
|
||||
block <- ({ {|
|
||||
(ignored_line %nl)*
|
||||
line_of_statements (nodent line_of_statements)*
|
||||
(%nl ignored_line)* |} }) -> Block
|
||||
inline_block <- ({ {| inline_line_of_statements |} }) -> Block
|
||||
|
||||
line_of_statements <- statement (%ws? ";" %ws? statement)*
|
||||
inline_line_of_statements <- inline_statement (%ws? ";" %ws? inline_statement)*
|
||||
|
||||
statement <- ({ functioncall / expression }) -> Statement
|
||||
inline_statement <- ({ inline_functioncall / expression }) -> Statement
|
||||
|
||||
expression <- (
|
||||
longstring / string / number / variable / list / thunk / block_functioncall
|
||||
/ ("(" %ws? (inline_thunk / inline_functioncall) %ws? ")"))
|
||||
|
||||
-- Function calls need at least one word in them
|
||||
functioncall <- ({ {|
|
||||
(expression (dotdot / tok_gap))* word ((dotdot / tok_gap) (expression / word))*
|
||||
|} }) -> FunctionCall
|
||||
inline_functioncall <- ({ {|
|
||||
(expression tok_gap)* word (tok_gap (expression / word))*
|
||||
|} }) -> FunctionCall
|
||||
block_functioncall <- "(..)" indent functioncall (dedent / (({.+} ("" -> "Error while parsing block function call")) => error))
|
||||
|
||||
word <- ({ !number {%wordchar (!"'" %wordchar)*} }) -> Word
|
||||
expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression
|
||||
|
||||
thunk <- ({ ":" ((indent block (dedent / (({.+} ("" -> "Error while parsing thunk")) => error)))
|
||||
/ (%ws? inline_block)) }) -> Thunk
|
||||
inline_thunk <- ({ ":" %ws? inline_block }) -> Thunk
|
||||
|
||||
string <- ({ (!longstring) '"' {(("\" [^%nl]) / [^"%nl])*} '"' }) -> String
|
||||
|
||||
longstring <- ({ '".."' %ws?
|
||||
{|
|
||||
(("|" {| ({("\\" / (!string_interpolation [^%nl]))+} / string_interpolation)* |})
|
||||
/ %line_comment)?
|
||||
(%indent
|
||||
(%new_line "|" {|
|
||||
({("\\" / (!string_interpolation [^%nl]))+} / string_interpolation)*
|
||||
|})+
|
||||
((%dedent (%new_line '..')?) / errors))?
|
||||
|}}) -> Longstring
|
||||
string_interpolation <- "\" %ws? (functioncall / expression) %ws? "\"
|
||||
number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
|
||||
{| (longstring_line (indent
|
||||
longstring_line (nodent longstring_line)*
|
||||
(dedent / longstring_error))?)
|
||||
/(indent
|
||||
longstring_line (nodent longstring_line)*
|
||||
(dedent / longstring_error)) |} }) -> Longstring
|
||||
longstring_line <- "|" {| ({("\\" / (!string_interpolation [^%nl]))+} / string_interpolation)* |}
|
||||
longstring_error <- (({.+} ("" -> "Error while parsing Longstring")) => error)
|
||||
string_interpolation <- "\" %ws? (inline_functioncall / expression) %ws? "\"
|
||||
|
||||
number <- ({ {'-'? ([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+) } }) -> Number
|
||||
|
||||
-- Hack to allow %foo's to parse as "%foo" and "'s" separately
|
||||
variable <- ({ ("%" {%wordchar (!"'" %wordchar)*}) }) -> Var
|
||||
|
||||
subexpression <-
|
||||
("(" %ws? (functioncall / expression) %ws? ")")
|
||||
/ ("(..)" %ws? %line_comment? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?)
|
||||
|
||||
list <- ({ {|
|
||||
("[..]" %ws? %line_comment? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors))
|
||||
/ ("[" %ws? (list_items ","?)? %ws?"]")
|
||||
("[..]" indent
|
||||
list_line (nodent list_line)*
|
||||
(dedent / (({.+} ("" -> "Error while parsing list")) => error)))
|
||||
/("[" %ws? (list_line %ws?)? "]")
|
||||
|} }) -> List
|
||||
list_items <- ((functioncall / expression) (list_sep list_items)?)
|
||||
list_sep <- %ws? "," %ws?
|
||||
indented_list <-
|
||||
(functioncall / expression) (((list_sep (%line_comment? %new_line)?) / (%line_comment? %new_line)) indented_list)?
|
||||
]=]
|
||||
lingo = make_parser lingo
|
||||
list_line <- list_bit (%ws? "," tok_gap list_bit)* (%ws? ",")?
|
||||
list_bit <- inline_functioncall / expression
|
||||
|
||||
block_comment <- "#.." [^%nl]* indent [^%nl]* (%nl ((%ws? (!. / &%nl)) / (!%dedented [^%nl]*)))*
|
||||
line_comment <- "#" [^%nl]*
|
||||
|
||||
eol <- %ws? line_comment? (!. / &%nl)
|
||||
ignored_line <- (%nodented (block_comment / line_comment)) / (%ws? (!. / &%nl))
|
||||
indent <- eol (%nl ignored_line)* %nl %indented
|
||||
nodent <- eol (%nl ignored_line)* %nl %nodented
|
||||
dedent <- eol (%nl ignored_line)* (((!.) &%dedented) / (&(%nl %dedented)))
|
||||
tok_gap <- %ws / %prev_edge / &("[" / [.,:;{("#%'])
|
||||
dotdot <- nodent ".." %ws?
|
||||
]=]
|
||||
|
||||
whitespace = S(" \t")^1
|
||||
defs =
|
||||
ws:whitespace, nl: P("\n")
|
||||
wordchar: P(1)-S(' \t\n\r%#:;,.{}[]()"\\')
|
||||
indented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_indent)
|
||||
nodented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_nodent)
|
||||
dedented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_dedent)
|
||||
prev_edge: B(S(" \t\n.,:;}])\""))
|
||||
error: (src,pos,errors,err_msg)->
|
||||
line_no = 1
|
||||
for _ in src\sub(1,-#errors)\gmatch("\n") do line_no += 1
|
||||
err_pos = #src - #errors + 1
|
||||
if errors\sub(1,1) == "\n"
|
||||
-- Indentation error
|
||||
err_pos += #errors\match("[ \t]*", 2)
|
||||
start_of_err_line = err_pos
|
||||
while src\sub(start_of_err_line, start_of_err_line) != "\n" and start_of_err_line > 1
|
||||
start_of_err_line -= 1
|
||||
start_of_prev_line = start_of_err_line - 1
|
||||
while src\sub(start_of_prev_line, start_of_prev_line) != "\n" and start_of_prev_line > 1
|
||||
start_of_prev_line -= 1
|
||||
|
||||
local prev_line,err_line,next_line
|
||||
prev_line,err_line,next_line = src\match("([^\n]*)\n([^\n]*)\n([^\n]*)", start_of_prev_line+1)
|
||||
|
||||
pointer = ("-")\rep(err_pos - start_of_err_line + 0) .. "^"
|
||||
error("\n#{err_msg or "Parse error"} in #{filename} on line #{line_no}:\n\n#{prev_line}\n#{err_line}\n#{pointer}\n#{next_line}\n")
|
||||
|
||||
setmetatable(defs, {
|
||||
__index: (t,key)->
|
||||
fn = (src, value, errors)->
|
||||
token = {type: key, :src, :value, :errors}
|
||||
return token
|
||||
t[key] = fn
|
||||
return fn
|
||||
})
|
||||
lingo = re.compile(lingo, defs)
|
||||
tree = lingo\match(str\gsub("\r","").."\n")
|
||||
if @debug
|
||||
@writeln("\nPARSE TREE:")
|
||||
@ -305,6 +311,8 @@ class NomsuCompiler
|
||||
|
||||
tree_to_lua: (tree, kind="Expression")=>
|
||||
assert tree, "No tree provided."
|
||||
if not tree.type
|
||||
@error "Invalid tree: #{utils.repr(tree)}"
|
||||
indent = ""
|
||||
buffer = {}
|
||||
return_value = nil
|
||||
@ -361,9 +369,6 @@ class NomsuCompiler
|
||||
else
|
||||
add "ret = "..(to_lua(tree.value)\match("%s*(.*)"))
|
||||
|
||||
when "Expression"
|
||||
add to_lua(tree.value)
|
||||
|
||||
when "FunctionCall"
|
||||
name = @fn_name_from_tree(tree)
|
||||
if @defs[name] and @defs[name].is_macro
|
||||
@ -485,9 +490,6 @@ class NomsuCompiler
|
||||
when "Statement"
|
||||
@_yield_tree(tree.value, indent_level)
|
||||
|
||||
when "Expression"
|
||||
@_yield_tree(tree.value, indent_level)
|
||||
|
||||
when "FunctionCall"
|
||||
name = @fn_name_from_tree(tree)
|
||||
args = [a for a in *tree.value when a.type != "Word"]
|
||||
@ -534,10 +536,10 @@ class NomsuCompiler
|
||||
table.insert(result, line)
|
||||
return table.concat result, "\n"
|
||||
|
||||
compile: (src, output_file=nil)=>
|
||||
compile: (src, filename, output_file=nil)=>
|
||||
if @debug
|
||||
@writeln "COMPILING:\n#{src}"
|
||||
tree = @parse(src)
|
||||
tree = @parse(src, filename)
|
||||
assert tree, "Tree failed to compile: #{src}"
|
||||
code, retval = @tree_to_lua(tree)
|
||||
if output_file
|
||||
@ -555,7 +557,7 @@ class NomsuCompiler
|
||||
@callstack = {}
|
||||
error!
|
||||
|
||||
test: (src, expected)=>
|
||||
test: (src, filename, expected)=>
|
||||
i = 1
|
||||
while i != nil
|
||||
start,stop = src\find("\n\n", i)
|
||||
@ -567,7 +569,7 @@ class NomsuCompiler
|
||||
@error("WHERE'S THE ===? in:\n#{test}")
|
||||
test_src, expected = test\sub(1,start-1), test\sub(stop+1,-1)
|
||||
expected = expected\match'[\n]*(.*[^\n])'
|
||||
tree = @parse(test_src)
|
||||
tree = @parse(test_src, filename)
|
||||
got = @stringify_tree(tree.value.body)
|
||||
if got != expected
|
||||
@error"TEST FAILED!\nSource:\n#{test_src}\nExpected:\n#{expected}\n\nGot:\n#{got}"
|
||||
@ -597,26 +599,29 @@ class NomsuCompiler
|
||||
@def "require %filename", (vars)=>
|
||||
if not @loaded_files[vars.filename]
|
||||
file = io.open(vars.filename)
|
||||
@loaded_files[vars.filename] = @run(file\read('*a'))
|
||||
if not file
|
||||
@error "File does not exist: #{vars.filename}"
|
||||
@loaded_files[vars.filename] = @run(file\read('*a'), vars.filename)
|
||||
return @loaded_files[vars.filename]
|
||||
|
||||
@def "run file %filename", (vars)=>
|
||||
file = io.open(vars.filename)
|
||||
return @run(file\read('*a'))
|
||||
if not file
|
||||
@error "File does not exist: #{vars.filename}"
|
||||
return @run(file\read('*a'), vars.filename)
|
||||
|
||||
|
||||
-- Run on the command line via "./nomsu.moon input_file.nom" to execute
|
||||
-- and "./nomsu.moon input_file.nom output_file.lua" to compile (use "-" to compile to stdout)
|
||||
if arg and arg[1]
|
||||
c = NomsuCompiler()
|
||||
--c.debug = true
|
||||
input = io.open(arg[1])\read("*a")
|
||||
-- If run via "./nomsu.moon file.nom -", then silence output and print generated
|
||||
-- source code instead.
|
||||
_write = c.write
|
||||
if arg[2] == "-"
|
||||
c.write = ->
|
||||
code, retval = c\compile(input)
|
||||
code, retval = c\compile(input, arg[1])
|
||||
c.write = _write -- put it back
|
||||
if arg[2]
|
||||
output = if arg[2] == "-"
|
||||
@ -637,7 +642,7 @@ if arg and arg[1]
|
||||
elseif arg
|
||||
-- REPL:
|
||||
c = NomsuCompiler()
|
||||
c\run('run file "core.nom"')
|
||||
c\run('require "lib/core.nom"')
|
||||
while true
|
||||
buff = ""
|
||||
while true
|
||||
|
Loading…
Reference in New Issue
Block a user