Skip to content

Commit

Permalink
implement array slicing
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedanese committed Feb 25, 2016
1 parent 709da7f commit ed6a951
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 42 deletions.
8 changes: 7 additions & 1 deletion core/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,17 @@ struct Importstr : public AST {
* One of index and id will be nullptr before desugaring. After desugaring id will be nullptr.
*/
struct Index : public AST {
bool isSlice;
AST *target;
AST *index;
AST *end;
AST *step;
const Identifier *id;
Index(const LocationRange &lr, AST *target, AST *index, const Identifier *id)
: AST(lr, AST_INDEX), target(target), index(index), id(id)
: AST(lr, AST_INDEX), isSlice(false), target(target), index(index), end(nullptr), step(nullptr), id(id)
{ }
Index(const LocationRange &lr, AST *target, AST *index, AST *end, AST *step)
: AST(lr, AST_INDEX), isSlice(true), target(target), index(index), end(end), step(step), id(nullptr)
{ }
};

Expand Down
47 changes: 39 additions & 8 deletions core/desugaring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ limitations under the License.
#include "parser.h"

static LocationRange E; // Empty.

static unsigned long max_builtin = 24;
BuiltinDecl jsonnet_builtin_decl(unsigned long builtin)
{
Expand Down Expand Up @@ -438,7 +438,7 @@ class Desugarer {
} break;
}
}

ast_ = in;

} else if (auto *ast = dynamic_cast<Assert*>(ast_)) {
Expand Down Expand Up @@ -511,13 +511,44 @@ class Desugarer {
// Nothing to do.

} else if (auto *ast = dynamic_cast<Index*>(ast_)) {
desugar(ast->target, obj_level);
if (ast->id != nullptr) {
assert(ast->index == nullptr);
ast->index = str(ast->id->name);
ast->id = nullptr;
if (ast->isSlice) {
if (ast->index == nullptr)
ast->index = make<LiteralNull>(ast->location);
desugar(ast->index, obj_level);

if (ast->end == nullptr)
ast->end = make<LiteralNull>(ast->location);
desugar(ast->end, obj_level);

if (ast->step == nullptr)
ast->step = make<LiteralNull>(ast->location);
desugar(ast->step, obj_level);

ast_ = make<Apply>(
ast->location,
make<Index>(
E,
std(),
str(U"slice"),
nullptr),
std::vector<AST*>{
ast->target,
ast->index,
ast->end,
ast->step,
},
false,
false
);
} else {
desugar(ast->target, obj_level);
if (ast->id != nullptr) {
assert(ast->index == nullptr);
ast->index = str(ast->id->name);
ast->id = nullptr;
}
desugar(ast->index, obj_level);
}
desugar(ast->index, obj_level);

} else if (auto *ast = dynamic_cast<Local*>(ast_)) {
for (auto &bind: ast->binds)
Expand Down
55 changes: 52 additions & 3 deletions core/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,10 @@ namespace {
return tok;
}

void push(Token tok) {
tokens.push_front(tok);
}

Token peek(void)
{
Token tok = tokens.front();
Expand Down Expand Up @@ -955,7 +959,7 @@ namespace {
}
} while (true);
return alloc->make<Array>(span(tok, next), elements, got_comma);

}

case Token::PAREN_L: {
Expand Down Expand Up @@ -1186,9 +1190,54 @@ namespace {

Token op = pop();
if (op.kind == Token::BRACKET_L) {
AST *index = parse(MAX_PRECEDENCE);
bool is_slice = true;
AST *first = nullptr;
AST *second = nullptr;
AST *third = nullptr;

if (peek().kind == Token::BRACKET_R)
throw unexpected(pop(), "parsing index");

if (peek().data == "::") {
Token joined = popExpect(Token::OPERATOR, "::");
Token delim = Token(Token::OPERATOR, joined.fodder, ":",
joined.stringBlockIndent, joined.stringBlockTermIndent,
joined.location);
push(delim);
push(delim);
}

if (peek().data != ":")
first = parse(MAX_PRECEDENCE);

if (peek().kind != Token::BRACKET_R) {

Token delim = pop();
if (delim.data != ":")
throw unexpected(delim, "parsing slice");

if (peek().data != ":" &&
peek().kind != Token::BRACKET_R)
second = parse(MAX_PRECEDENCE);

if (peek().kind != Token::BRACKET_R) {

Token delim = pop();
if (delim.data != ":")
throw unexpected(delim, "parsing slice");

if (peek().kind != Token::BRACKET_R)
third= parse(MAX_PRECEDENCE);
}
} else {
is_slice = false;
}
Token end = popExpect(Token::BRACKET_R);
lhs = alloc->make<Index>(span(begin, end), lhs, index, nullptr);

if (is_slice)
lhs = alloc->make<Index>(span(begin, end), lhs, first, second, third);
else
lhs = alloc->make<Index>(span(begin, end), lhs, first, nullptr);

} else if (op.kind == Token::DOT) {
Token field_id = popExpect(Token::IDENTIFIER);
Expand Down
43 changes: 39 additions & 4 deletions stdlib/std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,41 @@ limitations under the License.
range(from, to)::
std.makeArray(to - from + 1, function(i) i + from),

slice(indexable, index, end, step)::
if (index != null && index < 0) || (end != null && end < 0) || (step != null && step < 0) then
error("got [%s:%s:%s] but negative index, end, and steps are not implemented" % [index, end, step] )
else if std.type(indexable) != "string" && std.type(indexable) != "array" then
error("std.slice accepts a string or an array, but got: %s" % std.type(indexable))
else
local invar =
// loop invariant with defaults applied
{
indexable: indexable,
index:
if index == null then 0
else index,
end:
if end == null then std.length(indexable)
else end,
step:
if step == null then 1
else step,
length: std.length(indexable),
type: std.type(indexable)
};
local build(slice, cur) =
if cur >= invar.end || cur >= invar.length then
slice
else
build(
if invar.type == "string" then
slice + invar.indexable[cur]
else
slice + [ invar.indexable[cur] ],
cur + invar.step
) tailstrict;
build(if invar.type == "string" then "" else [], invar.index),

count(arr, x):: std.length(std.filter(function(v) v==x, arr)),

mod(a, b)::
Expand Down Expand Up @@ -260,7 +295,7 @@ limitations under the License.
{ i: i + 1, v: "%", caps: false }
else
error "Unrecognised conversion type: " + c;


// Parsed initial %, now the rest.
local parse_code(str, i) =
Expand Down Expand Up @@ -658,10 +693,10 @@ limitations under the License.
else
ch;
"\"%s\"" % std.foldl(function(a, b) a + trans(b), std.stringChars(str), ""),

escapeStringPython(str)::
std.escapeStringJson(str),

escapeStringBash(str_)::
local str = std.toString(str_);
local trans(ch) =
Expand Down Expand Up @@ -754,7 +789,7 @@ limitations under the License.
else
aux(bytes, 0, ""),


base64DecodeBytes(str)::
if std.length(str) % 4 != 0 then
error "Not a base64 encoded string \"%s\"" % str
Expand Down
2 changes: 1 addition & 1 deletion test_suite/error.equality_function.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
RUNTIME ERROR: Cannot test equality of functions
std.jsonnet:913:17-41 function <anonymous>
std.jsonnet:948:17-41 function <anonymous>
error.equality_function.jsonnet:17:1-31
16 changes: 8 additions & 8 deletions test_suite/error.inside_equals_array.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
RUNTIME ERROR: foobar
error.inside_equals_array.jsonnet:18:18-31 thunk <array_element>
std.jsonnet:893:41-44 thunk <b>
std.jsonnet:881:29 thunk <x>
std.jsonnet:881:20-30 thunk <tb>
std.jsonnet:882:37-38 thunk <b>
std.jsonnet:882:13-39 function <anonymous>
std.jsonnet:893:33-44 function <aux>
std.jsonnet:896:29-44 function <aux>
std.jsonnet:897:21-32 function <anonymous>
std.jsonnet:928:41-44 thunk <b>
std.jsonnet:916:29 thunk <x>
std.jsonnet:916:20-30 thunk <tb>
std.jsonnet:917:37-38 thunk <b>
std.jsonnet:917:13-39 function <anonymous>
std.jsonnet:928:33-44 function <aux>
std.jsonnet:931:29-44 function <aux>
std.jsonnet:932:21-32 function <anonymous>
error.inside_equals_array.jsonnet:19:1-6
16 changes: 8 additions & 8 deletions test_suite/error.inside_equals_object.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
RUNTIME ERROR: foobar
error.inside_equals_object.jsonnet:18:22-35 object <b>
std.jsonnet:907:62-65 thunk <b>
std.jsonnet:881:29 thunk <x>
std.jsonnet:881:20-30 thunk <tb>
std.jsonnet:882:37-38 thunk <b>
std.jsonnet:882:13-39 function <anonymous>
std.jsonnet:907:54-65 function <aux>
std.jsonnet:910:29-44 function <aux>
std.jsonnet:911:21-32 function <anonymous>
std.jsonnet:942:62-65 thunk <b>
std.jsonnet:916:29 thunk <x>
std.jsonnet:916:20-30 thunk <tb>
std.jsonnet:917:37-38 thunk <b>
std.jsonnet:917:13-39 function <anonymous>
std.jsonnet:942:54-65 function <aux>
std.jsonnet:945:29-44 function <aux>
std.jsonnet:946:21-32 function <anonymous>
error.inside_equals_object.jsonnet:19:1-6
14 changes: 7 additions & 7 deletions test_suite/error.invariant.equality.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
RUNTIME ERROR: Object assertion failed.
error.invariant.equality.jsonnet:17:10-14 thunk <object_assert>
std.jsonnet:907:54-57 thunk <a>
std.jsonnet:880:29 thunk <x>
std.jsonnet:880:20-30 thunk <ta>
std.jsonnet:882:33-34 thunk <a>
std.jsonnet:882:13-39 function <anonymous>
std.jsonnet:907:54-65 function <aux>
std.jsonnet:911:21-32 function <anonymous>
std.jsonnet:942:54-57 thunk <a>
std.jsonnet:915:29 thunk <x>
std.jsonnet:915:20-30 thunk <ta>
std.jsonnet:917:33-34 thunk <a>
std.jsonnet:917:13-39 function <anonymous>
std.jsonnet:942:54-65 function <aux>
std.jsonnet:946:21-32 function <anonymous>
error.invariant.equality.jsonnet:17:1-32
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:600:13-55 function <anonymous>
std.jsonnet:635:13-55 function <anonymous>
error.sanity.jsonnet:17:1-21
3 changes: 3 additions & 0 deletions test_suite/error.slice_out_of_bounds.sugar.jsonnet.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RUNTIME ERROR: Array bounds error: 6 not within [0, 6)
std.jsonnet:92:46-58 thunk <array_element>
During manifestation
2 changes: 1 addition & 1 deletion test_suite/refresh_golden.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ for FILE in "$@" ; do
echo "Could not read: \"$FILE\"" 2>&1
exit 1
fi
jsonnet "$FILE" > "${FILE}.golden" 2>&1
../jsonnet "$FILE" > "${FILE}.golden" 2>&1
done


Loading

0 comments on commit ed6a951

Please sign in to comment.