sassc 1.7.1 → 1.8.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/ext/libsass/.gitignore +10 -6
- data/ext/libsass/.travis.yml +4 -1
- data/ext/libsass/GNUmakefile.am +88 -0
- data/ext/libsass/Makefile +157 -76
- data/ext/libsass/Makefile.conf +47 -0
- data/ext/libsass/Readme.md +13 -14
- data/ext/libsass/appveyor.yml +25 -41
- data/ext/libsass/configure.ac +20 -7
- data/ext/libsass/contrib/plugin.cpp +1 -1
- data/ext/libsass/include/sass.h +15 -0
- data/ext/libsass/{sass.h → include/sass/base.h} +17 -9
- data/ext/libsass/{sass_context.h → include/sass/context.h} +3 -1
- data/ext/libsass/{sass_functions.h → include/sass/functions.h} +4 -4
- data/ext/libsass/{sass_interface.h → include/sass/interface.h} +5 -2
- data/ext/libsass/{sass_values.h → include/sass/values.h} +15 -1
- data/ext/libsass/{sass_version.h → include/sass/version.h} +0 -0
- data/ext/libsass/{sass_version.h.in → include/sass/version.h.in} +0 -0
- data/ext/libsass/{sass2scss.h → include/sass2scss.h} +6 -7
- data/ext/libsass/m4/m4-ax_cxx_compile_stdcxx_11.m4 +167 -0
- data/ext/libsass/script/ci-build-libsass +67 -23
- data/ext/libsass/src/GNUmakefile.am +54 -0
- data/ext/libsass/src/ast.cpp +2029 -0
- data/ext/libsass/{ast.hpp → src/ast.hpp} +832 -660
- data/ext/libsass/src/ast_def_macros.hpp +47 -0
- data/ext/libsass/src/ast_factory.hpp +93 -0
- data/ext/libsass/{ast_fwd_decl.hpp → src/ast_fwd_decl.hpp} +9 -4
- data/ext/libsass/{b64 → src/b64}/cencode.h +1 -1
- data/ext/libsass/{b64 → src/b64}/encode.h +0 -0
- data/ext/libsass/{backtrace.hpp → src/backtrace.hpp} +9 -10
- data/ext/libsass/{base64vlq.cpp → src/base64vlq.cpp} +2 -2
- data/ext/libsass/{base64vlq.hpp → src/base64vlq.hpp} +1 -2
- data/ext/libsass/{bind.cpp → src/bind.cpp} +96 -59
- data/ext/libsass/{bind.hpp → src/bind.hpp} +1 -1
- data/ext/libsass/src/c99func.c +54 -0
- data/ext/libsass/{cencode.c → src/cencode.c} +5 -5
- data/ext/libsass/src/color_maps.cpp +643 -0
- data/ext/libsass/src/color_maps.hpp +333 -0
- data/ext/libsass/{constants.cpp → src/constants.cpp} +10 -1
- data/ext/libsass/{constants.hpp → src/constants.hpp} +7 -0
- data/ext/libsass/{context.cpp → src/context.cpp} +152 -122
- data/ext/libsass/src/context.hpp +150 -0
- data/ext/libsass/{cssize.cpp → src/cssize.cpp} +123 -109
- data/ext/libsass/{cssize.hpp → src/cssize.hpp} +9 -13
- data/ext/libsass/{debug.hpp → src/debug.hpp} +9 -9
- data/ext/libsass/src/debugger.hpp +683 -0
- data/ext/libsass/{emitter.cpp → src/emitter.cpp} +13 -13
- data/ext/libsass/{emitter.hpp → src/emitter.hpp} +10 -11
- data/ext/libsass/src/environment.cpp +184 -0
- data/ext/libsass/src/environment.hpp +92 -0
- data/ext/libsass/src/error_handling.cpp +46 -0
- data/ext/libsass/src/error_handling.hpp +34 -0
- data/ext/libsass/src/eval.cpp +1462 -0
- data/ext/libsass/src/eval.hpp +107 -0
- data/ext/libsass/src/expand.cpp +653 -0
- data/ext/libsass/{expand.hpp → src/expand.hpp} +17 -16
- data/ext/libsass/{extend.cpp → src/extend.cpp} +198 -139
- data/ext/libsass/{extend.hpp → src/extend.hpp} +7 -8
- data/ext/libsass/{file.cpp → src/file.cpp} +103 -57
- data/ext/libsass/{file.hpp → src/file.hpp} +23 -14
- data/ext/libsass/{functions.cpp → src/functions.cpp} +642 -333
- data/ext/libsass/{functions.hpp → src/functions.hpp} +17 -4
- data/ext/libsass/{inspect.cpp → src/inspect.cpp} +147 -260
- data/ext/libsass/{inspect.hpp → src/inspect.hpp} +7 -7
- data/ext/libsass/{json.cpp → src/json.cpp} +33 -43
- data/ext/libsass/{json.hpp → src/json.hpp} +1 -1
- data/ext/libsass/{kwd_arg_macros.hpp → src/kwd_arg_macros.hpp} +0 -0
- data/ext/libsass/{lexer.cpp → src/lexer.cpp} +28 -0
- data/ext/libsass/{lexer.hpp → src/lexer.hpp} +25 -10
- data/ext/libsass/{listize.cpp → src/listize.cpp} +17 -13
- data/ext/libsass/{listize.hpp → src/listize.hpp} +0 -2
- data/ext/libsass/{mapping.hpp → src/mapping.hpp} +0 -0
- data/ext/libsass/src/memory_manager.cpp +76 -0
- data/ext/libsass/src/memory_manager.hpp +48 -0
- data/ext/libsass/{node.cpp → src/node.cpp} +89 -18
- data/ext/libsass/{node.hpp → src/node.hpp} +5 -6
- data/ext/libsass/{operation.hpp → src/operation.hpp} +18 -12
- data/ext/libsass/{output.cpp → src/output.cpp} +47 -55
- data/ext/libsass/{output.hpp → src/output.hpp} +5 -4
- data/ext/libsass/src/parser.cpp +2529 -0
- data/ext/libsass/{parser.hpp → src/parser.hpp} +84 -60
- data/ext/libsass/{paths.hpp → src/paths.hpp} +10 -13
- data/ext/libsass/{plugins.cpp → src/plugins.cpp} +14 -17
- data/ext/libsass/{plugins.hpp → src/plugins.hpp} +10 -11
- data/ext/libsass/{position.cpp → src/position.cpp} +5 -6
- data/ext/libsass/{position.hpp → src/position.hpp} +19 -22
- data/ext/libsass/{prelexer.cpp → src/prelexer.cpp} +401 -53
- data/ext/libsass/{prelexer.hpp → src/prelexer.hpp} +50 -10
- data/ext/libsass/{remove_placeholders.cpp → src/remove_placeholders.cpp} +12 -16
- data/ext/libsass/{remove_placeholders.hpp → src/remove_placeholders.hpp} +1 -7
- data/ext/libsass/{sass.cpp → src/sass.cpp} +3 -5
- data/ext/libsass/{sass2scss.cpp → src/sass2scss.cpp} +51 -46
- data/ext/libsass/{sass_context.cpp → src/sass_context.cpp} +114 -112
- data/ext/libsass/{sass_functions.cpp → src/sass_functions.cpp} +11 -18
- data/ext/libsass/{sass_interface.cpp → src/sass_interface.cpp} +44 -81
- data/ext/libsass/{sass_util.cpp → src/sass_util.cpp} +26 -8
- data/ext/libsass/{sass_util.hpp → src/sass_util.hpp} +14 -18
- data/ext/libsass/{sass_values.cpp → src/sass_values.cpp} +91 -20
- data/ext/libsass/{source_map.cpp → src/source_map.cpp} +13 -13
- data/ext/libsass/{source_map.hpp → src/source_map.hpp} +9 -9
- data/ext/libsass/{subset_map.hpp → src/subset_map.hpp} +29 -31
- data/ext/libsass/{support → src/support}/libsass.pc.in +0 -0
- data/ext/libsass/src/to_c.cpp +73 -0
- data/ext/libsass/src/to_c.hpp +41 -0
- data/ext/libsass/src/to_string.cpp +47 -0
- data/ext/libsass/{to_string.hpp → src/to_string.hpp} +9 -7
- data/ext/libsass/src/to_value.cpp +109 -0
- data/ext/libsass/src/to_value.hpp +50 -0
- data/ext/libsass/{units.cpp → src/units.cpp} +56 -51
- data/ext/libsass/{units.hpp → src/units.hpp} +8 -9
- data/ext/libsass/{utf8.h → src/utf8.h} +0 -0
- data/ext/libsass/{utf8 → src/utf8}/checked.h +0 -0
- data/ext/libsass/{utf8 → src/utf8}/core.h +12 -12
- data/ext/libsass/{utf8 → src/utf8}/unchecked.h +0 -0
- data/ext/libsass/{utf8_string.cpp → src/utf8_string.cpp} +0 -0
- data/ext/libsass/{utf8_string.hpp → src/utf8_string.hpp} +6 -6
- data/ext/libsass/{util.cpp → src/util.cpp} +144 -86
- data/ext/libsass/src/util.hpp +59 -0
- data/ext/libsass/src/values.cpp +137 -0
- data/ext/libsass/src/values.hpp +12 -0
- data/ext/libsass/test/test_node.cpp +33 -33
- data/ext/libsass/test/test_paths.cpp +5 -6
- data/ext/libsass/test/test_selector_difference.cpp +4 -5
- data/ext/libsass/test/test_specificity.cpp +4 -5
- data/ext/libsass/test/test_subset_map.cpp +91 -91
- data/ext/libsass/test/test_superselector.cpp +11 -11
- data/ext/libsass/test/test_unification.cpp +4 -4
- data/ext/libsass/win/libsass.targets +101 -0
- data/ext/libsass/win/libsass.vcxproj +45 -127
- data/ext/libsass/win/libsass.vcxproj.filters +303 -0
- data/lib/sassc/import_handler.rb +1 -1
- data/lib/sassc/native/native_functions_api.rb +3 -3
- data/lib/sassc/version.rb +1 -1
- data/test/custom_importer_test.rb +1 -4
- data/test/functions_test.rb +3 -2
- data/test/native_test.rb +4 -3
- metadata +117 -110
- data/ext/libsass/Makefile.am +0 -146
- data/ext/libsass/ast.cpp +0 -945
- data/ext/libsass/ast_def_macros.hpp +0 -21
- data/ext/libsass/ast_factory.hpp +0 -92
- data/ext/libsass/color_names.hpp +0 -327
- data/ext/libsass/context.hpp +0 -157
- data/ext/libsass/contextualize.cpp +0 -148
- data/ext/libsass/contextualize.hpp +0 -46
- data/ext/libsass/contextualize_eval.cpp +0 -93
- data/ext/libsass/contextualize_eval.hpp +0 -44
- data/ext/libsass/debugger.hpp +0 -558
- data/ext/libsass/environment.hpp +0 -163
- data/ext/libsass/error_handling.cpp +0 -35
- data/ext/libsass/error_handling.hpp +0 -32
- data/ext/libsass/eval.cpp +0 -1392
- data/ext/libsass/eval.hpp +0 -88
- data/ext/libsass/expand.cpp +0 -575
- data/ext/libsass/memory_manager.hpp +0 -57
- data/ext/libsass/parser.cpp +0 -2403
- data/ext/libsass/posix/getopt.c +0 -562
- data/ext/libsass/posix/getopt.h +0 -95
- data/ext/libsass/to_c.cpp +0 -61
- data/ext/libsass/to_c.hpp +0 -44
- data/ext/libsass/to_string.cpp +0 -34
- data/ext/libsass/util.hpp +0 -54
- data/ext/libsass/win/libsass.filters +0 -312
@@ -1,57 +0,0 @@
|
|
1
|
-
#ifndef SASS_MEMORY_MANAGER_H
|
2
|
-
#define SASS_MEMORY_MANAGER_H
|
3
|
-
|
4
|
-
#include <vector>
|
5
|
-
#include <iostream>
|
6
|
-
|
7
|
-
namespace Sass {
|
8
|
-
using namespace std;
|
9
|
-
/////////////////////////////////////////////////////////////////////////////
|
10
|
-
// A class for tracking allocations of AST_Node objects. The intended usage
|
11
|
-
// is something like: Some_Node* n = new (mem_mgr) Some_Node(...);
|
12
|
-
// Then, at the end of the program, the memory manager will delete all of the
|
13
|
-
// allocated nodes that have been passed to it.
|
14
|
-
// In the future, this class may implement a custom allocator.
|
15
|
-
/////////////////////////////////////////////////////////////////////////////
|
16
|
-
template <typename T>
|
17
|
-
class Memory_Manager {
|
18
|
-
vector<T*> nodes;
|
19
|
-
|
20
|
-
public:
|
21
|
-
Memory_Manager(size_t size = 0) : nodes(vector<T*>())
|
22
|
-
{ nodes.reserve(size); }
|
23
|
-
|
24
|
-
~Memory_Manager()
|
25
|
-
{
|
26
|
-
for (size_t i = 0, S = nodes.size(); i < S; ++i) {
|
27
|
-
// cout << "deleting " << typeid(*nodes[i]).name() << endl;
|
28
|
-
delete nodes[i];
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
T* operator()(T* np)
|
33
|
-
{
|
34
|
-
nodes.push_back(np);
|
35
|
-
// cout << "registering " << typeid(*np).name() << endl;
|
36
|
-
return np;
|
37
|
-
}
|
38
|
-
|
39
|
-
void remove(T* np)
|
40
|
-
{
|
41
|
-
nodes.erase(find(nodes.begin(), nodes.end(), np));
|
42
|
-
}
|
43
|
-
};
|
44
|
-
}
|
45
|
-
|
46
|
-
template <typename T>
|
47
|
-
inline void* operator new(size_t size, Sass::Memory_Manager<T>& mem_mgr)
|
48
|
-
{ return mem_mgr(static_cast<T*>(operator new(size))); }
|
49
|
-
|
50
|
-
template <typename T>
|
51
|
-
inline void operator delete(void *np, Sass::Memory_Manager<T>& mem_mgr)
|
52
|
-
{
|
53
|
-
mem_mgr.remove(reinterpret_cast<T*>(np));
|
54
|
-
operator delete(np);
|
55
|
-
}
|
56
|
-
|
57
|
-
#endif
|
data/ext/libsass/parser.cpp
DELETED
@@ -1,2403 +0,0 @@
|
|
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
|
-
#include "prelexer.hpp"
|
11
|
-
#include "sass_functions.h"
|
12
|
-
|
13
|
-
#include <typeinfo>
|
14
|
-
|
15
|
-
namespace Sass {
|
16
|
-
using namespace std;
|
17
|
-
using namespace Constants;
|
18
|
-
|
19
|
-
Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate)
|
20
|
-
{
|
21
|
-
Parser p(ctx, pstate);
|
22
|
-
p.source = str;
|
23
|
-
p.position = p.source;
|
24
|
-
p.end = str + strlen(str);
|
25
|
-
Block* root = new (ctx.mem) Block(pstate);
|
26
|
-
p.block_stack.push_back(root);
|
27
|
-
root->is_root(true);
|
28
|
-
return p;
|
29
|
-
}
|
30
|
-
|
31
|
-
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)
|
32
|
-
{
|
33
|
-
Parser p(ctx, pstate);
|
34
|
-
p.source = beg;
|
35
|
-
p.position = p.source;
|
36
|
-
p.end = end;
|
37
|
-
Block* root = new (ctx.mem) Block(pstate);
|
38
|
-
p.block_stack.push_back(root);
|
39
|
-
root->is_root(true);
|
40
|
-
return p;
|
41
|
-
}
|
42
|
-
|
43
|
-
Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)
|
44
|
-
{
|
45
|
-
Parser p = Parser::from_c_str(src, ctx, pstate);
|
46
|
-
// ToDo: ruby sass errors on parent references
|
47
|
-
// ToDo: remap the source-map entries somehow
|
48
|
-
return p.parse_selector_group();
|
49
|
-
}
|
50
|
-
|
51
|
-
bool Parser::peek_newline(const char* start)
|
52
|
-
{
|
53
|
-
return peek_linefeed(start ? start : position);
|
54
|
-
}
|
55
|
-
|
56
|
-
Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)
|
57
|
-
{
|
58
|
-
Parser p(ctx, pstate);
|
59
|
-
p.source = t.begin;
|
60
|
-
p.position = p.source;
|
61
|
-
p.end = t.end;
|
62
|
-
Block* root = new (ctx.mem) Block(pstate);
|
63
|
-
p.block_stack.push_back(root);
|
64
|
-
root->is_root(true);
|
65
|
-
return p;
|
66
|
-
}
|
67
|
-
|
68
|
-
Block* Parser::parse()
|
69
|
-
{
|
70
|
-
Block* root = new (ctx.mem) Block(pstate);
|
71
|
-
block_stack.push_back(root);
|
72
|
-
root->is_root(true);
|
73
|
-
read_bom();
|
74
|
-
|
75
|
-
if (ctx.queue.size() == 1) {
|
76
|
-
Import* pre = new (ctx.mem) Import(pstate);
|
77
|
-
string load_path(ctx.queue[0].load_path);
|
78
|
-
do_import(load_path, pre, ctx.c_headers, false);
|
79
|
-
ctx.head_imports = ctx.queue.size() - 1;
|
80
|
-
if (!pre->urls().empty()) (*root) << pre;
|
81
|
-
if (!pre->files().empty()) {
|
82
|
-
for (size_t i = 0, S = pre->files().size(); i < S; ++i) {
|
83
|
-
(*root) << new (ctx.mem) Import_Stub(pstate, pre->files()[i]);
|
84
|
-
}
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
bool semicolon = false;
|
89
|
-
string(error_message);
|
90
|
-
lex< optional_spaces >();
|
91
|
-
Selector_Lookahead lookahead_result;
|
92
|
-
while (position < end) {
|
93
|
-
parse_block_comments(root);
|
94
|
-
if (peek< kwd_import >()) {
|
95
|
-
Import* imp = parse_import();
|
96
|
-
if (!imp->urls().empty()) (*root) << imp;
|
97
|
-
if (!imp->files().empty()) {
|
98
|
-
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
|
99
|
-
(*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);
|
100
|
-
}
|
101
|
-
}
|
102
|
-
semicolon = true;
|
103
|
-
error_message = "top-level @import directive must be terminated by ';'";
|
104
|
-
}
|
105
|
-
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
|
106
|
-
(*root) << parse_definition();
|
107
|
-
}
|
108
|
-
else if (peek< variable >()) {
|
109
|
-
(*root) << parse_assignment();
|
110
|
-
semicolon = true;
|
111
|
-
error_message = "top-level variable binding must be terminated by ';'";
|
112
|
-
}
|
113
|
-
/*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
|
114
|
-
(*root) << parse_propset();
|
115
|
-
}*/
|
116
|
-
else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {
|
117
|
-
Mixin_Call* mixin_call = parse_mixin_call();
|
118
|
-
(*root) << mixin_call;
|
119
|
-
if (!mixin_call->block()) {
|
120
|
-
semicolon = true;
|
121
|
-
error_message = "top-level @include directive must be terminated by ';'";
|
122
|
-
}
|
123
|
-
}
|
124
|
-
else if (peek< kwd_if_directive >()) {
|
125
|
-
(*root) << parse_if_directive();
|
126
|
-
}
|
127
|
-
else if (peek< kwd_for_directive >()) {
|
128
|
-
(*root) << parse_for_directive();
|
129
|
-
}
|
130
|
-
else if (peek< kwd_each_directive >()) {
|
131
|
-
(*root) << parse_each_directive();
|
132
|
-
}
|
133
|
-
else if (peek< kwd_while_directive >()) {
|
134
|
-
(*root) << parse_while_directive();
|
135
|
-
}
|
136
|
-
else if (peek< kwd_media >()) {
|
137
|
-
(*root) << parse_media_block();
|
138
|
-
}
|
139
|
-
else if (peek< kwd_at_root >()) {
|
140
|
-
(*root) << parse_at_root_block();
|
141
|
-
}
|
142
|
-
else if (peek< kwd_supports >()) {
|
143
|
-
(*root) << parse_feature_block();
|
144
|
-
}
|
145
|
-
else if (peek< kwd_warn >()) {
|
146
|
-
(*root) << parse_warning();
|
147
|
-
semicolon = true;
|
148
|
-
error_message = "top-level @warn directive must be terminated by ';'";
|
149
|
-
}
|
150
|
-
else if (peek< kwd_err >()) {
|
151
|
-
(*root) << parse_error();
|
152
|
-
semicolon = true;
|
153
|
-
error_message = "top-level @error directive must be terminated by ';'";
|
154
|
-
}
|
155
|
-
else if (peek< kwd_dbg >()) {
|
156
|
-
(*root) << parse_debug();
|
157
|
-
semicolon = true;
|
158
|
-
error_message = "top-level @debug directive must be terminated by ';'";
|
159
|
-
}
|
160
|
-
// ignore the @charset directive for now
|
161
|
-
else if (lex< exactly< charset_kwd > >()) {
|
162
|
-
lex< quoted_string >();
|
163
|
-
lex< one_plus< exactly<';'> > >();
|
164
|
-
}
|
165
|
-
else if (peek< at_keyword >()) {
|
166
|
-
At_Rule* at_rule = parse_at_rule();
|
167
|
-
(*root) << at_rule;
|
168
|
-
if (!at_rule->block()){
|
169
|
-
semicolon = true;
|
170
|
-
error_message = "top-level directive must be terminated by ';'";
|
171
|
-
}
|
172
|
-
}
|
173
|
-
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
174
|
-
(*root) << parse_ruleset(lookahead_result);
|
175
|
-
}
|
176
|
-
else if (peek< exactly<';'> >()) {
|
177
|
-
lex< one_plus< exactly<';'> > >();
|
178
|
-
}
|
179
|
-
else {
|
180
|
-
lex< css_whitespace >();
|
181
|
-
if (position >= end) break;
|
182
|
-
error("invalid top-level expression", after_token);
|
183
|
-
}
|
184
|
-
if (semicolon) {
|
185
|
-
if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end)
|
186
|
-
{ error(error_message, pstate); }
|
187
|
-
semicolon = false;
|
188
|
-
}
|
189
|
-
lex< optional_spaces >();
|
190
|
-
}
|
191
|
-
block_stack.pop_back();
|
192
|
-
return root;
|
193
|
-
}
|
194
|
-
|
195
|
-
void Parser::add_single_file (Import* imp, string import_path) {
|
196
|
-
|
197
|
-
string extension;
|
198
|
-
string unquoted(unquote(import_path));
|
199
|
-
if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css
|
200
|
-
// a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end
|
201
|
-
extension = unquoted.substr(unquoted.length() - 4, 4);
|
202
|
-
}
|
203
|
-
|
204
|
-
if (extension == ".css") {
|
205
|
-
String_Constant* loc = new (ctx.mem) String_Constant(pstate, unquote(import_path));
|
206
|
-
Argument* loc_arg = new (ctx.mem) Argument(pstate, loc);
|
207
|
-
Arguments* loc_args = new (ctx.mem) Arguments(pstate);
|
208
|
-
(*loc_args) << loc_arg;
|
209
|
-
Function_Call* new_url = new (ctx.mem) Function_Call(pstate, "url", loc_args);
|
210
|
-
imp->urls().push_back(new_url);
|
211
|
-
}
|
212
|
-
else {
|
213
|
-
string current_dir = File::dir_name(path);
|
214
|
-
string resolved(ctx.add_file(current_dir, unquoted));
|
215
|
-
if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir, pstate);
|
216
|
-
imp->files().push_back(resolved);
|
217
|
-
}
|
218
|
-
|
219
|
-
}
|
220
|
-
|
221
|
-
void Parser::import_single_file (Import* imp, string import_path) {
|
222
|
-
|
223
|
-
if (!unquote(import_path).substr(0, 7).compare("http://") ||
|
224
|
-
!unquote(import_path).substr(0, 8).compare("https://") ||
|
225
|
-
!unquote(import_path).substr(0, 2).compare("//"))
|
226
|
-
{
|
227
|
-
imp->urls().push_back(new (ctx.mem) String_Quoted(pstate, import_path));
|
228
|
-
}
|
229
|
-
else {
|
230
|
-
add_single_file(imp, import_path);
|
231
|
-
}
|
232
|
-
|
233
|
-
}
|
234
|
-
|
235
|
-
bool Parser::do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one)
|
236
|
-
{
|
237
|
-
bool has_import = false;
|
238
|
-
string load_path = unquote(import_path);
|
239
|
-
for (auto importer : importers) {
|
240
|
-
// int priority = sass_importer_get_priority(importer);
|
241
|
-
Sass_Importer_Fn fn = sass_importer_get_function(importer);
|
242
|
-
if (Sass_Import_List includes =
|
243
|
-
fn(load_path.c_str(), importer, ctx.c_compiler)
|
244
|
-
) {
|
245
|
-
Sass_Import_List list = includes;
|
246
|
-
while (*includes) {
|
247
|
-
Sass_Import_Entry include = *includes;
|
248
|
-
const char *file = sass_import_get_path(include);
|
249
|
-
char* source = sass_import_take_source(include);
|
250
|
-
size_t line = sass_import_get_error_line(include);
|
251
|
-
size_t column = sass_import_get_error_column(include);
|
252
|
-
const char* message = sass_import_get_error_message(include);
|
253
|
-
if (message) {
|
254
|
-
if (line == string::npos && column == string::npos) error(message, pstate);
|
255
|
-
else error(message, ParserState(message, source, Position(line, column)));
|
256
|
-
} else if (source) {
|
257
|
-
if (file) {
|
258
|
-
ctx.add_source(file, load_path, source);
|
259
|
-
imp->files().push_back(file);
|
260
|
-
} else {
|
261
|
-
ctx.add_source(load_path, load_path, source);
|
262
|
-
imp->files().push_back(load_path);
|
263
|
-
}
|
264
|
-
} else if(file) {
|
265
|
-
import_single_file(imp, file);
|
266
|
-
}
|
267
|
-
++includes;
|
268
|
-
}
|
269
|
-
// deallocate returned memory
|
270
|
-
sass_delete_import_list(list);
|
271
|
-
// set success flag
|
272
|
-
has_import = true;
|
273
|
-
// break import chain
|
274
|
-
if (only_one) return true;
|
275
|
-
}
|
276
|
-
}
|
277
|
-
// return result
|
278
|
-
return has_import;
|
279
|
-
}
|
280
|
-
|
281
|
-
Import* Parser::parse_import()
|
282
|
-
{
|
283
|
-
lex< kwd_import >();
|
284
|
-
Import* imp = new (ctx.mem) Import(pstate);
|
285
|
-
bool first = true;
|
286
|
-
do {
|
287
|
-
while (lex< block_comment >());
|
288
|
-
if (lex< quoted_string >()) {
|
289
|
-
if (!do_import(lexed, imp, ctx.c_importers, true))
|
290
|
-
{
|
291
|
-
// push single file import
|
292
|
-
import_single_file(imp, lexed);
|
293
|
-
}
|
294
|
-
}
|
295
|
-
else if (lex< uri_prefix >()) {
|
296
|
-
Arguments* args = new (ctx.mem) Arguments(pstate);
|
297
|
-
Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);
|
298
|
-
if (lex< quoted_string >()) {
|
299
|
-
Expression* the_url = parse_string();
|
300
|
-
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
301
|
-
}
|
302
|
-
else if (lex < uri_value >(position)) { // chunk seems to work too!
|
303
|
-
String* the_url = parse_interpolated_chunk(lexed);
|
304
|
-
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
305
|
-
}
|
306
|
-
else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {
|
307
|
-
Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);
|
308
|
-
*args << new (ctx.mem) Argument(the_url->pstate(), the_url);
|
309
|
-
}
|
310
|
-
else {
|
311
|
-
error("malformed URL", pstate);
|
312
|
-
}
|
313
|
-
if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
|
314
|
-
imp->urls().push_back(result);
|
315
|
-
}
|
316
|
-
else {
|
317
|
-
if (first) error("@import directive requires a url or quoted path", pstate);
|
318
|
-
else error("expecting another url or quoted path in @import list", pstate);
|
319
|
-
}
|
320
|
-
first = false;
|
321
|
-
} while (lex_css< exactly<','> >());
|
322
|
-
return imp;
|
323
|
-
}
|
324
|
-
|
325
|
-
Definition* Parser::parse_definition()
|
326
|
-
{
|
327
|
-
Definition::Type which_type = Definition::MIXIN;
|
328
|
-
if (lex< kwd_mixin >()) which_type = Definition::MIXIN;
|
329
|
-
else if (lex< kwd_function >()) which_type = Definition::FUNCTION;
|
330
|
-
string which_str(lexed);
|
331
|
-
if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);
|
332
|
-
string name(Util::normalize_underscores(lexed));
|
333
|
-
if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
|
334
|
-
{ error("Invalid function name \"" + name + "\".", pstate); }
|
335
|
-
ParserState source_position_of_def = pstate;
|
336
|
-
Parameters* params = parse_parameters();
|
337
|
-
if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'", pstate);
|
338
|
-
if (which_type == Definition::MIXIN) stack.push_back(mixin_def);
|
339
|
-
else stack.push_back(function_def);
|
340
|
-
Block* body = parse_block();
|
341
|
-
stack.pop_back();
|
342
|
-
Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, &ctx, which_type);
|
343
|
-
return def;
|
344
|
-
}
|
345
|
-
|
346
|
-
Parameters* Parser::parse_parameters()
|
347
|
-
{
|
348
|
-
string name(lexed);
|
349
|
-
Position position = after_token;
|
350
|
-
Parameters* params = new (ctx.mem) Parameters(pstate);
|
351
|
-
if (lex_css< exactly<'('> >()) {
|
352
|
-
// if there's anything there at all
|
353
|
-
if (!peek_css< exactly<')'> >()) {
|
354
|
-
do (*params) << parse_parameter();
|
355
|
-
while (lex_css< exactly<','> >());
|
356
|
-
}
|
357
|
-
if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
|
358
|
-
}
|
359
|
-
return params;
|
360
|
-
}
|
361
|
-
|
362
|
-
Parameter* Parser::parse_parameter()
|
363
|
-
{
|
364
|
-
while (lex< alternatives < spaces, block_comment > >());
|
365
|
-
lex< variable >();
|
366
|
-
string name(Util::normalize_underscores(lexed));
|
367
|
-
ParserState pos = pstate;
|
368
|
-
Expression* val = 0;
|
369
|
-
bool is_rest = false;
|
370
|
-
while (lex< alternatives < spaces, block_comment > >());
|
371
|
-
if (lex< exactly<':'> >()) { // there's a default value
|
372
|
-
while (lex< block_comment >());
|
373
|
-
val = parse_space_list();
|
374
|
-
val->is_delayed(false);
|
375
|
-
}
|
376
|
-
else if (lex< exactly< ellipsis > >()) {
|
377
|
-
is_rest = true;
|
378
|
-
}
|
379
|
-
Parameter* p = new (ctx.mem) Parameter(pos, name, val, is_rest);
|
380
|
-
return p;
|
381
|
-
}
|
382
|
-
|
383
|
-
Mixin_Call* Parser::parse_mixin_call()
|
384
|
-
{
|
385
|
-
lex< kwd_include >() /* || lex< exactly<'+'> >() */;
|
386
|
-
if (!lex< identifier >()) error("invalid name in @include directive", pstate);
|
387
|
-
ParserState source_position_of_call = pstate;
|
388
|
-
string name(Util::normalize_underscores(lexed));
|
389
|
-
Arguments* args = parse_arguments();
|
390
|
-
Block* content = 0;
|
391
|
-
if (peek< exactly<'{'> >()) {
|
392
|
-
content = parse_block();
|
393
|
-
}
|
394
|
-
Mixin_Call* the_call = new (ctx.mem) Mixin_Call(source_position_of_call, name, args, content);
|
395
|
-
return the_call;
|
396
|
-
}
|
397
|
-
|
398
|
-
Arguments* Parser::parse_arguments(bool has_url)
|
399
|
-
{
|
400
|
-
string name(lexed);
|
401
|
-
Position position = after_token;
|
402
|
-
Arguments* args = new (ctx.mem) Arguments(pstate);
|
403
|
-
if (lex_css< exactly<'('> >()) {
|
404
|
-
// if there's anything there at all
|
405
|
-
if (!peek_css< exactly<')'> >()) {
|
406
|
-
do (*args) << parse_argument(has_url);
|
407
|
-
while (lex_css< exactly<','> >());
|
408
|
-
}
|
409
|
-
if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);
|
410
|
-
}
|
411
|
-
return args;
|
412
|
-
}
|
413
|
-
|
414
|
-
Argument* Parser::parse_argument(bool has_url)
|
415
|
-
{
|
416
|
-
|
417
|
-
Argument* arg;
|
418
|
-
// some urls can look like line comments (parse literally - chunk would not work)
|
419
|
-
if (has_url && lex< sequence < uri_value, lookahead < loosely<')'> > > >(false)) {
|
420
|
-
String* the_url = parse_interpolated_chunk(lexed);
|
421
|
-
arg = new (ctx.mem) Argument(the_url->pstate(), the_url);
|
422
|
-
}
|
423
|
-
else if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {
|
424
|
-
lex_css< variable >();
|
425
|
-
string name(Util::normalize_underscores(lexed));
|
426
|
-
ParserState p = pstate;
|
427
|
-
lex_css< exactly<':'> >();
|
428
|
-
Expression* val = parse_space_list();
|
429
|
-
val->is_delayed(false);
|
430
|
-
arg = new (ctx.mem) Argument(p, val, name);
|
431
|
-
}
|
432
|
-
else {
|
433
|
-
bool is_arglist = false;
|
434
|
-
bool is_keyword = false;
|
435
|
-
Expression* val = parse_space_list();
|
436
|
-
val->is_delayed(false);
|
437
|
-
if (lex_css< exactly< ellipsis > >()) {
|
438
|
-
if (val->concrete_type() == Expression::MAP) is_keyword = true;
|
439
|
-
else is_arglist = true;
|
440
|
-
}
|
441
|
-
arg = new (ctx.mem) Argument(pstate, val, "", is_arglist, is_keyword);
|
442
|
-
}
|
443
|
-
return arg;
|
444
|
-
}
|
445
|
-
|
446
|
-
Assignment* Parser::parse_assignment()
|
447
|
-
{
|
448
|
-
lex< variable >();
|
449
|
-
string name(Util::normalize_underscores(lexed));
|
450
|
-
ParserState var_source_position = pstate;
|
451
|
-
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
|
452
|
-
Expression* val;
|
453
|
-
Selector_Lookahead lookahead = lookahead_for_value(position);
|
454
|
-
if (lookahead.has_interpolants && lookahead.found) {
|
455
|
-
val = parse_value_schema(lookahead.found);
|
456
|
-
} else {
|
457
|
-
val = parse_list();
|
458
|
-
}
|
459
|
-
val->is_delayed(false);
|
460
|
-
bool is_default = false;
|
461
|
-
bool is_global = false;
|
462
|
-
while (peek< default_flag >() || peek< global_flag >()) {
|
463
|
-
is_default = lex< default_flag >() || is_default;
|
464
|
-
is_global = lex< global_flag >() || is_global;
|
465
|
-
}
|
466
|
-
Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_default, is_global);
|
467
|
-
return var;
|
468
|
-
}
|
469
|
-
|
470
|
-
/* not used anymore - remove?
|
471
|
-
Propset* Parser::parse_propset()
|
472
|
-
{
|
473
|
-
String* property_segment;
|
474
|
-
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
|
475
|
-
property_segment = parse_identifier_schema();
|
476
|
-
}
|
477
|
-
else {
|
478
|
-
lex< sequence< optional< exactly<'*'> >, identifier > >();
|
479
|
-
property_segment = new (ctx.mem) String_Quoted(pstate, lexed);
|
480
|
-
}
|
481
|
-
Propset* propset = new (ctx.mem) Propset(pstate, property_segment);
|
482
|
-
lex< exactly<':'> >();
|
483
|
-
|
484
|
-
if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property", pstate);
|
485
|
-
|
486
|
-
propset->block(parse_block());
|
487
|
-
|
488
|
-
propset->tabs(indentation);
|
489
|
-
|
490
|
-
return propset;
|
491
|
-
} */
|
492
|
-
|
493
|
-
Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)
|
494
|
-
{
|
495
|
-
Selector* sel;
|
496
|
-
if (lookahead.has_interpolants) {
|
497
|
-
sel = parse_selector_schema(lookahead.found);
|
498
|
-
}
|
499
|
-
else {
|
500
|
-
sel = parse_selector_group();
|
501
|
-
}
|
502
|
-
bool old_in_at_root = in_at_root;
|
503
|
-
ParserState r_source_position = pstate;
|
504
|
-
lex < css_comments >();
|
505
|
-
in_at_root = false;
|
506
|
-
if (!peek< exactly<'{'> >()) error("expected a '{' after the selector", pstate);
|
507
|
-
Block* block = parse_block();
|
508
|
-
in_at_root = old_in_at_root;
|
509
|
-
old_in_at_root = false;
|
510
|
-
Ruleset* ruleset = new (ctx.mem) Ruleset(r_source_position, sel, block);
|
511
|
-
return ruleset;
|
512
|
-
}
|
513
|
-
|
514
|
-
Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)
|
515
|
-
{
|
516
|
-
lex< optional_spaces >();
|
517
|
-
const char* i = position;
|
518
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
519
|
-
while (i < end_of_selector) {
|
520
|
-
// try to parse mutliple interpolants
|
521
|
-
if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {
|
522
|
-
// accumulate the preceding segment if the position has advanced
|
523
|
-
if (i < p) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));
|
524
|
-
// skip to the delimiter by skipping occurences in quoted strings
|
525
|
-
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
526
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
527
|
-
}
|
528
|
-
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);
|
529
|
-
Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
|
530
|
-
interpolant->is_interpolant(true);
|
531
|
-
(*schema) << interpolant;
|
532
|
-
i = j;
|
533
|
-
}
|
534
|
-
// no more interpolants have been found
|
535
|
-
// add the last segment if there is one
|
536
|
-
else {
|
537
|
-
if (i < end_of_selector) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, end_of_selector));
|
538
|
-
break;
|
539
|
-
}
|
540
|
-
}
|
541
|
-
position = end_of_selector;
|
542
|
-
Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema);
|
543
|
-
selector_schema->media_block(last_media_block);
|
544
|
-
selector_schema->last_block(block_stack.back());
|
545
|
-
return selector_schema;
|
546
|
-
}
|
547
|
-
|
548
|
-
Selector_List* Parser::parse_selector_group()
|
549
|
-
{
|
550
|
-
bool reloop = true;
|
551
|
-
To_String to_string(&ctx);
|
552
|
-
lex< css_whitespace >();
|
553
|
-
Selector_List* group = new (ctx.mem) Selector_List(pstate);
|
554
|
-
group->media_block(last_media_block);
|
555
|
-
group->last_block(block_stack.back());
|
556
|
-
do {
|
557
|
-
reloop = false;
|
558
|
-
if (peek< alternatives <
|
559
|
-
exactly<'{'>,
|
560
|
-
exactly<'}'>,
|
561
|
-
exactly<')'>,
|
562
|
-
exactly<';'>
|
563
|
-
> >())
|
564
|
-
break; // in case there are superfluous commas at the end
|
565
|
-
Complex_Selector* comb = parse_selector_combination();
|
566
|
-
if (!comb->has_reference() && !in_at_root) {
|
567
|
-
ParserState sel_source_position = pstate;
|
568
|
-
Selector_Reference* ref = new (ctx.mem) Selector_Reference(sel_source_position);
|
569
|
-
Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(sel_source_position);
|
570
|
-
ref_wrap->media_block(last_media_block);
|
571
|
-
ref_wrap->last_block(block_stack.back());
|
572
|
-
(*ref_wrap) << ref;
|
573
|
-
if (!comb->head()) {
|
574
|
-
comb->head(ref_wrap);
|
575
|
-
comb->has_reference(true);
|
576
|
-
}
|
577
|
-
else {
|
578
|
-
comb = new (ctx.mem) Complex_Selector(sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb);
|
579
|
-
comb->media_block(last_media_block);
|
580
|
-
comb->last_block(block_stack.back());
|
581
|
-
comb->has_reference(true);
|
582
|
-
}
|
583
|
-
if (peek_newline()) ref_wrap->has_line_break(true);
|
584
|
-
}
|
585
|
-
while (peek_css< exactly<','> >())
|
586
|
-
{
|
587
|
-
// consume everything up and including the comma speparator
|
588
|
-
reloop = lex< sequence < optional_css_comments, exactly<','> > >() != 0;
|
589
|
-
// remember line break (also between some commas)
|
590
|
-
if (peek_newline()) comb->has_line_feed(true);
|
591
|
-
if (comb->tail() && peek_newline()) comb->tail()->has_line_feed(true);
|
592
|
-
if (comb->tail() && comb->tail()->head() && peek_newline()) comb->tail()->head()->has_line_feed(true);
|
593
|
-
// remember line break (also between some commas)
|
594
|
-
}
|
595
|
-
(*group) << comb;
|
596
|
-
}
|
597
|
-
while (reloop);
|
598
|
-
while (lex< optional >()) {
|
599
|
-
group->is_optional(true);
|
600
|
-
}
|
601
|
-
return group;
|
602
|
-
}
|
603
|
-
|
604
|
-
Complex_Selector* Parser::parse_selector_combination()
|
605
|
-
{
|
606
|
-
Position sel_source_position(-1);
|
607
|
-
Compound_Selector* lhs;
|
608
|
-
if (peek_css< alternatives <
|
609
|
-
exactly<'+'>,
|
610
|
-
exactly<'~'>,
|
611
|
-
exactly<'>'>
|
612
|
-
> >())
|
613
|
-
// no selector before the combinator
|
614
|
-
{ lhs = 0; }
|
615
|
-
else {
|
616
|
-
lhs = parse_simple_selector_sequence();
|
617
|
-
sel_source_position = before_token;
|
618
|
-
lhs->has_line_break(peek_newline());
|
619
|
-
}
|
620
|
-
|
621
|
-
Complex_Selector::Combinator cmb;
|
622
|
-
if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;
|
623
|
-
else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;
|
624
|
-
else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;
|
625
|
-
else cmb = Complex_Selector::ANCESTOR_OF;
|
626
|
-
bool cpx_lf = peek_newline();
|
627
|
-
|
628
|
-
Complex_Selector* rhs;
|
629
|
-
if (peek_css< alternatives <
|
630
|
-
exactly<','>,
|
631
|
-
exactly<')'>,
|
632
|
-
exactly<'{'>,
|
633
|
-
exactly<'}'>,
|
634
|
-
exactly<';'>,
|
635
|
-
optional
|
636
|
-
> >())
|
637
|
-
// no selector after the combinator
|
638
|
-
{ rhs = 0; }
|
639
|
-
else {
|
640
|
-
rhs = parse_selector_combination();
|
641
|
-
sel_source_position = before_token;
|
642
|
-
}
|
643
|
-
if (!sel_source_position.line) sel_source_position = before_token;
|
644
|
-
Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), cmb, lhs, rhs);
|
645
|
-
cpx->media_block(last_media_block);
|
646
|
-
cpx->last_block(block_stack.back());
|
647
|
-
if (cpx_lf) cpx->has_line_break(cpx_lf);
|
648
|
-
return cpx;
|
649
|
-
}
|
650
|
-
|
651
|
-
Compound_Selector* Parser::parse_simple_selector_sequence()
|
652
|
-
{
|
653
|
-
Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate);
|
654
|
-
seq->media_block(last_media_block);
|
655
|
-
seq->last_block(block_stack.back());
|
656
|
-
bool sawsomething = false;
|
657
|
-
if (lex_css< exactly<'&'> >()) {
|
658
|
-
// check if we have a parent selector on the root level block
|
659
|
-
if (block_stack.back() && block_stack.back()->is_root()) {
|
660
|
-
//error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
|
661
|
-
}
|
662
|
-
(*seq) << new (ctx.mem) Selector_Reference(pstate);
|
663
|
-
sawsomething = true;
|
664
|
-
// if you see a space after a &, then you're done
|
665
|
-
if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) {
|
666
|
-
return seq;
|
667
|
-
}
|
668
|
-
}
|
669
|
-
if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) {
|
670
|
-
// saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning
|
671
|
-
(*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed));
|
672
|
-
} else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {
|
673
|
-
// if you see a type selector
|
674
|
-
(*seq) << new (ctx.mem) Type_Selector(pstate, lexed);
|
675
|
-
sawsomething = true;
|
676
|
-
}
|
677
|
-
if (!sawsomething) {
|
678
|
-
// don't blindly do this if you saw a & or selector
|
679
|
-
(*seq) << parse_simple_selector();
|
680
|
-
}
|
681
|
-
|
682
|
-
while (!peek< spaces >(position) &&
|
683
|
-
!(peek_css < alternatives <
|
684
|
-
exactly<'+'>,
|
685
|
-
exactly<'~'>,
|
686
|
-
exactly<'>'>,
|
687
|
-
exactly<','>,
|
688
|
-
exactly<')'>,
|
689
|
-
exactly<'{'>,
|
690
|
-
exactly<'}'>,
|
691
|
-
exactly<';'>
|
692
|
-
> >(position))) {
|
693
|
-
(*seq) << parse_simple_selector();
|
694
|
-
}
|
695
|
-
return seq;
|
696
|
-
}
|
697
|
-
|
698
|
-
Simple_Selector* Parser::parse_simple_selector()
|
699
|
-
{
|
700
|
-
lex < css_comments >();
|
701
|
-
if (lex< alternatives < id_name, class_name > >()) {
|
702
|
-
return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed));
|
703
|
-
}
|
704
|
-
else if (lex< quoted_string >()) {
|
705
|
-
return new (ctx.mem) Type_Selector(pstate, unquote(lexed));
|
706
|
-
}
|
707
|
-
else if (lex< alternatives < number, kwd_sel_deep > >()) {
|
708
|
-
return new (ctx.mem) Type_Selector(pstate, lexed);
|
709
|
-
}
|
710
|
-
else if (peek< pseudo_not >()) {
|
711
|
-
return parse_negated_selector();
|
712
|
-
}
|
713
|
-
else if (peek< exactly<':'> >(position) || peek< functional >()) {
|
714
|
-
return parse_pseudo_selector();
|
715
|
-
}
|
716
|
-
else if (peek< exactly<'['> >(position)) {
|
717
|
-
return parse_attribute_selector();
|
718
|
-
}
|
719
|
-
else if (lex< placeholder >()) {
|
720
|
-
Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, unquote(lexed));
|
721
|
-
sel->media_block(last_media_block);
|
722
|
-
sel->last_block(block_stack.back());
|
723
|
-
return sel;
|
724
|
-
}
|
725
|
-
else {
|
726
|
-
error("invalid selector after " + lexed.to_string(), pstate);
|
727
|
-
}
|
728
|
-
// unreachable statement
|
729
|
-
return 0;
|
730
|
-
}
|
731
|
-
|
732
|
-
Wrapped_Selector* Parser::parse_negated_selector()
|
733
|
-
{
|
734
|
-
lex< pseudo_not >();
|
735
|
-
string name(lexed);
|
736
|
-
ParserState nsource_position = pstate;
|
737
|
-
Selector* negated = parse_selector_group();
|
738
|
-
if (!lex< exactly<')'> >()) {
|
739
|
-
error("negated selector is missing ')'", pstate);
|
740
|
-
}
|
741
|
-
return new (ctx.mem) Wrapped_Selector(nsource_position, name, negated);
|
742
|
-
}
|
743
|
-
|
744
|
-
Simple_Selector* Parser::parse_pseudo_selector() {
|
745
|
-
if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) {
|
746
|
-
string name(lexed);
|
747
|
-
String* expr = 0;
|
748
|
-
ParserState p = pstate;
|
749
|
-
Selector* wrapped = 0;
|
750
|
-
if (lex< alternatives< even, odd > >()) {
|
751
|
-
expr = new (ctx.mem) String_Quoted(p, lexed);
|
752
|
-
}
|
753
|
-
else if (lex< binomial >(position)) {
|
754
|
-
expr = new (ctx.mem) String_Constant(p, lexed);
|
755
|
-
((String_Constant*)expr)->can_compress_whitespace(true);
|
756
|
-
}
|
757
|
-
else if (peek< sequence< optional<sign>,
|
758
|
-
zero_plus<digit>,
|
759
|
-
exactly<'n'>,
|
760
|
-
optional_css_whitespace,
|
761
|
-
exactly<')'> > >()) {
|
762
|
-
lex< sequence< optional<sign>,
|
763
|
-
zero_plus<digit>,
|
764
|
-
exactly<'n'> > >();
|
765
|
-
expr = new (ctx.mem) String_Quoted(p, lexed);
|
766
|
-
}
|
767
|
-
else if (lex< sequence< optional<sign>, one_plus < digit > > >()) {
|
768
|
-
expr = new (ctx.mem) String_Quoted(p, lexed);
|
769
|
-
}
|
770
|
-
else if (peek< sequence< identifier, optional_css_whitespace, exactly<')'> > >()) {
|
771
|
-
lex< identifier >();
|
772
|
-
expr = new (ctx.mem) String_Quoted(p, lexed);
|
773
|
-
}
|
774
|
-
else if (lex< quoted_string >()) {
|
775
|
-
expr = new (ctx.mem) String_Quoted(p, lexed);
|
776
|
-
}
|
777
|
-
else if (peek< exactly<')'> >()) {
|
778
|
-
expr = new (ctx.mem) String_Constant(p, "");
|
779
|
-
}
|
780
|
-
else {
|
781
|
-
wrapped = parse_selector_group();
|
782
|
-
}
|
783
|
-
if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)", pstate);
|
784
|
-
if (wrapped) {
|
785
|
-
return new (ctx.mem) Wrapped_Selector(p, name, wrapped);
|
786
|
-
}
|
787
|
-
return new (ctx.mem) Pseudo_Selector(p, name, expr);
|
788
|
-
}
|
789
|
-
else if (lex < sequence< pseudo_prefix, identifier > >()) {
|
790
|
-
return new (ctx.mem) Pseudo_Selector(pstate, unquote(lexed));
|
791
|
-
}
|
792
|
-
else {
|
793
|
-
error("unrecognized pseudo-class or pseudo-element", pstate);
|
794
|
-
}
|
795
|
-
// unreachable statement
|
796
|
-
return 0;
|
797
|
-
}
|
798
|
-
|
799
|
-
Attribute_Selector* Parser::parse_attribute_selector()
|
800
|
-
{
|
801
|
-
lex_css< exactly<'['> >();
|
802
|
-
ParserState p = pstate;
|
803
|
-
if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
|
804
|
-
string name(lexed);
|
805
|
-
if (lex_css< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);
|
806
|
-
if (!lex_css< alternatives< exact_match, class_match, dash_match,
|
807
|
-
prefix_match, suffix_match, substring_match > >()) {
|
808
|
-
error("invalid operator in attribute selector for " + name, pstate);
|
809
|
-
}
|
810
|
-
string matcher(lexed);
|
811
|
-
|
812
|
-
String* value = 0;
|
813
|
-
if (lex_css< identifier >()) {
|
814
|
-
value = new (ctx.mem) String_Constant(p, lexed);
|
815
|
-
}
|
816
|
-
else if (lex_css< quoted_string >()) {
|
817
|
-
value = parse_interpolated_chunk(lexed, true); // needed!
|
818
|
-
}
|
819
|
-
else {
|
820
|
-
error("expected a string constant or identifier in attribute selector for " + name, pstate);
|
821
|
-
}
|
822
|
-
|
823
|
-
if (!lex_css< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);
|
824
|
-
return new (ctx.mem) Attribute_Selector(p, name, matcher, value);
|
825
|
-
}
|
826
|
-
|
827
|
-
/* parse block comment and add to block */
|
828
|
-
void Parser::parse_block_comments(Block* block)
|
829
|
-
{
|
830
|
-
while (lex< block_comment >()) {
|
831
|
-
bool is_important = lexed.begin[2] == '!';
|
832
|
-
String* contents = parse_interpolated_chunk(lexed);
|
833
|
-
(*block) << new (ctx.mem) Comment(pstate, contents, is_important);
|
834
|
-
}
|
835
|
-
}
|
836
|
-
|
837
|
-
Block* Parser::parse_block()
|
838
|
-
{
|
839
|
-
lex< exactly<'{'> >();
|
840
|
-
bool semicolon = false;
|
841
|
-
Selector_Lookahead lookahead_result;
|
842
|
-
Block* block = new (ctx.mem) Block(pstate);
|
843
|
-
block_stack.push_back(block);
|
844
|
-
lex< zero_plus < alternatives < space, line_comment > > >();
|
845
|
-
// JMA - ensure that a block containing only block_comments is parsed
|
846
|
-
parse_block_comments(block);
|
847
|
-
|
848
|
-
while (!lex< exactly<'}'> >()) {
|
849
|
-
parse_block_comments(block);
|
850
|
-
if (semicolon) {
|
851
|
-
if (!lex< one_plus< exactly<';'> > >()) {
|
852
|
-
error("non-terminal statement or declaration must end with ';'", pstate);
|
853
|
-
}
|
854
|
-
semicolon = false;
|
855
|
-
parse_block_comments(block);
|
856
|
-
if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;
|
857
|
-
}
|
858
|
-
else if (peek< kwd_import >(position)) {
|
859
|
-
if (stack.back() == mixin_def || stack.back() == function_def) {
|
860
|
-
lex< kwd_import >(); // to adjust the before_token number
|
861
|
-
error("@import directives are not allowed inside mixins and functions", pstate);
|
862
|
-
}
|
863
|
-
Import* imp = parse_import();
|
864
|
-
if (!imp->urls().empty()) (*block) << imp;
|
865
|
-
if (!imp->files().empty()) {
|
866
|
-
for (size_t i = 0, S = imp->files().size(); i < S; ++i) {
|
867
|
-
(*block) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);
|
868
|
-
}
|
869
|
-
}
|
870
|
-
semicolon = true;
|
871
|
-
}
|
872
|
-
else if (lex< variable >()) {
|
873
|
-
(*block) << parse_assignment();
|
874
|
-
semicolon = true;
|
875
|
-
}
|
876
|
-
else if (lex< line_comment >()) {
|
877
|
-
// throw line comments away
|
878
|
-
}
|
879
|
-
else if (peek< kwd_if_directive >()) {
|
880
|
-
(*block) << parse_if_directive();
|
881
|
-
}
|
882
|
-
else if (peek< kwd_for_directive >()) {
|
883
|
-
(*block) << parse_for_directive();
|
884
|
-
}
|
885
|
-
else if (peek< kwd_each_directive >()) {
|
886
|
-
(*block) << parse_each_directive();
|
887
|
-
}
|
888
|
-
else if (peek < kwd_while_directive >()) {
|
889
|
-
(*block) << parse_while_directive();
|
890
|
-
}
|
891
|
-
else if (lex < kwd_return_directive >()) {
|
892
|
-
(*block) << new (ctx.mem) Return(pstate, parse_list());
|
893
|
-
semicolon = true;
|
894
|
-
}
|
895
|
-
else if (peek< kwd_warn >()) {
|
896
|
-
(*block) << parse_warning();
|
897
|
-
semicolon = true;
|
898
|
-
}
|
899
|
-
else if (peek< kwd_err >()) {
|
900
|
-
(*block) << parse_error();
|
901
|
-
semicolon = true;
|
902
|
-
}
|
903
|
-
else if (peek< kwd_dbg >()) {
|
904
|
-
(*block) << parse_debug();
|
905
|
-
semicolon = true;
|
906
|
-
}
|
907
|
-
else if (stack.back() == function_def) {
|
908
|
-
error("only variable declarations and control directives are allowed inside functions", pstate);
|
909
|
-
}
|
910
|
-
else if (peek< kwd_mixin >() || peek< kwd_function >()) {
|
911
|
-
(*block) << parse_definition();
|
912
|
-
}
|
913
|
-
else if (peek< kwd_include >(position)) {
|
914
|
-
Mixin_Call* the_call = parse_mixin_call();
|
915
|
-
(*block) << the_call;
|
916
|
-
// don't need a semicolon after a content block
|
917
|
-
semicolon = (the_call->block()) ? false : true;
|
918
|
-
}
|
919
|
-
else if (lex< kwd_content >()) {
|
920
|
-
if (stack.back() != mixin_def) {
|
921
|
-
error("@content may only be used within a mixin", pstate);
|
922
|
-
}
|
923
|
-
(*block) << new (ctx.mem) Content(pstate);
|
924
|
-
semicolon = true;
|
925
|
-
}
|
926
|
-
/*
|
927
|
-
else if (peek< exactly<'+'> >()) {
|
928
|
-
(*block) << parse_mixin_call();
|
929
|
-
semicolon = true;
|
930
|
-
}
|
931
|
-
*/
|
932
|
-
else if (lex< kwd_extend >()) {
|
933
|
-
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
|
934
|
-
if (!lookahead.found) error("invalid selector for @extend", pstate);
|
935
|
-
Selector* target;
|
936
|
-
if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);
|
937
|
-
else target = parse_selector_group();
|
938
|
-
(*block) << new (ctx.mem) Extension(pstate, target);
|
939
|
-
semicolon = true;
|
940
|
-
}
|
941
|
-
else if (peek< kwd_media >()) {
|
942
|
-
(*block) << parse_media_block();
|
943
|
-
}
|
944
|
-
else if (peek< kwd_supports >()) {
|
945
|
-
(*block) << parse_feature_block();
|
946
|
-
}
|
947
|
-
else if (peek< kwd_at_root >()) {
|
948
|
-
(*block) << parse_at_root_block();
|
949
|
-
}
|
950
|
-
// ignore the @charset directive for now
|
951
|
-
else if (lex< exactly< charset_kwd > >()) {
|
952
|
-
lex< quoted_string >();
|
953
|
-
lex< one_plus< exactly<';'> > >();
|
954
|
-
}
|
955
|
-
else if (peek< at_keyword >()) {
|
956
|
-
At_Rule* at_rule = parse_at_rule();
|
957
|
-
(*block) << at_rule;
|
958
|
-
if (!at_rule->block()) semicolon = true;
|
959
|
-
}
|
960
|
-
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
961
|
-
(*block) << parse_ruleset(lookahead_result);
|
962
|
-
}/* not used anymore - remove?
|
963
|
-
else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
|
964
|
-
(*block) << parse_propset();
|
965
|
-
}*/
|
966
|
-
else if (!peek< exactly<';'> >()) {
|
967
|
-
bool indent = ! peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position);
|
968
|
-
/* not used anymore - remove?
|
969
|
-
if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {
|
970
|
-
(*block) << parse_propset();
|
971
|
-
}
|
972
|
-
else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {
|
973
|
-
(*block) << parse_propset();
|
974
|
-
}
|
975
|
-
else */ {
|
976
|
-
Declaration* decl = parse_declaration();
|
977
|
-
decl->tabs(indentation);
|
978
|
-
(*block) << decl;
|
979
|
-
if (peek< exactly<'{'> >()) {
|
980
|
-
// parse a propset that rides on the declaration's property
|
981
|
-
if (indent) indentation++;
|
982
|
-
Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block());
|
983
|
-
if (indent) indentation--;
|
984
|
-
(*block) << ps;
|
985
|
-
}
|
986
|
-
else {
|
987
|
-
// finish and let the semicolon get munched
|
988
|
-
semicolon = true;
|
989
|
-
}
|
990
|
-
}
|
991
|
-
}
|
992
|
-
else lex< one_plus< exactly<';'> > >();
|
993
|
-
parse_block_comments(block);
|
994
|
-
}
|
995
|
-
block_stack.pop_back();
|
996
|
-
return block;
|
997
|
-
}
|
998
|
-
|
999
|
-
Declaration* Parser::parse_declaration() {
|
1000
|
-
String* prop = 0;
|
1001
|
-
if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {
|
1002
|
-
prop = parse_identifier_schema();
|
1003
|
-
}
|
1004
|
-
else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
|
1005
|
-
prop = new (ctx.mem) String_Quoted(pstate, lexed);
|
1006
|
-
prop->is_delayed(true);
|
1007
|
-
}
|
1008
|
-
else {
|
1009
|
-
error("invalid property name", pstate);
|
1010
|
-
}
|
1011
|
-
const string property(lexed);
|
1012
|
-
if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
|
1013
|
-
if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);
|
1014
|
-
if (peek_css< static_value >()) {
|
1015
|
-
return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex<important>()*/);
|
1016
|
-
}
|
1017
|
-
else {
|
1018
|
-
Expression* value;
|
1019
|
-
Selector_Lookahead lookahead = lookahead_for_value(position);
|
1020
|
-
if (lookahead.found) {
|
1021
|
-
if (lookahead.has_interpolants) {
|
1022
|
-
value = parse_value_schema(lookahead.found);
|
1023
|
-
} else {
|
1024
|
-
value = parse_list();
|
1025
|
-
}
|
1026
|
-
}
|
1027
|
-
else {
|
1028
|
-
value = parse_list();
|
1029
|
-
if (List* list = dynamic_cast<List*>(value)) {
|
1030
|
-
if (list->length() == 0 && !peek< exactly <'{'> >()) {
|
1031
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1032
|
-
}
|
1033
|
-
}
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
return new (ctx.mem) Declaration(prop->pstate(), prop, value/*, lex<important>()*/);
|
1037
|
-
}
|
1038
|
-
}
|
1039
|
-
|
1040
|
-
// parse +/- and return false if negative
|
1041
|
-
bool Parser::parse_number_prefix()
|
1042
|
-
{
|
1043
|
-
bool positive = true;
|
1044
|
-
while(true) {
|
1045
|
-
if (lex < block_comment >()) continue;
|
1046
|
-
if (lex < number_prefix >()) continue;
|
1047
|
-
if (lex < exactly < '-' > >()) {
|
1048
|
-
positive = !positive;
|
1049
|
-
continue;
|
1050
|
-
}
|
1051
|
-
break;
|
1052
|
-
}
|
1053
|
-
return positive;
|
1054
|
-
}
|
1055
|
-
|
1056
|
-
Expression* Parser::parse_map()
|
1057
|
-
{
|
1058
|
-
ParserState opstate = pstate;
|
1059
|
-
Expression* key = parse_list();
|
1060
|
-
if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
|
1061
|
-
if (!str->quote_mark() && !str->is_delayed()) {
|
1062
|
-
if (ctx.names_to_colors.count(str->value())) {
|
1063
|
-
Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);
|
1064
|
-
c->pstate(str->pstate());
|
1065
|
-
c->disp(str->value());
|
1066
|
-
key = c;
|
1067
|
-
}
|
1068
|
-
}
|
1069
|
-
}
|
1070
|
-
|
1071
|
-
// it's not a map so return the lexed value as a list value
|
1072
|
-
if (!peek< exactly<':'> >())
|
1073
|
-
{ return key; }
|
1074
|
-
|
1075
|
-
lex< exactly<':'> >();
|
1076
|
-
|
1077
|
-
Expression* value = parse_space_list();
|
1078
|
-
|
1079
|
-
Map* map = new (ctx.mem) Map(opstate, 1);
|
1080
|
-
(*map) << make_pair(key, value);
|
1081
|
-
|
1082
|
-
while (lex_css< exactly<','> >())
|
1083
|
-
{
|
1084
|
-
// allow trailing commas - #495
|
1085
|
-
if (peek_css< exactly<')'> >(position))
|
1086
|
-
{ break; }
|
1087
|
-
|
1088
|
-
Expression* key = parse_list();
|
1089
|
-
if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {
|
1090
|
-
if (!str->quote_mark() && !str->is_delayed()) {
|
1091
|
-
if (ctx.names_to_colors.count(str->value())) {
|
1092
|
-
Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);
|
1093
|
-
c->pstate(str->pstate());
|
1094
|
-
c->disp(str->value());
|
1095
|
-
key = c;
|
1096
|
-
}
|
1097
|
-
}
|
1098
|
-
}
|
1099
|
-
|
1100
|
-
if (!(lex< exactly<':'> >()))
|
1101
|
-
{ error("invalid syntax", pstate); }
|
1102
|
-
|
1103
|
-
Expression* value = parse_space_list();
|
1104
|
-
|
1105
|
-
(*map) << make_pair(key, value);
|
1106
|
-
}
|
1107
|
-
|
1108
|
-
ParserState ps = map->pstate();
|
1109
|
-
ps.offset = pstate - ps + pstate.offset;
|
1110
|
-
map->pstate(ps);
|
1111
|
-
|
1112
|
-
return map;
|
1113
|
-
}
|
1114
|
-
|
1115
|
-
Expression* Parser::parse_list()
|
1116
|
-
{
|
1117
|
-
return parse_comma_list();
|
1118
|
-
}
|
1119
|
-
|
1120
|
-
Expression* Parser::parse_comma_list()
|
1121
|
-
{
|
1122
|
-
if (peek_css< alternatives <
|
1123
|
-
// exactly<'!'>,
|
1124
|
-
// exactly<':'>,
|
1125
|
-
exactly<';'>,
|
1126
|
-
exactly<'}'>,
|
1127
|
-
exactly<'{'>,
|
1128
|
-
exactly<')'>,
|
1129
|
-
exactly<ellipsis>
|
1130
|
-
> >(position))
|
1131
|
-
{ return new (ctx.mem) List(pstate, 0); }
|
1132
|
-
Expression* list1 = parse_space_list();
|
1133
|
-
// if it's a singleton, return it directly; don't wrap it
|
1134
|
-
if (!peek_css< exactly<','> >(position)) return list1;
|
1135
|
-
|
1136
|
-
List* comma_list = new (ctx.mem) List(pstate, 2, List::COMMA);
|
1137
|
-
(*comma_list) << list1;
|
1138
|
-
|
1139
|
-
while (lex_css< exactly<','> >())
|
1140
|
-
{
|
1141
|
-
if (peek_css< alternatives <
|
1142
|
-
// exactly<'!'>,
|
1143
|
-
exactly<';'>,
|
1144
|
-
exactly<'}'>,
|
1145
|
-
exactly<'{'>,
|
1146
|
-
exactly<')'>,
|
1147
|
-
exactly<':'>,
|
1148
|
-
exactly<ellipsis>
|
1149
|
-
> >(position)
|
1150
|
-
) { break; }
|
1151
|
-
Expression* list = parse_space_list();
|
1152
|
-
(*comma_list) << list;
|
1153
|
-
}
|
1154
|
-
|
1155
|
-
return comma_list;
|
1156
|
-
}
|
1157
|
-
|
1158
|
-
Expression* Parser::parse_space_list()
|
1159
|
-
{
|
1160
|
-
Expression* disj1 = parse_disjunction();
|
1161
|
-
// if it's a singleton, return it directly; don't wrap it
|
1162
|
-
if (peek_css< alternatives <
|
1163
|
-
// exactly<'!'>,
|
1164
|
-
exactly<';'>,
|
1165
|
-
exactly<'}'>,
|
1166
|
-
exactly<'{'>,
|
1167
|
-
exactly<')'>,
|
1168
|
-
exactly<','>,
|
1169
|
-
exactly<':'>,
|
1170
|
-
exactly<ellipsis>,
|
1171
|
-
default_flag,
|
1172
|
-
global_flag
|
1173
|
-
> >(position)
|
1174
|
-
) { return disj1; }
|
1175
|
-
|
1176
|
-
List* space_list = new (ctx.mem) List(pstate, 2, List::SPACE);
|
1177
|
-
(*space_list) << disj1;
|
1178
|
-
|
1179
|
-
while (!(peek_css< alternatives <
|
1180
|
-
// exactly<'!'>,
|
1181
|
-
exactly<';'>,
|
1182
|
-
exactly<'}'>,
|
1183
|
-
exactly<'{'>,
|
1184
|
-
exactly<')'>,
|
1185
|
-
exactly<','>,
|
1186
|
-
exactly<':'>,
|
1187
|
-
exactly<ellipsis>,
|
1188
|
-
default_flag,
|
1189
|
-
global_flag
|
1190
|
-
> >(position)) && peek_css< optional_css_whitespace >() != end
|
1191
|
-
) {
|
1192
|
-
(*space_list) << parse_disjunction();
|
1193
|
-
}
|
1194
|
-
|
1195
|
-
return space_list;
|
1196
|
-
}
|
1197
|
-
|
1198
|
-
Expression* Parser::parse_disjunction()
|
1199
|
-
{
|
1200
|
-
Expression* conj1 = parse_conjunction();
|
1201
|
-
// if it's a singleton, return it directly; don't wrap it
|
1202
|
-
if (!peek_css< kwd_or >()) return conj1;
|
1203
|
-
|
1204
|
-
vector<Expression*> operands;
|
1205
|
-
while (lex_css< kwd_or >())
|
1206
|
-
operands.push_back(parse_conjunction());
|
1207
|
-
|
1208
|
-
return fold_operands(conj1, operands, Binary_Expression::OR);
|
1209
|
-
}
|
1210
|
-
|
1211
|
-
Expression* Parser::parse_conjunction()
|
1212
|
-
{
|
1213
|
-
Expression* rel1 = parse_relation();
|
1214
|
-
// if it's a singleton, return it directly; don't wrap it
|
1215
|
-
if (!peek_css< kwd_and >()) return rel1;
|
1216
|
-
|
1217
|
-
vector<Expression*> operands;
|
1218
|
-
while (lex_css< kwd_and >())
|
1219
|
-
operands.push_back(parse_relation());
|
1220
|
-
|
1221
|
-
return fold_operands(rel1, operands, Binary_Expression::AND);
|
1222
|
-
}
|
1223
|
-
|
1224
|
-
Expression* Parser::parse_relation()
|
1225
|
-
{
|
1226
|
-
Expression* expr1 = parse_expression();
|
1227
|
-
// if it's a singleton, return it directly; don't wrap it
|
1228
|
-
if (!(peek< alternatives <
|
1229
|
-
kwd_eq,
|
1230
|
-
kwd_neq,
|
1231
|
-
kwd_gte,
|
1232
|
-
kwd_gt,
|
1233
|
-
kwd_lte,
|
1234
|
-
kwd_lt
|
1235
|
-
> >(position)))
|
1236
|
-
{ return expr1; }
|
1237
|
-
|
1238
|
-
Binary_Expression::Type op
|
1239
|
-
= lex<kwd_eq>() ? Binary_Expression::EQ
|
1240
|
-
: lex<kwd_neq>() ? Binary_Expression::NEQ
|
1241
|
-
: lex<kwd_gte>() ? Binary_Expression::GTE
|
1242
|
-
: lex<kwd_lte>() ? Binary_Expression::LTE
|
1243
|
-
: lex<kwd_gt>() ? Binary_Expression::GT
|
1244
|
-
: lex<kwd_lt>() ? Binary_Expression::LT
|
1245
|
-
: Binary_Expression::LT; // whatever
|
1246
|
-
|
1247
|
-
Expression* expr2 = parse_expression();
|
1248
|
-
|
1249
|
-
return new (ctx.mem) Binary_Expression(expr1->pstate(), op, expr1, expr2);
|
1250
|
-
}
|
1251
|
-
|
1252
|
-
Expression* Parser::parse_expression()
|
1253
|
-
{
|
1254
|
-
Expression* term1 = parse_term();
|
1255
|
-
// if it's a singleton, return it directly; don't wrap it
|
1256
|
-
if (!(peek< exactly<'+'> >(position) ||
|
1257
|
-
(peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||
|
1258
|
-
(peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||
|
1259
|
-
peek< identifier >(position))
|
1260
|
-
{ return term1; }
|
1261
|
-
|
1262
|
-
vector<Expression*> operands;
|
1263
|
-
vector<Binary_Expression::Type> operators;
|
1264
|
-
while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {
|
1265
|
-
operators.push_back(lexed.to_string() == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);
|
1266
|
-
operands.push_back(parse_term());
|
1267
|
-
}
|
1268
|
-
|
1269
|
-
return fold_operands(term1, operands, operators);
|
1270
|
-
}
|
1271
|
-
|
1272
|
-
Expression* Parser::parse_term()
|
1273
|
-
{
|
1274
|
-
Expression* factor = parse_factor();
|
1275
|
-
// Special case: Ruby sass never tries to modulo if the lhs contains an interpolant
|
1276
|
-
if (peek_css< exactly<'%'> >(position) && factor->concrete_type() == Expression::STRING) {
|
1277
|
-
String_Schema* ss = dynamic_cast<String_Schema*>(factor);
|
1278
|
-
if (ss && ss->has_interpolants()) return factor;
|
1279
|
-
}
|
1280
|
-
// if it's a singleton, return it directly; don't wrap it
|
1281
|
-
if (!peek< class_char< static_ops > >(position)) return factor;
|
1282
|
-
return parse_operators(factor);
|
1283
|
-
}
|
1284
|
-
|
1285
|
-
Expression* Parser::parse_operators(Expression* factor)
|
1286
|
-
{
|
1287
|
-
// parse more factors and operators
|
1288
|
-
vector<Expression*> operands; // factors
|
1289
|
-
vector<Binary_Expression::Type> operators; // ops
|
1290
|
-
while (lex_css< class_char< static_ops > >()) {
|
1291
|
-
switch(*lexed.begin) {
|
1292
|
-
case '*': operators.push_back(Binary_Expression::MUL); break;
|
1293
|
-
case '/': operators.push_back(Binary_Expression::DIV); break;
|
1294
|
-
case '%': operators.push_back(Binary_Expression::MOD); break;
|
1295
|
-
default: throw runtime_error("unknown static op parsed"); break;
|
1296
|
-
}
|
1297
|
-
operands.push_back(parse_factor());
|
1298
|
-
}
|
1299
|
-
// operands and operators to binary expression
|
1300
|
-
return fold_operands(factor, operands, operators);
|
1301
|
-
}
|
1302
|
-
|
1303
|
-
Expression* Parser::parse_factor()
|
1304
|
-
{
|
1305
|
-
if (lex_css< exactly<'('> >()) {
|
1306
|
-
Expression* value = parse_map();
|
1307
|
-
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);
|
1308
|
-
value->is_delayed(false);
|
1309
|
-
// make sure wrapped lists and division expressions are non-delayed within parentheses
|
1310
|
-
if (value->concrete_type() == Expression::LIST) {
|
1311
|
-
List* l = static_cast<List*>(value);
|
1312
|
-
if (!l->empty()) (*l)[0]->is_delayed(false);
|
1313
|
-
} else if (typeid(*value) == typeid(Binary_Expression)) {
|
1314
|
-
Binary_Expression* b = static_cast<Binary_Expression*>(value);
|
1315
|
-
Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());
|
1316
|
-
if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false);
|
1317
|
-
}
|
1318
|
-
return value;
|
1319
|
-
}
|
1320
|
-
else if (peek< ie_property >()) {
|
1321
|
-
return parse_ie_property();
|
1322
|
-
}
|
1323
|
-
else if (peek< ie_keyword_arg >()) {
|
1324
|
-
return parse_ie_keyword_arg();
|
1325
|
-
}
|
1326
|
-
else if (peek< exactly< calc_kwd > >() ||
|
1327
|
-
peek< exactly< moz_calc_kwd > >() ||
|
1328
|
-
peek< exactly< ms_calc_kwd > >() ||
|
1329
|
-
peek< exactly< webkit_calc_kwd > >()) {
|
1330
|
-
return parse_calc_function();
|
1331
|
-
}
|
1332
|
-
else if (peek< functional_schema >()) {
|
1333
|
-
return parse_function_call_schema();
|
1334
|
-
}
|
1335
|
-
else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {
|
1336
|
-
return parse_identifier_schema();
|
1337
|
-
}
|
1338
|
-
else if (peek< functional >()) {
|
1339
|
-
return parse_function_call();
|
1340
|
-
}
|
1341
|
-
else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) {
|
1342
|
-
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::PLUS, parse_factor());
|
1343
|
-
}
|
1344
|
-
else if (lex< sequence< exactly<'-'>, optional_css_whitespace, negate< number> > >()) {
|
1345
|
-
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_factor());
|
1346
|
-
}
|
1347
|
-
else if (lex< sequence< kwd_not, css_whitespace > >()) {
|
1348
|
-
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::NOT, parse_factor());
|
1349
|
-
}
|
1350
|
-
else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {
|
1351
|
-
if (parse_number_prefix()) return parse_value(); // prefix is positive
|
1352
|
-
return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_value());
|
1353
|
-
}
|
1354
|
-
else {
|
1355
|
-
return parse_value();
|
1356
|
-
}
|
1357
|
-
}
|
1358
|
-
|
1359
|
-
Expression* Parser::parse_value()
|
1360
|
-
{
|
1361
|
-
lex< css_comments >();
|
1362
|
-
if (lex< ampersand >())
|
1363
|
-
{
|
1364
|
-
return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); }
|
1365
|
-
|
1366
|
-
if (lex< important >())
|
1367
|
-
{ return new (ctx.mem) String_Constant(pstate, "!important"); }
|
1368
|
-
|
1369
|
-
const char* stop;
|
1370
|
-
if ((stop = peek< value_schema >()))
|
1371
|
-
{ return parse_value_schema(stop); }
|
1372
|
-
|
1373
|
-
if (lex< kwd_true >())
|
1374
|
-
{ return new (ctx.mem) Boolean(pstate, true); }
|
1375
|
-
|
1376
|
-
if (lex< kwd_false >())
|
1377
|
-
{ return new (ctx.mem) Boolean(pstate, false); }
|
1378
|
-
|
1379
|
-
if (lex< kwd_null >())
|
1380
|
-
{ return new (ctx.mem) Null(pstate); }
|
1381
|
-
|
1382
|
-
if (lex< identifier >()) {
|
1383
|
-
String_Constant* str = new (ctx.mem) String_Quoted(pstate, lexed);
|
1384
|
-
// Dont' delay this string if it is a name color. Fixes #652.
|
1385
|
-
str->is_delayed(ctx.names_to_colors.count(unquote(lexed)) == 0);
|
1386
|
-
return str;
|
1387
|
-
}
|
1388
|
-
|
1389
|
-
if (lex< percentage >())
|
1390
|
-
{ return new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed); }
|
1391
|
-
|
1392
|
-
// match hex number first because 0x000 looks like a number followed by an indentifier
|
1393
|
-
if (lex< alternatives< hex, hex0 > >())
|
1394
|
-
{ return new (ctx.mem) Textual(pstate, Textual::HEX, lexed); }
|
1395
|
-
|
1396
|
-
// also handle the 10em- foo special case
|
1397
|
-
if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())
|
1398
|
-
{ return new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); }
|
1399
|
-
|
1400
|
-
if (lex< number >())
|
1401
|
-
{ return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); }
|
1402
|
-
|
1403
|
-
if (peek< quoted_string >())
|
1404
|
-
{ return parse_string(); }
|
1405
|
-
|
1406
|
-
if (lex< variable >())
|
1407
|
-
{ return new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); }
|
1408
|
-
|
1409
|
-
// Special case handling for `%` proceeding an interpolant.
|
1410
|
-
if (lex< sequence< exactly<'%'>, optional< percentage > > >())
|
1411
|
-
{ return new (ctx.mem) String_Quoted(pstate, lexed); }
|
1412
|
-
|
1413
|
-
error("error reading values after " + lexed.to_string(), pstate);
|
1414
|
-
|
1415
|
-
// unreachable statement
|
1416
|
-
return 0;
|
1417
|
-
}
|
1418
|
-
|
1419
|
-
// this parses interpolation inside other strings
|
1420
|
-
// means the result should later be quoted again
|
1421
|
-
String* Parser::parse_interpolated_chunk(Token chunk, bool constant)
|
1422
|
-
{
|
1423
|
-
const char* i = chunk.begin;
|
1424
|
-
// see if there any interpolants
|
1425
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
|
1426
|
-
if (!p) {
|
1427
|
-
String_Quoted* str_quoted = new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));
|
1428
|
-
if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
|
1429
|
-
str_quoted->is_delayed(true);
|
1430
|
-
return str_quoted;
|
1431
|
-
}
|
1432
|
-
|
1433
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1434
|
-
while (i < chunk.end) {
|
1435
|
-
p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);
|
1436
|
-
if (p) {
|
1437
|
-
if (i < p) {
|
1438
|
-
// accumulate the preceding segment if it's nonempty
|
1439
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));
|
1440
|
-
}
|
1441
|
-
// we need to skip anything inside strings
|
1442
|
-
// create a new target in parser/prelexer
|
1443
|
-
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1444
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1445
|
-
}
|
1446
|
-
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
|
1447
|
-
if (j) { --j;
|
1448
|
-
// parse the interpolant and accumulate it
|
1449
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1450
|
-
interp_node->is_interpolant(true);
|
1451
|
-
(*schema) << interp_node;
|
1452
|
-
i = j;
|
1453
|
-
}
|
1454
|
-
else {
|
1455
|
-
// throw an error if the interpolant is unterminated
|
1456
|
-
error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);
|
1457
|
-
}
|
1458
|
-
}
|
1459
|
-
else { // no interpolants left; add the last segment if nonempty
|
1460
|
-
// check if we need quotes here (was not sure after merge)
|
1461
|
-
if (i < chunk.end) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));
|
1462
|
-
break;
|
1463
|
-
}
|
1464
|
-
++ i;
|
1465
|
-
}
|
1466
|
-
return schema;
|
1467
|
-
}
|
1468
|
-
|
1469
|
-
String_Constant* Parser::parse_static_expression()
|
1470
|
-
{
|
1471
|
-
if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {
|
1472
|
-
return parse_static_value();
|
1473
|
-
}
|
1474
|
-
return 0;
|
1475
|
-
}
|
1476
|
-
|
1477
|
-
String_Constant* Parser::parse_static_value()
|
1478
|
-
{
|
1479
|
-
lex< static_value >();
|
1480
|
-
Token str(lexed);
|
1481
|
-
--str.end;
|
1482
|
-
--position;
|
1483
|
-
|
1484
|
-
String_Constant* str_node = new (ctx.mem) String_Constant(pstate, str.time_wspace());
|
1485
|
-
str_node->is_delayed(true);
|
1486
|
-
return str_node;
|
1487
|
-
}
|
1488
|
-
|
1489
|
-
String* Parser::parse_string()
|
1490
|
-
{
|
1491
|
-
lex< quoted_string >();
|
1492
|
-
Token token(lexed);
|
1493
|
-
return parse_interpolated_chunk(token);
|
1494
|
-
}
|
1495
|
-
|
1496
|
-
String* Parser::parse_ie_property()
|
1497
|
-
{
|
1498
|
-
lex< ie_property >();
|
1499
|
-
Token str(lexed);
|
1500
|
-
const char* i = str.begin;
|
1501
|
-
// see if there any interpolants
|
1502
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);
|
1503
|
-
if (!p) {
|
1504
|
-
String_Constant* str_node = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(str.begin, str.end)));
|
1505
|
-
str_node->is_delayed(true);
|
1506
|
-
str_node->quote_mark('*');
|
1507
|
-
return str_node;
|
1508
|
-
}
|
1509
|
-
|
1510
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1511
|
-
while (i < str.end) {
|
1512
|
-
p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);
|
1513
|
-
if (p) {
|
1514
|
-
if (i < p) {
|
1515
|
-
String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, p))); // accumulate the preceding segment if it's nonempty
|
1516
|
-
part->is_delayed(true);
|
1517
|
-
part->quote_mark('*'); // avoid unquote in interpolation
|
1518
|
-
(*schema) << part;
|
1519
|
-
}
|
1520
|
-
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1521
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1522
|
-
}
|
1523
|
-
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
|
1524
|
-
if (j) {
|
1525
|
-
// parse the interpolant and accumulate it
|
1526
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1527
|
-
interp_node->is_interpolant(true);
|
1528
|
-
(*schema) << interp_node;
|
1529
|
-
i = j;
|
1530
|
-
}
|
1531
|
-
else {
|
1532
|
-
// throw an error if the interpolant is unterminated
|
1533
|
-
error("unterminated interpolant inside IE function " + str.to_string(), pstate);
|
1534
|
-
}
|
1535
|
-
}
|
1536
|
-
else { // no interpolants left; add the last segment if nonempty
|
1537
|
-
if (i < str.end) {
|
1538
|
-
String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, str.end)));
|
1539
|
-
part->is_delayed(true);
|
1540
|
-
part->quote_mark('*'); // avoid unquote in interpolation
|
1541
|
-
(*schema) << part;
|
1542
|
-
}
|
1543
|
-
break;
|
1544
|
-
}
|
1545
|
-
}
|
1546
|
-
return schema;
|
1547
|
-
}
|
1548
|
-
|
1549
|
-
String* Parser::parse_ie_keyword_arg()
|
1550
|
-
{
|
1551
|
-
String_Schema* kwd_arg = new (ctx.mem) String_Schema(pstate, 3);
|
1552
|
-
if (lex< variable >()) {
|
1553
|
-
*kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
|
1554
|
-
} else {
|
1555
|
-
lex< alternatives< identifier_schema, identifier > >();
|
1556
|
-
*kwd_arg << new (ctx.mem) String_Constant(pstate, lexed);
|
1557
|
-
}
|
1558
|
-
lex< exactly<'='> >();
|
1559
|
-
*kwd_arg << new (ctx.mem) String_Constant(pstate, lexed);
|
1560
|
-
if (peek< variable >()) *kwd_arg << parse_list();
|
1561
|
-
else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed));
|
1562
|
-
else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }
|
1563
|
-
return kwd_arg;
|
1564
|
-
}
|
1565
|
-
|
1566
|
-
String_Schema* Parser::parse_value_schema(const char* stop)
|
1567
|
-
{
|
1568
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1569
|
-
size_t num_items = 0;
|
1570
|
-
if (peek<exactly<'}'>>()) {
|
1571
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1572
|
-
}
|
1573
|
-
while (position < stop) {
|
1574
|
-
if (lex< spaces >() && num_items) {
|
1575
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, " ");
|
1576
|
-
}
|
1577
|
-
else if (lex< interpolant >()) {
|
1578
|
-
Token insides(Token(lexed.begin + 2, lexed.end - 1));
|
1579
|
-
Expression* interp_node;
|
1580
|
-
Parser p = Parser::from_token(insides, ctx, pstate);
|
1581
|
-
if (!(interp_node = p.parse_static_expression())) {
|
1582
|
-
interp_node = p.parse_list();
|
1583
|
-
interp_node->is_interpolant(true);
|
1584
|
-
}
|
1585
|
-
(*schema) << interp_node;
|
1586
|
-
}
|
1587
|
-
else if (lex< exactly<'%'> >()) {
|
1588
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, lexed);
|
1589
|
-
}
|
1590
|
-
else if (lex< identifier >()) {
|
1591
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1592
|
-
}
|
1593
|
-
else if (lex< percentage >()) {
|
1594
|
-
(*schema) << new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed);
|
1595
|
-
}
|
1596
|
-
else if (lex< dimension >()) {
|
1597
|
-
(*schema) << new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed);
|
1598
|
-
}
|
1599
|
-
else if (lex< number >()) {
|
1600
|
-
Expression* factor = new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed);
|
1601
|
-
if (peek< class_char< static_ops > >()) {
|
1602
|
-
(*schema) << parse_operators(factor);
|
1603
|
-
} else {
|
1604
|
-
(*schema) << factor;
|
1605
|
-
}
|
1606
|
-
}
|
1607
|
-
else if (lex< hex >()) {
|
1608
|
-
(*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed));
|
1609
|
-
}
|
1610
|
-
else if (lex < exactly < '-' > >()) {
|
1611
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, lexed);
|
1612
|
-
}
|
1613
|
-
else if (lex< quoted_string >()) {
|
1614
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1615
|
-
}
|
1616
|
-
else if (lex< variable >()) {
|
1617
|
-
(*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));
|
1618
|
-
}
|
1619
|
-
else if (peek< parenthese_scope >()) {
|
1620
|
-
(*schema) << parse_factor();
|
1621
|
-
}
|
1622
|
-
else {
|
1623
|
-
error("error parsing interpolated value", pstate);
|
1624
|
-
}
|
1625
|
-
++num_items;
|
1626
|
-
}
|
1627
|
-
return schema;
|
1628
|
-
}
|
1629
|
-
|
1630
|
-
/* not used anymore - remove?
|
1631
|
-
String_Schema* Parser::parse_url_schema()
|
1632
|
-
{
|
1633
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1634
|
-
|
1635
|
-
while (position < end) {
|
1636
|
-
if (position[0] == '/') {
|
1637
|
-
lexed = Token(position, position+1, before_token);
|
1638
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1639
|
-
++position;
|
1640
|
-
}
|
1641
|
-
else if (lex< interpolant >()) {
|
1642
|
-
Token insides(Token(lexed.begin + 2, lexed.end - 1, before_token));
|
1643
|
-
Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
|
1644
|
-
interp_node->is_interpolant(true);
|
1645
|
-
(*schema) << interp_node;
|
1646
|
-
}
|
1647
|
-
else if (lex< sequence< identifier, exactly<':'> > >()) {
|
1648
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1649
|
-
}
|
1650
|
-
else if (lex< filename >()) {
|
1651
|
-
(*schema) << new (ctx.mem) String_Quoted(pstate, lexed);
|
1652
|
-
}
|
1653
|
-
else {
|
1654
|
-
error("error parsing interpolated url", pstate);
|
1655
|
-
}
|
1656
|
-
}
|
1657
|
-
return schema;
|
1658
|
-
} */
|
1659
|
-
|
1660
|
-
// this parses interpolation outside other strings
|
1661
|
-
// means the result must not be quoted again later
|
1662
|
-
String* Parser::parse_identifier_schema()
|
1663
|
-
{
|
1664
|
-
// first lex away whatever we have found
|
1665
|
-
lex< sequence< optional< exactly<'*'> >, identifier_schema > >();
|
1666
|
-
Token id(lexed);
|
1667
|
-
const char* i = id.begin;
|
1668
|
-
// see if there any interpolants
|
1669
|
-
const char* p = find_first_in_interval< exactly<hash_lbrace> >(id.begin, id.end);
|
1670
|
-
if (!p) {
|
1671
|
-
return new (ctx.mem) String_Quoted(pstate, string(id.begin, id.end));
|
1672
|
-
}
|
1673
|
-
|
1674
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1675
|
-
while (i < id.end) {
|
1676
|
-
p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);
|
1677
|
-
if (p) {
|
1678
|
-
if (i < p) {
|
1679
|
-
// accumulate the preceding segment if it's nonempty
|
1680
|
-
(*schema) << new (ctx.mem) String_Constant(pstate, string(i, p));
|
1681
|
-
}
|
1682
|
-
// we need to skip anything inside strings
|
1683
|
-
// create a new target in parser/prelexer
|
1684
|
-
if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;
|
1685
|
-
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
|
1686
|
-
}
|
1687
|
-
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
|
1688
|
-
if (j) {
|
1689
|
-
// parse the interpolant and accumulate it
|
1690
|
-
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list();
|
1691
|
-
interp_node->is_interpolant(true);
|
1692
|
-
(*schema) << interp_node;
|
1693
|
-
schema->has_interpolants(true);
|
1694
|
-
i = j;
|
1695
|
-
}
|
1696
|
-
else {
|
1697
|
-
// throw an error if the interpolant is unterminated
|
1698
|
-
error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);
|
1699
|
-
}
|
1700
|
-
}
|
1701
|
-
else { // no interpolants left; add the last segment if nonempty
|
1702
|
-
if (i < end) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, id.end));
|
1703
|
-
break;
|
1704
|
-
}
|
1705
|
-
}
|
1706
|
-
return schema;
|
1707
|
-
}
|
1708
|
-
|
1709
|
-
Function_Call* Parser::parse_calc_function()
|
1710
|
-
{
|
1711
|
-
lex< identifier >();
|
1712
|
-
string name(lexed);
|
1713
|
-
ParserState call_pos = pstate;
|
1714
|
-
lex< exactly<'('> >();
|
1715
|
-
ParserState arg_pos = pstate;
|
1716
|
-
const char* arg_beg = position;
|
1717
|
-
parse_list();
|
1718
|
-
const char* arg_end = position;
|
1719
|
-
lex< exactly<')'> >();
|
1720
|
-
|
1721
|
-
Argument* arg = new (ctx.mem) Argument(arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));
|
1722
|
-
Arguments* args = new (ctx.mem) Arguments(arg_pos);
|
1723
|
-
*args << arg;
|
1724
|
-
return new (ctx.mem) Function_Call(call_pos, name, args);
|
1725
|
-
}
|
1726
|
-
|
1727
|
-
Function_Call* Parser::parse_function_call()
|
1728
|
-
{
|
1729
|
-
lex< identifier >();
|
1730
|
-
string name(lexed);
|
1731
|
-
|
1732
|
-
ParserState call_pos = pstate;
|
1733
|
-
Arguments* args = parse_arguments(name == "url");
|
1734
|
-
return new (ctx.mem) Function_Call(call_pos, name, args);
|
1735
|
-
}
|
1736
|
-
|
1737
|
-
Function_Call_Schema* Parser::parse_function_call_schema()
|
1738
|
-
{
|
1739
|
-
String* name = parse_identifier_schema();
|
1740
|
-
ParserState source_position_of_call = pstate;
|
1741
|
-
|
1742
|
-
Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(source_position_of_call, name, parse_arguments());
|
1743
|
-
return the_call;
|
1744
|
-
}
|
1745
|
-
|
1746
|
-
If* Parser::parse_if_directive(bool else_if)
|
1747
|
-
{
|
1748
|
-
lex< kwd_if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());
|
1749
|
-
ParserState if_source_position = pstate;
|
1750
|
-
Expression* predicate = parse_list();
|
1751
|
-
predicate->is_delayed(false);
|
1752
|
-
if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if", pstate);
|
1753
|
-
Block* consequent = parse_block();
|
1754
|
-
Block* alternative = 0;
|
1755
|
-
|
1756
|
-
if (lex< elseif_directive >()) {
|
1757
|
-
alternative = new (ctx.mem) Block(pstate);
|
1758
|
-
(*alternative) << parse_if_directive(true);
|
1759
|
-
}
|
1760
|
-
else if (lex< kwd_else_directive >()) {
|
1761
|
-
if (!peek< exactly<'{'> >()) {
|
1762
|
-
error("expected '{' after @else", pstate);
|
1763
|
-
}
|
1764
|
-
else {
|
1765
|
-
alternative = parse_block();
|
1766
|
-
}
|
1767
|
-
}
|
1768
|
-
return new (ctx.mem) If(if_source_position, predicate, consequent, alternative);
|
1769
|
-
}
|
1770
|
-
|
1771
|
-
For* Parser::parse_for_directive()
|
1772
|
-
{
|
1773
|
-
lex< kwd_for_directive >();
|
1774
|
-
ParserState for_source_position = pstate;
|
1775
|
-
if (!lex< variable >()) error("@for directive requires an iteration variable", pstate);
|
1776
|
-
string var(Util::normalize_underscores(lexed));
|
1777
|
-
if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);
|
1778
|
-
Expression* lower_bound = parse_expression();
|
1779
|
-
lower_bound->is_delayed(false);
|
1780
|
-
bool inclusive = false;
|
1781
|
-
if (lex< kwd_through >()) inclusive = true;
|
1782
|
-
else if (lex< kwd_to >()) inclusive = false;
|
1783
|
-
else error("expected 'through' or 'to' keyword in @for directive", pstate);
|
1784
|
-
Expression* upper_bound = parse_expression();
|
1785
|
-
upper_bound->is_delayed(false);
|
1786
|
-
if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive", pstate);
|
1787
|
-
Block* body = parse_block();
|
1788
|
-
return new (ctx.mem) For(for_source_position, var, lower_bound, upper_bound, body, inclusive);
|
1789
|
-
}
|
1790
|
-
|
1791
|
-
Each* Parser::parse_each_directive()
|
1792
|
-
{
|
1793
|
-
lex < kwd_each_directive >();
|
1794
|
-
ParserState each_source_position = pstate;
|
1795
|
-
if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
|
1796
|
-
vector<string> vars;
|
1797
|
-
vars.push_back(Util::normalize_underscores(lexed));
|
1798
|
-
while (lex< exactly<','> >()) {
|
1799
|
-
if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
|
1800
|
-
vars.push_back(Util::normalize_underscores(lexed));
|
1801
|
-
}
|
1802
|
-
if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);
|
1803
|
-
Expression* list = parse_list();
|
1804
|
-
list->is_delayed(false);
|
1805
|
-
if (list->concrete_type() == Expression::LIST) {
|
1806
|
-
List* l = static_cast<List*>(list);
|
1807
|
-
for (size_t i = 0, L = l->length(); i < L; ++i) {
|
1808
|
-
(*l)[i]->is_delayed(false);
|
1809
|
-
}
|
1810
|
-
}
|
1811
|
-
if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive", pstate);
|
1812
|
-
Block* body = parse_block();
|
1813
|
-
return new (ctx.mem) Each(each_source_position, vars, list, body);
|
1814
|
-
}
|
1815
|
-
|
1816
|
-
While* Parser::parse_while_directive()
|
1817
|
-
{
|
1818
|
-
lex< kwd_while_directive >();
|
1819
|
-
ParserState while_source_position = pstate;
|
1820
|
-
Expression* predicate = parse_list();
|
1821
|
-
predicate->is_delayed(false);
|
1822
|
-
Block* body = parse_block();
|
1823
|
-
return new (ctx.mem) While(while_source_position, predicate, body);
|
1824
|
-
}
|
1825
|
-
|
1826
|
-
Media_Block* Parser::parse_media_block()
|
1827
|
-
{
|
1828
|
-
lex< kwd_media >();
|
1829
|
-
ParserState media_source_position = pstate;
|
1830
|
-
|
1831
|
-
List* media_queries = parse_media_queries();
|
1832
|
-
|
1833
|
-
if (!peek< exactly<'{'> >()) {
|
1834
|
-
error("expected '{' in media query", pstate);
|
1835
|
-
}
|
1836
|
-
Media_Block* media_block = new (ctx.mem) Media_Block(media_source_position, media_queries, 0);
|
1837
|
-
Media_Block* prev_media_block = last_media_block;
|
1838
|
-
last_media_block = media_block;
|
1839
|
-
media_block->block(parse_block());
|
1840
|
-
last_media_block = prev_media_block;
|
1841
|
-
|
1842
|
-
return media_block;
|
1843
|
-
}
|
1844
|
-
|
1845
|
-
List* Parser::parse_media_queries()
|
1846
|
-
{
|
1847
|
-
List* media_queries = new (ctx.mem) List(pstate, 0, List::COMMA);
|
1848
|
-
if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query();
|
1849
|
-
while (lex< exactly<','> >()) (*media_queries) << parse_media_query();
|
1850
|
-
return media_queries;
|
1851
|
-
}
|
1852
|
-
|
1853
|
-
// Expression* Parser::parse_media_query()
|
1854
|
-
Media_Query* Parser::parse_media_query()
|
1855
|
-
{
|
1856
|
-
Media_Query* media_query = new (ctx.mem) Media_Query(pstate);
|
1857
|
-
|
1858
|
-
if (lex< exactly< not_kwd > >()) media_query->is_negated(true);
|
1859
|
-
else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true);
|
1860
|
-
|
1861
|
-
if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema());
|
1862
|
-
else if (lex< identifier >()) media_query->media_type(parse_interpolated_chunk(lexed));
|
1863
|
-
else (*media_query) << parse_media_expression();
|
1864
|
-
|
1865
|
-
while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();
|
1866
|
-
if (peek< identifier_schema >()) {
|
1867
|
-
String_Schema* schema = new (ctx.mem) String_Schema(pstate);
|
1868
|
-
*schema << media_query->media_type();
|
1869
|
-
*schema << new (ctx.mem) String_Constant(pstate, " ");
|
1870
|
-
*schema << parse_identifier_schema();
|
1871
|
-
media_query->media_type(schema);
|
1872
|
-
}
|
1873
|
-
while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();
|
1874
|
-
return media_query;
|
1875
|
-
}
|
1876
|
-
|
1877
|
-
Media_Query_Expression* Parser::parse_media_expression()
|
1878
|
-
{
|
1879
|
-
if (peek< identifier_schema >()) {
|
1880
|
-
String* ss = parse_identifier_schema();
|
1881
|
-
return new (ctx.mem) Media_Query_Expression(pstate, ss, 0, true);
|
1882
|
-
}
|
1883
|
-
if (!lex< exactly<'('> >()) {
|
1884
|
-
error("media query expression must begin with '('", pstate);
|
1885
|
-
}
|
1886
|
-
Expression* feature = 0;
|
1887
|
-
if (peek< exactly<')'> >()) {
|
1888
|
-
error("media feature required in media query expression", pstate);
|
1889
|
-
}
|
1890
|
-
feature = parse_expression();
|
1891
|
-
Expression* expression = 0;
|
1892
|
-
if (lex< exactly<':'> >()) {
|
1893
|
-
expression = parse_list();
|
1894
|
-
}
|
1895
|
-
if (!lex< exactly<')'> >()) {
|
1896
|
-
error("unclosed parenthesis in media query expression", pstate);
|
1897
|
-
}
|
1898
|
-
return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression);
|
1899
|
-
}
|
1900
|
-
|
1901
|
-
Feature_Block* Parser::parse_feature_block()
|
1902
|
-
{
|
1903
|
-
lex< kwd_supports >();
|
1904
|
-
ParserState supports_source_position = pstate;
|
1905
|
-
|
1906
|
-
Feature_Query* feature_queries = parse_feature_queries();
|
1907
|
-
|
1908
|
-
if (!peek< exactly<'{'> >()) {
|
1909
|
-
error("expected '{' in feature query", pstate);
|
1910
|
-
}
|
1911
|
-
Block* block = parse_block();
|
1912
|
-
|
1913
|
-
return new (ctx.mem) Feature_Block(supports_source_position, feature_queries, block);
|
1914
|
-
}
|
1915
|
-
|
1916
|
-
Feature_Query* Parser::parse_feature_queries()
|
1917
|
-
{
|
1918
|
-
Feature_Query* fq = new (ctx.mem) Feature_Query(pstate);
|
1919
|
-
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate);
|
1920
|
-
cond->is_root(true);
|
1921
|
-
while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
|
1922
|
-
(*cond) << parse_feature_query();
|
1923
|
-
(*fq) << cond;
|
1924
|
-
|
1925
|
-
if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate);
|
1926
|
-
|
1927
|
-
return fq;
|
1928
|
-
}
|
1929
|
-
|
1930
|
-
Feature_Query_Condition* Parser::parse_feature_query()
|
1931
|
-
{
|
1932
|
-
if (peek< kwd_not >(position)) return parse_supports_negation();
|
1933
|
-
else if (peek< kwd_and >(position)) return parse_supports_conjunction();
|
1934
|
-
else if (peek< kwd_or >(position)) return parse_supports_disjunction();
|
1935
|
-
else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();
|
1936
|
-
else return parse_supports_declaration();
|
1937
|
-
}
|
1938
|
-
|
1939
|
-
Feature_Query_Condition* Parser::parse_feature_query_in_parens()
|
1940
|
-
{
|
1941
|
-
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate);
|
1942
|
-
|
1943
|
-
if (!lex< exactly<'('> >()) error("@supports declaration expected '('", pstate);
|
1944
|
-
while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))
|
1945
|
-
(*cond) << parse_feature_query();
|
1946
|
-
if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration", pstate);
|
1947
|
-
|
1948
|
-
return (cond->length() == 1) ? (*cond)[0] : cond;
|
1949
|
-
}
|
1950
|
-
|
1951
|
-
Feature_Query_Condition* Parser::parse_supports_negation()
|
1952
|
-
{
|
1953
|
-
lex< kwd_not >();
|
1954
|
-
|
1955
|
-
Feature_Query_Condition* cond = parse_feature_query();
|
1956
|
-
cond->operand(Feature_Query_Condition::NOT);
|
1957
|
-
|
1958
|
-
return cond;
|
1959
|
-
}
|
1960
|
-
|
1961
|
-
Feature_Query_Condition* Parser::parse_supports_conjunction()
|
1962
|
-
{
|
1963
|
-
lex< kwd_and >();
|
1964
|
-
|
1965
|
-
Feature_Query_Condition* cond = parse_feature_query();
|
1966
|
-
cond->operand(Feature_Query_Condition::AND);
|
1967
|
-
|
1968
|
-
return cond;
|
1969
|
-
}
|
1970
|
-
|
1971
|
-
Feature_Query_Condition* Parser::parse_supports_disjunction()
|
1972
|
-
{
|
1973
|
-
lex< kwd_or >();
|
1974
|
-
|
1975
|
-
Feature_Query_Condition* cond = parse_feature_query();
|
1976
|
-
cond->operand(Feature_Query_Condition::OR);
|
1977
|
-
|
1978
|
-
return cond;
|
1979
|
-
}
|
1980
|
-
|
1981
|
-
Feature_Query_Condition* Parser::parse_supports_declaration()
|
1982
|
-
{
|
1983
|
-
Declaration* declaration = parse_declaration();
|
1984
|
-
Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->pstate(),
|
1985
|
-
1,
|
1986
|
-
declaration->property(),
|
1987
|
-
declaration->value());
|
1988
|
-
return cond;
|
1989
|
-
}
|
1990
|
-
|
1991
|
-
At_Root_Block* Parser::parse_at_root_block()
|
1992
|
-
{
|
1993
|
-
lex<kwd_at_root>();
|
1994
|
-
ParserState at_source_position = pstate;
|
1995
|
-
Block* body = 0;
|
1996
|
-
At_Root_Expression* expr = 0;
|
1997
|
-
Selector_Lookahead lookahead_result;
|
1998
|
-
in_at_root = true;
|
1999
|
-
if (peek< exactly<'('> >()) {
|
2000
|
-
expr = parse_at_root_expression();
|
2001
|
-
body = parse_block();
|
2002
|
-
}
|
2003
|
-
else if (peek< exactly<'{'> >()) {
|
2004
|
-
body = parse_block();
|
2005
|
-
}
|
2006
|
-
else if ((lookahead_result = lookahead_for_selector(position)).found) {
|
2007
|
-
Ruleset* r = parse_ruleset(lookahead_result);
|
2008
|
-
body = new (ctx.mem) Block(r->pstate(), 1);
|
2009
|
-
*body << r;
|
2010
|
-
}
|
2011
|
-
in_at_root = false;
|
2012
|
-
At_Root_Block* at_root = new (ctx.mem) At_Root_Block(at_source_position, body);
|
2013
|
-
if (expr) at_root->expression(expr);
|
2014
|
-
return at_root;
|
2015
|
-
}
|
2016
|
-
|
2017
|
-
At_Root_Expression* Parser::parse_at_root_expression()
|
2018
|
-
{
|
2019
|
-
lex< exactly<'('> >();
|
2020
|
-
if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);
|
2021
|
-
|
2022
|
-
if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {
|
2023
|
-
css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");
|
2024
|
-
}
|
2025
|
-
|
2026
|
-
Declaration* declaration = parse_declaration();
|
2027
|
-
List* value = new (ctx.mem) List(declaration->value()->pstate(), 1);
|
2028
|
-
|
2029
|
-
if (declaration->value()->concrete_type() == Expression::LIST) {
|
2030
|
-
value = static_cast<List*>(declaration->value());
|
2031
|
-
}
|
2032
|
-
else *value << declaration->value();
|
2033
|
-
|
2034
|
-
At_Root_Expression* cond = new (ctx.mem) At_Root_Expression(declaration->pstate(),
|
2035
|
-
declaration->property(),
|
2036
|
-
value);
|
2037
|
-
if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);
|
2038
|
-
return cond;
|
2039
|
-
}
|
2040
|
-
|
2041
|
-
At_Rule* Parser::parse_at_rule()
|
2042
|
-
{
|
2043
|
-
lex<at_keyword>();
|
2044
|
-
string kwd(lexed);
|
2045
|
-
ParserState at_source_position = pstate;
|
2046
|
-
Selector* sel = 0;
|
2047
|
-
Expression* val = 0;
|
2048
|
-
Selector_Lookahead lookahead = lookahead_for_extension_target(position);
|
2049
|
-
if (lookahead.found) {
|
2050
|
-
if (lookahead.has_interpolants) {
|
2051
|
-
sel = parse_selector_schema(lookahead.found);
|
2052
|
-
}
|
2053
|
-
else {
|
2054
|
-
sel = parse_selector_group();
|
2055
|
-
}
|
2056
|
-
}
|
2057
|
-
else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {
|
2058
|
-
val = parse_list();
|
2059
|
-
}
|
2060
|
-
Block* body = 0;
|
2061
|
-
if (peek< exactly<'{'> >()) body = parse_block();
|
2062
|
-
At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body);
|
2063
|
-
if (!sel) rule->value(val);
|
2064
|
-
return rule;
|
2065
|
-
}
|
2066
|
-
|
2067
|
-
Warning* Parser::parse_warning()
|
2068
|
-
{
|
2069
|
-
lex< kwd_warn >();
|
2070
|
-
return new (ctx.mem) Warning(pstate, parse_list());
|
2071
|
-
}
|
2072
|
-
|
2073
|
-
Error* Parser::parse_error()
|
2074
|
-
{
|
2075
|
-
lex< kwd_err >();
|
2076
|
-
return new (ctx.mem) Error(pstate, parse_list());
|
2077
|
-
}
|
2078
|
-
|
2079
|
-
Debug* Parser::parse_debug()
|
2080
|
-
{
|
2081
|
-
lex< kwd_dbg >();
|
2082
|
-
return new (ctx.mem) Debug(pstate, parse_list());
|
2083
|
-
}
|
2084
|
-
|
2085
|
-
Selector_Lookahead Parser::lookahead_for_selector(const char* start)
|
2086
|
-
{
|
2087
|
-
const char* p = start ? start : position;
|
2088
|
-
const char* q;
|
2089
|
-
bool saw_stuff = false;
|
2090
|
-
bool saw_interpolant = false;
|
2091
|
-
|
2092
|
-
while ((q = peek< identifier >(p)) ||
|
2093
|
-
(q = peek< hyphens_and_identifier >(p)) ||
|
2094
|
-
(q = peek< hyphens_and_name >(p)) ||
|
2095
|
-
(q = peek< type_selector >(p)) ||
|
2096
|
-
(q = peek< id_name >(p)) ||
|
2097
|
-
(q = peek< class_name >(p)) ||
|
2098
|
-
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
2099
|
-
(q = peek< percentage >(p)) ||
|
2100
|
-
(q = peek< variable >(p)) ||
|
2101
|
-
(q = peek< dimension >(p)) ||
|
2102
|
-
(q = peek< quoted_string >(p)) ||
|
2103
|
-
(q = peek< exactly<'*'> >(p)) ||
|
2104
|
-
(q = peek< exactly<sel_deep_kwd> >(p)) ||
|
2105
|
-
(q = peek< exactly<'('> >(p)) ||
|
2106
|
-
(q = peek< exactly<')'> >(p)) ||
|
2107
|
-
(q = peek< exactly<'['> >(p)) ||
|
2108
|
-
(q = peek< exactly<']'> >(p)) ||
|
2109
|
-
(q = peek< exactly<'+'> >(p)) ||
|
2110
|
-
(q = peek< exactly<'~'> >(p)) ||
|
2111
|
-
(q = peek< exactly<'>'> >(p)) ||
|
2112
|
-
(q = peek< exactly<','> >(p)) ||
|
2113
|
-
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
|
2114
|
-
(q = peek< binomial >(p)) ||
|
2115
|
-
(q = peek< block_comment >(p)) ||
|
2116
|
-
(q = peek< sequence< optional<sign>,
|
2117
|
-
zero_plus<digit>,
|
2118
|
-
exactly<'n'> > >(p)) ||
|
2119
|
-
(q = peek< sequence< optional<sign>,
|
2120
|
-
one_plus<digit> > >(p)) ||
|
2121
|
-
(q = peek< number >(p)) ||
|
2122
|
-
(q = peek< sequence< exactly<'&'>,
|
2123
|
-
identifier_alnums > >(p)) ||
|
2124
|
-
(q = peek< exactly<'&'> >(p)) ||
|
2125
|
-
(q = peek< exactly<'%'> >(p)) ||
|
2126
|
-
(q = peek< alternatives<exact_match,
|
2127
|
-
class_match,
|
2128
|
-
dash_match,
|
2129
|
-
prefix_match,
|
2130
|
-
suffix_match,
|
2131
|
-
substring_match> >(p)) ||
|
2132
|
-
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
2133
|
-
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
2134
|
-
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
2135
|
-
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
2136
|
-
(q = peek< interpolant >(p))) {
|
2137
|
-
saw_stuff = true;
|
2138
|
-
p = q;
|
2139
|
-
if (*(p - 1) == '}') saw_interpolant = true;
|
2140
|
-
}
|
2141
|
-
|
2142
|
-
Selector_Lookahead result;
|
2143
|
-
result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;
|
2144
|
-
result.has_interpolants = saw_interpolant;
|
2145
|
-
|
2146
|
-
return result;
|
2147
|
-
}
|
2148
|
-
|
2149
|
-
Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)
|
2150
|
-
{
|
2151
|
-
const char* p = start ? start : position;
|
2152
|
-
const char* q;
|
2153
|
-
bool saw_interpolant = false;
|
2154
|
-
bool saw_stuff = false;
|
2155
|
-
|
2156
|
-
while ((q = peek< identifier >(p)) ||
|
2157
|
-
(q = peek< type_selector >(p)) ||
|
2158
|
-
(q = peek< id_name >(p)) ||
|
2159
|
-
(q = peek< class_name >(p)) ||
|
2160
|
-
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
|
2161
|
-
(q = peek< percentage >(p)) ||
|
2162
|
-
(q = peek< dimension >(p)) ||
|
2163
|
-
(q = peek< quoted_string >(p)) ||
|
2164
|
-
(q = peek< exactly<'*'> >(p)) ||
|
2165
|
-
(q = peek< exactly<'('> >(p)) ||
|
2166
|
-
(q = peek< exactly<')'> >(p)) ||
|
2167
|
-
(q = peek< exactly<'['> >(p)) ||
|
2168
|
-
(q = peek< exactly<']'> >(p)) ||
|
2169
|
-
(q = peek< exactly<'+'> >(p)) ||
|
2170
|
-
(q = peek< exactly<'~'> >(p)) ||
|
2171
|
-
(q = peek< exactly<'>'> >(p)) ||
|
2172
|
-
(q = peek< exactly<','> >(p)) ||
|
2173
|
-
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
|
2174
|
-
(q = peek< binomial >(p)) ||
|
2175
|
-
(q = peek< block_comment >(p)) ||
|
2176
|
-
(q = peek< sequence< optional<sign>,
|
2177
|
-
zero_plus<digit>,
|
2178
|
-
exactly<'n'> > >(p)) ||
|
2179
|
-
(q = peek< sequence< optional<sign>,
|
2180
|
-
one_plus<digit> > >(p)) ||
|
2181
|
-
(q = peek< number >(p)) ||
|
2182
|
-
(q = peek< sequence< exactly<'&'>,
|
2183
|
-
identifier_alnums > >(p)) ||
|
2184
|
-
(q = peek< exactly<'&'> >(p)) ||
|
2185
|
-
(q = peek< exactly<'%'> >(p)) ||
|
2186
|
-
(q = peek< alternatives<exact_match,
|
2187
|
-
class_match,
|
2188
|
-
dash_match,
|
2189
|
-
prefix_match,
|
2190
|
-
suffix_match,
|
2191
|
-
substring_match> >(p)) ||
|
2192
|
-
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
2193
|
-
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
2194
|
-
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
2195
|
-
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
2196
|
-
(q = peek< interpolant >(p)) ||
|
2197
|
-
(q = peek< optional >(p))) {
|
2198
|
-
p = q;
|
2199
|
-
if (*(p - 1) == '}') saw_interpolant = true;
|
2200
|
-
saw_stuff = true;
|
2201
|
-
}
|
2202
|
-
|
2203
|
-
Selector_Lookahead result;
|
2204
|
-
result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
|
2205
|
-
result.has_interpolants = saw_interpolant;
|
2206
|
-
|
2207
|
-
return result;
|
2208
|
-
}
|
2209
|
-
|
2210
|
-
|
2211
|
-
Selector_Lookahead Parser::lookahead_for_value(const char* start)
|
2212
|
-
{
|
2213
|
-
const char* p = start ? start : position;
|
2214
|
-
const char* q;
|
2215
|
-
bool saw_interpolant = false;
|
2216
|
-
bool saw_stuff = false;
|
2217
|
-
|
2218
|
-
while ((q = peek< identifier >(p)) ||
|
2219
|
-
(q = peek< percentage >(p)) ||
|
2220
|
-
(q = peek< dimension >(p)) ||
|
2221
|
-
(q = peek< quoted_string >(p)) ||
|
2222
|
-
(q = peek< variable >(p)) ||
|
2223
|
-
(q = peek< exactly<'*'> >(p)) ||
|
2224
|
-
(q = peek< exactly<'+'> >(p)) ||
|
2225
|
-
(q = peek< exactly<'~'> >(p)) ||
|
2226
|
-
(q = peek< exactly<'>'> >(p)) ||
|
2227
|
-
(q = peek< exactly<','> >(p)) ||
|
2228
|
-
(q = peek< sequence<parenthese_scope, interpolant>>(p)) ||
|
2229
|
-
(saw_stuff && (q = peek< exactly<'-'> >(p))) ||
|
2230
|
-
(q = peek< binomial >(p)) ||
|
2231
|
-
(q = peek< block_comment >(p)) ||
|
2232
|
-
(q = peek< sequence< optional<sign>,
|
2233
|
-
zero_plus<digit>,
|
2234
|
-
exactly<'n'> > >(p)) ||
|
2235
|
-
(q = peek< sequence< optional<sign>,
|
2236
|
-
one_plus<digit> > >(p)) ||
|
2237
|
-
(q = peek< number >(p)) ||
|
2238
|
-
(q = peek< sequence< exactly<'&'>,
|
2239
|
-
identifier_alnums > >(p)) ||
|
2240
|
-
(q = peek< exactly<'&'> >(p)) ||
|
2241
|
-
(q = peek< exactly<'%'> >(p)) ||
|
2242
|
-
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
|
2243
|
-
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
|
2244
|
-
(q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||
|
2245
|
-
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
|
2246
|
-
(q = peek< interpolant >(p)) ||
|
2247
|
-
(q = peek< optional >(p))) {
|
2248
|
-
p = q;
|
2249
|
-
if (*(p - 1) == '}') saw_interpolant = true;
|
2250
|
-
saw_stuff = true;
|
2251
|
-
}
|
2252
|
-
|
2253
|
-
Selector_Lookahead result;
|
2254
|
-
result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;
|
2255
|
-
result.has_interpolants = saw_interpolant;
|
2256
|
-
|
2257
|
-
return result;
|
2258
|
-
}
|
2259
|
-
|
2260
|
-
void Parser::read_bom()
|
2261
|
-
{
|
2262
|
-
size_t skip = 0;
|
2263
|
-
string encoding;
|
2264
|
-
bool utf_8 = false;
|
2265
|
-
switch ((unsigned char) source[0]) {
|
2266
|
-
case 0xEF:
|
2267
|
-
skip = check_bom_chars(source, end, utf_8_bom, 3);
|
2268
|
-
encoding = "UTF-8";
|
2269
|
-
utf_8 = true;
|
2270
|
-
break;
|
2271
|
-
case 0xFE:
|
2272
|
-
skip = check_bom_chars(source, end, utf_16_bom_be, 2);
|
2273
|
-
encoding = "UTF-16 (big endian)";
|
2274
|
-
break;
|
2275
|
-
case 0xFF:
|
2276
|
-
skip = check_bom_chars(source, end, utf_16_bom_le, 2);
|
2277
|
-
skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);
|
2278
|
-
encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");
|
2279
|
-
break;
|
2280
|
-
case 0x00:
|
2281
|
-
skip = check_bom_chars(source, end, utf_32_bom_be, 4);
|
2282
|
-
encoding = "UTF-32 (big endian)";
|
2283
|
-
break;
|
2284
|
-
case 0x2B:
|
2285
|
-
skip = check_bom_chars(source, end, utf_7_bom_1, 4)
|
2286
|
-
| check_bom_chars(source, end, utf_7_bom_2, 4)
|
2287
|
-
| check_bom_chars(source, end, utf_7_bom_3, 4)
|
2288
|
-
| check_bom_chars(source, end, utf_7_bom_4, 4)
|
2289
|
-
| check_bom_chars(source, end, utf_7_bom_5, 5);
|
2290
|
-
encoding = "UTF-7";
|
2291
|
-
break;
|
2292
|
-
case 0xF7:
|
2293
|
-
skip = check_bom_chars(source, end, utf_1_bom, 3);
|
2294
|
-
encoding = "UTF-1";
|
2295
|
-
break;
|
2296
|
-
case 0xDD:
|
2297
|
-
skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);
|
2298
|
-
encoding = "UTF-EBCDIC";
|
2299
|
-
break;
|
2300
|
-
case 0x0E:
|
2301
|
-
skip = check_bom_chars(source, end, scsu_bom, 3);
|
2302
|
-
encoding = "SCSU";
|
2303
|
-
break;
|
2304
|
-
case 0xFB:
|
2305
|
-
skip = check_bom_chars(source, end, bocu_1_bom, 3);
|
2306
|
-
encoding = "BOCU-1";
|
2307
|
-
break;
|
2308
|
-
case 0x84:
|
2309
|
-
skip = check_bom_chars(source, end, gb_18030_bom, 4);
|
2310
|
-
encoding = "GB-18030";
|
2311
|
-
break;
|
2312
|
-
}
|
2313
|
-
if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);
|
2314
|
-
position += skip;
|
2315
|
-
}
|
2316
|
-
|
2317
|
-
size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)
|
2318
|
-
{
|
2319
|
-
size_t skip = 0;
|
2320
|
-
if (src + len > end) return 0;
|
2321
|
-
for (size_t i = 0; i < len; ++i, ++skip) {
|
2322
|
-
if ((unsigned char) src[i] != bom[i]) return 0;
|
2323
|
-
}
|
2324
|
-
return skip;
|
2325
|
-
}
|
2326
|
-
|
2327
|
-
|
2328
|
-
Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op)
|
2329
|
-
{
|
2330
|
-
for (size_t i = 0, S = operands.size(); i < S; ++i) {
|
2331
|
-
base = new (ctx.mem) Binary_Expression(pstate, op, base, operands[i]);
|
2332
|
-
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
2333
|
-
if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2334
|
-
base->is_delayed(true);
|
2335
|
-
}
|
2336
|
-
else {
|
2337
|
-
b->left()->is_delayed(false);
|
2338
|
-
b->right()->is_delayed(false);
|
2339
|
-
}
|
2340
|
-
}
|
2341
|
-
return base;
|
2342
|
-
}
|
2343
|
-
|
2344
|
-
Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops)
|
2345
|
-
{
|
2346
|
-
for (size_t i = 0, S = operands.size(); i < S; ++i) {
|
2347
|
-
base = new (ctx.mem) Binary_Expression(base->pstate(), ops[i], base, operands[i]);
|
2348
|
-
Binary_Expression* b = static_cast<Binary_Expression*>(base);
|
2349
|
-
if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {
|
2350
|
-
base->is_delayed(true);
|
2351
|
-
}
|
2352
|
-
else {
|
2353
|
-
b->left()->is_delayed(false);
|
2354
|
-
b->right()->is_delayed(false);
|
2355
|
-
}
|
2356
|
-
}
|
2357
|
-
return base;
|
2358
|
-
}
|
2359
|
-
|
2360
|
-
void Parser::error(string msg, Position pos)
|
2361
|
-
{
|
2362
|
-
throw Sass_Error(Sass_Error::syntax, ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);
|
2363
|
-
}
|
2364
|
-
|
2365
|
-
// print a css parsing error with actual context information from parsed source
|
2366
|
-
void Parser::css_error(const string& msg, const string& prefix, const string& middle)
|
2367
|
-
{
|
2368
|
-
int max_len = 14;
|
2369
|
-
const char* pos = peek < optional_spaces >();
|
2370
|
-
bool ellipsis_left = false;
|
2371
|
-
const char* pos_left(pos);
|
2372
|
-
while (*pos_left && pos_left > source) {
|
2373
|
-
if (pos - pos_left > max_len) {
|
2374
|
-
ellipsis_left = true;
|
2375
|
-
break;
|
2376
|
-
}
|
2377
|
-
const char* prev = pos_left - 1;
|
2378
|
-
if (*prev == '\r') break;
|
2379
|
-
if (*prev == '\n') break;
|
2380
|
-
if (*prev == 10) break;
|
2381
|
-
pos_left = prev;
|
2382
|
-
}
|
2383
|
-
bool ellipsis_right = false;
|
2384
|
-
const char* pos_right(pos);
|
2385
|
-
while (*pos_right && pos_right <= end) {
|
2386
|
-
if (pos_right - pos > max_len) {
|
2387
|
-
ellipsis_right = true;
|
2388
|
-
break;
|
2389
|
-
}
|
2390
|
-
if (*pos_right == '\r') break;
|
2391
|
-
if (*pos_right == '\n') break;
|
2392
|
-
if (*pos_left == 10) break;
|
2393
|
-
++ pos_right;
|
2394
|
-
}
|
2395
|
-
string left(pos_left, pos);
|
2396
|
-
string right(pos, pos_right);
|
2397
|
-
if (ellipsis_left) left = ellipsis + left;
|
2398
|
-
if (ellipsis_right) right = right + ellipsis;
|
2399
|
-
// now pass new message to the more generic error function
|
2400
|
-
error(msg + prefix + quote(left) + middle + quote(right), pstate);
|
2401
|
-
}
|
2402
|
-
|
2403
|
-
}
|