Commit 0375044e by Aaron Leung

Parsing mixin definitions, and tweaking the other functions to handle one-pass…

Parsing mixin definitions, and tweaking the other functions to handle one-pass parsing and expansion.
parent 3608b174
...@@ -3,11 +3,13 @@ namespace Sass { ...@@ -3,11 +3,13 @@ namespace Sass {
struct Context { struct Context {
map<Token, Node> environment; map<Token, Node> environment;
map<Token, Node> mixins;
vector<char*> source_refs; vector<char*> source_refs;
size_t ref_count; size_t ref_count;
Context() Context()
: environment(map<Token, Node>()), : environment(map<Token, Node>()),
mixins(map<Token, Node>()),
source_refs(vector<char*>()), source_refs(vector<char*>()),
ref_count(0) ref_count(0)
{ } { }
......
...@@ -111,6 +111,8 @@ namespace Sass { ...@@ -111,6 +111,8 @@ namespace Sass {
void parse_scss(); void parse_scss();
Node parse_import(); Node parse_import();
void parse_mixin_def();
Node parse_mixin_params();
void parse_var_def(); void parse_var_def();
Node parse_ruleset(); Node parse_ruleset();
Node parse_selector_group(); Node parse_selector_group();
...@@ -119,16 +121,16 @@ namespace Sass { ...@@ -119,16 +121,16 @@ namespace Sass {
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 delay = false);
Node parse_rule(); Node parse_rule(bool delay = false);
Node parse_values(); Node parse_values();
Node parse_list(); Node parse_list(bool delay = false);
Node parse_comma_list(); Node parse_comma_list(bool delay = false);
Node parse_space_list(); Node parse_space_list(bool delay = false);
Node parse_expression(); Node parse_expression(bool delay = false);
Node parse_term(); Node parse_term(bool delay = false);
Node parse_factor(); Node parse_factor(bool delay = false);
Node parse_value(); Node parse_value(bool delay = false);
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);
......
...@@ -16,6 +16,9 @@ namespace Sass { ...@@ -16,6 +16,9 @@ namespace Sass {
root += parse_import(); root += parse_import();
lex< exactly<';'> >(); lex< exactly<';'> >();
} }
else if (peek< mixin >(position)) {
parse_mixin_def();
}
else if (peek< variable >(position)) { else if (peek< variable >(position)) {
parse_var_def(); parse_var_def();
lex< exactly<';'> >(); lex< exactly<';'> >();
...@@ -40,6 +43,41 @@ namespace Sass { ...@@ -40,6 +43,41 @@ namespace Sass {
// source_refs.push_back(importee.source); // source_refs.push_back(importee.source);
return importee.root; return importee.root;
} }
void Document::parse_mixin_def() {
lex< mixin >();
lex< identifier >();
Node name(line_number, Node::identifier, lexed);
Node params(parse_mixin_params());
Node body(parse_block(true));
Node mixin(line_number, Node::mixin, 3);
mixin << name << params << body;
context.mixins[name.token] = mixin;
cerr << "parsing mixin definition: ";
cerr << string(mixin[0].token) << "(";
if (params.size() > 0) {
cerr << string(params[0].token);
for (int i = 1; i < params.size(); ++i) {
cerr << " ," << string(params[i].token);
}
}
cerr << ")" << endl;
}
Node Document::parse_mixin_params() {
Node params(line_number, Node::parameters);
lex< exactly<'('> >();
if (lex< variable >()) {
params << Node(line_number, Node::variable, lexed);
while (lex< exactly<','> >()) {
lex< variable >();
params << Node(line_number, Node::variable, lexed);
}
}
lex< exactly<')'> >();
return params;
}
void Document::parse_var_def() void Document::parse_var_def()
{ {
...@@ -196,7 +234,7 @@ namespace Sass { ...@@ -196,7 +234,7 @@ namespace Sass {
return attr_sel; return attr_sel;
} }
Node Document::parse_block() Node Document::parse_block(bool delay)
{ {
lex< exactly<'{'> >(); lex< exactly<'{'> >();
bool semicolon = false; bool semicolon = false;
...@@ -244,7 +282,7 @@ namespace Sass { ...@@ -244,7 +282,7 @@ namespace Sass {
block.has_rulesets = true; block.has_rulesets = true;
} }
else if (!peek< exactly<';'> >()) { else if (!peek< exactly<';'> >()) {
block << parse_rule(); block << parse_rule(delay);
block.has_rules_or_comments = true; block.has_rules_or_comments = true;
semicolon = true; semicolon = true;
} }
...@@ -253,13 +291,13 @@ namespace Sass { ...@@ -253,13 +291,13 @@ namespace Sass {
return block; return block;
} }
Node Document::parse_rule() { Node Document::parse_rule(bool delay) {
Node rule(line_number, Node::rule, 2); Node rule(line_number, Node::rule, 2);
lex< identifier >(); lex< identifier >();
rule << Node(line_number, Node::property, lexed); rule << Node(line_number, Node::property, lexed);
lex< exactly<':'> >(); lex< exactly<':'> >();
// rule << parse_values(); // rule << parse_values();
rule << parse_list(); rule << parse_list(delay);
return rule; return rule;
} }
...@@ -283,32 +321,40 @@ namespace Sass { ...@@ -283,32 +321,40 @@ namespace Sass {
return values; return values;
} }
Node Document::parse_list() Node Document::parse_list(bool delay)
{ {
Node val(parse_comma_list()); Node val(parse_comma_list(delay));
if (val.type != Node::comma_list && val.type != Node::space_list && val.eval_me) { if (!delay) {
return eval(val); if (val.type != Node::comma_list && val.type != Node::space_list && val.eval_me) {
} return eval(val);
else if (val.type == Node::comma_list || val.type == Node::space_list) { }
for (int i = 0; i < val.children->size(); ++i) { else if (val.type == Node::comma_list || val.type == Node::space_list) {
if (val.children->at(i).eval_me) { for (int i = 0; i < val.children->size(); ++i) {
val.children->at(i) = eval(val.children->at(i)); if (val.children->at(i).eval_me) {
val.children->at(i) = eval(val.children->at(i));
}
} }
return val;
}
else {
return val;
} }
} }
return val; else {
return val;
}
// return parse_comma_list(); // return parse_comma_list();
} }
Node Document::parse_comma_list() Node Document::parse_comma_list(bool delay)
{ {
if (peek< exactly<';'> >(position) || if (peek< exactly<';'> >(position) ||
peek< exactly<'}'> >(position) || peek< exactly<'}'> >(position) ||
peek< exactly<')'> >(position)) { peek< exactly<')'> >(position)) {
return Node(line_number, Node::nil); return Node(line_number, Node::nil);
} }
Node list1(parse_space_list()); Node list1(parse_space_list(delay));
// if it's a singleton, return it directly; don't wrap it // if it's a singleton, return it directly; don't wrap it
if (!peek< exactly<','> >(position)) return list1; if (!peek< exactly<','> >(position)) return list1;
...@@ -316,14 +362,14 @@ namespace Sass { ...@@ -316,14 +362,14 @@ namespace Sass {
comma_list << list1; comma_list << list1;
while (lex< exactly<','> >()) while (lex< exactly<','> >())
{ comma_list << parse_space_list(); } { comma_list << parse_space_list(delay); }
return comma_list; return comma_list;
} }
Node Document::parse_space_list() Node Document::parse_space_list(bool delay)
{ {
Node expr1(parse_expression()); Node expr1(parse_expression(delay));
// if it's a singleton, return it directly; don't wrap it // if it's a singleton, return it directly; don't wrap it
if (peek< exactly<';'> >(position) || if (peek< exactly<';'> >(position) ||
peek< exactly<'}'> >(position) || peek< exactly<'}'> >(position) ||
...@@ -342,14 +388,14 @@ namespace Sass { ...@@ -342,14 +388,14 @@ namespace Sass {
peek< exactly<','> >(position))) peek< exactly<','> >(position)))
// { Node expr(parse_expression()); // { Node expr(parse_expression());
// space_list << (expr.eval_me ? eval(expr) : expr); } // space_list << (expr.eval_me ? eval(expr) : expr); }
{ space_list << parse_expression(); } { space_list << parse_expression(delay); }
return space_list; return space_list;
} }
Node Document::parse_expression() Node Document::parse_expression(bool delay)
{ {
Node term1(parse_term()); Node term1(parse_term(delay));
// if it's a singleton, return it directly; don't wrap it // if it's a singleton, return it directly; don't wrap it
if (!(peek< exactly<'+'> >(position) || if (!(peek< exactly<'+'> >(position) ||
peek< exactly<'-'> >(position))) peek< exactly<'-'> >(position)))
...@@ -366,7 +412,7 @@ namespace Sass { ...@@ -366,7 +412,7 @@ namespace Sass {
else { else {
expression << Node(line_number, Node::sub, lexed); expression << Node(line_number, Node::sub, lexed);
} }
Node term(parse_term()); Node term(parse_term(delay));
term.eval_me = true; term.eval_me = true;
expression << term; expression << term;
} }
...@@ -386,9 +432,9 @@ namespace Sass { ...@@ -386,9 +432,9 @@ namespace Sass {
return expression; return expression;
} }
Node Document::parse_term() Node Document::parse_term(bool delay)
{ {
Node fact1(parse_factor()); Node fact1(parse_factor(delay));
// if it's a singleton, return it directly; don't wrap it // if it's a singleton, return it directly; don't wrap it
if (!(peek< exactly<'*'> >(position) || if (!(peek< exactly<'*'> >(position) ||
peek< exactly<'/'> >(position))) peek< exactly<'/'> >(position)))
...@@ -406,7 +452,7 @@ namespace Sass { ...@@ -406,7 +452,7 @@ namespace Sass {
else { else {
term << Node(line_number, Node::div, lexed); term << Node(line_number, Node::div, lexed);
} }
Node fact(parse_factor()); Node fact(parse_factor(delay));
if (fact.from_variable || fact.eval_me) term.eval_me = true; if (fact.from_variable || fact.eval_me) term.eval_me = true;
term << fact; term << fact;
} }
...@@ -419,11 +465,11 @@ namespace Sass { ...@@ -419,11 +465,11 @@ namespace Sass {
return term; return term;
} }
Node Document::parse_factor() Node Document::parse_factor(bool delay)
{ {
if (lex< exactly<'('> >()) { if (lex< exactly<'('> >()) {
// Node value(parse_list()); // Node value(parse_list());
Node value(parse_comma_list()); Node value(parse_comma_list(delay));
value.eval_me = true; value.eval_me = true;
if (value.type == Node::comma_list || value.type == Node::space_list) { if (value.type == Node::comma_list || value.type == Node::space_list) {
value.children->front().eval_me = true; value.children->front().eval_me = true;
...@@ -432,11 +478,11 @@ namespace Sass { ...@@ -432,11 +478,11 @@ namespace Sass {
return value; return value;
} }
else { else {
return parse_value(); return parse_value(delay);
} }
} }
Node Document::parse_value() Node Document::parse_value(bool delay)
{ {
if (lex< uri_prefix >()) if (lex< uri_prefix >())
{ {
...@@ -479,8 +525,14 @@ namespace Sass { ...@@ -479,8 +525,14 @@ namespace Sass {
if (lex< string_constant >()) if (lex< string_constant >())
{ return Node(line_number, Node::string_constant, lexed); } { return Node(line_number, Node::string_constant, lexed); }
if (lex< variable >()) if (lex< variable >()) {
{ return context.environment[lexed]; } if (delay) {
return Node(line_number, Node::var_ref, lexed);
}
else {
return context.environment[lexed];
}
}
} }
// const char* Document::look_for_rule(const char* start) // const char* Document::look_for_rule(const char* start)
......
...@@ -5,6 +5,9 @@ $z: global-z; ...@@ -5,6 +5,9 @@ $z: global-z;
@mixin foo($x, $y) { @mixin foo($x, $y) {
/* begin foo */ /* begin foo */
margin: $x $y; margin: $x $y;
blip {
hey: now;
}
/* end foo */ /* end foo */
} }
...@@ -55,4 +58,9 @@ div { ...@@ -55,4 +58,9 @@ div {
div { div {
/* calls to nullary mixins may omit the empty argument list */ /* calls to nullary mixins may omit the empty argument list */
@include bung; @include bung;
} }
\ No newline at end of file
div {
@include foo(arg1, arg2, $x: kwdarg1, $y: kwdarg2);
@include foo($x: kwdarg1, $y: kwdarg2, arg1, arg2);
}
...@@ -60,7 +60,13 @@ namespace Sass { ...@@ -60,7 +60,13 @@ namespace Sass {
number, number,
hex_triple, hex_triple,
comment comment,
mixin,
parameters,
variable,
var_ref,
include
}; };
static size_t fresh; static size_t fresh;
...@@ -214,6 +220,12 @@ namespace Sass { ...@@ -214,6 +220,12 @@ namespace Sass {
//~Node() { delete children; } //~Node() { delete children; }
size_t size()
{ return children->size(); }
Node& operator[](const size_t i)
{ return children->at(i); }
Node& operator=(const Node& n) Node& operator=(const Node& n)
{ {
line_number = n.line_number; line_number = n.line_number;
......
...@@ -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,10 @@ namespace Sass { ...@@ -87,6 +87,10 @@ 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);
}
const char* name(const char* src) { const char* name(const char* src) {
return one_plus< alternatives< alnum, return one_plus< alternatives< alnum,
......
...@@ -275,6 +275,7 @@ namespace Sass { ...@@ -275,6 +275,7 @@ 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);
// 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);
......
$blah: bloo blee; $blah: bloo blee;
$blip: "a 'red' and \"blue\" value"; $blip: "a 'red' and \"blue\" value";
@mixin foo($arg) {
whatever: $arg;
}
/* top level comment -- should be preserved */ /* top level comment -- should be preserved */
div { div {
/* another comment that should be preserved */ /* another comment that should be preserved */
......
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