Commit 33ed00df by Hampton Catlin

Merge branch 'master' of github.com:hcatlin/libsass

parents 6e45b9c8 a339e8e5
build: sassc.cpp document.cpp node.cpp token.cpp prelexer.cpp
g++ -o bin/sassc sassc.cpp document.cpp document_parser.cpp evaluator.cpp document_emitter.cpp node.cpp token.cpp prelexer.cpp
g++ -o bin/sassc sassc.cpp document.cpp document_parser.cpp eval_apply.cpp node.cpp token.cpp prelexer.cpp
test: build
ruby spec.rb spec/basic/
......
#define SASS_CONTEXT_INCLUDED
namespace Sass {
using std::map;
struct Environment {
map<Token, Node> current_frame;
Environment* parent;
Environment* global;
Environment()
: current_frame(map<Token, Node>()), parent(0), global(0)
{ }
void link(Environment& env)
{
parent = &env;
global = parent->global ? parent->global : parent;
}
bool query(const Token& key) const
{
if (current_frame.count(key)) return true;
else if (parent) return parent->query(key);
else return false;
}
Node& operator[](const Token& key)
{
if (current_frame.count(key)) return current_frame[key];
else if (parent) return (*parent)[key];
else return current_frame[key];
}
};
struct Context {
map<Token, Node> environment;
Environment global_env;
vector<Node> pending;
vector<char*> source_refs;
size_t ref_count;
Context()
: environment(map<Token, Node>()),
: pending(vector<Node>()),
source_refs(vector<char*>()),
ref_count(0)
{ }
......@@ -19,4 +52,5 @@ namespace Sass {
}
}
};
}
#include <cstdio>
#include "document.hpp"
#include "eval_apply.hpp"
#include <iostream>
namespace Sass {
......@@ -58,4 +59,32 @@ namespace Sass {
if (context.ref_count == 0) delete &context;
}
void Document::eval_pending()
{
for (int i = 0; i < context.pending.size(); ++i) {
eval(context.pending[i], context.global_env);
}
}
using std::string;
using std::stringstream;
using std::endl;
string Document::emit_css(CSS_Style style) {
stringstream output;
switch (style) {
case echo:
root.echo(output);
break;
case nested:
root.emit_nested_css(output, 0, vector<string>());
break;
case expanded:
root.emit_expanded_css(output, "");
break;
}
string retval(output.str());
if (!retval.empty()) retval.resize(retval.size()-1);
return retval;
}
}
\ No newline at end of file
......@@ -40,7 +40,7 @@ namespace Sass {
if (!start) start = position;
const char* after_whitespace;
if (mx == block_comment) {
after_whitespace =
after_whitespace = // start;
zero_plus< alternatives<spaces, line_comment> >(start);
}
else if (/*mx == ancestor_of ||*/ mx == no_spaces) {
......@@ -75,7 +75,7 @@ namespace Sass {
{
const char* after_whitespace;
if (mx == block_comment) {
after_whitespace =
after_whitespace = // position;
zero_plus< alternatives<spaces, line_comment> >(position);
}
else if (mx == ancestor_of || mx == no_spaces) {
......@@ -111,15 +111,22 @@ namespace Sass {
void parse_scss();
Node parse_import();
void parse_var_def();
Node parse_ruleset();
Node parse_include();
Node parse_mixin_definition();
Node parse_mixin_parameters();
Node parse_parameter();
Node parse_mixin_call();
Node parse_mixin_arguments();
Node parse_argument();
Node parse_assignment();
Node parse_ruleset(bool definition = false);
Node parse_selector_group();
Node parse_selector();
Node parse_simple_selector_sequence();
Node parse_simple_selector();
Node parse_pseudo();
Node parse_attribute_selector();
Node parse_block();
Node parse_block(bool definition = false);
Node parse_rule();
Node parse_values();
Node parse_list();
......@@ -129,6 +136,8 @@ namespace Sass {
Node parse_term();
Node parse_factor();
Node parse_value();
Node parse_identifier();
Node parse_variable();
const char* look_for_rule(const char* start = 0);
const char* look_for_values(const char* start = 0);
......@@ -140,6 +149,7 @@ namespace Sass {
const char* look_for_pseudo(const char* start = 0);
const char* look_for_attrib(const char* start = 0);
void eval_pending();
string emit_css(CSS_Style style);
};
......
#include "document.hpp"
namespace Sass {
using std::string;
using std::stringstream;
using std::endl;
string Document::emit_css(CSS_Style style) {
stringstream output;
switch (style) {
case echo:
root.echo(output);
break;
case nested:
root.emit_nested_css(output, 0, vector<string>());
break;
case expanded:
root.emit_expanded_css(output, "");
break;
}
string retval(output.str());
if (!retval.empty()) retval.resize(retval.size()-1);
return retval;
}
}
\ No newline at end of file
#include "eval_apply.hpp"
#include <iostream>
#include <cstdlib>
namespace Sass {
using std::cerr; using std::endl;
Node eval(Node& expr, Environment& env)
{
switch (expr.type)
{
case Node::mixin: {
env[expr[0].token] = expr;
// cerr << "DEFINED MIXIN: " << string(expr[0].token) << endl << endl;
return expr;
} break;
case Node::expansion: {
Token name(expr[0].token);
// cerr << "EVALUATING EXPANSION: " << string(name) << endl;
Node args(expr[1]);
Node mixin(env[name]);
Node expansion(apply(mixin, args, env));
expr.children->pop_back();
expr.children->pop_back();
expr += expansion;
// expr[0].has_rules_or_comments |= expansion[0].has_rules_or_comments;
// expr[0].has_rulesets |= expansion[0].has_rulesets;
// expr[0].has_propsets |= expansion[0].has_propsets;
// expr[0].has_expansions |= expansion[0].has_expansions;
return expr;
} break;
case Node::ruleset: {
eval(expr[1], env);
return expr;
} break;
case Node::root: {
for (int i = 0; i < expr.size(); ++i) {
eval(expr[i], env);
}
return expr;
} break;
case Node::block: {
Environment current;
current.link(env);
for (int i = 0; i < expr.size(); ++i) {
eval(expr[i], current);
}
return expr;
} break;
case Node::assignment: {
Node val(expr[1]);
if (val.type == Node::comma_list || val.type == Node::space_list) {
for (int i = 0; i < val.size(); ++i) {
if (val[i].eval_me) val[i] = eval(val[i], env);
}
}
else {
val = eval(val, env);
}
Node var(expr[0]);
if (env.query(var.token)) {
env[var.token] = val;
}
else {
env.current_frame[var.token] = val;
}
return expr;
} break;
case Node::rule: {
Node rhs(expr[1]);
if (rhs.type == Node::comma_list || rhs.type == Node::space_list) {
for (int i = 0; i < rhs.size(); ++i) {
if (rhs[i].eval_me) rhs[i] = eval(rhs[i], env);
}
}
else {
if (rhs.eval_me) expr[1] = eval(rhs, env);
}
return expr;
} break;
case Node::comma_list:
case Node::space_list: {
if (expr.eval_me) {
// *(expr.children->begin()) = eval(expr[0], env);
expr[0] = eval(expr[0], env);
}
return expr;
} break;
case Node::expression: {
Node acc(expr.line_number, Node::expression, eval(expr[0], env));
Node rhs(eval(expr[2], env));
accumulate(expr[1].type, acc, rhs);
for (int i = 3; i < expr.size(); i += 2) {
Node rhs(eval(expr[i+1], env));
accumulate(expr[i].type, acc, rhs);
}
return acc.size() == 1 ? acc[0] : acc;
} break;
case Node::term: {
if (expr.eval_me) {
Node acc(expr.line_number, Node::expression, eval(expr[0], env));
Node rhs(eval(expr[2], env));
accumulate(expr[1].type, acc, rhs);
for (int i = 3; i < expr.size(); i += 2) {
Node rhs(eval(expr[i+1], env));
accumulate(expr[i].type, acc, rhs);
}
return acc.size() == 1 ? acc[0] : acc;
}
else {
return expr;
}
} break;
case Node::textual_percentage:
case Node::textual_dimension: {
double numval = std::atof(expr.token.begin);
Token unit(Prelexer::number(expr.token.begin), expr.token.end);
return Node(expr.line_number, numval, unit);
} break;
case Node::textual_number: {
double numval = std::atof(expr.token.begin);
return Node(expr.line_number, numval);
} break;
case Node::textual_hex: {
Node triple(expr.line_number, Node::hex_triple, 3);
Token hext(expr.token.begin+1, expr.token.end);
if (hext.length() == 6) {
for (int i = 0; i < 6; i += 2) {
Node thing(expr.line_number, static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
triple << Node(expr.line_number, static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
}
}
else {
for (int i = 0; i < 3; ++i) {
triple << Node(expr.line_number, static_cast<double>(std::strtol(string(2, hext.begin[i]).c_str(), NULL, 16)));
}
}
return triple;
} break;
case Node::variable: {
return env[expr.token];
} break;
default: {
return expr;
}
}
}
Node accumulate(Node::Type op, Node& acc, Node& rhs)
{
Node lhs(acc.children->back());
double lnum = lhs.numeric_value;
double rnum = rhs.numeric_value;
if (lhs.type == Node::number && rhs.type == Node::number) {
Node result(acc.line_number, operate(op, lnum, rnum));
acc.children->pop_back();
acc.children->push_back(result);
}
// TO DO: find a way to merge the following two clauses
else if (lhs.type == Node::number && rhs.type == Node::numeric_dimension) {
// TO DO: disallow division
Node result(acc.line_number, operate(op, lnum, rnum), rhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::number) {
Node result(acc.line_number, operate(op, lnum, rnum), lhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::numeric_dimension) {
// TO DO: CHECK FOR MISMATCHED UNITS HERE
Node result;
if (op == Node::div)
{ result = Node(acc.line_number, operate(op, lnum, rnum)); }
else
{ result = Node(acc.line_number, operate(op, lnum, rnum), lhs.token); }
acc.children->pop_back();
acc.children->push_back(result);
}
// TO DO: find a way to merge the following two clauses
else if (lhs.type == Node::number && rhs.type == Node::hex_triple) {
if (op != Node::sub && op != Node::div) {
double h1 = operate(op, lhs.numeric_value, rhs[0].numeric_value);
double h2 = operate(op, lhs.numeric_value, rhs[1].numeric_value);
double h3 = operate(op, lhs.numeric_value, rhs[2].numeric_value);
acc.children->pop_back();
acc << Node(acc.line_number, h1, h2, h3);
}
// trying to handle weird edge cases ... not sure if it's worth it
else if (op == Node::div) {
acc << Node(acc.line_number, Node::div);
acc << rhs;
}
else if (op == Node::sub) {
acc << Node(acc.line_number, Node::sub);
acc << rhs;
}
else {
acc << rhs;
}
}
else if (lhs.type == Node::hex_triple && rhs.type == Node::number) {
double h1 = operate(op, lhs[0].numeric_value, rhs.numeric_value);
double h2 = operate(op, lhs[1].numeric_value, rhs.numeric_value);
double h3 = operate(op, lhs[2].numeric_value, rhs.numeric_value);
acc.children->pop_back();
acc << Node(acc.line_number, h1, h2, h3);
}
else if (lhs.type == Node::hex_triple && rhs.type == Node::hex_triple) {
double h1 = operate(op, lhs[0].numeric_value, rhs[0].numeric_value);
double h2 = operate(op, lhs[1].numeric_value, rhs[1].numeric_value);
double h3 = operate(op, lhs[2].numeric_value, rhs[2].numeric_value);
acc.children->pop_back();
acc << Node(acc.line_number, h1, h2, h3);
}
else {
// TO DO: disallow division and multiplication on lists
acc.children->push_back(rhs);
}
return acc;
}
double operate(Node::Type op, double lhs, double rhs)
{
// TO DO: check for division by zero
switch (op)
{
case Node::add: return lhs + rhs; break;
case Node::sub: return lhs - rhs; break;
case Node::mul: return lhs * rhs; break;
case Node::div: return lhs / rhs; break;
default: return 0; break;
}
}
Node apply(Node& mixin, const Node& args, Environment& env)
{
// cerr << "APPLYING MIXIN: " << string(mixin[0].token) << endl;
Node params(mixin[1]);
Node body(mixin[2].clone());
Environment m_env;
// cerr << "CLONED BODY" << endl;
// bind arguments
for (int i = 0, j = 0; i < args.size(); ++i) {
if (args[i].type == Node::assignment) {
Node arg(args[i]);
Token name(arg[0].token);
if (!m_env.query(name)) {
m_env[name] = eval(arg[1], env);
}
}
else {
// TO DO: ensure (j < params.size())
Node param(params[j]);
Token name(param.type == Node::variable ? param.token : param[0].token);
m_env[name] = eval(args[i], env);
++j;
}
}
// cerr << "BOUND ARGS FOR " << string(mixin[0].token) << endl;
// plug the holes with default arguments if any
for (int i = 0; i < params.size(); ++i) {
if (params[i].type == Node::assignment) {
Node param(params[i]);
Token name(param[0].token);
if (!m_env.query(name)) {
m_env[name] = eval(param[1], env);
}
}
}
// cerr << "BOUND DEFAULT ARGS FOR " << string(mixin[0].token) << endl;
m_env.link(env.global ? *env.global : env);
// cerr << "LINKED ENVIRONMENT FOR " << string(mixin[0].token) << endl << endl;
for (int i = 0; i < body.size(); ++i) {
body[i] = eval(body[i], m_env);
}
return body;
}
}
\ No newline at end of file
#include <map>
#ifndef SASS_NODE_INCLUDED
#include "node.hpp"
#endif
#ifndef SASS_CONTEXT_INCLUDED
#include "context.hpp"
#endif
namespace Sass {
using std::map;
Node eval(Node& expr, Environment& env);
Node accumulate(Node::Type op, Node& acc, Node& rhs);
double operate(Node::Type op, double lhs, double rhs);
Node apply(Node& mixin, const Node& args, Environment& env);
}
\ No newline at end of file
#include "evaluator.hpp"
#include <iostream>
#include <cstdlib>
namespace Sass {
using std::cerr; using std::endl;
Node eval(const Node& expr)
{
cerr << "evaluating type " << expr.type << ": " << expr.to_string("") << endl;
switch (expr.type)
{
case Node::comma_list:
case Node::space_list: {
if (expr.eval_me) {
*(expr.children->begin()) = eval(expr.children->front());
}
return expr;
} break;
case Node::expression: {
Node acc(expr.line_number, Node::expression, eval(expr.children->at(0)));
Node rhs(eval(expr.children->at(2)));
accumulate(expr.children->at(1).type, acc, rhs);
cerr << "blah" << endl;
for (int i = 3; i < expr.children->size(); i += 2) {
Node rhs(eval(expr.children->at(i+1)));
accumulate(expr.children->at(i).type, acc, rhs);
}
return acc.children->size() == 1 ? acc.children->front() : acc;
} break;
case Node::term: {
if (expr.eval_me) {
Node acc(expr.line_number, Node::expression, eval(expr.children->at(0)));
Node rhs(eval(expr.children->at(2)));
accumulate(expr.children->at(1).type, acc, rhs);
for (int i = 3; i < expr.children->size(); i += 2) {
Node rhs(eval(expr.children->at(i+1)));
accumulate(expr.children->at(i).type, acc, rhs);
}
return acc.children->size() == 1 ? acc.children->front() : acc;
}
else {
return expr;
}
} break;
case Node::textual_percentage:
case Node::textual_dimension: {
double numval = std::atof(expr.token.begin);
Token unit(Prelexer::number(expr.token.begin), expr.token.end);
return Node(expr.line_number, numval, unit);
} break;
case Node::textual_number: {
double numval = std::atof(expr.token.begin);
return Node(expr.line_number, numval);
} break;
case Node::textual_hex: {
long numval = std::strtol(expr.token.begin + 1, NULL, 16);
Node result(expr.line_number, numval);
result.is_hex = true;
return result;
} break;
default: {
return expr;
}
}
}
Node accumulate(const Node::Type op, Node& acc, Node& rhs)
{
Node lhs(acc.children->back());
double lnum = lhs.numeric_value;
double rnum = rhs.numeric_value;
// cerr << "accumulate's args: " << lhs.to_string("") << "\t" << rhs.to_string("") << endl;
// cerr << "accumulate's arg types: " << lhs.type << "\t" << rhs.type << endl;
// cerr << endl;
if (lhs.type == Node::number && rhs.type == Node::number) {
Node result(acc.line_number, operate(op, lnum, rnum));
// cerr << "accumulate just made a node: " << result.to_string("") << "\t" << result.type << endl;
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::number && rhs.type == Node::numeric_dimension) {
Node result(acc.line_number, operate(op, lnum, rnum), rhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::number) {
Node result(acc.line_number, operate(op, lnum, rnum), lhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else if (lhs.type == Node::numeric_dimension && rhs.type == Node::numeric_dimension) {
// TO DO: CHECK FOR MISMATCHED UNITS HERE
Node result(acc.line_number, operate(op, lnum, rnum), lhs.token);
acc.children->pop_back();
acc.children->push_back(result);
}
else {
// cerr << "accumulate: didn't do anything" << endl;
acc.children->push_back(rhs);
}
return acc;
}
double operate(const Node::Type op, double lhs, double rhs)
{
switch (op)
{
case Node::add: return lhs + rhs; break;
case Node::sub: return lhs - rhs; break;
case Node::mul: return lhs * rhs; break;
case Node::div: return lhs / rhs; break;
default: return 0; break;
}
}
}
\ No newline at end of file
#ifndef SASS_NODE_INCLUDED
#include "node.hpp"
#endif
namespace Sass {
Node eval(const Node& expr);
Node accumulate(const Node::Type op, Node& acc, Node& rhs);
double operate(const Node::Type op, double lhs, double rhs);
}
\ No newline at end of file
div {
p01: #abc;
p02: #aabbcc;
p03: #abc + hello;
p04: #abc + 1; // add 1 to each triplet
p05: #abc + #001; // triplet-wise addition
p06: #0000ff + 1; // add 1 to each triplet; ignore overflow because it doesn't correspond to a color name
p07: #0000ff + #000001; // convert overflow to name of color (blue)
p08: #00ffff + #000101; // aqua
p09: #000000;
p10: #000000 - 1; // black
p11: #000000 - #000001; // black
p12: #ffff00 + #010100; // yellow
p13: (#101010 / 7);
p14: #000 + 0;
p15: 10 - #222;
p16: #000 - #001;
p17: #f0f + #101;
p18: 10 #222 + 1;
p19: (10 / #222);
p20: rgb(10,10,10) + #010001;
p21: #010000 + rgb(255, 255, 255);
}
\ No newline at end of file
......@@ -21,13 +21,12 @@ div {
/* and this */
k: 15 / $three;
l: 15 / 5 / $three;
m: 1/2 + $stuff;
m: 1/2 + (1 2 3);
n: $stuff + 1/2;
n: (1/2 2/4 3) + 1/2 + 1/2;
n: 1/2 + 1/2 + 1/2;
m: 1/2, $stuff;
n: 1 2 3, $stuff 4 5 (6, 7 8 9);
o: 3px + 3px;
p: 4 + 1em;
q: (20pt / 10pt);
r: 16em * 4;
s: (5em / 2);
t: 1 + (2 + (3/4 + (4/5 6/7)));
}
\ No newline at end of file
$x: global-x;
$y: global-y;
$z: global-z;
@mixin foo($x, $y) {
margin: $x $y;
blip {
hey: now;
}
}
@mixin foogoo($x, $y, $z) {
margin: $x $y $z;
}
@mixin hux($y) {
color: $y;
@include foo(called-from-hux);
}
div {
@include foo(1, 2);
@include foo(1);
@include foogoo(1, 2);
@include foogoo($y: kwd-y, $z: kwd-z);
}
div {
@include hux();
}
$y: different-global-y;
div {
@include hux(calling-hux-again);
}
@mixin bung() {
blah: original-bung;
}
div {
@include bung();
}
@mixin bung() {
blah: redefined-bung;
}
div {
@include bung();
}
div {
@include bung;
}
div {
@include foo(arg1, arg2, $x: kwdarg1, $y: kwdarg2);
@include foo($x: kwdarg1, $y: kwdarg2, arg1, arg2);
}
@mixin ruleset() {
moo: goo;
hoo {
color: boo;
}
}
@include ruleset();
$da: default argument;
@mixin default_args($x, $y: $da) {
blah: $x $y;
}
$da: some other default;
div {
@include default_args(boogoo);
}
@mixin original() {
value: original;
}
div {
@include original();
}
@mixin original() {
value: no longer original;
}
div {
@include original();
}
@mixin set-x($x) {
$x: changed local x;
arg: $x;
$y: changed global y;
blarg: $y;
}
div {
@include set-x(blah);
a: $x;
b: $y;
}
\ No newline at end of file
#include <iostream>
#include <iomanip>
#include <string>
#include <cctype>
#include <cstdlib>
......@@ -13,16 +14,42 @@ using std::endl;
namespace Sass {
size_t Node::fresh = 0;
size_t Node::copied = 0;
size_t Node::allocations = 0;
Node Node::clone() const
{
Node n;
n.line_number = line_number;
n.token = token;
n.numeric_value = numeric_value;
n.type = type;
n.has_rules_or_comments = has_rules_or_comments;
n.has_rulesets = has_rulesets;
n.has_propsets = has_propsets;
n.has_expansions = has_expansions;
n.has_backref = has_backref;
n.from_variable = from_variable;
n.eval_me = eval_me;
if (children) {
n.children = new vector<Node>();
n.children->reserve(size());
for (int i = 0; i < size(); ++i) {
n << at(i).clone();
}
}
++fresh;
return n;
}
string Node::to_string(const string& prefix) const
{
switch (type)
{
case selector_group: { // really only needed for arg to :not
string result(children->at(0).to_string(""));
for (int i = 1; i < children->size(); ++i) {
string result(at(0).to_string(""));
for (int i = 1; i < size(); ++i) {
result += ", ";
result += children->at(i).to_string("");
result += at(i).to_string("");
}
return result;
} break;
......@@ -77,9 +104,8 @@ namespace Sass {
case attribute_selector: {
string result("[");
result += children->at(0).to_string(prefix);
result += children->at(1).to_string(prefix);
result += children->at(2).to_string(prefix);
for (int i = 0; i < 3; ++i)
{ result += children->at(i).to_string(prefix); }
result += ']';
return result;
} break;
......@@ -115,7 +141,7 @@ namespace Sass {
// }
for (int i = 1; i < children->size(); ++i) {
if (!(children->at(i).type == add ||
children->at(i).type == sub ||
// children->at(i).type == sub || // another edge case -- consider uncommenting
children->at(i).type == mul)) {
result += children->at(i).to_string(prefix);
}
......@@ -123,6 +149,15 @@ namespace Sass {
return result;
} break;
//edge case
case sub: {
return "-";
} break;
case div: {
return "/";
} break;
case numeric_dimension: {
stringstream ss;
// ss.setf(std::ios::fixed, std::ios::floatfield);
......@@ -139,6 +174,52 @@ namespace Sass {
return ss.str();
} break;
case hex_triple: {
double a = children->at(0).numeric_value;
double b = children->at(1).numeric_value;
double c = children->at(2).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 (int i = 0; i < 3; ++i) {
double x = children->at(i).numeric_value;
if (x > 0xff) x = 0xff;
else if (x < 0) x = 0;
ss << std::hex << std::setw(2) << static_cast<unsigned long>(x);
}
return ss.str();
}
} break;
case uri: {
string result("url(");
result += string(token);
result += ")";
return result;
} break;
// case expansion: {
// string result("MIXIN CALL: ");
// return result;
// } break;
default: {
return string(token);
} break;
......@@ -208,56 +289,69 @@ namespace Sass {
size_t depth,
const vector<string>& prefixes)
{
switch (type) {
switch (type)
{
case root:
if (at(0).has_expansions) {
flatten();
}
for (int i = 0; i < children->size(); ++i) {
children->at(i).emit_nested_css(buf, depth, prefixes);
}
break;
case ruleset: {
Node sel_group(children->at(0));
Node block(children->at(1));
Node sel_group(at(0));
Node block(at(1));
vector<string> new_prefixes;
if (prefixes.empty()) {
new_prefixes.reserve(sel_group.children->size());
for (int i = 0; i < sel_group.children->size(); ++i) {
new_prefixes.push_back(sel_group.children->at(i).to_string(string()));
new_prefixes.reserve(sel_group.size());
for (int i = 0; i < sel_group.size(); ++i) {
new_prefixes.push_back(sel_group[i].to_string(string()));
}
}
else {
new_prefixes.reserve(prefixes.size() * sel_group.children->size());
new_prefixes.reserve(prefixes.size() * sel_group.size());
for (int i = 0; i < prefixes.size(); ++i) {
for (int j = 0; j < sel_group.children->size(); ++j) {
new_prefixes.push_back(sel_group.children->at(j).to_string(prefixes[i]));
for (int j = 0; j < sel_group.size(); ++j) {
new_prefixes.push_back(sel_group[j].to_string(prefixes[i]));
}
}
}
if (block.has_rules_or_comments) {
if (block[0].has_expansions) block.flatten();
if (block[0].has_rules_or_comments) {
buf << string(2*depth, ' ') << new_prefixes[0];
for (int i = 1; i < new_prefixes.size(); ++i) {
buf << ", " << new_prefixes[i];
}
buf << " {";
for (int i = 0; i < block.children->size(); ++i) {
Type stm_type = block.children->at(i).type;
for (int i = 0; i < block.size(); ++i) {
Type stm_type = block[i].type;
if (stm_type == comment || stm_type == rule) {
block.children->at(i).emit_nested_css(buf, depth+1); // NEED OVERLOADED VERSION FOR COMMENTS AND RULES
block[i].emit_nested_css(buf, depth+1); // NEED OVERLOADED VERSION FOR COMMENTS AND RULES
}
// else if (stm_type == expansion) {
// // buf << string(2*(depth+1), ' ') << block[i].to_string(""); // TEMPORARY
// for (int j = 0; j < block[i].size(); ++j) {
// block[i][j].emit_nested_css(buf, depth+1);
// }
// }
}
buf << " }" << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets
}
if (block.has_rulesets) {
for (int i = 0; i < block.children->size(); ++i) {
if (block.children->at(i).type == ruleset) {
block.children->at(i).emit_nested_css(buf, depth, new_prefixes);
if (block[0].has_rulesets) {
for (int i = 0; i < block.size(); ++i) {
if (block[i].type == ruleset) {
block[i].emit_nested_css(buf, depth, new_prefixes);
}
}
}
if (block.has_rules_or_comments) --depth;
if (block[0].has_rules_or_comments) --depth;
if (depth == 0 && prefixes.empty()) buf << endl;
} break;
default:
emit_nested_css(buf, depth); // pass it along to the simpler version
break;
......@@ -266,26 +360,31 @@ namespace Sass {
void Node::emit_nested_css(stringstream& buf, size_t depth)
{
switch (type) {
switch (type)
{
case rule:
buf << endl << string(2*depth, ' ');
children->at(0).emit_nested_css(buf, depth); // property
children->at(1).emit_nested_css(buf, depth); // values
buf << ";";
break;
case property:
buf << string(token) << ": ";
break;
case values:
for (int i = 0; i < children->size(); ++i) {
buf << " " << string(children->at(i).token);
}
break;
case comment:
if (depth != 0) buf << endl;
buf << string(2*depth, ' ') << string(token);
if (depth == 0) buf << endl;
break;
default:
buf << to_string("");
break;
......@@ -340,4 +439,34 @@ namespace Sass {
}
void Node::flatten()
{
if (type != block && type != expansion && type != root) return;
for (int i = 0; i < size(); ++i) {
if (at(i).type == expansion) {
Node expn = at(i);
if (expn[0].has_expansions) expn.flatten();
at(0).has_rules_or_comments |= expn[0].has_rules_or_comments;
at(0).has_rulesets |= expn[0].has_rulesets;
at(0).has_propsets |= expn[0].has_propsets;
at(0).has_expansions |= expn[0].has_expansions;
at(i).type = none;
children->insert(children->begin() + i, expn.children->begin(), expn.children->end());
}
}
}
//
// void flatten_block(Node& block)
// {
//
// for (int i = 0; i < block.size(); ++i) {
//
// if (block[i].type == Node::expansion
//
// }
//
//
//
// }
}
\ No newline at end of file
......@@ -58,12 +58,24 @@ namespace Sass {
numeric_percentage,
numeric_dimension,
number,
hex_triple,
comment
mixin,
parameters,
expansion,
arguments,
variable,
assignment,
comment,
none,
flags
};
static size_t fresh;
static size_t copied;
static size_t allocations;
size_t line_number;
mutable vector<Node>* children;
......@@ -73,12 +85,21 @@ namespace Sass {
bool has_rules_or_comments;
bool has_rulesets;
bool has_propsets;
bool has_expansions;
bool has_backref;
bool from_variable;
bool eval_me;
bool is_hex;
Node() : type(nil), children(0) { ++fresh; }
Node() : type(none), children(0) { ++fresh; }
Node(Node::Type type)
: type(type),
children(0),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false)
{ ++fresh; }
Node(const Node& n)
: line_number(n.line_number),
......@@ -89,11 +110,11 @@ namespace Sass {
has_rules_or_comments(n.has_rules_or_comments),
has_rulesets(n.has_rulesets),
has_propsets(n.has_propsets),
has_expansions(n.has_expansions),
has_backref(n.has_backref),
from_variable(n.from_variable),
eval_me(n.eval_me),
is_hex(n.is_hex)
{ /*n.release();*/ ++copied; } // No joint custody.
eval_me(n.eval_me)
{ ++copied; }
Node(size_t line_number, Type type, size_t length = 0)
: line_number(line_number),
......@@ -104,11 +125,11 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
{ children->reserve(length); ++fresh; }
eval_me(false)
{ children->reserve(length); ++fresh; ++allocations; }
Node(size_t line_number, Type type, const Node& n)
: line_number(line_number),
......@@ -119,11 +140,11 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
{ ++fresh; }
eval_me(false)
{ ++fresh; ++allocations; }
Node(size_t line_number, Type type, const Node& n, const Node& m)
: line_number(line_number),
......@@ -134,15 +155,16 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
eval_me(false)
{
children->reserve(2);
children->push_back(n);
children->push_back(m);
++fresh;
++allocations;
}
Node(size_t line_number, Type type, Token& token)
......@@ -154,10 +176,10 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
eval_me(false)
{ ++fresh; }
Node(size_t line_number, double d)
......@@ -169,10 +191,10 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
eval_me(false)
{ ++fresh; }
Node(size_t line_number, double d, Token& token)
......@@ -184,19 +206,49 @@ namespace Sass {
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false),
is_hex(false)
eval_me(false)
{ ++fresh; }
Node(size_t line_number, double a, double b, double c)
: line_number(line_number),
children(new vector<Node>()),
token(Token()),
numeric_value(0),
type(hex_triple),
has_rules_or_comments(false),
has_rulesets(false),
has_propsets(false),
has_expansions(false),
has_backref(false),
from_variable(false),
eval_me(false)
{
children->reserve(3);
children->push_back(Node(line_number, a));
children->push_back(Node(line_number, b));
children->push_back(Node(line_number, c));
++fresh;
++allocations;
}
//~Node() { delete children; }
size_t size() const
{ return children->size(); }
Node& operator[](const size_t i) const
{ return children->at(i); }
Node& at(const size_t i) const
{ return children->at(i); }
Node& operator=(const Node& n)
{
line_number = n.line_number;
children = n.children;
// n.release();
token = n.token;
numeric_value = n.numeric_value;
type = n.type;
......@@ -206,7 +258,6 @@ namespace Sass {
has_backref = n.has_backref;
from_variable = n.from_variable;
eval_me = n.eval_me;
is_hex = n.is_hex;
++copied;
return *this;
}
......@@ -229,11 +280,15 @@ namespace Sass {
void release() const { children = 0; }
Node clone() 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_expanded_css(stringstream& buf, const string& prefix);
void flatten();
};
}
\ No newline at end of file
......@@ -38,7 +38,7 @@ namespace Sass {
extern const char slash_star[] = "/*";
extern const char star_slash[] = "*/";
const char* block_comment(const char* src) {
return delimited_by<slash_star, star_slash, false>(src);
return sequence< optional_spaces, delimited_by<slash_star, star_slash, false> >(src);
}
// Match either comment.
const char* comment(const char* src) {
......@@ -87,6 +87,14 @@ namespace Sass {
const char* import(const char* src) {
return exactly<import_kwd>(src);
}
extern const char mixin_kwd[] = "@mixin";
const char* mixin(const char* src) {
return exactly<mixin_kwd>(src);
}
extern const char include_kwd[] = "@include";
const char* include(const char* src) {
return exactly<include_kwd>(src);
}
const char* name(const char* src) {
return one_plus< alternatives< alnum,
......@@ -166,10 +174,17 @@ namespace Sass {
int len = p - src;
return (len != 4 && len != 7) ? 0 : p;
}
extern const char rgb_kwd[] = "rgb(";
const char* rgb_prefix(const char* src) {
return exactly<rgb_kwd>(src);
}
// Match CSS uri specifiers.
extern const char url_call[] = "url(";
extern const char url_kwd[] = "url(";
const char* uri_prefix(const char* src) {
return exactly<url_kwd>(src);
}
const char* uri(const char* src) {
return sequence< exactly<url_call>,
return sequence< exactly<url_kwd>,
optional<spaces>,
string_constant,
optional<spaces>,
......
......@@ -275,6 +275,8 @@ namespace Sass {
// Match CSS '@' keywords.
const char* at_keyword(const char* src);
const char* import(const char* src);
const char* mixin(const char* src);
const char* include(const char* src);
// Match CSS type selectors
const char* namespace_prefix(const char* src);
const char* type_selector(const char* src);
......@@ -292,7 +294,9 @@ namespace Sass {
const char* percentage(const char* src);
const char* dimension(const char* src);
const char* hex(const char* src);
const char* rgb_prefix(const char* src);
// Match CSS uri specifiers.
const char* uri_prefix(const char* src);
const char* uri(const char* src);
// Match CSS "!important" keyword.
const char* important(const char* src);
......
......@@ -2,6 +2,7 @@
#include <string>
#include <map>
#include "document.hpp"
#include "eval_apply.hpp"
using namespace Sass;
using namespace std;
......@@ -29,12 +30,16 @@ int main(int argc, char* argv[]) {
Document doc(path, 0);
doc.parse_scss();
cerr << "successfully parsed document" << endl;
cerr << "SUCCESSFULLY PARSED DOCUMENT" << endl;
// doc.eval_pending();
eval(doc.root, doc.context.global_env);
cerr << "SUCCESSFULLY EVALED DOCUMENT" << endl;
string output = doc.emit_css(style);
cout << output;
cerr << "Fresh nodes:\t" << Node::fresh << endl;
cerr << "Copied nodes:\t" << Node::copied << endl;
cerr << "Allocations:\t" << Node::allocations << endl;
cout << output;
return 0;
}
\ No newline at end of file
......@@ -21,13 +21,12 @@ div {
/* and this */
k: 15 / $three;
l: 15 / 5 / $three;
m: 1/2 + $stuff;
m: 1/2 + (1 2 3);
n: $stuff + 1/2;
n: (1/2 2/4 3) + 1/2 + 1/2;
n: 1/2 + 1/2 + 1/2;
m: 1/2, $stuff url("www.foo.com/blah.png") blah blah;
n: 1 2 3, $stuff 4 5 (6, 7 8 9);
o: 3px + 3px;
p: 4 + 1em;
q: (20pt / 10pt);
r: 16em * 4;
s: (5em / 2);
t: 1 + (2 + (3/4 + (4/5 6/7)));
}
\ No newline at end of file
......@@ -17,12 +17,11 @@ div {
/* and this */
k: 5;
l: 1;
m: 1/21 2 3;
m: 1/21 2 3;
n: 1 2 31/2;
n: 0.5 2/4 31/21/2;
n: 1.5;
m: 1/2, 1 2 3 url("www.foo.com/blah.png") blah blah;
n: 1 2 3, 1 2 3 4 5 6, 7 8 9;
o: 6px;
p: 5em;
q: 2;
r: 64em; }
r: 64em;
s: 2.5em;
t: 120.750.8 6/7; }
div {
p01: #abc;
p02: #aabbcc;
p03: #abc + hello;
p04: #abc + 1; // add 1 to each triplet
p05: #abc + #001; // triplet-wise addition
p06: #0000ff + 1; // add 1 to each triplet; ignore overflow because it doesn't correspond to a color name
p07: #0000ff + #000001; // convert overflow to name of color (blue)
p08: #00ffff + #000101; // aqua
p09: #000000;
p10: #000000 - 1; // black
p11: #000000 - #000001; // black
p12: #ffff00 + #010100; // yellow
p13: (#101010 / 7);
p14: #000 + 0;
p15: 10 - #222;
p16: #000 - #001;
p17: #f0f + #101;
p18: 10 #222 + 1;
p19: (10 / #222);
p20: rgb(10,10,10) + #010001;
p21: #010000 + rgb(255, 255, 255);
}
\ No newline at end of file
div {
p01: #abc;
p02: #aabbcc;
p03: #aabbcchello;
p04: #abbccd;
p05: #aabbdd;
p06: #0101ff;
p07: blue;
p08: aqua;
p09: #000000;
p10: black;
p11: black;
p12: yellow;
p13: #020202;
p14: black;
p15: 10-#222222;
p16: black;
p17: fuchsia;
p18: 10 #232323;
p19: 10/#222222;
p20: #0b0a0b;
p21: white; }
@mixin foo($x, $y) {
hugabug: $y $x;
}
@mixin bar($a, $b: flug) {
flugablug: $a $b glug;
}
@mixin hux() {
no: parameters here;
div, span {
some: nested stuff;
foo, bar {
more: stuff and so forth;
blah: blah;
}
}
/* end of hux */
}
a {
hey: ho;
@include foo($x: kwd-arg another-kwd-arg, second, third);
@include foo(eks, why, $x: kwd-eks);
@include foo($y: kwd-y, $x: kwd-x);
goo: boo hoo;
@include hux;
@include bar(pug);
@include bar(pug, mug);
}
$x: from a variable;
div {
blah: blah $x blah;
}
\ No newline at end of file
a {
hey: ho;
hugabug: third second;
hugabug: why eks;
hugabug: kwd-y kwd-x;
goo: boo hoo;
no: parameters here;
/* end of hux */
flugablug: pug flug glug;
flugablug: pug mug glug; }
a div, a span {
some: nested stuff; }
a div foo, a div bar, a span foo, a span bar {
more: stuff and so forth;
blah: blah; }
div {
blah: blah from a variable blah; }
$x: global x;
$y: global y;
@mixin foo($x) {
f-a: $x;
f-b: $y;
$x: local x changed by foo;
$y: global y changed by foo;
$z: new local z;
f-a: $x;
f-b: $y;
f-c: $z;
}
div {
a: $x;
b: $y;
@include foo(arg);
a: $x;
b: $y;
}
\ No newline at end of file
div {
a: global x;
b: global y;
f-a: arg;
f-b: global y;
f-a: local x changed by foo;
f-b: global y changed by foo;
f-c: new local z;
a: global x;
b: global y changed by foo; }
$x: global-x;
$y: global-y;
$z: global-z;
@mixin foo($x, $y) {
/* begin foo */
margin: $x $y;
blip {
hey: now;
}
/* end foo */
}
@mixin foogoo($x, $y, $z) {
margin: $x $y $z;
}
@mixin hux($y) {
/* begin hux */
color: $y;
@include foo(called-from-hux);
/* end hux */
}
div {
@include foo(1, 2);
@include foo(1);
@include foogoo(1, 2);
@include foogoo($y /* blah */ : kwd-y, $z: kwd-z);
}
div {
@include hux();
}
$y: different-global-y;
div {
@include hux(calling-hux-again);
}
@mixin bung() {
blah: original-bung;
}
div {
@include bung();
}
@mixin bung() {
blah: redefined-bung;
}
div {
@include bung();
}
div {
/* calls to nullary mixins may omit the empty argument list */
@include bung;
}
div {
@include foo(arg1, arg2, $x: kwdarg1, $y: kwdarg2);
@include foo($x: kwdarg1, $y: kwdarg2, arg1, arg2);
}
@mixin ruleset() {
hoo {
color: boo;
}
}
@include ruleset();
$da: default argument;
@mixin default_args($x, $y: $da) {
blah: $x $y;
}
$da: some other default;
div {
@include default_args(boogoo);
}
@mixin original() {
value: original;
}
div {
@include original();
}
@mixin original() {
value: no longer original;
}
div {
@include original();
}
@mixin set-x($x) {
$x: changed local x;
arg: $x;
$y: changed global y;
blarg: $y;
}
div {
@include set-x(blah);
a: $x;
b: $y;
}
\ No newline at end of file
div {
/* begin foo */
margin: 1 2;
/* end foo */
/* begin foo */
margin: 1 global-y;
/* end foo */
margin: 1 2 global-z;
margin: global-x kwd-y kwd-z; }
div blip {
hey: now; }
div blip {
hey: now; }
div {
/* begin hux */
color: global-y;
/* begin foo */
margin: called-from-hux global-y;
/* end foo */
/* end hux */ }
div blip {
hey: now; }
div {
/* begin hux */
color: calling-hux-again;
/* begin foo */
margin: called-from-hux different-global-y;
/* end foo */
/* end hux */ }
div blip {
hey: now; }
div {
blah: original-bung; }
div {
blah: redefined-bung; }
div {
/* calls to nullary mixins may omit the empty argument list */
blah: redefined-bung; }
div {
/* begin foo */
margin: arg1 arg2;
/* end foo */
/* begin foo */
margin: arg1 arg2;
/* end foo */ }
div blip {
hey: now; }
div blip {
hey: now; }
hoo {
color: boo; }
div {
blah: boogoo some other default; }
div {
value: original; }
div {
value: no longer original; }
div {
arg: changed local x;
blarg: changed global y;
a: global-x;
b: changed global y; }
@mixin foo() {
/* begin foo */
/* assigning to $x */
$x: inside foo;
x: $x;
/* end foo */
}
outer {
/* assigning to $x */
$x: inside outer scope;
blah: blah;
inner {
@include foo();
x: $x;
}
}
\ No newline at end of file
outer {
/* assigning to $x */
blah: blah; }
outer inner {
/* begin foo */
/* assigning to $x */
x: inside foo;
/* end foo */
x: inside outer scope; }
......@@ -26,6 +26,8 @@ div {
}
p {
padding: 10px 8%;
arithmetic: 1 + 2 * 3 / 4;
hex-arithmetic: #abcdef + 1;
-webkit-box-sizing: $blux; ; ;
; ; ;;;
}
......
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