-
Notifications
You must be signed in to change notification settings - Fork 467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement SassScript Maps #481
Conversation
if (!(m->concrete_type() == Expression::MAP || m->concrete_type() == Expression::LIST)) { | ||
error("$map: is not a map for `" + string(sig) + "`", path, position); | ||
} | ||
Map* map = dynamic_cast<Map*>(env["$map"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error checking block is repeated a few times. Is there a better way?
Expression* m = ARG("$map", Expression);
Expression* v = ARG("$key", Expression);
if (!(m->concrete_type() == Expression::MAP || m->concrete_type() == Expression::LIST)) {
error("$map: is not a map for `" + string(sig) + "`", path, position);
}
Map* map = dynamic_cast<Map*>(env["$map"]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm ... I assume that this function is supposed to accept lists and treat them as maps that have integer keys? If you need to handle both cases, I can't think of a good way to do the type checking that wouldn't involve some boilerplate ... I suppose you could write variants of ARG
and get_arg
that can take 2 or 3 different types, and check that the argument matches one of them....
Also, even though you check for both Maps and Lists, you seem to cast only to a Map afterward -- is this an omission?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, I'll clarify. The pattern with these function is they take a map, or an empty map ()
which is indistinguishable from a list, and is actually treated a list by the parser.
So in essence I'm just trying to determine an $map
is infact a map with zero or more keys.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caveat with this approach is that I'm not sure what'll happen with a list in fact passed in and coerced with Map* map = dynamic_cast<Map*>(env["$map"]);
. Sass would return an error
$map: ("foo" "bar" "baz") is not a map for `map-get'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, now that I've consulted the Ruby Sass docs and code snippets, I'm not sure why you're checking if the $map
argument might also be a list -- can't you just require it to be a map? Then you could just use
Map* m = ARG("$map", Map);
on the first line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I appreciate the feedback. Generally speaking I think I'm doing mostly the right the things.
I'm just not sure whether it's the correct way to do them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the issue I ran into with
Map* m = ARG("$map", Map);
is that this
foo: map-get((), foo);
would resulting in
error: argument
$mapof
map-get($map, $key) must be a map
because ARG
thinks ()
is a list. I suppose the correct thing to do would be to update ARG
to understand that ()
is both an empty list and map.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth seeing if you can add a template specialization of get_arg
(specialized for Map) that does some extra checking to see if the arg is an empty list, and if it is, then replace the arg with a newly-constructed empty map and return a pointer to that map. (If you try this, you'll need to modify get_arg
to take a Context object so that the memory allocations can be tracked.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep I agree that's the sane thing to do. I'll give it a go today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 57dada0
This only implements the 3.3 syntax.
EEEEE!!!! |
It'd be ideal @akhleung could get his feedback in before Sunday US time. |
Hey sorry for the delay -- I've been ill for the past few days. I'll review this and comment on it today! |
for (size_t i = 0, L = l->length(); i < L; ++i) { | ||
if (!eq((*l)[i]->key(), (*r)[i]->key(), ctx)) return false; | ||
if (!eq((*l)[i]->value(), (*r)[i]->value(), ctx)) return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This equality check is naive, and could potentially be a performance issue with the right inputs. Maybe this is ok for the first iteration?
An alternative approach would be to maintain a has for each map or key value pair for equality comparisons.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems fine for a first pass; we can optimize later if it becomes a problem.
@akhleung, it's good to have you back! 👍 |
I've updated the Map casting boilerplate with a I think this is pretty much good to go. |
@akhleung can we have a special "Maps in LibSass" announcement for SassConf this week? |
Sure, I'll get this merged in time for SassConf. |
I'm getting syntax failure on the following. $nest: (
alpha: (
beta: (gamma: delta),
epsilon: 2 3 4,
zeta: 'hi'
)
);
.test {
out: map-get(map-get(map-get($nest, alpha), beta), gamma);
out: map-get(map-get($nest, alpha), epsilon);
out: map-get(map-get($nest, alpha), zeta);
} output with ruby sass 3.4.4 is the following: .test {
out: delta;
out: 2 3 4;
out: "hi";
} Running |
This PR implements SassScript Maps and the API as of 3.3.14. There were changes to some map APIs in 3.4 none of which are implemented here.
I think this is about ready to start the feedback process. I'm not a c/c++ developer by trade so there's likely plenty of room for improvement.
TODO
Known issues
- @debug support #223@debug
supportPossible issues
I think this can be implemented as a separate feature. It's probably best suited to being shipped with
call
.Spec
Specs for SassScript Maps have been added in sass/sass-spec#44. Theses specs admittedly focus primarily on the map-* functions and not the parsing of Maps.
/cc @akhleung