Commit 37dbd22d by Aaron Leung

More Node reorganization. Pretty clean now. I guess.

parent 6e2de911
#include <iostream> #include <sstream>
#include <iomanip>
#include <string>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include "node.hpp" #include "node.hpp"
using std::string;
using std::stringstream;
using std::cout;
using std::cerr;
using std::endl;
namespace Sass { namespace Sass {
size_t Node::allocations = 0; using namespace std;
size_t Node::destructed = 0;
Node Node::clone(vector<vector<Node>*>& registry) const
{
Node n(*this);
if (has_children) {
n.content.children = new vector<Node>;
++allocations;
n.content.children->reserve(size());
for (size_t i = 0; i < size(); ++i) {
n << at(i).clone(registry);
}
registry.push_back(n.content.children);
}
return n;
}
string Node::to_string(const string& prefix) const
{
switch (type)
{
case selector_group: { // really only needed for arg to :not
string result(at(0).to_string(""));
for (size_t i = 1; i < size(); ++i) {
result += ", ";
result += at(i).to_string("");
}
return result;
} break;
case selector: { // Node::Node(Node_Impl* ip) : ip_(ip) { }
string result; //
if (!has_backref && !prefix.empty()) { // inline Node_Type Node::type() { return ip_->type; }
result += prefix; //
result += ' '; // inline bool Node::has_children() { return ip_->has_children; }
} // inline bool Node::has_statements() { return ip_->has_statements; }
// inline bool Node::has_blocks() { return ip_->has_blocks; }
// if (at(0).type == selector_combinator) { // inline bool Node::has_expansions() { return ip_->has_expansions; }
// result += at(0).content.token.to_string(); // inline bool Node::has_backref() { return ip_->has_backref; }
// result += ' '; // inline bool Node::from_variable() { return ip_->from_variable; }
// inline bool Node::eval_me() { return ip_->eval_me; }
// inline bool Node::is_unquoted() { return ip_->is_unquoted; }
// inline bool Node::is_numeric() { return ip_->is_numeric(); }
//
// inline string Node::file_name() const { return *(ip_->file_name); }
// inline size_t Node::line_number() const { return ip_->line_number; }
// inline size_t Node::size() const { return ip_->size(); }
//
// inline Node& Node::at(size_t i) const { return ip_->at(i); }
// inline Node& Node::operator[](size_t i) const { return at(i); }
// inline Node& Node::pop_back() { return ip_->pop_back(); }
// inline Node& Node::push_back(Node n)
// {
// ip_->push_back(n);
// return *this;
// } // }
// else { // inline Node& Node::operator<<(Node n) { return push_back(n); }
// Node::Type t = at(0).type; // inline Node& Node::operator+=(Node n)
// result += at(0).to_string(t == backref ? prefix : ""); // {
// for (size_t i = 0, L = n.size(); i < L; ++i) push_back(n[i]);
// return *this;
// } // }
// inline bool Node::boolean_value() { return ip_->boolean_value(); }
// inline double Node::numeric_value() { return ip_->numeric_value(); }
result += at(0).to_string(at(0).has_backref ? prefix : ""); // ------------------------------------------------------------------------
// Token method implementations
for (size_t i = 1; i < size(); ++i) { // ------------------------------------------------------------------------
result += " ";
result += at(i).to_string(at(i).has_backref ? prefix : "");
}
return result;
} break;
case selector_combinator: {
string result(prefix.empty() ? "" : prefix + " ");
result += content.token.to_string();
return result;
// return content.token.to_string();
// if (std::isspace(content.token.begin[0])) return string(" ");
// else return string(content.token);
} break;
case simple_selector_sequence: { string Token::unquote() const
{
string result; string result;
if (!has_backref && !prefix.empty()) { const char* p = begin;
result += prefix; if (*begin == '\'' || *begin == '"') {
result += " "; ++p;
} while (p < end) {
for (size_t i = 0; i < size(); ++i) { if (*p == '\\') {
Node::Type t = at(i).type; switch (*(++p)) {
result += at(i).to_string(t == backref ? prefix : ""); case 'n': result += '\n'; break;
} case 't': result += '\t'; break;
return result; case 'b': result += '\b'; break;
} break; case 'r': result += '\r'; break;
case 'f': result += '\f'; break;
case pseudo: case 'v': result += '\v'; break;
case simple_selector: { case 'a': result += '\a'; break;
string result(prefix); case '\\': result += '\\'; break;
if (!prefix.empty()) result += " "; default: result += *p; break;
result += content.token.to_string(); }
return result; }
} break; else if (p == end - 1) {
case pseudo_negation: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += at(0).to_string("");
result += at(1).to_string("");
result += ')';
return result; return result;
} break;
case functional_pseudo: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += at(0).to_string("");
for (size_t i = 1; i < size(); ++i) {
result += at(i).to_string("");
} }
result += ')'; else {
return result; result += *p;
} break;
case attribute_selector: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += "[";
for (size_t i = 0; i < size(); ++i)
{ result += at(i).to_string(prefix); }
result += ']';
return result;
} break;
case backref: {
return prefix;
} break;
case comma_list: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (at(i).type == nil) continue;
result += ", ";
result += at(i).to_string(prefix);
} }
return result; ++p;
} break;
case space_list: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (at(i).type == nil) continue;
result += " ";
result += at(i).to_string(prefix);
} }
return result; return result;
} break;
case expression:
case term: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (!(at(i).type == add ||
// at(i).type == sub || // another edge case -- consider uncommenting
at(i).type == mul)) {
result += at(i).to_string(prefix);
} }
else {
while (p < end) {
result += *(p++);
} }
return result; return result;
} break;
//edge case
case sub: {
return "-";
} break;
case div: {
return "/";
} break;
case css_import: {
stringstream ss;
ss << "@import url(";
ss << at(0).to_string("");
// cerr << content.token.to_string() << endl;
ss << ")";
return ss.str();
} }
case function_call: {
stringstream ss;
ss << at(0).to_string("");
ss << "(";
ss << at(1).to_string("");
ss << ")";
return ss.str();
} }
case arguments: { void Token::unquote_to_stream(std::stringstream& buf) const
stringstream ss; {
if (size() > 0) { const char* p = begin;
ss << at(0).to_string(""); if (*begin == '\'' || *begin == '"') {
for (size_t i = 1; i < size(); ++i) { ++p;
ss << ", "; while (p < end) {
ss << at(i).to_string(""); if (*p == '\\') {
switch (*(++p)) {
case 'n': buf << '\n'; break;
case 't': buf << '\t'; break;
case 'b': buf << '\b'; break;
case 'r': buf << '\r'; break;
case 'f': buf << '\f'; break;
case 'v': buf << '\v'; break;
case 'a': buf << '\a'; break;
case '\\': buf << '\\'; break;
default: buf << *p; break;
}
}
else if (p == end - 1) {
return;
} }
else {
buf << *p;
} }
return ss.str(); ++p;
} }
return;
case unary_plus: {
stringstream ss;
ss << "+";
ss << at(0).to_string("");
return ss.str();
} }
else {
case unary_minus: { while (p < end) {
stringstream ss; buf << *(p++);
ss << "-"; }
ss << at(0).to_string(""); return;
return ss.str();
} }
case numeric_percentage: {
stringstream ss;
ss << content.dimension.numeric_value;
ss << '%';
return ss.str();
} }
case numeric_dimension: { bool Token::operator<(const Token& rhs) const
stringstream ss;
ss << content.dimension.numeric_value;
ss << string(content.dimension.unit, 2);
// << string(content.dimension.unit, Prelexer::identifier(content.dimension.unit) - content.dimension.unit);
// cerr << Token::make(content.dimension.unit, content.dimension.unit + 2).to_string();
// << Token::make(content.dimension.unit, Prelexer::identifier(content.dimension.unit)).to_string();
return ss.str();
} break;
case number: {
stringstream ss;
ss << content.numeric_value;
return ss.str();
} break;
case numeric_color: {
if (at(3).content.numeric_value >= 1.0) {
double a = at(0).content.numeric_value;
double b = at(1).content.numeric_value;
double c = at(2).content.numeric_value;
if (a >= 0xff && b >= 0xff && c >= 0xff)
{ return "white"; }
else if (a >= 0xff && b >= 0xff && c == 0)
{ return "yellow"; }
else if (a == 0 && b >= 0xff && c >= 0xff)
{ return "aqua"; }
else if (a >= 0xff && b == 0 && c >= 0xff)
{ return "fuchsia"; }
else if (a >= 0xff && b == 0 && c == 0)
{ return "red"; }
else if (a == 0 && b >= 0xff && c == 0)
{ return "lime"; }
else if (a == 0 && b == 0 && c >= 0xff)
{ return "blue"; }
else if (a <= 0 && b <= 0 && c <= 0)
{ return "black"; }
else
{ {
stringstream ss; const char* first1 = begin;
ss << '#' << std::setw(2) << std::setfill('0') << std::hex; const char* last1 = end;
for (size_t i = 0; i < 3; ++i) { const char* first2 = rhs.begin;
double x = at(i).content.numeric_value; const char* last2 = rhs.end;
if (x > 0xff) x = 0xff; while (first1!=last1)
else if (x < 0) x = 0; {
ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(x+0.5)); if (first2==last2 || *first2<*first1) return false;
} else if (*first1<*first2) return true;
return ss.str(); first1++; first2++;
}
}
else {
stringstream ss;
ss << "rgba(" << static_cast<unsigned long>(at(0).content.numeric_value);
for (size_t i = 1; i < 3; ++i) {
ss << ", " << static_cast<unsigned long>(at(i).content.numeric_value);
} }
ss << ", " << at(3).content.numeric_value << ')'; return (first2!=last2);
return ss.str();
} }
} break;
case uri: { bool Token::operator==(const Token& rhs) const
string result("url("); {
result += string(content.token); if (length() != rhs.length()) return false;
result += ")";
return result;
} break;
// case expansion: {
// string result("MIXIN CALL: ");
// return result;
// } break;
case string_constant: {
if (unquoted) return content.token.unquote();
else {
string result(content.token.to_string());
if (result[0] != '"' && result[0] != '\'') return "\"" + result + "\"";
else return result;
}
} break;
case boolean: {
if (content.boolean_value) return "true";
else return "false";
} break;
case important: {
return "!important";
} break;
case value_schema: { if ((begin[0] == '"' || begin[0] == '\'') &&
string result; (rhs.begin[0] == '"' || rhs.begin[0] == '\''))
for (size_t i = 0; i < size(); ++i) result += at(i).to_string(""); { return unquote() == rhs.unquote(); }
return result;
} break;
case string_schema: { const char* p = begin;
string result; const char* q = rhs.begin;
for (size_t i = 0; i < size(); ++i) { for (; p < end; ++p, ++q) if (*p != *q) return false;
string chunk(at(i).to_string("")); return true;
if (at(i).type == string_constant) {
result += chunk.substr(1, chunk.size()-2);
}
else {
result += chunk;
}
} }
return result;
} break;
default: {
// return content.token.to_string();
if (!has_children && type != flags) return content.token.to_string();
else return "";
} break;
}
}
void Node::echo(stringstream& buf, size_t depth) { // ------------------------------------------------------------------------
string indentation(2*depth, ' '); // Node_Impl method implementations
switch (type) { // ------------------------------------------------------------------------
case comment:
buf << indentation << string(content.token) << endl;
break;
case ruleset:
buf << indentation;
at(0).echo(buf, depth);
at(1).echo(buf, depth);
break;
case selector_group:
at(0).echo(buf, depth);
for (size_t i = 1; i < size(); ++i) {
buf << ", ";
at(i).echo(buf, depth);
}
break;
case selector:
for (size_t i = 0; i < size(); ++i) {
at(i).echo(buf, depth);
}
break;
case selector_combinator:
if (std::isspace(content.token.begin[0])) buf << ' ';
else buf << ' ' << string(content.token) << ' ';
break;
case simple_selector_sequence:
for (size_t i = 0; i < size(); ++i) {
buf << at(i).to_string(string());
}
break;
case simple_selector:
buf << string(content.token);
break;
case block:
buf << " {" << endl;
for (size_t i = 0; i < size(); at(i++).echo(buf, depth+1)) ;
buf << indentation << "}" << endl;
break;
case rule:
buf << indentation;
at(0).echo(buf, depth);
buf << ": ";
at(1).echo(buf, depth);
buf << ';' << endl;
break;
case property:
buf << string(content.token);
break;
case values:
for (size_t i = 0; i < size(); at(i++).echo(buf, depth)) ;
break;
case value:
buf << ' ' << string(content.token);
break;
default:
break;
}
}
void Node::emit_nested_css(stringstream& buf, // inline bool Node_Impl::is_numeric()
size_t depth, // { return type >= number && type <= numeric_dimension; }
const vector<string>& prefixes) //
// inline size_t Node_Impl::size()
// { return children.size(); }
//
// inline Node& Node_Impl::at(size_t i)
// { return children.at(i); }
//
// inline Node& Node_Impl::back()
// { return children.back(); }
//
// inline void Node_Impl::push_back(const Node& n)
// { children.push_back(n); }
//
// inline Node& Node_Impl::pop_back()
// { children.pop_back(); }
//
// inline bool Node_Impl::boolean_value()
// { return value.boolean; }
//
double Node_Impl::numeric_value()
{ {
switch (type) switch (type)
{ {
case root: case number:
if (at(0).has_expansions) { case numeric_percentage:
flatten(); return value.numeric;
} case numeric_dimension:
for (size_t i = 0; i < size(); ++i) { return value.dimension.numeric;
at(i).emit_nested_css(buf, depth, prefixes);
//if (at(i).type == css_import) buf << endl;
}
break;
case ruleset: {
Node sel_group(at(0));
size_t sel_group_size = (sel_group.type == selector_group) ? sel_group.size() : 1; // parser ensures no singletons
Node block(at(1));
vector<string> new_prefixes;
if (prefixes.empty()) {
new_prefixes.reserve(sel_group_size);
for (size_t i = 0; i < sel_group_size; ++i) {
new_prefixes.push_back(sel_group_size > 1 ? sel_group[i].to_string(string()) : sel_group.to_string(string()));
}
}
else {
new_prefixes.reserve(prefixes.size() * sel_group_size);
for (size_t i = 0; i < prefixes.size(); ++i) {
for (size_t j = 0; j < sel_group_size; ++j) {
new_prefixes.push_back(sel_group_size > 1 ? sel_group[j].to_string(prefixes[i]) : sel_group.to_string(prefixes[i]));
}
}
}
if (block[0].has_expansions) block.flatten();
if (block[0].has_statements) {
buf << string(2*depth, ' ') << new_prefixes[0];
for (size_t i = 1; i < new_prefixes.size(); ++i) {
buf << ", " << new_prefixes[i];
}
buf << " {";
for (size_t i = 0; i < block.size(); ++i) {
Type stm_type = block[i].type;
if (stm_type == comment || stm_type == rule || stm_type == css_import || stm_type == propset) {
block[i].emit_nested_css(buf, depth+1); // NEED OVERLOADED VERSION FOR COMMENTS AND RULES
}
// else if (stm_type == propset) {
// block[i].emit_nested_css(buf, depth, new_prefixes);
// }
}
buf << " }" << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets
}
if (block[0].has_blocks) {
for (size_t i = 0; i < block.size(); ++i) {
if (block[i].type == ruleset) {
block[i].emit_nested_css(buf, depth, new_prefixes);
}
}
}
if (block[0].has_statements) --depth; // see previous comment
if (depth == 0 && prefixes.empty()) buf << endl;
} break;
default: default:
emit_nested_css(buf, depth); // pass it along to the simpler version
break; break;
// throw an exception?
} }
return 0;
} }
void Node::emit_nested_css(stringstream& buf, size_t depth) string Node_Impl::unit()
{ {
switch (type) switch (type)
{ {
case propset: { case numeric_percentage: {
emit_propset(buf, depth, ""); return "\"%\"";
} break; } break;
case rule: case numeric_dimension: {
buf << endl << string(2*depth, ' '); string result("\"");
at(0).emit_nested_css(buf, depth); // property result += value.dimension.unit.to_string();
at(1).emit_nested_css(buf, depth); // values result += "\"";
buf << ";"; return result;
break; } break;
case css_import:
buf << string(2*depth, ' ');
buf << to_string("");
buf << ";" << endl;
break;
case property:
buf << string(content.token) << ": ";
break;
case values:
for (size_t i = 0; i < size(); ++i) {
buf << " " << string(at(i).content.token);
}
break;
case comment:
if (depth != 0) buf << endl;
buf << string(2*depth, ' ') << string(content.token);
if (depth == 0) buf << endl;
break;
default:
buf << to_string("");
break;
}
}
void Node::emit_propset(stringstream& buf, size_t depth, const string& prefix) {
string new_prefix(prefix);
bool has_prefix = false;
if (new_prefix.empty()) {
new_prefix += "\n";
new_prefix += string(2*depth, ' ');
new_prefix += at(0).content.token.to_string();
}
else {
new_prefix += "-";
new_prefix += at(0).content.token.to_string();
has_prefix = true;
}
Node rules(at(1));
for (size_t i = 0; i < rules.size(); ++i) {
if (rules[i].type == propset) {
rules[i].emit_propset(buf, depth+1, new_prefix);
}
else {
buf << new_prefix;
if (rules[i][0].content.token.to_string() != "") buf << '-';
rules[i][0].emit_nested_css(buf, depth);
rules[i][1].emit_nested_css(buf, depth);
buf << ';';
}
}
}
void Node::emit_expanded_css(stringstream& buf, const string& prefix) {
// switch (type) {
// case selector:
// if (!prefix.empty()) buf << " ";
// buf << string(token);
// break;
// case comment:
// if (!prefix.empty()) buf << " ";
// buf << string(token) << endl;
// break;
// case property:
// buf << string(token) << ":";
// break;
// case values:
// for (size_t i = 0; i < children.size(); ++i) {
// buf << " " << string(children[i].token);
// }
// break;
// case rule:
// buf << " ";
// children[0].emit_expanded_css(buf, prefix);
// children[1].emit_expanded_css(buf, prefix);
// buf << ";" << endl;
// break;
// case clauses:
// if (children.size() > 0) {
// buf << " {" << endl;
// for (size_t i = 0; i < children.size(); ++i)
// children[i].emit_expanded_css(buf, prefix);
// buf << "}" << endl;
// }
// for (size_t i = 0; i < opt_children.size(); ++i)
// opt_children[i].emit_expanded_css(buf, prefix);
// break;
// case ruleset:
// // buf << prefix;
// if (children[1].children.size() > 0) {
// buf << prefix << (prefix.empty() ? "" : " ");
// children[0].emit_expanded_css(buf, "");
// }
// string newprefix(prefix.empty() ? prefix : prefix + " ");
// children[1].emit_expanded_css(buf, newprefix + string(children[0].token));
// if (prefix.empty()) buf << endl;
// break;
// }
default: break;
} }
return "\"\"";
void Node::flatten()
{
if (type != block && type != expansion && type != root) return;
for (size_t i = 0; i < size(); ++i) {
if (at(i).type == expansion) {
Node expn = at(i);
if (expn[0].has_expansions) expn.flatten();
at(0).has_statements |= expn[0].has_statements;
at(0).has_blocks |= expn[0].has_blocks;
at(0).has_expansions |= expn[0].has_expansions;
at(i).type = none;
content.children->insert(content.children->begin() + i,
expn.content.children->begin(),
expn.content.children->end());
} }
}
}
//
// void flatten_block(Node& block)
// {
//
// for (size_t i = 0; i < block.size(); ++i) {
//
// if (block[i].type == Node::expansion
//
// }
//
//
//
// }
} }
\ No newline at end of file
#define SASS_NODE_INCLUDED #define SASS_NODE_INCLUDED
#include <string>
#include <vector> #include <vector>
#include <sstream>
#include "values.hpp"
#include <iostream>
namespace Sass { namespace Sass {
using std::string; using namespace std;
using std::vector;
using std::stringstream; enum Node_Type {
using std::cerr; using std::endl; none,
flags,
comment,
struct Node {
enum Type {
root, root,
ruleset, ruleset,
propset, propset,
...@@ -71,9 +69,9 @@ namespace Sass { ...@@ -71,9 +69,9 @@ namespace Sass {
textual_hex, textual_hex,
color_name, color_name,
string_constant, string_constant,
number,
numeric_percentage, numeric_percentage,
numeric_dimension, numeric_dimension,
number,
numeric_color, numeric_color,
boolean, boolean,
important, important,
...@@ -89,193 +87,200 @@ namespace Sass { ...@@ -89,193 +87,200 @@ namespace Sass {
arguments, arguments,
variable, variable,
assignment, assignment
};
comment, struct Node_Impl;
none,
flags class Node {
private:
friend class Node_Factory;
Node_Impl* ip_;
// private constructors; must use a Node_Factory
Node();
Node(Node_Impl* ip); // : ip_(ip) { }
public:
Node_Type type(); // { return ip_->type; }
bool has_children(); // { return ip_->has_children; }
bool has_statements(); // { return ip_->has_statements; }
bool has_blocks(); // { return ip_->has_blocks; }
bool has_expansions(); // { return ip_->has_expansions; }
bool has_backref(); // { return ip_->has_backref; }
bool from_variable(); // { return ip_->from_variable; }
bool eval_me(); // { return ip_->eval_me; }
bool is_unquoted(); // { return ip_->is_unquoted; }
bool is_numeric(); // { return ip_->is_numeric(); }
string file_name() const; // { return *(ip_->file_name); }
size_t line_number() const; // { return ip_->line_number; }
size_t size() const; // { return ip_->size(); }
Node& at(size_t i) const; // { return ip_->at(i); }
Node& operator[](size_t i) const; // { return at(i); }
void pop_back(); // { return ip_->pop_back(); }
Node& push_back(Node n);
// { ip_->push_back(n); return *this; }
Node& operator<<(Node n);
// { return push_back(n); }
Node& operator+=(Node n);
// {
// for (size_t i = 0, L = n.size(); i < L; ++i) push_back(n[i]);
// return *this;
// }
bool boolean_value(); // { return ip_->boolean_value(); }
double numeric_value(); // { return ip_->numeric_value(); }
}; };
Type type; struct Token {
unsigned int line_number; const char* begin;
const char* end;
bool has_children; // Need Token::make(...) because tokens are union members, and hence they
bool has_statements; // can't have non-trivial constructors.
bool has_blocks; static Token make()
bool has_expansions; {
bool has_backref; Token t;
bool from_variable; t.begin = 0;
bool eval_me; t.end = 0;
bool unquoted; return t;
}
static Token make(const char* s)
{
Token t;
t.begin = s;
t.end = s + std::strlen(s);
return t;
}
static Token make(const char* b, const char* e)
{
Token t;
t.begin = b;
t.end = e;
return t;
}
size_t length() const
{ return end - begin; }
string to_string() const
{ return string(begin, end - begin); }
string unquote() const;
void unquote_to_stream(std::stringstream& buf) const;
operator bool()
{ return begin && end && begin >= end; }
bool operator<(const Token& rhs) const;
bool operator==(const Token& rhs) const;
};
struct Dimension {
double numeric;
Token unit;
};
struct Node_Impl {
union { union {
bool boolean;
double numeric;
Token token; Token token;
mutable vector<Node>* children;
Dimension dimension; Dimension dimension;
double numeric_value; } value;
bool boolean_value;
} content;
const char* file_name; vector<Node> children;
static size_t allocations; string* file_name;
static size_t destructed; size_t line_number;
void clear() Node_Type type;
{
type = none; line_number = 0; file_name = 0;
has_children = false; has_statements = false; has_blocks = false;
has_expansions = false; has_backref = false; from_variable = false;
eval_me = false; unquoted = false;
}
size_t size() const
{ return content.children->size(); }
Node& operator[](const size_t i) const bool has_children;
{ return content.children->at(i); } bool has_statements;
bool has_blocks;
bool has_expansions;
bool has_backref;
bool from_variable;
bool eval_me;
bool is_unquoted;
Node& at(const size_t i) const // bool is_numeric();
{ return content.children->at(i); } //
// size_t size();
// Node& at(size_t i);
// Node& back();
// Node& pop_back();
// void push_back(const Node& n);
//
// bool boolean_value();
Node& operator<<(const Node& n) bool is_numeric()
{ { return type >= number && type <= numeric_dimension; }
content.children->push_back(n);
return *this;
}
bool is_numeric() const size_t size()
{ { return children.size(); }
switch (type)
{
case number:
case numeric_percentage:
case numeric_dimension:
return true;
break;
default:
return false;
}
}
double numeric_value() const Node& at(size_t i)
{ { return children.at(i); }
switch (type)
{
case number:
case numeric_percentage:
return content.numeric_value;
case numeric_dimension:
return content.dimension.numeric_value;
default:
break;
// throw an exception?
}
}
void set_numeric_value(double v) Node& back()
{ { return children.back(); }
switch (type)
{
case number:
case numeric_percentage:
content.numeric_value = v;
case numeric_dimension:
content.dimension.numeric_value = v;
default:
break;
// throw an exception?
}
}
Node& operator+=(const Node& n) void push_back(const Node& n)
{ { children.push_back(n); }
for (size_t i = 0; i < n.size(); ++i) {
content.children->push_back(n[i]);
}
return *this;
}
bool operator==(const Node& rhs) const; Node& pop_back()
bool operator!=(const Node& rhs) const; { children.pop_back(); }
bool operator<(const Node& rhs) const;
bool operator<=(const Node& rhs) const;
bool operator>(const Node& rhs) const;
bool operator>=(const Node& rhs) const;
string to_string(const string& prefix) const; bool boolean_value()
{ return value.boolean; }
void echo(stringstream& buf, size_t depth = 0); double numeric_value();
void emit_nested_css(stringstream& buf, string unit();
size_t depth, };
const vector<string>& prefixes);
void emit_nested_css(stringstream& buf, size_t depth);
void emit_propset(stringstream& buf, size_t depth, const string& prefix);
void emit_expanded_css(stringstream& buf, const string& prefix);
Node clone(vector<vector<Node>*>& registry) const;
void flatten();
Node() // ------------------------------------------------------------------------
{ clear(); } // Node method implementations -- in the header so they can be inlined
// ------------------------------------------------------------------------
Node(Type t) // flags or booleans inline Node::Node(Node_Impl* ip) : ip_(ip) { }
{ clear(); type = t; }
Node(Type t, vector<vector<Node>*>& registry, unsigned int ln, size_t s = 0) // nodes with children inline Node_Type Node::type() { return ip_->type; }
{
clear();
type = t;
line_number = ln;
content.children = new vector<Node>;
registry.push_back(content.children);
content.children->reserve(s);
has_children = true;
++allocations;
}
Node(Type t, unsigned int ln, const Token& tok) // nodes with a single token inline bool Node::has_children() { return ip_->has_children; }
{ inline bool Node::has_statements() { return ip_->has_statements; }
clear(); inline bool Node::has_blocks() { return ip_->has_blocks; }
type = t; inline bool Node::has_expansions() { return ip_->has_expansions; }
line_number = ln; inline bool Node::has_backref() { return ip_->has_backref; }
content.token = tok; inline bool Node::from_variable() { return ip_->from_variable; }
} inline bool Node::eval_me() { return ip_->eval_me; }
inline bool Node::is_unquoted() { return ip_->is_unquoted; }
inline bool Node::is_numeric() { return ip_->is_numeric(); }
Node(unsigned int ln, double val) // numeric values inline string Node::file_name() const { return *(ip_->file_name); }
{ inline size_t Node::line_number() const { return ip_->line_number; }
clear(); inline size_t Node::size() const { return ip_->size(); }
type = number;
line_number = ln;
content.numeric_value = val;
}
Node(unsigned int ln, double val, const Token& tok) // dimensions inline Node& Node::at(size_t i) const { return ip_->at(i); }
inline Node& Node::operator[](size_t i) const { return at(i); }
inline void Node::pop_back() { ip_->pop_back(); }
inline Node& Node::push_back(Node n)
{ {
clear(); ip_->push_back(n);
type = numeric_dimension; return *this;
line_number = ln;
content.dimension = Dimension();
content.dimension.numeric_value = val;
content.dimension.unit = tok.begin;
} }
inline Node& Node::operator<<(Node n) { return push_back(n); }
Node(vector<vector<Node>*>& registry, unsigned int ln, double r, double g, double b, double a = 1.0) // colors inline Node& Node::operator+=(Node n)
{ {
clear(); for (size_t i = 0, L = n.size(); i < L; ++i) push_back(n[i]);
type = numeric_color; return *this;
line_number = ln;
content.children = new vector<Node>;
registry.push_back(content.children);
content.children->reserve(4);
content.children->push_back(Node(ln, r));
content.children->push_back(Node(ln, g));
content.children->push_back(Node(ln, b));
content.children->push_back(Node(ln, a));
has_children = true;
++allocations;
} }
inline bool Node::boolean_value() { return ip_->boolean_value(); }
inline double Node::numeric_value() { return ip_->numeric_value(); }
~Node() { ++destructed; }
};
} }
\ No newline at end of file
#include "node_factory.hpp" #include "node_factory.hpp"
#include "node_impl.hpp"
namespace Sass { namespace Sass {
......
#include <vector> #include <vector>
#ifndef SASS_NODE_INCLUDED #ifndef SASS_NODE_INCLUDED
#include "node_pimpl.hpp" #include "node.hpp"
#endif #endif
namespace Sass { namespace Sass {
......
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
#include "node_type.hpp" #include "node_type.hpp"
#endif #endif
#ifndef SASS_NODE_INCLUDED #include "node.hpp"
#include "node_pimpl.hpp"
#endif
namespace Sass { namespace Sass {
using namespace std; using namespace std;
......
#ifndef SASS_NODE_INCLUDED
#include "node_pimpl.hpp"
#endif
#include "node_impl.hpp"
namespace Sass {
using namespace std;
inline Node::Node(Node_Impl* ip) : ip_(ip) { }
inline Node_Type Node::type() { return ip_->type; }
inline bool Node::has_children() { return ip_->has_children; }
inline bool Node::has_statements() { return ip_->has_statements; }
inline bool Node::has_blocks() { return ip_->has_blocks; }
inline bool Node::has_expansions() { return ip_->has_expansions; }
inline bool Node::has_backref() { return ip_->has_backref; }
inline bool Node::from_variable() { return ip_->from_variable; }
inline bool Node::eval_me() { return ip_->eval_me; }
inline bool Node::is_unquoted() { return ip_->is_unquoted; }
inline bool Node::is_numeric() { return ip_->is_numeric(); }
inline string Node::file_name() const { return *(ip_->file_name); }
inline size_t Node::line_number() const { return ip_->line_number; }
inline size_t Node::size() const { return ip_->size(); }
inline Node& Node::at(size_t i) const { return ip_->at(i); }
inline Node& Node::operator[](size_t i) const { return at(i); }
inline Node& Node::pop_back() { return ip_->pop_back(); }
inline Node& Node::push_back(Node n)
{
ip_->push_back(n);
return *this;
}
inline Node& Node::operator<<(Node n) { return push_back(n); }
inline Node& Node::operator+=(Node n)
{
for (size_t i = 0, L = n.size(); i < L; ++i) push_back(n[i]);
return *this;
}
inline bool Node::boolean_value() { return ip_->boolean_value(); }
inline double Node::numeric_value() { return ip_->numeric_value(); }
}
\ No newline at end of file
#define SASS_NODE_INCLUDED
#include <string>
#ifndef SASS_NODE_TYPE_INCLUDED
#include "node_type.hpp"
#endif
namespace Sass {
using namespace std;
class Node_Impl; // forward declaration
class Node {
Node_Impl* ip_;
Node(Node_Impl* ip);
friend class Node_Factory;
public:
Node_Type type();
bool has_children();
bool has_statements();
bool has_blocks();
bool has_expansions();
bool has_backref();
bool from_variable();
bool eval_me();
bool is_unquoted();
bool is_numeric();
string file_name() const;
size_t line_number() const;
size_t size() const;
Node& at(size_t i) const;
Node& operator[](size_t i) const;
Node& pop_back();
Node& push_back(Node n);
Node& operator<<(Node n);
Node& operator+=(Node n);
double numeric_value();
bool boolean_value();
};
}
\ No newline at end of file
#include <iostream>
#include <iomanip>
#include <string>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include "node.hpp"
using std::string;
using std::stringstream;
using std::cout;
using std::cerr;
using std::endl;
namespace Sass {
size_t Node::allocations = 0;
size_t Node::destructed = 0;
Node Node::clone(vector<vector<Node>*>& registry) const
{
Node n(*this);
if (has_children) {
n.content.children = new vector<Node>;
++allocations;
n.content.children->reserve(size());
for (size_t i = 0; i < size(); ++i) {
n << at(i).clone(registry);
}
registry.push_back(n.content.children);
}
return n;
}
string Node::to_string(const string& prefix) const
{
switch (type)
{
case selector_group: { // really only needed for arg to :not
string result(at(0).to_string(""));
for (size_t i = 1; i < size(); ++i) {
result += ", ";
result += at(i).to_string("");
}
return result;
} break;
case selector: {
string result;
if (!has_backref && !prefix.empty()) {
result += prefix;
result += ' ';
}
// if (at(0).type == selector_combinator) {
// result += at(0).content.token.to_string();
// result += ' ';
// }
// else {
// Node::Type t = at(0).type;
// result += at(0).to_string(t == backref ? prefix : "");
// }
result += at(0).to_string(at(0).has_backref ? prefix : "");
for (size_t i = 1; i < size(); ++i) {
result += " ";
result += at(i).to_string(at(i).has_backref ? prefix : "");
}
return result;
} break;
case selector_combinator: {
string result(prefix.empty() ? "" : prefix + " ");
result += content.token.to_string();
return result;
// return content.token.to_string();
// if (std::isspace(content.token.begin[0])) return string(" ");
// else return string(content.token);
} break;
case simple_selector_sequence: {
string result;
if (!has_backref && !prefix.empty()) {
result += prefix;
result += " ";
}
for (size_t i = 0; i < size(); ++i) {
Node::Type t = at(i).type;
result += at(i).to_string(t == backref ? prefix : "");
}
return result;
} break;
case pseudo:
case simple_selector: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += content.token.to_string();
return result;
} break;
case pseudo_negation: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += at(0).to_string("");
result += at(1).to_string("");
result += ')';
return result;
} break;
case functional_pseudo: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += at(0).to_string("");
for (size_t i = 1; i < size(); ++i) {
result += at(i).to_string("");
}
result += ')';
return result;
} break;
case attribute_selector: {
string result(prefix);
if (!prefix.empty()) result += " ";
result += "[";
for (size_t i = 0; i < size(); ++i)
{ result += at(i).to_string(prefix); }
result += ']';
return result;
} break;
case backref: {
return prefix;
} break;
case comma_list: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (at(i).type == nil) continue;
result += ", ";
result += at(i).to_string(prefix);
}
return result;
} break;
case space_list: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (at(i).type == nil) continue;
result += " ";
result += at(i).to_string(prefix);
}
return result;
} break;
case expression:
case term: {
string result(at(0).to_string(prefix));
for (size_t i = 1; i < size(); ++i) {
if (!(at(i).type == add ||
// at(i).type == sub || // another edge case -- consider uncommenting
at(i).type == mul)) {
result += at(i).to_string(prefix);
}
}
return result;
} break;
//edge case
case sub: {
return "-";
} break;
case div: {
return "/";
} break;
case css_import: {
stringstream ss;
ss << "@import url(";
ss << at(0).to_string("");
// cerr << content.token.to_string() << endl;
ss << ")";
return ss.str();
}
case function_call: {
stringstream ss;
ss << at(0).to_string("");
ss << "(";
ss << at(1).to_string("");
ss << ")";
return ss.str();
}
case arguments: {
stringstream ss;
if (size() > 0) {
ss << at(0).to_string("");
for (size_t i = 1; i < size(); ++i) {
ss << ", ";
ss << at(i).to_string("");
}
}
return ss.str();
}
case unary_plus: {
stringstream ss;
ss << "+";
ss << at(0).to_string("");
return ss.str();
}
case unary_minus: {
stringstream ss;
ss << "-";
ss << at(0).to_string("");
return ss.str();
}
case numeric_percentage: {
stringstream ss;
ss << content.dimension.numeric_value;
ss << '%';
return ss.str();
}
case numeric_dimension: {
stringstream ss;
ss << content.dimension.numeric_value;
ss << string(content.dimension.unit, 2);
// << string(content.dimension.unit, Prelexer::identifier(content.dimension.unit) - content.dimension.unit);
// cerr << Token::make(content.dimension.unit, content.dimension.unit + 2).to_string();
// << Token::make(content.dimension.unit, Prelexer::identifier(content.dimension.unit)).to_string();
return ss.str();
} break;
case number: {
stringstream ss;
ss << content.numeric_value;
return ss.str();
} break;
case numeric_color: {
if (at(3).content.numeric_value >= 1.0) {
double a = at(0).content.numeric_value;
double b = at(1).content.numeric_value;
double c = at(2).content.numeric_value;
if (a >= 0xff && b >= 0xff && c >= 0xff)
{ return "white"; }
else if (a >= 0xff && b >= 0xff && c == 0)
{ return "yellow"; }
else if (a == 0 && b >= 0xff && c >= 0xff)
{ return "aqua"; }
else if (a >= 0xff && b == 0 && c >= 0xff)
{ return "fuchsia"; }
else if (a >= 0xff && b == 0 && c == 0)
{ return "red"; }
else if (a == 0 && b >= 0xff && c == 0)
{ return "lime"; }
else if (a == 0 && b == 0 && c >= 0xff)
{ return "blue"; }
else if (a <= 0 && b <= 0 && c <= 0)
{ return "black"; }
else
{
stringstream ss;
ss << '#' << std::setw(2) << std::setfill('0') << std::hex;
for (size_t i = 0; i < 3; ++i) {
double x = at(i).content.numeric_value;
if (x > 0xff) x = 0xff;
else if (x < 0) x = 0;
ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(x+0.5));
}
return ss.str();
}
}
else {
stringstream ss;
ss << "rgba(" << static_cast<unsigned long>(at(0).content.numeric_value);
for (size_t i = 1; i < 3; ++i) {
ss << ", " << static_cast<unsigned long>(at(i).content.numeric_value);
}
ss << ", " << at(3).content.numeric_value << ')';
return ss.str();
}
} break;
case uri: {
string result("url(");
result += string(content.token);
result += ")";
return result;
} break;
// case expansion: {
// string result("MIXIN CALL: ");
// return result;
// } break;
case string_constant: {
if (unquoted) return content.token.unquote();
else {
string result(content.token.to_string());
if (result[0] != '"' && result[0] != '\'') return "\"" + result + "\"";
else return result;
}
} break;
case boolean: {
if (content.boolean_value) return "true";
else return "false";
} break;
case important: {
return "!important";
} break;
case value_schema: {
string result;
for (size_t i = 0; i < size(); ++i) result += at(i).to_string("");
return result;
} break;
case string_schema: {
string result;
for (size_t i = 0; i < size(); ++i) {
string chunk(at(i).to_string(""));
if (at(i).type == string_constant) {
result += chunk.substr(1, chunk.size()-2);
}
else {
result += chunk;
}
}
return result;
} break;
default: {
// return content.token.to_string();
if (!has_children && type != flags) return content.token.to_string();
else return "";
} break;
}
}
void Node::echo(stringstream& buf, size_t depth) {
string indentation(2*depth, ' ');
switch (type) {
case comment:
buf << indentation << string(content.token) << endl;
break;
case ruleset:
buf << indentation;
at(0).echo(buf, depth);
at(1).echo(buf, depth);
break;
case selector_group:
at(0).echo(buf, depth);
for (size_t i = 1; i < size(); ++i) {
buf << ", ";
at(i).echo(buf, depth);
}
break;
case selector:
for (size_t i = 0; i < size(); ++i) {
at(i).echo(buf, depth);
}
break;
case selector_combinator:
if (std::isspace(content.token.begin[0])) buf << ' ';
else buf << ' ' << string(content.token) << ' ';
break;
case simple_selector_sequence:
for (size_t i = 0; i < size(); ++i) {
buf << at(i).to_string(string());
}
break;
case simple_selector:
buf << string(content.token);
break;
case block:
buf << " {" << endl;
for (size_t i = 0; i < size(); at(i++).echo(buf, depth+1)) ;
buf << indentation << "}" << endl;
break;
case rule:
buf << indentation;
at(0).echo(buf, depth);
buf << ": ";
at(1).echo(buf, depth);
buf << ';' << endl;
break;
case property:
buf << string(content.token);
break;
case values:
for (size_t i = 0; i < size(); at(i++).echo(buf, depth)) ;
break;
case value:
buf << ' ' << string(content.token);
break;
default:
break;
}
}
void Node::emit_nested_css(stringstream& buf,
size_t depth,
const vector<string>& prefixes)
{
switch (type)
{
case root:
if (at(0).has_expansions) {
flatten();
}
for (size_t i = 0; i < size(); ++i) {
at(i).emit_nested_css(buf, depth, prefixes);
//if (at(i).type == css_import) buf << endl;
}
break;
case ruleset: {
Node sel_group(at(0));
size_t sel_group_size = (sel_group.type == selector_group) ? sel_group.size() : 1; // parser ensures no singletons
Node block(at(1));
vector<string> new_prefixes;
if (prefixes.empty()) {
new_prefixes.reserve(sel_group_size);
for (size_t i = 0; i < sel_group_size; ++i) {
new_prefixes.push_back(sel_group_size > 1 ? sel_group[i].to_string(string()) : sel_group.to_string(string()));
}
}
else {
new_prefixes.reserve(prefixes.size() * sel_group_size);
for (size_t i = 0; i < prefixes.size(); ++i) {
for (size_t j = 0; j < sel_group_size; ++j) {
new_prefixes.push_back(sel_group_size > 1 ? sel_group[j].to_string(prefixes[i]) : sel_group.to_string(prefixes[i]));
}
}
}
if (block[0].has_expansions) block.flatten();
if (block[0].has_statements) {
buf << string(2*depth, ' ') << new_prefixes[0];
for (size_t i = 1; i < new_prefixes.size(); ++i) {
buf << ", " << new_prefixes[i];
}
buf << " {";
for (size_t i = 0; i < block.size(); ++i) {
Type stm_type = block[i].type;
if (stm_type == comment || stm_type == rule || stm_type == css_import || stm_type == propset) {
block[i].emit_nested_css(buf, depth+1); // NEED OVERLOADED VERSION FOR COMMENTS AND RULES
}
// else if (stm_type == propset) {
// block[i].emit_nested_css(buf, depth, new_prefixes);
// }
}
buf << " }" << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets
}
if (block[0].has_blocks) {
for (size_t i = 0; i < block.size(); ++i) {
if (block[i].type == ruleset) {
block[i].emit_nested_css(buf, depth, new_prefixes);
}
}
}
if (block[0].has_statements) --depth; // see previous comment
if (depth == 0 && prefixes.empty()) buf << endl;
} break;
default:
emit_nested_css(buf, depth); // pass it along to the simpler version
break;
}
}
void Node::emit_nested_css(stringstream& buf, size_t depth)
{
switch (type)
{
case propset: {
emit_propset(buf, depth, "");
} break;
case rule:
buf << endl << string(2*depth, ' ');
at(0).emit_nested_css(buf, depth); // property
at(1).emit_nested_css(buf, depth); // values
buf << ";";
break;
case css_import:
buf << string(2*depth, ' ');
buf << to_string("");
buf << ";" << endl;
break;
case property:
buf << string(content.token) << ": ";
break;
case values:
for (size_t i = 0; i < size(); ++i) {
buf << " " << string(at(i).content.token);
}
break;
case comment:
if (depth != 0) buf << endl;
buf << string(2*depth, ' ') << string(content.token);
if (depth == 0) buf << endl;
break;
default:
buf << to_string("");
break;
}
}
void Node::emit_propset(stringstream& buf, size_t depth, const string& prefix) {
string new_prefix(prefix);
bool has_prefix = false;
if (new_prefix.empty()) {
new_prefix += "\n";
new_prefix += string(2*depth, ' ');
new_prefix += at(0).content.token.to_string();
}
else {
new_prefix += "-";
new_prefix += at(0).content.token.to_string();
has_prefix = true;
}
Node rules(at(1));
for (size_t i = 0; i < rules.size(); ++i) {
if (rules[i].type == propset) {
rules[i].emit_propset(buf, depth+1, new_prefix);
}
else {
buf << new_prefix;
if (rules[i][0].content.token.to_string() != "") buf << '-';
rules[i][0].emit_nested_css(buf, depth);
rules[i][1].emit_nested_css(buf, depth);
buf << ';';
}
}
}
void Node::emit_expanded_css(stringstream& buf, const string& prefix) {
// switch (type) {
// case selector:
// if (!prefix.empty()) buf << " ";
// buf << string(token);
// break;
// case comment:
// if (!prefix.empty()) buf << " ";
// buf << string(token) << endl;
// break;
// case property:
// buf << string(token) << ":";
// break;
// case values:
// for (size_t i = 0; i < children.size(); ++i) {
// buf << " " << string(children[i].token);
// }
// break;
// case rule:
// buf << " ";
// children[0].emit_expanded_css(buf, prefix);
// children[1].emit_expanded_css(buf, prefix);
// buf << ";" << endl;
// break;
// case clauses:
// if (children.size() > 0) {
// buf << " {" << endl;
// for (size_t i = 0; i < children.size(); ++i)
// children[i].emit_expanded_css(buf, prefix);
// buf << "}" << endl;
// }
// for (size_t i = 0; i < opt_children.size(); ++i)
// opt_children[i].emit_expanded_css(buf, prefix);
// break;
// case ruleset:
// // buf << prefix;
// if (children[1].children.size() > 0) {
// buf << prefix << (prefix.empty() ? "" : " ");
// children[0].emit_expanded_css(buf, "");
// }
// string newprefix(prefix.empty() ? prefix : prefix + " ");
// children[1].emit_expanded_css(buf, newprefix + string(children[0].token));
// if (prefix.empty()) buf << endl;
// break;
// }
}
void Node::flatten()
{
if (type != block && type != expansion && type != root) return;
for (size_t i = 0; i < size(); ++i) {
if (at(i).type == expansion) {
Node expn = at(i);
if (expn[0].has_expansions) expn.flatten();
at(0).has_statements |= expn[0].has_statements;
at(0).has_blocks |= expn[0].has_blocks;
at(0).has_expansions |= expn[0].has_expansions;
at(i).type = none;
content.children->insert(content.children->begin() + i,
expn.content.children->begin(),
expn.content.children->end());
}
}
}
//
// void flatten_block(Node& block)
// {
//
// for (size_t i = 0; i < block.size(); ++i) {
//
// if (block[i].type == Node::expansion
//
// }
//
//
//
// }
}
#define SASS_NODE_INCLUDED
#include <vector>
#include <sstream>
#include "values.hpp"
#include <iostream>
namespace Sass {
using std::string;
using std::vector;
using std::stringstream;
using std::cerr; using std::endl;
struct Node {
enum Type {
root,
ruleset,
propset,
selector_group,
selector,
selector_combinator,
simple_selector_sequence,
backref,
simple_selector,
type_selector,
class_selector,
id_selector,
pseudo,
pseudo_negation,
functional_pseudo,
attribute_selector,
block,
rule,
property,
nil,
comma_list,
space_list,
disjunction,
conjunction,
relation,
eq,
neq,
gt,
gte,
lt,
lte,
expression,
add,
sub,
term,
mul,
div,
factor,
unary_plus,
unary_minus,
values,
value,
identifier,
uri,
textual_percentage,
textual_dimension,
textual_number,
textual_hex,
color_name,
string_constant,
numeric_percentage,
numeric_dimension,
number,
numeric_color,
boolean,
important,
value_schema,
string_schema,
css_import,
function_call,
mixin,
parameters,
expansion,
arguments,
variable,
assignment,
comment,
none,
flags
};
Type type;
unsigned int line_number;
bool has_children;
bool has_statements;
bool has_blocks;
bool has_expansions;
bool has_backref;
bool from_variable;
bool eval_me;
bool unquoted;
union {
Token token;
mutable vector<Node>* children;
Dimension dimension;
double numeric_value;
bool boolean_value;
} content;
const char* file_name;
static size_t allocations;
static size_t destructed;
void clear()
{
type = none; line_number = 0; file_name = 0;
has_children = false; has_statements = false; has_blocks = false;
has_expansions = false; has_backref = false; from_variable = false;
eval_me = false; unquoted = false;
}
size_t size() const
{ return content.children->size(); }
Node& operator[](const size_t i) const
{ return content.children->at(i); }
Node& at(const size_t i) const
{ return content.children->at(i); }
Node& operator<<(const Node& n)
{
content.children->push_back(n);
return *this;
}
bool is_numeric() const
{
switch (type)
{
case number:
case numeric_percentage:
case numeric_dimension:
return true;
break;
default:
return false;
}
}
double numeric_value() const
{
switch (type)
{
case number:
case numeric_percentage:
return content.numeric_value;
case numeric_dimension:
return content.dimension.numeric_value;
default:
break;
// throw an exception?
}
}
void set_numeric_value(double v)
{
switch (type)
{
case number:
case numeric_percentage:
content.numeric_value = v;
case numeric_dimension:
content.dimension.numeric_value = v;
default:
break;
// throw an exception?
}
}
Node& operator+=(const Node& n)
{
for (size_t i = 0; i < n.size(); ++i) {
content.children->push_back(n[i]);
}
return *this;
}
bool operator==(const Node& rhs) const;
bool operator!=(const Node& rhs) const;
bool operator<(const Node& rhs) const;
bool operator<=(const Node& rhs) const;
bool operator>(const Node& rhs) const;
bool operator>=(const Node& rhs) const;
string to_string(const string& prefix) const;
void echo(stringstream& buf, size_t depth = 0);
void emit_nested_css(stringstream& buf,
size_t depth,
const vector<string>& prefixes);
void emit_nested_css(stringstream& buf, size_t depth);
void emit_propset(stringstream& buf, size_t depth, const string& prefix);
void emit_expanded_css(stringstream& buf, const string& prefix);
Node clone(vector<vector<Node>*>& registry) const;
void flatten();
Node()
{ clear(); }
Node(Type t) // flags or booleans
{ clear(); type = t; }
Node(Type t, vector<vector<Node>*>& registry, unsigned int ln, size_t s = 0) // nodes with children
{
clear();
type = t;
line_number = ln;
content.children = new vector<Node>;
registry.push_back(content.children);
content.children->reserve(s);
has_children = true;
++allocations;
}
Node(Type t, unsigned int ln, const Token& tok) // nodes with a single token
{
clear();
type = t;
line_number = ln;
content.token = tok;
}
Node(unsigned int ln, double val) // numeric values
{
clear();
type = number;
line_number = ln;
content.numeric_value = val;
}
Node(unsigned int ln, double val, const Token& tok) // dimensions
{
clear();
type = numeric_dimension;
line_number = ln;
content.dimension = Dimension();
content.dimension.numeric_value = val;
content.dimension.unit = tok.begin;
}
Node(vector<vector<Node>*>& registry, unsigned int ln, double r, double g, double b, double a = 1.0) // colors
{
clear();
type = numeric_color;
line_number = ln;
content.children = new vector<Node>;
registry.push_back(content.children);
content.children->reserve(4);
content.children->push_back(Node(ln, r));
content.children->push_back(Node(ln, g));
content.children->push_back(Node(ln, b));
content.children->push_back(Node(ln, a));
has_children = true;
++allocations;
}
~Node() { ++destructed; }
};
}
#include <iostream>
#ifndef SASS_NODE_INCLUDED
#include "node.hpp"
#endif
#include "node_factory.hpp"
int main()
{
using namespace Sass;
using namespace std;
cout << sizeof(Node) << endl;
cout << sizeof(Node_Impl) << endl;
return 0;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment