sassc 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.gitmodules +3 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +21 -0
- data/ext/libsass/.editorconfig +15 -0
- data/ext/libsass/.gitattributes +2 -0
- data/ext/libsass/.gitignore +61 -0
- data/ext/libsass/.travis.yml +38 -0
- data/ext/libsass/COPYING +25 -0
- data/ext/libsass/INSTALL +1 -0
- data/ext/libsass/LICENSE +25 -0
- data/ext/libsass/Makefile +223 -0
- data/ext/libsass/Makefile.am +145 -0
- data/ext/libsass/Readme.md +93 -0
- data/ext/libsass/appveyor.yml +76 -0
- data/ext/libsass/ast.cpp +581 -0
- data/ext/libsass/ast.hpp +1949 -0
- data/ext/libsass/ast_def_macros.hpp +16 -0
- data/ext/libsass/ast_factory.hpp +87 -0
- data/ext/libsass/ast_fwd_decl.hpp +72 -0
- data/ext/libsass/b64/cencode.h +32 -0
- data/ext/libsass/b64/encode.h +77 -0
- data/ext/libsass/backtrace.hpp +81 -0
- data/ext/libsass/base64vlq.cpp +43 -0
- data/ext/libsass/base64vlq.hpp +28 -0
- data/ext/libsass/bind.cpp +187 -0
- data/ext/libsass/bind.hpp +18 -0
- data/ext/libsass/cencode.c +102 -0
- data/ext/libsass/color_names.hpp +324 -0
- data/ext/libsass/configure.ac +130 -0
- data/ext/libsass/constants.cpp +144 -0
- data/ext/libsass/constants.hpp +145 -0
- data/ext/libsass/context.cpp +507 -0
- data/ext/libsass/context.hpp +150 -0
- data/ext/libsass/contextualize.cpp +157 -0
- data/ext/libsass/contextualize.hpp +65 -0
- data/ext/libsass/copy_c_str.cpp +13 -0
- data/ext/libsass/copy_c_str.hpp +5 -0
- data/ext/libsass/debug.hpp +39 -0
- data/ext/libsass/environment.hpp +75 -0
- data/ext/libsass/error_handling.cpp +28 -0
- data/ext/libsass/error_handling.hpp +28 -0
- data/ext/libsass/eval.cpp +1149 -0
- data/ext/libsass/eval.hpp +80 -0
- data/ext/libsass/expand.cpp +430 -0
- data/ext/libsass/expand.hpp +77 -0
- data/ext/libsass/extconf.rb +6 -0
- data/ext/libsass/extend.cpp +1962 -0
- data/ext/libsass/extend.hpp +50 -0
- data/ext/libsass/file.cpp +291 -0
- data/ext/libsass/file.hpp +18 -0
- data/ext/libsass/functions.cpp +1565 -0
- data/ext/libsass/functions.hpp +187 -0
- data/ext/libsass/inspect.cpp +727 -0
- data/ext/libsass/inspect.hpp +108 -0
- data/ext/libsass/json.cpp +1411 -0
- data/ext/libsass/json.hpp +117 -0
- data/ext/libsass/kwd_arg_macros.hpp +23 -0
- data/ext/libsass/m4/.gitkeep +0 -0
- data/ext/libsass/mapping.hpp +17 -0
- data/ext/libsass/memory_manager.hpp +54 -0
- data/ext/libsass/node.cpp +251 -0
- data/ext/libsass/node.hpp +122 -0
- data/ext/libsass/operation.hpp +153 -0
- data/ext/libsass/output_compressed.cpp +401 -0
- data/ext/libsass/output_compressed.hpp +95 -0
- data/ext/libsass/output_nested.cpp +364 -0
- data/ext/libsass/output_nested.hpp +108 -0
- data/ext/libsass/parser.cpp +2016 -0
- data/ext/libsass/parser.hpp +264 -0
- data/ext/libsass/paths.hpp +69 -0
- data/ext/libsass/position.hpp +22 -0
- data/ext/libsass/posix/getopt.c +562 -0
- data/ext/libsass/posix/getopt.h +95 -0
- data/ext/libsass/prelexer.cpp +688 -0
- data/ext/libsass/prelexer.hpp +513 -0
- data/ext/libsass/remove_placeholders.cpp +59 -0
- data/ext/libsass/remove_placeholders.hpp +43 -0
- data/ext/libsass/res/resource.rc +35 -0
- data/ext/libsass/sass.cpp +33 -0
- data/ext/libsass/sass.h +60 -0
- data/ext/libsass/sass2scss.cpp +834 -0
- data/ext/libsass/sass2scss.h +110 -0
- data/ext/libsass/sass_context.cpp +709 -0
- data/ext/libsass/sass_context.h +120 -0
- data/ext/libsass/sass_functions.cpp +137 -0
- data/ext/libsass/sass_functions.h +90 -0
- data/ext/libsass/sass_interface.cpp +277 -0
- data/ext/libsass/sass_interface.h +97 -0
- data/ext/libsass/sass_util.cpp +136 -0
- data/ext/libsass/sass_util.hpp +259 -0
- data/ext/libsass/sass_values.cpp +337 -0
- data/ext/libsass/sass_values.h +124 -0
- data/ext/libsass/script/bootstrap +10 -0
- data/ext/libsass/script/branding +10 -0
- data/ext/libsass/script/ci-build-libsass +72 -0
- data/ext/libsass/script/ci-install-compiler +4 -0
- data/ext/libsass/script/ci-install-deps +19 -0
- data/ext/libsass/script/ci-report-coverage +25 -0
- data/ext/libsass/script/coveralls-debug +32 -0
- data/ext/libsass/script/spec +5 -0
- data/ext/libsass/script/tap-driver +652 -0
- data/ext/libsass/script/tap-runner +1 -0
- data/ext/libsass/source_map.cpp +133 -0
- data/ext/libsass/source_map.hpp +46 -0
- data/ext/libsass/subset_map.hpp +145 -0
- data/ext/libsass/support/libsass.pc.in +11 -0
- data/ext/libsass/test-driver +127 -0
- data/ext/libsass/test/test_node.cpp +98 -0
- data/ext/libsass/test/test_paths.cpp +29 -0
- data/ext/libsass/test/test_selector_difference.cpp +28 -0
- data/ext/libsass/test/test_specificity.cpp +28 -0
- data/ext/libsass/test/test_subset_map.cpp +472 -0
- data/ext/libsass/test/test_superselector.cpp +71 -0
- data/ext/libsass/test/test_unification.cpp +33 -0
- data/ext/libsass/to_c.cpp +61 -0
- data/ext/libsass/to_c.hpp +44 -0
- data/ext/libsass/to_string.cpp +29 -0
- data/ext/libsass/to_string.hpp +32 -0
- data/ext/libsass/token.hpp +32 -0
- data/ext/libsass/units.cpp +54 -0
- data/ext/libsass/units.hpp +10 -0
- data/ext/libsass/utf8.h +34 -0
- data/ext/libsass/utf8/checked.h +327 -0
- data/ext/libsass/utf8/core.h +329 -0
- data/ext/libsass/utf8/unchecked.h +228 -0
- data/ext/libsass/utf8_string.cpp +102 -0
- data/ext/libsass/utf8_string.hpp +36 -0
- data/ext/libsass/util.cpp +189 -0
- data/ext/libsass/util.hpp +26 -0
- data/ext/libsass/win/libsass.filters +291 -0
- data/ext/libsass/win/libsass.sln +28 -0
- data/ext/libsass/win/libsass.vcxproj +255 -0
- data/lib/sassc.rb +6 -0
- data/lib/sassc/engine.rb +13 -0
- data/lib/sassc/native.rb +44 -0
- data/lib/sassc/native/native_context_api.rb +140 -0
- data/lib/sassc/native/native_functions_api.rb +41 -0
- data/lib/sassc/native/sass_input_style.rb +11 -0
- data/lib/sassc/native/sass_output_style.rb +10 -0
- data/lib/sassc/native/sass_value.rb +95 -0
- data/lib/sassc/native/string_list.rb +8 -0
- data/lib/sassc/version.rb +3 -0
- data/sassc.gemspec +43 -0
- data/test/smoke_test.rb +171 -0
- data/test/test_helper.rb +4 -0
- metadata +281 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#include <string>
|
|
2
|
+
|
|
3
|
+
#ifndef SASS_OPERATION
|
|
4
|
+
#include "operation.hpp"
|
|
5
|
+
#endif
|
|
6
|
+
|
|
7
|
+
// #ifndef SASS_TO_STRING
|
|
8
|
+
// #include "to_string.hpp"
|
|
9
|
+
// #endif
|
|
10
|
+
|
|
11
|
+
namespace Sass {
|
|
12
|
+
using namespace std;
|
|
13
|
+
struct Context;
|
|
14
|
+
|
|
15
|
+
class Output_Nested : public Operation_CRTP<void, Output_Nested> {
|
|
16
|
+
// import all the class-specific methods and override as desired
|
|
17
|
+
using Operation_CRTP<void, Output_Nested>::operator();
|
|
18
|
+
|
|
19
|
+
string buffer;
|
|
20
|
+
string rendered_imports;
|
|
21
|
+
size_t indentation;
|
|
22
|
+
bool source_comments;
|
|
23
|
+
Context* ctx;
|
|
24
|
+
bool seen_utf8;
|
|
25
|
+
void indent();
|
|
26
|
+
|
|
27
|
+
void fallback_impl(AST_Node* n);
|
|
28
|
+
|
|
29
|
+
void append_to_buffer(const string& text);
|
|
30
|
+
|
|
31
|
+
public:
|
|
32
|
+
|
|
33
|
+
Output_Nested(bool source_comments = false, Context* ctx = 0);
|
|
34
|
+
virtual ~Output_Nested();
|
|
35
|
+
|
|
36
|
+
string get_buffer() {
|
|
37
|
+
if (!rendered_imports.empty() && !buffer.empty()) {
|
|
38
|
+
rendered_imports += "\n";
|
|
39
|
+
}
|
|
40
|
+
return (seen_utf8 ? "@charset \"UTF-8\";\n" : "")
|
|
41
|
+
+ rendered_imports + buffer;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// statements
|
|
45
|
+
virtual void operator()(Block*);
|
|
46
|
+
virtual void operator()(Ruleset*);
|
|
47
|
+
// virtual void operator()(Propset*);
|
|
48
|
+
virtual void operator()(Feature_Block*);
|
|
49
|
+
virtual void operator()(Media_Block*);
|
|
50
|
+
virtual void operator()(At_Rule*);
|
|
51
|
+
// virtual void operator()(Declaration*);
|
|
52
|
+
// virtual void operator()(Assignment*);
|
|
53
|
+
virtual void operator()(Import*);
|
|
54
|
+
// virtual void operator()(Import_Stub*);
|
|
55
|
+
// virtual void operator()(Warning*);
|
|
56
|
+
// virtual void operator()(Error*);
|
|
57
|
+
// virtual void operator()(Debug*);
|
|
58
|
+
// virtual void operator()(Comment*);
|
|
59
|
+
// virtual void operator()(If*);
|
|
60
|
+
// virtual void operator()(For*);
|
|
61
|
+
// virtual void operator()(Each*);
|
|
62
|
+
// virtual void operator()(While*);
|
|
63
|
+
// virtual void operator()(Return*);
|
|
64
|
+
// virtual void operator()(Extension*);
|
|
65
|
+
// virtual void operator()(Definition*);
|
|
66
|
+
// virtual void operator()(Mixin_Call*);
|
|
67
|
+
// virtual void operator()(Content*);
|
|
68
|
+
// // expressions
|
|
69
|
+
// virtual void operator()(List*);
|
|
70
|
+
// virtual void operator()(Binary_Expression*);
|
|
71
|
+
// virtual void operator()(Unary_Expression*);
|
|
72
|
+
// virtual void operator()(Function_Call*);
|
|
73
|
+
// virtual void operator()(Function_Call_Schema*);
|
|
74
|
+
// virtual void operator()(Variable*);
|
|
75
|
+
// virtual void operator()(Textual*);
|
|
76
|
+
// virtual void operator()(Number*);
|
|
77
|
+
// virtual void operator()(Color*);
|
|
78
|
+
// virtual void operator()(Boolean*);
|
|
79
|
+
// virtual void operator()(String_Schema*);
|
|
80
|
+
// virtual void operator()(String_Constant* x);
|
|
81
|
+
// virtual void operator()(Media_Query*);
|
|
82
|
+
// virtual void operator()(Media_Query_Expression*);
|
|
83
|
+
// // parameters and arguments
|
|
84
|
+
// virtual void operator()(Parameter*);
|
|
85
|
+
// virtual void operator()(Parameters*);
|
|
86
|
+
// virtual void operator()(Argument*);
|
|
87
|
+
// virtual void operator()(Arguments*);
|
|
88
|
+
// // selectors
|
|
89
|
+
// virtual void operator()(Selector_Schema*);
|
|
90
|
+
// virtual void operator()(Selector_Reference*);
|
|
91
|
+
// virtual void operator()(Selector_Placeholder*);
|
|
92
|
+
// virtual void operator()(Type_Selector*);
|
|
93
|
+
// virtual void operator()(Selector_Qualifier*);
|
|
94
|
+
// virtual void operator()(Attribute_Selector*);
|
|
95
|
+
// virtual void operator()(Pseudo_Selector*);
|
|
96
|
+
// virtual void operator()(Wrapped_Selector*);
|
|
97
|
+
// virtual void operator()(Compound_Selector*);
|
|
98
|
+
// virtual void operator()(Complex_Selector*);
|
|
99
|
+
// virtual void operator()(Selector_List*);
|
|
100
|
+
|
|
101
|
+
template <typename U>
|
|
102
|
+
void fallback(U x) { fallback_impl(x); }
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
string unquote(const string&);
|
|
106
|
+
string quote(const string&, char);
|
|
107
|
+
|
|
108
|
+
}
|
|
@@ -0,0 +1,2016 @@
|
|
|
1
|
+
#include <cstdlib>
|
|
2
|
+
#include <iostream>
|
|
3
|
+
#include <vector>
|
|
4
|
+
#include "parser.hpp"
|
|
5
|
+
#include "file.hpp"
|
|
6
|
+
#include "inspect.hpp"
|
|
7
|
+
#include "to_string.hpp"
|
|
8
|
+
#include "constants.hpp"
|
|
9
|
+
#include "util.hpp"
|
|
10
|
+
|
|
11
|
+
#ifndef SASS_PRELEXER
|
|
12
|
+
#include "prelexer.hpp"
|
|
13
|
+
#endif
|
|
14
|
+
|
|
15
|
+
#include "sass_functions.h"
|
|
16
|
+
|
|
17
|
+
#include <typeinfo>
|
|
18
|
+
|
|
19
|
+
namespace Sass {
|
|
20
|
+
using namespace std;
|
|
21
|
+
using namespace Constants;
|
|
22
|
+
|
|
23
|
+
Parser Parser::from_c_str(const char* str, Context& ctx, string path, Position source_position)
|
|
24
|
+
{
|
|
25
|
+
Parser p(ctx, path, source_position);
|
|
26
|
+
p.source = str;
|
|
27
|
+
p.position = p.source;
|
|
28
|
+
p.end = str + strlen(str);
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Parser Parser::from_token(Token t, Context& ctx, string path, Position source_position)
|
|
33
|
+
{
|
|
34
|
+
Parser p(ctx, path, source_position);
|
|
35
|
+
p.source = t.begin;
|
|
36
|
+
p.position = p.source;
|
|
37
|
+
p.end = t.end;
|
|
38
|
+
p.dequote = true;
|
|
39
|
+
return p;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
Block* Parser::parse()
|
|
43
|
+
{
|
|
44
|
+
Block* root = new (ctx.mem) Block(path, source_position);
|
|
45
|
+
root->is_root(true);
|
|
46
|
+
read_bom();
|
|
47
|
+
lex< optional_spaces >();
|
|
48
|
+
Selector_Lookahead lookahead_result;
|
|
49
|
+
while (position < end) {
|
|
50
|
+
if (lex< block_comment >()) {
|
|
51
|
+
String* contents = parse_interpolated_chunk(lexed);
|
|
52
|
+
Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
|
|
53
|
+
(*root) << comment;
|
|
54
|
+
}
|
|
55
|
+
else if (peek< import >()) {
|
|
56
|
+
Import* imp = parse_import();
|
|
57
|
+
if (!imp->urls().empty()) (*root) << imp;
|
|
58
|
+
if (!imp->files().empty()) {
|
|
59
|
+
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
|
|
60
|
+
(*root) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'");
|
|
64
|
+
}
|
|
65
|
+
else if (peek< mixin >() || peek< function >()) {
|
|
66
|
+
(*root) << parse_definition();
|
|
67
|
+
}
|
|
68
|
+
else if (peek< variable >()) {
|
|
69
|
+
(*root) << parse_assignment();
|
|
70
|
+
if (!lex< one_plus< exactly<';'> > >()) error("top-level variable binding must be terminated by ';'");
|
|
71
|
+
}
|
|
72
|
+
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
|
|
73
|
+
(*root) << parse_propset();
|
|
74
|
+
}
|
|
75
|
+
else if (peek< include >() /* || peek< exactly<'+'> >() */) {
|
|
76
|
+
Mixin_Call* mixin_call = parse_mixin_call();
|
|
77
|
+
(*root) << mixin_call;
|
|
78
|
+
if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'");
|
|
79
|
+
}
|
|
80
|
+
else if (peek< if_directive >()) {
|
|
81
|
+
(*root) << parse_if_directive();
|
|
82
|
+
}
|
|
83
|
+
else if (peek< for_directive >()) {
|
|
84
|
+
(*root) << parse_for_directive();
|
|
85
|
+
}
|
|
86
|
+
else if (peek< each_directive >()) {
|
|
87
|
+
(*root) << parse_each_directive();
|
|
88
|
+
}
|
|
89
|
+
else if (peek< while_directive >()) {
|
|
90
|
+
(*root) << parse_while_directive();
|
|
91
|
+
}
|
|
92
|
+
else if (peek< media >()) {
|
|
93
|
+
(*root) << parse_media_block();
|
|
94
|
+
}
|
|
95
|
+
else if (peek< supports >()) {
|
|
96
|
+
(*root) << parse_feature_block();
|
|
97
|
+
}
|
|
98
|
+
else if (peek< warn >()) {
|
|
99
|
+
(*root) << parse_warning();
|
|
100
|
+
if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'");
|
|
101
|
+
}
|
|
102
|
+
else if (peek< err >()) {
|
|
103
|
+
(*root) << parse_error();
|
|
104
|
+
if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'");
|
|
105
|
+
}
|
|
106
|
+
else if (peek< dbg >()) {
|
|
107
|
+
(*root) << parse_debug();
|
|
108
|
+
if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'");
|
|
109
|
+
}
|
|
110
|
+
// ignore the @charset directive for now
|
|
111
|
+
else if (lex< exactly< charset_kwd > >()) {
|
|
112
|
+
lex< string_constant >();
|
|
113
|
+
lex< one_plus< exactly<';'> > >();
|
|
114
|
+
}
|
|
115
|
+
else if (peek< at_keyword >()) {
|
|
116
|
+
At_Rule* at_rule = parse_at_rule();
|
|
117
|
+
(*root) << at_rule;
|
|
118
|
+
if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'");
|
|
119
|
+
}
|
|
120
|
+
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
|
121
|
+
(*root) << parse_ruleset(lookahead_result);
|
|
122
|
+
}
|
|
123
|
+
else if (peek< exactly<';'> >()) {
|
|
124
|
+
lex< one_plus< exactly<';'> > >();
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
lex< spaces_and_comments >();
|
|
128
|
+
if (position >= end) break;
|
|
129
|
+
error("invalid top-level expression");
|
|
130
|
+
}
|
|
131
|
+
lex< optional_spaces >();
|
|
132
|
+
}
|
|
133
|
+
return root;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void Parser::add_single_file (Import* imp, string import_path) {
|
|
137
|
+
|
|
138
|
+
string extension;
|
|
139
|
+
string unquoted(unquote(import_path));
|
|
140
|
+
if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css
|
|
141
|
+
// a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end
|
|
142
|
+
extension = unquoted.substr(unquoted.length() - 4, 4);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (extension == ".css") {
|
|
146
|
+
String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path, true);
|
|
147
|
+
Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc);
|
|
148
|
+
Arguments* loc_args = new (ctx.mem) Arguments(path, source_position);
|
|
149
|
+
(*loc_args) << loc_arg;
|
|
150
|
+
Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args);
|
|
151
|
+
imp->urls().push_back(new_url);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
string current_dir = File::dir_name(path);
|
|
155
|
+
string resolved(ctx.add_file(current_dir, unquoted));
|
|
156
|
+
if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir);
|
|
157
|
+
imp->files().push_back(resolved);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
Import* Parser::parse_import()
|
|
163
|
+
{
|
|
164
|
+
lex< import >();
|
|
165
|
+
Import* imp = new (ctx.mem) Import(path, source_position);
|
|
166
|
+
bool first = true;
|
|
167
|
+
do {
|
|
168
|
+
if (lex< string_constant >()) {
|
|
169
|
+
string import_path(lexed);
|
|
170
|
+
|
|
171
|
+
// struct Sass_Options opt = sass_context_get_options(ctx)
|
|
172
|
+
Sass_C_Import_Callback importer = ctx.importer;
|
|
173
|
+
// custom importer
|
|
174
|
+
if (importer) {
|
|
175
|
+
Sass_Import* current = ctx.import_stack.back();
|
|
176
|
+
Sass_C_Import_Fn fn = sass_import_get_function(importer);
|
|
177
|
+
void* cookie = sass_import_get_cookie(importer);
|
|
178
|
+
// create a new import entry
|
|
179
|
+
string inc_path = unquote(import_path);
|
|
180
|
+
struct Sass_Import** includes = fn(
|
|
181
|
+
inc_path.c_str(),
|
|
182
|
+
sass_import_get_path(current),
|
|
183
|
+
cookie);
|
|
184
|
+
if (includes) {
|
|
185
|
+
struct Sass_Import** list = includes;
|
|
186
|
+
while (*includes) {
|
|
187
|
+
struct Sass_Import* include = *includes;
|
|
188
|
+
const char *file = sass_import_get_path(include);
|
|
189
|
+
char *source = sass_import_take_source(include);
|
|
190
|
+
// char *srcmap = sass_import_take_srcmap(include);
|
|
191
|
+
if (source) {
|
|
192
|
+
if (file) {
|
|
193
|
+
ctx.add_source(file, inc_path, source);
|
|
194
|
+
imp->files().push_back(file);
|
|
195
|
+
} else {
|
|
196
|
+
ctx.add_source(inc_path, inc_path, source);
|
|
197
|
+
imp->files().push_back(inc_path);
|
|
198
|
+
}
|
|
199
|
+
} else if(file) {
|
|
200
|
+
add_single_file(imp, file);
|
|
201
|
+
}
|
|
202
|
+
++includes;
|
|
203
|
+
}
|
|
204
|
+
// deallocate returned memory
|
|
205
|
+
sass_delete_import_list(list);
|
|
206
|
+
// parse next import
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
add_single_file(imp, import_path);
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
else if (peek< uri_prefix >()) {
|
|
215
|
+
imp->urls().push_back(parse_value());
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
if (first) error("@import directive requires a url or quoted path");
|
|
219
|
+
else error("expecting another url or quoted path in @import list");
|
|
220
|
+
}
|
|
221
|
+
first = false;
|
|
222
|
+
} while (lex< exactly<','> >());
|
|
223
|
+
return imp;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
Definition* Parser::parse_definition()
|
|
227
|
+
{
|
|
228
|
+
Definition::Type which_type = Definition::MIXIN;
|
|
229
|
+
if (lex< mixin >()) which_type = Definition::MIXIN;
|
|
230
|
+
else if (lex< function >()) which_type = Definition::FUNCTION;
|
|
231
|
+
string which_str(lexed);
|
|
232
|
+
if (!lex< identifier >()) error("invalid name in " + which_str + " definition");
|
|
233
|
+
string name(Util::normalize_underscores(lexed));
|
|
234
|
+
if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
|
|
235
|
+
{ error("Invalid function name \"" + name + "\"."); }
|
|
236
|
+
Position source_position_of_def = source_position;
|
|
237
|
+
Parameters* params = parse_parameters();
|
|
238
|
+
if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'");
|
|
239
|
+
if (which_type == Definition::MIXIN) stack.push_back(mixin_def);
|
|
240
|
+
else stack.push_back(function_def);
|
|
241
|
+
Block* body = parse_block();
|
|
242
|
+
stack.pop_back();
|
|
243
|
+
Definition* def = new (ctx.mem) Definition(path, source_position_of_def, name, params, body, which_type);
|
|
244
|
+
return def;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Parameters* Parser::parse_parameters()
|
|
248
|
+
{
|
|
249
|
+
string name(lexed); // for the error message
|
|
250
|
+
Parameters* params = new (ctx.mem) Parameters(path, source_position);
|
|
251
|
+
if (lex< exactly<'('> >()) {
|
|
252
|
+
// if there's anything there at all
|
|
253
|
+
if (!peek< exactly<')'> >()) {
|
|
254
|
+
do (*params) << parse_parameter();
|
|
255
|
+
while (lex< exactly<','> >());
|
|
256
|
+
}
|
|
257
|
+
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
|
|
258
|
+
}
|
|
259
|
+
return params;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
Parameter* Parser::parse_parameter()
|
|
263
|
+
{
|
|
264
|
+
lex< variable >();
|
|
265
|
+
string name(Util::normalize_underscores(lexed));
|
|
266
|
+
Position pos = source_position;
|
|
267
|
+
Expression* val = 0;
|
|
268
|
+
bool is_rest = false;
|
|
269
|
+
if (lex< exactly<':'> >()) { // there's a default value
|
|
270
|
+
val = parse_space_list();
|
|
271
|
+
val->is_delayed(false);
|
|
272
|
+
}
|
|
273
|
+
else if (lex< exactly< ellipsis > >()) {
|
|
274
|
+
is_rest = true;
|
|
275
|
+
}
|
|
276
|
+
Parameter* p = new (ctx.mem) Parameter(path, pos, name, val, is_rest);
|
|
277
|
+
return p;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
Mixin_Call* Parser::parse_mixin_call()
|
|
281
|
+
{
|
|
282
|
+
lex< include >() /* || lex< exactly<'+'> >() */;
|
|
283
|
+
if (!lex< identifier >()) error("invalid name in @include directive");
|
|
284
|
+
Position source_position_of_call = source_position;
|
|
285
|
+
string name(Util::normalize_underscores(lexed));
|
|
286
|
+
Arguments* args = parse_arguments();
|
|
287
|
+
Block* content = 0;
|
|
288
|
+
if (peek< exactly<'{'> >()) {
|
|
289
|
+
content = parse_block();
|
|
290
|
+
}
|
|
291
|
+
Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, source_position_of_call, name, args, content);
|
|
292
|
+
return the_call;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
Arguments* Parser::parse_arguments()
|
|
296
|
+
{
|
|
297
|
+
string name(lexed);
|
|
298
|
+
Arguments* args = new (ctx.mem) Arguments(path, source_position);
|
|
299
|
+
|
|
300
|
+
if (lex< exactly<'('> >()) {
|
|
301
|
+
// if there's anything there at all
|
|
302
|
+
if (!peek< exactly<')'> >()) {
|
|
303
|
+
do (*args) << parse_argument();
|
|
304
|
+
while (lex< exactly<','> >());
|
|
305
|
+
}
|
|
306
|
+
if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return args;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Argument* Parser::parse_argument()
|
|
313
|
+
{
|
|
314
|
+
Argument* arg;
|
|
315
|
+
if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) {
|
|
316
|
+
lex< variable >();
|
|
317
|
+
string name(Util::normalize_underscores(lexed));
|
|
318
|
+
Position p = source_position;
|
|
319
|
+
lex< exactly<':'> >();
|
|
320
|
+
Expression* val = parse_space_list();
|
|
321
|
+
val->is_delayed(false);
|
|
322
|
+
arg = new (ctx.mem) Argument(path, p, val, name);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
bool is_arglist = false;
|
|
326
|
+
bool is_keyword = false;
|
|
327
|
+
Expression* val = parse_space_list();
|
|
328
|
+
val->is_delayed(false);
|
|
329
|
+
if (lex< exactly< ellipsis > >()) {
|
|
330
|
+
if (val->concrete_type() == Expression::MAP) is_keyword = true;
|
|
331
|
+
else is_arglist = true;
|
|
332
|
+
}
|
|
333
|
+
arg = new (ctx.mem) Argument(path, source_position, val, "", is_arglist, is_keyword);
|
|
334
|
+
}
|
|
335
|
+
return arg;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
Assignment* Parser::parse_assignment()
|
|
339
|
+
{
|
|
340
|
+
lex< variable >();
|
|
341
|
+
string name(Util::normalize_underscores(lexed));
|
|
342
|
+
Position var_source_position = source_position;
|
|
343
|
+
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
|
|
344
|
+
Expression* val = parse_list();
|
|
345
|
+
val->is_delayed(false);
|
|
346
|
+
bool is_guarded = false;
|
|
347
|
+
bool is_global = false;
|
|
348
|
+
while (peek< default_flag >() || peek< global_flag >()) {
|
|
349
|
+
is_guarded = lex< default_flag >() || is_guarded;
|
|
350
|
+
is_global = lex< global_flag >() || is_global;
|
|
351
|
+
}
|
|
352
|
+
Assignment* var = new (ctx.mem) Assignment(path, var_source_position, name, val, is_guarded, is_global);
|
|
353
|
+
return var;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
Propset* Parser::parse_propset()
|
|
357
|
+
{
|
|
358
|
+
String* property_segment;
|
|
359
|
+
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
|
|
360
|
+
property_segment = parse_identifier_schema();
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
lex< sequence< optional< exactly<'*'> >, identifier > >();
|
|
364
|
+
property_segment = new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
365
|
+
}
|
|
366
|
+
Propset* propset = new (ctx.mem) Propset(path, source_position, property_segment);
|
|
367
|
+
lex< exactly<':'> >();
|
|
368
|
+
|
|
369
|
+
if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property");
|
|
370
|
+
|
|
371
|
+
propset->block(parse_block());
|
|
372
|
+
|
|
373
|
+
return propset;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)
|
|
377
|
+
{
|
|
378
|
+
Selector* sel;
|
|
379
|
+
if (lookahead.has_interpolants) {
|
|
380
|
+
sel = parse_selector_schema(lookahead.found);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
sel = parse_selector_group();
|
|
384
|
+
}
|
|
385
|
+
Position r_source_position = source_position;
|
|
386
|
+
if (!peek< exactly<'{'> >()) error("expected a '{' after the selector");
|
|
387
|
+
Block* block = parse_block();
|
|
388
|
+
Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_source_position, sel, block);
|
|
389
|
+
return ruleset;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
|
|
393
|
+
{
|
|
394
|
+
lex< optional_spaces >();
|
|
395
|
+
const char* i = position;
|
|
396
|
+
const char* p;
|
|
397
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
398
|
+
|
|
399
|
+
while (i < end_of_selector) {
|
|
400
|
+
p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
|
|
401
|
+
if (p) {
|
|
402
|
+
// accumulate the preceding segment if there is one
|
|
403
|
+
if (i < p) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p));
|
|
404
|
+
// find the end of the interpolant and parse it
|
|
405
|
+
const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
|
|
406
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
|
|
407
|
+
interp_node->is_interpolant(true);
|
|
408
|
+
(*schema) << interp_node;
|
|
409
|
+
i = j + 1;
|
|
410
|
+
}
|
|
411
|
+
else { // no interpolants left; add the last segment if there is one
|
|
412
|
+
if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, end_of_selector));
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
position = end_of_selector;
|
|
417
|
+
return new (ctx.mem) Selector_Schema(path, source_position, schema);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
Selector_List* Parser::parse_selector_group()
|
|
421
|
+
{
|
|
422
|
+
To_String to_string;
|
|
423
|
+
lex< spaces_and_comments >();
|
|
424
|
+
Selector_List* group = new (ctx.mem) Selector_List(path, source_position);
|
|
425
|
+
do {
|
|
426
|
+
if (peek< exactly<'{'> >() ||
|
|
427
|
+
peek< exactly<'}'> >() ||
|
|
428
|
+
peek< exactly<')'> >() ||
|
|
429
|
+
peek< exactly<';'> >())
|
|
430
|
+
break; // in case there are superfluous commas at the end
|
|
431
|
+
Complex_Selector* comb = parse_selector_combination();
|
|
432
|
+
if (!comb->has_reference()) {
|
|
433
|
+
Position sel_source_position = source_position;
|
|
434
|
+
Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_source_position);
|
|
435
|
+
Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_source_position);
|
|
436
|
+
(*ref_wrap) << ref;
|
|
437
|
+
if (!comb->head()) {
|
|
438
|
+
comb->head(ref_wrap);
|
|
439
|
+
comb->has_reference(true);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
comb = new (ctx.mem) Complex_Selector(path, sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb);
|
|
443
|
+
comb->has_reference(true);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
(*group) << comb;
|
|
447
|
+
}
|
|
448
|
+
while (lex< one_plus< sequence< spaces_and_comments, exactly<','> > > >());
|
|
449
|
+
while (lex< optional >()); // JMA - ignore optional flag if it follows the selector group
|
|
450
|
+
return group;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
Complex_Selector* Parser::parse_selector_combination()
|
|
454
|
+
{
|
|
455
|
+
Position sel_source_position = Position();
|
|
456
|
+
Compound_Selector* lhs;
|
|
457
|
+
if (peek< exactly<'+'> >() ||
|
|
458
|
+
peek< exactly<'~'> >() ||
|
|
459
|
+
peek< exactly<'>'> >()) {
|
|
460
|
+
// no selector before the combinator
|
|
461
|
+
lhs = 0;
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
lhs = parse_simple_selector_sequence();
|
|
465
|
+
sel_source_position = source_position;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
Complex_Selector::Combinator cmb;
|
|
469
|
+
if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;
|
|
470
|
+
else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;
|
|
471
|
+
else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;
|
|
472
|
+
else cmb = Complex_Selector::ANCESTOR_OF;
|
|
473
|
+
|
|
474
|
+
Complex_Selector* rhs;
|
|
475
|
+
if (peek< exactly<','> >() ||
|
|
476
|
+
peek< exactly<')'> >() ||
|
|
477
|
+
peek< exactly<'{'> >() ||
|
|
478
|
+
peek< exactly<'}'> >() ||
|
|
479
|
+
peek< exactly<';'> >() ||
|
|
480
|
+
peek< optional >()) {
|
|
481
|
+
// no selector after the combinator
|
|
482
|
+
rhs = 0;
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
rhs = parse_selector_combination();
|
|
486
|
+
sel_source_position = source_position;
|
|
487
|
+
}
|
|
488
|
+
if (!sel_source_position.line) sel_source_position = source_position;
|
|
489
|
+
return new (ctx.mem) Complex_Selector(path, sel_source_position, cmb, lhs, rhs);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
Compound_Selector* Parser::parse_simple_selector_sequence()
|
|
493
|
+
{
|
|
494
|
+
Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, source_position);
|
|
495
|
+
bool sawsomething = false;
|
|
496
|
+
if (lex< exactly<'&'> >()) {
|
|
497
|
+
// if you see a &
|
|
498
|
+
(*seq) << new (ctx.mem) Selector_Reference(path, source_position);
|
|
499
|
+
sawsomething = true;
|
|
500
|
+
// if you see a space after a &, then you're done
|
|
501
|
+
if(lex< spaces >()) {
|
|
502
|
+
return seq;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (sawsomething && lex< sequence< negate< functional >, alternatives< identifier_fragment, universal, string_constant, dimension, percentage, number > > >()) {
|
|
506
|
+
// saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
|
|
507
|
+
(*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
|
|
508
|
+
} else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, string_constant, dimension, percentage, number > > >()) {
|
|
509
|
+
// if you see a type selector
|
|
510
|
+
(*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed);
|
|
511
|
+
sawsomething = true;
|
|
512
|
+
}
|
|
513
|
+
if (!sawsomething) {
|
|
514
|
+
// don't blindly do this if you saw a & or selector
|
|
515
|
+
(*seq) << parse_simple_selector();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
while (!peek< spaces >(position) &&
|
|
519
|
+
!(peek < exactly<'+'> >(position) ||
|
|
520
|
+
peek < exactly<'~'> >(position) ||
|
|
521
|
+
peek < exactly<'>'> >(position) ||
|
|
522
|
+
peek < exactly<','> >(position) ||
|
|
523
|
+
peek < exactly<')'> >(position) ||
|
|
524
|
+
peek < exactly<'{'> >(position) ||
|
|
525
|
+
peek < exactly<'}'> >(position) ||
|
|
526
|
+
peek < exactly<';'> >(position))) {
|
|
527
|
+
(*seq) << parse_simple_selector();
|
|
528
|
+
}
|
|
529
|
+
return seq;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
Simple_Selector* Parser::parse_simple_selector()
|
|
533
|
+
{
|
|
534
|
+
if (lex< id_name >() || lex< class_name >()) {
|
|
535
|
+
return new (ctx.mem) Selector_Qualifier(path, source_position, lexed);
|
|
536
|
+
}
|
|
537
|
+
else if (lex< string_constant >() || lex< number >()) {
|
|
538
|
+
return new (ctx.mem) Type_Selector(path, source_position, lexed);
|
|
539
|
+
}
|
|
540
|
+
else if (peek< pseudo_not >()) {
|
|
541
|
+
return parse_negated_selector();
|
|
542
|
+
}
|
|
543
|
+
else if (peek< exactly<':'> >(position) || peek< functional >()) {
|
|
544
|
+
return parse_pseudo_selector();
|
|
545
|
+
}
|
|
546
|
+
else if (peek< exactly<'['> >(position)) {
|
|
547
|
+
return parse_attribute_selector();
|
|
548
|
+
}
|
|
549
|
+
else if (lex< placeholder >()) {
|
|
550
|
+
return new (ctx.mem) Selector_Placeholder(path, source_position, lexed);
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
error("invalid selector after " + lexed.to_string());
|
|
554
|
+
}
|
|
555
|
+
// unreachable statement
|
|
556
|
+
return 0;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
Wrapped_Selector* Parser::parse_negated_selector()
|
|
560
|
+
{
|
|
561
|
+
lex< pseudo_not >();
|
|
562
|
+
string name(lexed);
|
|
563
|
+
Position nsource_position = source_position;
|
|
564
|
+
Selector* negated = parse_selector_group();
|
|
565
|
+
if (!lex< exactly<')'> >()) {
|
|
566
|
+
error("negated selector is missing ')'");
|
|
567
|
+
}
|
|
568
|
+
return new (ctx.mem) Wrapped_Selector(path, nsource_position, name, negated);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
Simple_Selector* Parser::parse_pseudo_selector() {
|
|
572
|
+
if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) {
|
|
573
|
+
string name(lexed);
|
|
574
|
+
String* expr = 0;
|
|
575
|
+
Position p = source_position;
|
|
576
|
+
Selector* wrapped = 0;
|
|
577
|
+
if (lex< alternatives< even, odd > >()) {
|
|
578
|
+
expr = new (ctx.mem) String_Constant(path, p, lexed);
|
|
579
|
+
}
|
|
580
|
+
else if (peek< binomial >(position)) {
|
|
581
|
+
lex< sequence< optional< coefficient >, exactly<'n'> > >();
|
|
582
|
+
String_Constant* var_coef = new (ctx.mem) String_Constant(path, p, lexed);
|
|
583
|
+
lex< sign >();
|
|
584
|
+
String_Constant* op = new (ctx.mem) String_Constant(path, p, lexed);
|
|
585
|
+
// Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
|
|
586
|
+
lex< digits >();
|
|
587
|
+
String_Constant* constant = new (ctx.mem) String_Constant(path, p, lexed);
|
|
588
|
+
// expr = new (ctx.mem) Binary_Expression(path, p, op, var_coef, constant);
|
|
589
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, p, 3);
|
|
590
|
+
*schema << var_coef << op << constant;
|
|
591
|
+
expr = schema;
|
|
592
|
+
}
|
|
593
|
+
else if (peek< sequence< optional<sign>,
|
|
594
|
+
optional<digits>,
|
|
595
|
+
exactly<'n'>,
|
|
596
|
+
spaces_and_comments,
|
|
597
|
+
exactly<')'> > >()) {
|
|
598
|
+
lex< sequence< optional<sign>,
|
|
599
|
+
optional<digits>,
|
|
600
|
+
exactly<'n'> > >();
|
|
601
|
+
expr = new (ctx.mem) String_Constant(path, p, lexed);
|
|
602
|
+
}
|
|
603
|
+
else if (lex< sequence< optional<sign>, digits > >()) {
|
|
604
|
+
expr = new (ctx.mem) String_Constant(path, p, lexed);
|
|
605
|
+
}
|
|
606
|
+
else if (peek< sequence< identifier, spaces_and_comments, exactly<')'> > >()) {
|
|
607
|
+
lex< identifier >();
|
|
608
|
+
expr = new (ctx.mem) String_Constant(path, p, lexed);
|
|
609
|
+
}
|
|
610
|
+
else if (lex< string_constant >()) {
|
|
611
|
+
expr = new (ctx.mem) String_Constant(path, p, lexed);
|
|
612
|
+
}
|
|
613
|
+
else if (peek< exactly<')'> >()) {
|
|
614
|
+
expr = new (ctx.mem) String_Constant(path, p, "");
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
wrapped = parse_selector_group();
|
|
618
|
+
}
|
|
619
|
+
if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)");
|
|
620
|
+
if (wrapped) {
|
|
621
|
+
return new (ctx.mem) Wrapped_Selector(path, p, name, wrapped);
|
|
622
|
+
}
|
|
623
|
+
return new (ctx.mem) Pseudo_Selector(path, p, name, expr);
|
|
624
|
+
}
|
|
625
|
+
else if (lex < sequence< pseudo_prefix, identifier > >()) {
|
|
626
|
+
return new (ctx.mem) Pseudo_Selector(path, source_position, lexed);
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
error("unrecognized pseudo-class or pseudo-element");
|
|
630
|
+
}
|
|
631
|
+
// unreachable statement
|
|
632
|
+
return 0;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
Attribute_Selector* Parser::parse_attribute_selector()
|
|
636
|
+
{
|
|
637
|
+
lex< exactly<'['> >();
|
|
638
|
+
Position p = source_position;
|
|
639
|
+
if (!lex< attribute_name >()) error("invalid attribute name in attribute selector");
|
|
640
|
+
string name(lexed);
|
|
641
|
+
if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, p, name, "", 0);
|
|
642
|
+
if (!lex< alternatives< exact_match, class_match, dash_match,
|
|
643
|
+
prefix_match, suffix_match, substring_match > >()) {
|
|
644
|
+
error("invalid operator in attribute selector for " + name);
|
|
645
|
+
}
|
|
646
|
+
string matcher(lexed);
|
|
647
|
+
|
|
648
|
+
String* value = 0;
|
|
649
|
+
if (lex< identifier >()) {
|
|
650
|
+
value = new (ctx.mem) String_Constant(path, p, lexed, true);
|
|
651
|
+
}
|
|
652
|
+
else if (lex< string_constant >()) {
|
|
653
|
+
value = parse_interpolated_chunk(lexed);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
error("expected a string constant or identifier in attribute selector for " + name);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name);
|
|
660
|
+
return new (ctx.mem) Attribute_Selector(path, p, name, matcher, value);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
Block* Parser::parse_block()
|
|
664
|
+
{
|
|
665
|
+
lex< exactly<'{'> >();
|
|
666
|
+
bool semicolon = false;
|
|
667
|
+
Selector_Lookahead lookahead_result;
|
|
668
|
+
Block* block = new (ctx.mem) Block(path, source_position);
|
|
669
|
+
|
|
670
|
+
// JMA - ensure that a block containing only block_comments is parsed
|
|
671
|
+
while (lex< block_comment >()) {
|
|
672
|
+
String* contents = parse_interpolated_chunk(lexed);
|
|
673
|
+
Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
|
|
674
|
+
(*block) << comment;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
while (!lex< exactly<'}'> >()) {
|
|
678
|
+
if (semicolon) {
|
|
679
|
+
if (!lex< one_plus< exactly<';'> > >()) {
|
|
680
|
+
error("non-terminal statement or declaration must end with ';'");
|
|
681
|
+
}
|
|
682
|
+
semicolon = false;
|
|
683
|
+
while (lex< block_comment >()) {
|
|
684
|
+
String* contents = parse_interpolated_chunk(lexed);
|
|
685
|
+
Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
|
|
686
|
+
(*block) << comment;
|
|
687
|
+
}
|
|
688
|
+
if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;
|
|
689
|
+
}
|
|
690
|
+
if (lex< block_comment >()) {
|
|
691
|
+
String* contents = parse_interpolated_chunk(lexed);
|
|
692
|
+
Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
|
|
693
|
+
(*block) << comment;
|
|
694
|
+
}
|
|
695
|
+
else if (peek< import >(position)) {
|
|
696
|
+
if (stack.back() == mixin_def || stack.back() == function_def) {
|
|
697
|
+
lex< import >(); // to adjust the source_position number
|
|
698
|
+
error("@import directives are not allowed inside mixins and functions");
|
|
699
|
+
}
|
|
700
|
+
Import* imp = parse_import();
|
|
701
|
+
if (!imp->urls().empty()) (*block) << imp;
|
|
702
|
+
if (!imp->files().empty()) {
|
|
703
|
+
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
|
|
704
|
+
(*block) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
semicolon = true;
|
|
708
|
+
}
|
|
709
|
+
else if (lex< variable >()) {
|
|
710
|
+
(*block) << parse_assignment();
|
|
711
|
+
semicolon = true;
|
|
712
|
+
}
|
|
713
|
+
else if (peek< if_directive >()) {
|
|
714
|
+
(*block) << parse_if_directive();
|
|
715
|
+
}
|
|
716
|
+
else if (peek< for_directive >()) {
|
|
717
|
+
(*block) << parse_for_directive();
|
|
718
|
+
}
|
|
719
|
+
else if (peek< each_directive >()) {
|
|
720
|
+
(*block) << parse_each_directive();
|
|
721
|
+
}
|
|
722
|
+
else if (peek < while_directive >()) {
|
|
723
|
+
(*block) << parse_while_directive();
|
|
724
|
+
}
|
|
725
|
+
else if (lex < return_directive >()) {
|
|
726
|
+
(*block) << new (ctx.mem) Return(path, source_position, parse_list());
|
|
727
|
+
semicolon = true;
|
|
728
|
+
}
|
|
729
|
+
else if (peek< warn >()) {
|
|
730
|
+
(*block) << parse_warning();
|
|
731
|
+
semicolon = true;
|
|
732
|
+
}
|
|
733
|
+
else if (peek< err >()) {
|
|
734
|
+
(*block) << parse_error();
|
|
735
|
+
semicolon = true;
|
|
736
|
+
}
|
|
737
|
+
else if (peek< dbg >()) {
|
|
738
|
+
(*block) << parse_debug();
|
|
739
|
+
semicolon = true;
|
|
740
|
+
}
|
|
741
|
+
else if (stack.back() == function_def) {
|
|
742
|
+
error("only variable declarations and control directives are allowed inside functions");
|
|
743
|
+
}
|
|
744
|
+
else if (peek< mixin >() || peek< function >()) {
|
|
745
|
+
(*block) << parse_definition();
|
|
746
|
+
}
|
|
747
|
+
else if (peek< include >(position)) {
|
|
748
|
+
Mixin_Call* the_call = parse_mixin_call();
|
|
749
|
+
(*block) << the_call;
|
|
750
|
+
// don't need a semicolon after a content block
|
|
751
|
+
semicolon = (the_call->block()) ? false : true;
|
|
752
|
+
}
|
|
753
|
+
else if (lex< content >()) {
|
|
754
|
+
if (stack.back() != mixin_def) {
|
|
755
|
+
error("@content may only be used within a mixin");
|
|
756
|
+
}
|
|
757
|
+
(*block) << new (ctx.mem) Content(path, source_position);
|
|
758
|
+
semicolon = true;
|
|
759
|
+
}
|
|
760
|
+
/*
|
|
761
|
+
else if (peek< exactly<'+'> >()) {
|
|
762
|
+
(*block) << parse_mixin_call();
|
|
763
|
+
semicolon = true;
|
|
764
|
+
}
|
|
765
|
+
*/
|
|
766
|
+
else if (lex< extend >()) {
|
|
767
|
+
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
|
|
768
|
+
if (!lookahead.found) error("invalid selector for @extend");
|
|
769
|
+
Selector* target;
|
|
770
|
+
if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
|
|
771
|
+
else target = parse_selector_group();
|
|
772
|
+
(*block) << new (ctx.mem) Extension(path, source_position, target);
|
|
773
|
+
semicolon = true;
|
|
774
|
+
}
|
|
775
|
+
else if (peek< media >()) {
|
|
776
|
+
(*block) << parse_media_block();
|
|
777
|
+
}
|
|
778
|
+
else if (peek< supports >()) {
|
|
779
|
+
(*block) << parse_feature_block();
|
|
780
|
+
}
|
|
781
|
+
// ignore the @charset directive for now
|
|
782
|
+
else if (lex< exactly< charset_kwd > >()) {
|
|
783
|
+
lex< string_constant >();
|
|
784
|
+
lex< one_plus< exactly<';'> > >();
|
|
785
|
+
}
|
|
786
|
+
else if (peek< at_keyword >()) {
|
|
787
|
+
At_Rule* at_rule = parse_at_rule();
|
|
788
|
+
(*block) << at_rule;
|
|
789
|
+
if (!at_rule->block()) semicolon = true;
|
|
790
|
+
}
|
|
791
|
+
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
|
792
|
+
(*block) << parse_ruleset(lookahead_result);
|
|
793
|
+
}
|
|
794
|
+
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
|
|
795
|
+
(*block) << parse_propset();
|
|
796
|
+
}
|
|
797
|
+
else if (!peek< exactly<';'> >()) {
|
|
798
|
+
if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {
|
|
799
|
+
(*block) << parse_propset();
|
|
800
|
+
}
|
|
801
|
+
else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {
|
|
802
|
+
(*block) << parse_propset();
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
Declaration* decl = parse_declaration();
|
|
806
|
+
(*block) << decl;
|
|
807
|
+
if (peek< exactly<'{'> >()) {
|
|
808
|
+
// parse a propset that rides on the declaration's property
|
|
809
|
+
Propset* ps = new (ctx.mem) Propset(path, source_position, decl->property(), parse_block());
|
|
810
|
+
(*block) << ps;
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
// finish and let the semicolon get munched
|
|
814
|
+
semicolon = true;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
else lex< one_plus< exactly<';'> > >();
|
|
819
|
+
while (lex< block_comment >()) {
|
|
820
|
+
String* contents = parse_interpolated_chunk(lexed);
|
|
821
|
+
Comment* comment = new (ctx.mem) Comment(path, source_position, contents);
|
|
822
|
+
(*block) << comment;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return block;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
Declaration* Parser::parse_declaration() {
|
|
829
|
+
String* prop = 0;
|
|
830
|
+
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
|
|
831
|
+
prop = parse_identifier_schema();
|
|
832
|
+
}
|
|
833
|
+
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
|
|
834
|
+
prop = new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
835
|
+
}
|
|
836
|
+
else if (lex< custom_property_name >()) {
|
|
837
|
+
prop = new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
error("invalid property name");
|
|
841
|
+
}
|
|
842
|
+
if (!lex< one_plus< exactly<':'> > >()) error("property \"" + string(lexed) + "\" must be followed by a ':'");
|
|
843
|
+
if (peek< exactly<';'> >()) error("style declaration must contain a value");
|
|
844
|
+
if (peek< static_value >()) {
|
|
845
|
+
return new (ctx.mem) Declaration(path, prop->position(), prop, parse_static_value()/*, lex<important>()*/);
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
return new (ctx.mem) Declaration(path, prop->position(), prop, parse_list()/*, lex<important>()*/);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
Expression* Parser::parse_map()
|
|
853
|
+
{
|
|
854
|
+
To_String to_string;
|
|
855
|
+
Expression* key = parse_list();
|
|
856
|
+
|
|
857
|
+
// it's not a map so return the lexed value as a list value
|
|
858
|
+
if (!peek< exactly<':'> >())
|
|
859
|
+
{ return key; }
|
|
860
|
+
|
|
861
|
+
lex< exactly<':'> >();
|
|
862
|
+
|
|
863
|
+
Expression* value = parse_space_list();
|
|
864
|
+
|
|
865
|
+
Map* map = new (ctx.mem) Map(path, source_position, 1);
|
|
866
|
+
(*map) << make_pair(key, value);
|
|
867
|
+
|
|
868
|
+
while (lex< exactly<','> >())
|
|
869
|
+
{
|
|
870
|
+
// allow trailing commas - #495
|
|
871
|
+
if (peek< exactly<')'> >(position))
|
|
872
|
+
{ break; }
|
|
873
|
+
|
|
874
|
+
Expression* key = parse_list();
|
|
875
|
+
|
|
876
|
+
if (!(lex< exactly<':'> >()))
|
|
877
|
+
{ error("invalid syntax"); }
|
|
878
|
+
|
|
879
|
+
Expression* value = parse_space_list();
|
|
880
|
+
|
|
881
|
+
(*map) << make_pair(key, value);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (map->has_duplicate_key())
|
|
885
|
+
{ error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + "."); }
|
|
886
|
+
|
|
887
|
+
return map;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
Expression* Parser::parse_list()
|
|
891
|
+
{
|
|
892
|
+
return parse_comma_list();
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
Expression* Parser::parse_comma_list()
|
|
896
|
+
{
|
|
897
|
+
if (//peek< exactly<'!'> >(position) ||
|
|
898
|
+
peek< exactly<';'> >(position) ||
|
|
899
|
+
peek< exactly<'}'> >(position) ||
|
|
900
|
+
peek< exactly<'{'> >(position) ||
|
|
901
|
+
peek< exactly<')'> >(position) ||
|
|
902
|
+
//peek< exactly<':'> >(position) ||
|
|
903
|
+
peek< exactly<ellipsis> >(position))
|
|
904
|
+
{ return new (ctx.mem) List(path, source_position, 0); }
|
|
905
|
+
Expression* list1 = parse_space_list();
|
|
906
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
907
|
+
if (!peek< exactly<','> >(position)) return list1;
|
|
908
|
+
|
|
909
|
+
List* comma_list = new (ctx.mem) List(path, source_position, 2, List::COMMA);
|
|
910
|
+
(*comma_list) << list1;
|
|
911
|
+
|
|
912
|
+
while (lex< exactly<','> >())
|
|
913
|
+
{
|
|
914
|
+
if (//peek< exactly<'!'> >(position) ||
|
|
915
|
+
peek< exactly<';'> >(position) ||
|
|
916
|
+
peek< exactly<'}'> >(position) ||
|
|
917
|
+
peek< exactly<'{'> >(position) ||
|
|
918
|
+
peek< exactly<')'> >(position) ||
|
|
919
|
+
peek< exactly<':'> >(position) ||
|
|
920
|
+
peek< exactly<ellipsis> >(position)) {
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
Expression* list = parse_space_list();
|
|
924
|
+
(*comma_list) << list;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
return comma_list;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
Expression* Parser::parse_space_list()
|
|
931
|
+
{
|
|
932
|
+
Expression* disj1 = parse_disjunction();
|
|
933
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
934
|
+
if (//peek< exactly<'!'> >(position) ||
|
|
935
|
+
peek< exactly<';'> >(position) ||
|
|
936
|
+
peek< exactly<'}'> >(position) ||
|
|
937
|
+
peek< exactly<'{'> >(position) ||
|
|
938
|
+
peek< exactly<')'> >(position) ||
|
|
939
|
+
peek< exactly<','> >(position) ||
|
|
940
|
+
peek< exactly<':'> >(position) ||
|
|
941
|
+
peek< exactly<ellipsis> >(position) ||
|
|
942
|
+
peek< default_flag >(position) ||
|
|
943
|
+
peek< global_flag >(position))
|
|
944
|
+
{ return disj1; }
|
|
945
|
+
|
|
946
|
+
List* space_list = new (ctx.mem) List(path, source_position, 2, List::SPACE);
|
|
947
|
+
(*space_list) << disj1;
|
|
948
|
+
|
|
949
|
+
while (!(//peek< exactly<'!'> >(position) ||
|
|
950
|
+
peek< exactly<';'> >(position) ||
|
|
951
|
+
peek< exactly<'}'> >(position) ||
|
|
952
|
+
peek< exactly<'{'> >(position) ||
|
|
953
|
+
peek< exactly<')'> >(position) ||
|
|
954
|
+
peek< exactly<','> >(position) ||
|
|
955
|
+
peek< exactly<':'> >(position) ||
|
|
956
|
+
peek< exactly<ellipsis> >(position) ||
|
|
957
|
+
peek< default_flag >(position) ||
|
|
958
|
+
peek< global_flag >(position)))
|
|
959
|
+
{
|
|
960
|
+
(*space_list) << parse_disjunction();
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
return space_list;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
Expression* Parser::parse_disjunction()
|
|
967
|
+
{
|
|
968
|
+
Expression* conj1 = parse_conjunction();
|
|
969
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
970
|
+
if (!peek< sequence< or_op, negate< identifier > > >()) return conj1;
|
|
971
|
+
|
|
972
|
+
vector<Expression*> operands;
|
|
973
|
+
while (lex< sequence< or_op, negate< identifier > > >())
|
|
974
|
+
operands.push_back(parse_conjunction());
|
|
975
|
+
|
|
976
|
+
return fold_operands(conj1, operands, Binary_Expression::OR);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
Expression* Parser::parse_conjunction()
|
|
980
|
+
{
|
|
981
|
+
Expression* rel1 = parse_relation();
|
|
982
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
983
|
+
if (!peek< sequence< and_op, negate< identifier > > >()) return rel1;
|
|
984
|
+
|
|
985
|
+
vector<Expression*> operands;
|
|
986
|
+
while (lex< sequence< and_op, negate< identifier > > >())
|
|
987
|
+
operands.push_back(parse_relation());
|
|
988
|
+
|
|
989
|
+
return fold_operands(rel1, operands, Binary_Expression::AND);
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
Expression* Parser::parse_relation()
|
|
993
|
+
{
|
|
994
|
+
Expression* expr1 = parse_expression();
|
|
995
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
996
|
+
if (!(peek< eq_op >(position) ||
|
|
997
|
+
peek< neq_op >(position) ||
|
|
998
|
+
peek< gte_op >(position) ||
|
|
999
|
+
peek< gt_op >(position) ||
|
|
1000
|
+
peek< lte_op >(position) ||
|
|
1001
|
+
peek< lt_op >(position)))
|
|
1002
|
+
{ return expr1; }
|
|
1003
|
+
|
|
1004
|
+
Binary_Expression::Type op
|
|
1005
|
+
= lex<eq_op>() ? Binary_Expression::EQ
|
|
1006
|
+
: lex<neq_op>() ? Binary_Expression::NEQ
|
|
1007
|
+
: lex<gte_op>() ? Binary_Expression::GTE
|
|
1008
|
+
: lex<lte_op>() ? Binary_Expression::LTE
|
|
1009
|
+
: lex<gt_op>() ? Binary_Expression::GT
|
|
1010
|
+
: lex<lt_op>() ? Binary_Expression::LT
|
|
1011
|
+
: Binary_Expression::LT; // whatever
|
|
1012
|
+
|
|
1013
|
+
Expression* expr2 = parse_expression();
|
|
1014
|
+
|
|
1015
|
+
return new (ctx.mem) Binary_Expression(path, expr1->position(), op, expr1, expr2);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
Expression* Parser::parse_expression()
|
|
1019
|
+
{
|
|
1020
|
+
Expression* term1 = parse_term();
|
|
1021
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
1022
|
+
if (!(peek< exactly<'+'> >(position) ||
|
|
1023
|
+
peek< sequence< negate< number >, exactly<'-'> > >(position)) ||
|
|
1024
|
+
peek< identifier >(position))
|
|
1025
|
+
{ return term1; }
|
|
1026
|
+
|
|
1027
|
+
vector<Expression*> operands;
|
|
1028
|
+
vector<Binary_Expression::Type> operators;
|
|
1029
|
+
while (lex< exactly<'+'> >() || lex< sequence< negate< number >, exactly<'-'> > >()) {
|
|
1030
|
+
operators.push_back(lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
|
|
1031
|
+
operands.push_back(parse_term());
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
return fold_operands(term1, operands, operators);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
Expression* Parser::parse_term()
|
|
1038
|
+
{
|
|
1039
|
+
Expression* fact1 = parse_factor();
|
|
1040
|
+
|
|
1041
|
+
// Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
|
|
1042
|
+
if (peek< exactly<'%'> >(position) && fact1->concrete_type() == Expression::STRING) {
|
|
1043
|
+
try {
|
|
1044
|
+
String_Schema* ss = dynamic_cast<String_Schema*>(fact1);
|
|
1045
|
+
if (ss->has_interpolants()) return fact1;
|
|
1046
|
+
}
|
|
1047
|
+
catch (bad_cast&) {}
|
|
1048
|
+
catch (...) { throw; }
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// if it's a singleton, return it directly; don't wrap it
|
|
1052
|
+
if (!(peek< exactly<'*'> >(position) ||
|
|
1053
|
+
peek< exactly<'/'> >(position) ||
|
|
1054
|
+
peek< exactly<'%'> >(position)))
|
|
1055
|
+
{ return fact1; }
|
|
1056
|
+
|
|
1057
|
+
vector<Expression*> operands;
|
|
1058
|
+
vector<Binary_Expression::Type> operators;
|
|
1059
|
+
while (lex< exactly<'*'> >() || lex< exactly<'/'> >() || lex< exactly<'%'> >()) {
|
|
1060
|
+
if (lexed == "*") operators.push_back(Binary_Expression::MUL);
|
|
1061
|
+
else if (lexed == "/") operators.push_back(Binary_Expression::DIV);
|
|
1062
|
+
else operators.push_back(Binary_Expression::MOD);
|
|
1063
|
+
operands.push_back(parse_factor());
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
return fold_operands(fact1, operands, operators);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
Expression* Parser::parse_factor()
|
|
1070
|
+
{
|
|
1071
|
+
if (lex< exactly<'('> >()) {
|
|
1072
|
+
Expression* value = parse_map();
|
|
1073
|
+
if (!lex< exactly<')'> >()) error("unclosed parenthesis");
|
|
1074
|
+
value->is_delayed(false);
|
|
1075
|
+
// make sure wrapped lists and division expressions are non-delayed within parentheses
|
|
1076
|
+
if (value->concrete_type() == Expression::LIST) {
|
|
1077
|
+
List* l = static_cast<List*>(value);
|
|
1078
|
+
if (!l->empty()) (*l)[0]->is_delayed(false);
|
|
1079
|
+
} else if (typeid(*value) == typeid(Binary_Expression)) {
|
|
1080
|
+
Binary_Expression* b = static_cast<Binary_Expression*>(value);
|
|
1081
|
+
Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
|
|
1082
|
+
if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false);
|
|
1083
|
+
}
|
|
1084
|
+
return value;
|
|
1085
|
+
}
|
|
1086
|
+
else if (peek< ie_property >()) {
|
|
1087
|
+
return parse_ie_property();
|
|
1088
|
+
}
|
|
1089
|
+
else if (peek< ie_keyword_arg >()) {
|
|
1090
|
+
return parse_ie_keyword_arg();
|
|
1091
|
+
}
|
|
1092
|
+
else if (peek< exactly< calc_kwd > >() ||
|
|
1093
|
+
peek< exactly< moz_calc_kwd > >() ||
|
|
1094
|
+
peek< exactly< webkit_calc_kwd > >()) {
|
|
1095
|
+
return parse_calc_function();
|
|
1096
|
+
}
|
|
1097
|
+
else if (peek< functional_schema >()) {
|
|
1098
|
+
return parse_function_call_schema();
|
|
1099
|
+
}
|
|
1100
|
+
else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
|
|
1101
|
+
return parse_identifier_schema();
|
|
1102
|
+
}
|
|
1103
|
+
else if (peek< functional >() && !peek< uri_prefix >()) {
|
|
1104
|
+
return parse_function_call();
|
|
1105
|
+
}
|
|
1106
|
+
else if (lex< sequence< exactly<'+'>, spaces_and_comments, negate< number > > >()) {
|
|
1107
|
+
return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::PLUS, parse_factor());
|
|
1108
|
+
}
|
|
1109
|
+
else if (lex< sequence< exactly<'-'>, spaces_and_comments, negate< number> > >()) {
|
|
1110
|
+
return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::MINUS, parse_factor());
|
|
1111
|
+
}
|
|
1112
|
+
else if (lex< sequence< not_op, spaces_and_comments > >()) {
|
|
1113
|
+
return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::NOT, parse_factor());
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
return parse_value();
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
Expression* Parser::parse_value()
|
|
1121
|
+
{
|
|
1122
|
+
if (lex< uri_prefix >()) {
|
|
1123
|
+
Arguments* args = new (ctx.mem) Arguments(path, source_position);
|
|
1124
|
+
Function_Call* result = new (ctx.mem) Function_Call(path, source_position, "url", args);
|
|
1125
|
+
const char* here = position;
|
|
1126
|
+
Position here_p = source_position;
|
|
1127
|
+
// Try to parse a SassScript expression. If it succeeds and we can munch
|
|
1128
|
+
// a matching rparen, then that's our url. If we can't munch a matching
|
|
1129
|
+
// rparen, or if the attempt to parse an expression fails, then try to
|
|
1130
|
+
// munch a regular CSS url.
|
|
1131
|
+
try {
|
|
1132
|
+
// special case -- if there's a comment, treat it as part of a URL
|
|
1133
|
+
lex<spaces>();
|
|
1134
|
+
if (peek<line_comment_prefix>() || peek<block_comment_prefix>()) error("comment in URL"); // doesn't really matter what we throw
|
|
1135
|
+
Expression* expr = parse_list();
|
|
1136
|
+
if (!lex< exactly<')'> >()) error("dangling expression in URL"); // doesn't really matter what we throw
|
|
1137
|
+
Argument* arg = new (ctx.mem) Argument(path, expr->position(), expr);
|
|
1138
|
+
*args << arg;
|
|
1139
|
+
return result;
|
|
1140
|
+
}
|
|
1141
|
+
catch (Sass_Error&) {
|
|
1142
|
+
// back up so we can try again
|
|
1143
|
+
position = here;
|
|
1144
|
+
source_position = here_p;
|
|
1145
|
+
}
|
|
1146
|
+
catch (...) { throw; }
|
|
1147
|
+
lex< spaces >();
|
|
1148
|
+
if (lex< url >()) {
|
|
1149
|
+
String* the_url = parse_interpolated_chunk(lexed);
|
|
1150
|
+
Argument* arg = new (ctx.mem) Argument(path, the_url->position(), the_url);
|
|
1151
|
+
*args << arg;
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
error("malformed URL");
|
|
1155
|
+
}
|
|
1156
|
+
if (!lex< exactly<')'> >()) error("URI is missing ')'");
|
|
1157
|
+
return result;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (lex< important >())
|
|
1161
|
+
{ return new (ctx.mem) String_Constant(path, source_position, "!important"); }
|
|
1162
|
+
|
|
1163
|
+
if (lex< value_schema >())
|
|
1164
|
+
{ return Parser::from_token(lexed, ctx, path, source_position).parse_value_schema(); }
|
|
1165
|
+
|
|
1166
|
+
if (lex< sequence< true_val, negate< identifier > > >())
|
|
1167
|
+
{ return new (ctx.mem) Boolean(path, source_position, true); }
|
|
1168
|
+
|
|
1169
|
+
if (lex< sequence< false_val, negate< identifier > > >())
|
|
1170
|
+
{ return new (ctx.mem) Boolean(path, source_position, false); }
|
|
1171
|
+
|
|
1172
|
+
if (lex< sequence< null, negate< identifier > > >())
|
|
1173
|
+
{ return new (ctx.mem) Null(path, source_position); }
|
|
1174
|
+
|
|
1175
|
+
if (lex< identifier >()) {
|
|
1176
|
+
String_Constant* str = new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1177
|
+
// Dont' delay this string if it is a name color. Fixes #652.
|
|
1178
|
+
str->is_delayed(ctx.names_to_colors.count(lexed) == 0);
|
|
1179
|
+
return str;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (lex< percentage >())
|
|
1183
|
+
{ return new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); }
|
|
1184
|
+
|
|
1185
|
+
if (lex< dimension >())
|
|
1186
|
+
{ return new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); }
|
|
1187
|
+
|
|
1188
|
+
if (lex< number >())
|
|
1189
|
+
{ return new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); }
|
|
1190
|
+
|
|
1191
|
+
if (lex< hex >())
|
|
1192
|
+
{ return new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); }
|
|
1193
|
+
|
|
1194
|
+
if (peek< string_constant >())
|
|
1195
|
+
{ return parse_string(); }
|
|
1196
|
+
|
|
1197
|
+
if (lex< variable >())
|
|
1198
|
+
{ return new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); }
|
|
1199
|
+
|
|
1200
|
+
// Special case handling for `%` proceeding an interpolant.
|
|
1201
|
+
if (lex< sequence< exactly<'%'>, optional< percentage > > >())
|
|
1202
|
+
{ return new (ctx.mem) String_Constant(path, source_position, lexed); }
|
|
1203
|
+
|
|
1204
|
+
error("error reading values after " + lexed.to_string());
|
|
1205
|
+
|
|
1206
|
+
// unreachable statement
|
|
1207
|
+
return 0;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
String* Parser::parse_interpolated_chunk(Token chunk)
|
|
1211
|
+
{
|
|
1212
|
+
const char* i = chunk.begin;
|
|
1213
|
+
// see if there any interpolants
|
|
1214
|
+
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(chunk.begin, chunk.end);
|
|
1215
|
+
if (!p) {
|
|
1216
|
+
String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, chunk, dequote);
|
|
1217
|
+
str_node->is_delayed(true);
|
|
1218
|
+
return str_node;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1222
|
+
schema->quote_mark(*chunk.begin);
|
|
1223
|
+
while (i < chunk.end) {
|
|
1224
|
+
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, chunk.end);
|
|
1225
|
+
if (p) {
|
|
1226
|
+
if (i < p) {
|
|
1227
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
|
|
1228
|
+
}
|
|
1229
|
+
const char* j = find_first_in_interval< exactly<rbrace> >(p, chunk.end); // find the closing brace
|
|
1230
|
+
if (j) {
|
|
1231
|
+
// parse the interpolant and accumulate it
|
|
1232
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
|
|
1233
|
+
interp_node->is_interpolant(true);
|
|
1234
|
+
(*schema) << interp_node;
|
|
1235
|
+
i = j+1;
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
// throw an error if the interpolant is unterminated
|
|
1239
|
+
error("unterminated interpolant inside string constant " + chunk.to_string());
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
else { // no interpolants left; add the last segment if nonempty
|
|
1243
|
+
if (i < chunk.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, chunk.end));
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return schema;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
String_Constant* Parser::parse_static_value()
|
|
1251
|
+
{
|
|
1252
|
+
lex< static_value >();
|
|
1253
|
+
Token str(lexed);
|
|
1254
|
+
--str.end;
|
|
1255
|
+
--position;
|
|
1256
|
+
String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
|
|
1257
|
+
str_node->is_delayed(true);
|
|
1258
|
+
return str_node;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
String* Parser::parse_string()
|
|
1262
|
+
{
|
|
1263
|
+
lex< string_constant >();
|
|
1264
|
+
Token str(lexed);
|
|
1265
|
+
return parse_interpolated_chunk(str);
|
|
1266
|
+
// const char* i = str.begin;
|
|
1267
|
+
// // see if there any interpolants
|
|
1268
|
+
// const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
|
|
1269
|
+
// if (!p) {
|
|
1270
|
+
// String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
|
|
1271
|
+
// str_node->is_delayed(true);
|
|
1272
|
+
// return str_node;
|
|
1273
|
+
// }
|
|
1274
|
+
|
|
1275
|
+
// String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1276
|
+
// schema->quote_mark(*str.begin);
|
|
1277
|
+
// while (i < str.end) {
|
|
1278
|
+
// p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
|
|
1279
|
+
// if (p) {
|
|
1280
|
+
// if (i < p) {
|
|
1281
|
+
// (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
|
|
1282
|
+
// }
|
|
1283
|
+
// const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
|
|
1284
|
+
// if (j) {
|
|
1285
|
+
// // parse the interpolant and accumulate it
|
|
1286
|
+
// Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
|
|
1287
|
+
// interp_node->is_interpolant(true);
|
|
1288
|
+
// (*schema) << interp_node;
|
|
1289
|
+
// i = j+1;
|
|
1290
|
+
// }
|
|
1291
|
+
// else {
|
|
1292
|
+
// // throw an error if the interpolant is unterminated
|
|
1293
|
+
// error("unterminated interpolant inside string constant " + str.to_string());
|
|
1294
|
+
// }
|
|
1295
|
+
// }
|
|
1296
|
+
// else { // no interpolants left; add the last segment if nonempty
|
|
1297
|
+
// if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
|
|
1298
|
+
// break;
|
|
1299
|
+
// }
|
|
1300
|
+
// }
|
|
1301
|
+
// return schema;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
String* Parser::parse_ie_property()
|
|
1305
|
+
{
|
|
1306
|
+
lex< ie_property >();
|
|
1307
|
+
Token str(lexed);
|
|
1308
|
+
const char* i = str.begin;
|
|
1309
|
+
// see if there any interpolants
|
|
1310
|
+
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
|
|
1311
|
+
if (!p) {
|
|
1312
|
+
String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
|
|
1313
|
+
str_node->is_delayed(true);
|
|
1314
|
+
return str_node;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1318
|
+
while (i < str.end) {
|
|
1319
|
+
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
|
|
1320
|
+
if (p) {
|
|
1321
|
+
if (i < p) {
|
|
1322
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
|
|
1323
|
+
}
|
|
1324
|
+
const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
|
|
1325
|
+
if (j) {
|
|
1326
|
+
// parse the interpolant and accumulate it
|
|
1327
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
|
|
1328
|
+
interp_node->is_interpolant(true);
|
|
1329
|
+
(*schema) << interp_node;
|
|
1330
|
+
i = j+1;
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
// throw an error if the interpolant is unterminated
|
|
1334
|
+
error("unterminated interpolant inside IE function " + str.to_string());
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
else { // no interpolants left; add the last segment if nonempty
|
|
1338
|
+
if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end));
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return schema;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
String* Parser::parse_ie_keyword_arg()
|
|
1346
|
+
{
|
|
1347
|
+
String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, source_position, 3);
|
|
1348
|
+
if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
|
|
1349
|
+
else {
|
|
1350
|
+
lex< alternatives< identifier_schema, identifier > >();
|
|
1351
|
+
*kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1352
|
+
}
|
|
1353
|
+
lex< exactly<'='> >();
|
|
1354
|
+
*kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1355
|
+
if (peek< variable >()) *kwd_arg << parse_list();
|
|
1356
|
+
else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, Util::normalize_decimals(lexed));
|
|
1357
|
+
else {
|
|
1358
|
+
lex< alternatives< identifier_schema, identifier, number, hex > >();
|
|
1359
|
+
*kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1360
|
+
}
|
|
1361
|
+
return kwd_arg;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
String_Schema* Parser::parse_value_schema()
|
|
1365
|
+
{
|
|
1366
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1367
|
+
size_t num_items = 0;
|
|
1368
|
+
while (position < end) {
|
|
1369
|
+
if (lex< interpolant >()) {
|
|
1370
|
+
Token insides(Token(lexed.begin + 2, lexed.end - 1));
|
|
1371
|
+
Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
|
|
1372
|
+
interp_node->is_interpolant(true);
|
|
1373
|
+
(*schema) << interp_node;
|
|
1374
|
+
}
|
|
1375
|
+
else if (lex< exactly<'%'> >()) {
|
|
1376
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1377
|
+
}
|
|
1378
|
+
else if (lex< identifier >()) {
|
|
1379
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1380
|
+
}
|
|
1381
|
+
else if (lex< percentage >()) {
|
|
1382
|
+
(*schema) << new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed);
|
|
1383
|
+
}
|
|
1384
|
+
else if (lex< dimension >()) {
|
|
1385
|
+
(*schema) << new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed);
|
|
1386
|
+
}
|
|
1387
|
+
else if (lex< number >()) {
|
|
1388
|
+
(*schema) << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed);
|
|
1389
|
+
}
|
|
1390
|
+
else if (lex< hex >()) {
|
|
1391
|
+
(*schema) << new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed);
|
|
1392
|
+
}
|
|
1393
|
+
else if (lex< string_constant >()) {
|
|
1394
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1395
|
+
if (!num_items) schema->quote_mark(*lexed.begin);
|
|
1396
|
+
}
|
|
1397
|
+
else if (lex< variable >()) {
|
|
1398
|
+
(*schema) << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed));
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
error("error parsing interpolated value");
|
|
1402
|
+
}
|
|
1403
|
+
++num_items;
|
|
1404
|
+
}
|
|
1405
|
+
return schema;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
String_Schema* Parser::parse_url_schema()
|
|
1409
|
+
{
|
|
1410
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1411
|
+
|
|
1412
|
+
while (position < end) {
|
|
1413
|
+
if (position[0] == '/') {
|
|
1414
|
+
lexed = Token(position, position+1);
|
|
1415
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1416
|
+
++position;
|
|
1417
|
+
}
|
|
1418
|
+
else if (lex< interpolant >()) {
|
|
1419
|
+
Token insides(Token(lexed.begin + 2, lexed.end - 1));
|
|
1420
|
+
Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
|
|
1421
|
+
interp_node->is_interpolant(true);
|
|
1422
|
+
(*schema) << interp_node;
|
|
1423
|
+
}
|
|
1424
|
+
else if (lex< sequence< identifier, exactly<':'> > >()) {
|
|
1425
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1426
|
+
}
|
|
1427
|
+
else if (lex< filename >()) {
|
|
1428
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, lexed);
|
|
1429
|
+
}
|
|
1430
|
+
else {
|
|
1431
|
+
error("error parsing interpolated url");
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return schema;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
String* Parser::parse_identifier_schema()
|
|
1438
|
+
{
|
|
1439
|
+
lex< sequence< optional< exactly<'*'> >, identifier_schema > >();
|
|
1440
|
+
Token id(lexed);
|
|
1441
|
+
const char* i = id.begin;
|
|
1442
|
+
// see if there any interpolants
|
|
1443
|
+
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(id.begin, id.end);
|
|
1444
|
+
if (!p) {
|
|
1445
|
+
return new (ctx.mem) String_Constant(path, source_position, id);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
|
|
1449
|
+
while (i < id.end) {
|
|
1450
|
+
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, id.end);
|
|
1451
|
+
if (p) {
|
|
1452
|
+
if (i < p) {
|
|
1453
|
+
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
|
|
1454
|
+
}
|
|
1455
|
+
const char* j = find_first_in_interval< exactly<rbrace> >(p, id.end); // find the closing brace
|
|
1456
|
+
if (j) {
|
|
1457
|
+
// parse the interpolant and accumulate it
|
|
1458
|
+
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
|
|
1459
|
+
interp_node->is_interpolant(true);
|
|
1460
|
+
(*schema) << interp_node;
|
|
1461
|
+
schema->has_interpolants(true);
|
|
1462
|
+
i = j+1;
|
|
1463
|
+
}
|
|
1464
|
+
else {
|
|
1465
|
+
// throw an error if the interpolant is unterminated
|
|
1466
|
+
error("unterminated interpolant inside interpolated identifier " + id.to_string());
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
else { // no interpolants left; add the last segment if nonempty
|
|
1470
|
+
if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, id.end));
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
return schema;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
Function_Call* Parser::parse_calc_function()
|
|
1478
|
+
{
|
|
1479
|
+
lex< identifier >();
|
|
1480
|
+
string name(lexed);
|
|
1481
|
+
Position call_pos = source_position;
|
|
1482
|
+
lex< exactly<'('> >();
|
|
1483
|
+
Position arg_pos = source_position;
|
|
1484
|
+
const char* arg_beg = position;
|
|
1485
|
+
parse_list();
|
|
1486
|
+
const char* arg_end = position;
|
|
1487
|
+
lex< exactly<')'> >();
|
|
1488
|
+
|
|
1489
|
+
Argument* arg = new (ctx.mem) Argument(path, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
|
|
1490
|
+
Arguments* args = new (ctx.mem) Arguments(path, arg_pos);
|
|
1491
|
+
*args << arg;
|
|
1492
|
+
return new (ctx.mem) Function_Call(path, call_pos, name, args);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
Function_Call* Parser::parse_function_call()
|
|
1496
|
+
{
|
|
1497
|
+
lex< identifier >();
|
|
1498
|
+
string name(Util::normalize_underscores(lexed));
|
|
1499
|
+
Position source_position_of_call = source_position;
|
|
1500
|
+
|
|
1501
|
+
Function_Call* the_call = new (ctx.mem) Function_Call(path, source_position_of_call, name, parse_arguments());
|
|
1502
|
+
return the_call;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
Function_Call_Schema* Parser::parse_function_call_schema()
|
|
1506
|
+
{
|
|
1507
|
+
String* name = parse_identifier_schema();
|
|
1508
|
+
Position source_position_of_call = source_position;
|
|
1509
|
+
|
|
1510
|
+
Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, source_position_of_call, name, parse_arguments());
|
|
1511
|
+
return the_call;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
If* Parser::parse_if_directive(bool else_if)
|
|
1515
|
+
{
|
|
1516
|
+
lex< if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
|
|
1517
|
+
Position if_source_position = source_position;
|
|
1518
|
+
Expression* predicate = parse_list();
|
|
1519
|
+
predicate->is_delayed(false);
|
|
1520
|
+
if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if");
|
|
1521
|
+
Block* consequent = parse_block();
|
|
1522
|
+
Block* alternative = 0;
|
|
1523
|
+
if (lex< else_directive >()) {
|
|
1524
|
+
if (peek< exactly<if_after_else_kwd> >()) {
|
|
1525
|
+
alternative = new (ctx.mem) Block(path, source_position);
|
|
1526
|
+
(*alternative) << parse_if_directive(true);
|
|
1527
|
+
}
|
|
1528
|
+
else if (!peek< exactly<'{'> >()) {
|
|
1529
|
+
error("expected '{' after @else");
|
|
1530
|
+
}
|
|
1531
|
+
else {
|
|
1532
|
+
alternative = parse_block();
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return new (ctx.mem) If(path, if_source_position, predicate, consequent, alternative);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
For* Parser::parse_for_directive()
|
|
1539
|
+
{
|
|
1540
|
+
lex< for_directive >();
|
|
1541
|
+
Position for_source_position = source_position;
|
|
1542
|
+
if (!lex< variable >()) error("@for directive requires an iteration variable");
|
|
1543
|
+
string var(Util::normalize_underscores(lexed));
|
|
1544
|
+
if (!lex< from >()) error("expected 'from' keyword in @for directive");
|
|
1545
|
+
Expression* lower_bound = parse_expression();
|
|
1546
|
+
lower_bound->is_delayed(false);
|
|
1547
|
+
bool inclusive = false;
|
|
1548
|
+
if (lex< through >()) inclusive = true;
|
|
1549
|
+
else if (lex< to >()) inclusive = false;
|
|
1550
|
+
else error("expected 'through' or 'to' keyword in @for directive");
|
|
1551
|
+
Expression* upper_bound = parse_expression();
|
|
1552
|
+
upper_bound->is_delayed(false);
|
|
1553
|
+
if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive");
|
|
1554
|
+
Block* body = parse_block();
|
|
1555
|
+
return new (ctx.mem) For(path, for_source_position, var, lower_bound, upper_bound, body, inclusive);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
Each* Parser::parse_each_directive()
|
|
1559
|
+
{
|
|
1560
|
+
lex < each_directive >();
|
|
1561
|
+
Position each_source_position = source_position;
|
|
1562
|
+
if (!lex< variable >()) error("@each directive requires an iteration variable");
|
|
1563
|
+
vector<string> vars;
|
|
1564
|
+
vars.push_back(Util::normalize_underscores(lexed));
|
|
1565
|
+
while (peek< exactly<','> >() && lex< exactly<','> >()) {
|
|
1566
|
+
if (!lex< variable >()) error("@each directive requires an iteration variable");
|
|
1567
|
+
vars.push_back(Util::normalize_underscores(lexed));
|
|
1568
|
+
}
|
|
1569
|
+
if (!lex< in >()) error("expected 'in' keyword in @each directive");
|
|
1570
|
+
Expression* list = parse_list();
|
|
1571
|
+
list->is_delayed(false);
|
|
1572
|
+
if (list->concrete_type() == Expression::LIST) {
|
|
1573
|
+
List* l = static_cast<List*>(list);
|
|
1574
|
+
for (size_t i = 0, L = l->length(); i < L; ++i) {
|
|
1575
|
+
(*l)[i]->is_delayed(false);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive");
|
|
1579
|
+
Block* body = parse_block();
|
|
1580
|
+
return new (ctx.mem) Each(path, each_source_position, vars, list, body);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
While* Parser::parse_while_directive()
|
|
1584
|
+
{
|
|
1585
|
+
lex< while_directive >();
|
|
1586
|
+
Position while_source_position = source_position;
|
|
1587
|
+
Expression* predicate = parse_list();
|
|
1588
|
+
predicate->is_delayed(false);
|
|
1589
|
+
Block* body = parse_block();
|
|
1590
|
+
return new (ctx.mem) While(path, while_source_position, predicate, body);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
Media_Block* Parser::parse_media_block()
|
|
1594
|
+
{
|
|
1595
|
+
lex< media >();
|
|
1596
|
+
Position media_source_position = source_position;
|
|
1597
|
+
|
|
1598
|
+
List* media_queries = parse_media_queries();
|
|
1599
|
+
|
|
1600
|
+
if (!peek< exactly<'{'> >()) {
|
|
1601
|
+
error("expected '{' in media query");
|
|
1602
|
+
}
|
|
1603
|
+
Block* block = parse_block();
|
|
1604
|
+
|
|
1605
|
+
return new (ctx.mem) Media_Block(path, media_source_position, media_queries, block);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
List* Parser::parse_media_queries()
|
|
1609
|
+
{
|
|
1610
|
+
List* media_queries = new (ctx.mem) List(path, source_position, 0, List::COMMA);
|
|
1611
|
+
if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query();
|
|
1612
|
+
while (lex< exactly<','> >()) (*media_queries) << parse_media_query();
|
|
1613
|
+
return media_queries;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Expression* Parser::parse_media_query()
|
|
1617
|
+
Media_Query* Parser::parse_media_query()
|
|
1618
|
+
{
|
|
1619
|
+
Media_Query* media_query = new (ctx.mem) Media_Query(path, source_position);
|
|
1620
|
+
|
|
1621
|
+
if (lex< exactly< not_kwd > >()) media_query->is_negated(true);
|
|
1622
|
+
else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true);
|
|
1623
|
+
|
|
1624
|
+
if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema());
|
|
1625
|
+
else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, source_position, lexed));
|
|
1626
|
+
else (*media_query) << parse_media_expression();
|
|
1627
|
+
|
|
1628
|
+
while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();
|
|
1629
|
+
|
|
1630
|
+
return media_query;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
Media_Query_Expression* Parser::parse_media_expression()
|
|
1634
|
+
{
|
|
1635
|
+
if (peek< identifier_schema >()) {
|
|
1636
|
+
String* ss = parse_identifier_schema();
|
|
1637
|
+
return new (ctx.mem) Media_Query_Expression(path, source_position, ss, 0, true);
|
|
1638
|
+
}
|
|
1639
|
+
if (!lex< exactly<'('> >()) {
|
|
1640
|
+
error("media query expression must begin with '('");
|
|
1641
|
+
}
|
|
1642
|
+
Expression* feature = 0;
|
|
1643
|
+
if (peek< exactly<')'> >()) {
|
|
1644
|
+
error("media feature required in media query expression");
|
|
1645
|
+
}
|
|
1646
|
+
feature = parse_expression();
|
|
1647
|
+
Expression* expression = 0;
|
|
1648
|
+
if (lex< exactly<':'> >()) {
|
|
1649
|
+
expression = parse_list();
|
|
1650
|
+
}
|
|
1651
|
+
if (!lex< exactly<')'> >()) {
|
|
1652
|
+
error("unclosed parenthesis in media query expression");
|
|
1653
|
+
}
|
|
1654
|
+
return new (ctx.mem) Media_Query_Expression(path, feature->position(), feature, expression);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
Feature_Block* Parser::parse_feature_block()
|
|
1658
|
+
{
|
|
1659
|
+
lex< supports >();
|
|
1660
|
+
Position supports_source_position = source_position;
|
|
1661
|
+
|
|
1662
|
+
Feature_Query* feature_queries = parse_feature_queries();
|
|
1663
|
+
|
|
1664
|
+
if (!peek< exactly<'{'> >()) {
|
|
1665
|
+
error("expected '{' in feature query");
|
|
1666
|
+
}
|
|
1667
|
+
Block* block = parse_block();
|
|
1668
|
+
|
|
1669
|
+
return new (ctx.mem) Feature_Block(path, supports_source_position, feature_queries, block);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
Feature_Query* Parser::parse_feature_queries()
|
|
1673
|
+
{
|
|
1674
|
+
Feature_Query* fq = new (ctx.mem) Feature_Query(path, source_position);
|
|
1675
|
+
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
|
|
1676
|
+
cond->is_root(true);
|
|
1677
|
+
while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
|
|
1678
|
+
(*cond) << parse_feature_query();
|
|
1679
|
+
(*fq) << cond;
|
|
1680
|
+
|
|
1681
|
+
if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))");
|
|
1682
|
+
|
|
1683
|
+
return fq;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
Feature_Query_Condition* Parser::parse_feature_query()
|
|
1687
|
+
{
|
|
1688
|
+
if (peek< not_op >(position)) return parse_supports_negation();
|
|
1689
|
+
else if (peek< and_op >(position)) return parse_supports_conjunction();
|
|
1690
|
+
else if (peek< or_op >(position)) return parse_supports_disjunction();
|
|
1691
|
+
else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();
|
|
1692
|
+
else return parse_supports_declaration();
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
Feature_Query_Condition* Parser::parse_feature_query_in_parens()
|
|
1696
|
+
{
|
|
1697
|
+
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(path, source_position);
|
|
1698
|
+
|
|
1699
|
+
if (!lex< exactly<'('> >()) error("@supports declaration expected '('");
|
|
1700
|
+
while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
|
|
1701
|
+
(*cond) << parse_feature_query();
|
|
1702
|
+
if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration");
|
|
1703
|
+
|
|
1704
|
+
return (cond->length() == 1) ? (*cond)[0] : cond;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
Feature_Query_Condition* Parser::parse_supports_negation()
|
|
1708
|
+
{
|
|
1709
|
+
lex< not_op >();
|
|
1710
|
+
|
|
1711
|
+
Feature_Query_Condition* cond = parse_feature_query();
|
|
1712
|
+
cond->operand(Feature_Query_Condition::NOT);
|
|
1713
|
+
|
|
1714
|
+
return cond;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
Feature_Query_Condition* Parser::parse_supports_conjunction()
|
|
1718
|
+
{
|
|
1719
|
+
lex< and_op >();
|
|
1720
|
+
|
|
1721
|
+
Feature_Query_Condition* cond = parse_feature_query();
|
|
1722
|
+
cond->operand(Feature_Query_Condition::AND);
|
|
1723
|
+
|
|
1724
|
+
return cond;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
Feature_Query_Condition* Parser::parse_supports_disjunction()
|
|
1728
|
+
{
|
|
1729
|
+
lex< or_op >();
|
|
1730
|
+
|
|
1731
|
+
Feature_Query_Condition* cond = parse_feature_query();
|
|
1732
|
+
cond->operand(Feature_Query_Condition::OR);
|
|
1733
|
+
|
|
1734
|
+
return cond;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
Feature_Query_Condition* Parser::parse_supports_declaration()
|
|
1738
|
+
{
|
|
1739
|
+
Declaration* declaration = parse_declaration();
|
|
1740
|
+
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->path(),
|
|
1741
|
+
declaration->position(),
|
|
1742
|
+
1,
|
|
1743
|
+
declaration->property(),
|
|
1744
|
+
declaration->value());
|
|
1745
|
+
return cond;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
At_Rule* Parser::parse_at_rule()
|
|
1749
|
+
{
|
|
1750
|
+
lex<at_keyword>();
|
|
1751
|
+
string kwd(lexed);
|
|
1752
|
+
Position at_source_position = source_position;
|
|
1753
|
+
Selector* sel = 0;
|
|
1754
|
+
Expression* val = 0;
|
|
1755
|
+
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
|
|
1756
|
+
if (lookahead.found) {
|
|
1757
|
+
if (lookahead.has_interpolants) {
|
|
1758
|
+
sel = parse_selector_schema(lookahead.found);
|
|
1759
|
+
}
|
|
1760
|
+
else {
|
|
1761
|
+
sel = parse_selector_group();
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {
|
|
1765
|
+
val = parse_list();
|
|
1766
|
+
}
|
|
1767
|
+
Block* body = 0;
|
|
1768
|
+
if (peek< exactly<'{'> >()) body = parse_block();
|
|
1769
|
+
At_Rule* rule = new (ctx.mem) At_Rule(path, at_source_position, kwd, sel, body);
|
|
1770
|
+
if (!sel) rule->value(val);
|
|
1771
|
+
return rule;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
Warning* Parser::parse_warning()
|
|
1775
|
+
{
|
|
1776
|
+
lex< warn >();
|
|
1777
|
+
return new (ctx.mem) Warning(path, source_position, parse_list());
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
Error* Parser::parse_error()
|
|
1781
|
+
{
|
|
1782
|
+
lex< err >();
|
|
1783
|
+
return new (ctx.mem) Error(path, source_position, parse_list());
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
Debug* Parser::parse_debug()
|
|
1787
|
+
{
|
|
1788
|
+
lex< dbg >();
|
|
1789
|
+
return new (ctx.mem) Debug(path, source_position, parse_list());
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
Selector_Lookahead Parser::lookahead_for_selector(const char* start)
|
|
1793
|
+
{
|
|
1794
|
+
const char* p = start ? start : position;
|
|
1795
|
+
const char* q;
|
|
1796
|
+
bool saw_stuff = false;
|
|
1797
|
+
bool saw_interpolant = false;
|
|
1798
|
+
|
|
1799
|
+
while ((q = peek< identifier >(p)) ||
|
|
1800
|
+
(q = peek< hyphens_and_identifier >(p)) ||
|
|
1801
|
+
(q = peek< hyphens_and_name >(p)) ||
|
|
1802
|
+
(q = peek< type_selector >(p)) ||
|
|
1803
|
+
(q = peek< id_name >(p)) ||
|
|
1804
|
+
(q = peek< class_name >(p)) ||
|
|
1805
|
+
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
|
1806
|
+
(q = peek< percentage >(p)) ||
|
|
1807
|
+
(q = peek< dimension >(p)) ||
|
|
1808
|
+
(q = peek< string_constant >(p)) ||
|
|
1809
|
+
(q = peek< exactly<'*'> >(p)) ||
|
|
1810
|
+
(q = peek< exactly<'('> >(p)) ||
|
|
1811
|
+
(q = peek< exactly<')'> >(p)) ||
|
|
1812
|
+
(q = peek< exactly<'['> >(p)) ||
|
|
1813
|
+
(q = peek< exactly<']'> >(p)) ||
|
|
1814
|
+
(q = peek< exactly<'+'> >(p)) ||
|
|
1815
|
+
(q = peek< exactly<'~'> >(p)) ||
|
|
1816
|
+
(q = peek< exactly<'>'> >(p)) ||
|
|
1817
|
+
(q = peek< exactly<','> >(p)) ||
|
|
1818
|
+
(q = peek< binomial >(p)) ||
|
|
1819
|
+
(q = peek< sequence< optional<sign>,
|
|
1820
|
+
optional<digits>,
|
|
1821
|
+
exactly<'n'> > >(p)) ||
|
|
1822
|
+
(q = peek< sequence< optional<sign>,
|
|
1823
|
+
digits > >(p)) ||
|
|
1824
|
+
(q = peek< number >(p)) ||
|
|
1825
|
+
(q = peek< sequence< exactly<'&'>,
|
|
1826
|
+
identifier_fragment > >(p)) ||
|
|
1827
|
+
(q = peek< exactly<'&'> >(p)) ||
|
|
1828
|
+
(q = peek< exactly<'%'> >(p)) ||
|
|
1829
|
+
(q = peek< alternatives<exact_match,
|
|
1830
|
+
class_match,
|
|
1831
|
+
dash_match,
|
|
1832
|
+
prefix_match,
|
|
1833
|
+
suffix_match,
|
|
1834
|
+
substring_match> >(p)) ||
|
|
1835
|
+
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
|
1836
|
+
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
|
1837
|
+
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
|
1838
|
+
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
|
1839
|
+
(q = peek< interpolant >(p))) {
|
|
1840
|
+
saw_stuff = true;
|
|
1841
|
+
p = q;
|
|
1842
|
+
if (*(p - 1) == '}') saw_interpolant = true;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
Selector_Lookahead result;
|
|
1846
|
+
result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;
|
|
1847
|
+
result.has_interpolants = saw_interpolant;
|
|
1848
|
+
|
|
1849
|
+
return result;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)
|
|
1853
|
+
{
|
|
1854
|
+
const char* p = start ? start : position;
|
|
1855
|
+
const char* q;
|
|
1856
|
+
bool saw_interpolant = false;
|
|
1857
|
+
bool saw_stuff = false;
|
|
1858
|
+
|
|
1859
|
+
while ((q = peek< identifier >(p)) ||
|
|
1860
|
+
(q = peek< type_selector >(p)) ||
|
|
1861
|
+
(q = peek< id_name >(p)) ||
|
|
1862
|
+
(q = peek< class_name >(p)) ||
|
|
1863
|
+
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
|
1864
|
+
(q = peek< percentage >(p)) ||
|
|
1865
|
+
(q = peek< dimension >(p)) ||
|
|
1866
|
+
(q = peek< string_constant >(p)) ||
|
|
1867
|
+
(q = peek< exactly<'*'> >(p)) ||
|
|
1868
|
+
(q = peek< exactly<'('> >(p)) ||
|
|
1869
|
+
(q = peek< exactly<')'> >(p)) ||
|
|
1870
|
+
(q = peek< exactly<'['> >(p)) ||
|
|
1871
|
+
(q = peek< exactly<']'> >(p)) ||
|
|
1872
|
+
(q = peek< exactly<'+'> >(p)) ||
|
|
1873
|
+
(q = peek< exactly<'~'> >(p)) ||
|
|
1874
|
+
(q = peek< exactly<'>'> >(p)) ||
|
|
1875
|
+
(q = peek< exactly<','> >(p)) ||
|
|
1876
|
+
(q = peek< binomial >(p)) ||
|
|
1877
|
+
(q = peek< sequence< optional<sign>,
|
|
1878
|
+
optional<digits>,
|
|
1879
|
+
exactly<'n'> > >(p)) ||
|
|
1880
|
+
(q = peek< sequence< optional<sign>,
|
|
1881
|
+
digits > >(p)) ||
|
|
1882
|
+
(q = peek< number >(p)) ||
|
|
1883
|
+
(q = peek< sequence< exactly<'&'>,
|
|
1884
|
+
identifier_fragment > >(p)) ||
|
|
1885
|
+
(q = peek< exactly<'&'> >(p)) ||
|
|
1886
|
+
(q = peek< exactly<'%'> >(p)) ||
|
|
1887
|
+
(q = peek< alternatives<exact_match,
|
|
1888
|
+
class_match,
|
|
1889
|
+
dash_match,
|
|
1890
|
+
prefix_match,
|
|
1891
|
+
suffix_match,
|
|
1892
|
+
substring_match> >(p)) ||
|
|
1893
|
+
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
|
1894
|
+
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
|
1895
|
+
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
|
1896
|
+
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
|
1897
|
+
(q = peek< interpolant >(p)) ||
|
|
1898
|
+
(q = peek< optional >(p))) {
|
|
1899
|
+
p = q;
|
|
1900
|
+
if (*(p - 1) == '}') saw_interpolant = true;
|
|
1901
|
+
saw_stuff = true;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
Selector_Lookahead result;
|
|
1905
|
+
result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
|
|
1906
|
+
result.has_interpolants = saw_interpolant;
|
|
1907
|
+
|
|
1908
|
+
return result;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
void Parser::read_bom()
|
|
1912
|
+
{
|
|
1913
|
+
size_t skip = 0;
|
|
1914
|
+
string encoding;
|
|
1915
|
+
bool utf_8 = false;
|
|
1916
|
+
switch ((unsigned char) source[0]) {
|
|
1917
|
+
case 0xEF:
|
|
1918
|
+
skip = check_bom_chars(source, end, utf_8_bom, 3);
|
|
1919
|
+
encoding = "UTF-8";
|
|
1920
|
+
utf_8 = true;
|
|
1921
|
+
break;
|
|
1922
|
+
case 0xFE:
|
|
1923
|
+
skip = check_bom_chars(source, end, utf_16_bom_be, 2);
|
|
1924
|
+
encoding = "UTF-16 (big endian)";
|
|
1925
|
+
break;
|
|
1926
|
+
case 0xFF:
|
|
1927
|
+
skip = check_bom_chars(source, end, utf_16_bom_le, 2);
|
|
1928
|
+
skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);
|
|
1929
|
+
encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
|
|
1930
|
+
break;
|
|
1931
|
+
case 0x00:
|
|
1932
|
+
skip = check_bom_chars(source, end, utf_32_bom_be, 4);
|
|
1933
|
+
encoding = "UTF-32 (big endian)";
|
|
1934
|
+
break;
|
|
1935
|
+
case 0x2B:
|
|
1936
|
+
skip = check_bom_chars(source, end, utf_7_bom_1, 4)
|
|
1937
|
+
| check_bom_chars(source, end, utf_7_bom_2, 4)
|
|
1938
|
+
| check_bom_chars(source, end, utf_7_bom_3, 4)
|
|
1939
|
+
| check_bom_chars(source, end, utf_7_bom_4, 4)
|
|
1940
|
+
| check_bom_chars(source, end, utf_7_bom_5, 5);
|
|
1941
|
+
encoding = "UTF-7";
|
|
1942
|
+
break;
|
|
1943
|
+
case 0xF7:
|
|
1944
|
+
skip = check_bom_chars(source, end, utf_1_bom, 3);
|
|
1945
|
+
encoding = "UTF-1";
|
|
1946
|
+
break;
|
|
1947
|
+
case 0xDD:
|
|
1948
|
+
skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);
|
|
1949
|
+
encoding = "UTF-EBCDIC";
|
|
1950
|
+
break;
|
|
1951
|
+
case 0x0E:
|
|
1952
|
+
skip = check_bom_chars(source, end, scsu_bom, 3);
|
|
1953
|
+
encoding = "SCSU";
|
|
1954
|
+
break;
|
|
1955
|
+
case 0xFB:
|
|
1956
|
+
skip = check_bom_chars(source, end, bocu_1_bom, 3);
|
|
1957
|
+
encoding = "BOCU-1";
|
|
1958
|
+
break;
|
|
1959
|
+
case 0x84:
|
|
1960
|
+
skip = check_bom_chars(source, end, gb_18030_bom, 4);
|
|
1961
|
+
encoding = "GB-18030";
|
|
1962
|
+
break;
|
|
1963
|
+
}
|
|
1964
|
+
if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);
|
|
1965
|
+
position += skip;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
|
|
1969
|
+
{
|
|
1970
|
+
size_t skip = 0;
|
|
1971
|
+
if (src + len > end) return 0;
|
|
1972
|
+
for (size_t i = 0; i < len; ++i, ++skip) {
|
|
1973
|
+
if ((unsigned char) src[i] != bom[i]) return 0;
|
|
1974
|
+
}
|
|
1975
|
+
return skip;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
|
|
1979
|
+
Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op)
|
|
1980
|
+
{
|
|
1981
|
+
for (size_t i = 0, S = operands.size(); i < S; ++i) {
|
|
1982
|
+
base = new (ctx.mem) Binary_Expression(path, source_position, op, base, operands[i]);
|
|
1983
|
+
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
|
1984
|
+
if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
|
1985
|
+
base->is_delayed(true);
|
|
1986
|
+
}
|
|
1987
|
+
else {
|
|
1988
|
+
b->left()->is_delayed(false);
|
|
1989
|
+
b->right()->is_delayed(false);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return base;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops)
|
|
1996
|
+
{
|
|
1997
|
+
for (size_t i = 0, S = operands.size(); i < S; ++i) {
|
|
1998
|
+
base = new (ctx.mem) Binary_Expression(path, base->position(), ops[i], base, operands[i]);
|
|
1999
|
+
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
|
2000
|
+
if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
|
2001
|
+
base->is_delayed(true);
|
|
2002
|
+
}
|
|
2003
|
+
else {
|
|
2004
|
+
b->left()->is_delayed(false);
|
|
2005
|
+
b->right()->is_delayed(false);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return base;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
void Parser::error(string msg, Position pos)
|
|
2012
|
+
{
|
|
2013
|
+
throw Sass_Error(Sass_Error::syntax, path, pos.line ? pos : source_position, msg);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
}
|