7 changed files with 1637 additions and 0 deletions
@ -0,0 +1,773 @@ |
|||
/***************************************************************************** |
|||
* CSSGrammar.y: bison production rules for simplified css parsing |
|||
***************************************************************************** |
|||
* Copyright © 2017 VideoLabs, VideoLAN and VLC Authors |
|||
* |
|||
* Adapted from webkit's CSSGrammar.y: |
|||
* |
|||
* Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org) |
|||
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. |
|||
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
|||
* Copyright (C) 2008 Eric Seidel <eric@webkit.org> |
|||
* Copyright (C) 2012 Intel Corporation. All rights reserved. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, write to the Free Software |
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|||
* |
|||
*/ |
|||
%pure-parser |
|||
|
|||
%parse-param { yyscan_t scanner } { vlc_css_parser_t *css_parser } |
|||
%lex-param { yyscan_t scanner } { vlc_css_parser_t *css_parser } |
|||
|
|||
%{ |
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
#include <vlc_common.h> |
|||
#include "css_parser.h" |
|||
|
|||
#ifndef YY_TYPEDEF_YY_SCANNER_T |
|||
#define YY_TYPEDEF_YY_SCANNER_T |
|||
typedef void* yyscan_t; |
|||
#endif |
|||
%} |
|||
|
|||
%union { |
|||
bool boolean; |
|||
char character; |
|||
int integer; |
|||
char *string; |
|||
enum vlc_css_relation_e relation; |
|||
|
|||
vlc_css_term_t term; |
|||
vlc_css_expr_t *expr; |
|||
vlc_css_rule_t *rule; |
|||
vlc_css_declaration_t *declaration; |
|||
vlc_css_declaration_t *declarationList; |
|||
vlc_css_selector_t *selector; |
|||
vlc_css_selector_t *selectorList; |
|||
} |
|||
|
|||
%{ |
|||
/* See bison pure calling */ |
|||
int yylex(union YYSTYPE *, yyscan_t, vlc_css_parser_t *); |
|||
|
|||
static int yyerror(yyscan_t scanner, vlc_css_parser_t *p, const char *msg) |
|||
{ |
|||
VLC_UNUSED(scanner);VLC_UNUSED(p);VLC_UNUSED(msg); |
|||
return 1; |
|||
} |
|||
|
|||
%} |
|||
|
|||
%expect 10 |
|||
|
|||
%nonassoc LOWEST_PREC |
|||
|
|||
%left UNIMPORTANT_TOK |
|||
|
|||
%token WHITESPACE SGML_CD |
|||
%token TOKEN_EOF 0 |
|||
|
|||
%token INCLUDES |
|||
%token DASHMATCH |
|||
%token BEGINSWITH |
|||
%token ENDSWITH |
|||
%token CONTAINS |
|||
|
|||
%token <string> STRING |
|||
%right <string> IDENT |
|||
|
|||
%nonassoc <string> IDSEL |
|||
%nonassoc <string> HASH |
|||
%nonassoc ':' |
|||
%nonassoc '.' |
|||
%nonassoc '[' |
|||
%nonassoc <character> '*' |
|||
%nonassoc error |
|||
%left '|' |
|||
|
|||
%token FONT_FACE_SYM |
|||
%token CHARSET_SYM |
|||
|
|||
%token IMPORTANT_SYM |
|||
|
|||
%token CDO |
|||
%token CDC |
|||
%token <term> LENGTH |
|||
%token <term> ANGLE |
|||
%token <term> TIME |
|||
%token <term> FREQ |
|||
%token <term> DIMEN |
|||
%token <term> PERCENTAGE |
|||
%token <term> NUMBER |
|||
|
|||
%destructor { vlc_css_term_Clean($$); } <term> |
|||
|
|||
%token <string> URI |
|||
%token <string> FUNCTION |
|||
%token <string> UNICODERANGE |
|||
|
|||
%type <relation> combinator |
|||
|
|||
%type <rule> charset |
|||
%type <rule> ignored_charset |
|||
%type <rule> ruleset |
|||
%type <rule> font_face |
|||
%type <rule> invalid_rule |
|||
%type <rule> rule |
|||
%type <rule> valid_rule |
|||
%destructor { vlc_css_rules_Delete($$); } <rule> |
|||
|
|||
%type <string> ident_or_string |
|||
%type <string> property |
|||
|
|||
%type <selector> specifier |
|||
%type <selector> specifier_list |
|||
%type <selector> simple_selector |
|||
%type <selector> selector |
|||
%type <selectorList> selector_list |
|||
%type <selector> selector_with_trailing_whitespace |
|||
%type <selector> class |
|||
%type <selector> attrib |
|||
%type <selector> pseudo |
|||
%destructor { vlc_css_selectors_Delete($$); } <selector> <selectorList> |
|||
|
|||
%type <declarationList> declaration_list |
|||
%type <declarationList> decl_list |
|||
%type <declaration> declaration |
|||
%destructor { vlc_css_declarations_Delete($$); } <declaration> <declarationList> |
|||
|
|||
%type <boolean> prio |
|||
|
|||
%type <integer> match |
|||
%type <integer> unary_operator |
|||
%type <integer> maybe_unary_operator |
|||
%type <character> operator |
|||
|
|||
%type <expr> expr |
|||
%type <term> term |
|||
%type <term> unary_term |
|||
%type <term> function |
|||
%destructor { vlc_css_expression_Delete($$); } <expr> |
|||
|
|||
%type <string> element_name |
|||
%type <string> attr_name |
|||
|
|||
%destructor { free($$); } <string> |
|||
|
|||
%% |
|||
|
|||
stylesheet: |
|||
maybe_space maybe_charset maybe_sgml rule_list |
|||
; |
|||
|
|||
maybe_space: |
|||
/* empty */ %prec UNIMPORTANT_TOK |
|||
| maybe_space WHITESPACE |
|||
; |
|||
|
|||
maybe_sgml: |
|||
/* empty */ |
|||
| maybe_sgml SGML_CD |
|||
| maybe_sgml WHITESPACE |
|||
; |
|||
|
|||
maybe_charset: |
|||
/* empty */ |
|||
| charset { |
|||
vlc_css_rules_Delete($1); |
|||
} |
|||
; |
|||
|
|||
closing_brace: |
|||
'}' |
|||
| %prec LOWEST_PREC TOKEN_EOF |
|||
; |
|||
|
|||
charset: |
|||
CHARSET_SYM maybe_space STRING maybe_space ';' { |
|||
free( $3 ); |
|||
$$ = 0; |
|||
} |
|||
| CHARSET_SYM error invalid_block { |
|||
$$ = 0; |
|||
} |
|||
| CHARSET_SYM error ';' { |
|||
$$ = 0; |
|||
} |
|||
; |
|||
|
|||
ignored_charset: |
|||
CHARSET_SYM maybe_space STRING maybe_space ';' { |
|||
// Ignore any @charset rule not at the beginning of the style sheet. |
|||
free( $3 ); |
|||
$$ = 0; |
|||
} |
|||
| CHARSET_SYM maybe_space ';' { |
|||
$$ = 0; |
|||
} |
|||
; |
|||
|
|||
rule_list: |
|||
/* empty */ |
|||
| rule_list rule maybe_sgml { |
|||
if( $2 ) |
|||
vlc_css_parser_AddRule( css_parser, $2 ); |
|||
} |
|||
; |
|||
|
|||
valid_rule: |
|||
ruleset |
|||
| font_face |
|||
; |
|||
|
|||
rule: |
|||
valid_rule { |
|||
$$ = $1; |
|||
if($$) |
|||
$$->b_valid = true; |
|||
} |
|||
| ignored_charset |
|||
| invalid_rule |
|||
; |
|||
|
|||
font_face: |
|||
FONT_FACE_SYM maybe_space |
|||
'{' maybe_space declaration_list closing_brace { |
|||
vlc_css_declarations_Delete( $5 ); |
|||
$$ = NULL; |
|||
} |
|||
| FONT_FACE_SYM error invalid_block { |
|||
$$ = NULL; |
|||
} |
|||
| FONT_FACE_SYM error ';' { |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
combinator: |
|||
'+' maybe_space { $$ = RELATION_DIRECTADJACENT; } |
|||
| '~' maybe_space { $$ = RELATION_INDIRECTADJACENT; } |
|||
| '>' maybe_space { $$ = RELATION_CHILD; } |
|||
; |
|||
|
|||
maybe_unary_operator: |
|||
unary_operator { $$ = $1; } |
|||
| { $$ = 1; } |
|||
; |
|||
|
|||
unary_operator: |
|||
'-' { $$ = -1; } |
|||
| '+' { $$ = 1; } |
|||
; |
|||
|
|||
ruleset: |
|||
selector_list '{' maybe_space declaration_list closing_brace { |
|||
$$ = vlc_css_rule_New(); |
|||
if($$) |
|||
{ |
|||
$$->p_selectors = $1; |
|||
$$->p_declarations = $4; |
|||
} |
|||
} |
|||
; |
|||
|
|||
selector_list: |
|||
selector %prec UNIMPORTANT_TOK { |
|||
if ($1) { |
|||
$$ = $1; |
|||
} |
|||
} |
|||
| selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK { |
|||
if ($1 && $4 ) |
|||
{ |
|||
$$ = $1; |
|||
vlc_css_selector_Append( $$, $4 ); |
|||
} |
|||
else |
|||
{ |
|||
vlc_css_selectors_Delete( $1 ); |
|||
vlc_css_selectors_Delete( $4 ); |
|||
$$ = NULL; |
|||
} |
|||
} |
|||
| selector_list error { |
|||
vlc_css_selectors_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
selector_with_trailing_whitespace: |
|||
selector WHITESPACE { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
selector: |
|||
simple_selector { |
|||
$$ = $1; |
|||
} |
|||
| selector_with_trailing_whitespace |
|||
{ |
|||
$$ = $1; |
|||
} |
|||
| selector_with_trailing_whitespace simple_selector |
|||
{ |
|||
$$ = $1; |
|||
if ($$) |
|||
{ |
|||
vlc_css_selector_AddSpecifier( $$, $2 ); |
|||
$2->combinator = RELATION_DESCENDENT; |
|||
} |
|||
else $$ = $2; |
|||
} |
|||
| selector combinator simple_selector { |
|||
$$ = $1; |
|||
if ($$) |
|||
{ |
|||
vlc_css_selector_AddSpecifier( $$, $3 ); |
|||
$3->combinator = $2; |
|||
} |
|||
else $$ = $3; |
|||
} |
|||
| selector error { |
|||
vlc_css_selectors_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
simple_selector: |
|||
element_name { |
|||
$$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 ); |
|||
free( $1 ); |
|||
} |
|||
| element_name specifier_list { |
|||
$$ = vlc_css_selector_New( SELECTOR_SIMPLE, $1 ); |
|||
if( $$ && $2 ) |
|||
{ |
|||
vlc_css_selector_AddSpecifier( $$, $2 ); |
|||
} |
|||
else |
|||
{ |
|||
vlc_css_selectors_Delete( $2 ); |
|||
} |
|||
free( $1 ); |
|||
} |
|||
| specifier_list { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
element_name: |
|||
IDENT |
|||
| '*' { $$ = strdup("*"); } |
|||
; |
|||
|
|||
specifier_list: |
|||
specifier { |
|||
$$ = $1; |
|||
} |
|||
| specifier_list specifier { |
|||
if( $1 ) |
|||
{ |
|||
$$ = $1; |
|||
while( $1->specifiers.p_first ) |
|||
$1 = $1->specifiers.p_first; |
|||
vlc_css_selector_AddSpecifier( $1, $2 ); |
|||
} |
|||
else $$ = $2; |
|||
} |
|||
| specifier_list error { |
|||
vlc_css_selectors_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
specifier: |
|||
IDSEL { |
|||
$$ = vlc_css_selector_New( SPECIFIER_ID, $1 ); |
|||
free( $1 ); |
|||
} |
|||
/* Case when #fffaaa like token is lexed as HEX instead of IDSEL */ |
|||
| HASH { |
|||
if ($1[0] >= '0' && $1[0] <= '9') { |
|||
$$ = NULL; |
|||
} else { |
|||
$$ = vlc_css_selector_New( SPECIFIER_ID, $1 ); |
|||
} |
|||
free( $1 ); |
|||
} |
|||
| class |
|||
| attrib |
|||
| pseudo |
|||
; |
|||
|
|||
class: |
|||
'.' IDENT { |
|||
$$ = vlc_css_selector_New( SPECIFIER_CLASS, $2 ); |
|||
free( $2 ); |
|||
} |
|||
; |
|||
|
|||
attr_name: |
|||
IDENT maybe_space { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
attrib: |
|||
'[' maybe_space attr_name ']' { |
|||
$$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 ); |
|||
free( $3 ); |
|||
} |
|||
| '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' { |
|||
$$ = vlc_css_selector_New( SPECIFIER_ATTRIB, $3 ); |
|||
if( $$ ) |
|||
{ |
|||
$$->match = $4; |
|||
$$->p_matchsel = vlc_css_selector_New( SPECIFIER_ID, $6 ); |
|||
} |
|||
free( $3 ); |
|||
free( $6 ); |
|||
} |
|||
; |
|||
|
|||
match: |
|||
'=' { |
|||
$$ = MATCH_EQUALS; |
|||
} |
|||
| INCLUDES { |
|||
$$ = MATCH_INCLUDES; |
|||
} |
|||
| DASHMATCH { |
|||
$$ = MATCH_DASHMATCH; |
|||
} |
|||
| BEGINSWITH { |
|||
$$ = MATCH_BEGINSWITH; |
|||
} |
|||
| ENDSWITH { |
|||
$$ = MATCH_ENDSWITH; |
|||
} |
|||
| CONTAINS { |
|||
$$ = MATCH_CONTAINS; |
|||
} |
|||
; |
|||
|
|||
ident_or_string: |
|||
IDENT |
|||
| STRING |
|||
; |
|||
|
|||
pseudo: |
|||
':' IDENT { |
|||
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 ); |
|||
free( $2 ); |
|||
} |
|||
| ':' ':' IDENT { |
|||
$$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 ); |
|||
free( $3 ); |
|||
} |
|||
// used by :nth-* |
|||
| ':' FUNCTION maybe_space maybe_unary_operator NUMBER maybe_space ')' { |
|||
if(*$2 != 0) |
|||
$2[strlen($2) - 1] = 0; |
|||
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 ); |
|||
$5.val *= $4; |
|||
free( $2 ); |
|||
vlc_css_term_Clean( $5 ); |
|||
} |
|||
// required for WEBVTT weirdos cue::(::past) |
|||
| ':' ':' FUNCTION maybe_space selector maybe_space ')' { |
|||
if(*$3 != 0) |
|||
$3[strlen($3) - 1] = 0; |
|||
$$ = vlc_css_selector_New( SELECTOR_PSEUDOELEMENT, $3 ); |
|||
free( $3 ); |
|||
if( $$ && $5 ) |
|||
{ |
|||
vlc_css_selector_AddSpecifier( $$, $5 ); |
|||
$5->combinator = RELATION_SELF; |
|||
} |
|||
else |
|||
vlc_css_selectors_Delete( $5 ); |
|||
} |
|||
// used by :nth-*(odd/even) and :lang |
|||
| ':' FUNCTION maybe_space IDENT maybe_space ')' { |
|||
if(*$2 != 0) |
|||
$2[strlen($2) - 1] = 0; |
|||
$$ = vlc_css_selector_New( SELECTOR_PSEUDOCLASS, $2 ); |
|||
free( $2 ); |
|||
free( $4 ); |
|||
} |
|||
; |
|||
|
|||
declaration_list: |
|||
declaration { |
|||
$$ = $1; |
|||
} |
|||
| decl_list declaration { |
|||
$$ = $1; |
|||
if( $$ ) |
|||
vlc_css_declarations_Append( $$, $2 ); |
|||
} |
|||
| decl_list { |
|||
$$ = $1; |
|||
} |
|||
| error invalid_block_list error { |
|||
$$ = NULL; |
|||
} |
|||
| error { |
|||
$$ = NULL; |
|||
} |
|||
| decl_list error { |
|||
$$ = $1; |
|||
} |
|||
| decl_list invalid_block_list { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
decl_list: |
|||
declaration ';' maybe_space { |
|||
$$ = $1; |
|||
} |
|||
| declaration invalid_block_list maybe_space { |
|||
vlc_css_declarations_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
| declaration invalid_block_list ';' maybe_space { |
|||
vlc_css_declarations_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
| error ';' maybe_space { |
|||
$$ = NULL; |
|||
} |
|||
| error invalid_block_list error ';' maybe_space { |
|||
$$ = NULL; |
|||
} |
|||
| decl_list declaration ';' maybe_space { |
|||
if( $1 ) |
|||
{ |
|||
$$ = $1; |
|||
vlc_css_declarations_Append( $$, $2 ); |
|||
} |
|||
else $$ = $2; |
|||
} |
|||
| decl_list error ';' maybe_space { |
|||
$$ = $1; |
|||
} |
|||
| decl_list error invalid_block_list error ';' maybe_space { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
declaration: |
|||
property ':' maybe_space expr prio { |
|||
$$ = vlc_css_declaration_New( $1 ); |
|||
if( $$ ) |
|||
$$->expr = $4; |
|||
else |
|||
vlc_css_expression_Delete( $4 ); |
|||
free( $1 ); |
|||
} |
|||
| |
|||
property error { |
|||
free( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
property ':' maybe_space error expr prio { |
|||
free( $1 ); |
|||
vlc_css_expression_Delete( $5 ); |
|||
/* The default movable type template has letter-spacing: .none; Handle this by looking for |
|||
error tokens at the start of an expr, recover the expr and then treat as an error, cleaning |
|||
up and deleting the shifted expr. */ |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
property ':' maybe_space expr prio error { |
|||
free( $1 ); |
|||
vlc_css_expression_Delete( $4 ); |
|||
/* When we encounter something like p {color: red !important fail;} we should drop the declaration */ |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
IMPORTANT_SYM maybe_space { |
|||
/* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */ |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
property ':' maybe_space { |
|||
free( $1 ); |
|||
/* div { font-family: } Just reduce away this property with no value. */ |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
property ':' maybe_space error { |
|||
free( $1 ); |
|||
/* if we come across rules with invalid values like this case: p { weight: *; }, just discard the rule */ |
|||
$$ = NULL; |
|||
} |
|||
| |
|||
property invalid_block { |
|||
/* if we come across: div { color{;color:maroon} }, ignore everything within curly brackets */ |
|||
free( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
property: |
|||
IDENT maybe_space { |
|||
$$ = $1; |
|||
} |
|||
; |
|||
|
|||
prio: |
|||
IMPORTANT_SYM maybe_space { $$ = true; } |
|||
| /* empty */ { $$ = false; } |
|||
; |
|||
|
|||
expr: |
|||
term { |
|||
$$ = vlc_css_expression_New( $1 ); |
|||
if( !$$ ) |
|||
vlc_css_term_Clean( $1 ); |
|||
} |
|||
| expr operator term { |
|||
$$ = $1; |
|||
if( !$1 || !vlc_css_expression_AddTerm($1, $2, $3) ) |
|||
vlc_css_term_Clean( $3 ); |
|||
} |
|||
| expr invalid_block_list { |
|||
vlc_css_expression_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
| expr invalid_block_list error { |
|||
vlc_css_expression_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
| expr error { |
|||
vlc_css_expression_Delete( $1 ); |
|||
$$ = NULL; |
|||
} |
|||
; |
|||
|
|||
operator: |
|||
'/' maybe_space { |
|||
$$ = '/'; |
|||
} |
|||
| ',' maybe_space { |
|||
$$ = ','; |
|||
} |
|||
| /* empty */ { |
|||
$$ = 0; |
|||
} |
|||
; |
|||
|
|||
term: |
|||
unary_term { $$ = $1; } |
|||
| unary_operator unary_term { |
|||
$$ = $2; |
|||
$$.val *= $1; |
|||
} |
|||
| STRING maybe_space { $$.type = TYPE_STRING; $$.psz = $1; } |
|||
| IDENT maybe_space { $$.type = TYPE_IDENTIFIER; $$.psz = $1; } |
|||
/* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */ |
|||
| DIMEN maybe_space { $$ = $1; } |
|||
| unary_operator DIMEN maybe_space { $$ = $2; } |
|||
| URI maybe_space { $$.type = TYPE_URI; $$.psz = $1; } |
|||
| UNICODERANGE maybe_space { $$.type = TYPE_UNICODERANGE; $$.psz = $1; } |
|||
| IDSEL maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = $1; } |
|||
| HASH maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = $1; } |
|||
| '#' maybe_space { $$.type = TYPE_HEXCOLOR; $$.psz = NULL; } /* Handle error case: "color: #;" */ |
|||
/* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */ |
|||
| function { |
|||
$$ = $1; |
|||
} |
|||
| '%' maybe_space { /* Handle width: %; */ |
|||
$$.type = TYPE_PERCENT; $$.val = 0; |
|||
} |
|||
; |
|||
|
|||
unary_term: |
|||
NUMBER maybe_space |
|||
| PERCENTAGE maybe_space |
|||
| LENGTH maybe_space |
|||
| ANGLE maybe_space |
|||
| TIME maybe_space |
|||
| FREQ maybe_space |
|||
; |
|||
|
|||
function: |
|||
FUNCTION maybe_space expr ')' maybe_space { |
|||
$$.type = TYPE_FUNCTION; $$.function = $3; |
|||
$$.psz = $1; |
|||
if(*$$.psz != 0) |
|||
$$.psz[strlen($$.psz) - 1] = 0; |
|||
} | |
|||
FUNCTION maybe_space expr TOKEN_EOF { |
|||
$$.type = TYPE_FUNCTION; $$.function = $3; $$.psz = $1; |
|||
if(*$$.psz != 0) |
|||
$$.psz[strlen($$.psz) - 1] = 0; |
|||
} | |
|||
FUNCTION maybe_space ')' maybe_space { |
|||
$$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1; |
|||
if(*$$.psz != 0) |
|||
$$.psz[strlen($$.psz) - 1] = 0; |
|||
} | |
|||
FUNCTION maybe_space error { |
|||
$$.type = TYPE_FUNCTION; $$.function = NULL; $$.psz = $1; |
|||
if(*$$.psz != 0) |
|||
$$.psz[strlen($$.psz) - 1] = 0; |
|||
} |
|||
; |
|||
|
|||
/* error handling rules */ |
|||
|
|||
invalid_rule: |
|||
error invalid_block { |
|||
$$ = NULL; |
|||
} |
|||
|
|||
/* |
|||
Seems like the two rules below are trying too much and violating |
|||
http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html |
|||
|
|||
| error ';' { |
|||
$$ = 0; |
|||
} |
|||
| error '}' { |
|||
$$ = 0; |
|||
} |
|||
*/ |
|||
; |
|||
|
|||
invalid_block: |
|||
'{' error invalid_block_list error closing_brace |
|||
| '{' error closing_brace |
|||
; |
|||
|
|||
invalid_block_list: |
|||
invalid_block |
|||
| invalid_block_list error invalid_block |
|||
; |
|||
|
|||
%% |
|||
|
|||
#ifdef YYDEBUG |
|||
int yydebug=1; |
|||
#else |
|||
int yydebug=0; |
|||
#endif |
|||
@ -0,0 +1,107 @@ |
|||
/***************************************************************************** |
|||
* CSSLexer.l : lexer for simplified CSS, based on W3C spec |
|||
***************************************************************************** |
|||
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN |
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the GNU Lesser General Public License as published by |
|||
* the Free Software Foundation; either version 2.1 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
%option case-insensitive |
|||
%option reentrant |
|||
%option bison-bridge |
|||
%option noyywrap |
|||
%option nounput |
|||
%option noinput |
|||
%option never-interactive |
|||
%option nostdinit |
|||
|
|||
%{ |
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
#include <vlc_common.h> |
|||
#include "css_parser.h" |
|||
#include "CSSGrammar.h" |
|||
#define VAL(a, b) { yylval->term.val = (a); yylval->term.type = TYPE_ ## b; } |
|||
#define us_strtof strtof |
|||
char *d; |
|||
%} |
|||
|
|||
h [0-9a-f] |
|||
nonascii [\200-\377] |
|||
unicode \\{h}{1,6}[ \t\r\n\f]? |
|||
escape {unicode}|\\[ -~\200-\377] |
|||
nmstart [a-z]|{nonascii}|{escape} |
|||
nmchar [a-z0-9-]|{nonascii}|{escape} |
|||
string1 \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\" |
|||
string2 \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\' |
|||
|
|||
ident [-]?{nmstart}{nmchar}* |
|||
name {nmchar}+ |
|||
num [0-9]+|[0-9]*"."[0-9]+ |
|||
string {string1}|{string2} |
|||
url ([!#$%&*-~]|{nonascii}|{escape})* |
|||
w [ \t\r\n\f]* |
|||
nl \n|\r\n|\r|\f |
|||
range \?{1,6}|{h}(\?{0,5}|{h}(\?{0,4}|{h}(\?{0,3}|{h}(\?{0,2}|{h}(\??|{h}))))) |
|||
|
|||
%% |
|||
|
|||
[ \t\r\n\f]+ {return WHITESPACE;} |
|||
|
|||
\/\*[^*]*\*+([^/][^*]*\*+)*\/ /* ignore comments */ |
|||
|
|||
"<!--" {return CDO;} |
|||
"-->" {return CDC;} |
|||
"~=" {return INCLUDES;} |
|||
"|=" {return DASHMATCH;} |
|||
|
|||
{string} { yylval->string = vlc_css_unquotedunescaped(yytext); return STRING;} |
|||
|
|||
{ident} { yylval->string = vlc_css_unescaped(yytext); return IDENT;} |
|||
|
|||
"@font-face" {return FONT_FACE_SYM;} |
|||
|
|||
"!{w}important" {return IMPORTANT_SYM;} |
|||
|
|||
{num}em { VAL( us_strtof(yytext, &d), EMS ); return LENGTH;} |
|||
{num}ex { VAL( atoi(yytext), EXS ); return LENGTH;} |
|||
{num}px { VAL( atoi(yytext), PIXELS ); return LENGTH;} |
|||
{num}cm { VAL( us_strtof(yytext, &d) * 10, MILLIMETERS ); return LENGTH;} |
|||
{num}mm { VAL( atoi(yytext), MILLIMETERS ); return LENGTH;} |
|||
{num}in { VAL( us_strtof(yytext, &d) * 25.4, MILLIMETERS ); return LENGTH;} |
|||
{num}pt { VAL( us_strtof(yytext, &d), POINTS ); return LENGTH;} |
|||
{num}pc { VAL( us_strtof(yytext, &d), POINTS ); return LENGTH;} |
|||
{num}deg { VAL( us_strtof(yytext, &d), DEGREES ); return ANGLE;} |
|||
{num}rad { VAL( us_strtof(yytext, &d) * 0.0174533, DEGREES ); return ANGLE;} |
|||
{num}grad { VAL( us_strtof(yytext, &d) * 1.1111111, DEGREES ); return ANGLE;} |
|||
{num}ms { VAL( atoi(yytext), MILLISECONDS ); return TIME;} |
|||
{num}s { VAL( atoi(yytext) * 1000, MILLISECONDS ); return TIME;} |
|||
{num}Hz { VAL( atoi(yytext), HERTZ ); return FREQ;} |
|||
{num}kHz { VAL( atoi(yytext) * 1000, HERTZ ); return FREQ;} |
|||
{num}{ident} { VAL( 0, DIMENSION ); return DIMEN;} |
|||
{num}% { VAL( atoi(yytext), PERCENT ); return PERCENTAGE;} |
|||
{num} { VAL( us_strtof(yytext, &d), NONE ); return NUMBER;} |
|||
|
|||
"url("{w}{string}{w}")" { yylval->string = vlc_css_unquotedunescaped(yytext); return URI;} |
|||
"url("{w}{url}{w}")" { yylval->string = vlc_css_unquotedunescaped(yytext); return URI;} |
|||
{ident}"(" { yylval->string = vlc_css_unescaped(yytext); return FUNCTION;} |
|||
"#"{ident} {yylval->string = vlc_css_unescaped(yytext); return IDSEL;} |
|||
"#"{name} {yylval->string = vlc_css_unescaped(yytext); return HASH;} |
|||
|
|||
U\+{range} { yylval->string = strdup(yytext); return UNICODERANGE;} |
|||
U\+{h}{1,6}-{h}{1,6} { yylval->string = strdup(yytext); return UNICODERANGE;} |
|||
|
|||
. {return *yytext;} |
|||
|
|||
@ -0,0 +1,27 @@ |
|||
/*****************************************************************************
|
|||
* css_bridge.h : CSS grammar/lexer bridge |
|||
***************************************************************************** |
|||
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN |
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the GNU Lesser General Public License as published by |
|||
* the Free Software Foundation; either version 2.1 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
#define yyconst const |
|||
typedef void* yyscan_t; |
|||
typedef struct yy_buffer_state *YY_BUFFER_STATE; |
|||
int yylex_init (yyscan_t* scanner); |
|||
YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); |
|||
YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes, int, yyscan_t yyscanner ); |
|||
int yylex_destroy (yyscan_t yyscanner ); |
|||
void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner); |
|||
@ -0,0 +1,517 @@ |
|||
/*****************************************************************************
|
|||
* css_parser.c : CSS parser |
|||
***************************************************************************** |
|||
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN |
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the GNU Lesser General Public License as published by |
|||
* the Free Software Foundation; either version 2.1 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
|
|||
#include <vlc_common.h> |
|||
|
|||
#include "css_bridge.h" |
|||
#include "css_parser.h" |
|||
#include "CSSGrammar.h" |
|||
|
|||
#include <ctype.h> |
|||
|
|||
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth ); |
|||
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth ); |
|||
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth ); |
|||
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth ); |
|||
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth ); |
|||
|
|||
#define CHAIN_APPEND_IMPL(n, t) CHAIN_APPEND_DECL(n ,t)\ |
|||
{\ |
|||
t ** insert = &p_a->p_next;\ |
|||
while( *insert ) insert = &((*insert)->p_next);\ |
|||
*insert = p_b;\ |
|||
} |
|||
|
|||
void vlc_css_term_Clean( vlc_css_term_t a ) |
|||
{ |
|||
if( a.type >= TYPE_STRING ) |
|||
free( a.psz ); |
|||
|
|||
if( a.type == TYPE_FUNCTION ) |
|||
{ |
|||
if( a.function ) |
|||
vlc_css_expression_Delete( a.function ); |
|||
} |
|||
} |
|||
|
|||
static void vlc_css_term_Debug( const vlc_css_term_t a, int depth ) |
|||
{ |
|||
for(int i=0;i<depth;i++) printf(" "); |
|||
printf("term: "); |
|||
if( a.type >= TYPE_STRING ) |
|||
{ |
|||
printf("%x %s\n", a.type, a.psz); |
|||
if( a.type == TYPE_FUNCTION && a.function ) |
|||
vlc_css_expression_Debug( a.function, depth + 1 ); |
|||
} |
|||
else printf("%x %f\n", a.type, a.val); |
|||
} |
|||
|
|||
bool vlc_css_expression_AddTerm( vlc_css_expr_t *p_expr, |
|||
char op, vlc_css_term_t a ) |
|||
{ |
|||
if( p_expr->i_count >= p_expr->i_alloc ) |
|||
{ |
|||
size_t i_realloc = (p_expr->i_alloc == 0) ? 1 : p_expr->i_alloc + 4; |
|||
void *reac = realloc( p_expr->seq, i_realloc * sizeof(p_expr->seq[0]) ); |
|||
if( reac ) |
|||
{ |
|||
p_expr->seq = reac; |
|||
p_expr->i_alloc = i_realloc; |
|||
} |
|||
} |
|||
|
|||
if( p_expr->i_count >= p_expr->i_alloc ) |
|||
return false; |
|||
|
|||
p_expr->seq[p_expr->i_count].op = op; |
|||
p_expr->seq[p_expr->i_count++].term = a; |
|||
return true; |
|||
} |
|||
|
|||
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr ) |
|||
{ |
|||
if( p_expr ) |
|||
{ |
|||
for(size_t i=0; i<p_expr->i_count; i++) |
|||
vlc_css_term_Clean( p_expr->seq[i].term ); |
|||
free( p_expr->seq ); |
|||
} |
|||
free( p_expr ); |
|||
} |
|||
|
|||
static void vlc_css_expression_Debug( const vlc_css_expr_t *p_expr, int depth ) |
|||
{ |
|||
if( p_expr ) |
|||
{ |
|||
for(int i=0;i<depth;i++) printf(" "); |
|||
printf("expression: \n"); |
|||
for(size_t i=0; i<p_expr->i_count; i++) |
|||
vlc_css_term_Debug( p_expr->seq[i].term, depth + 1 ); |
|||
} |
|||
} |
|||
|
|||
vlc_css_expr_t * vlc_css_expression_New( vlc_css_term_t term ) |
|||
{ |
|||
vlc_css_expr_t *p_expr = calloc(1, sizeof(*p_expr)); |
|||
if(!vlc_css_expression_AddTerm( p_expr, 0, term )) |
|||
{ |
|||
free(p_expr); |
|||
p_expr = NULL; |
|||
} |
|||
return p_expr; |
|||
} |
|||
|
|||
CHAIN_APPEND_IMPL(vlc_css_declarations_Append, vlc_css_declaration_t) |
|||
|
|||
void vlc_css_declarations_Delete( vlc_css_declaration_t *p_decl ) |
|||
{ |
|||
while( p_decl ) |
|||
{ |
|||
vlc_css_declaration_t *p_next = p_decl->p_next; |
|||
vlc_css_expression_Delete( p_decl->expr ); |
|||
free( p_decl->psz_property ); |
|||
free( p_decl ); |
|||
p_decl = p_next; |
|||
} |
|||
} |
|||
|
|||
static void vlc_css_declarations_Debug( const vlc_css_declaration_t *p_decl, int depth ) |
|||
{ |
|||
while( p_decl ) |
|||
{ |
|||
for(int i=0;i<depth;i++) printf(" "); |
|||
printf("declaration: %s\n", p_decl->psz_property ); |
|||
vlc_css_expression_Debug( p_decl->expr, depth + 1 ); |
|||
p_decl = p_decl->p_next; |
|||
} |
|||
} |
|||
|
|||
vlc_css_declaration_t * vlc_css_declaration_New( const char *psz ) |
|||
{ |
|||
vlc_css_declaration_t *p_decl = calloc(1, sizeof(*p_decl)); |
|||
p_decl->psz_property = strdup(psz); |
|||
return p_decl; |
|||
} |
|||
|
|||
CHAIN_APPEND_IMPL(vlc_css_selector_Append, vlc_css_selector_t) |
|||
|
|||
void |
|||
vlc_css_selector_AddSpecifier( vlc_css_selector_t *p_sel, vlc_css_selector_t *p_spec ) |
|||
{ |
|||
*p_sel->specifiers.pp_append = p_spec; |
|||
while(p_spec) |
|||
{ |
|||
p_sel->specifiers.pp_append = &p_spec->p_next; |
|||
p_spec = p_spec->p_next; |
|||
} |
|||
} |
|||
|
|||
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel ) |
|||
{ |
|||
while( p_sel ) |
|||
{ |
|||
vlc_css_selector_t *p_next = p_sel->p_next; |
|||
free( p_sel->psz_name ); |
|||
vlc_css_selectors_Delete( p_sel->specifiers.p_first ); |
|||
vlc_css_selectors_Delete( p_sel->p_matchsel ); |
|||
free( p_sel ); |
|||
p_sel = p_next; |
|||
} |
|||
} |
|||
|
|||
static void vlc_css_selectors_Debug( const vlc_css_selector_t *p_sel, int depth ) |
|||
{ |
|||
while( p_sel ) |
|||
{ |
|||
for(int i=0;i<depth;i++) printf(" "); printf("selector %c%s:\n", p_sel->combinator, p_sel->psz_name ); |
|||
vlc_css_selectors_Debug( p_sel->p_matchsel, depth + 1 ); |
|||
vlc_css_selectors_Debug( p_sel->specifiers.p_first, depth + 1 ); |
|||
p_sel = p_sel->p_next; |
|||
} |
|||
} |
|||
|
|||
vlc_css_selector_t * vlc_css_selector_New( int type, const char *psz ) |
|||
{ |
|||
vlc_css_selector_t *p_sel = calloc(1, sizeof(*p_sel)); |
|||
p_sel->psz_name = strdup(psz); |
|||
p_sel->type = type; |
|||
p_sel->combinator = RELATION_SELF; |
|||
p_sel->specifiers.pp_append = &p_sel->specifiers.p_first; |
|||
return p_sel; |
|||
} |
|||
|
|||
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule ) |
|||
{ |
|||
while(p_rule) |
|||
{ |
|||
vlc_css_rule_t *p_next = p_rule->p_next; |
|||
vlc_css_selectors_Delete( p_rule->p_selectors ); |
|||
vlc_css_declarations_Delete( p_rule->p_declarations ); |
|||
free(p_rule); |
|||
p_rule = p_next; |
|||
} |
|||
} |
|||
|
|||
static void vlc_css_rules_Debug( const vlc_css_rule_t *p_rule, int depth ) |
|||
{ |
|||
int j = 0; |
|||
while(p_rule) |
|||
{ |
|||
for(int i=0;i<depth;i++) printf(" "); printf("rule %d:\n", j++); |
|||
vlc_css_selectors_Debug( p_rule->p_selectors, depth + 1 ); |
|||
vlc_css_declarations_Debug( p_rule->p_declarations, depth + 1 ); |
|||
p_rule = p_rule->p_next; |
|||
} |
|||
} |
|||
|
|||
vlc_css_rule_t * vlc_css_rule_New( void ) |
|||
{ |
|||
vlc_css_rule_t *p_rule = calloc(1, sizeof(*p_rule)); |
|||
return p_rule; |
|||
} |
|||
|
|||
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser, |
|||
vlc_css_rule_t *p_rule ) |
|||
{ |
|||
(*p_parser->rules.pp_append) = p_rule; |
|||
p_parser->rules.pp_append = &p_rule->p_next; |
|||
} |
|||
|
|||
void vlc_css_parser_Debug( const vlc_css_parser_t *p_parser ) |
|||
{ |
|||
vlc_css_rules_Debug( p_parser->rules.p_first, 0 ); |
|||
} |
|||
|
|||
void vlc_css_parser_Clean( vlc_css_parser_t *p_parser ) |
|||
{ |
|||
vlc_css_rules_Delete( p_parser->rules.p_first ); |
|||
} |
|||
|
|||
void vlc_css_parser_Init( vlc_css_parser_t *p_parser ) |
|||
{ |
|||
memset(p_parser, 0, sizeof(vlc_css_parser_t)); |
|||
p_parser->rules.pp_append = &p_parser->rules.p_first; |
|||
} |
|||
|
|||
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *p_data, size_t i_data ) |
|||
{ |
|||
yyscan_t yy; |
|||
yylex_init(&yy); |
|||
|
|||
YY_BUFFER_STATE buf = yy_scan_bytes( (const char*) p_data, i_data, yy ); |
|||
|
|||
bool b_ret = !yyparse( yy, p_parser ); |
|||
|
|||
yy_delete_buffer( buf, yy ); |
|||
yylex_destroy( yy ); |
|||
|
|||
return b_ret; |
|||
} |
|||
|
|||
bool vlc_css_parser_ParseString( vlc_css_parser_t *p_parser, const char *psz_css ) |
|||
{ |
|||
yyscan_t yy; |
|||
yylex_init(&yy); |
|||
|
|||
YY_BUFFER_STATE buf = yy_scan_string( psz_css, yy ); |
|||
|
|||
bool b_ret = !yyparse( yy, p_parser ); |
|||
|
|||
yy_delete_buffer( buf, yy ); |
|||
yylex_destroy( yy ); |
|||
|
|||
return b_ret; |
|||
} |
|||
|
|||
static int CodePointToUTF8( uint32_t ucs4, char *p ) |
|||
{ |
|||
/* adapted from codepoint conversion from strings.h */ |
|||
if( ucs4 <= 0x7F ) |
|||
{ |
|||
p[0] = ucs4; |
|||
return 1; |
|||
} |
|||
else if( ucs4 <= 0x7FF ) |
|||
{ |
|||
p[0] = 0xC0 | (ucs4 >> 6); |
|||
p[1] = 0x80 | (ucs4 & 0x3F); |
|||
return 2; |
|||
} |
|||
else if( ucs4 <= 0xFFFF ) |
|||
{ |
|||
p[0] = 0xE0 | (ucs4 >> 12); |
|||
p[1] = 0x80 | ((ucs4 >> 6) & 0x3F); |
|||
p[2] = 0x80 | (ucs4 & 0x3F); |
|||
return 3; |
|||
} |
|||
else if( ucs4 <= 0x1FFFFF ) |
|||
{ |
|||
p[0] = 0xF0 | (ucs4 >> 18); |
|||
p[1] = 0x80 | ((ucs4 >> 12) & 0x3F); |
|||
p[2] = 0x80 | ((ucs4 >> 6) & 0x3F); |
|||
p[3] = 0x80 | (ucs4 & 0x3F); |
|||
return 4; |
|||
} |
|||
else if( ucs4 <= 0x3FFFFFF ) |
|||
{ |
|||
p[0] = 0xF8 | (ucs4 >> 24); |
|||
p[1] = 0x80 | ((ucs4 >> 18) & 0x3F); |
|||
p[2] = 0x80 | ((ucs4 >> 12) & 0x3F); |
|||
p[3] = 0x80 | ((ucs4 >> 6) & 0x3F); |
|||
p[4] = 0x80 | (ucs4 & 0x3F); |
|||
return 5; |
|||
} |
|||
else |
|||
{ |
|||
p[0] = 0xFC | (ucs4 >> 30); |
|||
p[1] = 0x80 | ((ucs4 >> 24) & 0x3F); |
|||
p[2] = 0x80 | ((ucs4 >> 18) & 0x3F); |
|||
p[3] = 0x80 | ((ucs4 >> 12) & 0x3F); |
|||
p[4] = 0x80 | ((ucs4 >> 6) & 0x3F); |
|||
p[5] = 0x80 | (ucs4 & 0x3F); |
|||
return 6; |
|||
} |
|||
} |
|||
|
|||
void vlc_css_unescape( char *psz ) |
|||
{ |
|||
if( !psz ) |
|||
return; |
|||
char *r = psz; |
|||
char *w = psz; |
|||
|
|||
while( *r ) |
|||
{ |
|||
if( *r == '\\' ) |
|||
{ |
|||
r++; |
|||
/* newlines */ |
|||
if( *r == 0 ) |
|||
{ |
|||
break; |
|||
} |
|||
else if( strchr( "nfr", *r ) ) |
|||
{ |
|||
switch( r[0] ) |
|||
{ |
|||
case 'n': |
|||
*w++ = '\n'; |
|||
r++; |
|||
break; |
|||
case 'r': |
|||
*w++ = '\r'; |
|||
if( r[1] && r[1] == 'n' ) |
|||
{ |
|||
*w++ = '\n'; |
|||
r++; |
|||
} |
|||
r++; |
|||
break; |
|||
case 'f': |
|||
*w++ = '\f'; |
|||
r++; |
|||
break; |
|||
} |
|||
} |
|||
else if( isxdigit( *r ) ) |
|||
{ |
|||
const char *p_start = r; |
|||
int i; |
|||
for( i=0; i<6 && *r && isxdigit( *r ); i++ ) |
|||
r++; |
|||
const char backup = *r; |
|||
*r = 0; |
|||
unsigned i_value = strtoul( p_start, NULL, 16 ); |
|||
*r = backup; |
|||
if( i < 6 && *r && *r == ' ' ) |
|||
r++; |
|||
w += CodePointToUTF8( i_value, w ); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
*w++ = *r++; |
|||
} |
|||
} |
|||
|
|||
*w = 0; |
|||
} |
|||
|
|||
char * vlc_css_unescaped( const char *psz ) |
|||
{ |
|||
char *psz_ret = strdup( psz ); |
|||
vlc_css_unescape( psz_ret ); |
|||
return psz_ret; |
|||
} |
|||
|
|||
char * vlc_css_unquoted( const char *psz ) |
|||
{ |
|||
char *psz_ret; |
|||
if( *psz == '\'' || *psz == '\"' ) |
|||
{ |
|||
size_t i_len = strlen(psz); |
|||
if( psz[i_len - 1] == psz[0] ) |
|||
psz_ret = strndup( psz + 1, i_len - 2 ); |
|||
else |
|||
psz_ret = strdup( psz ); |
|||
} |
|||
else |
|||
{ |
|||
psz_ret = strdup( psz ); |
|||
} |
|||
return psz_ret; |
|||
} |
|||
|
|||
|
|||
char * vlc_css_unquotedunescaped( const char *psz ) |
|||
{ |
|||
char *psz_ret = vlc_css_unquoted( psz ); |
|||
if( psz_ret ) |
|||
vlc_css_unescape( psz_ret ); |
|||
return psz_ret; |
|||
} |
|||
|
|||
#ifdef CSS_PARSER_DEBUG |
|||
|
|||
|
|||
static void css_properties_Debug( const vlc_css_declaration_t *p_decl ) |
|||
{ |
|||
printf("set %s to ", p_decl->psz_property); |
|||
for( size_t i=0; i<p_decl->expr->i_count; i++ ) |
|||
{ |
|||
printf("term %s ", p_decl->expr->seq[i].term.psz); |
|||
} |
|||
printf("\n"); |
|||
} |
|||
|
|||
void css_selector_Debug( const vlc_css_selector_t *p_sel ) |
|||
{ |
|||
printf("select its "); |
|||
switch( p_sel->combinator ) |
|||
{ |
|||
case RELATION_DESCENDENT: |
|||
printf("descendent"); |
|||
break; |
|||
case RELATION_DIRECTADJACENT: |
|||
printf("adjacent"); |
|||
break; |
|||
case RELATION_INDIRECTADJACENT: |
|||
printf("indirect adjacent"); |
|||
break; |
|||
case RELATION_CHILD: |
|||
printf("child"); |
|||
break; |
|||
case RELATION_SELF: |
|||
break; |
|||
} |
|||
|
|||
printf(" nodes matching filter: "); |
|||
switch( p_sel->type ) |
|||
{ |
|||
case SELECTOR_SIMPLE: |
|||
printf("<%s>\n", p_sel->psz_name); |
|||
break; |
|||
case SELECTOR_PSEUDOCLASS: |
|||
printf(":%s\n", p_sel->psz_name); |
|||
break; |
|||
case SELECTOR_PSEUDOELEMENT: |
|||
printf("::%s\n", p_sel->psz_name); |
|||
break; |
|||
case SPECIFIER_ID: |
|||
printf("%s\n", p_sel->psz_name); |
|||
break; |
|||
case SPECIFIER_CLASS: |
|||
printf(".%s\n", p_sel->psz_name); |
|||
break; |
|||
case SPECIFIER_ATTRIB: |
|||
printf("[%s]\n", p_sel->psz_name); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void css_rule_Debug( const vlc_css_rule_t *p_rule ) |
|||
{ |
|||
if( p_rule == NULL ) |
|||
return; |
|||
printf("add for rule nodes:\n"); |
|||
for( const vlc_css_selector_t *p_sel = p_rule->p_selectors; |
|||
p_sel; p_sel = p_sel->p_next ) |
|||
{ |
|||
css_selector_Debug( p_sel ); |
|||
for( const vlc_css_selector_t *p_spec = p_sel->specifiers.p_first; |
|||
p_spec; p_spec = p_spec->p_next ) |
|||
css_selector_Debug( p_spec ); |
|||
|
|||
if( p_sel->p_next ) |
|||
printf("add nodes\n"); |
|||
} |
|||
|
|||
for( const vlc_css_declaration_t *p_decl = p_rule->p_declarations; |
|||
p_decl; p_decl = p_decl->p_next ) |
|||
{ |
|||
css_properties_Debug( p_decl ); |
|||
} |
|||
} |
|||
|
|||
#endif |
|||
@ -0,0 +1,182 @@ |
|||
/*****************************************************************************
|
|||
* css_parser.h : CSS parser interface |
|||
***************************************************************************** |
|||
* Copyright (C) 2017 VideoLabs, VLC authors and VideoLAN |
|||
* |
|||
* This program is free software; you can redistribute it and/or modify it |
|||
* under the terms of the GNU Lesser General Public License as published by |
|||
* the Free Software Foundation; either version 2.1 of the License, or |
|||
* (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
#ifndef CSS_PARSER_H |
|||
#define CSS_PARSER_H |
|||
|
|||
//#define YYDEBUG 1
|
|||
//#define CSS_PARSER_DEBUG
|
|||
|
|||
typedef struct vlc_css_parser_t vlc_css_parser_t; |
|||
typedef struct vlc_css_selector_t vlc_css_selector_t; |
|||
typedef struct vlc_css_declaration_t vlc_css_declaration_t; |
|||
typedef struct vlc_css_rule_t vlc_css_rule_t; |
|||
typedef struct vlc_css_expr_t vlc_css_expr_t; |
|||
|
|||
typedef struct |
|||
{ |
|||
float val; |
|||
char *psz; |
|||
vlc_css_expr_t *function; |
|||
enum |
|||
{ |
|||
TYPE_NONE = 0, |
|||
TYPE_EMS, |
|||
TYPE_EXS, |
|||
TYPE_PIXELS, |
|||
TYPE_POINTS, |
|||
TYPE_MILLIMETERS, |
|||
TYPE_PERCENT, |
|||
TYPE_MILLISECONDS, |
|||
TYPE_HERTZ, |
|||
TYPE_DEGREES, |
|||
TYPE_DIMENSION, |
|||
TYPE_STRING = 0x20, |
|||
TYPE_FUNCTION, |
|||
TYPE_IDENTIFIER, |
|||
TYPE_HEXCOLOR, |
|||
TYPE_UNICODERANGE, |
|||
TYPE_URI, |
|||
} type; |
|||
} vlc_css_term_t; |
|||
|
|||
struct vlc_css_expr_t |
|||
{ |
|||
struct |
|||
{ |
|||
char op; |
|||
vlc_css_term_t term; |
|||
} *seq; |
|||
size_t i_alloc; |
|||
size_t i_count; |
|||
}; |
|||
|
|||
struct vlc_css_declaration_t |
|||
{ |
|||
char *psz_property; |
|||
vlc_css_expr_t *expr; |
|||
vlc_css_declaration_t *p_next; |
|||
}; |
|||
|
|||
enum vlc_css_match_e |
|||
{ |
|||
MATCH_EQUALS, |
|||
MATCH_INCLUDES, |
|||
MATCH_DASHMATCH, |
|||
MATCH_BEGINSWITH, |
|||
MATCH_ENDSWITH, |
|||
MATCH_CONTAINS, |
|||
}; |
|||
|
|||
enum vlc_css_relation_e |
|||
{ |
|||
RELATION_SELF = 0, |
|||
RELATION_DESCENDENT = ' ', |
|||
RELATION_DIRECTADJACENT = '+', |
|||
RELATION_INDIRECTADJACENT = '~', |
|||
RELATION_CHILD = '>', |
|||
}; |
|||
|
|||
struct vlc_css_selector_t |
|||
{ |
|||
char *psz_name; |
|||
enum |
|||
{ |
|||
SELECTOR_SIMPLE = 0, |
|||
SELECTOR_PSEUDOCLASS, |
|||
SELECTOR_PSEUDOELEMENT, |
|||
/* SELECTOR_PSEUDONTHCHILD,
|
|||
SELECTOR_PSEUDONTHTYPE, |
|||
SELECTOR_PSEUDONTHLASTCHILD, |
|||
SELECTOR_PSEUDONTHLASTTYPE,*/ |
|||
SPECIFIER_ID, |
|||
SPECIFIER_CLASS, |
|||
SPECIFIER_ATTRIB, |
|||
} type; |
|||
struct |
|||
{ |
|||
vlc_css_selector_t *p_first; |
|||
vlc_css_selector_t **pp_append; |
|||
} specifiers; |
|||
|
|||
enum vlc_css_match_e match; |
|||
vlc_css_selector_t *p_matchsel; |
|||
|
|||
enum vlc_css_relation_e combinator; |
|||
vlc_css_selector_t *p_next; |
|||
}; |
|||
|
|||
struct vlc_css_rule_t |
|||
{ |
|||
bool b_valid; |
|||
vlc_css_selector_t *p_selectors; |
|||
vlc_css_declaration_t *p_declarations; |
|||
|
|||
vlc_css_rule_t *p_next; |
|||
}; |
|||
|
|||
struct vlc_css_parser_t |
|||
{ |
|||
struct |
|||
{ |
|||
vlc_css_rule_t *p_first; |
|||
vlc_css_rule_t **pp_append; |
|||
} rules; |
|||
}; |
|||
|
|||
#define CHAIN_APPEND_DECL(n, t) void n( t *p_a, t *p_b ) |
|||
|
|||
void vlc_css_term_Clean( vlc_css_term_t a ); |
|||
bool vlc_css_expression_AddTerm( vlc_css_expr_t *p_expr, char op, vlc_css_term_t a ); |
|||
void vlc_css_expression_Delete( vlc_css_expr_t *p_expr ); |
|||
vlc_css_expr_t * vlc_css_expression_New( vlc_css_term_t term ); |
|||
|
|||
CHAIN_APPEND_DECL(vlc_css_declarations_Append, vlc_css_declaration_t); |
|||
void vlc_css_declarations_Delete( vlc_css_declaration_t *p_decl ); |
|||
vlc_css_declaration_t * vlc_css_declaration_New( const char *psz ); |
|||
|
|||
CHAIN_APPEND_DECL(vlc_css_selector_Append, vlc_css_selector_t); |
|||
void vlc_css_selector_AddSpecifier( vlc_css_selector_t *p_sel, vlc_css_selector_t *p_spec ); |
|||
void vlc_css_selectors_Delete( vlc_css_selector_t *p_sel ); |
|||
vlc_css_selector_t * vlc_css_selector_New( int type, const char *psz ); |
|||
|
|||
void vlc_css_rules_Delete( vlc_css_rule_t *p_rule ); |
|||
vlc_css_rule_t * vlc_css_rule_New( void ); |
|||
|
|||
void vlc_css_parser_AddRule( vlc_css_parser_t *p_parser, vlc_css_rule_t *p_rule ); |
|||
void vlc_css_parser_Debug( const vlc_css_parser_t *p_parser ); |
|||
void vlc_css_parser_Clean( vlc_css_parser_t *p_parser ); |
|||
void vlc_css_parser_Init( vlc_css_parser_t *p_parser ); |
|||
|
|||
bool vlc_css_parser_ParseBytes( vlc_css_parser_t *p_parser, const uint8_t *, size_t ); |
|||
bool vlc_css_parser_ParseString( vlc_css_parser_t *p_parser, const char * ); |
|||
|
|||
void vlc_css_unescape( char *psz ); |
|||
char * vlc_css_unquoted( const char *psz ); |
|||
char * vlc_css_unescaped( const char *psz ); |
|||
char * vlc_css_unquotedunescaped( const char *psz ); |
|||
|
|||
# ifdef CSS_PARSER_DEBUG |
|||
|
|||
void css_selector_Debug( const vlc_css_selector_t *p_sel ); |
|||
void css_rule_Debug( const vlc_css_rule_t *p_rule ); |
|||
|
|||
# endif |
|||
|
|||
#endif |
|||
Loading…
Reference in new issue