Text-Sass-XS
view release on metacpan or search on metacpan
libsass/old/document_parser.cpp view on Meta::CPAN
#include <cstdlib>
#include <iostream>
#include "document.hpp"
#include "constants.hpp"
#include "error.hpp"
#ifndef SASS_PRELEXER
#include "prelexer.hpp"
#endif
namespace Sass {
using namespace std;
using namespace Constants;
void Document::parse_scss()
{
read_bom();
lex< optional_spaces >();
Selector_Lookahead lookahead_result;
while (position < end) {
if (lex< block_comment >()) {
root << context.new_Node(Node::comment, path, line, lexed);
}
else if (peek< import >()) {
Node importee(parse_import());
if (importee.type() == Node::css_import) root << importee;
else root += importee;
if (!lex< exactly<';'> >()) throw_syntax_error("top-level @import directive must be terminated by ';'");
}
else if (peek< mixin >() /* || peek< exactly<'='> >() */) {
root << parse_mixin_definition();
}
else if (peek< function >()) {
root << parse_function_definition();
}
else if (peek< variable >()) {
root << parse_assignment();
if (!lex< exactly<';'> >()) throw_syntax_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 ((lookahead_result = lookahead_for_selector(position)).found) {
root << parse_ruleset(lookahead_result);
}
else if (peek< include >() /* || peek< exactly<'+'> >() */) {
Node mixin_call(parse_mixin_call());
root << mixin_call;
if (mixin_call.size() < 3 && !lex< exactly<';'> >()) throw_syntax_error("top-level @include directive must be terminated by ';'");
}
else if (peek< if_directive >()) {
root << parse_if_directive(Node(), Node::none);
}
else if (peek< for_directive >()) {
root << parse_for_directive(Node(), Node::none);
}
else if (peek< each_directive >()) {
root << parse_each_directive(Node(), Node::none);
}
else if (peek< while_directive >()) {
root << parse_while_directive(Node(), Node::none);
}
else if (peek< media >()) {
root << parse_media_query(Node::none);
}
else if (peek< keyframes >()) {
root << parse_keyframes(Node::none);
}
else if (peek< warn >()) {
root << parse_warning();
if (!lex< exactly<';'> >()) throw_syntax_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< directive >()) {
Node dir(parse_directive(Node(), Node::none));
if (dir.type() == Node::blockless_directive) {
if (!lex< exactly<';'> >()) throw_syntax_error("top-level blockless directive must be terminated by ';'");
}
root << dir;
}
else {
lex< spaces_and_comments >();
if (position >= end) break;
throw_syntax_error("invalid top-level expression");
}
lex< optional_spaces >();
}
}
Node Document::parse_import()
{
lex< import >();
if (lex< uri_prefix >())
{
if (peek< string_constant >()) {
Node schema(parse_string());
Node importee(context.new_Node(Node::css_import, path, line, 1));
libsass/old/document_parser.cpp view on Meta::CPAN
return args;
}
Node Document::parse_argument(Node::Type arg_type)
{
// if arg_type is assignment, only accept keyword args from here onwards
if (arg_type == Node::assignment) {
if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
lex< variable >();
Node var(context.new_Node(Node::variable, path, line, lexed));
lex< exactly<':'> >();
Node val(parse_space_list());
val.should_eval() = true;
Node assn(context.new_Node(Node::assignment, path, line, 2));
assn << var << val;
return assn;
}
else {
throw_syntax_error("ordinal arguments must precede keyword arguments");
}
}
// otherwise accept either, and let the caller set the arg_type flag
else if (arg_type == Node::none &&
peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
lex< variable >();
Node var(context.new_Node(Node::variable, path, line, lexed));
lex< exactly<':'> >();
Node val(parse_space_list());
val.should_eval() = true;
Node assn(context.new_Node(Node::assignment, path, line, 2));
assn << var << val;
return assn;
}
// else if (arg_type == Node::none &&
// peek< sequence < variable, spaces_and_comments, exactly< ellipsis > > >()) {
// lex< variable >();
// lex< exactly< ellipsis > >();
// return context.new_Node(Node::rest, path, line, lexed);
// }
Node val(parse_space_list());
val.should_eval() = true;
if (lex< exactly< ellipsis > >()) {
val.is_arglist() = true;
val.is_splat() = true;
}
return val;
}
Node Document::parse_assignment()
{
lex< variable >();
Node var(context.new_Node(Node::variable, path, line, lexed));
if (!lex< exactly<':'> >()) throw_syntax_error("expected ':' after " + lexed.to_string() + " in assignment statement");
Node val(parse_list());
Node assn(context.new_Node(Node::assignment, path, line, 2));
assn << var << val;
if (lex< default_flag >()) assn << context.new_Node(Node::none, path, line, 0);
return assn;
}
Node Document::parse_propset()
{
Node property_segment;
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
property_segment = parse_identifier_schema();
}
else {
lex< sequence< optional< exactly<'*'> >, identifier > >();
property_segment = context.new_Node(Node::identifier, path, line, lexed);
}
lex< exactly<':'> >();
Node block(parse_block(Node()));
// lex< exactly<'{'> >();
// Node block(context.new_Node(Node::block, path, line, 1));
// while (!lex< exactly<'}'> >()) {
// if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
// block << parse_propset();
// }
// else {
// block << parse_rule();
// lex< exactly<';'> >();
// }
// }
if (block.empty()) throw_syntax_error("namespaced property cannot be empty");
Node propset(context.new_Node(Node::propset, path, line, 2));
propset << property_segment;
propset << block;
return propset;
}
Node Document::parse_ruleset(Selector_Lookahead lookahead, Node::Type inside_of)
{
Node ruleset(context.new_Node(Node::ruleset, path, line, 3));
if (lookahead.has_interpolants) {
ruleset << parse_selector_schema(lookahead.found);
}
else {
ruleset << parse_selector_group();
}
if (!peek< exactly<'{'> >()) throw_syntax_error("expected a '{' after the selector");
ruleset << parse_block(ruleset, inside_of);
return ruleset;
}
Node Document::parse_selector_schema(const char* end_of_selector)
{
const char* i = position;
const char* p;
Node schema(context.new_Node(Node::selector_schema, path, line, 1));
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 << context.new_Node(Node::identifier, path, line, Token::make(i, p));
// find the end of the interpolant and parse it
const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
Node interp_node(Document::make_from_token(context, Token::make(p+2, j), path, line).parse_list());
interp_node.should_eval() = 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 << context.new_Node(Node::identifier, path, line, Token::make(i, end_of_selector));
break;
}
}
position = end_of_selector;
return schema;
}
Node Document::parse_selector_group()
{
Node sel1(parse_selector());
if (!peek< exactly<','> >()) return sel1;
Node group(context.new_Node(Node::selector_group, path, line, 2));
group << sel1;
while (lex< exactly<','> >()) group << parse_selector();
return group;
}
Node Document::parse_selector()
{
Node seq1(parse_simple_selector_sequence());
if (peek< exactly<','> >() ||
peek< exactly<')'> >() ||
peek< exactly<'{'> >() ||
libsass/old/document_parser.cpp view on Meta::CPAN
else if (peek< import >(position)) {
if (inside_of == Node::mixin || inside_of == Node::function) {
lex< import >(); // to adjust the line number
throw_syntax_error("@import directive not allowed inside definition of mixin or function");
}
Node imported_tree(parse_import());
if (imported_tree.type() == Node::css_import) {
block << imported_tree;
}
else {
for (size_t i = 0, S = imported_tree.size(); i < S; ++i) {
block << imported_tree[i];
}
semicolon = true;
}
}
else if (lex< variable >()) {
block << parse_assignment();
semicolon = true;
}
else if (peek< if_directive >()) {
block << parse_if_directive(surrounding_ruleset, inside_of);
}
else if (peek< for_directive >()) {
block << parse_for_directive(surrounding_ruleset, inside_of);
}
else if (peek< each_directive >()) {
block << parse_each_directive(surrounding_ruleset, inside_of);
}
else if (peek < while_directive >()) {
block << parse_while_directive(surrounding_ruleset, inside_of);
}
else if (lex < return_directive >()) {
Node ret_expr(context.new_Node(Node::return_directive, path, line, 1));
ret_expr << parse_list();
ret_expr.should_eval() = true;
block << ret_expr;
semicolon = true;
}
else if (peek< warn >()) {
block << parse_warning();
semicolon = true;
}
else if (inside_of == Node::function) {
throw_syntax_error("only variable declarations and control directives are allowed inside functions");
}
else if (peek< include >(position)) {
Node the_call = parse_mixin_call(inside_of);
block << the_call;
// don't need a semicolon after a content block
semicolon = (the_call.size() == 3) ? false : true;
}
else if (lex< content >()) {
if (inside_of != Node::mixin) {
throw_syntax_error("@content may only be used within a mixin");
}
block << context.new_Node(Node::mixin_content, path, line, 0); // just an expansion stub
semicolon = true;
}
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
block << parse_propset();
}
else if (peek < keyframes >()) {
block << parse_keyframes(inside_of);
}
else if (peek< sequence< keyf, optional_spaces, exactly<'{'> > >()) {
block << parse_keyframe(inside_of);
}
else if ((lookahead_result = lookahead_for_selector(position)).found) {
block << parse_ruleset(lookahead_result, inside_of);
}
/*
else if (peek< exactly<'+'> >()) {
block << parse_mixin_call();
semicolon = true;
}
*/
else if (lex< extend >()) {
Node request(context.new_Node(Node::extend_directive, path, line, 1));
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
if (!lookahead.found) throw_syntax_error("invalid selector for @extend");
if (lookahead.has_interpolants) request << parse_selector_schema(lookahead.found);
else request << parse_selector_group();
semicolon = true;
block << request;
}
else if (peek< media >()) {
block << parse_media_query(inside_of);
}
// ignore the @charset directive for now
else if (lex< exactly< charset_kwd > >()) {
lex< string_constant >();
lex< exactly<';'> >();
}
else if (peek< directive >()) {
Node dir(parse_directive(surrounding_ruleset, inside_of));
if (dir.type() == Node::blockless_directive) semicolon = true;
block << dir;
}
else if (peek< percentage >() ){
lex< percentage >();
block << context.new_Node(path, line, atof(lexed.begin), Node::numeric_percentage);
if (peek< exactly<'{'> >()) {
Node inner(parse_block(Node()));
block << inner;
}
}
else if (!peek< exactly<';'> >()) {
Node rule(parse_rule());
// check for lbrace; if it's there, we have a namespace property with a value
if (peek< exactly<'{'> >()) {
Node inner(parse_block(Node()));
Node propset(context.new_Node(Node::propset, path, line, 2));
propset << rule[0];
rule[0] = context.new_Node(Node::property, path, line, Token::make());
inner.push_front(rule);
propset << inner;
block << propset;
}
else {
block << rule;
semicolon = true;
}
}
else lex< exactly<';'> >();
while (lex< block_comment >()) {
block << context.new_Node(Node::comment, path, line, lexed);
}
}
return block;
}
Node Document::parse_rule() {
Node rule(context.new_Node(Node::rule, path, line, 2));
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
rule << parse_identifier_schema();
}
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
rule << context.new_Node(Node::property, path, line, lexed);
}
else {
throw_syntax_error("invalid property name");
}
if (!lex< exactly<':'> >()) throw_syntax_error("property \"" + lexed.to_string() + "\" must be followed by a ':'");
rule << parse_list();
return rule;
}
Node Document::parse_list()
{
return parse_comma_list();
}
Node Document::parse_comma_list()
{
if (peek< exactly<';'> >(position) ||
peek< exactly<'}'> >(position) ||
peek< exactly<'{'> >(position) ||
peek< exactly<')'> >(position) ||
peek< exactly<ellipsis> >(position))
{ return context.new_Node(Node::list, path, line, 0); }
Node list1(parse_space_list());
// if it's a singleton, return it directly; don't wrap it
if (!peek< exactly<','> >(position)) return list1;
Node comma_list(context.new_Node(Node::list, path, line, 2));
comma_list.is_comma_separated() = true;
comma_list << list1;
comma_list.should_eval() |= list1.should_eval();
while (lex< exactly<','> >())
{
Node list(parse_space_list());
comma_list << list;
comma_list.should_eval() |= list.should_eval();
}
return comma_list;
( run in 1.228 second using v1.01-cache-2.11-cpan-71847e10f99 )