123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016 |
- #include <cstdlib>
- #include <iostream>
- #include <vector>
- #include "parser.hpp"
- #include "file.hpp"
- #include "inspect.hpp"
- #include "to_string.hpp"
- #include "constants.hpp"
- #include "util.hpp"
- #ifndef SASS_PRELEXER
- #include "prelexer.hpp"
- #endif
- #include "sass_functions.h"
- #include <typeinfo>
- namespace Sass {
- using namespace std;
- using namespace Constants;
- Parser Parser::from_c_str(const char* str, Context& ctx, string path, Position source_position)
- {
- Parser p(ctx, path, source_position);
- p.source = str;
- p.position = p.source;
- p.end = str + strlen(str);
- return p;
- }
- Parser Parser::from_token(Token t, Context& ctx, string path, Position source_position)
- {
- Parser p(ctx, path, source_position);
- p.source = t.begin;
- p.position = p.source;
- p.end = t.end;
- p.dequote = true;
- return p;
- }
- Block* Parser::parse()
- {
- Block* root = new (ctx.mem) Block(path, source_position);
- root->is_root(true);
- read_bom();
- lex< optional_spaces >();
- Selector_Lookahead lookahead_result;
- while (position < end) {
- if (lex< block_comment >()) {
- String* contents = parse_interpolated_chunk(lexed);
- Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
- (*root) << comment;
- }
- else if (peek< import >()) {
- Import* imp = parse_import();
- if (!imp->urls().empty()) (*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, source_position, imp->files()[i]);
- }
- }
- if (!lex< one_plus< 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< one_plus< 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< one_plus< 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< supports >()) {
- (*root) << parse_feature_block();
- }
- else if (peek< warn >()) {
- (*root) << parse_warning();
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'");
- }
- else if (peek< err >()) {
- (*root) << parse_error();
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'");
- }
- else if (peek< dbg >()) {
- (*root) << parse_debug();
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'");
- }
- // ignore the @charset directive for now
- else if (lex< exactly< charset_kwd > >()) {
- lex< string_constant >();
- lex< one_plus< exactly<';'> > >();
- }
- else if (peek< at_keyword >()) {
- At_Rule* at_rule = parse_at_rule();
- (*root) << at_rule;
- if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'");
- }
- else if ((lookahead_result = lookahead_for_selector(position)).found) {
- (*root) << parse_ruleset(lookahead_result);
- }
- else if (peek< exactly<';'> >()) {
- lex< one_plus< exactly<';'> > >();
- }
- else {
- lex< spaces_and_comments >();
- if (position >= end) break;
- error("invalid top-level expression");
- }
- lex< optional_spaces >();
- }
- return root;
- }
- void Parser::add_single_file (Import* imp, string import_path) {
- string extension;
- string unquoted(unquote(import_path));
- if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css
- // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end
- extension = unquoted.substr(unquoted.length() - 4, 4);
- }
- if (extension == ".css") {
- String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path, true);
- Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc);
- Arguments* loc_args = new (ctx.mem) Arguments(path, source_position);
- (*loc_args) << loc_arg;
- Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args);
- imp->urls().push_back(new_url);
- }
- else {
- string current_dir = File::dir_name(path);
- string resolved(ctx.add_file(current_dir, unquoted));
- if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir);
- imp->files().push_back(resolved);
- }
- }
- Import* Parser::parse_import()
- {
- lex< import >();
- Import* imp = new (ctx.mem) Import(path, source_position);
- bool first = true;
- do {
- if (lex< string_constant >()) {
- string import_path(lexed);
- // struct Sass_Options opt = sass_context_get_options(ctx)
- Sass_C_Import_Callback importer = ctx.importer;
- // custom importer
- if (importer) {
- Sass_Import* current = ctx.import_stack.back();
- Sass_C_Import_Fn fn = sass_import_get_function(importer);
- void* cookie = sass_import_get_cookie(importer);
- // create a new import entry
- string inc_path = unquote(import_path);
- struct Sass_Import** includes = fn(
- inc_path.c_str(),
- sass_import_get_path(current),
- cookie);
- if (includes) {
- struct Sass_Import** list = includes;
- while (*includes) {
- struct Sass_Import* include = *includes;
- const char *file = sass_import_get_path(include);
- char *source = sass_import_take_source(include);
- // char *srcmap = sass_import_take_srcmap(include);
- if (source) {
- if (file) {
- ctx.add_source(file, inc_path, source);
- imp->files().push_back(file);
- } else {
- ctx.add_source(inc_path, inc_path, source);
- imp->files().push_back(inc_path);
- }
- } else if(file) {
- add_single_file(imp, file);
- }
- ++includes;
- }
- // deallocate returned memory
- sass_delete_import_list(list);
- // parse next import
- continue;
- }
- }
- add_single_file(imp, import_path);
- }
- else if (peek< uri_prefix >()) {
- imp->urls().push_back(parse_value());
- }
- else {
- if (first) error("@import directive requires a url or quoted path");
- else error("expecting another url or quoted path in @import list");
- }
- first = false;
- } while (lex< exactly<','> >());
- return imp;
- }
- Definition* Parser::parse_definition()
- {
- Definition::Type which_type = Definition::MIXIN;
- if (lex< mixin >()) which_type = Definition::MIXIN;
- else if (lex< function >()) which_type = Definition::FUNCTION;
- string which_str(lexed);
- if (!lex< identifier >()) error("invalid name in " + which_str + " definition");
- string name(Util::normalize_underscores(lexed));
- if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
- { error("Invalid function name \"" + name + "\"."); }
- Position source_position_of_def = source_position;
- Parameters* params = parse_parameters();
- if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'");
- if (which_type == Definition::MIXIN) stack.push_back(mixin_def);
- else stack.push_back(function_def);
- Block* body = parse_block();
- stack.pop_back();
- Definition* def = new (ctx.mem) Definition(path, source_position_of_def, name, params, body, which_type);
- return def;
- }
- Parameters* Parser::parse_parameters()
- {
- string name(lexed); // for the error message
- Parameters* params = new (ctx.mem) Parameters(path, source_position);
- if (lex< exactly<'('> >()) {
- // if there's anything there at all
- if (!peek< exactly<')'> >()) {
- do (*params) << parse_parameter();
- while (lex< exactly<','> >());
- }
- if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
- }
- return params;
- }
- Parameter* Parser::parse_parameter()
- {
- lex< variable >();
- string name(Util::normalize_underscores(lexed));
- Position pos = source_position;
- Expression* val = 0;
- bool is_rest = false;
- if (lex< exactly<':'> >()) { // there's a default value
- val = parse_space_list();
- val->is_delayed(false);
- }
- else if (lex< exactly< ellipsis > >()) {
- is_rest = true;
- }
- Parameter* p = new (ctx.mem) Parameter(path, pos, name, val, is_rest);
- return p;
- }
- Mixin_Call* Parser::parse_mixin_call()
- {
- lex< include >() /* || lex< exactly<'+'> >() */;
- if (!lex< identifier >()) error("invalid name in @include directive");
- Position source_position_of_call = source_position;
- string name(Util::normalize_underscores(lexed));
- Arguments* args = parse_arguments();
- Block* content = 0;
- if (peek< exactly<'{'> >()) {
- content = parse_block();
- }
- Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, source_position_of_call, name, args, content);
- return the_call;
- }
- Arguments* Parser::parse_arguments()
- {
- string name(lexed);
- Arguments* args = new (ctx.mem) Arguments(path, source_position);
- 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(Util::normalize_underscores(lexed));
- Position p = source_position;
- lex< exactly<':'> >();
- Expression* val = parse_space_list();
- val->is_delayed(false);
- arg = new (ctx.mem) Argument(path, p, val, name);
- }
- else {
- bool is_arglist = false;
- bool is_keyword = false;
- Expression* val = parse_space_list();
- val->is_delayed(false);
- if (lex< exactly< ellipsis > >()) {
- if (val->concrete_type() == Expression::MAP) is_keyword = true;
- else is_arglist = true;
- }
- arg = new (ctx.mem) Argument(path, source_position, val, "", is_arglist, is_keyword);
- }
- return arg;
- }
- Assignment* Parser::parse_assignment()
- {
- lex< variable >();
- string name(Util::normalize_underscores(lexed));
- Position var_source_position = source_position;
- if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
- Expression* val = parse_list();
- val->is_delayed(false);
- bool is_guarded = false;
- bool is_global = false;
- while (peek< default_flag >() || peek< global_flag >()) {
- is_guarded = lex< default_flag >() || is_guarded;
- is_global = lex< global_flag >() || is_global;
- }
- Assignment* var = new (ctx.mem) Assignment(path, var_source_position, name, val, is_guarded, is_global);
- 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, source_position, lexed);
- }
- Propset* propset = new (ctx.mem) Propset(path, source_position, 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();
- }
- Position r_source_position = source_position;
- if (!peek< exactly<'{'> >()) error("expected a '{' after the selector");
- Block* block = parse_block();
- Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_source_position, sel, block);
- return ruleset;
- }
- Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
- {
- lex< optional_spaces >();
- const char* i = position;
- const char* p;
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- 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, source_position, 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, source_position).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, source_position, Token(i, end_of_selector));
- break;
- }
- }
- position = end_of_selector;
- return new (ctx.mem) Selector_Schema(path, source_position, schema);
- }
- Selector_List* Parser::parse_selector_group()
- {
- To_String to_string;
- lex< spaces_and_comments >();
- Selector_List* group = new (ctx.mem) Selector_List(path, source_position);
- do {
- if (peek< exactly<'{'> >() ||
- peek< exactly<'}'> >() ||
- peek< exactly<')'> >() ||
- peek< exactly<';'> >())
- break; // in case there are superfluous commas at the end
- Complex_Selector* comb = parse_selector_combination();
- if (!comb->has_reference()) {
- Position sel_source_position = source_position;
- Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_source_position);
- Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_source_position);
- (*ref_wrap) << ref;
- if (!comb->head()) {
- comb->head(ref_wrap);
- comb->has_reference(true);
- }
- else {
- comb = new (ctx.mem) Complex_Selector(path, sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb);
- comb->has_reference(true);
- }
- }
- (*group) << comb;
- }
- while (lex< one_plus< sequence< spaces_and_comments, exactly<','> > > >());
- while (lex< optional >()); // JMA - ignore optional flag if it follows the selector group
- return group;
- }
- Complex_Selector* Parser::parse_selector_combination()
- {
- Position sel_source_position = Position();
- Compound_Selector* lhs;
- if (peek< exactly<'+'> >() ||
- peek< exactly<'~'> >() ||
- peek< exactly<'>'> >()) {
- // no selector before the combinator
- lhs = 0;
- }
- else {
- lhs = parse_simple_selector_sequence();
- sel_source_position = source_position;
- }
- Complex_Selector::Combinator cmb;
- if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;
- else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;
- else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;
- else cmb = Complex_Selector::ANCESTOR_OF;
- Complex_Selector* rhs;
- if (peek< exactly<','> >() ||
- peek< exactly<')'> >() ||
- peek< exactly<'{'> >() ||
- peek< exactly<'}'> >() ||
- peek< exactly<';'> >() ||
- peek< optional >()) {
- // no selector after the combinator
- rhs = 0;
- }
- else {
- rhs = parse_selector_combination();
- sel_source_position = source_position;
- }
- if (!sel_source_position.line) sel_source_position = source_position;
- return new (ctx.mem) Complex_Selector(path, sel_source_position, cmb, lhs, rhs);
- }
- Compound_Selector* Parser::parse_simple_selector_sequence()
- {
- Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, source_position);
- bool sawsomething = false;
- if (lex< exactly<'&'> >()) {
- // if you see a &
- (*seq) << new (ctx.mem) Selector_Reference(path, source_position);
- sawsomething = true;
- // if you see a space after a &, then you're done
- if(lex< spaces >()) {
- return seq;
- }
- }
- if (sawsomething && lex< sequence< negate< functional >, alternatives< identifier_fragment, universal, string_constant, dimension, percentage, number > > >()) {
- // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
- (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
- } else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, string_constant, dimension, percentage, number > > >()) {
- // if you see a type selector
- (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
- sawsomething = true;
- }
- if (!sawsomething) {
- // don't blindly do this if you saw a & or selector
- (*seq) << parse_simple_selector();
- }
- while (!peek< spaces >(position) &&
- !(peek < exactly<'+'> >(position) ||
- peek < exactly<'~'> >(position) ||
- peek < exactly<'>'> >(position) ||
- peek < exactly<','> >(position) ||
- peek < exactly<')'> >(position) ||
- peek < exactly<'{'> >(position) ||
- peek < exactly<'}'> >(position) ||
- peek < exactly<';'> >(position))) {
- (*seq) << parse_simple_selector();
- }
- return seq;
- }
- Simple_Selector* Parser::parse_simple_selector()
- {
- if (lex< id_name >() || lex< class_name >()) {
- return new (ctx.mem) Selector_Qualifier(path, source_position, lexed);
- }
- else if (lex< string_constant >() || lex< number >()) {
- return new (ctx.mem) Type_Selector(path, source_position, lexed);
- }
- else if (peek< pseudo_not >()) {
- return parse_negated_selector();
- }
- else if (peek< exactly<':'> >(position) || peek< functional >()) {
- return parse_pseudo_selector();
- }
- else if (peek< exactly<'['> >(position)) {
- return parse_attribute_selector();
- }
- else if (lex< placeholder >()) {
- return new (ctx.mem) Selector_Placeholder(path, source_position, lexed);
- }
- else {
- error("invalid selector after " + lexed.to_string());
- }
- // unreachable statement
- return 0;
- }
- Wrapped_Selector* Parser::parse_negated_selector()
- {
- lex< pseudo_not >();
- string name(lexed);
- Position nsource_position = source_position;
- Selector* negated = parse_selector_group();
- if (!lex< exactly<')'> >()) {
- error("negated selector is missing ')'");
- }
- return new (ctx.mem) Wrapped_Selector(path, nsource_position, name, negated);
- }
- Simple_Selector* Parser::parse_pseudo_selector() {
- if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) {
- string name(lexed);
- String* expr = 0;
- Position p = source_position;
- Selector* wrapped = 0;
- if (lex< alternatives< even, odd > >()) {
- expr = new (ctx.mem) String_Constant(path, p, lexed);
- }
- else if (peek< binomial >(position)) {
- lex< sequence< optional< coefficient >, exactly<'n'> > >();
- String_Constant* var_coef = new (ctx.mem) String_Constant(path, p, lexed);
- lex< sign >();
- String_Constant* op = new (ctx.mem) String_Constant(path, p, lexed);
- // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
- lex< digits >();
- String_Constant* constant = new (ctx.mem) String_Constant(path, p, lexed);
- // expr = new (ctx.mem) Binary_Expression(path, p, op, var_coef, constant);
- String_Schema* schema = new (ctx.mem) String_Schema(path, p, 3);
- *schema << var_coef << op << constant;
- expr = schema;
- }
- else if (peek< sequence< optional<sign>,
- optional<digits>,
- exactly<'n'>,
- spaces_and_comments,
- exactly<')'> > >()) {
- lex< sequence< optional<sign>,
- optional<digits>,
- exactly<'n'> > >();
- expr = new (ctx.mem) String_Constant(path, p, lexed);
- }
- else if (lex< sequence< optional<sign>, digits > >()) {
- expr = new (ctx.mem) String_Constant(path, p, lexed);
- }
- else if (peek< sequence< identifier, spaces_and_comments, exactly<')'> > >()) {
- lex< identifier >();
- expr = new (ctx.mem) String_Constant(path, p, lexed);
- }
- else if (lex< string_constant >()) {
- expr = new (ctx.mem) String_Constant(path, p, lexed);
- }
- else if (peek< exactly<')'> >()) {
- expr = new (ctx.mem) String_Constant(path, p, "");
- }
- else {
- wrapped = parse_selector_group();
- }
- if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)");
- if (wrapped) {
- return new (ctx.mem) Wrapped_Selector(path, p, name, wrapped);
- }
- return new (ctx.mem) Pseudo_Selector(path, p, name, expr);
- }
- else if (lex < sequence< pseudo_prefix, identifier > >()) {
- return new (ctx.mem) Pseudo_Selector(path, source_position, lexed);
- }
- else {
- error("unrecognized pseudo-class or pseudo-element");
- }
- // unreachable statement
- return 0;
- }
- Attribute_Selector* Parser::parse_attribute_selector()
- {
- lex< exactly<'['> >();
- Position p = source_position;
- if (!lex< attribute_name >()) error("invalid attribute name in attribute selector");
- string name(lexed);
- if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, p, name, "", 0);
- if (!lex< alternatives< exact_match, class_match, dash_match,
- prefix_match, suffix_match, substring_match > >()) {
- error("invalid operator in attribute selector for " + name);
- }
- string matcher(lexed);
- String* value = 0;
- if (lex< identifier >()) {
- value = new (ctx.mem) String_Constant(path, p, lexed, true);
- }
- else if (lex< string_constant >()) {
- value = parse_interpolated_chunk(lexed);
- }
- else {
- error("expected a string constant or identifier in attribute selector for " + name);
- }
- if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name);
- return new (ctx.mem) Attribute_Selector(path, p, name, matcher, value);
- }
- Block* Parser::parse_block()
- {
- lex< exactly<'{'> >();
- bool semicolon = false;
- Selector_Lookahead lookahead_result;
- Block* block = new (ctx.mem) Block(path, source_position);
- // JMA - ensure that a block containing only block_comments is parsed
- while (lex< block_comment >()) {
- String* contents = parse_interpolated_chunk(lexed);
- Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
- (*block) << comment;
- }
- while (!lex< exactly<'}'> >()) {
- if (semicolon) {
- if (!lex< one_plus< exactly<';'> > >()) {
- error("non-terminal statement or declaration must end with ';'");
- }
- semicolon = false;
- while (lex< block_comment >()) {
- String* contents = parse_interpolated_chunk(lexed);
- Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
- (*block) << comment;
- }
- if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;
- }
- if (lex< block_comment >()) {
- String* contents = parse_interpolated_chunk(lexed);
- Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
- (*block) << comment;
- }
- else if (peek< import >(position)) {
- if (stack.back() == mixin_def || stack.back() == function_def) {
- lex< import >(); // to adjust the source_position number
- error("@import directives are not allowed inside mixins and functions");
- }
- Import* imp = parse_import();
- if (!imp->urls().empty()) (*block) << imp;
- if (!imp->files().empty()) {
- for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
- (*block) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]);
- }
- }
- semicolon = true;
- }
- else if (lex< variable >()) {
- (*block) << parse_assignment();
- semicolon = true;
- }
- else if (peek< if_directive >()) {
- (*block) << parse_if_directive();
- }
- else if (peek< for_directive >()) {
- (*block) << parse_for_directive();
- }
- else if (peek< each_directive >()) {
- (*block) << parse_each_directive();
- }
- else if (peek < while_directive >()) {
- (*block) << parse_while_directive();
- }
- else if (lex < return_directive >()) {
- (*block) << new (ctx.mem) Return(path, source_position, parse_list());
- semicolon = true;
- }
- else if (peek< warn >()) {
- (*block) << parse_warning();
- semicolon = true;
- }
- else if (peek< err >()) {
- (*block) << parse_error();
- semicolon = true;
- }
- else if (peek< dbg >()) {
- (*block) << parse_debug();
- 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, source_position);
- 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, source_position, target);
- semicolon = true;
- }
- else if (peek< media >()) {
- (*block) << parse_media_block();
- }
- else if (peek< supports >()) {
- (*block) << parse_feature_block();
- }
- // ignore the @charset directive for now
- else if (lex< exactly< charset_kwd > >()) {
- lex< string_constant >();
- lex< one_plus< 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 {
- Declaration* decl = parse_declaration();
- (*block) << decl;
- if (peek< exactly<'{'> >()) {
- // parse a propset that rides on the declaration's property
- Propset* ps = new (ctx.mem) Propset(path, source_position, decl->property(), parse_block());
- (*block) << ps;
- }
- else {
- // finish and let the semicolon get munched
- semicolon = true;
- }
- }
- }
- else lex< one_plus< exactly<';'> > >();
- while (lex< block_comment >()) {
- String* contents = parse_interpolated_chunk(lexed);
- Comment* comment = new (ctx.mem) Comment(path, source_position, 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, source_position, lexed);
- }
- else if (lex< custom_property_name >()) {
- prop = new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- else {
- error("invalid property name");
- }
- if (!lex< one_plus< exactly<':'> > >()) error("property \"" + string(lexed) + "\" must be followed by a ':'");
- if (peek< exactly<';'> >()) error("style declaration must contain a value");
- if (peek< static_value >()) {
- return new (ctx.mem) Declaration(path, prop->position(), prop, parse_static_value()/*, lex<important>()*/);
- }
- else {
- return new (ctx.mem) Declaration(path, prop->position(), prop, parse_list()/*, lex<important>()*/);
- }
- }
- Expression* Parser::parse_map()
- {
- To_String to_string;
- Expression* key = parse_list();
- // it's not a map so return the lexed value as a list value
- if (!peek< exactly<':'> >())
- { return key; }
- lex< exactly<':'> >();
- Expression* value = parse_space_list();
- Map* map = new (ctx.mem) Map(path, source_position, 1);
- (*map) << make_pair(key, value);
- while (lex< exactly<','> >())
- {
- // allow trailing commas - #495
- if (peek< exactly<')'> >(position))
- { break; }
- Expression* key = parse_list();
- if (!(lex< exactly<':'> >()))
- { error("invalid syntax"); }
- Expression* value = parse_space_list();
- (*map) << make_pair(key, value);
- }
- if (map->has_duplicate_key())
- { error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + "."); }
- return map;
- }
- 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, source_position, 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, source_position, 2, List::COMMA);
- (*comma_list) << list1;
- while (lex< exactly<','> >())
- {
- if (//peek< exactly<'!'> >(position) ||
- peek< exactly<';'> >(position) ||
- peek< exactly<'}'> >(position) ||
- peek< exactly<'{'> >(position) ||
- peek< exactly<')'> >(position) ||
- peek< exactly<':'> >(position) ||
- peek< exactly<ellipsis> >(position)) {
- break;
- }
- Expression* list = parse_space_list();
- (*comma_list) << list;
- }
- return comma_list;
- }
- Expression* Parser::parse_space_list()
- {
- Expression* disj1 = parse_disjunction();
- // if it's a singleton, return it directly; don't wrap it
- if (//peek< exactly<'!'> >(position) ||
- peek< exactly<';'> >(position) ||
- peek< exactly<'}'> >(position) ||
- peek< exactly<'{'> >(position) ||
- peek< exactly<')'> >(position) ||
- peek< exactly<','> >(position) ||
- peek< exactly<':'> >(position) ||
- peek< exactly<ellipsis> >(position) ||
- peek< default_flag >(position) ||
- peek< global_flag >(position))
- { return disj1; }
- List* space_list = new (ctx.mem) List(path, source_position, 2, List::SPACE);
- (*space_list) << disj1;
- while (!(//peek< exactly<'!'> >(position) ||
- peek< exactly<';'> >(position) ||
- peek< exactly<'}'> >(position) ||
- peek< exactly<'{'> >(position) ||
- peek< exactly<')'> >(position) ||
- peek< exactly<','> >(position) ||
- peek< exactly<':'> >(position) ||
- peek< exactly<ellipsis> >(position) ||
- peek< default_flag >(position) ||
- peek< global_flag >(position)))
- {
- (*space_list) << parse_disjunction();
- }
- return space_list;
- }
- Expression* Parser::parse_disjunction()
- {
- Expression* conj1 = parse_conjunction();
- // if it's a singleton, return it directly; don't wrap it
- if (!peek< sequence< or_op, negate< identifier > > >()) return conj1;
- vector<Expression*> operands;
- while (lex< sequence< or_op, negate< identifier > > >())
- operands.push_back(parse_conjunction());
- return fold_operands(conj1, operands, Binary_Expression::OR);
- }
- Expression* Parser::parse_conjunction()
- {
- Expression* rel1 = parse_relation();
- // if it's a singleton, return it directly; don't wrap it
- if (!peek< sequence< and_op, negate< identifier > > >()) return rel1;
- vector<Expression*> operands;
- while (lex< sequence< and_op, negate< identifier > > >())
- operands.push_back(parse_relation());
- return fold_operands(rel1, operands, Binary_Expression::AND);
- }
- Expression* Parser::parse_relation()
- {
- Expression* expr1 = parse_expression();
- // if it's a singleton, return it directly; don't wrap it
- if (!(peek< eq_op >(position) ||
- peek< neq_op >(position) ||
- peek< gte_op >(position) ||
- peek< gt_op >(position) ||
- peek< lte_op >(position) ||
- peek< lt_op >(position)))
- { return expr1; }
- Binary_Expression::Type op
- = lex<eq_op>() ? Binary_Expression::EQ
- : lex<neq_op>() ? Binary_Expression::NEQ
- : lex<gte_op>() ? Binary_Expression::GTE
- : lex<lte_op>() ? Binary_Expression::LTE
- : lex<gt_op>() ? Binary_Expression::GT
- : lex<lt_op>() ? Binary_Expression::LT
- : Binary_Expression::LT; // whatever
- Expression* expr2 = parse_expression();
- return new (ctx.mem) Binary_Expression(path, expr1->position(), op, expr1, expr2);
- }
- Expression* Parser::parse_expression()
- {
- Expression* term1 = parse_term();
- // if it's a singleton, return it directly; don't wrap it
- if (!(peek< exactly<'+'> >(position) ||
- peek< sequence< negate< number >, exactly<'-'> > >(position)) ||
- peek< identifier >(position))
- { return term1; }
- vector<Expression*> operands;
- vector<Binary_Expression::Type> operators;
- while (lex< exactly<'+'> >() || lex< sequence< negate< number >, exactly<'-'> > >()) {
- operators.push_back(lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
- operands.push_back(parse_term());
- }
- return fold_operands(term1, operands, operators);
- }
- Expression* Parser::parse_term()
- {
- Expression* fact1 = parse_factor();
- // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
- if (peek< exactly<'%'> >(position) && fact1->concrete_type() == Expression::STRING) {
- try {
- String_Schema* ss = dynamic_cast<String_Schema*>(fact1);
- if (ss->has_interpolants()) return fact1;
- }
- catch (bad_cast&) {}
- catch (...) { throw; }
- }
- // if it's a singleton, return it directly; don't wrap it
- if (!(peek< exactly<'*'> >(position) ||
- peek< exactly<'/'> >(position) ||
- peek< exactly<'%'> >(position)))
- { return fact1; }
- vector<Expression*> operands;
- vector<Binary_Expression::Type> operators;
- while (lex< exactly<'*'> >() || lex< exactly<'/'> >() || lex< exactly<'%'> >()) {
- if (lexed == "*") operators.push_back(Binary_Expression::MUL);
- else if (lexed == "/") operators.push_back(Binary_Expression::DIV);
- else operators.push_back(Binary_Expression::MOD);
- operands.push_back(parse_factor());
- }
- return fold_operands(fact1, operands, operators);
- }
- Expression* Parser::parse_factor()
- {
- if (lex< exactly<'('> >()) {
- Expression* value = parse_map();
- if (!lex< exactly<')'> >()) error("unclosed parenthesis");
- value->is_delayed(false);
- // make sure wrapped lists and division expressions are non-delayed within parentheses
- if (value->concrete_type() == Expression::LIST) {
- List* l = static_cast<List*>(value);
- if (!l->empty()) (*l)[0]->is_delayed(false);
- } else if (typeid(*value) == typeid(Binary_Expression)) {
- Binary_Expression* b = static_cast<Binary_Expression*>(value);
- Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
- if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false);
- }
- return value;
- }
- else if (peek< ie_property >()) {
- return parse_ie_property();
- }
- else if (peek< ie_keyword_arg >()) {
- return parse_ie_keyword_arg();
- }
- else if (peek< exactly< calc_kwd > >() ||
- peek< exactly< moz_calc_kwd > >() ||
- peek< exactly< webkit_calc_kwd > >()) {
- return parse_calc_function();
- }
- else if (peek< functional_schema >()) {
- return parse_function_call_schema();
- }
- else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
- return parse_identifier_schema();
- }
- else if (peek< functional >() && !peek< uri_prefix >()) {
- return parse_function_call();
- }
- else if (lex< sequence< exactly<'+'>, spaces_and_comments, negate< number > > >()) {
- return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::PLUS, parse_factor());
- }
- else if (lex< sequence< exactly<'-'>, spaces_and_comments, negate< number> > >()) {
- return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::MINUS, parse_factor());
- }
- else if (lex< sequence< not_op, spaces_and_comments > >()) {
- return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::NOT, parse_factor());
- }
- else {
- return parse_value();
- }
- }
- Expression* Parser::parse_value()
- {
- if (lex< uri_prefix >()) {
- Arguments* args = new (ctx.mem) Arguments(path, source_position);
- Function_Call* result = new (ctx.mem) Function_Call(path, source_position, "url", args);
- const char* here = position;
- Position here_p = source_position;
- // Try to parse a SassScript expression. If it succeeds and we can munch
- // a matching rparen, then that's our url. If we can't munch a matching
- // rparen, or if the attempt to parse an expression fails, then try to
- // munch a regular CSS url.
- try {
- // special case -- if there's a comment, treat it as part of a URL
- lex<spaces>();
- if (peek<line_comment_prefix>() || peek<block_comment_prefix>()) error("comment in URL"); // doesn't really matter what we throw
- Expression* expr = parse_list();
- if (!lex< exactly<')'> >()) error("dangling expression in URL"); // doesn't really matter what we throw
- Argument* arg = new (ctx.mem) Argument(path, expr->position(), expr);
- *args << arg;
- return result;
- }
- catch (Sass_Error&) {
- // back up so we can try again
- position = here;
- source_position = here_p;
- }
- catch (...) { throw; }
- lex< spaces >();
- if (lex< url >()) {
- String* the_url = parse_interpolated_chunk(lexed);
- Argument* arg = new (ctx.mem) Argument(path, the_url->position(), the_url);
- *args << arg;
- }
- else {
- error("malformed URL");
- }
- if (!lex< exactly<')'> >()) error("URI is missing ')'");
- return result;
- }
- if (lex< important >())
- { return new (ctx.mem) String_Constant(path, source_position, "!important"); }
- if (lex< value_schema >())
- { return Parser::from_token(lexed, ctx, path, source_position).parse_value_schema(); }
- if (lex< sequence< true_val, negate< identifier > > >())
- { return new (ctx.mem) Boolean(path, source_position, true); }
- if (lex< sequence< false_val, negate< identifier > > >())
- { return new (ctx.mem) Boolean(path, source_position, false); }
- if (lex< sequence< null, negate< identifier > > >())
- { return new (ctx.mem) Null(path, source_position); }
- if (lex< identifier >()) {
- String_Constant* str = new (ctx.mem) String_Constant(path, source_position, lexed);
- // Dont' delay this string if it is a name color. Fixes #652.
- str->is_delayed(ctx.names_to_colors.count(lexed) == 0);
- return str;
- }
- if (lex< percentage >())
- { return new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); }
- if (lex< dimension >())
- { return new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); }
- if (lex< number >())
- { return new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); }
- if (lex< hex >())
- { return new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); }
- if (peek< string_constant >())
- { return parse_string(); }
- if (lex< variable >())
- { return new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); }
- // Special case handling for `%` proceeding an interpolant.
- if (lex< sequence< exactly<'%'>, optional< percentage > > >())
- { return new (ctx.mem) String_Constant(path, source_position, lexed); }
- error("error reading values after " + lexed.to_string());
- // unreachable statement
- return 0;
- }
- String* Parser::parse_interpolated_chunk(Token chunk)
- {
- const char* i = chunk.begin;
- // see if there any interpolants
- const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(chunk.begin, chunk.end);
- if (!p) {
- String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, chunk, dequote);
- str_node->is_delayed(true);
- return str_node;
- }
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- schema->quote_mark(*chunk.begin);
- while (i < chunk.end) {
- p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, chunk.end);
- if (p) {
- if (i < p) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
- }
- const char* j = find_first_in_interval< exactly<rbrace> >(p, chunk.end); // find the closing brace
- if (j) {
- // parse the interpolant and accumulate it
- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
- interp_node->is_interpolant(true);
- (*schema) << interp_node;
- i = j+1;
- }
- else {
- // throw an error if the interpolant is unterminated
- error("unterminated interpolant inside string constant " + chunk.to_string());
- }
- }
- else { // no interpolants left; add the last segment if nonempty
- if (i < chunk.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, chunk.end));
- break;
- }
- }
- return schema;
- }
- String_Constant* Parser::parse_static_value()
- {
- lex< static_value >();
- Token str(lexed);
- --str.end;
- --position;
- String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
- str_node->is_delayed(true);
- return str_node;
- }
- String* Parser::parse_string()
- {
- lex< string_constant >();
- Token str(lexed);
- return parse_interpolated_chunk(str);
- // const char* i = str.begin;
- // // see if there any interpolants
- // const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
- // if (!p) {
- // String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
- // str_node->is_delayed(true);
- // return str_node;
- // }
- // String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- // schema->quote_mark(*str.begin);
- // while (i < str.end) {
- // p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
- // if (p) {
- // if (i < p) {
- // (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
- // }
- // const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
- // if (j) {
- // // parse the interpolant and accumulate it
- // Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
- // interp_node->is_interpolant(true);
- // (*schema) << interp_node;
- // i = j+1;
- // }
- // else {
- // // throw an error if the interpolant is unterminated
- // error("unterminated interpolant inside string constant " + str.to_string());
- // }
- // }
- // else { // no interpolants left; add the last segment if nonempty
- // if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
- // break;
- // }
- // }
- // return schema;
- }
- String* Parser::parse_ie_property()
- {
- lex< ie_property >();
- Token str(lexed);
- const char* i = str.begin;
- // see if there any interpolants
- const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
- if (!p) {
- String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
- str_node->is_delayed(true);
- return str_node;
- }
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- while (i < str.end) {
- p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
- if (p) {
- if (i < p) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
- }
- const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
- if (j) {
- // parse the interpolant and accumulate it
- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
- interp_node->is_interpolant(true);
- (*schema) << interp_node;
- i = j+1;
- }
- else {
- // throw an error if the interpolant is unterminated
- error("unterminated interpolant inside IE function " + str.to_string());
- }
- }
- else { // no interpolants left; add the last segment if nonempty
- if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
- break;
- }
- }
- return schema;
- }
- String* Parser::parse_ie_keyword_arg()
- {
- String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, source_position, 3);
- if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
- else {
- lex< alternatives< identifier_schema, identifier > >();
- *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- lex< exactly<'='> >();
- *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
- if (peek< variable >()) *kwd_arg << parse_list();
- else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, Util::normalize_decimals(lexed));
- else {
- lex< alternatives< identifier_schema, identifier, number, hex > >();
- *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- return kwd_arg;
- }
- String_Schema* Parser::parse_value_schema()
- {
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- size_t num_items = 0;
- while (position < end) {
- if (lex< interpolant >()) {
- Token insides(Token(lexed.begin + 2, lexed.end - 1));
- Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
- interp_node->is_interpolant(true);
- (*schema) << interp_node;
- }
- else if (lex< exactly<'%'> >()) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- else if (lex< identifier >()) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- else if (lex< percentage >()) {
- (*schema) << new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed);
- }
- else if (lex< dimension >()) {
- (*schema) << new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed);
- }
- else if (lex< number >()) {
- (*schema) << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed);
- }
- else if (lex< hex >()) {
- (*schema) << new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed);
- }
- else if (lex< string_constant >()) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- if (!num_items) schema->quote_mark(*lexed.begin);
- }
- else if (lex< variable >()) {
- (*schema) << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
- }
- else {
- error("error parsing interpolated value");
- }
- ++num_items;
- }
- return schema;
- }
- String_Schema* Parser::parse_url_schema()
- {
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- while (position < end) {
- if (position[0] == '/') {
- lexed = Token(position, position+1);
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- ++position;
- }
- else if (lex< interpolant >()) {
- Token insides(Token(lexed.begin + 2, lexed.end - 1));
- Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
- interp_node->is_interpolant(true);
- (*schema) << interp_node;
- }
- else if (lex< sequence< identifier, exactly<':'> > >()) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- else if (lex< filename >()) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
- }
- else {
- error("error parsing interpolated url");
- }
- }
- return schema;
- }
- String* Parser::parse_identifier_schema()
- {
- lex< sequence< optional< exactly<'*'> >, identifier_schema > >();
- Token id(lexed);
- const char* i = id.begin;
- // see if there any interpolants
- const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(id.begin, id.end);
- if (!p) {
- return new (ctx.mem) String_Constant(path, source_position, id);
- }
- String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
- while (i < id.end) {
- p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, id.end);
- if (p) {
- if (i < p) {
- (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
- }
- const char* j = find_first_in_interval< exactly<rbrace> >(p, id.end); // find the closing brace
- if (j) {
- // parse the interpolant and accumulate it
- Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
- interp_node->is_interpolant(true);
- (*schema) << interp_node;
- schema->has_interpolants(true);
- i = j+1;
- }
- else {
- // throw an error if the interpolant is unterminated
- error("unterminated interpolant inside interpolated identifier " + id.to_string());
- }
- }
- else { // no interpolants left; add the last segment if nonempty
- if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, id.end));
- break;
- }
- }
- return schema;
- }
- Function_Call* Parser::parse_calc_function()
- {
- lex< identifier >();
- string name(lexed);
- Position call_pos = source_position;
- lex< exactly<'('> >();
- Position arg_pos = source_position;
- const char* arg_beg = position;
- parse_list();
- const char* arg_end = position;
- lex< exactly<')'> >();
- Argument* arg = new (ctx.mem) Argument(path, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
- Arguments* args = new (ctx.mem) Arguments(path, arg_pos);
- *args << arg;
- return new (ctx.mem) Function_Call(path, call_pos, name, args);
- }
- Function_Call* Parser::parse_function_call()
- {
- lex< identifier >();
- string name(Util::normalize_underscores(lexed));
- Position source_position_of_call = source_position;
- Function_Call* the_call = new (ctx.mem) Function_Call(path, source_position_of_call, name, parse_arguments());
- return the_call;
- }
- Function_Call_Schema* Parser::parse_function_call_schema()
- {
- String* name = parse_identifier_schema();
- Position source_position_of_call = source_position;
- Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, source_position_of_call, name, parse_arguments());
- return the_call;
- }
- If* Parser::parse_if_directive(bool else_if)
- {
- lex< if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
- Position if_source_position = source_position;
- Expression* predicate = parse_list();
- predicate->is_delayed(false);
- if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if");
- Block* consequent = parse_block();
- Block* alternative = 0;
- if (lex< else_directive >()) {
- if (peek< exactly<if_after_else_kwd> >()) {
- alternative = new (ctx.mem) Block(path, source_position);
- (*alternative) << parse_if_directive(true);
- }
- else if (!peek< exactly<'{'> >()) {
- error("expected '{' after @else");
- }
- else {
- alternative = parse_block();
- }
- }
- return new (ctx.mem) If(path, if_source_position, predicate, consequent, alternative);
- }
- For* Parser::parse_for_directive()
- {
- lex< for_directive >();
- Position for_source_position = source_position;
- if (!lex< variable >()) error("@for directive requires an iteration variable");
- string var(Util::normalize_underscores(lexed));
- if (!lex< from >()) error("expected 'from' keyword in @for directive");
- Expression* lower_bound = parse_expression();
- lower_bound->is_delayed(false);
- bool inclusive = false;
- if (lex< through >()) inclusive = true;
- else if (lex< to >()) inclusive = false;
- else error("expected 'through' or 'to' keyword in @for directive");
- Expression* upper_bound = parse_expression();
- upper_bound->is_delayed(false);
- if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive");
- Block* body = parse_block();
- return new (ctx.mem) For(path, for_source_position, var, lower_bound, upper_bound, body, inclusive);
- }
- Each* Parser::parse_each_directive()
- {
- lex < each_directive >();
- Position each_source_position = source_position;
- if (!lex< variable >()) error("@each directive requires an iteration variable");
- vector<string> vars;
- vars.push_back(Util::normalize_underscores(lexed));
- while (peek< exactly<','> >() && lex< exactly<','> >()) {
- if (!lex< variable >()) error("@each directive requires an iteration variable");
- vars.push_back(Util::normalize_underscores(lexed));
- }
- if (!lex< in >()) error("expected 'in' keyword in @each directive");
- Expression* list = parse_list();
- list->is_delayed(false);
- if (list->concrete_type() == Expression::LIST) {
- List* l = static_cast<List*>(list);
- for (size_t i = 0, L = l->length(); i < L; ++i) {
- (*l)[i]->is_delayed(false);
- }
- }
- if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive");
- Block* body = parse_block();
- return new (ctx.mem) Each(path, each_source_position, vars, list, body);
- }
- While* Parser::parse_while_directive()
- {
- lex< while_directive >();
- Position while_source_position = source_position;
- Expression* predicate = parse_list();
- predicate->is_delayed(false);
- Block* body = parse_block();
- return new (ctx.mem) While(path, while_source_position, predicate, body);
- }
- Media_Block* Parser::parse_media_block()
- {
- lex< media >();
- Position media_source_position = source_position;
- List* media_queries = parse_media_queries();
- if (!peek< exactly<'{'> >()) {
- error("expected '{' in media query");
- }
- Block* block = parse_block();
- return new (ctx.mem) Media_Block(path, media_source_position, media_queries, block);
- }
- List* Parser::parse_media_queries()
- {
- List* media_queries = new (ctx.mem) List(path, source_position, 0, List::COMMA);
- if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query();
- while (lex< exactly<','> >()) (*media_queries) << parse_media_query();
- return media_queries;
- }
- // Expression* Parser::parse_media_query()
- Media_Query* Parser::parse_media_query()
- {
- Media_Query* media_query = new (ctx.mem) Media_Query(path, source_position);
- if (lex< exactly< not_kwd > >()) media_query->is_negated(true);
- else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true);
- if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema());
- else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, source_position, lexed));
- else (*media_query) << parse_media_expression();
- while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();
- return media_query;
- }
- Media_Query_Expression* Parser::parse_media_expression()
- {
- if (peek< identifier_schema >()) {
- String* ss = parse_identifier_schema();
- return new (ctx.mem) Media_Query_Expression(path, source_position, ss, 0, true);
- }
- if (!lex< exactly<'('> >()) {
- error("media query expression must begin with '('");
- }
- Expression* feature = 0;
- if (peek< exactly<')'> >()) {
- error("media feature required in media query expression");
- }
- feature = parse_expression();
- Expression* expression = 0;
- if (lex< exactly<':'> >()) {
- expression = parse_list();
- }
- if (!lex< exactly<')'> >()) {
- error("unclosed parenthesis in media query expression");
- }
- return new (ctx.mem) Media_Query_Expression(path, feature->position(), feature, expression);
- }
- Feature_Block* Parser::parse_feature_block()
- {
- lex< supports >();
- Position supports_source_position = source_position;
- Feature_Query* feature_queries = parse_feature_queries();
- if (!peek< exactly<'{'> >()) {
- error("expected '{' in feature query");
- }
- Block* block = parse_block();
- return new (ctx.mem) Feature_Block(path, supports_source_position, feature_queries, block);
- }
- Feature_Query* Parser::parse_feature_queries()
- {
- Feature_Query* fq = new (ctx.mem) Feature_Query(path, source_position);
- Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
- cond->is_root(true);
- while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
- (*cond) << parse_feature_query();
- (*fq) << cond;
- if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))");
- return fq;
- }
- Feature_Query_Condition* Parser::parse_feature_query()
- {
- if (peek< not_op >(position)) return parse_supports_negation();
- else if (peek< and_op >(position)) return parse_supports_conjunction();
- else if (peek< or_op >(position)) return parse_supports_disjunction();
- else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();
- else return parse_supports_declaration();
- }
- Feature_Query_Condition* Parser::parse_feature_query_in_parens()
- {
- Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
- if (!lex< exactly<'('> >()) error("@supports declaration expected '('");
- while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
- (*cond) << parse_feature_query();
- if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration");
- return (cond->length() == 1) ? (*cond)[0] : cond;
- }
- Feature_Query_Condition* Parser::parse_supports_negation()
- {
- lex< not_op >();
- Feature_Query_Condition* cond = parse_feature_query();
- cond->operand(Feature_Query_Condition::NOT);
- return cond;
- }
- Feature_Query_Condition* Parser::parse_supports_conjunction()
- {
- lex< and_op >();
- Feature_Query_Condition* cond = parse_feature_query();
- cond->operand(Feature_Query_Condition::AND);
- return cond;
- }
- Feature_Query_Condition* Parser::parse_supports_disjunction()
- {
- lex< or_op >();
- Feature_Query_Condition* cond = parse_feature_query();
- cond->operand(Feature_Query_Condition::OR);
- return cond;
- }
- Feature_Query_Condition* Parser::parse_supports_declaration()
- {
- Declaration* declaration = parse_declaration();
- Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->path(),
- declaration->position(),
- 1,
- declaration->property(),
- declaration->value());
- return cond;
- }
- At_Rule* Parser::parse_at_rule()
- {
- lex<at_keyword>();
- string kwd(lexed);
- Position at_source_position = source_position;
- Selector* sel = 0;
- Expression* val = 0;
- Selector_Lookahead lookahead = lookahead_for_extension_target(position);
- if (lookahead.found) {
- if (lookahead.has_interpolants) {
- sel = parse_selector_schema(lookahead.found);
- }
- else {
- sel = parse_selector_group();
- }
- }
- else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {
- val = parse_list();
- }
- Block* body = 0;
- if (peek< exactly<'{'> >()) body = parse_block();
- At_Rule* rule = new (ctx.mem) At_Rule(path, at_source_position, kwd, sel, body);
- if (!sel) rule->value(val);
- return rule;
- }
- Warning* Parser::parse_warning()
- {
- lex< warn >();
- return new (ctx.mem) Warning(path, source_position, parse_list());
- }
- Error* Parser::parse_error()
- {
- lex< err >();
- return new (ctx.mem) Error(path, source_position, parse_list());
- }
- Debug* Parser::parse_debug()
- {
- lex< dbg >();
- return new (ctx.mem) Debug(path, source_position, parse_list());
- }
- Selector_Lookahead Parser::lookahead_for_selector(const char* start)
- {
- const char* p = start ? start : position;
- const char* q;
- bool saw_stuff = false;
- bool saw_interpolant = false;
- while ((q = peek< identifier >(p)) ||
- (q = peek< hyphens_and_identifier >(p)) ||
- (q = peek< hyphens_and_name >(p)) ||
- (q = peek< type_selector >(p)) ||
- (q = peek< id_name >(p)) ||
- (q = peek< class_name >(p)) ||
- (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
- (q = peek< percentage >(p)) ||
- (q = peek< dimension >(p)) ||
- (q = peek< string_constant >(p)) ||
- (q = peek< exactly<'*'> >(p)) ||
- (q = peek< exactly<'('> >(p)) ||
- (q = peek< exactly<')'> >(p)) ||
- (q = peek< exactly<'['> >(p)) ||
- (q = peek< exactly<']'> >(p)) ||
- (q = peek< exactly<'+'> >(p)) ||
- (q = peek< exactly<'~'> >(p)) ||
- (q = peek< exactly<'>'> >(p)) ||
- (q = peek< exactly<','> >(p)) ||
- (q = peek< binomial >(p)) ||
- (q = peek< sequence< optional<sign>,
- optional<digits>,
- exactly<'n'> > >(p)) ||
- (q = peek< sequence< optional<sign>,
- digits > >(p)) ||
- (q = peek< number >(p)) ||
- (q = peek< sequence< exactly<'&'>,
- identifier_fragment > >(p)) ||
- (q = peek< exactly<'&'> >(p)) ||
- (q = peek< exactly<'%'> >(p)) ||
- (q = peek< alternatives<exact_match,
- class_match,
- dash_match,
- prefix_match,
- suffix_match,
- substring_match> >(p)) ||
- (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
- (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
- (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
- (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
- (q = peek< interpolant >(p))) {
- saw_stuff = true;
- p = q;
- if (*(p - 1) == '}') saw_interpolant = true;
- }
- Selector_Lookahead result;
- result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;
- result.has_interpolants = saw_interpolant;
- return result;
- }
- Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)
- {
- const char* p = start ? start : position;
- const char* q;
- bool saw_interpolant = false;
- bool saw_stuff = false;
- while ((q = peek< identifier >(p)) ||
- (q = peek< type_selector >(p)) ||
- (q = peek< id_name >(p)) ||
- (q = peek< class_name >(p)) ||
- (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
- (q = peek< percentage >(p)) ||
- (q = peek< dimension >(p)) ||
- (q = peek< string_constant >(p)) ||
- (q = peek< exactly<'*'> >(p)) ||
- (q = peek< exactly<'('> >(p)) ||
- (q = peek< exactly<')'> >(p)) ||
- (q = peek< exactly<'['> >(p)) ||
- (q = peek< exactly<']'> >(p)) ||
- (q = peek< exactly<'+'> >(p)) ||
- (q = peek< exactly<'~'> >(p)) ||
- (q = peek< exactly<'>'> >(p)) ||
- (q = peek< exactly<','> >(p)) ||
- (q = peek< binomial >(p)) ||
- (q = peek< sequence< optional<sign>,
- optional<digits>,
- exactly<'n'> > >(p)) ||
- (q = peek< sequence< optional<sign>,
- digits > >(p)) ||
- (q = peek< number >(p)) ||
- (q = peek< sequence< exactly<'&'>,
- identifier_fragment > >(p)) ||
- (q = peek< exactly<'&'> >(p)) ||
- (q = peek< exactly<'%'> >(p)) ||
- (q = peek< alternatives<exact_match,
- class_match,
- dash_match,
- prefix_match,
- suffix_match,
- substring_match> >(p)) ||
- (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
- (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
- (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
- (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
- (q = peek< interpolant >(p)) ||
- (q = peek< optional >(p))) {
- p = q;
- if (*(p - 1) == '}') saw_interpolant = true;
- saw_stuff = true;
- }
- Selector_Lookahead result;
- result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
- result.has_interpolants = saw_interpolant;
- return result;
- }
- void Parser::read_bom()
- {
- size_t skip = 0;
- string encoding;
- bool utf_8 = false;
- switch ((unsigned char) source[0]) {
- case 0xEF:
- skip = check_bom_chars(source, end, utf_8_bom, 3);
- encoding = "UTF-8";
- utf_8 = true;
- break;
- case 0xFE:
- skip = check_bom_chars(source, end, utf_16_bom_be, 2);
- encoding = "UTF-16 (big endian)";
- break;
- case 0xFF:
- skip = check_bom_chars(source, end, utf_16_bom_le, 2);
- skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);
- encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
- break;
- case 0x00:
- skip = check_bom_chars(source, end, utf_32_bom_be, 4);
- encoding = "UTF-32 (big endian)";
- break;
- case 0x2B:
- skip = check_bom_chars(source, end, utf_7_bom_1, 4)
- | check_bom_chars(source, end, utf_7_bom_2, 4)
- | check_bom_chars(source, end, utf_7_bom_3, 4)
- | check_bom_chars(source, end, utf_7_bom_4, 4)
- | check_bom_chars(source, end, utf_7_bom_5, 5);
- encoding = "UTF-7";
- break;
- case 0xF7:
- skip = check_bom_chars(source, end, utf_1_bom, 3);
- encoding = "UTF-1";
- break;
- case 0xDD:
- skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);
- encoding = "UTF-EBCDIC";
- break;
- case 0x0E:
- skip = check_bom_chars(source, end, scsu_bom, 3);
- encoding = "SCSU";
- break;
- case 0xFB:
- skip = check_bom_chars(source, end, bocu_1_bom, 3);
- encoding = "BOCU-1";
- break;
- case 0x84:
- skip = check_bom_chars(source, end, gb_18030_bom, 4);
- encoding = "GB-18030";
- break;
- }
- if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);
- position += skip;
- }
- size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
- {
- size_t skip = 0;
- if (src + len > end) return 0;
- for (size_t i = 0; i < len; ++i, ++skip) {
- if ((unsigned char) src[i] != bom[i]) return 0;
- }
- return skip;
- }
- Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op)
- {
- for (size_t i = 0, S = operands.size(); i < S; ++i) {
- base = new (ctx.mem) Binary_Expression(path, source_position, op, base, operands[i]);
- Binary_Expression* b = static_cast<Binary_Expression*>(base);
- if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
- base->is_delayed(true);
- }
- else {
- b->left()->is_delayed(false);
- b->right()->is_delayed(false);
- }
- }
- return base;
- }
- Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops)
- {
- for (size_t i = 0, S = operands.size(); i < S; ++i) {
- base = new (ctx.mem) Binary_Expression(path, base->position(), ops[i], base, operands[i]);
- Binary_Expression* b = static_cast<Binary_Expression*>(base);
- if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
- base->is_delayed(true);
- }
- else {
- b->left()->is_delayed(false);
- b->right()->is_delayed(false);
- }
- }
- return base;
- }
- void Parser::error(string msg, Position pos)
- {
- throw Sass_Error(Sass_Error::syntax, path, pos.line ? pos : source_position, msg);
- }
- }
|