minicss 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -0
- data/README.md +124 -5
- data/Rakefile +25 -1
- data/benchmarks/bootstrap-4.css +8975 -0
- data/benchmarks/bootstrap-4.min.css +7 -0
- data/benchmarks/ruby_prof.rb +41 -0
- data/benchmarks/selectors.rb +41 -0
- data/benchmarks/stylesheet.rb +75 -0
- data/ext/minicss_scanner/extconf.rb +7 -0
- data/ext/minicss_scanner/minicss_scanner.c +1436 -0
- data/ext/minicss_scanner/minicss_scanner.h +132 -0
- data/ext/minicss_token_stream/extconf.rb +5 -0
- data/ext/minicss_token_stream/minicss_token_stream.c +195 -0
- data/lib/minicss/css/parser.rb +50 -54
- data/lib/minicss/css/refinements.rb +21 -63
- data/lib/minicss/css/token_stream.rb +1 -54
- data/lib/minicss/css/tokenizer.rb +13 -552
- data/lib/minicss/css.rb +1 -1
- data/lib/minicss/sel.rb +10 -8
- data/lib/minicss/serializer.rb +48 -52
- data/lib/minicss/version.rb +1 -1
- metadata +19 -6
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Vito Sartori on 11/10/25.
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
#ifndef MINICSS_SCANNER_MINICSS_SCANNER_H
|
|
6
|
+
#define MINICSS_SCANNER_MINICSS_SCANNER_H
|
|
7
|
+
|
|
8
|
+
#define EOF_CP (-1)
|
|
9
|
+
#define NEWLINE 0x0A
|
|
10
|
+
#define CP(c) ((int)(c))
|
|
11
|
+
#define REVERSE_SOLIDUS '\\'
|
|
12
|
+
#define QUOTATION_MARK '"'
|
|
13
|
+
#define APOSTROPHE '\''
|
|
14
|
+
#define NUMBER_SIGN '#'
|
|
15
|
+
#define LEFT_PARENTHESIS '('
|
|
16
|
+
#define RIGHT_PARENTHESIS ')'
|
|
17
|
+
#define PLUS_SIGN '+'
|
|
18
|
+
#define FULL_STOP '.'
|
|
19
|
+
#define HYPHEN_MINUS '-'
|
|
20
|
+
#define PERCENTAGE_SIGN '%'
|
|
21
|
+
#define COMMA ','
|
|
22
|
+
#define GREATER_THAN '>'
|
|
23
|
+
#define COLON ':'
|
|
24
|
+
#define SEMICOLON ';'
|
|
25
|
+
#define LESS_THAN '<'
|
|
26
|
+
#define EXCLAMATION_MARK '!'
|
|
27
|
+
#define COMMERCIAL_AT '@'
|
|
28
|
+
#define LEFT_SQUARE_BRACKET '['
|
|
29
|
+
#define RIGHT_SQUARE_BRACKET ']'
|
|
30
|
+
#define LEFT_CURLY '{'
|
|
31
|
+
#define RIGHT_CURLY '}'
|
|
32
|
+
|
|
33
|
+
#define DEFINE_REUSABLE_SYMBOL(name) static ID id_type_##name; static VALUE sym_##name;
|
|
34
|
+
#define INITIALIZE_REUSABLE_SYMBOL(name) id_type_##name = rb_intern(#name); sym_##name = ID2SYM(id_type_##name);
|
|
35
|
+
|
|
36
|
+
DEFINE_REUSABLE_SYMBOL(line);
|
|
37
|
+
DEFINE_REUSABLE_SYMBOL(column);
|
|
38
|
+
DEFINE_REUSABLE_SYMBOL(offset);
|
|
39
|
+
DEFINE_REUSABLE_SYMBOL(whitespace);
|
|
40
|
+
DEFINE_REUSABLE_SYMBOL(start);
|
|
41
|
+
DEFINE_REUSABLE_SYMBOL(end);
|
|
42
|
+
DEFINE_REUSABLE_SYMBOL(unicode_range);
|
|
43
|
+
DEFINE_REUSABLE_SYMBOL(new);
|
|
44
|
+
DEFINE_REUSABLE_SYMBOL(bad_string);
|
|
45
|
+
DEFINE_REUSABLE_SYMBOL(literal);
|
|
46
|
+
DEFINE_REUSABLE_SYMBOL(quoting);
|
|
47
|
+
DEFINE_REUSABLE_SYMBOL(string);
|
|
48
|
+
DEFINE_REUSABLE_SYMBOL(value);
|
|
49
|
+
DEFINE_REUSABLE_SYMBOL(type);
|
|
50
|
+
DEFINE_REUSABLE_SYMBOL(integer);
|
|
51
|
+
DEFINE_REUSABLE_SYMBOL(number);
|
|
52
|
+
DEFINE_REUSABLE_SYMBOL(sign_character);
|
|
53
|
+
DEFINE_REUSABLE_SYMBOL(unit);
|
|
54
|
+
DEFINE_REUSABLE_SYMBOL(dimension);
|
|
55
|
+
DEFINE_REUSABLE_SYMBOL(percentage);
|
|
56
|
+
DEFINE_REUSABLE_SYMBOL(function);
|
|
57
|
+
DEFINE_REUSABLE_SYMBOL(name);
|
|
58
|
+
DEFINE_REUSABLE_SYMBOL(ident);
|
|
59
|
+
DEFINE_REUSABLE_SYMBOL(url);
|
|
60
|
+
DEFINE_REUSABLE_SYMBOL(bad_url);
|
|
61
|
+
DEFINE_REUSABLE_SYMBOL(flag);
|
|
62
|
+
DEFINE_REUSABLE_SYMBOL(hash);
|
|
63
|
+
DEFINE_REUSABLE_SYMBOL(delim);
|
|
64
|
+
DEFINE_REUSABLE_SYMBOL(left_parenthesis);
|
|
65
|
+
DEFINE_REUSABLE_SYMBOL(right_parenthesis);
|
|
66
|
+
DEFINE_REUSABLE_SYMBOL(comma);
|
|
67
|
+
DEFINE_REUSABLE_SYMBOL(cdc);
|
|
68
|
+
DEFINE_REUSABLE_SYMBOL(colon);
|
|
69
|
+
DEFINE_REUSABLE_SYMBOL(semicolon);
|
|
70
|
+
DEFINE_REUSABLE_SYMBOL(cdo);
|
|
71
|
+
DEFINE_REUSABLE_SYMBOL(at_keyword);
|
|
72
|
+
DEFINE_REUSABLE_SYMBOL(left_square_bracket);
|
|
73
|
+
DEFINE_REUSABLE_SYMBOL(right_square_bracket);
|
|
74
|
+
DEFINE_REUSABLE_SYMBOL(left_curly);
|
|
75
|
+
DEFINE_REUSABLE_SYMBOL(right_curly);
|
|
76
|
+
|
|
77
|
+
// Definitions -----------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* scanner_t - State for a CSS tokenizer scanner.
|
|
81
|
+
*
|
|
82
|
+
* Holds the current position, lookahead, and bookkeeping needed to
|
|
83
|
+
* tokenize a UTF-8 encoded CSS source string. This structure is the
|
|
84
|
+
* central state passed around by the tokenizer.
|
|
85
|
+
*
|
|
86
|
+
* Fields:
|
|
87
|
+
* str - Ruby String VALUE containing the source (kept alive
|
|
88
|
+
* to ensure the underlying buffer remains valid).
|
|
89
|
+
*
|
|
90
|
+
* p - Current byte pointer into the UTF-8 source.
|
|
91
|
+
* end - Pointer to one past the last byte of the source.
|
|
92
|
+
*
|
|
93
|
+
* idx_cp - Index of the current code point (0-based).
|
|
94
|
+
* line - Current line number (1-based, for error reporting).
|
|
95
|
+
* col - Current column number (1-based, for error reporting).
|
|
96
|
+
*
|
|
97
|
+
* look - Lookahead buffer of decoded code points:
|
|
98
|
+
* look[0] = next code point (peek)
|
|
99
|
+
* look[1] = second ahead
|
|
100
|
+
* look[2] = third ahead
|
|
101
|
+
* look[3] = fourth ahead
|
|
102
|
+
* Used for disambiguating tokens (e.g. distinguishing
|
|
103
|
+
* between `--ident` and `-->`).
|
|
104
|
+
*
|
|
105
|
+
* Notes:
|
|
106
|
+
* - `p` is advanced as tokens are consumed.
|
|
107
|
+
* - `line` and `col` are updated to provide CSS-spec-compliant
|
|
108
|
+
* source location information.
|
|
109
|
+
* - Lookahead avoids re-decoding UTF-8 sequences during parsing.
|
|
110
|
+
*/
|
|
111
|
+
typedef struct {
|
|
112
|
+
VALUE str;
|
|
113
|
+
VALUE owner;
|
|
114
|
+
VALUE tokens;
|
|
115
|
+
bool allow_unicode_ranges;
|
|
116
|
+
const uint8_t *p;
|
|
117
|
+
const uint8_t *end;
|
|
118
|
+
long idx_cp;
|
|
119
|
+
long line;
|
|
120
|
+
long col;
|
|
121
|
+
int look[4];
|
|
122
|
+
long start_token_offset;
|
|
123
|
+
long start_token_line;
|
|
124
|
+
long start_token_column;
|
|
125
|
+
} scanner_t;
|
|
126
|
+
|
|
127
|
+
// Forward definitions ---------------------------------------------------------
|
|
128
|
+
static void scanner_consume_ident_like_token(scanner_t *sc);
|
|
129
|
+
static void scanner_consume_url_token(scanner_t *sc);
|
|
130
|
+
static void scanner_consume_bad_url(scanner_t *sc);
|
|
131
|
+
|
|
132
|
+
#endif //MINICSS_SCANNER_MINICSS_SCANNER_H
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#include "ruby.h"
|
|
2
|
+
#include "ruby/encoding.h"
|
|
3
|
+
#include <stdint.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
|
|
6
|
+
#define DEFINE_REUSABLE_SYMBOL(name) static ID id_type_##name; static VALUE sym_##name;
|
|
7
|
+
#define INITIALIZE_REUSABLE_SYMBOL(name) id_type_##name = rb_intern(#name); sym_##name = ID2SYM(id_type_##name);
|
|
8
|
+
|
|
9
|
+
DEFINE_REUSABLE_SYMBOL(whitespace);
|
|
10
|
+
DEFINE_REUSABLE_SYMBOL(eof);
|
|
11
|
+
static VALUE cTokenClass;
|
|
12
|
+
|
|
13
|
+
typedef struct {
|
|
14
|
+
VALUE eof;
|
|
15
|
+
VALUE tokens;
|
|
16
|
+
int tokens_idx;
|
|
17
|
+
long tokens_len;
|
|
18
|
+
VALUE look[2];
|
|
19
|
+
int marks[128];
|
|
20
|
+
int marks_idx;
|
|
21
|
+
} stream_t;
|
|
22
|
+
|
|
23
|
+
static void stream_free(void *ptr) {
|
|
24
|
+
xfree(ptr);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static size_t stream_memsize(const void *ptr) {
|
|
28
|
+
return sizeof(stream_t);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static void stream_mark(void *ptr) {
|
|
32
|
+
const stream_t *s = ptr;
|
|
33
|
+
if (s->tokens) rb_gc_mark(s->tokens);
|
|
34
|
+
if (s->eof) rb_gc_mark(s->eof);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static const rb_data_type_t stream_type = {
|
|
38
|
+
"MiniCSS::CSS::TokenStream",
|
|
39
|
+
{stream_mark, stream_free, stream_memsize,},
|
|
40
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
static VALUE stream_alloc(const VALUE klass) {
|
|
44
|
+
stream_t *s = ALLOC(stream_t);
|
|
45
|
+
memset(s, 0, sizeof(stream_t));
|
|
46
|
+
return TypedData_Wrap_Struct(klass, &stream_type, s);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static VALUE stream_initialize(const VALUE self, VALUE tokens) {
|
|
50
|
+
Check_Type(tokens, T_ARRAY);
|
|
51
|
+
stream_t *s;
|
|
52
|
+
TypedData_Get_Struct(self, stream_t, &stream_type, s);
|
|
53
|
+
s->tokens = tokens;
|
|
54
|
+
s->tokens_idx = 0;
|
|
55
|
+
s->marks_idx = 0;
|
|
56
|
+
s->tokens_len = RARRAY_LEN(tokens);
|
|
57
|
+
s->eof = rb_const_get(cTokenClass, rb_intern("EOF"));
|
|
58
|
+
|
|
59
|
+
// prime lookahead
|
|
60
|
+
s->look[0] = s->tokens_len >= 1 ? rb_ary_entry(tokens, 0) : s->eof;
|
|
61
|
+
s->look[1] = s->tokens_len >= 2 ? rb_ary_entry(tokens, 1) : s->eof;
|
|
62
|
+
return self;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static void rotate(stream_t *s) {
|
|
66
|
+
s->look[0] = s->look[1];
|
|
67
|
+
s->look[1] = s->tokens_idx + 1 < s->tokens_len ? rb_ary_entry(s->tokens, s->tokens_idx + 1) : s->eof;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#define UNWRAP_STREAM stream_t *s; TypedData_Get_Struct(self, stream_t, &stream_type, s);
|
|
71
|
+
|
|
72
|
+
static VALUE stream_peek(const VALUE self) {
|
|
73
|
+
UNWRAP_STREAM;
|
|
74
|
+
return s->look[0];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static void stream_consume_c(stream_t *s) {
|
|
78
|
+
if (s->tokens_idx < s->tokens_len && s->tokens_idx + 1 < s->tokens_len) {
|
|
79
|
+
s->tokens_idx++;
|
|
80
|
+
}
|
|
81
|
+
rotate(s);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static VALUE stream_consume(const VALUE self) {
|
|
85
|
+
UNWRAP_STREAM;
|
|
86
|
+
const VALUE peek = s->look[0];
|
|
87
|
+
stream_consume_c(s);
|
|
88
|
+
return peek;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static VALUE stream_discard(const VALUE self) {
|
|
92
|
+
UNWRAP_STREAM;
|
|
93
|
+
stream_consume_c(s);
|
|
94
|
+
return Qnil;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static VALUE stream_discard_whitespace(const VALUE self) {
|
|
98
|
+
UNWRAP_STREAM;
|
|
99
|
+
for (;;) {
|
|
100
|
+
const VALUE k = rb_funcall(s->look[0], rb_intern("kind"), 0);
|
|
101
|
+
Check_Type(k, T_SYMBOL);
|
|
102
|
+
if (rb_sym2id(k) != id_type_whitespace) break;
|
|
103
|
+
stream_consume_c(s);
|
|
104
|
+
}
|
|
105
|
+
return Qnil;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static VALUE stream_create_mark(const VALUE self) {
|
|
109
|
+
UNWRAP_STREAM;
|
|
110
|
+
if (s->marks_idx >= 127) {
|
|
111
|
+
rb_raise(rb_eRuntimeError, "Too many marks in stream");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
s->marks[s->marks_idx++] = s->tokens_idx;
|
|
115
|
+
return Qnil;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static VALUE stream_mark_restore(const VALUE self) {
|
|
119
|
+
UNWRAP_STREAM;
|
|
120
|
+
if (s->marks_idx == 0) {
|
|
121
|
+
rb_raise(rb_eRuntimeError, "BUG: No mark to restore");
|
|
122
|
+
}
|
|
123
|
+
const int mark = s->marks[s->marks_idx - 1];
|
|
124
|
+
s->tokens_idx = mark;
|
|
125
|
+
s->look[0] = s->tokens_len > mark ? rb_ary_entry(s->tokens, mark) : s->eof;
|
|
126
|
+
s->look[1] = s->tokens_len > mark + 1 ? rb_ary_entry(s->tokens, mark + 1) : s->eof;
|
|
127
|
+
s->marks_idx--;
|
|
128
|
+
return Qnil;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static VALUE stream_mark_pop(const VALUE self) {
|
|
132
|
+
UNWRAP_STREAM;
|
|
133
|
+
if (s->marks_idx == 0) {
|
|
134
|
+
rb_raise(rb_eRuntimeError, "BUG: No mark to pop");
|
|
135
|
+
}
|
|
136
|
+
s->marks_idx--;
|
|
137
|
+
return Qnil;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static bool is_eof(const VALUE v) {
|
|
141
|
+
const VALUE k = rb_funcall(v, rb_intern("kind"), 0);
|
|
142
|
+
Check_Type(k, T_SYMBOL);
|
|
143
|
+
return rb_sym2id(k) == id_type_eof;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static VALUE stream_is_empty(const VALUE self) {
|
|
147
|
+
UNWRAP_STREAM;
|
|
148
|
+
if (s->tokens_idx >= s->tokens_len || is_eof(s->look[0]))
|
|
149
|
+
return Qtrue;
|
|
150
|
+
return Qfalse;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static VALUE stream_peek_kind(const VALUE self) {
|
|
154
|
+
UNWRAP_STREAM;
|
|
155
|
+
const VALUE k = rb_funcall(s->look[0], rb_intern("kind"), 0);
|
|
156
|
+
Check_Type(k, T_SYMBOL);
|
|
157
|
+
return k;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static VALUE stream_status(const VALUE self) {
|
|
161
|
+
UNWRAP_STREAM;
|
|
162
|
+
const VALUE h = rb_hash_new();
|
|
163
|
+
rb_hash_aset(h, ID2SYM(rb_intern("eof")), s->eof);
|
|
164
|
+
rb_hash_aset(h, ID2SYM(rb_intern("tokens")), s->tokens);
|
|
165
|
+
rb_hash_aset(h, ID2SYM(rb_intern("tokens_idx")), INT2NUM(s->tokens_idx));
|
|
166
|
+
rb_hash_aset(h, ID2SYM(rb_intern("tokens_len")), INT2NUM((int)s->tokens_len));
|
|
167
|
+
rb_hash_aset(h, ID2SYM(rb_intern("look0")), s->look[0]);
|
|
168
|
+
rb_hash_aset(h, ID2SYM(rb_intern("look1")), s->look[1]);
|
|
169
|
+
rb_hash_aset(h, ID2SYM(rb_intern("marks_idx")), INT2NUM(s->marks_idx));
|
|
170
|
+
return h;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void Init_minicss_token_stream(void) {
|
|
174
|
+
const VALUE mMiniCSS = rb_define_module("MiniCSS");
|
|
175
|
+
const VALUE mCSS = rb_define_module_under(mMiniCSS, "CSS");
|
|
176
|
+
const VALUE cStream = rb_define_class_under(mCSS, "TokenStream", rb_cObject);
|
|
177
|
+
cTokenClass = rb_path2class("MiniCSS::CSS::Token");
|
|
178
|
+
|
|
179
|
+
INITIALIZE_REUSABLE_SYMBOL(whitespace);
|
|
180
|
+
INITIALIZE_REUSABLE_SYMBOL(eof);
|
|
181
|
+
|
|
182
|
+
rb_define_alloc_func(cStream, stream_alloc);
|
|
183
|
+
rb_define_method(cStream, "initialize", stream_initialize, 1);
|
|
184
|
+
rb_define_method(cStream, "peek", stream_peek, 0);
|
|
185
|
+
rb_define_method(cStream, "peek_kind", stream_peek_kind, 0);
|
|
186
|
+
rb_define_method(cStream, "consume", stream_consume, 0);
|
|
187
|
+
rb_define_method(cStream, "discard_whitespace", stream_discard_whitespace, 0);
|
|
188
|
+
rb_define_method(cStream, "discard", stream_discard, 0);
|
|
189
|
+
|
|
190
|
+
rb_define_method(cStream, "mark", stream_create_mark, 0);
|
|
191
|
+
rb_define_method(cStream, "restore", stream_mark_restore, 0);
|
|
192
|
+
rb_define_method(cStream, "pop", stream_mark_pop, 0);
|
|
193
|
+
rb_define_method(cStream, "empty?", stream_is_empty, 0);
|
|
194
|
+
rb_define_method(cStream, "status", stream_status, 0);
|
|
195
|
+
}
|
data/lib/minicss/css/parser.rb
CHANGED
|
@@ -5,8 +5,6 @@ module MiniCSS
|
|
|
5
5
|
class Parser
|
|
6
6
|
using StringRefinements
|
|
7
7
|
|
|
8
|
-
attr_reader :stream
|
|
9
|
-
|
|
10
8
|
def initialize(tokens)
|
|
11
9
|
@stream = TokenStream.new(tokens)
|
|
12
10
|
@tokens = tokens
|
|
@@ -27,25 +25,25 @@ module MiniCSS
|
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
def parse_rule
|
|
30
|
-
stream.discard_whitespace
|
|
31
|
-
return SyntaxError.new("empty") if stream.empty?
|
|
28
|
+
@stream.discard_whitespace
|
|
29
|
+
return SyntaxError.new("empty") if @stream.empty?
|
|
32
30
|
|
|
33
|
-
result = if stream.peek.kind == :at_keyword
|
|
31
|
+
result = if @stream.peek.kind == :at_keyword
|
|
34
32
|
consume_at_rule
|
|
35
33
|
else
|
|
36
34
|
consume_qualified_rule
|
|
37
35
|
end
|
|
38
36
|
return SyntaxError.new("invalid") unless result
|
|
39
37
|
|
|
40
|
-
stream.discard_whitespace
|
|
41
|
-
return SyntaxError.new("extra-input") unless stream.empty?
|
|
38
|
+
@stream.discard_whitespace
|
|
39
|
+
return SyntaxError.new("extra-input") unless @stream.empty?
|
|
42
40
|
|
|
43
41
|
result
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
def parse_declaration
|
|
47
|
-
stream.discard_whitespace
|
|
48
|
-
return SyntaxError.new("empty") if stream.empty?
|
|
45
|
+
@stream.discard_whitespace
|
|
46
|
+
return SyntaxError.new("empty") if @stream.empty?
|
|
49
47
|
|
|
50
48
|
decl = consume_declaration
|
|
51
49
|
return SyntaxError.new("invalid") unless decl
|
|
@@ -54,25 +52,23 @@ module MiniCSS
|
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
def parse_component_value
|
|
57
|
-
stream.discard_whitespace
|
|
58
|
-
return SyntaxError.new("empty") if stream.empty?
|
|
55
|
+
@stream.discard_whitespace
|
|
56
|
+
return SyntaxError.new("empty") if @stream.empty?
|
|
59
57
|
|
|
60
58
|
value = consume_component_value
|
|
61
|
-
stream.discard_whitespace
|
|
62
|
-
return SyntaxError.new("extra-input") unless stream.empty?
|
|
59
|
+
@stream.discard_whitespace
|
|
60
|
+
return SyntaxError.new("extra-input") unless @stream.empty?
|
|
63
61
|
|
|
64
62
|
value
|
|
65
63
|
end
|
|
66
64
|
|
|
67
|
-
def parse_component_value_list
|
|
68
|
-
consume_component_value_list
|
|
69
|
-
end
|
|
65
|
+
def parse_component_value_list = consume_component_value_list
|
|
70
66
|
|
|
71
67
|
def parse_component_value_comma_list
|
|
72
68
|
groups = []
|
|
73
|
-
until stream.empty?
|
|
69
|
+
until @stream.empty?
|
|
74
70
|
groups << consume_component_value_list(stop: :comma)
|
|
75
|
-
stream.discard
|
|
71
|
+
@stream.discard
|
|
76
72
|
end
|
|
77
73
|
groups
|
|
78
74
|
end
|
|
@@ -80,7 +76,7 @@ module MiniCSS
|
|
|
80
76
|
# Helpers
|
|
81
77
|
|
|
82
78
|
def assert_next_token(kind)
|
|
83
|
-
stream.peek.kind == kind
|
|
79
|
+
@stream.peek.kind == kind
|
|
84
80
|
end
|
|
85
81
|
|
|
86
82
|
# Parsers
|
|
@@ -88,9 +84,9 @@ module MiniCSS
|
|
|
88
84
|
def consume_stylesheet_contents
|
|
89
85
|
rules = []
|
|
90
86
|
loop do
|
|
91
|
-
case stream.peek.kind
|
|
87
|
+
case @stream.peek.kind
|
|
92
88
|
when :whitespace, :cdo, :cdc
|
|
93
|
-
stream.discard
|
|
89
|
+
@stream.discard
|
|
94
90
|
|
|
95
91
|
when :eof
|
|
96
92
|
return rules
|
|
@@ -109,11 +105,11 @@ module MiniCSS
|
|
|
109
105
|
def consume_at_rule(nested: false)
|
|
110
106
|
return unless assert_next_token(:at_keyword)
|
|
111
107
|
|
|
112
|
-
rule = AST::AtRule.new(name: stream.consume)
|
|
108
|
+
rule = AST::AtRule.new(name: @stream.consume)
|
|
113
109
|
loop do
|
|
114
|
-
case stream.peek.kind
|
|
110
|
+
case @stream.peek.kind
|
|
115
111
|
when :semicolon, :eof
|
|
116
|
-
stream.discard
|
|
112
|
+
@stream.discard
|
|
117
113
|
# TODO: If rule is valid in the current context, return it; otherwise return nothing.
|
|
118
114
|
return rule
|
|
119
115
|
|
|
@@ -123,10 +119,10 @@ module MiniCSS
|
|
|
123
119
|
return rule
|
|
124
120
|
end
|
|
125
121
|
|
|
126
|
-
rule.prelude << consume
|
|
122
|
+
rule.prelude << @stream.consume
|
|
127
123
|
|
|
128
124
|
when :left_curly
|
|
129
|
-
|
|
125
|
+
rule.child_rules.append(*consume_block)
|
|
130
126
|
return rule
|
|
131
127
|
|
|
132
128
|
else
|
|
@@ -139,14 +135,14 @@ module MiniCSS
|
|
|
139
135
|
rule = AST::QualifiedRule.new
|
|
140
136
|
|
|
141
137
|
loop do
|
|
142
|
-
case stream.peek.kind
|
|
138
|
+
case @stream.peek.kind
|
|
143
139
|
when :eof, stop
|
|
144
140
|
return nil
|
|
145
141
|
|
|
146
142
|
when :right_curly
|
|
147
143
|
return nil if nested
|
|
148
144
|
|
|
149
|
-
rule.prelude << consume
|
|
145
|
+
rule.prelude << @stream.consume
|
|
150
146
|
|
|
151
147
|
when :left_curly
|
|
152
148
|
non_ws = rule.prelude.reject { it.kind == :whitespace }
|
|
@@ -179,9 +175,9 @@ module MiniCSS
|
|
|
179
175
|
def consume_block
|
|
180
176
|
return unless assert_next_token(:left_curly)
|
|
181
177
|
|
|
182
|
-
stream.discard
|
|
178
|
+
@stream.discard
|
|
183
179
|
rules = consume_block_contents
|
|
184
|
-
stream.discard
|
|
180
|
+
@stream.discard
|
|
185
181
|
rules
|
|
186
182
|
end
|
|
187
183
|
|
|
@@ -190,9 +186,9 @@ module MiniCSS
|
|
|
190
186
|
decls = AST::DeclarationList.new
|
|
191
187
|
|
|
192
188
|
loop do
|
|
193
|
-
case stream.peek.kind
|
|
189
|
+
case @stream.peek.kind
|
|
194
190
|
when :whitespace, :semicolon
|
|
195
|
-
stream.discard
|
|
191
|
+
@stream.discard
|
|
196
192
|
when :eof, :right_curly
|
|
197
193
|
rules << decls unless decls.empty?
|
|
198
194
|
return rules
|
|
@@ -204,15 +200,15 @@ module MiniCSS
|
|
|
204
200
|
at = consume_at_rule(nested: true)
|
|
205
201
|
rules << at if at.is_a? AST::AtRule
|
|
206
202
|
else
|
|
207
|
-
stream.mark
|
|
203
|
+
@stream.mark
|
|
208
204
|
decl = consume_declaration(nested: true)
|
|
209
205
|
if decl.is_a? AST::Declaration
|
|
210
206
|
decls << decl
|
|
211
|
-
stream.pop
|
|
207
|
+
@stream.pop
|
|
212
208
|
next
|
|
213
209
|
end
|
|
214
210
|
|
|
215
|
-
stream.restore
|
|
211
|
+
@stream.restore
|
|
216
212
|
rule = consume_qualified_rule(nested: true)
|
|
217
213
|
if rule.is_a? InvalidRuleError
|
|
218
214
|
unless decls.empty?
|
|
@@ -232,23 +228,23 @@ module MiniCSS
|
|
|
232
228
|
|
|
233
229
|
def consume_declaration(nested: false)
|
|
234
230
|
decl = AST::Declaration.new
|
|
235
|
-
if stream.peek.kind == :ident
|
|
236
|
-
decl.name = stream.consume
|
|
231
|
+
if @stream.peek.kind == :ident
|
|
232
|
+
decl.name = @stream.consume
|
|
237
233
|
else
|
|
238
234
|
consume_bad_declaration(nested:)
|
|
239
235
|
return nil
|
|
240
236
|
end
|
|
241
237
|
|
|
242
|
-
stream.discard_whitespace
|
|
238
|
+
@stream.discard_whitespace
|
|
243
239
|
|
|
244
|
-
if stream.peek.kind == :colon
|
|
245
|
-
stream.discard
|
|
240
|
+
if @stream.peek.kind == :colon
|
|
241
|
+
@stream.discard
|
|
246
242
|
else
|
|
247
243
|
consume_bad_declaration(nested:)
|
|
248
244
|
return nil
|
|
249
245
|
end
|
|
250
246
|
|
|
251
|
-
stream.discard_whitespace
|
|
247
|
+
@stream.discard_whitespace
|
|
252
248
|
|
|
253
249
|
decl.value = consume_component_value_list(nested:, stop: :semicolon)
|
|
254
250
|
|
|
@@ -276,14 +272,14 @@ module MiniCSS
|
|
|
276
272
|
|
|
277
273
|
def consume_bad_declaration(nested:)
|
|
278
274
|
loop do
|
|
279
|
-
case stream.peek.kind
|
|
275
|
+
case @stream.peek.kind
|
|
280
276
|
when :eof, :semicolon
|
|
281
|
-
stream.discard
|
|
277
|
+
@stream.discard
|
|
282
278
|
return nil
|
|
283
279
|
when :right_curly
|
|
284
280
|
return if nested
|
|
285
281
|
|
|
286
|
-
stream.discard
|
|
282
|
+
@stream.discard
|
|
287
283
|
else
|
|
288
284
|
consume_component_value
|
|
289
285
|
end
|
|
@@ -293,14 +289,14 @@ module MiniCSS
|
|
|
293
289
|
def consume_component_value_list(stop: nil, nested: false)
|
|
294
290
|
values = []
|
|
295
291
|
loop do
|
|
296
|
-
case stream.peek.kind
|
|
292
|
+
case @stream.peek.kind
|
|
297
293
|
when :eof, stop
|
|
298
294
|
return values
|
|
299
295
|
|
|
300
296
|
when :right_curly
|
|
301
297
|
return values if nested
|
|
302
298
|
|
|
303
|
-
values << stream.consume
|
|
299
|
+
values << @stream.consume
|
|
304
300
|
|
|
305
301
|
else
|
|
306
302
|
values << consume_component_value
|
|
@@ -309,20 +305,20 @@ module MiniCSS
|
|
|
309
305
|
end
|
|
310
306
|
|
|
311
307
|
def consume_component_value
|
|
312
|
-
case stream.
|
|
308
|
+
case @stream.peek_kind
|
|
313
309
|
when :left_curly, :left_square_bracket, :left_parenthesis
|
|
314
310
|
consume_simple_block
|
|
315
311
|
when :function
|
|
316
312
|
consume_function
|
|
317
313
|
else
|
|
318
|
-
stream.consume
|
|
314
|
+
@stream.consume
|
|
319
315
|
end
|
|
320
316
|
end
|
|
321
317
|
|
|
322
318
|
def consume_simple_block
|
|
323
319
|
return if !assert_next_token(:left_curly) && !assert_next_token(:left_square_bracket) && !assert_next_token(:left_parenthesis)
|
|
324
320
|
|
|
325
|
-
block = AST::SimpleBlock.new(associated_token: stream.consume.kind)
|
|
321
|
+
block = AST::SimpleBlock.new(associated_token: @stream.consume.kind)
|
|
326
322
|
ending_token = {
|
|
327
323
|
left_curly: :right_curly,
|
|
328
324
|
left_parenthesis: :right_parenthesis,
|
|
@@ -330,9 +326,9 @@ module MiniCSS
|
|
|
330
326
|
}[block.associated_token]
|
|
331
327
|
|
|
332
328
|
loop do
|
|
333
|
-
case stream.peek.kind
|
|
329
|
+
case @stream.peek.kind
|
|
334
330
|
when :eof, ending_token
|
|
335
|
-
stream.discard
|
|
331
|
+
@stream.discard
|
|
336
332
|
return block
|
|
337
333
|
else
|
|
338
334
|
block.value << consume_component_value
|
|
@@ -343,12 +339,12 @@ module MiniCSS
|
|
|
343
339
|
def consume_function
|
|
344
340
|
return unless assert_next_token(:function)
|
|
345
341
|
|
|
346
|
-
func = AST::Function.new(name: stream.consume)
|
|
342
|
+
func = AST::Function.new(name: @stream.consume)
|
|
347
343
|
|
|
348
344
|
loop do
|
|
349
|
-
case stream.peek.kind
|
|
345
|
+
case @stream.peek.kind
|
|
350
346
|
when :eof, :right_parenthesis
|
|
351
|
-
stream.discard
|
|
347
|
+
@stream.discard
|
|
352
348
|
return func
|
|
353
349
|
else
|
|
354
350
|
func.value << consume_component_value
|