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 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 test: build
ruby spec.rb spec/basic/ ruby spec.rb spec/basic/
......
#define SASS_CONTEXT_INCLUDED
namespace Sass { namespace Sass {
using std::map; 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 { struct Context {
map<Token, Node> environment; Environment global_env;
vector<Node> pending;
vector<char*> source_refs; vector<char*> source_refs;
size_t ref_count; size_t ref_count;
Context() Context()
: environment(map<Token, Node>()), : pending(vector<Node>()),
source_refs(vector<char*>()), source_refs(vector<char*>()),
ref_count(0) ref_count(0)
{ } { }
...@@ -19,4 +52,5 @@ namespace Sass { ...@@ -19,4 +52,5 @@ namespace Sass {
} }
} }
}; };
}
\ No newline at end of file }
#include <cstdio> #include <cstdio>
#include "document.hpp" #include "document.hpp"
#include "eval_apply.hpp"
#include <iostream> #include <iostream>
namespace Sass { namespace Sass {
...@@ -58,4 +59,32 @@ namespace Sass { ...@@ -58,4 +59,32 @@ namespace Sass {
if (context.ref_count == 0) delete &context; 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 { ...@@ -40,7 +40,7 @@ namespace Sass {
if (!start) start = position; if (!start) start = position;
const char* after_whitespace; const char* after_whitespace;
if (mx == block_comment) { if (mx == block_comment) {
after_whitespace = after_whitespace = // start;
zero_plus< alternatives<spaces, line_comment> >(start); zero_plus< alternatives<spaces, line_comment> >(start);
} }
else if (/*mx == ancestor_of ||*/ mx == no_spaces) { else if (/*mx == ancestor_of ||*/ mx == no_spaces) {
...@@ -75,7 +75,7 @@ namespace Sass { ...@@ -75,7 +75,7 @@ namespace Sass {
{ {
const char* after_whitespace; const char* after_whitespace;
if (mx == block_comment) { if (mx == block_comment) {
after_whitespace = after_whitespace = // position;
zero_plus< alternatives<spaces, line_comment> >(position); zero_plus< alternatives<spaces, line_comment> >(position);
} }
else if (mx == ancestor_of || mx == no_spaces) { else if (mx == ancestor_of || mx == no_spaces) {
...@@ -111,15 +111,22 @@ namespace Sass { ...@@ -111,15 +111,22 @@ namespace Sass {
void parse_scss(); void parse_scss();
Node parse_import(); Node parse_import();
void parse_var_def(); Node parse_include();
Node parse_ruleset(); 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_group();
Node parse_selector(); Node parse_selector();
Node parse_simple_selector_sequence(); Node parse_simple_selector_sequence();
Node parse_simple_selector(); Node parse_simple_selector();
Node parse_pseudo(); Node parse_pseudo();
Node parse_attribute_selector(); Node parse_attribute_selector();
Node parse_block(); Node parse_block(bool definition = false);
Node parse_rule(); Node parse_rule();
Node parse_values(); Node parse_values();
Node parse_list(); Node parse_list();
...@@ -129,6 +136,8 @@ namespace Sass { ...@@ -129,6 +136,8 @@ namespace Sass {
Node parse_term(); Node parse_term();
Node parse_factor(); Node parse_factor();
Node parse_value(); Node parse_value();
Node parse_identifier();
Node parse_variable();
const char* look_for_rule(const char* start = 0); const char* look_for_rule(const char* start = 0);
const char* look_for_values(const char* start = 0); const char* look_for_values(const char* start = 0);
...@@ -140,6 +149,7 @@ namespace Sass { ...@@ -140,6 +149,7 @@ namespace Sass {
const char* look_for_pseudo(const char* start = 0); const char* look_for_pseudo(const char* start = 0);
const char* look_for_attrib(const char* start = 0); const char* look_for_attrib(const char* start = 0);
void eval_pending();
string emit_css(CSS_Style style); 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 { ...@@ -21,13 +21,12 @@ div {
/* and this */ /* and this */
k: 15 / $three; k: 15 / $three;
l: 15 / 5 / $three; l: 15 / 5 / $three;
m: 1/2 + $stuff; m: 1/2, $stuff;
m: 1/2 + (1 2 3); n: 1 2 3, $stuff 4 5 (6, 7 8 9);
n: $stuff + 1/2;
n: (1/2 2/4 3) + 1/2 + 1/2;
n: 1/2 + 1/2 + 1/2;
o: 3px + 3px; o: 3px + 3px;
p: 4 + 1em; p: 4 + 1em;
q: (20pt / 10pt); q: (20pt / 10pt);
r: 16em * 4; 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 <iostream>
#include <iomanip>
#include <string> #include <string>
#include <cctype> #include <cctype>
#include <cstdlib> #include <cstdlib>
...@@ -13,16 +14,42 @@ using std::endl; ...@@ -13,16 +14,42 @@ using std::endl;
namespace Sass { namespace Sass {
size_t Node::fresh = 0; size_t Node::fresh = 0;
size_t Node::copied = 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 string Node::to_string(const string& prefix) const
{ {
switch (type) switch (type)
{ {
case selector_group: { // really only needed for arg to :not case selector_group: { // really only needed for arg to :not
string result(children->at(0).to_string("")); string result(at(0).to_string(""));
for (int i = 1; i < children->size(); ++i) { for (int i = 1; i < size(); ++i) {
result += ", "; result += ", ";
result += children->at(i).to_string(""); result += at(i).to_string("");
} }
return result; return result;
} break; } break;
...@@ -77,9 +104,8 @@ namespace Sass { ...@@ -77,9 +104,8 @@ namespace Sass {
case attribute_selector: { case attribute_selector: {
string result("["); string result("[");
result += children->at(0).to_string(prefix); for (int i = 0; i < 3; ++i)
result += children->at(1).to_string(prefix); { result += children->at(i).to_string(prefix); }
result += children->at(2).to_string(prefix);
result += ']'; result += ']';
return result; return result;
} break; } break;
...@@ -115,7 +141,7 @@ namespace Sass { ...@@ -115,7 +141,7 @@ namespace Sass {
// } // }
for (int i = 1; i < children->size(); ++i) { for (int i = 1; i < children->size(); ++i) {
if (!(children->at(i).type == add || 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)) { children->at(i).type == mul)) {
result += children->at(i).to_string(prefix); result += children->at(i).to_string(prefix);
} }
...@@ -123,6 +149,15 @@ namespace Sass { ...@@ -123,6 +149,15 @@ namespace Sass {
return result; return result;
} break; } break;
//edge case
case sub: {
return "-";
} break;
case div: {
return "/";
} break;
case numeric_dimension: { case numeric_dimension: {
stringstream ss; stringstream ss;
// ss.setf(std::ios::fixed, std::ios::floatfield); // ss.setf(std::ios::fixed, std::ios::floatfield);
...@@ -138,6 +173,52 @@ namespace Sass { ...@@ -138,6 +173,52 @@ namespace Sass {
ss << numeric_value; ss << numeric_value;
return ss.str(); return ss.str();
} break; } 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: { default: {
return string(token); return string(token);
...@@ -208,56 +289,69 @@ namespace Sass { ...@@ -208,56 +289,69 @@ namespace Sass {
size_t depth, size_t depth,
const vector<string>& prefixes) const vector<string>& prefixes)
{ {
switch (type) { switch (type)
{
case root: case root:
if (at(0).has_expansions) {
flatten();
}
for (int i = 0; i < children->size(); ++i) { for (int i = 0; i < children->size(); ++i) {
children->at(i).emit_nested_css(buf, depth, prefixes); children->at(i).emit_nested_css(buf, depth, prefixes);
} }
break; break;
case ruleset: { case ruleset: {
Node sel_group(children->at(0)); Node sel_group(at(0));
Node block(children->at(1)); Node block(at(1));
vector<string> new_prefixes; vector<string> new_prefixes;
if (prefixes.empty()) { if (prefixes.empty()) {
new_prefixes.reserve(sel_group.children->size()); new_prefixes.reserve(sel_group.size());
for (int i = 0; i < sel_group.children->size(); ++i) { for (int i = 0; i < sel_group.size(); ++i) {
new_prefixes.push_back(sel_group.children->at(i).to_string(string())); new_prefixes.push_back(sel_group[i].to_string(string()));
} }
} }
else { 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 i = 0; i < prefixes.size(); ++i) {
for (int j = 0; j < sel_group.children->size(); ++j) { for (int j = 0; j < sel_group.size(); ++j) {
new_prefixes.push_back(sel_group.children->at(j).to_string(prefixes[i])); 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]; buf << string(2*depth, ' ') << new_prefixes[0];
for (int i = 1; i < new_prefixes.size(); ++i) { for (int i = 1; i < new_prefixes.size(); ++i) {
buf << ", " << new_prefixes[i]; buf << ", " << new_prefixes[i];
} }
buf << " {"; buf << " {";
for (int i = 0; i < block.children->size(); ++i) { for (int i = 0; i < block.size(); ++i) {
Type stm_type = block.children->at(i).type; Type stm_type = block[i].type;
if (stm_type == comment || stm_type == rule) { 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; buf << " }" << endl;
++depth; // if we printed content at this level, we need to indent any nested rulesets ++depth; // if we printed content at this level, we need to indent any nested rulesets
} }
if (block.has_rulesets) { if (block[0].has_rulesets) {
for (int i = 0; i < block.children->size(); ++i) { for (int i = 0; i < block.size(); ++i) {
if (block.children->at(i).type == ruleset) { if (block[i].type == ruleset) {
block.children->at(i).emit_nested_css(buf, depth, new_prefixes); 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; if (depth == 0 && prefixes.empty()) buf << endl;
} break; } break;
default: default:
emit_nested_css(buf, depth); // pass it along to the simpler version emit_nested_css(buf, depth); // pass it along to the simpler version
break; break;
...@@ -266,26 +360,31 @@ namespace Sass { ...@@ -266,26 +360,31 @@ namespace Sass {
void Node::emit_nested_css(stringstream& buf, size_t depth) void Node::emit_nested_css(stringstream& buf, size_t depth)
{ {
switch (type) { switch (type)
{
case rule: case rule:
buf << endl << string(2*depth, ' '); buf << endl << string(2*depth, ' ');
children->at(0).emit_nested_css(buf, depth); // property children->at(0).emit_nested_css(buf, depth); // property
children->at(1).emit_nested_css(buf, depth); // values children->at(1).emit_nested_css(buf, depth); // values
buf << ";"; buf << ";";
break; break;
case property: case property:
buf << string(token) << ": "; buf << string(token) << ": ";
break; break;
case values: case values:
for (int i = 0; i < children->size(); ++i) { for (int i = 0; i < children->size(); ++i) {
buf << " " << string(children->at(i).token); buf << " " << string(children->at(i).token);
} }
break; break;
case comment: case comment:
if (depth != 0) buf << endl; if (depth != 0) buf << endl;
buf << string(2*depth, ' ') << string(token); buf << string(2*depth, ' ') << string(token);
if (depth == 0) buf << endl; if (depth == 0) buf << endl;
break; break;
default: default:
buf << to_string(""); buf << to_string("");
break; break;
...@@ -339,5 +438,35 @@ namespace Sass { ...@@ -339,5 +438,35 @@ 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 { ...@@ -58,12 +58,24 @@ namespace Sass {
numeric_percentage, numeric_percentage,
numeric_dimension, numeric_dimension,
number, number,
hex_triple,
comment
mixin,
parameters,
expansion,
arguments,
variable,
assignment,
comment,
none,
flags
}; };
static size_t fresh; static size_t fresh;
static size_t copied; static size_t copied;
static size_t allocations;
size_t line_number; size_t line_number;
mutable vector<Node>* children; mutable vector<Node>* children;
...@@ -73,12 +85,21 @@ namespace Sass { ...@@ -73,12 +85,21 @@ namespace Sass {
bool has_rules_or_comments; bool has_rules_or_comments;
bool has_rulesets; bool has_rulesets;
bool has_propsets; bool has_propsets;
bool has_expansions;
bool has_backref; bool has_backref;
bool from_variable; bool from_variable;
bool eval_me; 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) Node(const Node& n)
: line_number(n.line_number), : line_number(n.line_number),
...@@ -89,11 +110,11 @@ namespace Sass { ...@@ -89,11 +110,11 @@ namespace Sass {
has_rules_or_comments(n.has_rules_or_comments), has_rules_or_comments(n.has_rules_or_comments),
has_rulesets(n.has_rulesets), has_rulesets(n.has_rulesets),
has_propsets(n.has_propsets), has_propsets(n.has_propsets),
has_expansions(n.has_expansions),
has_backref(n.has_backref), has_backref(n.has_backref),
from_variable(n.from_variable), from_variable(n.from_variable),
eval_me(n.eval_me), eval_me(n.eval_me)
is_hex(n.is_hex) { ++copied; }
{ /*n.release();*/ ++copied; } // No joint custody.
Node(size_t line_number, Type type, size_t length = 0) Node(size_t line_number, Type type, size_t length = 0)
: line_number(line_number), : line_number(line_number),
...@@ -104,11 +125,11 @@ namespace Sass { ...@@ -104,11 +125,11 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false) { children->reserve(length); ++fresh; ++allocations; }
{ children->reserve(length); ++fresh; }
Node(size_t line_number, Type type, const Node& n) Node(size_t line_number, Type type, const Node& n)
: line_number(line_number), : line_number(line_number),
...@@ -119,11 +140,11 @@ namespace Sass { ...@@ -119,11 +140,11 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false) { ++fresh; ++allocations; }
{ ++fresh; }
Node(size_t line_number, Type type, const Node& n, const Node& m) Node(size_t line_number, Type type, const Node& n, const Node& m)
: line_number(line_number), : line_number(line_number),
...@@ -134,15 +155,16 @@ namespace Sass { ...@@ -134,15 +155,16 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false)
{ {
children->reserve(2); children->reserve(2);
children->push_back(n); children->push_back(n);
children->push_back(m); children->push_back(m);
++fresh; ++fresh;
++allocations;
} }
Node(size_t line_number, Type type, Token& token) Node(size_t line_number, Type type, Token& token)
...@@ -154,10 +176,10 @@ namespace Sass { ...@@ -154,10 +176,10 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false)
{ ++fresh; } { ++fresh; }
Node(size_t line_number, double d) Node(size_t line_number, double d)
...@@ -169,10 +191,10 @@ namespace Sass { ...@@ -169,10 +191,10 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false)
{ ++fresh; } { ++fresh; }
Node(size_t line_number, double d, Token& token) Node(size_t line_number, double d, Token& token)
...@@ -184,19 +206,49 @@ namespace Sass { ...@@ -184,19 +206,49 @@ namespace Sass {
has_rules_or_comments(false), has_rules_or_comments(false),
has_rulesets(false), has_rulesets(false),
has_propsets(false), has_propsets(false),
has_expansions(false),
has_backref(false), has_backref(false),
from_variable(false), from_variable(false),
eval_me(false), eval_me(false)
is_hex(false)
{ ++fresh; } { ++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; } //~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) Node& operator=(const Node& n)
{ {
line_number = n.line_number; line_number = n.line_number;
children = n.children; children = n.children;
// n.release();
token = n.token; token = n.token;
numeric_value = n.numeric_value; numeric_value = n.numeric_value;
type = n.type; type = n.type;
...@@ -206,7 +258,6 @@ namespace Sass { ...@@ -206,7 +258,6 @@ namespace Sass {
has_backref = n.has_backref; has_backref = n.has_backref;
from_variable = n.from_variable; from_variable = n.from_variable;
eval_me = n.eval_me; eval_me = n.eval_me;
is_hex = n.is_hex;
++copied; ++copied;
return *this; return *this;
} }
...@@ -229,11 +280,15 @@ namespace Sass { ...@@ -229,11 +280,15 @@ namespace Sass {
void release() const { children = 0; } void release() const { children = 0; }
Node clone() const;
void echo(stringstream& buf, size_t depth = 0); void echo(stringstream& buf, size_t depth = 0);
void emit_nested_css(stringstream& buf, void emit_nested_css(stringstream& buf,
size_t depth, size_t depth,
const vector<string>& prefixes); const vector<string>& prefixes);
void emit_nested_css(stringstream& buf, size_t depth); void emit_nested_css(stringstream& buf, size_t depth);
void emit_expanded_css(stringstream& buf, const string& prefix); void emit_expanded_css(stringstream& buf, const string& prefix);
void flatten();
}; };
} }
\ No newline at end of file
...@@ -38,7 +38,7 @@ namespace Sass { ...@@ -38,7 +38,7 @@ namespace Sass {
extern const char slash_star[] = "/*"; extern const char slash_star[] = "/*";
extern const char star_slash[] = "*/"; extern const char star_slash[] = "*/";
const char* block_comment(const char* src) { 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. // Match either comment.
const char* comment(const char* src) { const char* comment(const char* src) {
...@@ -87,6 +87,14 @@ namespace Sass { ...@@ -87,6 +87,14 @@ namespace Sass {
const char* import(const char* src) { const char* import(const char* src) {
return exactly<import_kwd>(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) { const char* name(const char* src) {
return one_plus< alternatives< alnum, return one_plus< alternatives< alnum,
...@@ -166,10 +174,17 @@ namespace Sass { ...@@ -166,10 +174,17 @@ namespace Sass {
int len = p - src; int len = p - src;
return (len != 4 && len != 7) ? 0 : p; 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. // 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) { const char* uri(const char* src) {
return sequence< exactly<url_call>, return sequence< exactly<url_kwd>,
optional<spaces>, optional<spaces>,
string_constant, string_constant,
optional<spaces>, optional<spaces>,
......
...@@ -275,6 +275,8 @@ namespace Sass { ...@@ -275,6 +275,8 @@ namespace Sass {
// Match CSS '@' keywords. // Match CSS '@' keywords.
const char* at_keyword(const char* src); const char* at_keyword(const char* src);
const char* import(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 // Match CSS type selectors
const char* namespace_prefix(const char* src); const char* namespace_prefix(const char* src);
const char* type_selector(const char* src); const char* type_selector(const char* src);
...@@ -292,7 +294,9 @@ namespace Sass { ...@@ -292,7 +294,9 @@ namespace Sass {
const char* percentage(const char* src); const char* percentage(const char* src);
const char* dimension(const char* src); const char* dimension(const char* src);
const char* hex(const char* src); const char* hex(const char* src);
const char* rgb_prefix(const char* src);
// Match CSS uri specifiers. // Match CSS uri specifiers.
const char* uri_prefix(const char* src);
const char* uri(const char* src); const char* uri(const char* src);
// Match CSS "!important" keyword. // Match CSS "!important" keyword.
const char* important(const char* src); const char* important(const char* src);
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include "document.hpp" #include "document.hpp"
#include "eval_apply.hpp"
using namespace Sass; using namespace Sass;
using namespace std; using namespace std;
...@@ -29,12 +30,16 @@ int main(int argc, char* argv[]) { ...@@ -29,12 +30,16 @@ int main(int argc, char* argv[]) {
Document doc(path, 0); Document doc(path, 0);
doc.parse_scss(); 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); string output = doc.emit_css(style);
cout << output;
cerr << "Fresh nodes:\t" << Node::fresh << endl; cerr << "Fresh nodes:\t" << Node::fresh << endl;
cerr << "Copied nodes:\t" << Node::copied << endl; cerr << "Copied nodes:\t" << Node::copied << endl;
cerr << "Allocations:\t" << Node::allocations << endl;
cout << output;
return 0; return 0;
} }
\ No newline at end of file
...@@ -21,13 +21,12 @@ div { ...@@ -21,13 +21,12 @@ div {
/* and this */ /* and this */
k: 15 / $three; k: 15 / $three;
l: 15 / 5 / $three; l: 15 / 5 / $three;
m: 1/2 + $stuff; m: 1/2, $stuff url("www.foo.com/blah.png") blah blah;
m: 1/2 + (1 2 3); n: 1 2 3, $stuff 4 5 (6, 7 8 9);
n: $stuff + 1/2;
n: (1/2 2/4 3) + 1/2 + 1/2;
n: 1/2 + 1/2 + 1/2;
o: 3px + 3px; o: 3px + 3px;
p: 4 + 1em; p: 4 + 1em;
q: (20pt / 10pt); q: (20pt / 10pt);
r: 16em * 4; 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 { ...@@ -17,12 +17,11 @@ div {
/* and this */ /* and this */
k: 5; k: 5;
l: 1; l: 1;
m: 1/21 2 3; m: 1/2, 1 2 3 url("www.foo.com/blah.png") blah blah;
m: 1/21 2 3; n: 1 2 3, 1 2 3 4 5 6, 7 8 9;
n: 1 2 31/2;
n: 0.5 2/4 31/21/2;
n: 1.5;
o: 6px; o: 6px;
p: 5em; p: 5em;
q: 2; 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 { ...@@ -26,6 +26,8 @@ div {
} }
p { p {
padding: 10px 8%; padding: 10px 8%;
arithmetic: 1 + 2 * 3 / 4;
hex-arithmetic: #abcdef + 1;
-webkit-box-sizing: $blux; ; ; -webkit-box-sizing: $blux; ; ;
; ; ;;; ; ; ;;;
} }
......
...@@ -81,9 +81,9 @@ namespace Sass { ...@@ -81,9 +81,9 @@ namespace Sass {
bool Token::operator<(const Token& rhs) const bool Token::operator<(const Token& rhs) const
{ {
const char* first1 = begin; const char* first1 = begin;
const char* last1 = end; const char* last1 = end;
const char* first2 = rhs.begin; const char* first2 = rhs.begin;
const char* last2 = rhs.end; const char* last2 = rhs.end;
while (first1!=last1) while (first1!=last1)
{ {
if (first2==last2 || *first2<*first1) return false; if (first2==last2 || *first2<*first1) return false;
......
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