--
-- This file is part of FactorCSS
-- Licensed under the MIT License,
--                http://www.opensource.org/licenses/mit-license
-- Copyright 2004 James Bursa <james@semichrome.net>
--

-- Parser for CSS 2.1
-- See CSS 2.1 G.2

{
module Parser where
import Tokeniser
import CSS
}

%name parser
%tokentype { TokenPosn }
%token
	INCLUDES	{ (INCLUDES	, _) }
	DASHMATCH	{ (DASHMATCH	, _) }
	LBRACE		{ (LBRACE	, _) }
	PLUS		{ (PLUS		, _) }
	GREATER		{ (GREATER	, _) }
	COMMA		{ (COMMA	, _) }
	STRING		{ (STRING $$	, _) }
	IDENT		{ (IDENT $$	, _) }
	HASH		{ (HASH $$	, _) }
	IMPORT_SYM	{ (IMPORT_SYM	, _) }
	PAGE_SYM	{ (PAGE_SYM	, _) }
	MEDIA_SYM	{ (MEDIA_SYM	, _) }
	CHARSET_SYM	{ (CHARSET_SYM	, _) }
	IMPORTANT_SYM	{ (IMPORTANT_SYM, _) }
	EMS		{ (EMS $$	, _) }
	EXS		{ (EXS $$	, _) }
	LENGTH		{ (LENGTH $$	, _) }
	ANGLE		{ (ANGLE $$	, _) }
	TIME		{ (TIME $$	, _) }
	FREQ		{ (FREQ $$	, _) }
	DIMEN		{ (DIMEN $$	, _) }
	PERCENTAGE	{ (PERCENTAGE $$, _) }
	NUMBER		{ (NUMBER $$	, _) }
	URI		{ (URI $$	, _) }
	FUNCTION	{ (FUNCTION $$	, _) }
	DELIM		{ (DELIM $$	, _) }
	SEMI		{ (SEMI		, _) }
	RBRACE		{ (RBRACE	, _) }
	COLON		{ (COLON	, _) }
	SLASH		{ (SLASH	, _) }
	MINUS		{ (MINUS	, _) }
	DOT		{ (DOT		, _) }
	LBRAC		{ (LBRAC	, _) }
	RBRAC		{ (RBRAC	, _) }
	ASTERISK	{ (ASTERISK	, _) }
	EQUALS		{ (EQUALS	, _) }
	RPAREN		{ (RPAREN	, _) }

%%

stylesheet	:: { Stylesheet }
		: charset import_list statement_list	{ Stylesheet $1 $2 $3 }

charset		:: { Maybe String }
		:					{ Nothing }
		| CHARSET_SYM STRING SEMI		{ Just $2 }

import_list	:: { [(String, [String])] }
		:					{ [] }
		| import_list import			{ $2 : $1 }

import		:: { (String, [String]) }
		: IMPORT_SYM STRING medium_list SEMI	{ ($2, $3) }
		| IMPORT_SYM URI medium_list SEMI	{ ($2, $3) }

statement_list	:: { [Statement] }
		:					{ [] }
		| statement_list statement		{ $2 : $1 }

statement	:: { Statement }
		: ruleset				{ $1 }
		| media					{ $1 }
		| page					{ $1 }

medium_list	:: { [String] }
		:					{ [] }
		| medium_list_1				{ $1 }

medium_list_1	:: { [String] }
		: medium				{ [$1] }
		| medium_list_1 COMMA medium		{ $3 : $1 }

media		:: { Statement }
		: MEDIA_SYM medium_list_1 LBRACE ruleset_list RBRACE
							{ Media $2 $4 }

medium		:: { String }
		: IDENT					{ $1 }

ruleset_list	:: { [Statement] }
		:					{ [] }
		| ruleset_list ruleset			{ $2 : $1 }

page		:: { Statement }
		: PAGE_SYM LBRACE declaration_list_1 RBRACE
							{ Page Nothing $3 }
		| PAGE_SYM pseudo_page LBRACE declaration_list_1 RBRACE
							{ Page (Just $2) $4 }

declaration_list_1 :: { [Declaration] }
		:					{ [] }
		| declaration				{ [$1] }
		| declaration_list_1 SEMI		{ $1 }
		| declaration_list_1 SEMI declaration	{ $3 : $1 }

pseudo_page	:: { String }
		: COLON IDENT				{ $2 }

ruleset		:: { Statement }
		: selector_list_1 LBRACE declaration_list_1 RBRACE
							{ Ruleset $1 $3 }

selector_list_1	:: { [Selector] }
		: selector				{ [$1] }
		| selector_list_1 COMMA selector	{ $3 : $1 }

selector	:: { Selector }
		: simple_selector			{ [($1, NoMore)] }
		| simple_selector combinator selector	{ ($1, $2) : $3 }

combinator	:: { Combinator }
		:					{ Ancestor }
		| PLUS					{ Preceded }
		| GREATER				{ Parent }

simple_selector	:: { SimpleSelector }
		: element_name detail_list_1		{ ($1, $2) }
		| element_name				{ ($1, []) }
		| detail_list_1				{ (Nothing, $1) }

element_name	:: { Maybe String }
		: IDENT					{ Just $1 }
		| ASTERISK				{ Nothing }

detail_list_1	:: { [Detail] }
		: HASH					{ [Id $1] }
		| class					{ [$1] }
		| attrib				{ [$1] }
		| pseudo				{ [$1] }
		| detail_list_1 HASH			{ Id $2 : $1 }
		| detail_list_1 class			{ $2 : $1 }
		| detail_list_1 attrib			{ $2 : $1 }
		| detail_list_1 pseudo			{ $2 : $1 }

class		:: { Detail }
		: DOT IDENT				{ Class $2 }

attrib		:: { Detail }
		: LBRAC IDENT RBRAC			{ Attrib $2 }
		| LBRAC IDENT EQUALS IDENT RBRAC	{ AttribEq $2 $4 }
		| LBRAC IDENT EQUALS STRING RBRAC	{ AttribEq $2 $4 }
		| LBRAC IDENT INCLUDES IDENT RBRAC	{ AttribInc $2 $4 }
		| LBRAC IDENT INCLUDES STRING RBRAC	{ AttribInc $2 $4 }
		| LBRAC IDENT DASHMATCH IDENT RBRAC	{ AttribDM $2 $4 }
		| LBRAC IDENT DASHMATCH STRING RBRAC	{ AttribDM $2 $4 }

pseudo		:: { Detail }
		: COLON IDENT				{ Pseudo $2 }
		| COLON FUNCTION RPAREN			{ PseudoFunc $2 "" }
		| COLON FUNCTION IDENT RPAREN		{ PseudoFunc $2 $3 }

declaration	:: { Declaration }
		: property COLON value_list_1		{ ($1, $3, False) }
		| property COLON value_list_1 prio	{ ($1, $3, True) }

property	:: { String }
		: IDENT					{ $1 }

prio		:: { Bool }
		: IMPORTANT_SYM				{ True }

value_list	:: { [Value] }
		:					{ [] }
		| value value_list			{ $1 : $2 }

value_list_1	:: { [Value] }
		: value					{ [$1] }
		| value value_list_1			{ $1 : $2 }

value		:: { Value }
		: NUMBER				{ Number $1 }
		| PERCENTAGE				{ Percentage $1 }
		| LENGTH				{ Length $1 }
		| EMS					{ Ems $1 }
		| EXS					{ Exs $1 }
		| ANGLE					{ Angle $1 }
		| TIME					{ Time $1 }
		| FREQ					{ Freq $1 }
		| STRING				{ StringV $1 }
		| IDENT					{ Ident $1 }
		| URI					{ Uri $1 }
		| HASH					{ HexColour $1 }
		| SLASH					{ Slash }
		| COMMA					{ Comma }
		| FUNCTION value_list RPAREN		{ Function $1 $2 }

{
happyError :: [TokenPosn] -> a
happyError tks = error ("Parse error at " ++ lcn)
	where
	lcn = 	case tks of
		  [] -> "end of file"
		  tk:_ -> "line " ++ show l ++ ", column " ++ show c
			where
			AlexPn _ l c = snd tk
}
