Skip to content

Commit

Permalink
Remove std.force in favor of tailcall annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
sparkprime committed May 5, 2015
1 parent c637463 commit f938d9f
Show file tree
Hide file tree
Showing 14 changed files with 86 additions and 105 deletions.
5 changes: 3 additions & 2 deletions ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ struct AST {
struct Apply : public AST {
AST *target;
std::vector<AST*> arguments;
Apply(const LocationRange &lr, AST *target, const std::vector<AST*> &arguments)
: AST(lr, AST_APPLY), target(target), arguments(arguments)
bool tailcall;
Apply(const LocationRange &lr, AST *target, const std::vector<AST*> &arguments, bool tailcall)
: AST(lr, AST_APPLY), target(target), arguments(arguments), tailcall(tailcall)
{ }
};

Expand Down
8 changes: 0 additions & 8 deletions doc/stdlib.html
Original file line number Diff line number Diff line change
Expand Up @@ -525,14 +525,6 @@ <h4>std.base64Decode(s)</h4>
<p>Behaves like std.base64DecodeBytes() except returns a string instead of an array of bytes.</p>


<h3>Miscellaneous</h3>

<h4>std.force(x)</h4>

<p>Always returns true, but always evaluates its argument. this allows controlling memory usage in
recursive algorithms, and causes runtime errors in x to be discovered immediately.</p>


<div style="margin-bottom: 50px"></div>
<hr />
<p class="copyright">
Expand Down
8 changes: 0 additions & 8 deletions doc/stdlib.html.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,4 @@ words, it consumes the output of std.base64().</p>
<p>Behaves like std.base64DecodeBytes() except returns a string instead of an array of bytes.</p>


<h3>Miscellaneous</h3>

<h4>std.force(x)</h4>

<p>Always returns true, but always evaluates its argument. this allows controlling memory usage in
recursive algorithms, and causes runtime errors in x to be discovered immediately.</p>


{% include 'footer.html.jinja' %}
2 changes: 1 addition & 1 deletion editors/atom/language-jsonnet/grammars/jsonnet.cson
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@

{
'name': 'keyword.other.jsonnet'
'match': '\\b(local)\\b'
'match': '\\b(local|tailcall)\\b'
},

{
Expand Down
2 changes: 1 addition & 1 deletion editors/vim/syntax/jsonnet.vim
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ syn match Keyword "\<[a-zA-Z_][a-z0-9A-Z_]*\s*\(([^)]*)\)\?\s*+\?::\?:\?"
syntax keyword Include import importstr
syntax keyword Type function self super
syntax keyword Statement if then else for in
syntax keyword Special local
syntax keyword Special local tailcall
syntax keyword Constant true false null
syntax keyword Underlined error

Expand Down
18 changes: 11 additions & 7 deletions parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,15 +489,15 @@ namespace {
AST *map_str = alloc->make<LiteralString>(l, "map");
AST *map = alloc->make<Index>(l, std, map_str);
std::vector<AST*> args = {map_func, arr};
return alloc->make<Apply>(span(tok, maybe_if), map, args);
return alloc->make<Apply>(span(tok, maybe_if), map, args, false);
} else if (maybe_if.kind == Token::IF) {
AST *cond = parse(MAX_PRECEDENCE, obj_level);
Token last = popExpect(Token::BRACKET_R);
AST *filter_func = alloc->make<Function>(cond->location, params, cond);
AST *fmap_str = alloc->make<LiteralString>(l, "filterMap");
AST *fmap = alloc->make<Index>(l, std, fmap_str);
std::vector<AST*> args = {filter_func, map_func, arr};
return alloc->make<Apply>(span(tok, last), fmap, args);
return alloc->make<Apply>(span(tok, last), fmap, args, false);
} else {
std::stringstream ss;
ss << "Expected if or ] after for clause, got: " << maybe_if;
Expand Down Expand Up @@ -731,7 +731,12 @@ namespace {
std::vector<AST*> args;
Token end = parseCommaList(args, Token::PAREN_R,
"function argument", obj_level);
lhs = alloc->make<Apply>(span(begin, end), lhs, args);
bool tailcall = false;
if (peek().kind == Token::TAILCALL) {
pop();
tailcall = true;
}
lhs = alloc->make<Apply>(span(begin, end), lhs, args, tailcall);

} else if (op.kind == Token::BRACE_L) {
AST *obj;
Expand All @@ -745,7 +750,7 @@ namespace {
AST *mod_str = alloc->make<LiteralString>(l, "mod");
AST *f_mod = alloc->make<Index>(l, std, mod_str);
std::vector<AST*> args = {lhs, rhs};
lhs = alloc->make<Apply>(span(begin, rhs), f_mod, args);
lhs = alloc->make<Apply>(span(begin, rhs), f_mod, args, false);

} else {
// Logical / arithmetic binary operator.
Expand All @@ -767,7 +772,7 @@ namespace {
};
}

static unsigned long max_builtin = 24;
static unsigned long max_builtin = 23;
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
{
switch (builtin) {
Expand All @@ -794,8 +799,7 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
case 20: return {"mantissa", {"n"}};
case 21: return {"exponent", {"n"}};
case 22: return {"modulo", {"a", "b"}};
case 23: return {"force", {"x"}};
case 24: return {"extVar", {"x"}};
case 23: return {"extVar", {"x"}};
default:
std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl;
std::abort();
Expand Down
51 changes: 13 additions & 38 deletions std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,10 @@ limitations under the License.
local i2 = i + 1;
if i >= std.length(str) then
arr + [v]
else if std.force(i2) then
if c == delim then
local arr2 = arr + [v];
if std.force(arr2) then
aux(str, delim, i2, arr2, "")
else
null
else
local v2 = v + c;
if std.force(v2) then
aux(str, delim, i2, arr, v2);
else if c == delim then
aux(str, delim, i2, arr + [v], "") tailcall
else
aux(str, delim, i2, arr, v + c) tailcall;
aux(str, c, 0, [], ""),

range(from, to)::
Expand All @@ -92,23 +85,11 @@ limitations under the License.
if i >= std.length(arr) then
running
else if arr[i] == null then
local i2 = i + 1;
if std.force(i2) then
aux(arr, i2, first, running)
else
null
aux(arr, i + 1, first, running) tailcall
else if first then
local i2 = i + 1;
local running2 = running + arr[i];
if std.force(i2) && std.force(running2) then
aux(arr, i2, false, running2)
else
null
aux(arr, i + 1, false, running + arr[i]) tailcall
else
local i2 = i + 1;
local running2 = running + sep + arr[i];
if std.force(i2) && std.force(running2) then
aux(arr, i2, false, running2);
aux(arr, i + 1, false, running + sep + arr[i]) tailcall;
if std.type(arr) != "array" then
error "join second parameter should be array, got " + std.type(arr)
else if std.type(sep) == "string" then
Expand Down Expand Up @@ -564,21 +545,15 @@ limitations under the License.
if idx < 0 then
running
else
local running2 = func(arr[idx], running);
local idx2 = idx - 1;
if std.force(running2) && std.force(idx2) then
aux(func, arr, running2, idx2);
aux(func, arr, func(arr[idx], running), idx - 1) tailcall;
aux(func, arr, init, std.length(arr) - 1),

foldl(func, arr, init)::
local aux(func, arr, running, idx) =
if idx >= std.length(arr) then
running
else
local running2 = func(running, arr[idx]);
local idx2 = idx + 1;
if std.force(running2) && std.force(idx2) then
aux(func, arr, running2, idx2);
aux(func, arr, func(running, arr[idx]), idx + 1) tailcall;
aux(func, arr, init, 0),


Expand Down Expand Up @@ -723,7 +698,7 @@ limitations under the License.
// 2 LSB of i
base64_table[(arr[i] & 3) << 4] +
"==";
aux(arr, i + 3, r + str)
aux(arr, i + 3, r + str) tailcall
else if i + 2 >= std.length(arr) then
local str =
// 6 MSB of i
Expand All @@ -733,7 +708,7 @@ limitations under the License.
// 4 LSB of i+1
base64_table[(arr[i+1] & 15) << 2] +
"=";
aux(arr, i + 3, r + str)
aux(arr, i + 3, r + str) tailcall
else
local str =
// 6 MSB of i
Expand All @@ -744,7 +719,7 @@ limitations under the License.
base64_table[(arr[i+1] & 15) << 2 | (arr[i+2] & 192) >> 6] +
// 6 LSB of i+2
base64_table[(arr[i+2] & 63)];
aux(arr, i + 3, r + str);
aux(arr, i + 3, r + str) tailcall;

local sanity = std.foldl(function(r, a) r && (a < 256), bytes, true);
if !sanity then
Expand All @@ -771,7 +746,7 @@ limitations under the License.
local n3 =
if str[i+3] == "=" then []
else [(base64_inv[str[i+2]] & 3) << 6 | base64_inv[str[i+3]]];
aux(str, i+4, r + n1 + n2 + n3);
aux(str, i+4, r + n1 + n2 + n3) tailcall;
aux(str, 0, []),

base64Decode(str)::
Expand Down
4 changes: 3 additions & 1 deletion test_suite/error.01.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
RUNTIME ERROR: foo
error.01.jsonnet:17:29-39 function <bananas>
error.01.jsonnet:18:29-38
error.01.jsonnet:18:29-38 function <oranges>
error.01.jsonnet:19:28-37 function <apples>
error.01.jsonnet:20:1-9
3 changes: 2 additions & 1 deletion test_suite/error.06.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
RUNTIME ERROR: Division by zero.
error.06.jsonnet:17:13-15 thunk <err>
error.06.jsonnet:18:22-24
error.06.jsonnet:18:22-24 function <f>
error.06.jsonnet:19:1-3
3 changes: 2 additions & 1 deletion test_suite/error.07.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
RUNTIME ERROR: sarcasm
error.07.jsonnet:18:32-46 thunk <array_element>
error.07.jsonnet:17:29-34 thunk <toxic>
error.07.jsonnet:17:29-34 function <third>
error.07.jsonnet:18:15-54 thunk <toxic>
error.07.jsonnet:19:1-5
20 changes: 10 additions & 10 deletions test_suite/error.recursive_object_non_term.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
RUNTIME ERROR: Max stack frames exceeded.
error.recursive_object_non_term.jsonnet:20:44-48 object <anonymous>
error.recursive_object_non_term.jsonnet:20:11-16 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
error.recursive_object_non_term.jsonnet:20:44-50 object <anonymous>
...
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
error.recursive_object_non_term.jsonnet:20:35-54 object <Fib>
Expand Down
2 changes: 1 addition & 1 deletion test_suite/error.sanity.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
RUNTIME ERROR: Assertion failed. 1 != 2
std.jsonnet:599:13-55 function <anonymous>
std.jsonnet:574:13-55 function <anonymous>
error.sanity.jsonnet:17:1-21
4 changes: 1 addition & 3 deletions test_suite/recursive_function.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ local sum(x, v) =
if x <= 0 then
v
else
local x2 = x - 1, v2 = x + v;
if x2 == x2 && v2 == v2 then
sum(x2, v2);
sum(x - 1, x + v) tailcall;

local sz = 10000;
std.assertEqual(sum(sz, 0), sz * (sz+1)/2) &&
Expand Down
Loading

0 comments on commit f938d9f

Please sign in to comment.