Skip to content

Commit a8c3de5

Browse files
committed
Implement to_string for Value base
1 parent 10d2d30 commit a8c3de5

File tree

2 files changed

+255
-10
lines changed

2 files changed

+255
-10
lines changed

ast.cpp

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
#include "node.hpp"
44
#include "extend.hpp"
55
#include "to_string.hpp"
6+
#include "color_maps.hpp"
67
#include <set>
8+
#include <iomanip>
79
#include <algorithm>
810
#include <iostream>
911

@@ -1352,5 +1354,236 @@ namespace Sass {
13521354
else { return &sass_null; }
13531355
}
13541356

1357+
string Map::to_string(bool compressed, int precision) const
1358+
{
1359+
string res("");
1360+
if (empty()) return res;
1361+
if (is_invisible()) return res;
1362+
bool items_output = false;
1363+
for (auto key : keys()) {
1364+
if (key->is_invisible()) continue;
1365+
if (at(key)->is_invisible()) continue;
1366+
if (items_output) res += compressed ? "," : ", ";
1367+
Value* v_key = dynamic_cast<Value*>(key);
1368+
Value* v_val = dynamic_cast<Value*>(at(key));
1369+
if (v_key) res += v_key->to_string(compressed, precision);
1370+
res += compressed ? ":" : ": ";
1371+
if (v_val) res += v_val->to_string(compressed, precision);
1372+
items_output = true;
1373+
}
1374+
return res;
1375+
}
1376+
1377+
string List::to_string(bool compressed, int precision) const
1378+
{
1379+
string res("");
1380+
if (empty()) return res;
1381+
if (is_invisible()) return res;
1382+
bool items_output = false;
1383+
string sep = separator() == SASS_COMMA ? "," : " ";
1384+
if (!compressed && sep == ",") sep += " ";
1385+
for (size_t i = 0, L = size(); i < L; ++i) {
1386+
Expression* item = (*this)[i];
1387+
if (item->is_invisible()) continue;
1388+
if (items_output) res += sep;
1389+
if (Value* v_val = dynamic_cast<Value*>(item))
1390+
{ res += v_val->to_string(compressed, precision); }
1391+
items_output = true;
1392+
}
1393+
return res;
1394+
}
1395+
1396+
string String_Schema::to_string(bool compressed, int precision) const
1397+
{
1398+
string res("");
1399+
for (size_t i = 0, L = length(); i < L; ++i) {
1400+
if ((*this)[i]->is_interpolant()) res += "#{";
1401+
if (Value* val = dynamic_cast<Value*>((*this)[i]))
1402+
{ res += val->to_string(compressed, precision); }
1403+
if ((*this)[i]->is_interpolant()) res += "}";
1404+
}
1405+
return res;
1406+
}
1407+
1408+
string Null::to_string(bool compressed, int precision) const
1409+
{
1410+
return "null";
1411+
}
1412+
1413+
string Boolean::to_string(bool compressed, int precision) const
1414+
{
1415+
return value_ ? "true" : "false";
1416+
}
1417+
1418+
// helper function for serializing colors
1419+
template <size_t range>
1420+
static double cap_channel(double c) {
1421+
if (c > range) return range;
1422+
else if (c < 0) return 0;
1423+
else return c;
1424+
}
1425+
1426+
string Color::to_string(bool compressed, int precision) const
1427+
{
1428+
stringstream ss;
1429+
1430+
// original color name
1431+
// maybe an unknown token
1432+
string name = disp();
1433+
1434+
// resolved color
1435+
string res_name = name;
1436+
1437+
double r = round(cap_channel<0xff>(r_));
1438+
double g = round(cap_channel<0xff>(g_));
1439+
double b = round(cap_channel<0xff>(b_));
1440+
double a = cap_channel<1> (a_);
1441+
1442+
// get color from given name (if one was given at all)
1443+
if (name != "" && names_to_colors.count(name)) {
1444+
Color* n = names_to_colors.find(name)->second;
1445+
r = round(cap_channel<0xff>(n->r()));
1446+
g = round(cap_channel<0xff>(n->g()));
1447+
b = round(cap_channel<0xff>(n->b()));
1448+
a = cap_channel<1> (n->a());
1449+
}
1450+
// otherwise get the possible resolved color name
1451+
else {
1452+
int numval = static_cast<int>(r) * 0x10000 + static_cast<int>(g) * 0x100 + static_cast<int>(b);
1453+
if (colors_to_names.count(numval))
1454+
res_name = colors_to_names.find(numval)->second;
1455+
}
1456+
1457+
stringstream hexlet;
1458+
hexlet << '#' << setw(1) << setfill('0');
1459+
// create a short color hexlet if there is any need for it
1460+
if (compressed && is_color_doublet(r, g, b) && a == 1) {
1461+
hexlet << hex << setw(1) << (static_cast<unsigned long>(r) >> 4);
1462+
hexlet << hex << setw(1) << (static_cast<unsigned long>(g) >> 4);
1463+
hexlet << hex << setw(1) << (static_cast<unsigned long>(b) >> 4);
1464+
} else {
1465+
hexlet << hex << setw(2) << static_cast<unsigned long>(r);
1466+
hexlet << hex << setw(2) << static_cast<unsigned long>(g);
1467+
hexlet << hex << setw(2) << static_cast<unsigned long>(b);
1468+
}
1469+
1470+
if (compressed && !this->is_delayed()) name = "";
1471+
1472+
// retain the originally specified color definition if unchanged
1473+
if (name != "") {
1474+
ss << name;
1475+
}
1476+
else if (r == 0 && g == 0 && b == 0 && a == 0) {
1477+
ss << "transparent";
1478+
}
1479+
else if (a >= 1) {
1480+
if (res_name != "") {
1481+
if (compressed && hexlet.str().size() < res_name.size()) {
1482+
ss << hexlet.str();
1483+
} else {
1484+
ss << res_name;
1485+
}
1486+
}
1487+
else {
1488+
ss << hexlet.str();
1489+
}
1490+
}
1491+
else {
1492+
ss << "rgba(";
1493+
ss << static_cast<unsigned long>(r) << ",";
1494+
if (!compressed) ss << " ";
1495+
ss << static_cast<unsigned long>(g) << ",";
1496+
if (!compressed) ss << " ";
1497+
ss << static_cast<unsigned long>(b) << ",";
1498+
if (!compressed) ss << " ";
1499+
ss << a << ')';
1500+
}
1501+
1502+
return ss.str();
1503+
1504+
}
1505+
1506+
string Number::to_string(bool compressed, int precision) const
1507+
{
1508+
1509+
string res;
1510+
1511+
// check if the fractional part of the value equals to zero
1512+
// neat trick from http://stackoverflow.com/a/1521682/1550314
1513+
// double int_part; bool is_int = modf(value, &int_part) == 0.0;
1514+
1515+
// this all cannot be done with one run only, since fixed
1516+
// output differs from normal output and regular output
1517+
// can contain scientific notation which we do not want!
1518+
1519+
// first sample
1520+
stringstream ss;
1521+
ss.precision(12);
1522+
ss << value_;
1523+
1524+
// check if we got scientific notation in result
1525+
if (ss.str().find_first_of("e") != string::npos) {
1526+
ss.clear(); ss.str(string());
1527+
ss.precision(max(12, precision));
1528+
ss << fixed << value_;
1529+
}
1530+
1531+
string tmp = ss.str();
1532+
size_t pos_point = tmp.find_first_of(".,");
1533+
size_t pos_fract = tmp.find_last_not_of("0");
1534+
bool is_int = pos_point == pos_fract ||
1535+
pos_point == string::npos;
1536+
1537+
// reset stream for another run
1538+
ss.clear(); ss.str(string());
1539+
1540+
// take a shortcut for integers
1541+
if (is_int)
1542+
{
1543+
ss.precision(0);
1544+
ss << fixed << value_;
1545+
res = string(ss.str());
1546+
}
1547+
// process floats
1548+
else
1549+
{
1550+
// do we have have too much precision?
1551+
if (pos_fract < precision + pos_point)
1552+
{ precision = pos_fract - pos_point; }
1553+
// round value again
1554+
ss.precision(precision);
1555+
ss << fixed << value_;
1556+
res = string(ss.str());
1557+
// maybe we truncated up to decimal point
1558+
size_t pos = res.find_last_not_of("0");
1559+
bool at_dec_point = res[pos] == '.' ||
1560+
res[pos] == ',';
1561+
// don't leave a blank point
1562+
if (at_dec_point) ++ pos;
1563+
res.resize (pos + 1);
1564+
}
1565+
1566+
// some final cosmetics
1567+
if (res == "-0.0") res.erase(0, 1);
1568+
else if (res == "-0") res.erase(0, 1);
1569+
1570+
// add unit now
1571+
res += unit();
1572+
1573+
// and return
1574+
return res;
1575+
1576+
}
1577+
1578+
string String_Quoted::to_string(bool compressed, int precision) const
1579+
{
1580+
return quote_mark_ ? quote(value_, quote_mark_, true) : value_;
1581+
}
1582+
1583+
string String_Constant::to_string(bool compressed, int precision) const
1584+
{
1585+
return quote_mark_ ? quote(value_, quote_mark_, true) : value_;
1586+
}
1587+
13551588
}
13561589

ast.hpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ namespace Sass {
142142
{ }
143143
virtual bool operator== (Expression& rhs) const = 0;
144144
virtual bool operator== (Expression* rhs) const = 0;
145+
virtual string to_string(bool compressed = false, int precision = 5) const = 0;
145146
};
146147
}
147148

@@ -791,8 +792,6 @@ namespace Sass {
791792
Expression* value_at_index(size_t i);
792793

793794
virtual size_t size() const;
794-
virtual bool operator==(Expression& rhs) const;
795-
virtual bool operator==(Expression* rhs) const;
796795

797796
virtual size_t hash()
798797
{
@@ -811,6 +810,10 @@ namespace Sass {
811810
is_delayed(delayed);
812811
}
813812

813+
virtual bool operator==(Expression& rhs) const;
814+
virtual bool operator==(Expression* rhs) const;
815+
virtual string to_string(bool compressed = false, int precision = 5) const;
816+
814817
ATTACH_OPERATIONS()
815818
};
816819

@@ -843,6 +846,7 @@ namespace Sass {
843846

844847
virtual bool operator== (Expression& rhs) const;
845848
virtual bool operator== (Expression* rhs) const;
849+
virtual string to_string(bool compressed = false, int precision = 5) const;
846850

847851
ATTACH_OPERATIONS()
848852
};
@@ -1192,11 +1196,6 @@ namespace Sass {
11921196
// useful for making one number compatible with another
11931197
string find_convertible_unit() const;
11941198

1195-
virtual bool operator< (Number& rhs) const;
1196-
virtual bool operator< (Number* rhs) const;
1197-
virtual bool operator== (Expression& rhs) const;
1198-
virtual bool operator== (Expression* rhs) const;
1199-
12001199
virtual size_t hash()
12011200
{
12021201
if (hash_ == 0) {
@@ -1205,6 +1204,12 @@ namespace Sass {
12051204
return hash_;
12061205
}
12071206

1207+
virtual bool operator< (Number& rhs) const;
1208+
virtual bool operator< (Number* rhs) const;
1209+
virtual bool operator== (Expression& rhs) const;
1210+
virtual bool operator== (Expression* rhs) const;
1211+
virtual string to_string(bool compressed = false, int precision = 5) const;
1212+
12081213
ATTACH_OPERATIONS()
12091214
};
12101215

@@ -1240,6 +1245,7 @@ namespace Sass {
12401245

12411246
virtual bool operator== (Expression& rhs) const;
12421247
virtual bool operator== (Expression* rhs) const;
1248+
virtual string to_string(bool compressed = false, int precision = 5) const;
12431249

12441250
ATTACH_OPERATIONS()
12451251
};
@@ -1270,6 +1276,7 @@ namespace Sass {
12701276

12711277
virtual bool operator== (Expression& rhs) const;
12721278
virtual bool operator== (Expression* rhs) const;
1279+
virtual string to_string(bool compressed = false, int precision = 5) const;
12731280

12741281
ATTACH_OPERATIONS()
12751282
};
@@ -1288,6 +1295,7 @@ namespace Sass {
12881295
virtual ~String() = 0;
12891296
virtual bool operator==(Expression& rhs) const = 0;
12901297
virtual bool operator==(Expression* rhs) const = 0;
1298+
virtual string to_string(bool compressed = false, int precision = 5) const = 0;
12911299
ATTACH_OPERATIONS()
12921300
};
12931301
inline String::~String() { };
@@ -1317,6 +1325,7 @@ namespace Sass {
13171325

13181326
virtual bool operator==(Expression& rhs) const;
13191327
virtual bool operator==(Expression* rhs) const;
1328+
virtual string to_string(bool compressed = false, int precision = 5) const;
13201329

13211330
ATTACH_OPERATIONS()
13221331
};
@@ -1356,6 +1365,7 @@ namespace Sass {
13561365

13571366
virtual bool operator==(Expression& rhs) const;
13581367
virtual bool operator==(Expression* rhs) const;
1368+
virtual string to_string(bool compressed = false, int precision = 5) const;
13591369

13601370
// static char auto_quote() { return '*'; }
13611371
static char double_quote() { return '"'; }
@@ -1376,6 +1386,7 @@ namespace Sass {
13761386
}
13771387
virtual bool operator==(Expression& rhs) const;
13781388
virtual bool operator==(Expression* rhs) const;
1389+
virtual string to_string(bool compressed = false, int precision = 5) const;
13791390
ATTACH_OPERATIONS()
13801391
};
13811392

@@ -1534,14 +1545,15 @@ namespace Sass {
15341545
operator bool() { return false; }
15351546
bool is_false() { return true; }
15361547

1537-
virtual bool operator== (Expression& rhs) const;
1538-
virtual bool operator== (Expression* rhs) const;
1539-
15401548
virtual size_t hash()
15411549
{
15421550
return -1;
15431551
}
15441552

1553+
virtual bool operator== (Expression& rhs) const;
1554+
virtual bool operator== (Expression* rhs) const;
1555+
virtual string to_string(bool compressed = false, int precision = 5) const;
1556+
15451557
ATTACH_OPERATIONS()
15461558
};
15471559

0 commit comments

Comments
 (0)