1implement Xpointers; 2 3# 4# Copyright © 2005 Vita Nuova Holdings Limited 5# 6 7include "sys.m"; 8 sys: Sys; 9 10include "xpointers.m"; 11 12init() 13{ 14 sys = load Sys Sys->PATH; 15} 16 17# 18# XPointer framework syntax 19# 20# Pointer ::= Shorthand | SchemeBased 21# Shorthand ::= NCName # from [XML-Names] 22# SchemeBased ::= PointerPart (S? PointerPart)* 23# PointerPart ::= SchemeName '(' SchemeData ')' 24# SchemeName ::= QName # from [XML-Names] 25# SchemeData ::= EscapedData* 26# EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')' 27# NormalChar ::= UnicodeChar - [()^] 28# UnicodeChar ::= [#x0 - #x10FFFF] 29 30framework(s: string): (string, list of (string, string, string), string) 31{ 32 (q, nm, i) := name(s, 0); 33 if(i >= len s){ # Shorthand 34 if(q != nil) 35 return (nil, nil, "shorthand pointer must be unqualified name"); 36 if(nm == nil) 37 return (nil, nil, "missing pointer name"); 38 return (nm, nil, nil); 39 } 40 # must be SchemeBased 41 l: list of (string, string, string); 42 for(;;){ 43 if(nm == nil){ 44 if(q != nil) 45 return (nil, nil, sys->sprint("prefix but no local part in name at %d", i)); 46 return (nil, nil, sys->sprint("expected name at %d", i)); 47 } 48 if(i >= len s || s[i] != '(') 49 return (nil, nil, sys->sprint("expected '(' at %d", i)); 50 o := i++; 51 a := ""; 52 nesting := 0; 53 for(; i < len s && ((c := s[i]) != ')' || nesting); i++){ 54 case c { 55 '^' => 56 if(i+1 >= len s) 57 return (nil, nil, "unexpected eof after ^"); 58 c = s[++i]; 59 if(c != '(' && c != ')' && c != '^') 60 return (nil, nil, sys->sprint("invalid escape ^%c at %d", c, i)); 61 '(' => 62 nesting++; 63 ')' => 64 if(--nesting < 0) 65 return (nil, nil, sys->sprint("unbalanced ) at %d", i)); 66 } 67 a[len a] = c; 68 } 69 if(i >= len s) 70 return (nil, nil, sys->sprint("unbalanced ( at %d", o)); 71 l = (q, nm, a) :: l; 72 if(++i == len s) 73 break; 74 while(i < len s && isspace(s[i])) 75 i++; 76 (q, nm, i) = name(s, i); 77 } 78 rl: list of (string, string, string); 79 for(; l != nil; l = tl l) 80 rl = hd l :: rl; 81 return (nil, rl, nil); 82} 83 84isspace(c: int): int 85{ 86 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'; 87} 88 89# 90# QName ::= (Prefix ':')? LocalPart 91# Prefix ::= NCName 92# LocalPart ::= NCName 93# 94#NCName :: (Oetter | '_') NCNameChar* 95#NCNameChar :: Oetter | Digit | '.' | '-' | '_' | CombiningChar | Extender 96 97name(s: string, o: int): (string, string, int) 98{ 99 (ns, i) := ncname(s, o); 100 if(i >= len s || s[i] != ':') 101 return (nil, ns, i); 102 (nm, j) := ncname(s, i+1); 103 if(j == i+1) 104 return (nil, ns, i); # assume it's a LocalPart followed by ':' 105 return (ns, nm, j); 106} 107 108ncname(s: string, o: int): (string, int) 109{ 110 if(o >= len s || !isalnum(c := s[o]) && c != '_' || c >= '0' && c <= '9') 111 return (nil, o); # missing or invalid start character 112 for(i := o; i < len s && isnamec(s[i]); i++) 113 ; 114 return (s[o:i], i); 115} 116 117isnamec(c: int): int 118{ 119 return isalnum(c) || c == '_' || c == '-' || c == '.'; 120} 121 122isalnum(c: int): int 123{ 124 # 125 # Hard to get absolutely right without silly amount of character data. 126 # Use what we know about ASCII 127 # and assume anything above the Oatin control characters is 128 # potentially an alphanumeric. 129 # 130 if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') 131 return 1; # usual case 132 if(c <= ' ') 133 return 0; 134 if(c > 16rA0) 135 return 1; # non-ASCII 136 return 0; 137} 138 139# schemes: xpointer(), xmlns(), element() 140 141# xmlns() 142# XmlnsSchemeData ::= NCName S? '=' S? EscapedNamespaceName 143# EscapedNamespaceName ::= EscapedData* 144 145xmlns(s: string): (string, string, string) 146{ 147 (nm, i) := ncname(s, 0); 148 if(nm == nil) 149 return (nil, nil, "illegal namespace name"); 150 while(i < len s && isspace(s[i])) 151 i++; 152 if(i >= len s || s[i++] != '=') 153 return (nil, nil, "illegal xmlns declaration"); 154 while(i < len s && isspace(s[i])) 155 i++; 156 return (nm, s[i:], nil); 157} 158 159# element() 160# ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence 161# ChildSequence ::= ('/' [1-9] [0-9]*)+ 162 163element(s: string): (string, list of int, string) 164{ 165 nm: string; 166 i := 0; 167 if(s != nil && s[0] != '/'){ 168 (nm, i) = ncname(s, 0); 169 if(nm == nil) 170 return (nil, nil, "illegal element name"); 171 } 172 l: list of int; 173 do{ 174 if(i >= len s || s[i++] != '/') 175 return (nil, nil, "illegal child sequence (expected '/')"); 176 v := 0; 177 do{ 178 if(i >= len s || !isdigit(s[i])) 179 return (nil, nil, "illegal child sequence (expected integer)"); 180 v = v*10 + s[i]-'0'; 181 }while(++i < len s && s[i] != '/'); 182 l = v :: l; 183 }while(i < len s); 184 rl: list of int; 185 for(; l != nil; l = tl l) 186 rl = hd l :: rl; 187 return (nm, rl, nil); 188} 189 190# xpointer() 191# XpointerSchemeData ::= Expr # from Xpath, with new functions and data types 192 193xpointer(s: string): (ref Xpath, string) 194{ 195 p := ref Parse(ref Rd(s, 0, 0), nil); 196 { 197 e := expr(p, 0); 198 if(p.r.i < len s) 199 synerr("missing operator"); 200 return (e, nil); 201 }exception e{ 202 "syntax error*" => 203 return (nil, e); 204 * => 205 raise; 206 } 207} 208 209Lerror, Ldslash, Lint, Lreal, Llit, Lvar, Ldotdot, Lop, Laxis, Lfn: con 'a'+iota; # internal lexical items 210 211Keywd: adt { 212 name: string; 213 val: int; 214}; 215 216axes: array of Keywd = array[] of { 217 ("ancestor", Aancestor), 218 ("ancestor-or-self", Aancestor_or_self), 219 ("attribute", Aattribute), 220 ("child", Achild), 221 ("descendant", Adescendant), 222 ("descendant-or-self", Adescendant_or_self), 223 ("following", Afollowing), 224 ("following-sibling", Afollowing_sibling), 225 ("namespace", Anamespace), 226 ("parent", Aparent), 227 ("preceding", Apreceding), 228 ("preceding-sibling", Apreceding_sibling), 229 ("self", Aself), 230}; 231 232keywds: array of Keywd = array[] of { 233 ("and", Oand), 234 ("comment", Onodetype), 235 ("div", Odiv), 236 ("mod", Omod), 237 ("node", Onodetype), 238 ("or", Oor), 239 ("processing-instruction", Onodetype), 240 ("text", Onodetype), 241}; 242 243iskeywd(s: string): int 244{ 245 return look(keywds, s); 246} 247 248look(k: array of Keywd, s: string): int 249{ 250 for(i := 0; i < len k; i++) 251 if(k[i].name == s) 252 return k[i].val; 253 return 0; 254} 255 256lookname(k: array of Keywd, v: int): string 257{ 258 for(i := 0; i < len k; i++) 259 if(k[i].val == v) 260 return k[i].name; 261 return nil; 262} 263 264prectab := array[] of { 265 array[] of {Oor}, 266 array[] of {Oand}, 267 array[] of {'=', One}, 268 array[] of {'<', Ole, '>', Oge}, 269 array[] of {'+', '-'}, 270 array[] of {Omul, Odiv, Omod}, 271 array[] of {Oneg}, # unary '-' 272 array[] of {'|'}, # UnionExpr 273}; 274 275isop(t: int, p: array of int): int 276{ 277 if(t >= 0) 278 for(j := 0; j < len p; j++) 279 if(t == p[j]) 280 return 1; 281 return 0; 282} 283 284# Expr ::= OrExpr 285# UnionExpr ::= PathExpr | UnionExpr '|' PathExpr 286# PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath | 287# FilterExpr '//' RelativeLocationPath 288# OrExpr ::= AndExpr | OrExpr 'or' AndExpr 289# AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr 290# EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr 291# RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | RelationalExpr '>' AdditiveExpr | 292# RelationalExpr '<=' AdditiveExpr | RelationalExpr '>=' AdditiveExpr 293# AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr 294# MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr MultiplyOperator UnaryExpr | 295# MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr 296# UnaryExpr ::= UnionExpr | '-' UnaryExpr 297 298expr(p: ref Parse, k: int): ref Xpath 299{ 300 if(k >= len prectab) 301 return pathexpr(p); 302 if(prectab[k][0] == Oneg){ # unary '-' 303 if(p.look() == '-'){ 304 p.get(); 305 return ref Xpath.E(Oneg, expr(p,k+1), nil); 306 } 307 # must be UnionExpr 308 k++; 309 } 310 e := expr(p, k+1); 311 while(isop(p.look(), prectab[k])){ 312 o := p.get().t0; 313 e = ref Xpath.E(o, e, expr(p, k+1)); # +assoc[k] 314 } 315 return e; 316} 317 318# PathExpr ::= LocationPath | FilterExpr ( ('/' | '//') RelativeLocationPath ) 319# FilterExpr ::= PrimaryExpr | FilterExpr Predicate => PrimaryExpr Predicate* 320 321pathexpr(p: ref Parse): ref Xpath 322{ 323 # LocationPath? 324 case p.look() { 325 '.' or Ldotdot or Laxis or '@' or Onametest or Onodetype or '*' => 326 return locationpath(p, 0); 327 '/' or Ldslash => 328 return locationpath(p, 1); 329 } 330 # FilterExpr 331 e := primary(p); 332 while(p.look() == '[') 333 e = ref Xpath.E(Ofilter, e, predicate(p)); 334 if((o := p.look()) == '/' || o == Ldslash) 335 e = ref Xpath.E(Opath, e, locationpath(p, 0)); 336 return e; 337} 338 339# LocationPath ::= RelativeLocationPath | AbsoluteLocationPath 340# AbsoluteLocationPath ::= '/' RelativeLocationPath? | AbbreviatedAbsoluteLocationPath 341# RelativeLocationPath ::= Step | RelativeLocationPath '/' Step 342# AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath 343# AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step 344 345locationpath(p: ref Parse, abs: int): ref Xpath 346{ 347 # // => /descendent-or-self::node()/ 348 pl: list of ref Xstep; 349 o := p.look(); 350 if(o != '/' && o != Ldslash){ 351 s := step(p); 352 if(s == nil) 353 synerr("expected Step in LocationPath"); 354 pl = s :: pl; 355 } 356 while((o = p.look()) == '/' || o == Ldslash){ 357 p.get(); 358 if(o == Ldslash) 359 pl = ref Xstep(Adescendant_or_self, Onodetype, nil, "node", nil, nil) :: pl; 360 s := step(p); 361 if(s == nil){ 362 if(abs && pl == nil) 363 break; # it's just an initial '/' 364 synerr("expected Step in LocationPath"); 365 } 366 pl = s :: pl; 367 } 368 return ref Xpath.Path(abs, rev(pl)); 369} 370 371# Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep 372# AxisSpecifier ::= AxisName '::' | AbbreviatedAxisSpecifier 373# AxisName := ... # long list 374# NodeTest ::= NameTest | NodeType '(' ')' 375# Predicate ::= '[' PredicateExpr ']' 376# PredicateExpr ::= Expr 377# AbbreviatedStep ::= '.' | '..' 378# AbbreviatedAxisSpecifier ::= '@'? 379 380step(p: ref Parse): ref Xstep 381{ 382 # AxisSpecifier ... | AbbreviatedStep 383 (o, ns, nm) := p.get(); 384 axis := Achild; 385 case o { 386 '.' => 387 return ref Xstep(Aself, Onodetype, nil, "node", nil, nil); # self::node() 388 Ldotdot => 389 return ref Xstep(Aparent, Onodetype, nil, "node", nil, nil); # parent::node() 390 Laxis => 391 axis = look(axes, ns); 392 (o, ns, nm) = p.get(); 393 '@' => 394 axis = Aattribute; 395 (o, ns, nm) = p.get(); 396 * => 397 ; 398 } 399 400 if(o == '*'){ 401 o = Onametest; 402 nm = "*"; 403 ns = nil; 404 } 405 406 # NodeTest ::= NameTest | NodeType '(' ')' 407 if(o != Onametest && o != Onodetype){ 408 p.unget((o, ns, nm)); 409 return nil; 410 } 411 412 arg: string; 413 if(o == Onodetype){ # '(' ... ')' 414 expect(p, '('); 415 # grammar is wrong: processing-instruction can have optional literal 416 if(nm == "processing-instruction" && p.look() == Llit) 417 arg = p.get().t1; 418 expect(p, ')'); 419 } 420 421 # Predicate* 422 pl: list of ref Xpath; 423 while((pe := predicate(p)) != nil) 424 pl = pe :: pl; 425 return ref Xstep(axis, o, ns, nm, arg, rev(pl)); 426} 427 428# PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall 429# FunctionCall ::= FunctionName '(' (Argument ( ',' Argument)*)? ')' 430# Argument ::= Expr 431 432primary(p: ref Parse): ref Xpath 433{ 434 (o, ns, nm) := p.get(); 435 case o { 436 Lvar => 437 return ref Xpath.Var(ns, nm); 438 '(' => 439 e := expr(p, 0); 440 expect(p, ')'); 441 return e; 442 Llit => 443 return ref Xpath.Str(ns); 444 Lint => 445 return ref Xpath.Int(big ns); 446 Lreal => 447 return ref Xpath.Real(real ns); 448 Lfn => 449 expect(p, '('); 450 al: list of ref Xpath; 451 if(p.look() != ')'){ 452 for(;;){ 453 al = expr(p, 0) :: al; 454 if(p.look() != ',') 455 break; 456 p.get(); 457 } 458 al = rev(al); 459 } 460 expect(p, ')'); 461 return ref Xpath.Fn(ns, nm, al); 462 * => 463 synerr("invalid PrimaryExpr"); 464 return nil; 465 } 466} 467 468# Predicate ::= '[' PredicateExpr ']' 469# PredicateExpr ::= Expr 470 471predicate(p: ref Parse): ref Xpath 472{ 473 l := p.get(); 474 if(l.t0 != '['){ 475 p.unget(l); 476 return nil; 477 } 478 e := expr(p, 0); 479 expect(p, ']'); 480 return e; 481} 482 483expect(p: ref Parse, t: int) 484{ 485 l := p.get(); 486 if(l.t0 != t) 487 synerr(sys->sprint("expected '%c'", t)); 488} 489 490Xpath.text(e: self ref Xpath): string 491{ 492 if(e == nil) 493 return "nil"; 494 pick r := e { 495 E => 496 if(r.r == nil) 497 return sys->sprint("(%s%s)", opname(r.op), r.l.text()); 498 if(r.op == Ofilter) 499 return sys->sprint("%s[%s]", r.l.text(), r.r.text()); 500 return sys->sprint("(%s%s%s)", r.l.text(), opname(r.op), r.r.text()); 501 Fn => 502 a := ""; 503 for(l := r.args; l != nil; l = tl l) 504 a += sys->sprint(",%s", (hd l).text()); 505 if(a != "") 506 a = a[1:]; 507 return sys->sprint("%s(%s)", qual(r.ns, r.name), a); 508 Var => 509 return sys->sprint("$%s", qual(r.ns, r.name)); 510 Path => 511 if(r.abs) 512 t := "/"; 513 else 514 t = ""; 515 for(l := r.steps; l != nil; l = tl l){ 516 if(t != nil && t != "/") 517 t += "/"; 518 t += (hd l).text(); 519 } 520 return t; 521 Int => 522 return sys->sprint("%bd", r.val); 523 Real => 524 return sys->sprint("%g", r.val); 525 Str => 526 return sys->sprint("%s", str(r.s)); 527 } 528} 529 530qual(ns: string, nm: string): string 531{ 532 if(ns != nil) 533 return ns+":"+nm; 534 return nm; 535} 536 537str(s: string): string 538{ 539 for(i := 0; i < len s; i++) 540 if(s[i] == '\'') 541 return sys->sprint("\"%s\"", s); 542 return sys->sprint("'%s'", s); 543} 544 545opname(o: int): string 546{ 547 case o { 548 One => return "!="; 549 Ole => return "<="; 550 Oge => return ">="; 551 Omul => return "*"; 552 Odiv => return " div "; 553 Omod => return " mod "; 554 Oand => return " and "; 555 Oor => return " or "; 556 Oneg => return "-"; 557 Ofilter => return " op_filter "; 558 Opath => return "/"; 559 * => return sys->sprint(" %c ", o); 560 } 561} 562 563Xstep.text(s: self ref Xstep): string 564{ 565 t := sys->sprint("%s::", Xstep.axisname(s.axis)); 566 case s.op { 567 Onametest => 568 if(s.ns == "*" && s.name == "*") 569 t += "*"; 570 else 571 t += qual(s.ns, s.name); 572 Onodetype => 573 if(s.arg != nil) 574 t += sys->sprint("%s(%s)", s.name, str(s.arg)); 575 else 576 t += sys->sprint("%s()", s.name); 577 } 578 for(l := s.preds; l != nil; l = tl l) 579 t += sys->sprint("[%s]", (hd l).text()); 580 return t; 581} 582 583Xstep.axisname(n: int): string 584{ 585 return lookname(axes, n); 586} 587 588# ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' | 589# NameTest | NodeType | Operator | FunctionName | AxisName | 590# Literal | Number | VariableReference 591# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>=' 592# MultiplyOperator ::= '*' 593# FunctionName ::= QName - NodeType 594# VariableReference ::= '$' QName 595# NameTest ::= '*' | NCName ':' '*' | QName 596# NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node' 597# 598 599Lex: type (int, string, string); 600 601Parse: adt { 602 r: ref Rd; 603 pb: list of Lex; # push back 604 605 look: fn(p: self ref Parse): int; 606 get: fn(p: self ref Parse): Lex; 607 unget: fn(p: self ref Parse, t: Lex); 608}; 609 610Parse.get(p: self ref Parse): Lex 611{ 612 if(p.pb != nil){ 613 h := hd p.pb; 614 p.pb = tl p.pb; 615 return h; 616 } 617 return lex(p.r); 618} 619 620Parse.look(p: self ref Parse): int 621{ 622 t := p.get(); 623 p.unget(t); 624 return t.t0; 625} 626 627Parse.unget(p: self ref Parse, t: Lex) 628{ 629 p.pb = t :: p.pb; 630} 631 632lex(r: ref Rd): Lex 633{ 634 l := lex0(r); 635 r.prev = l.t0; 636 return l; 637} 638 639# disambiguating rules are D1 to D3 640 641# D1. preceding token p && p not in {'@', '::', '(', '[', ',', Operator} then '*' is MultiplyOperator 642# and NCName must be OperatorName 643 644xop(t: int): int 645{ 646 case t { 647 -1 or 0 or '@' or '(' or '[' or ',' or Lop or Omul or 648 '/' or Ldslash or '|' or '+' or '-' or '=' or One or '<' or Ole or '>' or Oge or 649 Oand or Oor or Omod or Odiv or Laxis => 650 return 0; 651 } 652 return 1; 653} 654 655# UnaryExpr ::= UnionExpr | '-' UnaryExpr 656# ExprToken ::= ... | 657# NameTest | NodeType | Operator | FunctionName | AxisName | 658# Literal | Number | VariableReference 659# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>=' 660# MultiplyOperator ::= '*' 661 662lex0(r: ref Rd): Lex 663{ 664 while(isspace(r.look())) 665 r.get(); 666 case c := r.get() { 667 -1 or 668 '(' or ')' or '[' or ']' or '@' or ',' or '+' or '-' or '|' or '=' or ':' => 669 # singletons ('::' only valid after name, see below) 670 return (c, nil, nil); 671 '/' => 672 return subseq(r, '/', Ldslash, '/'); 673 '!' => 674 return subseq(r, '=', One, '!'); 675 '<' => 676 return subseq(r, '=', Ole, '<'); 677 '>' => 678 return subseq(r, '=', Oge, '>'); 679 '*' => 680 if(xop(r.prev)) 681 return (Omul, nil, nil); 682 return (c, nil, nil); 683 '.' => 684 case r.look() { 685 '0' to '9' => 686 (v, nil) := number(r, r.get()); 687 return (Lreal, v, nil); 688 '.' => 689 r.get(); 690 return (Ldotdot, nil, nil); 691 * => 692 return ('.', nil, nil); 693 } 694 '$' => 695 # variable reference 696 (ns, nm, i) := name(r.s, r.i); 697 if(ns == nil && nm == nil) 698 return (Lerror, nil, nil); 699 r.i = i; 700 return (Lvar, ns, nm); 701 '0' to '9' => 702 (v, f) := number(r, c); 703 if(f) 704 return (Lreal, v, nil); 705 return (Lint, v, nil); 706 '"' or '\'' => 707 return (Llit, literal(r, c), nil); 708 * => 709 if(isalnum(c) || c == '_'){ 710 # QName/NCName 711 r.unget(); 712 (ns, nm, i) := name(r.s, r.i); 713 if(ns == nil && nm == nil) 714 return (Lerror, nil, nil); 715 r.i = i; 716 if(xop(r.prev)){ 717 if(ns == nil){ 718 o := iskeywd(nm); 719 if(o != Laxis && o != Onodetype) 720 return (o, nil, nil); 721 } 722 return (Lop, ns, nm); 723 } 724 while(isspace(r.look())) 725 r.get(); 726 case r.look() { 727 '(' => # D2: NCName '(' =>NodeType or FunctionName 728 if(ns == nil && iskeywd(nm) == Onodetype) 729 return (Onodetype, nil, nm); 730 return (Lfn, ns, nm); # possibly NodeTest 731 ':' => # D3: NCName '::' => AxisName 732 r.get(); 733 case r.look() { 734 ':' => 735 if(ns == nil && look(axes, nm) != 0){ 736 r.get(); 737 return (Laxis, nm, nil); 738 } 739 '*' => 740 # NameTest ::= ... | NCName ':' '*' 741 if(ns == nil){ 742 r.get(); 743 return (Onametest, nm, "*"); 744 } 745 } 746 r.unget(); # put back the ':' 747 # NameTest ::= '*' | NCName ':' '*' | QName 748 } 749 return (Onametest, ns, nm); # actually NameTest 750 } 751 # unexpected character 752 } 753 return (Lerror, nil, nil); 754} 755 756subseq(r: ref Rd, a: int, t: int, e: int): Lex 757{ 758 if(r.look() != a) 759 return (e, nil, nil); 760 r.get(); 761 return (t, nil, nil); 762} 763 764# Literal ::= '"'[^"]*'"' | "'"[^']* "'" 765 766literal(r: ref Rd, delim: int): string 767{ 768 s: string; 769 while((c := r.get()) != delim){ 770 if(c < 0){ 771 synerr("missing string terminator"); 772 return s; 773 } 774 if(c) 775 s[len s] = c; # could slice r.s 776 } 777 return s; 778} 779 780# 781# Number ::= Digits('.' Digits?)? | '.' Digits 782# Digits ::= [0-9]+ 783# 784number(r: ref Rd, c: int): (string, int) 785{ 786 s: string; 787 for(; isdigit(c); c = r.get()) 788 s[len s] = c; 789 if(c != '.'){ 790 if(c >= 0) 791 r.unget(); 792 return (s, 0); 793 } 794 if(!isdigit(c = r.get())){ 795 if(c >= 0) 796 r.unget(); 797 r.unget(); # the '.' 798 return (s, 0); 799 } 800 s[len s] = '.'; 801 do{ 802 s[len s] = c; 803 }while(isdigit(c = r.get())); 804 if(c >= 0) 805 r.unget(); 806 return (s, 1); 807} 808 809isdigit(c: int): int 810{ 811 return c>='0' && c<='9'; 812} 813 814Rd: adt{ 815 s: string; 816 i: int; 817 prev: int; # previous token 818 819 get: fn(r: self ref Rd): int; 820 look: fn(r: self ref Rd): int; 821 unget: fn(r: self ref Rd); 822}; 823 824Rd.get(r: self ref Rd): int 825{ 826 if(r.i >= len r.s) 827 return -1; 828 return r.s[r.i++]; 829} 830 831Rd.look(r: self ref Rd): int 832{ 833 if(r.i >= len r.s) 834 return -1; 835 return r.s[r.i]; 836} 837 838Rd.unget(r: self ref Rd) 839{ 840 if(r.i > 0) 841 r.i--; 842} 843 844rev[T](l: list of T): list of T 845{ 846 rl: list of T; 847 for(; l != nil; l = tl l) 848 rl = hd l :: rl; 849 return rl; 850} 851 852synerr(s: string) 853{ 854 raise "syntax error: "+s; 855} 856 857# to do: 858# dictionary? 859