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::Node(Node_Impl* ip) : ip_(ip) { }
{ //
Node n(*this); // inline Node_Type Node::type() { return ip_->type; }
if (has_children) { //
n.content.children = new vector<Node>; // inline bool Node::has_children() { return ip_->has_children; }
++allocations; // inline bool Node::has_statements() { return ip_->has_statements; }
n.content.children->reserve(size()); // inline bool Node::has_blocks() { return ip_->has_blocks; }
for (size_t i = 0; i < size(); ++i) { // inline bool Node::has_expansions() { return ip_->has_expansions; }
n << at(i).clone(registry); // inline bool Node::has_backref() { return ip_->has_backref; }
} // inline bool Node::from_variable() { return ip_->from_variable; }
registry.push_back(n.content.children); // inline bool Node::eval_me() { return ip_->eval_me; }
} // inline bool Node::is_unquoted() { return ip_->is_unquoted; }
return n; // 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(); }
// ------------------------------------------------------------------------
// Token method implementations
// ------------------------------------------------------------------------
string Node::to_string(const string& prefix) const string Token::unquote() const
{ {
switch (type) string result;
{ const char* p = begin;
case selector_group: { // really only needed for arg to :not if (*begin == '\'' || *begin == '"') {
string result(at(0).to_string("")); ++p;
for (size_t i = 1; i < size(); ++i) { while (p < end) {
result += ", "; if (*p == '\\') {
result += at(i).to_string(""); switch (*(++p)) {
} case 'n': result += '\n'; break;
return result; case 't': result += '\t'; break;
} break; case 'b': result += '\b'; break;
case 'r': result += '\r'; break;
case selector: { case 'f': result += '\f'; break;
string result; case 'v': result += '\v'; break;
if (!has_backref && !prefix.empty()) { case 'a': result += '\a'; break;
result += prefix; case '\\': result += '\\'; break;
result += ' '; default: result += *p; break;
}
// 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 { else if (p == end - 1) {
stringstream ss; return result;
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 { else {
string result(content.token.to_string()); result += *p;
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; ++p;
} 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; return result;
case selector_combinator: }
if (std::isspace(content.token.begin[0])) buf << ' '; else {
else buf << ' ' << string(content.token) << ' '; while (p < end) {
break; result += *(p++);
case simple_selector_sequence:
for (size_t i = 0; i < size(); ++i) {
buf << at(i).to_string(string());
} }
break; return result;
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, void Token::unquote_to_stream(std::stringstream& buf) const
size_t depth,
const vector<string>& prefixes)
{ {
switch (type) const char* p = begin;
{ if (*begin == '\'' || *begin == '"') {
case root: ++p;
if (at(0).has_expansions) { while (p < end) {
flatten(); if (*p == '\\') {
} switch (*(++p)) {
for (size_t i = 0; i < size(); ++i) { case 'n': buf << '\n'; break;
at(i).emit_nested_css(buf, depth, prefixes); case 't': buf << '\t'; break;
//if (at(i).type == css_import) buf << endl; case 'b': buf << '\b'; break;
} case 'r': buf << '\r'; break;
break; case 'f': buf << '\f'; break;
case 'v': buf << '\v'; break;
case ruleset: { case 'a': buf << '\a'; break;
Node sel_group(at(0)); case '\\': buf << '\\'; break;
size_t sel_group_size = (sel_group.type == selector_group) ? sel_group.size() : 1; // parser ensures no singletons default: buf << *p; break;
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]));
} }
} }
} else if (p == end - 1) {
return;
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 << " {"; else {
for (size_t i = 0; i < block.size(); ++i) { buf << *p;
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; ++p;
++depth; // if we printed content at this level, we need to indent any nested rulesets
} }
if (block[0].has_blocks) { return;
for (size_t i = 0; i < block.size(); ++i) { }
if (block[i].type == ruleset) { else {
block[i].emit_nested_css(buf, depth, new_prefixes); while (p < end) {
} buf << *(p++);
}
} }
if (block[0].has_statements) --depth; // see previous comment return;
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) bool Token::operator<(const Token& rhs) const
{ {
switch (type) const char* first1 = begin;
const char* last1 = end;
const char* first2 = rhs.begin;
const char* last2 = rhs.end;
while (first1!=last1)
{ {
case propset: { if (first2==last2 || *first2<*first1) return false;
emit_propset(buf, depth, ""); else if (*first1<*first2) return true;
} break; first1++; first2++;
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;
} }
return (first2!=last2);
} }
void Node::emit_propset(stringstream& buf, size_t depth, const string& prefix) { bool Token::operator==(const Token& rhs) const
string new_prefix(prefix); {
bool has_prefix = false; if (length() != rhs.length()) return false;
if (new_prefix.empty()) {
new_prefix += "\n"; if ((begin[0] == '"' || begin[0] == '\'') &&
new_prefix += string(2*depth, ' '); (rhs.begin[0] == '"' || rhs.begin[0] == '\''))
new_prefix += at(0).content.token.to_string(); { return unquote() == rhs.unquote(); }
}
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 << ';';
}
}
const char* p = begin;
const char* q = rhs.begin;
for (; p < end; ++p, ++q) if (*p != *q) return false;
return true;
} }
void Node::emit_expanded_css(stringstream& buf, const string& prefix) {
// switch (type) { // ------------------------------------------------------------------------
// case selector: // Node_Impl method implementations
// if (!prefix.empty()) buf << " "; // ------------------------------------------------------------------------
// buf << string(token);
// break; // inline bool Node_Impl::is_numeric()
// case comment: // { return type >= number && type <= numeric_dimension; }
// if (!prefix.empty()) buf << " "; //
// buf << string(token) << endl; // inline size_t Node_Impl::size()
// break; // { return children.size(); }
// case property: //
// buf << string(token) << ":"; // inline Node& Node_Impl::at(size_t i)
// break; // { return children.at(i); }
// case values: //
// for (size_t i = 0; i < children.size(); ++i) { // inline Node& Node_Impl::back()
// buf << " " << string(children[i].token); // { return children.back(); }
// } //
// break; // inline void Node_Impl::push_back(const Node& n)
// case rule: // { children.push_back(n); }
// buf << " "; //
// children[0].emit_expanded_css(buf, prefix); // inline Node& Node_Impl::pop_back()
// children[1].emit_expanded_css(buf, prefix); // { children.pop_back(); }
// buf << ";" << endl; //
// break; // inline bool Node_Impl::boolean_value()
// case clauses: // { return value.boolean; }
// if (children.size() > 0) { //
// buf << " {" << endl; double Node_Impl::numeric_value()
// for (size_t i = 0; i < children.size(); ++i) {
// children[i].emit_expanded_css(buf, prefix); switch (type)
// buf << "}" << endl; {
// } case number:
// for (size_t i = 0; i < opt_children.size(); ++i) case numeric_percentage:
// opt_children[i].emit_expanded_css(buf, prefix); return value.numeric;
// break; case numeric_dimension:
// case ruleset: return value.dimension.numeric;
// // buf << prefix; default:
// if (children[1].children.size() > 0) { break;
// buf << prefix << (prefix.empty() ? "" : " "); // throw an exception?
// children[0].emit_expanded_css(buf, ""); }
// } return 0;
// 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() string Node_Impl::unit()
{ {
if (type != block && type != expansion && type != root) return; switch (type)
for (size_t i = 0; i < size(); ++i) { {
if (at(i).type == expansion) { case numeric_percentage: {
Node expn = at(i); return "\"%\"";
if (expn[0].has_expansions) expn.flatten(); } break;
at(0).has_statements |= expn[0].has_statements;
at(0).has_blocks |= expn[0].has_blocks; case numeric_dimension: {
at(0).has_expansions |= expn[0].has_expansions; string result("\"");
at(i).type = none; result += value.dimension.unit.to_string();
content.children->insert(content.children->begin() + i, result += "\"";
expn.content.children->begin(), return result;
expn.content.children->end()); } break;
}
default: break;
} }
return "\"\"";
} }
//
// 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,
struct Node { comment,
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; root,
bool has_statements; ruleset,
bool has_blocks; propset,
bool has_expansions;
bool has_backref;
bool from_variable;
bool eval_me;
bool unquoted;
union { selector_group,
Token token; selector,
mutable vector<Node>* children; selector_combinator,
Dimension dimension; simple_selector_sequence,
double numeric_value; backref,
bool boolean_value; simple_selector,
} content; type_selector,
class_selector,
const char* file_name; id_selector,
pseudo,
static size_t allocations; pseudo_negation,
static size_t destructed; functional_pseudo,
attribute_selector,
void clear()
{ block,
type = none; line_number = 0; file_name = 0; rule,
has_children = false; has_statements = false; has_blocks = false; property,
has_expansions = false; has_backref = false; from_variable = false;
eval_me = false; unquoted = false; nil,
} comma_list,
space_list,
size_t size() const
{ return content.children->size(); } disjunction,
conjunction,
Node& operator[](const size_t i) const
{ return content.children->at(i); } relation,
eq,
Node& at(const size_t i) const neq,
{ return content.children->at(i); } gt,
gte,
Node& operator<<(const Node& n) lt,
{ lte,
content.children->push_back(n);
return *this; expression,
} add,
sub,
bool is_numeric() const
{ term,
switch (type) mul,
{ div,
case number:
case numeric_percentage: factor,
case numeric_dimension: unary_plus,
return true; unary_minus,
break; values,
default: value,
return false; identifier,
} uri,
} textual_percentage,
textual_dimension,
double numeric_value() const textual_number,
{ textual_hex,
switch (type) color_name,
{ string_constant,
case number: number,
case numeric_percentage: numeric_percentage,
return content.numeric_value; numeric_dimension,
case numeric_dimension: numeric_color,
return content.dimension.numeric_value; boolean,
default: important,
break;
// throw an exception? value_schema,
} string_schema,
}
css_import,
void set_numeric_value(double v) function_call,
{ mixin,
switch (type) parameters,
{ expansion,
case number: arguments,
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 variable,
{ clear(); type = t; } assignment
};
struct Node_Impl;
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; }
Node(Type t, vector<vector<Node>*>& registry, unsigned int ln, size_t s = 0) // nodes with children 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(); }
};
struct Token {
const char* begin;
const char* end;
// Need Token::make(...) because tokens are union members, and hence they
// can't have non-trivial constructors.
static Token make()
{ {
clear(); Token t;
type = t; t.begin = 0;
line_number = ln; t.end = 0;
content.children = new vector<Node>; return t;
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 static Token make(const char* s)
{ {
clear(); Token t;
type = t; t.begin = s;
line_number = ln; t.end = s + std::strlen(s);
content.token = tok; return t;
} }
Node(unsigned int ln, double val) // numeric values static Token make(const char* b, const char* e)
{ {
clear(); Token t;
type = number; t.begin = b;
line_number = ln; t.end = e;
content.numeric_value = val; 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;
Node(unsigned int ln, double val, const Token& tok) // dimensions operator bool()
{ { return begin && end && begin >= end; }
clear();
type = numeric_dimension; bool operator<(const Token& rhs) const;
line_number = ln; bool operator==(const Token& rhs) const;
content.dimension = Dimension(); };
content.dimension.numeric_value = val;
content.dimension.unit = tok.begin; struct Dimension {
} double numeric;
Token unit;
};
struct Node_Impl {
union {
bool boolean;
double numeric;
Token token;
Dimension dimension;
} value;
vector<Node> children;
string* file_name;
size_t line_number;
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();
//
// size_t size();
// Node& at(size_t i);
// Node& back();
// Node& pop_back();
// void push_back(const Node& n);
//
// bool boolean_value();
Node(vector<vector<Node>*>& registry, unsigned int ln, double r, double g, double b, double a = 1.0) // colors bool is_numeric()
{ { return type >= number && type <= numeric_dimension; }
clear();
type = numeric_color; size_t size()
line_number = ln; { return children.size(); }
content.children = new vector<Node>;
registry.push_back(content.children); Node& at(size_t i)
content.children->reserve(4); { return children.at(i); }
content.children->push_back(Node(ln, r));
content.children->push_back(Node(ln, g)); Node& back()
content.children->push_back(Node(ln, b)); { return children.back(); }
content.children->push_back(Node(ln, a));
has_children = true; void push_back(const Node& n)
++allocations; { children.push_back(n); }
}
Node& pop_back()
{ children.pop_back(); }
bool boolean_value()
{ return value.boolean; }
~Node() { ++destructed; } double numeric_value();
string unit();
}; };
}
// ------------------------------------------------------------------------
// Node method implementations -- in the header so they can be inlined
// ------------------------------------------------------------------------
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 void Node::pop_back() { 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
#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