/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ %{ #include #include "cssI.h" %} %token NUMBER STRING PERCENTAGE LENGTH EMS %token EXS IDENT HEXCOLOR URL RGB %token CDO CDC %token IMPORTANT_SYM %token IMPORT_SYM %token DOT_AFTER_IDENT DOT %token LINK_PSCLASS VISITED_PSCLASS ACTIVE_PSCLASS %token LEADING_LINK_PSCLASS %token LEADING_VISITED_PSCLASS %token LEADING_ACTIVE_PSCLASS %token FIRST_LINE %token FIRST_LETTER %token WILD %token BACKGROUND %token BG_COLOR BG_IMAGE BG_REPEAT BG_ATTACHMENT BG_POSITION %token FONT %token FONT_STYLE FONT_VARIANT FONT_WEIGHT FONT_SIZE %token FONT_NORMAL LINE_HEIGHT %token LIST_STYLE LS_TYPE LS_NONE LS_POSITION %token BORDER BORDER_STYLE BORDER_WIDTH %token FONT_SIZE_PROPERTY %token FONTDEF %type stylesheet %type import ruleset unary_operator operator property %type import_value %type selector_list declaration_list declaration selector %type pseudo_element simple_selector %type contextual_selector %type contextual_selector_list %type element_name pseudo_class class id %type leading_pseudo_class %type expr prio term optional_priority %type numeric_const unsigned_numeric_const %type numeric_unit unsigned_numeric_unit %type unsigned_symbol color_code %type url %type background_property %type background_values_list %type background_value %type background_values %type background_position_expr %type background_position_value %type background_position_keyword %type font_property font_size_property %type font_values_list %type font_size_value font_family_value line_height_value %type font_optional_value font_optional_values_list %type font_family_operator font_family_expr %type list_style_property %type list_style_values_list list_style_value %type border_property %type border_values_list border_value %type atrule fontdef %union { css_node binary_node; } %{ /* Background Shorthand Property */ typedef struct backgroundShorthandRecord { css_node color; css_node image; css_node repeat; css_node attachment; css_node position; int parse_error; } BackgroundShorthandRecord, *BackgroundShorthand; #define BackgroundColor 1 #define BackgroundImage 2 #define BackgroundRepeat 3 #define BackgroundAttachment 4 #define BackgroundPosition 5 static void ClearBackground(void); static void AddBackground(int node_type, css_node node); static css_node AssembleBackground(void); static BackgroundShorthandRecord bg; /* background property names */ static char * bg_image = "background-image"; static char * bg_color = "background-color"; static char * bg_repeat = "background-repeat"; static char * bg_attachment = "background-attachment"; static char * bg_position = "background-position"; /* background property initial values */ static char * css_none = "none"; static char * css_transparent = "transparent"; static char * css_repeat = "repeat"; static char * css_scroll = "scroll"; static char * css_origin = "0% 0%"; /* Font Shorthand Property */ typedef struct fontShorthandRecord { css_node style; css_node variant; css_node weight; css_node size; css_node leading; css_node family; int normal_count; int parse_error; } FontShorthandRecord, *FontShorthand; #define FontStyle 6 #define FontVariant 7 #define FontWeight 8 #define FontNormal 9 #define FontSize 10 #define FontLeading 11 #define FontFamily 12 static void ClearFont(void); static void AddFont(int node_type, css_node node); static css_node AssembleFont(void); static FontShorthandRecord font; /* font property names */ static char * line_height = "line-height"; static char * font_family = "font-family"; static char * font_style = "font-style"; static char * font_variant = "font-variant"; static char * font_weight = "font-weight"; static char * font_size = "font-size"; /* font property initial values */ static char * css_normal = "normal"; /* List-Style Shorthand Property */ typedef struct listStyleShorthandRecord { css_node marker; css_node image; css_node position; int none_count; int parse_error; } ListStyleShorthandRecord, *ListStyleShorthand; #define ListStyleMarker 13 #define ListStyleImage 14 #define ListStylePosition 15 #define ListStyleNone 16 static void ClearListStyle(void); static void AddListStyle(int node_type, css_node node); static css_node AssembleListStyle(void); static ListStyleShorthandRecord ls; /* list-style property names */ static char * list_style_type = "list-style-type"; static char * list_style_image = "list-style-image"; static char * list_style_position = "list-style-position"; /* list-style initial values */ static char * css_disc = "disc"; static char * css_outside = "outside"; /* Border Shorthand Property */ typedef struct borderShorthandRecord { css_node width; css_node style; css_node color; int parse_error; } BorderShorthandRecord, *BorderShorthand; #define BorderWidth 17 #define BorderStyle 18 #define BorderColor 19 static void ClearBorder(void); static void AddBorder(int node_type, css_node node); static css_node AssembleBorder(void); static BorderShorthandRecord border; /* border property names */ static char * border_width = "border-width"; static char * border_style = "border-style"; static char * border_color = "border-color"; /* border initial values */ static char * css_medium = "medium"; /* Define yyoverflow and the forward declarations for bison. */ #define yyoverflow css_overflow static void css_overflow(const char *message, short **yyss1, int yyss1_size, YYSTYPE **yyvs1, int yyvs1_size, int *yystacksize); static css_node NewNode(int node_id, char *ss, css_node left, css_node right); static void LeftAppendNode(css_node head, css_node new_node); static css_node NewDeclarationNode(int node_id, char *ss, char *prop); static css_node NewComponentNode(css_node value, char *prop); css_node css_tree_root; /* pseudo-classes */ static char * css_link = "link"; static char * css_visited = "visited"; static char * css_active = "active"; #ifdef CSS_PARSE_DEBUG #define TRACE1(str) trace1(str) #define TRACE2(format, str) trace2(format, str) static trace1(const char * str) { printf("%s", str); } static trace2(const char *fmt, char *str) { printf(fmt, str); } #else #define TRACE1(str) #define TRACE2(format, str) #endif int css_error(const char * diagnostic) { #ifdef CSS_PARSE_REPORT_ERRORS char * identifier = "CSS1 parser message:"; (void) fprintf(stderr, "%s error, text ='%s', diagnostic ='%s'\n", identifier, css_text, diagnostic ? diagnostic : ""); #endif return 1; } int css_wrap(void) { #ifdef CSS_PARSE_DEBUG printf("css_wrap() was called.\n"); #endif return 1; } %} %% stylesheet : { $$ = NULL; } | stylesheet CDO { TRACE1("-cdo\n"); $$ = $1; } | stylesheet atrule { $$ = $1; if ($$ == NULL) { css_tree_root = $2; $$ = css_tree_root; } else LeftAppendNode($$, $2); } | stylesheet ruleset { css_node tmp; tmp = NewNode(NODE_RULESET_LIST, NULL, NULL, $2); $$ = $1; if ($$ == NULL) { css_tree_root = tmp; $$ = css_tree_root; } else LeftAppendNode($$, tmp); } | stylesheet CDC { TRACE1("-cdc\n"); $$ = $1; } | stylesheet error { yyerrok; yyclearin; } ; atrule : import { $$ = NewNode(NODE_IMPORT_LIST, NULL, NULL, $1); } | fontdef { $$ = NewNode(NODE_FONTDEF_LIST, NULL, NULL, $1); } ; import : IMPORT_SYM import_value ';' { $$ = $2; } ; fontdef : FONTDEF import_value ';' { $$ = $2; } ; import_value : STRING { $$ = NewNode(NODE_IMPORT_STRING, css_text, NULL, NULL); } | url { TRACE1("-import url\n"); $1->node_id = NODE_IMPORT_URL; $$ = $1; } ; unary_operator : '-' { TRACE1("-unary_op '-'\n"); $$ = NewNode(NODE_UNARY_OP, css_text, NULL, NULL); } | '+' { TRACE1("-unary_op '+'\n"); $$ = NewNode(NODE_UNARY_OP, css_text, NULL, NULL); } ; operator : { TRACE1("-empty operator\n"); $$ = NewNode(NODE_EMPTY_OP, NULL, NULL, NULL); } | '/' { TRACE1("-operator : '/'\n"); $$ = NewNode(NODE_EXPR_OP, css_text, NULL, NULL); } | ',' { TRACE1("-operator : ','\n"); $$ = NewNode(NODE_EXPR_OP, css_text, NULL, NULL); } ; property : IDENT { TRACE2("-property : IDENT '%s'\n", css_text); $$ = NewNode(NODE_PROPERTY, css_text, NULL, NULL); } ; font_size_property : FONT_SIZE_PROPERTY { $$ = NewNode(NODE_PROPERTY, css_text, NULL, NULL); } ; background_property : BACKGROUND { TRACE2("-property : BACKGROUND '%s'\n", css_text); ClearBackground(); $$ = NULL; /* This shorthand property will generate five component properties. */ } ; font_property : FONT { TRACE2("-property : FONT '%s'\n", css_text); ClearFont(); $$ = NULL; /* This shorthand property will generate six component properties. */ } ; list_style_property : LIST_STYLE { ClearListStyle(); $$ = NULL; /* This shorthand property will generate three component properties. */ } ; border_property : BORDER { ClearBorder(); $$ = NULL; /* This shorthand property will generate 2 or 3 component properties. */ } ; ruleset : selector_list '{' declaration_list '}' { TRACE1("-selector_list { declaration_list }\n"); $$ = NewNode(NODE_RULESET, NULL, $1, $3); } ; selector_list : selector { TRACE1("-selector\n"); $$ = NewNode(NODE_SELECTOR_LIST, NULL, NULL, $1); } | selector_list ',' selector { css_node tmp; TRACE1("-selector_list , selector\n"); tmp = NewNode(NODE_SELECTOR_LIST, NULL, NULL, $3); $$ = $1; if( $$ == NULL ) $$ = tmp; else LeftAppendNode( $$, tmp ); } ; selector : simple_selector { TRACE1("-simple_selector\n"); $$ = NewNode(NODE_SELECTOR, NULL, $1, NULL); } | simple_selector pseudo_element { TRACE1("-simple_selector : pseudo_element\n"); $$ = NewNode(NODE_SELECTOR, NULL, $1, $2); } | contextual_selector { TRACE1("-contextual_selector\n"); $$ = NewNode(NODE_SELECTOR, NULL, $1, NULL); } | contextual_selector pseudo_element { TRACE1("-contextual_selector : pseudo_element\n"); $$ = NewNode(NODE_SELECTOR, NULL, $1, $2); } ; /* * A simple_selector is something like H1, PRE.FOO, * .FOO, etc., or it is an ID: #p004 */ contextual_selector : contextual_selector_list simple_selector { TRACE1("-contextual_selector_list simple_selector\n"); $$ = NewNode(NODE_SELECTOR_CONTEXTUAL, NULL, $1, $2); } ; contextual_selector_list : simple_selector { TRACE1("-simple_selector\n"); $$ = NewNode(NODE_SELECTOR_CONTEXTUAL, NULL, NULL, $1); } | contextual_selector_list simple_selector { TRACE1("-contextual_selector_list simple_selector\n"); $$ = NewNode(NODE_SELECTOR_CONTEXTUAL, NULL, $1, $2); } ; simple_selector : element_name { TRACE1("-element_name\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_NAME_ONLY, NULL, $1, NULL); } | DOT class { TRACE1("-dot class\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_DOT_AND_CLASS, NULL, $2, NULL); } | id { TRACE1("-id\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_ID_SELECTOR, NULL, $1, NULL); } | element_name DOT_AFTER_IDENT class { TRACE1("-element dot class\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_NAME_AND_CLASS, NULL, $1, $3); } | element_name pseudo_class { TRACE1("-element pseudo_class\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_NAME_PSEUDO_CLASS, NULL, $1, $2); } | element_name DOT_AFTER_IDENT class pseudo_class { TRACE2("-element_name '%s' dot class pseudo_class\n", $1->string); $1->node_id = NODE_SIMPLE_SELECTOR_NAME_CLASS_PSEUDO_CLASS; $1->left = $3; $1->right = $4; $$ = $1; } | DOT class pseudo_class { TRACE1("-dot class pseudo_class\n"); $$ = NewNode(NODE_SIMPLE_SELECTOR_NAME_CLASS_PSEUDO_CLASS, "A", $2, $3); } | leading_pseudo_class { css_node tmp; TRACE1("solitary pseudo_class\n"); /* See CSS1 spec of 17 December 1996 section 2.1 Anchor pseudo-classes */ tmp = NewNode(NODE_ELEMENT_NAME, "A", NULL, NULL); $$ = NewNode(NODE_SIMPLE_SELECTOR_NAME_PSEUDO_CLASS, NULL, tmp, $1); } | WILD { $$ = NewNode(NODE_WILD, NULL, NULL, NULL); } ; element_name : IDENT { TRACE2("-IDENT '%s' to element_name\n", css_text); $$ = NewNode(NODE_ELEMENT_NAME, css_text, NULL, NULL); } ; class : IDENT { TRACE2("-IDENT '%s' to class\n", css_text); $$ = NewNode(NODE_CLASS, css_text, NULL, NULL); } ; id : '#' IDENT { TRACE2("-IDENT '%s' to id\n", css_text); $$ = NewNode(NODE_ID_SELECTOR, css_text, NULL, NULL); } ; pseudo_class : LINK_PSCLASS { $$ = NewNode(NODE_LINK_PSCLASS, css_link, NULL, NULL); } | VISITED_PSCLASS { $$ = NewNode(NODE_VISITED_PSCLASS, css_visited, NULL, NULL); } | ACTIVE_PSCLASS { $$ = NewNode(NODE_ACTIVE_PSCLASS, css_active, NULL, NULL); } ; leading_pseudo_class : LEADING_LINK_PSCLASS { $$ = NewNode(NODE_LINK_PSCLASS, css_link, NULL, NULL); } | LEADING_VISITED_PSCLASS { $$ = NewNode(NODE_VISITED_PSCLASS, css_visited, NULL, NULL); } | LEADING_ACTIVE_PSCLASS { $$ = NewNode(NODE_ACTIVE_PSCLASS, css_active, NULL, NULL); } ; pseudo_element : FIRST_LINE { TRACE2("-IDENT '%s' to pseudo_element\n", css_text); $$ = NewNode(NODE_PSEUDO_ELEMENT, "first-Line", NULL, NULL); } | FIRST_LETTER { TRACE2("-IDENT '%s' to pseudo_element\n", css_text); $$ = NewNode(NODE_PSEUDO_ELEMENT, "first-Letter", NULL, NULL); } ; declaration_list : declaration { TRACE1("-declaration\n"); $$ = $1; } | declaration_list ';' declaration { /* to keep the order, append the new node to the end. */ TRACE1("-declaration_list ';' declaration\n"); $$ = $1; if (NULL == $$) $$ = $3; else LeftAppendNode($$, $3); } ; declaration : { TRACE1("-empty declaration\n"); $$ = NewNode(NODE_DECLARATION_LIST, NULL, NULL, NULL); } | property ':' expr optional_priority { css_node dcl; TRACE1("-property : expr\n"); dcl = NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, $1, $3); $$ = NewNode(NODE_DECLARATION_LIST, NULL, NULL, dcl); } | font_size_property ':' font_size_value optional_priority { css_node expr, dcl; expr = NewNode(NODE_EXPR, NULL, NULL, $3); dcl = NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, $1, expr); $$ = NewNode(NODE_DECLARATION_LIST, NULL, NULL, dcl); } | background_property ':' background_values_list optional_priority { /* The priority notation is ignored by the translator * so we conveniently ignore it here, too. */ TRACE1("-background property : background_values_list\n"); $$ = AssembleBackground(); } | font_property ':' font_values_list optional_priority { TRACE1("-font property : font_values_list prio\n"); /* The priority notation is ignored by the translator * so we conveniently ignore it here, too. */ $$ = AssembleFont(); } | list_style_property ':' list_style_values_list optional_priority { $$ = AssembleListStyle(); } | border_property ':' border_values_list optional_priority { $$ = AssembleBorder(); } ; optional_priority : { /* nothing */ } | prio ; prio : IMPORTANT_SYM { /* !important */ TRACE1("-IMPORTANT_SYM\n"); $$ = NULL; } ; expr : term { TRACE1("-term\n"); $$ = NewNode(NODE_EXPR, NULL, NULL, $1); } | expr operator term { TRACE1("-expr op term\n"); $$ = $1; /* put the new term at the end */ $2->right = $3; if ($$ == NULL) $$ = $2; else LeftAppendNode( $$, $2 ); } ; term : unsigned_symbol | color_code | url | numeric_unit | numeric_const ; numeric_const : unsigned_numeric_const | unary_operator unsigned_numeric_const { TRACE1("-unary_operator signed_const to term\n"); $$ = $1; $$->left = $2; } ; unsigned_numeric_const : NUMBER { $$ = NewNode(NODE_NUMBER, css_text, NULL, NULL); } ; numeric_unit : unsigned_numeric_unit | unary_operator unsigned_numeric_unit { $$ = $1; $$->left = $2; } ; unsigned_numeric_unit : PERCENTAGE { $$ = NewNode(NODE_PERCENTAGE, css_text, NULL, NULL); } | LENGTH { $$ = NewNode(NODE_LENGTH, css_text, NULL, NULL); } | EMS { $$ = NewNode(NODE_EMS, css_text, NULL, NULL); } | EXS{ $$ = NewNode(NODE_EMS, css_text, NULL, NULL); } ; unsigned_symbol : STRING { $$ = NewNode(NODE_STRING, css_text, NULL, NULL); } | IDENT { TRACE2("-IDENT '%s' to unsigned_symbol\n", css_text); $$ = NewNode(NODE_IDENT, css_text, NULL, NULL); } ; url : URL { $$ = NewNode(NODE_URL, css_text, NULL, NULL); } ; color_code : HEXCOLOR { $$ = NewNode(NODE_HEXCOLOR, css_text, NULL, NULL); } | RGB { $$ = NewNode(NODE_RGB, css_text, NULL, NULL); } ; background_values_list : background_values | background_position_value | background_values background_position_value | background_position_value background_values | background_values background_position_value background_values ; background_values : background_value | background_values background_value ; background_value : URL { $$ = NewDeclarationNode(NODE_URL, css_text, bg_image); AddBackground(BackgroundImage, $$); } | BG_IMAGE { $$ = NewDeclarationNode(NODE_IDENT, css_text, bg_image); AddBackground(BackgroundImage, $$); } | color_code { $$ = NewComponentNode($1, bg_color); AddBackground(BackgroundColor, $$); } | IDENT { $$ = NewDeclarationNode(NODE_IDENT, css_text, bg_color); AddBackground(BackgroundColor, $$); } | BG_COLOR { $$ = NewDeclarationNode(NODE_IDENT, css_text, bg_color); AddBackground(BackgroundColor, $$); } | BG_REPEAT { $$ = NewDeclarationNode(NODE_IDENT, css_text, "background-repeat"); AddBackground(BackgroundRepeat, $$); } | BG_ATTACHMENT { $$ = NewDeclarationNode(NODE_IDENT, css_text, "background-attachment"); AddBackground(BackgroundAttachment, $$); } ; background_position_value : background_position_expr { css_node property, declaration; property = NewNode(NODE_PROPERTY, bg_position, NULL, NULL); declaration = NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, property, $1); AddBackground(BackgroundPosition, declaration); } ; background_position_expr : numeric_unit { $$ = NewNode(NODE_EXPR, NULL, NULL, $1); } | numeric_unit numeric_unit { css_node operator; $$ = NewNode(NODE_EXPR, NULL, NULL, $1); operator = NewNode(NODE_EMPTY_OP, NULL, NULL, $2); if ($$ == NULL) $$ = operator; else LeftAppendNode($$, operator); } | background_position_keyword { $$ = NewNode(NODE_EXPR, NULL, NULL, $1); } | background_position_keyword background_position_keyword { css_node operator; $$ = $1; operator = NewNode(NODE_EMPTY_OP, NULL, NULL, $2); if ($$ == NULL) $$ = operator; else LeftAppendNode($$, operator); } ; background_position_keyword : BG_POSITION { $$ = NewNode(NODE_IDENT, css_text, NULL, NULL); } ; font_values_list : font_optional_values_list font_size_value font_family_value { css_node tmp; tmp = NewComponentNode($2, font_size); AddFont(FontSize, tmp); AddFont(FontFamily, $3); $$ = NULL; } | font_optional_values_list font_size_value '/' line_height_value font_family_value { css_node tmp; tmp = NewComponentNode($2, font_size); AddFont(FontSize, tmp); tmp = NewComponentNode($4, line_height); AddFont(FontLeading, tmp); AddFont(FontFamily, $5); $$ = NULL; } ; font_family_operator : { TRACE1("-empty operator\n"); $$ = NewNode(NODE_EMPTY_OP, NULL, NULL, NULL); } | ',' { TRACE1("-operator : ','\n"); $$ = NewNode(NODE_EXPR_OP, css_text, NULL, NULL); } ; font_family_value : font_family_expr { css_node property; TRACE1("-font_family_expr to font_family_value\n"); property = NewNode(NODE_PROPERTY, font_family, NULL, NULL); $$ = NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, property, $1); } ; font_family_expr : unsigned_symbol { TRACE1("-unsigned_symbol\n"); $$ = NewNode(NODE_EXPR, NULL, NULL, $1); } | font_family_expr font_family_operator unsigned_symbol { TRACE1("-font_family_value font_family_op unsigned_symbol\n"); $$ = $1; /* put the new term at the end */ $2->right = $3; if ($$ == NULL) $$ = $2; else LeftAppendNode( $$, $2 ); } ; font_optional_values_list : { /* empty */ $$ = NULL; } | font_optional_values_list font_optional_value { $$ = NULL; } ; font_optional_value : FONT_STYLE { TRACE2("-FONT_STYLE '%s' to font_optional_value\n", css_text); $$ = NewDeclarationNode(NODE_IDENT, css_text, font_style); AddFont(FontStyle, $$); } | FONT_VARIANT { TRACE2("-FONT_VARIANT '%s' to font_optional_value\n", css_text); $$ = NewDeclarationNode(NODE_IDENT, css_text, font_variant); AddFont(FontVariant, $$); } | FONT_WEIGHT { TRACE2("-FONT_WEIGHT '%s' to font_optional_value\n", css_text); $$ = NewDeclarationNode(NODE_IDENT, css_text, font_weight); AddFont(FontWeight, $$); } | FONT_NORMAL { AddFont(FontNormal, NULL); } ; font_size_value : unsigned_numeric_unit | FONT_SIZE { $$ = NewNode(NODE_IDENT, css_text, NULL, NULL); } | '+' unsigned_numeric_unit { /* just drop the '+' on the floor */ $$ = $2; } ; line_height_value : numeric_unit | numeric_const | LINE_HEIGHT { /* The only valid identifier is the word "normal". * There's an idea that a normal leading value is font-specific, * so the identifier is passed upwards. */ $$ = NewNode(NODE_IDENT, css_text, NULL, NULL); } ; list_style_values_list : list_style_value | list_style_values_list list_style_value ; list_style_value : LS_TYPE { $$ = NewDeclarationNode(NODE_IDENT, css_text, list_style_type); AddListStyle(ListStyleMarker, $$); $$ = NULL; } | LS_NONE { AddListStyle(ListStyleNone, NULL); $$ = NULL; } | LS_POSITION { $$ = NewDeclarationNode(NODE_IDENT, css_text, list_style_position); AddListStyle(ListStylePosition, $$); $$ = NULL; } | url { $$ = NewComponentNode($1, list_style_image); AddListStyle(ListStyleImage, $$); $$ = NULL; } ; border_values_list : border_value | border_values_list border_value ; border_value : BORDER_STYLE { $$ = NewDeclarationNode(NODE_IDENT, css_text, border_style); AddBorder(BorderStyle, $$); $$ = NULL; } | BORDER_WIDTH { $$ = NewDeclarationNode(NODE_IDENT, css_text, border_width); AddBorder(BorderWidth, $$); $$ = NULL; } | numeric_unit { $$ = NewComponentNode($1, border_width); AddBorder(BorderWidth, $$); $$ = NULL; } | color_code { $$ = NewComponentNode($1, border_color); AddBorder(BorderColor, $$); $$ = NULL; } | IDENT { $$ = NewDeclarationNode(NODE_IDENT, css_text, border_color); AddBorder(BorderColor, $$); $$ = NULL; } ; %% #include "xp_mem.h" #include "xpassert.h" /* Memory allocated here will be freed by css_FreeNode in csstojs.c */ static css_node NewNode(int node_id, char *ss, css_node left, css_node right) { register css_node pp; if ((pp = XP_NEW(css_nodeRecord)) == NULL) return NULL; pp->node_id = node_id; pp->string = NULL; if (ss) { if ((pp->string = (char *) XP_ALLOC(strlen(ss) + 1)) != NULL) (void) strcpy(pp->string, ss); } pp->left = left; pp->right = right; return pp; } /* Append new_node to the leftmost leaf of head */ static void LeftAppendNode(css_node head, css_node new_node) { if (head == NULL) return; while (head->left != NULL) head = head->left; head->left = new_node; } static css_node NewDeclarationNode(int node_id, char *ss, char *prop) { css_node value, expression, property; value = NewNode(node_id, ss, NULL, NULL); expression = NewNode(NODE_EXPR, NULL, NULL, value); property = NewNode(NODE_PROPERTY, prop, NULL, NULL); return NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, property, expression); } static css_node NewComponentNode(css_node value, char *prop) { css_node expression, property; expression = NewNode(NODE_EXPR, NULL, NULL, value); property = NewNode(NODE_PROPERTY, prop, NULL, NULL); return NewNode(NODE_DECLARATION_PROPERTY_EXPR, NULL, property, expression); } static void ClearFont(void) { font.style = font.variant = font.weight = NULL; font.size = font.leading = font.family = NULL; font.normal_count = font.parse_error = 0; } static void AddFont(int node_type, css_node node) { if (FontStyle == node_type && (! font.style)) font.style = node; else if (FontVariant == node_type && (! font.variant)) font.variant = node; else if (FontWeight == node_type && (! font.weight)) font.weight = node; else if (FontNormal == node_type) font.normal_count++; else if (FontSize == node_type && (! font.size)) font.size = node; else if (FontLeading == node_type && (! font.leading)) font.leading = node; else if (FontFamily == node_type && (! font.family)) font.family = node; else { font.parse_error++; if (node) css_FreeNode(node); } } static css_node AssembleFont(void) { css_node head, element; int count; count = 0; if (font.style) count++; if (font.variant) count++; if (font.weight) count++; if (font.normal_count > (3 - count)) font.parse_error++; if (font.parse_error) { if (font.style) css_FreeNode(font.style); if (font.variant) css_FreeNode(font.variant); if (font.weight) css_FreeNode(font.weight); if (font.size) css_FreeNode(font.size); if (font.leading) css_FreeNode(font.leading); if (font.family) css_FreeNode(font.family); ClearFont(); return NULL; } if (! font.style) font.style = NewDeclarationNode(NODE_IDENT, css_normal, font_style); if (! font.variant) font.variant = NewDeclarationNode(NODE_IDENT, css_normal, font_variant); if (! font.weight) font.weight = NewDeclarationNode(NODE_IDENT, css_normal, font_weight); if (! font.leading) font.leading = NewDeclarationNode(NODE_IDENT, css_normal, line_height); head = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.style); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.variant); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.weight); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.size); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.leading); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, font.family); LeftAppendNode(head, element); ClearFont(); return head; } static void ClearBackground(void) { bg.color = bg.image = bg.repeat = NULL; bg.attachment = bg.position = NULL; bg.parse_error = 0; } static void AddBackground(int node_type, css_node node) { if (BackgroundColor == node_type && (! bg.color)) bg.color = node; else if (BackgroundImage == node_type && (! bg.image)) bg.image = node; else if (BackgroundRepeat == node_type && (! bg.repeat)) bg.repeat = node; else if (BackgroundAttachment == node_type && (! bg.attachment)) bg.attachment = node; else if (BackgroundPosition == node_type && (! bg.position)) bg.position = node; else { bg.parse_error++; css_FreeNode(node); } } static css_node AssembleBackground(void) { css_node head, element; if (bg.parse_error) { if (bg.color) css_FreeNode(bg.color); if (bg.image) css_FreeNode(bg.image); if (bg.repeat) css_FreeNode(bg.repeat); if (bg.attachment) css_FreeNode(bg.attachment); if (bg.position) css_FreeNode(bg.position); ClearBackground(); return NULL; } if (! bg.color) bg.color = NewDeclarationNode(NODE_IDENT, css_transparent, bg_color); if (! bg.image) bg.image = NewDeclarationNode(NODE_IDENT, css_none, bg_image); if (! bg.repeat) bg.repeat = NewDeclarationNode(NODE_IDENT, css_repeat, bg_repeat); if (! bg.attachment) bg.attachment = NewDeclarationNode(NODE_IDENT, css_scroll, bg_attachment); if (! bg.position) bg.position = NewDeclarationNode(NODE_PERCENTAGE, css_origin, bg_position); head = NewNode(NODE_DECLARATION_LIST, NULL, NULL, bg.color); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, bg.image); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, bg.repeat); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, bg.attachment); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, bg.position); LeftAppendNode(head, element); ClearBackground(); return head; } static void ClearListStyle(void) { ls.marker = ls.image = ls.position = NULL; ls.none_count = ls.parse_error = 0; } static void AddListStyle(int node_type, css_node node) { if (ListStyleMarker == node_type && (! ls.marker)) ls.marker = node; else if (ListStyleImage == node_type && (! ls.image)) ls.image = node; else if (ListStylePosition == node_type && (! ls.position)) ls.position = node; else if (ListStyleNone == node_type) ls.none_count++; else { ls.parse_error++; if (node) css_FreeNode(node); } } static css_node AssembleListStyle(void) { css_node head, element; int count; char * marker_value; count = 0; if (ls.marker) count++; if (ls.image) count++; if (ls.none_count > (2 - count)) ls.parse_error++; if (ls.parse_error) { if (ls.marker) css_FreeNode(ls.marker); if (ls.image) css_FreeNode(ls.image); if (ls.position) css_FreeNode(ls.position); ClearListStyle(); return NULL; } if (! ls.marker) { /* list-style: none * could mean marker or image. It should set the marker; * the image will default to none. */ marker_value = ls.none_count ? css_none : css_disc; ls.marker = NewDeclarationNode(NODE_IDENT, marker_value, list_style_type); } if (! ls.image) ls.image = NewDeclarationNode(NODE_IDENT, css_none, list_style_image); if (! ls.position) ls.position = NewDeclarationNode(NODE_IDENT, css_outside, list_style_position); head = NewNode(NODE_DECLARATION_LIST, NULL, NULL, ls.marker); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, ls.image); LeftAppendNode(head, element); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, ls.position); LeftAppendNode(head, element); ClearListStyle(); return head; } static void ClearBorder(void) { border.width = border.style = border.color = (css_node) NULL; border.parse_error = 0; } static void AddBorder(int node_type, css_node node) { if (BorderWidth == node_type && (! border.width)) border.width = node; else if (BorderStyle == node_type && (! border.style)) border.style = node; else if (BorderColor == node_type && (! border.color)) border.color = node; else { border.parse_error++; if (node) css_FreeNode(node); } } static css_node AssembleBorder(void) { css_node head, element; if (border.parse_error) { if (border.width) css_FreeNode(border.width); if (border.style) css_FreeNode(border.style); if (border.color) css_FreeNode(border.color); ClearBorder(); return (css_node) NULL; } if (! border.width) border.width = NewDeclarationNode(NODE_IDENT, css_medium, border_width); if (! border.style) border.style = NewDeclarationNode(NODE_IDENT, css_none, border_style); head = NewNode(NODE_DECLARATION_LIST, NULL, NULL, border.width); element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, border.style); LeftAppendNode(head, element); if (border.color) { element = NewNode(NODE_DECLARATION_LIST, NULL, NULL, border.color); LeftAppendNode(head, element); } ClearBorder(); return head; } /* css_overflow is only for parsers generated by bison. */ static void css_overflow(const char *message, short **yyss1, int yyss1_size, YYSTYPE **yyvs1, int yyvs1_size, int *yystacksize) { short * yyss2; YYSTYPE * yyvs2; int new_size; if (*yystacksize >= YYMAXDEPTH) return; new_size = *yystacksize * 2; if (new_size > YYMAXDEPTH) new_size = YYMAXDEPTH; if (*yystacksize == YYINITDEPTH) { /* First time allocating from the heap. */ yyss2 = (short *) XP_ALLOC(new_size * sizeof(short)); if (yyss2) (void) memcpy((void *)yyss2, (void *) *yyss1, yyss1_size); yyvs2 = XP_ALLOC(new_size * sizeof(YYSTYPE)); if (yyvs2) (void) memcpy((void *)yyvs2, (void *) *yyvs1, yyvs1_size); } else { yyss2 = (short *) XP_REALLOC(*yyss1, new_size * sizeof(short)); yyvs2 = XP_REALLOC(*yyvs1, new_size * sizeof(YYSTYPE)); } if (yyss2 && yyvs2) { *yyss1 = yyss2; *yyvs1 = yyvs2; *yystacksize = new_size; } /* Any failure to allocate will be noticed by the caller. */ }