Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 2c52a74

Browse files
committed
Implement missing map merge and set variants
1 parent ea4ecf2 commit 2c52a74

File tree

4 files changed

+290
-6
lines changed

4 files changed

+290
-6
lines changed

src/ast_nodes.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,61 @@ namespace Sass {
167167
return *this;
168168
}
169169

170+
Values::iterator& Values::iterator::operator+=(size_t offset)
171+
{
172+
switch (type) {
173+
case MapIterator:
174+
cur = std::min(cur + offset, last);
175+
break;
176+
case ListIterator:
177+
cur = std::min(cur + offset, last);
178+
break;
179+
case SingleIterator:
180+
cur = 1;
181+
break;
182+
case NullPtrIterator:
183+
break;
184+
}
185+
return *this;
186+
}
187+
188+
Values::iterator& Values::iterator::operator-=(size_t offset)
189+
{
190+
switch (type) {
191+
case MapIterator:
192+
cur = std::max(cur - offset, (size_t)0);
193+
break;
194+
case ListIterator:
195+
cur = std::max(cur - offset, (size_t)0);
196+
break;
197+
case SingleIterator:
198+
cur = 1;
199+
break;
200+
case NullPtrIterator:
201+
break;
202+
}
203+
return *this;
204+
}
205+
206+
Values::iterator Values::iterator::operator-(size_t offset)
207+
{
208+
Values::iterator copy(*this);
209+
switch (type) {
210+
case MapIterator:
211+
copy.cur = std::max(copy.cur - offset, (size_t)0);
212+
break;
213+
case ListIterator:
214+
copy.cur = std::max(copy.cur - offset, (size_t)0);
215+
break;
216+
case SingleIterator:
217+
copy.cur = 1;
218+
break;
219+
case NullPtrIterator:
220+
break;
221+
}
222+
return copy;
223+
}
224+
170225
bool Values::iterator::isLast()
171226
{
172227
switch (type) {
@@ -197,6 +252,11 @@ namespace Sass {
197252
return nullptr;
198253
}
199254

255+
Value* Values::iterator::operator->()
256+
{
257+
return Values::iterator::operator*();
258+
}
259+
200260
bool Values::iterator::operator==(const iterator& other) const
201261
{
202262
return val == other.val && cur == other.cur;

src/ast_nodes.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,13 @@ namespace Sass {
290290

291291
// Dereference current item
292292
reference operator*();
293+
reference operator->();
293294

294295
// Move to the next item
295296
iterator& operator++();
297+
iterator& operator+=(size_t offset);
298+
iterator& operator-=(size_t offset);
299+
iterator operator-(size_t offset);
296300

297301
// Check if it's the last item
298302
bool isLast();

src/fn_maps.cpp

Lines changed: 218 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "fn_maps.hpp"
55

66
#include "compiler.hpp"
7+
#include "exceptions.hpp"
78
#include "ast_values.hpp"
89

910
namespace Sass {
@@ -74,11 +75,111 @@ namespace Sass {
7475
{
7576
MapObj map = arguments[0]->assertMap(compiler, Strings::map);
7677
Value* key = arguments[1]->assertValue(compiler, Strings::key);
78+
79+
7780
auto it = map->find(key);
78-
if (it != map->end()) return it->second;
81+
if (it != map->end()) {
82+
auto rv = it->second;
83+
84+
auto inner = arguments[2]->iterator();
85+
auto cur = inner.begin(), end = inner.end();
86+
if (cur == end) {
87+
return rv;
88+
}
89+
while (cur != end) {
90+
if (auto deep = rv->isaMap()) {
91+
auto dada = deep->find(*cur);
92+
if (dada != deep->end()) {
93+
rv = dada.value();
94+
}
95+
else {
96+
return SASS_MEMORY_NEW(Null, pstate);
97+
}
98+
}
99+
else {
100+
return SASS_MEMORY_NEW(Null, pstate);
101+
}
102+
++cur;
103+
}
104+
105+
return rv;
106+
}
79107
return SASS_MEMORY_NEW(Null, pstate);
80108
}
81109

110+
111+
BUILT_IN_FN(fnMapSetThreeArgs)
112+
{
113+
auto map = arguments[0]->assertMap(compiler, Strings::map);
114+
auto copy = SASS_MEMORY_COPY(map);
115+
auto it = copy->find(arguments[1]);
116+
if (it == copy->end()) {
117+
copy->insert({ arguments[1], arguments[2] });
118+
}
119+
else {
120+
it.value() = arguments[2];
121+
}
122+
return copy;
123+
}
124+
125+
BUILT_IN_FN(fnMapSetTwoArgs)
126+
{
127+
auto map = arguments[0]->assertMap(compiler, Strings::map);
128+
MapObj copy = map = SASS_MEMORY_COPY(map);
129+
auto it = arguments[1]->iterator();
130+
auto size = arguments[1]->lengthAsList();
131+
auto cur = it.begin(), end = it.end();
132+
if (size == 0) {
133+
throw Exception::RuntimeException(compiler,
134+
"Expected $args to contain a key.");
135+
}
136+
else if (size == 1) {
137+
throw Exception::RuntimeException(compiler,
138+
"Expected $args to contain a value.");
139+
}
140+
141+
142+
while (cur != end) {
143+
auto qwe = *cur;
144+
auto it = map->find(qwe);
145+
if (it != map->end()) {
146+
ValueObj inner = it->second;
147+
if (auto qwe = inner->isaMap()) {
148+
map = qwe;
149+
}
150+
else {
151+
if (cur == end - 2) {
152+
++cur;
153+
it.value() = *cur;
154+
break;
155+
}
156+
else {
157+
auto newMap = SASS_MEMORY_NEW(Map,
158+
map->pstate(), {});
159+
it.value() = newMap;
160+
map = newMap;
161+
}
162+
}
163+
}
164+
else {
165+
if (cur == end - 2) {
166+
++cur;
167+
map->insert({ qwe, *cur });
168+
break;
169+
}
170+
else {
171+
auto newMap = SASS_MEMORY_NEW(Map,
172+
map->pstate(), {});
173+
map->insert({ qwe, newMap });
174+
map = newMap;
175+
}
176+
}
177+
++cur;
178+
}
179+
180+
return copy.detach();
181+
}
182+
82183
/*******************************************************************/
83184

84185
BUILT_IN_FN(merge)
@@ -99,6 +200,65 @@ namespace Sass {
99200
return copy;
100201
}
101202

203+
BUILT_IN_FN(merge_many)
204+
{
205+
MapObj map1 = arguments[0]->assertMap(compiler, Strings::map1);
206+
207+
auto it = arguments[1]->iterator();
208+
auto size = arguments[1]->lengthAsList();
209+
210+
if (size == 0) {
211+
throw Exception::RuntimeException(compiler,
212+
"Expected $args to contain a key.");
213+
}
214+
else if (size == 1) {
215+
throw Exception::RuntimeException(compiler,
216+
"Expected $args to contain a map.");
217+
}
218+
219+
auto cur = it.begin(), end = it.end() - 1;
220+
221+
Map *last = (end)->assertMap(compiler, Strings::map2);
222+
223+
MapObj copy = map1 = SASS_MEMORY_COPY(map1);
224+
225+
while (cur != end) {
226+
227+
auto it = copy->find(*cur);
228+
if (it != copy->end()) {
229+
if (auto inner = it->second->isaMap()) {
230+
copy = SASS_MEMORY_COPY(inner);
231+
it.value() = copy;
232+
}
233+
else {
234+
Map* empty = SASS_MEMORY_NEW(Map,
235+
it->second->pstate());
236+
it.value() = empty;
237+
copy = empty;
238+
}
239+
}
240+
else {
241+
Map* empty = SASS_MEMORY_NEW(Map,
242+
last->pstate());
243+
copy->insert({ *cur, empty });
244+
copy = empty;
245+
}
246+
247+
// if (!cur->isaMap()) {
248+
// *cur = SASS_MEMORY_NEW(Map, )
249+
// }
250+
251+
252+
++cur;
253+
}
254+
255+
for (auto kv : last->elements()) {
256+
copy->insertOrSet(kv); }
257+
return map1.detach();
258+
}
259+
260+
261+
102262
/*******************************************************************/
103263

104264
// Because the signature below has an explicit `$key` argument, it doesn't
@@ -157,7 +317,48 @@ namespace Sass {
157317
{
158318
MapObj map = arguments[0]->assertMap(compiler, Strings::map);
159319
Value* key = arguments[1]->assertValue(compiler, Strings::key);
160-
return SASS_MEMORY_NEW(Boolean, pstate, map->has(key));
320+
if (arguments[2]->lengthAsList() == 0) {
321+
return SASS_MEMORY_NEW(Boolean, pstate, map->has(key));
322+
}
323+
else {
324+
Map* current = map;
325+
auto first = current->find(key);
326+
if (first != current->end()) {
327+
current = first->second->isaMap();
328+
if (!current) {
329+
return SASS_MEMORY_NEW(Boolean, pstate, false);
330+
}
331+
auto it = arguments[2]->iterator();
332+
auto cur = it.begin(), end = it.end() - 1;
333+
Value* last = *end;
334+
while (cur != end) {
335+
auto inner = current->find(*cur);
336+
if (inner != current->end()) {
337+
if (auto imap = inner->second->isaMap()) {
338+
current = imap;
339+
}
340+
else {
341+
return SASS_MEMORY_NEW(Boolean, pstate, false);
342+
}
343+
}
344+
else {
345+
return SASS_MEMORY_NEW(Boolean, pstate, false);
346+
}
347+
++cur;
348+
}
349+
350+
// Still here, check now
351+
auto rv = current->find(last);
352+
return SASS_MEMORY_NEW(Boolean, pstate,
353+
rv != current->end());
354+
355+
}
356+
else {
357+
return SASS_MEMORY_NEW(Boolean, pstate, false);
358+
}
359+
360+
}
361+
return SASS_MEMORY_NEW(Boolean, pstate, false);
161362
}
162363

163364
BUILT_IN_FN(fnDeepMerge)
@@ -211,16 +412,27 @@ namespace Sass {
211412
void registerFunctions(Compiler& ctx)
212413
{
213414
Module& module(ctx.createModule("map"));
214-
// module.addFunction("get", ctx.registerBuiltInFunction("map-get", "$map, $key", set));
215-
module.addFunction("get", ctx.registerBuiltInFunction("map-get", "$map, $key", get));
216-
module.addFunction("merge", ctx.registerBuiltInFunction("map-merge", "$map1, $map2", merge));
415+
416+
module.addFunction("set", ctx.createBuiltInOverloadFns("map-set", {
417+
std::make_pair("$map, $key, $value", fnMapSetThreeArgs),
418+
std::make_pair("$map, $args...", fnMapSetTwoArgs)
419+
}));
420+
421+
module.addFunction("get", ctx.registerBuiltInFunction("map-get", "$map, $key, $keys...", get));
422+
423+
module.addFunction("merge", ctx.registerBuiltInOverloadFns("map-merge", {
424+
std::make_pair("$map1, $map2", merge),
425+
std::make_pair("$map1, $args...", merge_many)
426+
}));
427+
217428
module.addFunction("remove", ctx.registerBuiltInOverloadFns("map-remove", {
218429
std::make_pair("$map", remove_one),
219430
std::make_pair("$map, $key, $keys...", remove_many)
220431
}));
432+
221433
module.addFunction("keys", ctx.registerBuiltInFunction("map-keys", "$map", keys));
222434
module.addFunction("values", ctx.registerBuiltInFunction("map-values", "$map", values));
223-
module.addFunction("has-key", ctx.registerBuiltInFunction("map-has-key", "$map, $key", hasKey));
435+
module.addFunction("has-key", ctx.registerBuiltInFunction("map-has-key", "$map, $key, $keys...", hasKey));
224436

225437
module.addFunction("deep-merge", ctx.createBuiltInFunction("deep-merge", "$map1, $map2", fnDeepMerge));
226438
module.addFunction("deep-remove", ctx.createBuiltInFunction("deep-remove", "$map, $key, $keys...", fnDeepRemove));

src/fn_meta.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ namespace Sass {
4545

4646
/*******************************************************************/
4747

48+
BUILT_IN_FN(fnIf)
49+
{
50+
// Always evaluates both sides!
51+
return arguments[0]->isTruthy() ?
52+
arguments[1] : arguments[0];
53+
}
54+
4855
BUILT_IN_FN(keywords)
4956
{
5057
ArgumentList* argumentList = arguments[0]->assertArgumentList(compiler, Sass::Strings::args);
@@ -587,6 +594,7 @@ namespace Sass {
587594
module.addFunction("inspect", compiler.registerBuiltInFunction("inspect", "$value", inspect));
588595
module.addFunction("keywords", compiler.registerBuiltInFunction("keywords", "$args", keywords));
589596

597+
compiler.registerBuiltInFunction("if", "$condition, $if-true, $if-false", fnIf);
590598
// ToDo: dart-sass keeps them on the local environment scope, see below:
591599
// These functions are defined in the context of the evaluator because
592600
// they need access to the [_environment] or other local state.

0 commit comments

Comments
 (0)