1*37da2899SCharles.Forsythimplement CSS; 2*37da2899SCharles.Forsyth 3*37da2899SCharles.Forsyth# 4*37da2899SCharles.Forsyth# CSS2 parsing module 5*37da2899SCharles.Forsyth# 6*37da2899SCharles.Forsyth# CSS2.1 style sheets 7*37da2899SCharles.Forsyth# 8*37da2899SCharles.Forsyth# Copyright © 2001, 2005 Vita Nuova Holdings Limited. All rights reserved. 9*37da2899SCharles.Forsyth# 10*37da2899SCharles.Forsyth 11*37da2899SCharles.Forsythinclude "sys.m"; 12*37da2899SCharles.Forsyth sys: Sys; 13*37da2899SCharles.Forsyth 14*37da2899SCharles.Forsythinclude "css.m"; 15*37da2899SCharles.Forsyth 16*37da2899SCharles.ForsythB, NUMBER, IDENT, STRING, URL, PERCENTAGE, UNIT, 17*37da2899SCharles.Forsyth HASH, ATKEYWORD, IMPORTANT, IMPORT, PSEUDO, CLASS, INCLUDES, 18*37da2899SCharles.Forsyth DASHMATCH, FUNCTION: con 16rE000+iota; 19*37da2899SCharles.Forsyth 20*37da2899SCharles.Forsythtoknames := array[] of{ 21*37da2899SCharles.Forsyth B-B => "Zero", 22*37da2899SCharles.Forsyth NUMBER-B => "NUMBER", 23*37da2899SCharles.Forsyth IDENT-B => "IDENT", 24*37da2899SCharles.Forsyth STRING-B => "STRING", 25*37da2899SCharles.Forsyth URL-B => "URL", 26*37da2899SCharles.Forsyth PERCENTAGE-B => "PERCENTAGE", 27*37da2899SCharles.Forsyth UNIT-B => "UNIT", 28*37da2899SCharles.Forsyth HASH-B => "HASH", 29*37da2899SCharles.Forsyth ATKEYWORD-B => "ATKEYWORD", 30*37da2899SCharles.Forsyth IMPORTANT-B => "IMPORTANT", 31*37da2899SCharles.Forsyth CLASS-B => "CLASS", 32*37da2899SCharles.Forsyth INCLUDES-B => "INCLUDES", 33*37da2899SCharles.Forsyth DASHMATCH-B => "DASHMATCH", 34*37da2899SCharles.Forsyth PSEUDO-B => "PSEUDO", 35*37da2899SCharles.Forsyth FUNCTION-B => "FUNCTION", 36*37da2899SCharles.Forsyth}; 37*37da2899SCharles.Forsyth 38*37da2899SCharles.Forsythprintdiag := 0; 39*37da2899SCharles.Forsyth 40*37da2899SCharles.Forsythinit(d: int) 41*37da2899SCharles.Forsyth{ 42*37da2899SCharles.Forsyth sys = load Sys Sys->PATH; 43*37da2899SCharles.Forsyth printdiag = d; 44*37da2899SCharles.Forsyth} 45*37da2899SCharles.Forsyth 46*37da2899SCharles.Forsythparse(s: string): (ref Stylesheet, string) 47*37da2899SCharles.Forsyth{ 48*37da2899SCharles.Forsyth return stylesheet(ref Cparse(-1, 0, nil, nil, Clex.new(s,1))); 49*37da2899SCharles.Forsyth} 50*37da2899SCharles.Forsyth 51*37da2899SCharles.Forsythparsedecl(s: string): (list of ref Decl, string) 52*37da2899SCharles.Forsyth{ 53*37da2899SCharles.Forsyth return (declarations(ref Cparse(-1, 0, nil, nil, Clex.new(s,0))), nil); 54*37da2899SCharles.Forsyth} 55*37da2899SCharles.Forsyth 56*37da2899SCharles.Forsythptok(c: int): string 57*37da2899SCharles.Forsyth{ 58*37da2899SCharles.Forsyth if(c < 0) 59*37da2899SCharles.Forsyth return "eof"; 60*37da2899SCharles.Forsyth if(c == 0) 61*37da2899SCharles.Forsyth return "zero?"; 62*37da2899SCharles.Forsyth if(c >= B) 63*37da2899SCharles.Forsyth return sys->sprint("%s", toknames[c-B]); 64*37da2899SCharles.Forsyth return sys->sprint("%c", c); 65*37da2899SCharles.Forsyth} 66*37da2899SCharles.Forsyth 67*37da2899SCharles.ForsythCparse: adt { 68*37da2899SCharles.Forsyth lookahead: int; 69*37da2899SCharles.Forsyth eof: int; 70*37da2899SCharles.Forsyth value: string; 71*37da2899SCharles.Forsyth suffix: string; 72*37da2899SCharles.Forsyth cs: ref Clex; 73*37da2899SCharles.Forsyth 74*37da2899SCharles.Forsyth get: fn(nil: self ref Cparse): int; 75*37da2899SCharles.Forsyth look: fn(nil: self ref Cparse): int; 76*37da2899SCharles.Forsyth unget: fn(nil: self ref Cparse, tok: int); 77*37da2899SCharles.Forsyth skipto: fn(nil: self ref Cparse, followset: string): int; 78*37da2899SCharles.Forsyth synerr: fn(nil: self ref Cparse, s: string); 79*37da2899SCharles.Forsyth}; 80*37da2899SCharles.Forsyth 81*37da2899SCharles.ForsythCparse.get(p: self ref Cparse): int 82*37da2899SCharles.Forsyth{ 83*37da2899SCharles.Forsyth if((c := p.lookahead) >= 0){ 84*37da2899SCharles.Forsyth p.lookahead = -1; 85*37da2899SCharles.Forsyth return c; 86*37da2899SCharles.Forsyth } 87*37da2899SCharles.Forsyth if(p.eof) 88*37da2899SCharles.Forsyth return -1; 89*37da2899SCharles.Forsyth (c, p.value, p.suffix) = csslex(p.cs); 90*37da2899SCharles.Forsyth if(c < 0) 91*37da2899SCharles.Forsyth p.eof = 1; 92*37da2899SCharles.Forsyth if(printdiag > 1) 93*37da2899SCharles.Forsyth sys->print("lex: %s v=%s s=%s\n", ptok(c), p.value, p.suffix); 94*37da2899SCharles.Forsyth return c; 95*37da2899SCharles.Forsyth} 96*37da2899SCharles.Forsyth 97*37da2899SCharles.ForsythCparse.look(p: self ref Cparse): int 98*37da2899SCharles.Forsyth{ 99*37da2899SCharles.Forsyth c := p.get(); 100*37da2899SCharles.Forsyth p.unget(c); 101*37da2899SCharles.Forsyth return c; 102*37da2899SCharles.Forsyth} 103*37da2899SCharles.Forsyth 104*37da2899SCharles.ForsythCparse.unget(p: self ref Cparse, c: int) 105*37da2899SCharles.Forsyth{ 106*37da2899SCharles.Forsyth if(p.lookahead >= 0) 107*37da2899SCharles.Forsyth raise "css: internal error: Cparse.unget"; 108*37da2899SCharles.Forsyth p.lookahead = c; # note that p.value and p.suffix are assumed to be those of c 109*37da2899SCharles.Forsyth} 110*37da2899SCharles.Forsyth 111*37da2899SCharles.ForsythCparse.skipto(p: self ref Cparse, followset: string): int 112*37da2899SCharles.Forsyth{ 113*37da2899SCharles.Forsyth while((c := p.get()) >= 0) 114*37da2899SCharles.Forsyth for(i := 0; i < len followset; i++) 115*37da2899SCharles.Forsyth if(followset[i] == c){ 116*37da2899SCharles.Forsyth p.unget(c); 117*37da2899SCharles.Forsyth return c; 118*37da2899SCharles.Forsyth } 119*37da2899SCharles.Forsyth return -1; 120*37da2899SCharles.Forsyth} 121*37da2899SCharles.Forsyth 122*37da2899SCharles.ForsythCparse.synerr(p: self ref Cparse, s: string) 123*37da2899SCharles.Forsyth{ 124*37da2899SCharles.Forsyth p.cs.synerr(s); 125*37da2899SCharles.Forsyth} 126*37da2899SCharles.Forsyth 127*37da2899SCharles.Forsyth# 128*37da2899SCharles.Forsyth# stylesheet: 129*37da2899SCharles.Forsyth# ["@charset" STRING ';']? 130*37da2899SCharles.Forsyth# [CDO|CDC]* [import [CDO|CDC]*]* 131*37da2899SCharles.Forsyth# [[ruleset | media | page ] [CDO|CDC]*]* 132*37da2899SCharles.Forsyth# import: 133*37da2899SCharles.Forsyth# "@import" [STRING|URL] [ medium [',' medium]*]? ';' 134*37da2899SCharles.Forsyth# media: 135*37da2899SCharles.Forsyth# "@media" medium [',' medium]* '{' ruleset* '}' 136*37da2899SCharles.Forsyth# medium: 137*37da2899SCharles.Forsyth# IDENT 138*37da2899SCharles.Forsyth# page: 139*37da2899SCharles.Forsyth# "@page" pseudo_page? '{' declaration [';' declaration]* '}' 140*37da2899SCharles.Forsyth# pseudo_page: 141*37da2899SCharles.Forsyth# ':' IDENT 142*37da2899SCharles.Forsyth# 143*37da2899SCharles.Forsyth 144*37da2899SCharles.Forsythstylesheet(p: ref Cparse): (ref Stylesheet, string) 145*37da2899SCharles.Forsyth{ 146*37da2899SCharles.Forsyth charset: string; 147*37da2899SCharles.Forsyth if(atkeywd(p, "@charset")){ 148*37da2899SCharles.Forsyth if(itisa(p, STRING)){ 149*37da2899SCharles.Forsyth charset = p.value; 150*37da2899SCharles.Forsyth itisa(p, ';'); 151*37da2899SCharles.Forsyth }else 152*37da2899SCharles.Forsyth p.synerr("bad @charset declaration"); 153*37da2899SCharles.Forsyth } 154*37da2899SCharles.Forsyth imports: list of ref Import; 155*37da2899SCharles.Forsyth while(atkeywd(p, "@import")){ 156*37da2899SCharles.Forsyth c := p.get(); 157*37da2899SCharles.Forsyth if(c == STRING || c == URL){ 158*37da2899SCharles.Forsyth name := p.value; 159*37da2899SCharles.Forsyth media: list of string; 160*37da2899SCharles.Forsyth c = p.get(); 161*37da2899SCharles.Forsyth if(c == IDENT){ # optional medium [, ...] 162*37da2899SCharles.Forsyth p.unget(c); 163*37da2899SCharles.Forsyth media = medialist(p); 164*37da2899SCharles.Forsyth } 165*37da2899SCharles.Forsyth imports = ref Import(name, media) :: imports; 166*37da2899SCharles.Forsyth }else 167*37da2899SCharles.Forsyth p.synerr("bad @import"); 168*37da2899SCharles.Forsyth if(c != ';'){ 169*37da2899SCharles.Forsyth p.synerr("missing ; in @import"); 170*37da2899SCharles.Forsyth p.unget(c); 171*37da2899SCharles.Forsyth if(p.skipto(";}") < 0) 172*37da2899SCharles.Forsyth break; 173*37da2899SCharles.Forsyth } 174*37da2899SCharles.Forsyth } 175*37da2899SCharles.Forsyth imports = rev(imports); 176*37da2899SCharles.Forsyth 177*37da2899SCharles.Forsyth stmts: list of ref Statement; 178*37da2899SCharles.Forsyth do{ 179*37da2899SCharles.Forsyth while((c := p.get()) == ATKEYWORD) 180*37da2899SCharles.Forsyth case p.value { 181*37da2899SCharles.Forsyth "@media" => # medium[,medium]* { ruleset*} 182*37da2899SCharles.Forsyth media := medialist(p); 183*37da2899SCharles.Forsyth if(!itisa(p, '{')){ 184*37da2899SCharles.Forsyth p.synerr("bad @media"); 185*37da2899SCharles.Forsyth skipatrule("@media", p); 186*37da2899SCharles.Forsyth continue; 187*37da2899SCharles.Forsyth } 188*37da2899SCharles.Forsyth rules: list of ref Statement.Ruleset; 189*37da2899SCharles.Forsyth do{ 190*37da2899SCharles.Forsyth rule := checkrule(p); 191*37da2899SCharles.Forsyth if(rule != nil) 192*37da2899SCharles.Forsyth rules = rule :: rules; 193*37da2899SCharles.Forsyth }while(!itisa(p, '}') && !p.eof); 194*37da2899SCharles.Forsyth stmts = ref Statement.Media(media, rev(rules)) :: stmts; 195*37da2899SCharles.Forsyth "@page" => # [:ident]? { declaration [; declaration]* } 196*37da2899SCharles.Forsyth pseudo: string; 197*37da2899SCharles.Forsyth if(itisa(p, PSEUDO)) 198*37da2899SCharles.Forsyth pseudo = p.value; 199*37da2899SCharles.Forsyth if(!itisa(p, '{')){ 200*37da2899SCharles.Forsyth p.synerr("bad @page"); 201*37da2899SCharles.Forsyth skipatrule("@page", p); 202*37da2899SCharles.Forsyth continue; 203*37da2899SCharles.Forsyth } 204*37da2899SCharles.Forsyth decls := declarations(p); 205*37da2899SCharles.Forsyth if(!itisa(p, '}')){ 206*37da2899SCharles.Forsyth p.synerr("unclosed @page declaration block"); 207*37da2899SCharles.Forsyth skipatrule("@page", p); 208*37da2899SCharles.Forsyth continue; 209*37da2899SCharles.Forsyth } 210*37da2899SCharles.Forsyth stmts = ref Statement.Page(pseudo, decls) :: stmts; 211*37da2899SCharles.Forsyth * => 212*37da2899SCharles.Forsyth skipatrule(p.value, p); # skip unknown or misplaced at-rule 213*37da2899SCharles.Forsyth } 214*37da2899SCharles.Forsyth p.unget(c); 215*37da2899SCharles.Forsyth rule := checkrule(p); 216*37da2899SCharles.Forsyth if(rule != nil) 217*37da2899SCharles.Forsyth stmts = rule :: stmts; 218*37da2899SCharles.Forsyth }while(!p.eof); 219*37da2899SCharles.Forsyth rl := stmts; 220*37da2899SCharles.Forsyth stmts = nil; 221*37da2899SCharles.Forsyth for(; rl != nil; rl = tl rl) 222*37da2899SCharles.Forsyth stmts = hd rl :: stmts; 223*37da2899SCharles.Forsyth return (ref Stylesheet(charset, imports, stmts), nil); 224*37da2899SCharles.Forsyth} 225*37da2899SCharles.Forsyth 226*37da2899SCharles.Forsythcheckrule(p: ref Cparse): ref Statement.Ruleset 227*37da2899SCharles.Forsyth{ 228*37da2899SCharles.Forsyth (rule, err) := ruleset(p); 229*37da2899SCharles.Forsyth if(rule == nil){ 230*37da2899SCharles.Forsyth if(err != nil){ 231*37da2899SCharles.Forsyth p.synerr(sys->sprint("bad ruleset: %s", err)); 232*37da2899SCharles.Forsyth p.get(); # make some progress 233*37da2899SCharles.Forsyth } 234*37da2899SCharles.Forsyth } 235*37da2899SCharles.Forsyth return rule; 236*37da2899SCharles.Forsyth} 237*37da2899SCharles.Forsyth 238*37da2899SCharles.Forsythmedialist(p: ref Cparse): list of string 239*37da2899SCharles.Forsyth{ 240*37da2899SCharles.Forsyth media: list of string; 241*37da2899SCharles.Forsyth do{ 242*37da2899SCharles.Forsyth c := p.get(); 243*37da2899SCharles.Forsyth if(c != IDENT){ 244*37da2899SCharles.Forsyth p.unget(c); 245*37da2899SCharles.Forsyth p.synerr("missing medium identifier"); 246*37da2899SCharles.Forsyth break; 247*37da2899SCharles.Forsyth } 248*37da2899SCharles.Forsyth media = p.value :: media; 249*37da2899SCharles.Forsyth }while(itisa(p, ',')); 250*37da2899SCharles.Forsyth return rev(media); 251*37da2899SCharles.Forsyth} 252*37da2899SCharles.Forsyth 253*37da2899SCharles.Forsythitisa(p: ref Cparse, expect: int): int 254*37da2899SCharles.Forsyth{ 255*37da2899SCharles.Forsyth if((c := p.get()) == expect) 256*37da2899SCharles.Forsyth return 1; 257*37da2899SCharles.Forsyth p.unget(c); 258*37da2899SCharles.Forsyth return 0; 259*37da2899SCharles.Forsyth} 260*37da2899SCharles.Forsyth 261*37da2899SCharles.Forsythatkeywd(p: ref Cparse, expect: string): int 262*37da2899SCharles.Forsyth{ 263*37da2899SCharles.Forsyth if((c := p.get()) == ATKEYWORD && p.value == expect) 264*37da2899SCharles.Forsyth return 1; 265*37da2899SCharles.Forsyth p.unget(c); 266*37da2899SCharles.Forsyth return 0; 267*37da2899SCharles.Forsyth} 268*37da2899SCharles.Forsyth 269*37da2899SCharles.Forsythskipatrule(name: string, p: ref Cparse) 270*37da2899SCharles.Forsyth{ 271*37da2899SCharles.Forsyth if(printdiag) 272*37da2899SCharles.Forsyth sys->print("skip unimplemented or misplaced %s\n", name); 273*37da2899SCharles.Forsyth if((c := p.get()) == '{'){ # block 274*37da2899SCharles.Forsyth for(nesting := '}' :: nil; nesting != nil && c >= 0; nesting = tl nesting){ 275*37da2899SCharles.Forsyth while((c = p.cs.getc()) >= 0 && c != hd nesting) 276*37da2899SCharles.Forsyth case c { 277*37da2899SCharles.Forsyth '{' => 278*37da2899SCharles.Forsyth nesting = '}' :: nesting; 279*37da2899SCharles.Forsyth '(' => 280*37da2899SCharles.Forsyth nesting = ')' :: nesting; 281*37da2899SCharles.Forsyth '[' => 282*37da2899SCharles.Forsyth nesting = ']' :: nesting; 283*37da2899SCharles.Forsyth '"' or '\'' => 284*37da2899SCharles.Forsyth quotedstring(p.cs, c); 285*37da2899SCharles.Forsyth } 286*37da2899SCharles.Forsyth } 287*37da2899SCharles.Forsyth }else{ 288*37da2899SCharles.Forsyth while(c >= 0 && c != ';') 289*37da2899SCharles.Forsyth c = p.get(); 290*37da2899SCharles.Forsyth } 291*37da2899SCharles.Forsyth} 292*37da2899SCharles.Forsyth 293*37da2899SCharles.Forsyth# ruleset: 294*37da2899SCharles.Forsyth# selector [',' S* selector]* '{' S* declaration [';' S* declaration]* '}' S* 295*37da2899SCharles.Forsyth 296*37da2899SCharles.Forsythruleset(p: ref Cparse): (ref Statement.Ruleset, string) 297*37da2899SCharles.Forsyth{ 298*37da2899SCharles.Forsyth selectors: list of list of (int, list of ref Select); 299*37da2899SCharles.Forsyth c := -1; 300*37da2899SCharles.Forsyth do{ 301*37da2899SCharles.Forsyth s := selector(p); 302*37da2899SCharles.Forsyth if(s == nil){ 303*37da2899SCharles.Forsyth if(p.eof) 304*37da2899SCharles.Forsyth return (nil, nil); 305*37da2899SCharles.Forsyth p.synerr("expected selector"); 306*37da2899SCharles.Forsyth if(p.skipto(",{}") < 0) 307*37da2899SCharles.Forsyth return (nil, nil); 308*37da2899SCharles.Forsyth c = p.look(); 309*37da2899SCharles.Forsyth }else 310*37da2899SCharles.Forsyth selectors = s :: selectors; 311*37da2899SCharles.Forsyth }while((c = p.get()) == ','); 312*37da2899SCharles.Forsyth if(c != '{') 313*37da2899SCharles.Forsyth return (nil, "expected declaration block"); 314*37da2899SCharles.Forsyth sl := selectors; 315*37da2899SCharles.Forsyth selectors = nil; 316*37da2899SCharles.Forsyth for(; sl != nil; sl = tl sl) 317*37da2899SCharles.Forsyth selectors = hd sl :: selectors; 318*37da2899SCharles.Forsyth decls := declarations(p); 319*37da2899SCharles.Forsyth if(!itisa(p, '}')){ 320*37da2899SCharles.Forsyth p.synerr("unclosed declaration block"); 321*37da2899SCharles.Forsyth } 322*37da2899SCharles.Forsyth return (ref Statement.Ruleset(selectors, decls), nil); 323*37da2899SCharles.Forsyth} 324*37da2899SCharles.Forsyth 325*37da2899SCharles.Forsythdeclarations(p: ref Cparse): list of ref Decl 326*37da2899SCharles.Forsyth{ 327*37da2899SCharles.Forsyth decls: list of ref Decl; 328*37da2899SCharles.Forsyth c: int; 329*37da2899SCharles.Forsyth do{ 330*37da2899SCharles.Forsyth (d, e) := declaration(p); 331*37da2899SCharles.Forsyth if(d != nil) 332*37da2899SCharles.Forsyth decls = d :: decls; 333*37da2899SCharles.Forsyth else if(e != nil){ 334*37da2899SCharles.Forsyth p.synerr("ruleset declaration: "+e); 335*37da2899SCharles.Forsyth if((c = p.skipto(";}")) < 0) 336*37da2899SCharles.Forsyth break; 337*37da2899SCharles.Forsyth } 338*37da2899SCharles.Forsyth }while((c = p.get()) == ';'); 339*37da2899SCharles.Forsyth p.unget(c); 340*37da2899SCharles.Forsyth l := decls; 341*37da2899SCharles.Forsyth for(decls = nil; l != nil; l = tl l) 342*37da2899SCharles.Forsyth decls = hd l :: decls; 343*37da2899SCharles.Forsyth return decls; 344*37da2899SCharles.Forsyth} 345*37da2899SCharles.Forsyth 346*37da2899SCharles.Forsyth# selector: 347*37da2899SCharles.Forsyth# simple_selector [combinator simple_selector]* 348*37da2899SCharles.Forsyth# combinator: 349*37da2899SCharles.Forsyth# '+' S* | '>' S* | /* empty */ 350*37da2899SCharles.Forsyth# 351*37da2899SCharles.Forsyth 352*37da2899SCharles.Forsythselector(p: ref Cparse): list of (int, list of ref Select) 353*37da2899SCharles.Forsyth{ 354*37da2899SCharles.Forsyth sel: list of (int, list of ref Select); 355*37da2899SCharles.Forsyth op := ' '; 356*37da2899SCharles.Forsyth while((s := selector1(p)) != nil){ 357*37da2899SCharles.Forsyth sel = (op, s) :: sel; 358*37da2899SCharles.Forsyth if((c := p.look()) == '+' || c == '>') 359*37da2899SCharles.Forsyth op = p.get(); 360*37da2899SCharles.Forsyth else 361*37da2899SCharles.Forsyth op = ' '; 362*37da2899SCharles.Forsyth } 363*37da2899SCharles.Forsyth l: list of (int, list of ref Select); 364*37da2899SCharles.Forsyth for(; sel != nil; sel = tl sel) 365*37da2899SCharles.Forsyth l = hd sel :: l; 366*37da2899SCharles.Forsyth return l; 367*37da2899SCharles.Forsyth} 368*37da2899SCharles.Forsyth 369*37da2899SCharles.Forsyth# 370*37da2899SCharles.Forsyth# simple_selector: 371*37da2899SCharles.Forsyth# element_name? [HASH | class | attrib | pseudo]* S* 372*37da2899SCharles.Forsyth# element_name: 373*37da2899SCharles.Forsyth# IDENT | '*' 374*37da2899SCharles.Forsyth# class: 375*37da2899SCharles.Forsyth# '.' IDENT 376*37da2899SCharles.Forsyth# attrib: 377*37da2899SCharles.Forsyth# '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* [IDENT | STRING] S* ]? ']' 378*37da2899SCharles.Forsyth# pseudo 379*37da2899SCharles.Forsyth# ':' [ IDENT | FUNCTION S* IDENT? S* ')' ] 380*37da2899SCharles.Forsyth 381*37da2899SCharles.Forsythselector1(p: ref Cparse): list of ref Select 382*37da2899SCharles.Forsyth{ 383*37da2899SCharles.Forsyth sel: list of ref Select; 384*37da2899SCharles.Forsyth c := p.get(); 385*37da2899SCharles.Forsyth if(c == IDENT) 386*37da2899SCharles.Forsyth sel = ref Select.Element(p.value) :: sel; 387*37da2899SCharles.Forsyth else if(c== '*') 388*37da2899SCharles.Forsyth sel = ref Select.Any("*") :: sel; 389*37da2899SCharles.Forsyth else 390*37da2899SCharles.Forsyth p.unget(c); 391*37da2899SCharles.ForsythSel: 392*37da2899SCharles.Forsyth for(;;){ 393*37da2899SCharles.Forsyth c = p.get(); 394*37da2899SCharles.Forsyth case c { 395*37da2899SCharles.Forsyth HASH => 396*37da2899SCharles.Forsyth sel = ref Select.ID(p.value) :: sel; 397*37da2899SCharles.Forsyth CLASS => 398*37da2899SCharles.Forsyth sel = ref Select.Class(p.value) :: sel; 399*37da2899SCharles.Forsyth '[' => 400*37da2899SCharles.Forsyth if(!itisa(p, IDENT)) 401*37da2899SCharles.Forsyth break; 402*37da2899SCharles.Forsyth name := p.value; 403*37da2899SCharles.Forsyth case c = p.get() { 404*37da2899SCharles.Forsyth '=' => 405*37da2899SCharles.Forsyth sel = ref Select.Attrib(name, "=", optaval(p)) :: sel; 406*37da2899SCharles.Forsyth INCLUDES => 407*37da2899SCharles.Forsyth sel = ref Select.Attrib(name, "~=", optaval(p)) :: sel; 408*37da2899SCharles.Forsyth DASHMATCH => 409*37da2899SCharles.Forsyth sel = ref Select.Attrib(name, "|=", optaval(p)) :: sel; 410*37da2899SCharles.Forsyth * => 411*37da2899SCharles.Forsyth sel = ref Select.Attrib(name, nil, nil) :: sel; 412*37da2899SCharles.Forsyth p.unget(c); 413*37da2899SCharles.Forsyth } 414*37da2899SCharles.Forsyth if((c = p.get()) != ']'){ 415*37da2899SCharles.Forsyth p.synerr("bad attribute syntax"); 416*37da2899SCharles.Forsyth p.unget(c); 417*37da2899SCharles.Forsyth break Sel; 418*37da2899SCharles.Forsyth } 419*37da2899SCharles.Forsyth PSEUDO => 420*37da2899SCharles.Forsyth case c = p.get() { 421*37da2899SCharles.Forsyth IDENT => 422*37da2899SCharles.Forsyth sel = ref Select.Pseudo(p.value) :: sel; 423*37da2899SCharles.Forsyth FUNCTION => 424*37da2899SCharles.Forsyth name := p.value; 425*37da2899SCharles.Forsyth case c = p.get() { 426*37da2899SCharles.Forsyth IDENT => 427*37da2899SCharles.Forsyth sel = ref Select.Pseudofn(name, lowercase(p.value)) :: sel; 428*37da2899SCharles.Forsyth ')' => 429*37da2899SCharles.Forsyth p.unget(c); 430*37da2899SCharles.Forsyth sel = ref Select.Pseudofn(name, nil) :: sel; 431*37da2899SCharles.Forsyth * => 432*37da2899SCharles.Forsyth p.synerr("bad pseudo-function syntax"); 433*37da2899SCharles.Forsyth p.unget(c); 434*37da2899SCharles.Forsyth break Sel; 435*37da2899SCharles.Forsyth } 436*37da2899SCharles.Forsyth if((c = p.get()) != ')'){ 437*37da2899SCharles.Forsyth p.synerr("missing ')' for pseudo-function"); 438*37da2899SCharles.Forsyth p.unget(c); 439*37da2899SCharles.Forsyth break Sel; 440*37da2899SCharles.Forsyth } 441*37da2899SCharles.Forsyth * => 442*37da2899SCharles.Forsyth p.synerr(sys->sprint("unexpected :pseudo: %s:%s", ptok(c), p.value)); 443*37da2899SCharles.Forsyth p.unget(c); 444*37da2899SCharles.Forsyth break Sel; 445*37da2899SCharles.Forsyth } 446*37da2899SCharles.Forsyth * => 447*37da2899SCharles.Forsyth p.unget(c); 448*37da2899SCharles.Forsyth break Sel; 449*37da2899SCharles.Forsyth } 450*37da2899SCharles.Forsyth # qualifiers must be adjacent to the first item, and each other 451*37da2899SCharles.Forsyth c = p.cs.getc(); 452*37da2899SCharles.Forsyth p.cs.ungetc(c); 453*37da2899SCharles.Forsyth if(isspace(c)) 454*37da2899SCharles.Forsyth break; 455*37da2899SCharles.Forsyth } 456*37da2899SCharles.Forsyth sl := sel; 457*37da2899SCharles.Forsyth for(sel = nil; sl != nil; sl = tl sl) 458*37da2899SCharles.Forsyth sel = hd sl :: sel; 459*37da2899SCharles.Forsyth return sel; 460*37da2899SCharles.Forsyth} 461*37da2899SCharles.Forsyth 462*37da2899SCharles.Forsythoptaval(p: ref Cparse): ref Value 463*37da2899SCharles.Forsyth{ 464*37da2899SCharles.Forsyth case c := p.get() { 465*37da2899SCharles.Forsyth IDENT => 466*37da2899SCharles.Forsyth return ref Value.Ident(' ', p.value); 467*37da2899SCharles.Forsyth STRING => 468*37da2899SCharles.Forsyth return ref Value.String(' ', p.value); 469*37da2899SCharles.Forsyth * => 470*37da2899SCharles.Forsyth p.unget(c); 471*37da2899SCharles.Forsyth return nil; 472*37da2899SCharles.Forsyth } 473*37da2899SCharles.Forsyth} 474*37da2899SCharles.Forsyth 475*37da2899SCharles.Forsyth# declaration: 476*37da2899SCharles.Forsyth# property ':' S* expr prio? 477*37da2899SCharles.Forsyth# | /* empty */ 478*37da2899SCharles.Forsyth# property: 479*37da2899SCharles.Forsyth# IDENT 480*37da2899SCharles.Forsyth# prio: 481*37da2899SCharles.Forsyth# IMPORTANT S* /* ! important */ 482*37da2899SCharles.Forsyth 483*37da2899SCharles.Forsythdeclaration(p: ref Cparse): (ref Decl, string) 484*37da2899SCharles.Forsyth{ 485*37da2899SCharles.Forsyth c := p.get(); 486*37da2899SCharles.Forsyth if(c != IDENT){ 487*37da2899SCharles.Forsyth p.unget(c); 488*37da2899SCharles.Forsyth return (nil, nil); 489*37da2899SCharles.Forsyth } 490*37da2899SCharles.Forsyth prop := lowercase(p.value); 491*37da2899SCharles.Forsyth c = p.get(); 492*37da2899SCharles.Forsyth if(c != ':'){ 493*37da2899SCharles.Forsyth p.unget(c); 494*37da2899SCharles.Forsyth return (nil, "missing :"); 495*37da2899SCharles.Forsyth } 496*37da2899SCharles.Forsyth values := expr(p); 497*37da2899SCharles.Forsyth if(values == nil) 498*37da2899SCharles.Forsyth return (nil, "missing expression(s)"); 499*37da2899SCharles.Forsyth prio := 0; 500*37da2899SCharles.Forsyth if(p.look() == IMPORTANT){ 501*37da2899SCharles.Forsyth p.get(); 502*37da2899SCharles.Forsyth prio = 1; 503*37da2899SCharles.Forsyth } 504*37da2899SCharles.Forsyth return (ref Decl(prop, values, prio), nil); 505*37da2899SCharles.Forsyth} 506*37da2899SCharles.Forsyth 507*37da2899SCharles.Forsyth# expr: 508*37da2899SCharles.Forsyth# term [operator term]* 509*37da2899SCharles.Forsyth# operator: 510*37da2899SCharles.Forsyth# '/' | ',' | /* empty */ 511*37da2899SCharles.Forsyth 512*37da2899SCharles.Forsythexpr(p: ref Cparse): list of ref Value 513*37da2899SCharles.Forsyth{ 514*37da2899SCharles.Forsyth values: list of ref Value; 515*37da2899SCharles.Forsyth sep := ' '; 516*37da2899SCharles.Forsyth while((t := term(p, sep)) != nil){ 517*37da2899SCharles.Forsyth values = t :: values; 518*37da2899SCharles.Forsyth if((c := p.look()) == '/' || c == ',') 519*37da2899SCharles.Forsyth sep = p.get(); # need something fancier here? 520*37da2899SCharles.Forsyth else 521*37da2899SCharles.Forsyth sep = ' '; 522*37da2899SCharles.Forsyth } 523*37da2899SCharles.Forsyth vl := values; 524*37da2899SCharles.Forsyth for(values = nil; vl != nil; vl = tl vl) 525*37da2899SCharles.Forsyth values = hd vl :: values; 526*37da2899SCharles.Forsyth return values; 527*37da2899SCharles.Forsyth} 528*37da2899SCharles.Forsyth 529*37da2899SCharles.Forsyth# 530*37da2899SCharles.Forsyth# term: 531*37da2899SCharles.Forsyth# unary_operator? [NUMBER | PERCENTAGE | LENGTH | EMS | EXS | ANGLE | TIME | FREQ | function] 532*37da2899SCharles.Forsyth# | STRING | IDENT | URI | RGB | UNICODERANGE | hexcolour 533*37da2899SCharles.Forsyth# function: 534*37da2899SCharles.Forsyth# FUNCTION expr ')' 535*37da2899SCharles.Forsyth# unary_operator: 536*37da2899SCharles.Forsyth# '-' | '+' 537*37da2899SCharles.Forsyth# hexcolour: 538*37da2899SCharles.Forsyth# HASH S* 539*37da2899SCharles.Forsyth# 540*37da2899SCharles.Forsyth# LENGTH, EMS, ... FREQ have been combined into UNIT here 541*37da2899SCharles.Forsyth# 542*37da2899SCharles.Forsyth# TO DO: UNICODERANGE 543*37da2899SCharles.Forsyth 544*37da2899SCharles.Forsythterm(p: ref Cparse, sep: int): ref Value 545*37da2899SCharles.Forsyth{ 546*37da2899SCharles.Forsyth prefix: string; 547*37da2899SCharles.Forsyth case p.look(){ 548*37da2899SCharles.Forsyth '+' or '-' => 549*37da2899SCharles.Forsyth prefix[0] = p.get(); 550*37da2899SCharles.Forsyth } 551*37da2899SCharles.Forsyth c := p.get(); 552*37da2899SCharles.Forsyth case c { 553*37da2899SCharles.Forsyth NUMBER => 554*37da2899SCharles.Forsyth return ref Value.Number(sep, prefix+p.value); 555*37da2899SCharles.Forsyth PERCENTAGE => 556*37da2899SCharles.Forsyth return ref Value.Percentage(sep, prefix+p.value); 557*37da2899SCharles.Forsyth UNIT => 558*37da2899SCharles.Forsyth return ref Value.Unit(sep, prefix+p.value, p.suffix); 559*37da2899SCharles.Forsyth } 560*37da2899SCharles.Forsyth if(prefix != nil) 561*37da2899SCharles.Forsyth p.synerr("+/- before non-numeric"); 562*37da2899SCharles.Forsyth case c { 563*37da2899SCharles.Forsyth STRING => 564*37da2899SCharles.Forsyth return ref Value.String(sep, p.value); 565*37da2899SCharles.Forsyth IDENT => 566*37da2899SCharles.Forsyth return ref Value.Ident(sep, lowercase(p.value)); 567*37da2899SCharles.Forsyth URL => 568*37da2899SCharles.Forsyth return ref Value.Url(sep, p.value); 569*37da2899SCharles.Forsyth HASH => 570*37da2899SCharles.Forsyth # could check value: 3 or 6 hex digits 571*37da2899SCharles.Forsyth (r, g, b) := torgb(p.value); 572*37da2899SCharles.Forsyth if(r < 0) 573*37da2899SCharles.Forsyth return nil; 574*37da2899SCharles.Forsyth return ref Value.Hexcolour(sep, p.value, (r,g,b)); 575*37da2899SCharles.Forsyth FUNCTION => 576*37da2899SCharles.Forsyth name := p.value; 577*37da2899SCharles.Forsyth args := expr(p); 578*37da2899SCharles.Forsyth c = p.get(); 579*37da2899SCharles.Forsyth if(c != ')'){ 580*37da2899SCharles.Forsyth p.synerr(sys->sprint("missing ')' for function %s", name)); 581*37da2899SCharles.Forsyth return nil; 582*37da2899SCharles.Forsyth } 583*37da2899SCharles.Forsyth if(name == "rgb"){ 584*37da2899SCharles.Forsyth if(len args != 3){ 585*37da2899SCharles.Forsyth p.synerr("wrong number of arguments to rgb()"); 586*37da2899SCharles.Forsyth return nil; 587*37da2899SCharles.Forsyth } 588*37da2899SCharles.Forsyth r := colourof(hd args); 589*37da2899SCharles.Forsyth g := colourof(hd tl args); 590*37da2899SCharles.Forsyth b := colourof(hd tl tl args); 591*37da2899SCharles.Forsyth if(r < 0 || g < 0 || b < 0){ 592*37da2899SCharles.Forsyth p.synerr("invalid rgb() parameters"); 593*37da2899SCharles.Forsyth return nil; 594*37da2899SCharles.Forsyth } 595*37da2899SCharles.Forsyth return ref Value.RGB(sep, args, (r,g,b)); 596*37da2899SCharles.Forsyth } 597*37da2899SCharles.Forsyth return ref Value.Function(sep, name, args); 598*37da2899SCharles.Forsyth * => 599*37da2899SCharles.Forsyth p.unget(c); 600*37da2899SCharles.Forsyth return nil; 601*37da2899SCharles.Forsyth } 602*37da2899SCharles.Forsyth} 603*37da2899SCharles.Forsyth 604*37da2899SCharles.Forsythtorgb(s: string): (int, int, int) 605*37da2899SCharles.Forsyth{ 606*37da2899SCharles.Forsyth case len s { 607*37da2899SCharles.Forsyth 3 => 608*37da2899SCharles.Forsyth r := hex(s[0]); 609*37da2899SCharles.Forsyth g := hex(s[1]); 610*37da2899SCharles.Forsyth b := hex(s[2]); 611*37da2899SCharles.Forsyth if(r >= 0 && g >= 0 && b >= 0) 612*37da2899SCharles.Forsyth return ((r<<4)|r, (g<<4)|g, (b<<4)|b); 613*37da2899SCharles.Forsyth 6 => 614*37da2899SCharles.Forsyth v := 0; 615*37da2899SCharles.Forsyth for(i := 0; i < 6; i++){ 616*37da2899SCharles.Forsyth n := hex(s[i]); 617*37da2899SCharles.Forsyth if(n < 0) 618*37da2899SCharles.Forsyth return (-1, 0, 0); 619*37da2899SCharles.Forsyth v = (v<<4) | n; 620*37da2899SCharles.Forsyth } 621*37da2899SCharles.Forsyth return (v>>16, (v>>8)&16rFF, v&16rFF); 622*37da2899SCharles.Forsyth } 623*37da2899SCharles.Forsyth return (-1, 0, 0); 624*37da2899SCharles.Forsyth} 625*37da2899SCharles.Forsyth 626*37da2899SCharles.Forsythcolourof(v: ref Value): int 627*37da2899SCharles.Forsyth{ 628*37da2899SCharles.Forsyth pick r := v { 629*37da2899SCharles.Forsyth Number => 630*37da2899SCharles.Forsyth return clip(int r.value, 0, 255); 631*37da2899SCharles.Forsyth Percentage => 632*37da2899SCharles.Forsyth # just the integer part 633*37da2899SCharles.Forsyth return clip((int r.value*255 + 50)/100, 0, 255); 634*37da2899SCharles.Forsyth * => 635*37da2899SCharles.Forsyth return -1; 636*37da2899SCharles.Forsyth } 637*37da2899SCharles.Forsyth} 638*37da2899SCharles.Forsyth 639*37da2899SCharles.Forsythclip(v: int, l: int, u: int): int 640*37da2899SCharles.Forsyth{ 641*37da2899SCharles.Forsyth if(v < l) 642*37da2899SCharles.Forsyth return l; 643*37da2899SCharles.Forsyth if(v > u) 644*37da2899SCharles.Forsyth return u; 645*37da2899SCharles.Forsyth return v; 646*37da2899SCharles.Forsyth} 647*37da2899SCharles.Forsyth 648*37da2899SCharles.Forsythrev[T](l: list of T): list of T 649*37da2899SCharles.Forsyth{ 650*37da2899SCharles.Forsyth t: list of T; 651*37da2899SCharles.Forsyth for(; l != nil; l = tl l) 652*37da2899SCharles.Forsyth t = hd l :: t; 653*37da2899SCharles.Forsyth return t; 654*37da2899SCharles.Forsyth} 655*37da2899SCharles.Forsyth 656*37da2899SCharles.ForsythClex: adt { 657*37da2899SCharles.Forsyth context: list of int; # characters 658*37da2899SCharles.Forsyth input: string; 659*37da2899SCharles.Forsyth lim: int; 660*37da2899SCharles.Forsyth n: int; 661*37da2899SCharles.Forsyth lineno: int; 662*37da2899SCharles.Forsyth 663*37da2899SCharles.Forsyth new: fn(s: string, lno: int): ref Clex; 664*37da2899SCharles.Forsyth getc: fn(cs: self ref Clex): int; 665*37da2899SCharles.Forsyth ungetc: fn(cs: self ref Clex, c: int); 666*37da2899SCharles.Forsyth synerr: fn(nil: self ref Clex, s: string); 667*37da2899SCharles.Forsyth}; 668*37da2899SCharles.Forsyth 669*37da2899SCharles.ForsythClex.new(s: string, lno: int): ref Clex 670*37da2899SCharles.Forsyth{ 671*37da2899SCharles.Forsyth return ref Clex(nil, s, len s, 0, lno); 672*37da2899SCharles.Forsyth} 673*37da2899SCharles.Forsyth 674*37da2899SCharles.ForsythClex.getc(cs: self ref Clex): int 675*37da2899SCharles.Forsyth{ 676*37da2899SCharles.Forsyth if(cs.context != nil){ 677*37da2899SCharles.Forsyth c := hd cs.context; 678*37da2899SCharles.Forsyth cs.context = tl cs.context; 679*37da2899SCharles.Forsyth return c; 680*37da2899SCharles.Forsyth } 681*37da2899SCharles.Forsyth if(cs.n >= cs.lim) 682*37da2899SCharles.Forsyth return -1; 683*37da2899SCharles.Forsyth c := cs.input[cs.n++]; 684*37da2899SCharles.Forsyth if(c == '\n') 685*37da2899SCharles.Forsyth cs.lineno++; 686*37da2899SCharles.Forsyth return c; 687*37da2899SCharles.Forsyth} 688*37da2899SCharles.Forsyth 689*37da2899SCharles.ForsythClex.ungetc(cs: self ref Clex, c: int) 690*37da2899SCharles.Forsyth{ 691*37da2899SCharles.Forsyth cs.context = c :: cs.context; 692*37da2899SCharles.Forsyth} 693*37da2899SCharles.Forsyth 694*37da2899SCharles.ForsythClex.synerr(cs: self ref Clex, s: string) 695*37da2899SCharles.Forsyth{ 696*37da2899SCharles.Forsyth if(printdiag) 697*37da2899SCharles.Forsyth sys->fprint(sys->fildes(2), "%d: err: %s\n", cs.lineno, s); 698*37da2899SCharles.Forsyth} 699*37da2899SCharles.Forsyth 700*37da2899SCharles.Forsythcsslex(cs: ref Clex): (int, string, string) 701*37da2899SCharles.Forsyth{ 702*37da2899SCharles.Forsyth for(;;){ 703*37da2899SCharles.Forsyth c := skipws(cs); 704*37da2899SCharles.Forsyth if(c < 0) 705*37da2899SCharles.Forsyth return (-1, nil, nil); 706*37da2899SCharles.Forsyth case c { 707*37da2899SCharles.Forsyth '<' => 708*37da2899SCharles.Forsyth if(seq(cs, "!--")) 709*37da2899SCharles.Forsyth break; # <!-- ignore HTML comment start (CDO) 710*37da2899SCharles.Forsyth return (c, nil, nil); 711*37da2899SCharles.Forsyth '-' => 712*37da2899SCharles.Forsyth if(seq(cs, "->")) 713*37da2899SCharles.Forsyth break; # --> ignore HTML comment end (CDC) 714*37da2899SCharles.Forsyth return (c, nil, nil); 715*37da2899SCharles.Forsyth ':' => 716*37da2899SCharles.Forsyth c = cs.getc(); 717*37da2899SCharles.Forsyth cs.ungetc(c); 718*37da2899SCharles.Forsyth if(isnamec(c, 0)) 719*37da2899SCharles.Forsyth return (PSEUDO, nil, nil); 720*37da2899SCharles.Forsyth return (':', nil, nil); 721*37da2899SCharles.Forsyth '#' => 722*37da2899SCharles.Forsyth c = cs.getc(); 723*37da2899SCharles.Forsyth if(isnamec(c, 1)) 724*37da2899SCharles.Forsyth return (HASH, name(cs, c), nil); 725*37da2899SCharles.Forsyth cs.ungetc(c); 726*37da2899SCharles.Forsyth return ('#', nil, nil); 727*37da2899SCharles.Forsyth '/' => 728*37da2899SCharles.Forsyth if(subseq(cs, '*', 1, 0)){ 729*37da2899SCharles.Forsyth comment(cs); 730*37da2899SCharles.Forsyth break; 731*37da2899SCharles.Forsyth } 732*37da2899SCharles.Forsyth return (c, nil, nil); 733*37da2899SCharles.Forsyth '\'' or '"' => 734*37da2899SCharles.Forsyth return (STRING, quotedstring(cs, c), nil); 735*37da2899SCharles.Forsyth '0' to '9' or '.' => 736*37da2899SCharles.Forsyth if(c == '.'){ 737*37da2899SCharles.Forsyth d := cs.getc(); 738*37da2899SCharles.Forsyth cs.ungetc(d); 739*37da2899SCharles.Forsyth if(!isdigit(d)){ 740*37da2899SCharles.Forsyth if(isnamec(d, 1)) 741*37da2899SCharles.Forsyth return (CLASS, name(cs, cs.getc()), nil); 742*37da2899SCharles.Forsyth return ('.', nil, nil); 743*37da2899SCharles.Forsyth } 744*37da2899SCharles.Forsyth # apply CSS2 treatment: .55 is a number not a class 745*37da2899SCharles.Forsyth } 746*37da2899SCharles.Forsyth val := number(cs, c); 747*37da2899SCharles.Forsyth c = cs.getc(); 748*37da2899SCharles.Forsyth if(c == '%') 749*37da2899SCharles.Forsyth return (PERCENTAGE, val, "%"); 750*37da2899SCharles.Forsyth if(isnamec(c, 0)) # use CSS2 interpetation 751*37da2899SCharles.Forsyth return (UNIT, val, lowercase(name(cs, c))); 752*37da2899SCharles.Forsyth cs.ungetc(c); 753*37da2899SCharles.Forsyth return (NUMBER, val, nil); 754*37da2899SCharles.Forsyth '\\' => 755*37da2899SCharles.Forsyth d := cs.getc(); 756*37da2899SCharles.Forsyth if(d >= ' ' && d <= '~' || islatin1(d)){ # probably should handle it in name 757*37da2899SCharles.Forsyth wd := name(cs, d); 758*37da2899SCharles.Forsyth return (IDENT, "\\"+wd, nil); 759*37da2899SCharles.Forsyth } 760*37da2899SCharles.Forsyth cs.ungetc(d); 761*37da2899SCharles.Forsyth return ('\\', nil, nil); 762*37da2899SCharles.Forsyth '@' => 763*37da2899SCharles.Forsyth c = cs.getc(); 764*37da2899SCharles.Forsyth if(isnamec(c, 0)) # @something 765*37da2899SCharles.Forsyth return (ATKEYWORD, "@"+lowercase(name(cs,c)), nil); 766*37da2899SCharles.Forsyth cs.ungetc(c); 767*37da2899SCharles.Forsyth return ('@', nil, nil); 768*37da2899SCharles.Forsyth '!' => 769*37da2899SCharles.Forsyth c = skipws(cs); 770*37da2899SCharles.Forsyth if(isnamec(c, 0)){ # !something 771*37da2899SCharles.Forsyth wd := name(cs, c); 772*37da2899SCharles.Forsyth if(lowercase(wd) == "important") 773*37da2899SCharles.Forsyth return (IMPORTANT, nil, nil); 774*37da2899SCharles.Forsyth pushback(cs, wd); 775*37da2899SCharles.Forsyth }else 776*37da2899SCharles.Forsyth cs.ungetc(c); 777*37da2899SCharles.Forsyth return ('!', nil, nil); 778*37da2899SCharles.Forsyth '~' => 779*37da2899SCharles.Forsyth if(subseq(cs, '=', 1, 0)) 780*37da2899SCharles.Forsyth return (INCLUDES, "~=", nil); 781*37da2899SCharles.Forsyth return ('~', nil, nil); 782*37da2899SCharles.Forsyth '|' => 783*37da2899SCharles.Forsyth if(subseq(cs, '=', 1, 0)) 784*37da2899SCharles.Forsyth return (DASHMATCH, "|=", nil); 785*37da2899SCharles.Forsyth return ('|', nil, nil); 786*37da2899SCharles.Forsyth * => 787*37da2899SCharles.Forsyth if(isnamec(c, 0)){ 788*37da2899SCharles.Forsyth wd := name(cs, c); 789*37da2899SCharles.Forsyth d := cs.getc(); 790*37da2899SCharles.Forsyth if(d != '('){ 791*37da2899SCharles.Forsyth cs.ungetc(d); 792*37da2899SCharles.Forsyth return (IDENT, wd, nil); 793*37da2899SCharles.Forsyth } 794*37da2899SCharles.Forsyth val := lowercase(wd); 795*37da2899SCharles.Forsyth if(val == "url") 796*37da2899SCharles.Forsyth return (URL, url(cs), nil); # bizarre special case 797*37da2899SCharles.Forsyth return (FUNCTION, val, nil); 798*37da2899SCharles.Forsyth } 799*37da2899SCharles.Forsyth return (c, nil, nil); 800*37da2899SCharles.Forsyth } 801*37da2899SCharles.Forsyth 802*37da2899SCharles.Forsyth } 803*37da2899SCharles.Forsyth} 804*37da2899SCharles.Forsyth 805*37da2899SCharles.Forsythskipws(cs: ref Clex): int 806*37da2899SCharles.Forsyth{ 807*37da2899SCharles.Forsyth for(;;){ 808*37da2899SCharles.Forsyth while((c := cs.getc()) == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f') 809*37da2899SCharles.Forsyth ; 810*37da2899SCharles.Forsyth if(c != '/') 811*37da2899SCharles.Forsyth return c; 812*37da2899SCharles.Forsyth c = cs.getc(); 813*37da2899SCharles.Forsyth if(c != '*'){ 814*37da2899SCharles.Forsyth cs.ungetc(c); 815*37da2899SCharles.Forsyth return '/'; 816*37da2899SCharles.Forsyth } 817*37da2899SCharles.Forsyth comment(cs); 818*37da2899SCharles.Forsyth } 819*37da2899SCharles.Forsyth} 820*37da2899SCharles.Forsyth 821*37da2899SCharles.Forsythseq(cs: ref Clex, s: string): int 822*37da2899SCharles.Forsyth{ 823*37da2899SCharles.Forsyth for(i := 0; i < len s; i++) 824*37da2899SCharles.Forsyth if((c := cs.getc()) != s[i]) 825*37da2899SCharles.Forsyth break; 826*37da2899SCharles.Forsyth if(i == len s) 827*37da2899SCharles.Forsyth return 1; 828*37da2899SCharles.Forsyth cs.ungetc(c); 829*37da2899SCharles.Forsyth while(i > 0) 830*37da2899SCharles.Forsyth cs.ungetc(s[--i]); 831*37da2899SCharles.Forsyth if(c < 0) 832*37da2899SCharles.Forsyth return -1; 833*37da2899SCharles.Forsyth return 0; 834*37da2899SCharles.Forsyth} 835*37da2899SCharles.Forsyth 836*37da2899SCharles.Forsythsubseq(cs: ref Clex, a: int, t: int, e: int): int 837*37da2899SCharles.Forsyth{ 838*37da2899SCharles.Forsyth if((c := cs.getc()) != a){ 839*37da2899SCharles.Forsyth cs.ungetc(c); 840*37da2899SCharles.Forsyth return e; 841*37da2899SCharles.Forsyth } 842*37da2899SCharles.Forsyth return t; 843*37da2899SCharles.Forsyth} 844*37da2899SCharles.Forsyth 845*37da2899SCharles.Forsythpushback(cs: ref Clex, wd: string) 846*37da2899SCharles.Forsyth{ 847*37da2899SCharles.Forsyth for(i := len wd; --i >= 0;) 848*37da2899SCharles.Forsyth cs.ungetc(wd[i]); 849*37da2899SCharles.Forsyth} 850*37da2899SCharles.Forsyth 851*37da2899SCharles.Forsythcomment(cs: ref Clex) 852*37da2899SCharles.Forsyth{ 853*37da2899SCharles.Forsyth while((c := cs.getc()) != '*' || (c = cs.getc()) != '/') 854*37da2899SCharles.Forsyth if(c < 0) { 855*37da2899SCharles.Forsyth # end of file in comment 856*37da2899SCharles.Forsyth break; 857*37da2899SCharles.Forsyth } 858*37da2899SCharles.Forsyth} 859*37da2899SCharles.Forsyth 860*37da2899SCharles.Forsythnumber(cs: ref Clex, c: int): string 861*37da2899SCharles.Forsyth{ 862*37da2899SCharles.Forsyth s: string; 863*37da2899SCharles.Forsyth for(; isdigit(c); c = cs.getc()) 864*37da2899SCharles.Forsyth s[len s] = c; 865*37da2899SCharles.Forsyth if(c != '.'){ 866*37da2899SCharles.Forsyth cs.ungetc(c); 867*37da2899SCharles.Forsyth return s; 868*37da2899SCharles.Forsyth } 869*37da2899SCharles.Forsyth if(!isdigit(c = cs.getc())){ 870*37da2899SCharles.Forsyth cs.ungetc(c); 871*37da2899SCharles.Forsyth cs.ungetc('.'); 872*37da2899SCharles.Forsyth return s; 873*37da2899SCharles.Forsyth } 874*37da2899SCharles.Forsyth s[len s] = '.'; 875*37da2899SCharles.Forsyth do{ 876*37da2899SCharles.Forsyth s[len s] = c; 877*37da2899SCharles.Forsyth }while(isdigit(c = cs.getc())); 878*37da2899SCharles.Forsyth cs.ungetc(c); 879*37da2899SCharles.Forsyth return s; 880*37da2899SCharles.Forsyth} 881*37da2899SCharles.Forsyth 882*37da2899SCharles.Forsythname(cs: ref Clex, c: int): string 883*37da2899SCharles.Forsyth{ 884*37da2899SCharles.Forsyth s: string; 885*37da2899SCharles.Forsyth for(; isnamec(c, 1); c = cs.getc()){ 886*37da2899SCharles.Forsyth s[len s] = c; 887*37da2899SCharles.Forsyth if(c == '\\'){ 888*37da2899SCharles.Forsyth c = cs.getc(); 889*37da2899SCharles.Forsyth if(isescapable(c)) 890*37da2899SCharles.Forsyth s[len s] = c; 891*37da2899SCharles.Forsyth } 892*37da2899SCharles.Forsyth } 893*37da2899SCharles.Forsyth cs.ungetc(c); 894*37da2899SCharles.Forsyth return s; 895*37da2899SCharles.Forsyth} 896*37da2899SCharles.Forsyth 897*37da2899SCharles.Forsythisescapable(c: int): int 898*37da2899SCharles.Forsyth{ 899*37da2899SCharles.Forsyth return c >= ' ' && c <= '~' || isnamec(c, 1); 900*37da2899SCharles.Forsyth} 901*37da2899SCharles.Forsyth 902*37da2899SCharles.Forsythislatin1(c: int): int 903*37da2899SCharles.Forsyth{ 904*37da2899SCharles.Forsyth return c >= 16rA1 && c <= 16rFF; # printable latin-1 905*37da2899SCharles.Forsyth} 906*37da2899SCharles.Forsyth 907*37da2899SCharles.Forsythisnamec(c: int, notfirst: int): int 908*37da2899SCharles.Forsyth{ 909*37da2899SCharles.Forsyth return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '\\' || 910*37da2899SCharles.Forsyth notfirst && (c >= '0' && c <= '9' || c == '-') || 911*37da2899SCharles.Forsyth c >= 16rA1 && c <= 16rFF; # printable latin-1 912*37da2899SCharles.Forsyth} 913*37da2899SCharles.Forsyth 914*37da2899SCharles.Forsythisxdigit(c: int): int 915*37da2899SCharles.Forsyth{ 916*37da2899SCharles.Forsyth return c>='0' && c<='9' || c>='a'&&c<='f' || c>='A'&&c<='F'; 917*37da2899SCharles.Forsyth} 918*37da2899SCharles.Forsyth 919*37da2899SCharles.Forsythisdigit(c: int): int 920*37da2899SCharles.Forsyth{ 921*37da2899SCharles.Forsyth return c >= '0' && c <= '9'; 922*37da2899SCharles.Forsyth} 923*37da2899SCharles.Forsyth 924*37da2899SCharles.Forsythisspace(c: int): int 925*37da2899SCharles.Forsyth{ 926*37da2899SCharles.Forsyth return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; 927*37da2899SCharles.Forsyth} 928*37da2899SCharles.Forsyth 929*37da2899SCharles.Forsythhex(c: int): int 930*37da2899SCharles.Forsyth{ 931*37da2899SCharles.Forsyth if(c >= '0' && c <= '9') 932*37da2899SCharles.Forsyth return c-'0'; 933*37da2899SCharles.Forsyth if(c >= 'A' && c <= 'F') 934*37da2899SCharles.Forsyth return c-'A' + 10; 935*37da2899SCharles.Forsyth if(c >= 'a' && c <= 'f') 936*37da2899SCharles.Forsyth return c-'a' + 10; 937*37da2899SCharles.Forsyth return -1; 938*37da2899SCharles.Forsyth} 939*37da2899SCharles.Forsyth 940*37da2899SCharles.Forsythquotedstring(cs: ref Clex, delim: int): string 941*37da2899SCharles.Forsyth{ 942*37da2899SCharles.Forsyth s: string; 943*37da2899SCharles.Forsyth while((c := cs.getc()) != delim){ 944*37da2899SCharles.Forsyth if(c < 0){ 945*37da2899SCharles.Forsyth cs.synerr("end-of-file in string"); 946*37da2899SCharles.Forsyth return s; 947*37da2899SCharles.Forsyth } 948*37da2899SCharles.Forsyth if(c == '\\'){ 949*37da2899SCharles.Forsyth c = cs.getc(); 950*37da2899SCharles.Forsyth if(c < 0){ 951*37da2899SCharles.Forsyth cs.synerr("end-of-file in string"); 952*37da2899SCharles.Forsyth return s; 953*37da2899SCharles.Forsyth } 954*37da2899SCharles.Forsyth if(isxdigit(c)){ 955*37da2899SCharles.Forsyth # unicode escape 956*37da2899SCharles.Forsyth n := 0; 957*37da2899SCharles.Forsyth for(i := 0;;){ 958*37da2899SCharles.Forsyth n = (n<<4) | hex(c); 959*37da2899SCharles.Forsyth c = cs.getc(); 960*37da2899SCharles.Forsyth if(!isxdigit(c) || ++i >= 6){ 961*37da2899SCharles.Forsyth if(!isspace(c)) 962*37da2899SCharles.Forsyth cs.ungetc(c); # CSS2 ignores the first white space following 963*37da2899SCharles.Forsyth break; 964*37da2899SCharles.Forsyth } 965*37da2899SCharles.Forsyth } 966*37da2899SCharles.Forsyth s[len s] = n; 967*37da2899SCharles.Forsyth }else if(c == '\n'){ 968*37da2899SCharles.Forsyth ; # escaped newline 969*37da2899SCharles.Forsyth }else if(isescapable(c)) 970*37da2899SCharles.Forsyth s[len s] = c; 971*37da2899SCharles.Forsyth }else if(c) 972*37da2899SCharles.Forsyth s[len s] = c; 973*37da2899SCharles.Forsyth } 974*37da2899SCharles.Forsyth return s; 975*37da2899SCharles.Forsyth} 976*37da2899SCharles.Forsyth 977*37da2899SCharles.Forsythurl(cs: ref Clex): string 978*37da2899SCharles.Forsyth{ 979*37da2899SCharles.Forsyth s: string; 980*37da2899SCharles.Forsyth c := skipws(cs); 981*37da2899SCharles.Forsyth if(c != '"' && c != '\''){ # not a quoted string 982*37da2899SCharles.Forsyth while(c != ' ' && c != '\n' && c != '\'' && c != '"' && c != ')'){ 983*37da2899SCharles.Forsyth s[len s] = c; 984*37da2899SCharles.Forsyth c = cs.getc(); 985*37da2899SCharles.Forsyth if(c == '\\'){ 986*37da2899SCharles.Forsyth c = cs.getc(); 987*37da2899SCharles.Forsyth if(c < 0){ 988*37da2899SCharles.Forsyth cs.synerr("end of file in url parameter"); 989*37da2899SCharles.Forsyth break; 990*37da2899SCharles.Forsyth } 991*37da2899SCharles.Forsyth if(c == ' ' || c == '\'' || c == '"' || c == ')') 992*37da2899SCharles.Forsyth s[len s] = c; 993*37da2899SCharles.Forsyth else{ 994*37da2899SCharles.Forsyth cs.synerr("invalid escape sequence in url"); 995*37da2899SCharles.Forsyth s[len s] = '\\'; 996*37da2899SCharles.Forsyth s[len s] = c; 997*37da2899SCharles.Forsyth } 998*37da2899SCharles.Forsyth c = cs.getc(); 999*37da2899SCharles.Forsyth } 1000*37da2899SCharles.Forsyth } 1001*37da2899SCharles.Forsyth cs.ungetc(c); 1002*37da2899SCharles.Forsyth# if(s == nil) 1003*37da2899SCharles.Forsyth# p.synerr("empty parameter to url"); 1004*37da2899SCharles.Forsyth }else 1005*37da2899SCharles.Forsyth s = quotedstring(cs, c); 1006*37da2899SCharles.Forsyth if((c = skipws(cs)) != ')'){ 1007*37da2899SCharles.Forsyth cs.synerr("unclosed parameter to url"); 1008*37da2899SCharles.Forsyth cs.ungetc(c); 1009*37da2899SCharles.Forsyth } 1010*37da2899SCharles.Forsyth return s; 1011*37da2899SCharles.Forsyth} 1012*37da2899SCharles.Forsyth 1013*37da2899SCharles.Forsythlowercase(s: string): string 1014*37da2899SCharles.Forsyth{ 1015*37da2899SCharles.Forsyth for(i := 0; i < len s; i++) 1016*37da2899SCharles.Forsyth if((c := s[i]) >= 'A' && c <= 'Z') 1017*37da2899SCharles.Forsyth s[i] = c-'A' + 'a'; 1018*37da2899SCharles.Forsyth return s; 1019*37da2899SCharles.Forsyth} 1020