Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file css_parser.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318(* Recursive descent parsers. A parser returns false if based on a
single token lookahead it decides that the given text can not be
parsed. Any other parse errors are handled by raising exceptions.
Some parsers return unit because we only call them when a failure
to parse implies a parse error (and not that some parser higher up
in the call chain should try to parse something else).
For simplicity the parsers themselves just validate and don't
produce any values. That leads to a few unelegant constructs
(primarily in declaration), but means we otherwise have rather
simple code that also allocates very little.
*)openCoreopen!Int.Replace_polymorphic_compareletrecnextct=Css_tokenizer.nextct;ifCss_tokenizer.(Token.equal(currentct)Comment)thennextctelse();;letskip_white_spacect=whileCss_tokenizer.(Token.equal(currentct)White_space)donextctdone;;letacceptctexpected=letgot=Css_tokenizer.currentctinifCss_tokenizer.Token.equalgotexpectedthen(nextct;true)elsefalse;;letexpectctexpected=letgot=Css_tokenizer.currentctinifCss_tokenizer.Token.equalgotexpectedthennextctelseraise_s[%message"Unexpected token"(expected:Css_tokenizer.Token.t)(got:Css_tokenizer.Token.t)];;letrecmany(ct:Css_tokenizer.t)f=iffctthenmanyctfelse()letmany1(ct:Css_tokenizer.t)f=iffctthen(manyctf;true)elsefalse;;letrecanyct:bool=letres=matchCss_tokenizer.currentctwith|Ident|Number|Percentage|Dimension|String|Uri|Delim|Hash|Comma->nextct;true|Function->nextct;skip_white_spacect;manyctany;expectctRparen;true|Lparen->nextct;skip_white_spacect;expect_anyct;expectctRparen;true|Lbracket->nextct;skip_white_spacect;expect_anyct;expectctRbracket;true|Rcurly|Rparen|Rbracket->false|Lcurly->false|Atkeyword|Colon|Semi_colon->false|Comment|White_space|Eof|Error->falseinifresthenskip_white_spacectelse();resandexpect_anyct=ifanyctthen()elseraise_s[%message"Expected <any>"]andvalue0ct=anyct||blockct||ifacceptctAtkeywordthen(skip_white_spacect;true)elsefalseandvaluect=many1ctvalue0andblockct:bool=ifacceptctLcurlythen(skip_white_spacect;manyct(funct->value0ct||ifacceptctSemi_colonthen(skip_white_spacect;true)elsefalse);expectctRcurly;skip_white_spacect;true)elsefalseandexpect_valuect=ifvaluectthen()elseraise_s[%message"Expected <value>"]letdeclarationct=letident_start,ident_len=Css_tokenizer.slicectinifacceptctIdentthen(skip_white_spacect;expectctColon;skip_white_spacect;letvalue_start=Css_tokenizer.slicect|>fstinexpect_valuect;letnext_token_start=Css_tokenizer.slicect|>fstinletsource=Css_tokenizer.sourcectinSome(String.subsource~pos:ident_start~len:ident_len,String.rstrip(String.subsource~pos:value_start~len:(next_token_start-value_start))))elseNone;;letexpect_declarationct=matchdeclarationctwith|Some(field,value)->field,value|None->raise_s[%message"Expected <declaration>"];;(* As per: https://www.w3.org/TR/css-style-attr/
declaration-list
: S* declaration? [ ';' S* declaration? ]*
;
*)letexpect_declaration_listct=letres=ref[]inletaddkv=matchkvwith|None->()|Some(k,v)->res:=(k,v)::!resinskip_white_spacect;add(declarationct);manyct(funct->ifacceptctSemi_colonthen(skip_white_spacect;add(declarationct);true)elsefalse);List.rev!res;;letparseparser_fs=letct=Css_tokenizer.createsinwhileCss_tokenizer.(Token.equal(currentct)Comment)doCss_tokenizer.nextctdone;Or_error.try_with(fun()->letres=parser_fctinexpectctEof;res);;letprint_tokenss=letct=Css_tokenizer.createsinwhileCss_tokenizer.(not(Token.equal(currentct)Eof))doprint_s(Css_tokenizer.Token.sexp_of_t(Css_tokenizer.currentct));Css_tokenizer.nextctdone;;letvalidate_value=parseexpect_valueletparse_declaration_lists=parseexpect_declaration_listslettest_parserpsexp_of_args=letr=parsepsinprintf!"%s --> %{sexp:arg Or_error.t}\n"sr;;let%test_module"tests"=(modulestructlet%expect_test""=letvalue="0 4px 8px 0 RGBA(var(--js-text-color-rgb), 0.12), 0 2px 4px 0 \
RGBA(var(--js-text-color-rgb), 0.08)"inprint_tokensvalue;[%expect{|
Number
White_space
Dimension
White_space
Dimension
White_space
Number
White_space
Function
Function
Ident
Rparen
Comma
White_space
Number
Rparen
Comma
White_space
Number
White_space
Dimension
White_space
Dimension
White_space
Number
White_space
Function
Function
Ident
Rparen
Comma
White_space
Number
Rparen |}];print_s[%message(validate_valuevalue:unitOr_error.t)];[%expect{|
("validate_value value" (Ok ())) |}];;let%expect_test"values"=lettest=test_parserexpect_valueUnit.sexp_of_tintest"x";test"3";test"3in";test"3%";test"#fff";test"1 0 auto";test"'Hello World'";test"rgb(0,0,0)";[%expect{|
x --> (Ok ())
3 --> (Ok ())
3in --> (Ok ())
3% --> (Ok ())
#fff --> (Ok ())
1 0 auto --> (Ok ())
'Hello World' --> (Ok ())
rgb(0,0,0) --> (Ok ()) |}];;let%expect_test"declaration"=lettest=test_parserexpect_declaration[%sexp_of:string*string]intest"flex: 1 0 auto";test"content: 'Hello World'";test"content: foo;";(* Semi's are handled in declaration list *)test"content: bar ";(* but whitespace is handled in declaration (any really) *)[%expect{|
flex: 1 0 auto --> (Ok (flex "1 0 auto"))
content: 'Hello World' --> (Ok (content "'Hello World'"))
content: foo; --> (Error ("Unexpected token" (expected Eof) (got Semi_colon)))
content: bar --> (Ok (content bar)) |}];;let%expect_test"unicode"=lettest=test_parserexpect_declaration[%sexp_of:string*string]intest"content: '← ↑ → ↓ ↔ ↕ ⇪ ↹ ⬈ ↘ ⟾ ↶'";print_endline(Sexp.to_string(Sexp.Atom"← ↑ → ↓ ↔ ↕ ⇪ ↹ ⬈ ↘ ⟾ ↶"));[%expect{|
content: '← ↑ → ↓ ↔ ↕ ⇪ ↹ ⬈ ↘ ⟾ ↶' --> (Ok
(content
"'\226\134\144 \226\134\145 \226\134\146 \226\134\147 \226\134\148 \226\134\149 \226\135\170 \226\134\185 \226\172\136 \226\134\152 \226\159\190 \226\134\182'"))
"\226\134\144 \226\134\145 \226\134\146 \226\134\147 \226\134\148 \226\134\149 \226\135\170 \226\134\185 \226\172\136 \226\134\152 \226\159\190 \226\134\182" |}];;let%expect_test"declaration list"=lettest=test_parserexpect_declaration_list[%sexp_of:(string*string)list]intest"flex: 1 0 auto";test"flex: 1 0 auto;";test"background: #5d9ab2 url(\"img_tree.png\") no-repeat top left;margin-left: 200px";test";;;;;";test"flex: 1 0 auto ;; other : sa ";[%expect{|
flex: 1 0 auto --> (Ok ((flex "1 0 auto")))
flex: 1 0 auto; --> (Ok ((flex "1 0 auto")))
background: #5d9ab2 url("img_tree.png") no-repeat top left;margin-left: 200px --> (Ok
((background "#5d9ab2 url(\"img_tree.png\") no-repeat top left")
(margin-left 200px)))
;;;;; --> (Ok ())
flex: 1 0 auto ;; other : sa --> (Ok ((flex "1 0 auto") (other sa))) |}];;end);;