Misc changes, including text indented interpolations are now indented

relative to the text, not the opening '("', code objects can now remove
all free vars, the REPL uses global vars. Error API is changing a bit.
This commit is contained in:
Bruce Hill 2019-01-01 15:05:58 -08:00
parent 0760d4fb64
commit b6d3cbd61c
15 changed files with 213 additions and 204 deletions

View File

@ -320,6 +320,10 @@ do
return self:dirty()
end,
remove_free_vars = function(self, vars)
if vars == nil then
vars = nil
end
vars = vars or self:get_free_vars()
if not (#vars > 0) then
return
end
@ -351,33 +355,34 @@ do
end
return self:dirty()
end,
get_free_vars = function(self)
local vars, seen = { }, { }
local gather_from
gather_from = function(self)
local _list_0 = self.free_vars
for _index_0 = 1, #_list_0 do
local var = _list_0[_index_0]
if not (seen[var]) then
seen[var] = true
vars[#vars + 1] = var
end
end
local _list_1 = self.bits
for _index_0 = 1, #_list_1 do
local bit = _list_1[_index_0]
if not (type(bit) == 'string') then
gather_from(bit)
end
end
end
gather_from(self)
return vars
end,
declare_locals = function(self, to_declare)
if to_declare == nil then
to_declare = nil
end
if to_declare == nil then
local seen
to_declare, seen = { }, { }
local gather_from
gather_from = function(self)
local _list_0 = self.free_vars
for _index_0 = 1, #_list_0 do
local var = _list_0[_index_0]
if not (seen[var]) then
seen[var] = true
to_declare[#to_declare + 1] = var
end
end
local _list_1 = self.bits
for _index_0 = 1, #_list_1 do
local bit = _list_1[_index_0]
if not (type(bit) == 'string') then
gather_from(bit)
end
end
end
gather_from(self)
end
to_declare = to_declare or self:get_free_vars()
if #to_declare > 0 then
self:remove_free_vars(to_declare)
self:prepend("local " .. tostring(concat(to_declare, ", ")) .. ";\n")

View File

@ -179,7 +179,8 @@ class LuaCode extends Code
seen[var] = true
@dirty!
remove_free_vars: (vars)=>
remove_free_vars: (vars=nil)=>
vars or= @get_free_vars!
return unless #vars > 0
removals = {}
for var in *vars
@ -198,18 +199,21 @@ class LuaCode extends Code
stack[#stack+1] = b
@dirty!
get_free_vars: =>
vars, seen = {}, {}
gather_from = =>
for var in *@free_vars
unless seen[var]
seen[var] = true
vars[#vars+1] = var
for bit in *@bits
unless type(bit) == 'string'
gather_from bit
gather_from self
return vars
declare_locals: (to_declare=nil)=>
if to_declare == nil
to_declare, seen = {}, {}
gather_from = =>
for var in *@free_vars
unless seen[var]
seen[var] = true
to_declare[#to_declare+1] = var
for bit in *@bits
unless type(bit) == 'string'
gather_from bit
gather_from self
to_declare or= @get_free_vars!
if #to_declare > 0
@remove_free_vars to_declare
@prepend "local #{concat to_declare, ", "};\n"

View File

@ -251,18 +251,6 @@ List = function(t)
return error("Unsupported List type: " .. type(t))
end
end
local walk_items
walk_items = function(self, i)
i = i + 1
local k, v = next(self.table, self.key)
if k ~= nil then
self.key = k
return i, Dict({
key = k,
value = v
})
end
end
local _dict_mt = {
__type = "Dict",
__eq = function(self, other)
@ -321,12 +309,6 @@ local _dict_mt = {
return _accum_0
end)(), ", ") .. "}"
end,
__ipairs = function(self)
return walk_items, {
table = self,
key = nil
}, 0
end,
__band = function(self, other)
return Dict((function()
local _tbl_0 = { }
@ -339,30 +321,28 @@ local _dict_mt = {
end)())
end,
__bor = function(self, other)
local ret
do
local ret = Dict((function()
local _tbl_0 = { }
for k, v in pairs(self) do
_tbl_0[k] = v
end
ret = _tbl_0
end
return _tbl_0
end)())
for k, v in pairs(other) do
if ret[k] == nil then
ret[k] = v
end
end
return Dict(ret)
return ret
end,
__bxor = function(self, other)
local ret
do
local ret = Dict((function()
local _tbl_0 = { }
for k, v in pairs(self) do
_tbl_0[k] = v
end
ret = _tbl_0
end
return _tbl_0
end)())
for k, v in pairs(other) do
if ret[k] == nil then
ret[k] = v
@ -370,17 +350,16 @@ local _dict_mt = {
ret[k] = nil
end
end
return Dict(ret)
return ret
end,
__add = function(self, other)
local ret
do
local ret = Dict((function()
local _tbl_0 = { }
for k, v in pairs(self) do
_tbl_0[k] = v
end
ret = _tbl_0
end
return _tbl_0
end)())
for k, v in pairs(other) do
if ret[k] == nil then
ret[k] = v
@ -388,17 +367,16 @@ local _dict_mt = {
ret[k] = ret[k] + v
end
end
return Dict(ret)
return ret
end,
__sub = function(self, other)
local ret
do
local ret = Dict((function()
local _tbl_0 = { }
for k, v in pairs(self) do
_tbl_0[k] = v
end
ret = _tbl_0
end
return _tbl_0
end)())
for k, v in pairs(other) do
if ret[k] == nil then
ret[k] = -v
@ -406,7 +384,7 @@ local _dict_mt = {
ret[k] = ret[k] - v
end
end
return Dict(ret)
return ret
end
}
Dict = function(t)
@ -430,11 +408,6 @@ Dict = function(t)
return error("Unsupported Dict type: " .. type(t))
end
end
for i, entry in ipairs(Dict({
x = 99
})) do
assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue")
end
do
local reverse, upper, lower, find, byte, match, gmatch, gsub, sub, format, rep
do
@ -502,9 +475,9 @@ do
return (match(self, patt))
end,
matching_groups = function(self, patt)
return {
return List({
match(self, patt)
}
})
end,
[as_lua_id("* 1")] = function(self, n)
return rep(self, n)

View File

@ -106,13 +106,6 @@ List = (t)->
return l
else error("Unsupported List type: "..type(t))
walk_items = (i)=>
i = i + 1
k, v = next(@table, @key)
if k != nil
@key = k
return i, Dict{key:k, value:v}
_dict_mt =
__type: "Dict"
__eq: (other)=>
@ -133,32 +126,31 @@ _dict_mt =
"{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}"
as_lua: =>
"Dict{"..concat(["[ #{as_lua(k)}]= #{as_lua(v)}" for k,v in pairs @], ", ").."}"
__ipairs: => walk_items, {table:@, key:nil}, 0
__band: (other)=>
Dict{k,v for k,v in pairs(@) when other[k] != nil}
__bor: (other)=>
ret = {k,v for k,v in pairs(@)}
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = v
return Dict(ret)
return ret
__bxor: (other)=>
ret = {k,v for k,v in pairs(@)}
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = v
else ret[k] = nil
return Dict(ret)
return ret
__add: (other)=>
ret = {k,v for k,v in pairs(@)}
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = v
else ret[k] += v
return Dict(ret)
return ret
__sub: (other)=>
ret = {k,v for k,v in pairs(@)}
ret = Dict{k,v for k,v in pairs(@)}
for k,v in pairs(other)
if ret[k] == nil then ret[k] = -v
else ret[k] -= v
return Dict(ret)
return ret
Dict = (t)->
if type(t) == 'table'
return setmetatable(t, _dict_mt)
@ -171,8 +163,6 @@ Dict = (t)->
return d
else error("Unsupported Dict type: "..type(t))
for i,entry in ipairs(Dict({x:99}))
assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue")
do
{:reverse, :upper, :lower, :find, :byte, :match, :gmatch, :gsub, :sub, :format, :rep} = string
@ -204,7 +194,7 @@ do
line_position_at: (i)=> select(3, line_at(@, i))
matches: (patt)=> match(@, patt) and true or false
matching: (patt)=> (match(@, patt))
matching_groups: (patt)=> {match(@, patt)}
matching_groups: (patt)=> List{match(@, patt)}
[as_lua_id "* 1"]: (n)=> rep(@, n)
all_matches_of: (patt)=>
result = {}

View File

@ -37,7 +37,6 @@ test:
test:
$dict = {.x = 1, .y = 2, .z = 3}
assume (size of $dict) == 3
assume [: for $ in {.x = 1}: add $] == [{.key = "x", .value = 1}]
assume [: for $k = $v in {.x = 1}: add {.key = $k, .value = $v}] ==
[{.key = "x", .value = 1}]
assume ({.x = 1, .y = 1} + {.y = 10, .z = 10}) == {.x = 1, .y = 11, .z = 10}

View File

@ -5,7 +5,6 @@
use "core/metaprogramming.nom"
use "core/operators.nom"
use "core/errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -448,32 +447,6 @@ test:
end -- do
")
test:
$d = {}
try:
do:
$d.x = "bad"
barf
..then always:
$d.x = "good"
assume ($d.x == "good")
(do $action then always $final_action) compiles to:
define mangler
return
Lua ("
do
local \(mangle "fell_through") = false
local \(mangle "ok"), \(mangle "ret") = pcall(function()
\($action as lua)
\(mangle "fell_through") = true
end)
\($final_action as lua)
if not \(mangle "ok") then error(ret, 0) end
if not \(mangle "fell_through") then return ret end
end
")
test:
assume ((result of: return 99) == 99)

View File

@ -3,11 +3,12 @@
This file contains basic error reporting code
use "core/metaprogramming.nom"
use "core/operators.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(barf $msg) compiles to
"error(\(=lua "\$msg and \($msg as lua expr) or 'nil'"), 0);"
(fail $msg) compiles to
"error(\(($msg as lua expr) if $msg else "nil"), 0);"
(assume $condition) compiles to:
lua> ("
@ -37,62 +38,86 @@ use "core/metaprogramming.nom"
end
")
(assume $condition or barf $message) compiles to ("
if not \($condition as lua expr) then
error(\($message as lua expr), 0)
end
")
test:
try (barf) and if it succeeds:
barf "try failed."
try: fail
$worked = (no)
try (barf) and if it barfs:
try: fail
..if it fails:
$worked = (yes)
assume $worked or barf "try/catch failed"
$x = 1
try:
$x = 2
do (barf) then always: $x = 3
..and if it barfs:
do nothing
assume ($x == 3) or barf "do/then always failed"
..if it succeeds:
fail "'try' incorrectly ran success case."
unless $worked:
fail "'try' failed to recover from failure"
# Try/except
[
try $action and if it succeeds $success or if it barfs $msg $fallback
try $action and if it barfs $msg $fallback or if it succeeds $success
] all compile to ("
do
local fell_through = false
local err, erred = nil, false
local ok, ret = xpcall(function()
\($action as lua)
fell_through = true
end, function(\(=lua "\$fallback and \($msg as lua expr) or ''"))
local ok, ret = pcall(function()
\((=lua "\$fallback or \$msg") as lua)
end)
if not ok then err, erred = ret, true end
end)
if ok then
\($success as lua)
if not fell_through then
return ret
try $action if it succeeds $success if it fails $fallback
try $action if it fails $fallback if it succeeds $success
] all compile to:
$success_lua = ($success as lua)
if ((#"\$success_lua") > 0): $success_lua, add "\n"
$success_lua, prepend "-- Success:\n"
$success_lua, add "if not _fell_through then return table.unpack(_result, 2) end"
$fallback_lua = ($fallback as lua)
if ((#"\$fallback_lua") > 0):
$fallback_lua, prepend "\nlocal function failure() return _result[2] end\n"
$fallback_lua, prepend "-- Failure:"
return
Lua ("
do
local _fell_through = false
local _result = {pcall(function()
\($action as lua)
_fell_through = true
end)}
if _result[1] then
\$success_lua
else
\$fallback_lua
end
end
elseif erred then
error(err, 0)
end
end
")
")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(try $action) parses as
try $action and if it succeeds (do nothing) or if it barfs (do nothing)
try $action if it succeeds (do nothing) if it fails (do nothing)
(try $action and if it barfs $msg $fallback) parses as
try $action and if it succeeds (do nothing) or if it barfs $msg $fallback
(try $action if it fails $msg $fallback) parses as
try $action if it succeeds (do nothing) if it fails $msg $fallback
(try $action and if it succeeds $success) parses as
try $action and if it succeeds $success or if it barfs (do nothing)
(try $action if it succeeds $success) parses as
try $action if it succeeds $success if it fails (do nothing)
(try $action if it fails $fallback if it succeeds $success) parses as
try $action if it succeeds $success if it fails $fallback
test:
$success = (no)
try:
do: fail
..then always:
$success = (yes)
..if it succeeds:
fail "'try ... then always ...' didn't propagate failure"
unless $success:
fail "'try ... then always ...' didn't execute the 'always' code"
(do $action then always $final_action) compiles to ("
do -- do/then always
local _fell_through = false
local _results = {pcall(function()
\($action as lua)
_fell_through = true
end)}
\($final_action as lua)
if not _results[1] then error(_results[2], 0) end
if not _fell_through then return table.unpack(_results, 2) end
end
")
~~~
(barf $) parses as (fail $)
(assume $1 or barf $2) parses as (unless $1: fail $2)

View File

@ -308,11 +308,11 @@ externally ($ is $kind syntax tree) means
($tree with $t -> $replacement) compiles to ("
\($tree as lua expr):map(function(\($t as lua expr))
\(
=lua ("
\$replacement.type == 'Block' and \($replacement as lua) or 'return '..\
..\($replacement as lua expr)
")
)
=lua ("
\$replacement.type == 'Block' and \($replacement as lua) or 'return '..\
..\($replacement as lua expr)
")
)
end)
")

View File

@ -3,7 +3,6 @@
This file contains definitions of operators like "+" and "and".
use "core/metaprogramming.nom"
use "core/errors.nom"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -168,7 +168,11 @@ blank_text_lines <-
{~ (%nl ((ws* -> '') (&%nl / !.) / (=curr_indent -> '') &[^%nl]))+ ~}
text_interpolation <-
("\" (indented_block (blank_lines =curr_indent "..")? / indented_expression))
({|
-- %indentation will backtrack and match the actual indentation of the current line
"\" {:curr_indent: %indentation :}
(indented_block (blank_lines =curr_indent "..")? / indented_expression)
|} -> unpack)
/ inline_text_interpolation

View File

@ -304,7 +304,7 @@ local compile = setmetatable({
lua:add(",")
end
if lua:trailing_line_len() + #(entry_lua:text():match("^[\n]*")) > MAX_LINE then
lua:add("\n")
lua:add("\n ")
elseif needs_comma then
lua:add(" ")
end
@ -417,6 +417,8 @@ local compile = setmetatable({
if tree[i].type == "Comment" then
items_lua:add("\n")
sep = ''
elseif items_lua:trailing_line_len() > MAX_LINE then
sep = ',\n '
else
sep = ', '
end

View File

@ -234,7 +234,7 @@ compile = setmetatable({
entry_lua\add as_lua(v)
if needs_comma then lua\add ","
if lua\trailing_line_len! + #(entry_lua\text!\match("^[\n]*")) > MAX_LINE
lua\add "\n"
lua\add "\n "
elseif needs_comma
lua\add " "
lua\add entry_lua
@ -322,6 +322,8 @@ compile = setmetatable({
if tree[i].type == "Comment"
items_lua\add "\n"
sep = ''
elseif items_lua\trailing_line_len! > MAX_LINE
sep = ',\n '
else
sep = ', '
i += 1

View File

@ -26,6 +26,13 @@ do
_with_0.unpack = unpack or table.unpack
_with_0["nil"] = Cc(nil)
_with_0.userdata = Carg(1)
_with_0.indentation = lpeg.Cmt(P(0), function(s, i)
local sub = string.sub
while i > 1 and sub(s, i - 1, i - 1) ~= '\n' do
i = i - 1
end
return true, (s:match("^ *", i))
end)
_with_0.utf8_char = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191"))
_with_0.Tree = function(t, userdata)
return userdata.make_tree(t, userdata)

View File

@ -25,6 +25,13 @@ DEFS = with {}
.unpack = unpack or table.unpack
.nil = Cc(nil)
.userdata = Carg(1)
-- Always match and capture the indentation (spaces only) of the current line
-- i.e. the leading space of the chunk of non-newline characters leading up to s[i]
.indentation = lpeg.Cmt P(0),
(s, i)->
sub = string.sub
while i > 1 and sub(s,i-1,i-1) != '\n' do i -= 1
return true, (s\match("^ *", i))
.utf8_char = (
R("\194\223")*R("\128\191") +
R("\224\239")*R("\128\191")*R("\128\191") +

View File

@ -42,25 +42,44 @@ repeat:
if ((size of $buff) == 0): stop
$buff = ($buff, joined)
# TODO: support local variables
spoof file $buff
try:
$ret = (run $buff)
..and if it barfs $err: say $err
..or if it succeeds:
if (type of $ret) is:
"nil":
do nothing
$tree = ($buff parsed)
..and if it barfs $err:
say $err
do next
"boolean":
say "= \("yes" if $ret else "no")"
unless $tree:
do next
"table":
if $ret.as_nomsu:
say "= \($ret, as nomsu)"
..else:
for $chunk in $tree:
try:
$lua = ($chunk as lua)
..and if it barfs $err: say $err
unless $lua:
do next
# TODO: this is a bit hacky, it just defaults variables to global
so that stuff mostly works across multiple lines. It would be
nicer if local variables actually worked.
$lua, remove free vars
try:
$ret = (run $lua)
..and if it barfs $err: say $err
..or if it succeeds:
if (type of $ret) is:
"nil":
do nothing
"boolean":
say "= \("yes" if $ret else "no")"
"table":
if $ret.as_nomsu:
say "= \($ret, as nomsu)"
..else:
say "= \$ret"
else:
say "= \$ret"
else:
say "= \$ret"