Skip to content

Commit 48ebe55

Browse files
committed
Backport build functions from CSS::Specification
1 parent cb1ed3f commit 48ebe55

File tree

1 file changed

+149
-118
lines changed

1 file changed

+149
-118
lines changed

lib/CSS/Grammar/AST.rakumod

Lines changed: 149 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,173 @@
1-
use v6;
1+
unit class CSS::Grammar::AST;
22

3-
class CSS::Grammar::AST {
3+
use CSS::Grammar::Defs :CSSObject, :CSSValue, :CSSSelector, :CSSUnits, :CSSTrait;
44

5-
use CSS::Grammar::Defs :CSSObject, :CSSValue, :CSSSelector, :CSSUnits, :CSSTrait;
5+
# re-exports (may be deprecated)
6+
constant css-obj is export(:CSSObject) = CSSObject;
7+
constant css-val is export(:CSSObject) = CSSValue;
8+
constant css-sel is export(:CSSSelector) = CSSValue;
9+
constant css-units is export(:CSSUnits) = CSSUnits;
10+
constant css-trait is export(:CSSTrait) = CSSTrait;
611

7-
# re-exports (may be deprecated)
8-
constant css-obj is export(:CSSObject) = CSSObject;
9-
constant css-val is export(:CSSObject) = CSSValue;
10-
constant css-sel is export(:CSSSelector) = CSSValue;
11-
constant css-units is export(:CSSUnits) = CSSUnits;
12-
constant css-trait is export(:CSSTrait) = CSSTrait;
12+
BEGIN our %known-type =
13+
%( CSSObject.enums.invert ),
14+
%( CSSValue.enums.invert ),
15+
%( CSSSelector.enums.invert ),
16+
;
1317

14-
BEGIN our %known-type =
15-
%( CSSObject.enums.invert ),
16-
%( CSSValue.enums.invert ),
17-
%( CSSSelector.enums.invert ),
18-
;
18+
#| utility token builder method, e.g.: $.token(42, :type<cm>) --> :cm(42)
19+
method token(Mu $ast, Str :$type is copy) {
1920

20-
#| utility token builder method, e.g.: $.token(42, :type<cm>) --> :cm(42)
21-
method token(Mu $ast, Str :$type is copy) {
21+
die 'usage: $.token($ast, :$type)'
22+
unless $type;
2223

23-
die 'usage: $.token($ast, :$type)'
24-
unless $type;
24+
return unless $ast.defined;
2525

26-
return unless $ast.defined;
26+
my Str $units = $type;
27+
$type = $_ with CSSUnits.enums{$type};
2728

28-
my Str $units = $type;
29-
$type = $_ with CSSUnits.enums{$type};
29+
my $raw-type = $type.split(':').head;
30+
die "unknown type: '$raw-type'"
31+
unless %known-type{$raw-type}:exists;
3032

31-
my $raw-type = $type.split(':').head;
32-
die "unknown type: '$raw-type'"
33-
unless %known-type{$raw-type}:exists;
33+
$ast.isa(Pair)
34+
?? ($units => $ast.value)
35+
!! ($units => $ast);
36+
}
3437

35-
$ast.isa(Pair)
36-
?? ($units => $ast.value)
37-
!! ($units => $ast);
38-
}
38+
#| utility AST builder method for leaf nodes (no repeated tokens)
39+
method node($/ --> Hash) {
40+
my %terms;
41+
42+
# unwrap Parcels
43+
my @l = $/.can('caps')
44+
?? $/
45+
!! $/.grep: *.defined;
46+
47+
for @l {
48+
for .caps -> $cap {
49+
my ($key, $value) = $cap.kv;
50+
next if $key eq '0';
51+
$key .= lc;
52+
my $type = $key.split(':').head;
53+
54+
$value .= ast
55+
// next;
3956

40-
#| utility AST builder method for leaf nodes (no repeated tokens)
41-
method node($/ --> Hash) {
42-
my %terms;
43-
44-
# unwrap Parcels
45-
my @l = $/.can('caps')
46-
?? $/
47-
!! $/.grep: *.defined;
48-
49-
for @l {
50-
for .caps -> $cap {
51-
my ($key, $value) = $cap.kv;
52-
next if $key eq '0';
53-
$key .= lc;
54-
my $type = $key.split(':').head;
55-
56-
$value .= ast
57-
// next;
58-
59-
if $key.starts-with('expr-') {
60-
$key.substr-rw(4,1) = ':';
61-
}
62-
elsif $value.isa(Pair) {
63-
($key, $value) = $value.kv;
64-
}
65-
elsif %known-type{$type}:!exists {
66-
warn "{$value.perl} has unknown type: $type";
67-
}
68-
69-
if %terms{$key}:exists {
70-
warn "repeated term " ~ $key ~ ':' ~ $value;
71-
return Any;
72-
}
73-
74-
%terms{$key} = $value;
57+
if $key.starts-with('expr-') {
58+
$key.substr-rw(4,1) = ':';
59+
}
60+
elsif $value.isa(Pair) {
61+
($key, $value) = $value.kv;
62+
}
63+
elsif %known-type{$type}:!exists {
64+
warn "{$value.perl} has unknown type: $type";
65+
}
66+
67+
if %terms{$key}:exists {
68+
warn "repeated term " ~ $key ~ ':' ~ $value;
69+
return Any;
7570
}
76-
}
7771

78-
%terms;
72+
%terms{$key} = $value;
73+
}
7974
}
8075

81-
#| utility AST builder method for nodes with repeatable elements
82-
method list($/ --> Array) {
83-
my @terms;
84-
85-
# unwrap Parcels
86-
my @l = $/.can('caps')
87-
?? $/
88-
!! $/.grep: *.defined;
89-
90-
for @l {
91-
for .caps -> $cap {
92-
my ($key, $value) = $cap.kv;
93-
next if $key eq '0';
94-
$key .= lc;
95-
96-
my $type = $key.split(':').head;
97-
98-
$value .= ast
99-
// next;
100-
101-
if $key.starts-with('expr-') {
102-
$key.substr-rw(4,1) = ':';
103-
}
104-
elsif $value.isa(Pair) {
105-
($key, $value) = $value.kv;
106-
}
107-
elsif %known-type{$type}:!exists {
108-
warn "{$value.raku} has unknown type: $type";
109-
}
110-
111-
push( @terms, {$key => $value} );
76+
%terms;
77+
}
78+
79+
#| utility AST builder method for nodes with repeatable elements
80+
method list($/ --> Array) {
81+
my @terms;
82+
83+
# unwrap Parcels
84+
my @l = $/.can('caps')
85+
?? $/
86+
!! $/.grep: *.defined;
87+
88+
for @l {
89+
for .caps -> $cap {
90+
my ($key, $value) = $cap.kv;
91+
next if $key eq '0';
92+
$key .= lc;
93+
94+
my $type = $key.split(':').head;
95+
96+
$value .= ast
97+
// next;
98+
99+
if $key.starts-with('expr-') {
100+
$key.substr-rw(4,1) = ':';
101+
}
102+
elsif $value.isa(Pair) {
103+
($key, $value) = $value.kv;
104+
}
105+
elsif %known-type{$type}:!exists {
106+
warn "{$value.raku} has unknown type: $type";
112107
}
113-
}
114108

115-
@terms;
109+
push( @terms, {$key => $value} );
110+
}
116111
}
117112

118-
method at-rule($/) {
119-
my %terms = $.node($/);
120-
%terms{ CSSValue::AtKeywordComponent } //= $0.lc;
121-
return $.token( %terms, :type(CSSObject::AtRule));
122-
}
113+
@terms;
114+
}
123115

124-
method func(Str:D $ident,
125-
$args,
126-
:$type = CSSValue::FunctionComponent,
127-
:$arg-type = CSSValue::ArgumentListComponent,
128-
|c --> Pair) {
129-
my %ast = $args.isa(List)
130-
?? ($arg-type => $args)
131-
!! $args;
132-
%ast ,= :$ident;
133-
$.token( %ast, :$type, |c );
134-
}
116+
method at-rule($/) {
117+
my %terms = $.node($/);
118+
%terms{ CSSValue::AtKeywordComponent } //= $0.lc;
119+
return $.token( %terms, :type(CSSObject::AtRule));
120+
}
121+
122+
method func(Str:D $ident,
123+
$args,
124+
:$type = CSSValue::FunctionComponent,
125+
:$arg-type = CSSValue::ArgumentListComponent,
126+
|c --> Pair) {
127+
my %ast = $args.isa(List)
128+
?? ($arg-type => $args)
129+
!! $args;
130+
%ast ,= :$ident;
131+
$.token( %ast, :$type, |c );
132+
}
133+
134+
method pseudo-func( Str $ident, $/ --> Pair) {
135+
my $expr = $.list($/);
136+
my %ast = :$ident, :$expr;
137+
$.token( %ast, :type(CSSSelector::PseudoFunction) );
138+
}
135139

136-
method pseudo-func( Str $ident, $/ --> Pair) {
137-
my $expr = $.list($/);
138-
my %ast = :$ident, :$expr;
139-
$.token( %ast, :type(CSSSelector::PseudoFunction) );
140+
method decl($/, :$obj!) {
141+
142+
my %ast;
143+
144+
%ast<ident> = .trim.lc
145+
with $0;
146+
147+
with $<val> {
148+
my Hash $val = .ast;
149+
150+
with $val<usage> -> $synopsis {
151+
my $usage = 'usage ' ~ $synopsis;
152+
$usage ~= ' | ' ~ $_
153+
for @.proforma;
154+
$obj.warning($usage);
155+
return;
156+
}
157+
elsif ! $val<expr> {
158+
$obj.warning('dropping declaration', %ast<ident>);
159+
return;
160+
}
161+
else {
162+
%ast<expr> = $val<expr>;
163+
}
140164
}
141165

166+
return %ast;
142167
}
168+
169+
method rule($/) {
170+
$.node($/).pairs[0];
171+
}
172+
173+
method proforma { [] }

0 commit comments

Comments
 (0)