Text-Sass-XS
view release on metacpan or search on metacpan
libsass/src/parser.cpp view on Meta::CPAN
#include "to_string.hpp"
#include "constants.hpp"
#ifndef SASS_PRELEXER
#include "prelexer.hpp"
#endif
namespace Sass {
using namespace std;
using namespace Constants;
Parser Parser::from_c_str(const char* str, Context& ctx, string path, size_t line)
{
Parser p(ctx, path, line);
p.source = str;
p.position = p.source;
p.end = str + strlen(str);
return p;
}
Parser Parser::from_token(Token t, Context& ctx, string path, size_t line)
{
Parser p(ctx, path, line);
p.source = t.begin;
p.position = p.source;
p.end = t.end;
return p;
}
Block* Parser::parse()
{
Block* root = new (ctx.mem) Block(path, line);
root->is_root(true);
read_bom();
lex< optional_spaces >();
Selector_Lookahead lookahead_result;
while (position < end) {
if (lex< block_comment >()) {
String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed);
Comment* comment = new (ctx.mem) Comment(path, line, contents);
(*root) << comment;
}
else if (peek< import >()) {
Import* imp = parse_import();
(*root) << imp;
if (!imp->files().empty()) {
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
(*root) << new (ctx.mem) Import_Stub(path, line, imp->files()[i]);
}
}
if (!lex< exactly<';'> >()) error("top-level @import directive must be terminated by ';'");
}
else if (peek< mixin >() || peek< function >()) {
(*root) << parse_definition();
}
else if (peek< variable >()) {
(*root) << parse_assignment();
if (!lex< exactly<';'> >()) error("top-level variable binding must be terminated by ';'");
}
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
(*root) << parse_propset();
}
else if (peek< include >() /* || peek< exactly<'+'> >() */) {
Mixin_Call* mixin_call = parse_mixin_call();
(*root) << mixin_call;
if (!mixin_call->block() && !lex< exactly<';'> >()) error("top-level @include directive must be terminated by ';'");
}
else if (peek< if_directive >()) {
(*root) << parse_if_directive();
}
else if (peek< for_directive >()) {
(*root) << parse_for_directive();
}
else if (peek< each_directive >()) {
(*root) << parse_each_directive();
}
else if (peek< while_directive >()) {
(*root) << parse_while_directive();
}
else if (peek< media >()) {
(*root) << parse_media_block();
}
else if (peek< warn >()) {
(*root) << parse_warning();
if (!lex< exactly<';'> >()) error("top-level @warn directive must be terminated by ';'");
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< string_constant >();
lex< exactly<';'> >();
}
else if (peek< at_keyword >()) {
At_Rule* at_rule = parse_at_rule();
(*root) << at_rule;
if (!at_rule->block() && !lex< exactly<';'> >()) error("top-level directive must be terminated by ';'");
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
(*root) << parse_ruleset(lookahead_result);
}
else {
lex< spaces_and_comments >();
if (position >= end) break;
error("invalid top-level expression");
}
lex< optional_spaces >();
}
return root;
}
Import* Parser::parse_import()
{
lex< import >();
Import* imp = new (ctx.mem) Import(path, line);
bool first = true;
do {
if (lex< string_constant >()) {
string import_path(lexed);
size_t dot = import_path.find_last_of('.');
if (dot != string::npos && import_path.substr(dot, 4) == ".css") {
String_Constant* loc = new (ctx.mem) String_Constant(path, line, import_path);
Argument* loc_arg = new (ctx.mem) Argument(path, line, loc);
libsass/src/parser.cpp view on Meta::CPAN
if (peek< exactly<'{'> >()) {
content = parse_block();
}
Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, line_of_call, name, args, content);
return the_call;
}
Arguments* Parser::parse_arguments()
{
string name(lexed);
Arguments* args = new (ctx.mem) Arguments(path, line);
if (lex< exactly<'('> >()) {
// if there's anything there at all
if (!peek< exactly<')'> >()) {
do (*args) << parse_argument();
while (lex< exactly<','> >());
}
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
}
return args;
}
Argument* Parser::parse_argument()
{
Argument* arg;
if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
lex< variable >();
string name(lexed);
lex< exactly<':'> >();
Expression* val = parse_space_list();
val->is_delayed(false);
arg = new (ctx.mem) Argument(path, line, val, name);
}
else {
bool is_arglist = false;
Expression* val = parse_space_list();
val->is_delayed(false);
if (lex< exactly< ellipsis > >()) {
is_arglist = true;
}
arg = new (ctx.mem) Argument(path, line, val, "", is_arglist);
}
return arg;
}
Assignment* Parser::parse_assignment()
{
lex< variable >();
string name(lexed);
size_t var_line = line;
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
Expression* val = parse_list();
val->is_delayed(false);
bool is_guarded = lex< default_flag >();
Assignment* var = new (ctx.mem) Assignment(path, var_line, name, val, is_guarded);
return var;
}
Propset* Parser::parse_propset()
{
String* property_segment;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
property_segment = parse_identifier_schema();
}
else {
lex< sequence< optional< exactly<'*'> >, identifier > >();
property_segment = new (ctx.mem) String_Constant(path, line, lexed);
}
Propset* propset = new (ctx.mem) Propset(path, line, property_segment);
lex< exactly<':'> >();
if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property");
propset->block(parse_block());
return propset;
}
Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)
{
Selector* sel;
if (lookahead.has_interpolants) {
sel = parse_selector_schema(lookahead.found);
}
else {
sel = parse_selector_group();
}
if (!peek< exactly<'{'> >()) error("expected a '{' after the selector");
Block* block = parse_block();
Ruleset* ruleset = new (ctx.mem) Ruleset(path, line, sel, block);
return ruleset;
}
Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
{
const char* i = position;
const char* p;
String_Schema* schema = new (ctx.mem) String_Schema(path, line);
while (i < end_of_selector) {
p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
if (p) {
// accumulate the preceding segment if there is one
if (i < p) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, p));
// find the end of the interpolant and parse it
const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, line).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
i = j + 1;
}
else { // no interpolants left; add the last segment if there is one
if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, end_of_selector));
break;
}
}
position = end_of_selector;
return new (ctx.mem) Selector_Schema(path, line, schema);
}
Selector_Group* Parser::parse_selector_group()
{
To_String to_string;
Selector_Group* group = new (ctx.mem) Selector_Group(path, line);
do {
Selector_Combination* comb = parse_selector_combination();
if (!comb->has_reference()) {
size_t sel_line = line;
Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_line);
Simple_Selector_Sequence* ref_wrap = new (ctx.mem) Simple_Selector_Sequence(path, sel_line);
(*ref_wrap) << ref;
if (!comb->head()) {
comb->head(ref_wrap);
comb->has_reference(true);
}
else {
libsass/src/parser.cpp view on Meta::CPAN
}
else if (lex < return_directive >()) {
(*block) << new (ctx.mem) Return(path, line, parse_list());
semicolon = true;
}
else if (peek< warn >()) {
(*block) << parse_warning();
semicolon = true;
}
else if (stack.back() == function_def) {
error("only variable declarations and control directives are allowed inside functions");
}
else if (peek< mixin >() || peek< function >()) {
(*block) << parse_definition();
}
else if (peek< include >(position)) {
Mixin_Call* the_call = parse_mixin_call();
(*block) << the_call;
// don't need a semicolon after a content block
semicolon = (the_call->block()) ? false : true;
}
else if (lex< content >()) {
if (stack.back() != mixin_def) {
error("@content may only be used within a mixin");
}
(*block) << new (ctx.mem) Content(path, line);
semicolon = true;
}
/*
else if (peek< exactly<'+'> >()) {
(*block) << parse_mixin_call();
semicolon = true;
}
*/
else if (lex< extend >()) {
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
if (!lookahead.found) error("invalid selector for @extend");
Selector* target;
if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
else target = parse_selector_group();
(*block) << new (ctx.mem) Extension(path, line, target);
semicolon = true;
}
else if (peek< media >()) {
(*block) << parse_media_block();
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< string_constant >();
lex< exactly<';'> >();
}
else if (peek< at_keyword >()) {
At_Rule* at_rule = parse_at_rule();
(*block) << at_rule;
if (!at_rule->block()) semicolon = true;
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
(*block) << parse_ruleset(lookahead_result);
}
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
(*block) << parse_propset();
}
else if (!peek< exactly<';'> >()) {
if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {
(*block) << parse_propset();
}
else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {
(*block) << parse_propset();
}
else {
(*block) << parse_declaration();
semicolon = true;
}
}
else lex< exactly<';'> >();
while (lex< block_comment >()) {
String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed);
Comment* comment = new (ctx.mem) Comment(path, line, contents);
(*block) << comment;
}
}
return block;
}
Declaration* Parser::parse_declaration() {
String* prop = 0;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
prop = parse_identifier_schema();
}
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
prop = new (ctx.mem) String_Constant(path, line, lexed);
}
else {
error("invalid property name");
}
if (!lex< exactly<':'> >()) error("property \"" + string(lexed) + "\" must be followed by a ':'");
if (peek< exactly<';'> >()) error("style declaration must contain a value");
Expression* list = parse_list();
return new (ctx.mem) Declaration(path, prop->line(), prop, list, lex<important>());
}
Expression* Parser::parse_list()
{
return parse_comma_list();
}
Expression* Parser::parse_comma_list()
{
if (peek< exactly<'!'> >(position) ||
peek< exactly<';'> >(position) ||
peek< exactly<'}'> >(position) ||
peek< exactly<'{'> >(position) ||
peek< exactly<')'> >(position) ||
peek< exactly<':'> >(position) ||
peek< exactly<ellipsis> >(position))
{ return new (ctx.mem) List(path, line, 0); }
Expression* list1 = parse_space_list();
// if it's a singleton, return it directly; don't wrap it
if (!peek< exactly<','> >(position)) return list1;
List* comma_list = new (ctx.mem) List(path, line, 2, List::COMMA);
(*comma_list) << list1;
while (lex< exactly<','> >())
{
Expression* list = parse_space_list();
(*comma_list) << list;
}
( run in 1.180 second using v1.01-cache-2.11-cpan-71847e10f99 )