parser.mly 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. %{
  2. open Lexing
  3. open Types
  4. let filter_none l =
  5. let rec filter l = function
  6. | [] -> []
  7. | None :: tl -> filter l tl
  8. | Some hd :: tl -> filter (hd :: l) tl
  9. in
  10. List.rev (filter [] l)
  11. %}
  12. (* Tokens *)
  13. %token S CDO CDC IMPORT_SYM PAGE_SYM MEDIA_SYM CHARSET_SYM
  14. %token IMPORTANT_SYM
  15. %token <float> NUMBER
  16. %token <float * string> UNIT_VALUE
  17. %token <string> COMBINATOR RELATION STRING IDENT HASH URI FUNCTION
  18. %token RPAREN LBRACE RBRACE LBRACK RBRACK SEMICOL COLON COMMA DOT PLUS MINUS
  19. %token SLASH STAR
  20. (* Start symbol *)
  21. %type <Types.stylesheet> stylesheet
  22. %start stylesheet
  23. %%
  24. %inline mylist(sep, x):
  25. | l=separated_list(sep, delimited(S*, x, S*))
  26. { l }
  27. cd: CDO S* | CDC S* {}
  28. %inline statement: r=ruleset | r=media | r=page { r }
  29. stylesheet:
  30. | charset = charset? S* cd*
  31. imports = terminated(import, cd*)*
  32. statements = terminated(statement, cd*)*
  33. { let charset = match charset with None -> [] | Some c -> [c] in
  34. charset @ imports @ statements }
  35. charset:
  36. | CHARSET_SYM set=STRING SEMICOL
  37. { Charset set }
  38. %inline string_or_uri: s=STRING | s=URI { s }
  39. import:
  40. | IMPORT_SYM S* tgt=string_or_uri media=mylist(COMMA, IDENT) SEMICOL S*
  41. { Import (tgt, media) }
  42. media:
  43. | MEDIA_SYM S* queries=mylist(COMMA, IDENT) LBRACE S* rulesets=ruleset* RBRACE S*
  44. { Media (queries, rulesets) }
  45. page:
  46. | PAGE_SYM S* pseudo=pseudo_page? decls=decls_block
  47. { Page (pseudo, decls) }
  48. pseudo_page:
  49. | COLON pseudo=IDENT S*
  50. { pseudo }
  51. decls_block:
  52. | LBRACE S* decls=mylist(SEMICOL, declaration?) RBRACE S*
  53. { filter_none decls }
  54. ruleset:
  55. | selectors_hd = selector
  56. selectors_tl = separated_list(COMMA, preceded(S*, selector))
  57. decls = decls_block
  58. { Ruleset (selectors_hd :: selectors_tl, decls) }
  59. %inline combinator:
  60. | S* PLUS S* { ["+"] }
  61. | S* c=COMBINATOR S* { [c] }
  62. | S+ { [] }
  63. selector:
  64. | hd=simple_selector comb=combinator tl=selector
  65. { hd :: comb @ tl }
  66. | simple=simple_selector
  67. { [simple] }
  68. simple_selector:
  69. | elem=element_name addons=element_addon*
  70. { elem ^ String.concat "" addons }
  71. | addons=element_addon+
  72. { String.concat "" addons }
  73. element_addon:
  74. | a=HASH | a=cls | a=attrib | a=pseudo
  75. { a }
  76. cls:
  77. | DOT name=IDENT
  78. { "." ^ name }
  79. element_name:
  80. | tag=IDENT { tag }
  81. | STAR { "*" }
  82. %inline rel_value:
  83. | S* id=IDENT S* { id }
  84. | S* s=STRING S* { s }
  85. attrib:
  86. | LBRACK S* left=IDENT S* right=pair(RELATION, rel_value)? RBRACK
  87. { left ^ (match right with None -> "" | Some (rel, term) -> rel ^ term) }
  88. pseudo:
  89. | COLON id=IDENT
  90. { ":" ^ id }
  91. | COLON f=FUNCTION S* arg=terminated(IDENT, S*)? RPAREN
  92. { let arg = match arg with None -> "" | Some id -> id in
  93. ":" ^ f ^ "(" ^ arg ^ ")" }
  94. declaration:
  95. | name=IDENT S* COLON S* value=expr IMPORTANT_SYM S*
  96. { (name, Prio value) }
  97. | name=IDENT S* COLON S* value=expr
  98. { (name, value) }
  99. %inline unary_operator:
  100. | MINUS { "-" }
  101. | PLUS { "+" }
  102. expr:
  103. | left=expr right=expr
  104. { Concat [left; right] }
  105. | left=expr SLASH S* right=expr
  106. { Binop (left, "/", right) }
  107. | op=unary_operator n=NUMBER S*
  108. { Unop (op, Number n) }
  109. | op=unary_operator v=UNIT_VALUE S*
  110. { let (n, u) = v in Unop (op, Unit (n, u)) }
  111. | n=NUMBER S*
  112. { Number n }
  113. | v=UNIT_VALUE S*
  114. { let (n, u) = v in Unit (n, u) }
  115. | str=STRING S*
  116. { Strlit str }
  117. | id=IDENT S*
  118. { Ident id }
  119. | uri=URI S*
  120. { Uri uri }
  121. | fn=FUNCTION S* args=separated_list(COMMA, terminated(expr, S*)) RPAREN S*
  122. { Function (fn, args) }
  123. | hex=HASH S*
  124. { if Str.string_match (Str.regexp "\\d{3}\\d{3}?") hex 0
  125. then Hexcolor hex
  126. else raise (SyntaxError ("invalid color #" ^ hex)) }