github.com/gofiber/pug@v1.0.1/jade_lex.go (about) 1 package jade 2 3 import ( 4 "strings" 5 ) 6 7 func lexIndents(l *lexer) stateFn { 8 d := l.indents() 9 if d == -1 { 10 l.depth = 0 11 l.emit(itemEmptyLine) 12 } else { 13 l.depth = d 14 l.emit(itemIdent) 15 } 16 return lexTags 17 } 18 func (l *lexer) indents() (depth int) { 19 for { 20 switch l.next() { 21 case ' ': 22 depth += 1 23 case '\t': 24 depth += TabSize 25 case '\r': 26 // skip 27 case '\n': 28 return -1 29 default: 30 l.backup() 31 return 32 } 33 } 34 } 35 36 func lexEndLine(l *lexer) stateFn { 37 switch r := l.next(); { 38 case r == '\r': 39 if l.next() == '\n' { 40 l.emit(itemEndL) 41 return lexIndents 42 } 43 return l.errorf("lexTags: standalone '\\r' ") 44 case r == '\n': 45 l.emit(itemEndL) 46 return lexIndents 47 case r == eof: 48 l.depth = 0 49 l.emit(itemEOF) 50 return nil 51 default: 52 return l.errorf("lexEndLine: unexpected token %#U `%s`", r, string(r)) 53 } 54 } 55 56 // lexTags scans tags. 57 func lexTags(l *lexer) stateFn { 58 switch r := l.next(); { 59 60 case isEndOfLine(r), r == eof: 61 l.backup() 62 return lexEndLine 63 case r == ' ' || r == '\t': 64 l.backup() 65 return lexIndents 66 // 67 // 68 case r == '.': 69 n := l.skipSpaces() 70 if n == 0 { 71 l.emit(itemDiv) 72 return lexClass 73 } 74 if n == -1 { 75 l.ignore() 76 return lexLongText 77 } 78 return l.errorf("lexTags: class name cannot start with a space.") 79 case r == '#': 80 l.emit(itemDiv) 81 return lexID 82 case r == ':': 83 l.ignore() 84 if l.emitWordByType(itemFilter) { 85 r = l.next() 86 if r == ':' { 87 l.ignore() 88 l.emitWordByType(itemFilterSubf) 89 r = l.next() 90 } 91 if r == '(' { 92 l.ignore() 93 l.toStopRune(')', true) 94 l.emit(itemFilterArgs) 95 l.next() 96 l.ignore() 97 } else { 98 l.backup() 99 } 100 return lexFilter 101 } 102 return l.errorf("lexTags: expect filter name") 103 case r == '|': 104 r = l.next() 105 if r != ' ' { 106 l.backup() 107 } 108 l.ignore() 109 return lexText 110 case r == '<': 111 l.emitLineByType(itemHTMLTag) 112 return lexEndLine 113 case r == '+': 114 l.skipSpaces() 115 l.ignore() 116 if l.emitWordByType(itemMixinCall) { 117 return lexAfterTag 118 } 119 return l.errorf("lexTags: expect mixin name") 120 case r == '/': 121 return lexComment 122 case r == '-': 123 l.ignore() 124 return lexCode 125 case r == '=': 126 l.skipSpaces() 127 l.ignore() 128 l.emitLineByType(itemCodeBuffered) 129 return lexEndLine 130 case r == '!': 131 np := l.next() 132 if np == '=' { 133 l.skipSpaces() 134 l.ignore() 135 l.emitLineByType(itemCodeUnescaped) 136 return lexEndLine 137 } 138 if np == '!' && l.next() == '!' && l.depth == 0 { 139 l.ignore() 140 if l.skipSpaces() != -1 { 141 l.emitLineByType(itemDoctype) 142 } else { 143 l.emit(itemDoctype) 144 } 145 return lexEndLine 146 } 147 return l.errorf("expect '=' after '!'") 148 case isAlphaNumeric(r): 149 l.backup() 150 return lexTagName 151 default: 152 return l.errorf("lexTags: unexpected token %#U `%s`", r, string(r)) 153 } 154 } 155 156 // 157 // 158 159 func lexID(l *lexer) stateFn { 160 if l.emitWordByType(itemID) { 161 return lexAfterTag 162 } 163 return l.errorf("lexID: expect id name") 164 } 165 func lexClass(l *lexer) stateFn { 166 if l.emitWordByType(itemClass) { 167 return lexAfterTag 168 } 169 return l.errorf("lexClass: expect class name") 170 } 171 172 func lexFilter(l *lexer) stateFn { 173 l.multiline() 174 l.emit(itemFilterText) 175 return lexIndents 176 } 177 178 func lexCode(l *lexer) stateFn { 179 if l.skipSpaces() == -1 { 180 l.multiline() 181 l.emit(itemCode) 182 return lexIndents 183 } else { 184 l.ignore() 185 l.emitLineByType(itemCode) 186 return lexEndLine 187 } 188 } 189 func lexComment(l *lexer) stateFn { 190 sp := l.next() 191 tp := l.peek() 192 if sp == '/' { 193 if tp == '-' { 194 l.multiline() 195 l.ignore() 196 return lexIndents 197 } else { 198 l.ignore() 199 l.multiline() 200 l.emit(itemComment) 201 return lexIndents 202 } 203 } 204 return l.errorf("lexComment: unexpected token '%#U' expect '/'", sp) 205 } 206 207 // 208 // 209 210 func lexText(l *lexer) stateFn { 211 if l.skipSpaces() == -1 { 212 l.ignore() 213 return lexEndLine 214 } 215 return text(l) 216 } 217 func lexLongText(l *lexer) stateFn { 218 l.longtext = true 219 return text(l) 220 } 221 func text(l *lexer) stateFn { 222 for { 223 switch r := l.next(); { 224 case r == '\\': 225 l.next() 226 continue 227 case r == '#': 228 sp := l.peek() 229 if sp == '[' { 230 l.backup() 231 if l.pos > l.start { 232 l.emit(itemText) 233 } 234 l.next() 235 l.next() 236 l.skipSpaces() 237 l.interpolation += 1 238 l.depth += 1 239 // l.emit(itemInterpolation) 240 l.ignore() 241 return lexTags 242 } 243 if sp == '{' { 244 l.interpol(itemCodeBuffered) 245 } 246 // case r == '$': 247 // sp := l.peek() 248 // if sp == '{' { 249 // l.interpol(itemCodeBuffered) 250 // } 251 case r == '!': 252 sp := l.peek() 253 if sp == '{' { 254 l.interpol(itemCodeUnescaped) 255 } 256 case r == ']': 257 if l.interpolation > 0 { 258 l.backup() 259 if l.pos > l.start { 260 l.emit(itemText) 261 } 262 l.next() 263 // l.emit(itemInterpolationEnd) 264 l.ignore() 265 l.interpolation -= 1 266 l.depth -= 1 267 } 268 case r == eof: 269 l.backup() 270 l.emit(itemText) 271 return lexEndLine 272 case r == '\n': 273 if l.longtext { 274 var ( 275 indent int 276 pos Pos 277 ) 278 l.backup() 279 pos = l.pos 280 l.next() 281 indent = l.indents() 282 if indent != -1 { 283 if indent < l.depth { 284 l.pos = pos 285 if l.pos > l.start { 286 l.emit(itemText) 287 } 288 l.longtext = false 289 return lexIndents 290 } 291 } else { 292 l.backup() 293 } 294 } else { 295 l.backup() 296 if l.pos > l.start { 297 l.emit(itemText) 298 } 299 return lexIndents 300 } 301 } 302 } 303 } 304 func (l *lexer) interpol(item itemType) { 305 l.backup() 306 if l.pos > l.start { 307 l.emit(itemText) 308 } 309 l.next() 310 l.next() 311 l.skipSpaces() 312 l.ignore() 313 Loop: 314 for { 315 switch r := l.next(); { 316 case r == '`': 317 l.toStopRune('`', false) 318 case r == '"': 319 l.toStopRune('"', false) 320 case r == '\'': 321 l.toStopRune('\'', false) 322 case r == '\n', r == eof: 323 l.backup() 324 l.errorf("interpolation error: expect '}'") 325 return 326 case r == '}': 327 break Loop 328 } 329 } 330 l.backup() 331 l.emit(item) 332 l.next() 333 l.ignore() 334 } 335 336 func lexTagName(l *lexer) stateFn { 337 for { 338 switch r := l.next(); { 339 case isAlphaNumeric(r): 340 // absorb. 341 default: 342 l.backup() 343 word := l.input[l.start:l.pos] 344 if w, ok := key[word]; ok { 345 switch w { 346 case itemElse: 347 l.emit(w) 348 l.skipSpaces() 349 l.ignore() 350 return lexTags 351 case itemDoctype, itemExtends: 352 if l.depth == 0 { 353 ss := l.skipSpaces() 354 l.ignore() 355 if ss != -1 { 356 l.emitLineByType(w) 357 } else if w == itemDoctype { 358 l.emit(w) 359 } else { 360 return l.errorf("lexTagName: itemExtends need path ") 361 } 362 return lexEndLine 363 } else { 364 l.emit(itemTag) 365 } 366 case itemBlock: 367 sp := l.skipSpaces() 368 l.ignore() 369 if sp == -1 { 370 l.emit(itemMixinBlock) 371 } else if strings.HasPrefix(l.input[l.pos:], "prepend ") { 372 l.toStopRune(' ', true) 373 l.skipSpaces() 374 l.ignore() 375 l.emitLineByType(itemBlockPrepend) 376 } else if strings.HasPrefix(l.input[l.pos:], "append ") { 377 l.toStopRune(' ', true) 378 l.skipSpaces() 379 l.ignore() 380 l.emitLineByType(itemBlockAppend) 381 } else { 382 l.emitLineByType(itemBlock) 383 } 384 return lexEndLine 385 case itemBlockAppend, itemBlockPrepend, 386 itemIf, itemUnless, itemCase, 387 itemEach, itemWhile, itemFor, 388 itemInclude: 389 390 l.skipSpaces() 391 l.ignore() 392 l.emitLineByType(w) 393 return lexEndLine 394 case itemMixin: 395 l.skipSpaces() 396 l.ignore() 397 l.emitWordByType(w) 398 return lexAfterTag 399 case itemCaseWhen: 400 l.skipSpaces() 401 l.ignore() 402 l.toStopRune(':', true) 403 l.emit(w) 404 return lexAfterTag 405 default: 406 l.emit(w) 407 } 408 } else { 409 l.emit(itemTag) 410 } 411 return lexAfterTag 412 } 413 } 414 } 415 416 func lexAfterTag(l *lexer) stateFn { 417 switch r := l.next(); { 418 case r == '(': 419 l.emit(itemAttrStart) 420 return lexAttr 421 case r == '/': 422 l.emit(itemTagEnd) 423 return lexAfterTag 424 case r == ':': 425 l.skipSpaces() 426 l.ignore() 427 l.depth += 1 428 return lexTags 429 case r == ' ' || r == '\t': 430 l.ignore() 431 l.depth += 1 432 return lexText 433 case r == ']': 434 if l.interpolation > 0 { 435 l.ignore() 436 if l.pos > l.start { 437 l.emit(itemText) 438 } 439 l.interpolation -= 1 440 l.depth -= 1 441 if l.longtext { 442 return lexLongText 443 } else { 444 return lexText 445 } 446 } 447 return l.errorf("lexAfterTag: %#U", r) 448 case r == '=': 449 l.skipSpaces() 450 l.ignore() 451 l.depth += 1 452 l.emitLineByType(itemCodeBuffered) 453 return lexEndLine 454 case r == '!': 455 if l.next() == '=' { 456 l.skipSpaces() 457 l.ignore() 458 l.depth += 1 459 l.emitLineByType(itemCodeUnescaped) 460 return lexEndLine 461 } 462 return l.errorf("expect '=' after '!'") 463 case r == '#': 464 l.ignore() 465 return lexID 466 case r == '&': 467 l.toStopRune(')', false) 468 l.ignore() // TODO: now ignore div(data-bar="foo")&attributes({'data-foo': 'baz'}) 469 return lexAfterTag 470 case r == '.': 471 switch l.skipSpaces() { 472 case 0: 473 l.ignore() 474 return lexClass 475 case -1: 476 if sp := l.next(); sp != eof { 477 l.ignore() 478 l.depth += 1 479 return lexLongText 480 } 481 return lexEndLine 482 default: 483 l.ignore() 484 l.depth += 1 485 return lexText 486 } 487 case isEndOfLine(r), r == eof: 488 l.backup() 489 return lexEndLine 490 default: 491 return l.errorf("lexAfterTag: %#U", r) 492 } 493 } 494 495 // 496 // 497 498 func lexAttr(l *lexer) stateFn { 499 b1, b2, b3 := 0, 0, 0 500 for { 501 switch r := l.next(); { 502 case r == '"' || r == '\'': 503 l.toStopRune(r, false) 504 case r == '`': 505 for { 506 r = l.next() 507 if r == '`' { 508 break 509 } 510 } 511 case r == '(': 512 // b1 += 1 513 l.toStopRune(')', false) 514 case r == ')': 515 // b1 -= 1 516 // if b1 == -1 { 517 if b2 != 0 || b3 != 0 { 518 return l.errorf("lexAttrName: mismatched bracket") 519 } 520 l.backup() 521 if l.pos > l.start { 522 l.emit(itemAttr) 523 } 524 l.next() 525 l.emit(itemAttrEnd) 526 return lexAfterTag 527 // } 528 case r == '[': 529 b2 += 1 530 case r == ']': 531 b2 -= 1 532 if b2 == -1 { 533 return l.errorf("lexAttrName: mismatched bracket '['") 534 } 535 case r == '{': 536 b3 += 1 537 case r == '}': 538 b3 -= 1 539 if b3 == -1 { 540 return l.errorf("lexAttrName: mismatched bracket '{'") 541 } 542 case r == ' ' || r == '\t': 543 l.backup() 544 if l.pos > l.start { 545 l.emit(itemAttr) 546 } 547 l.skipSpaces() 548 l.emit(itemAttrSpace) 549 case r == '=': 550 if l.peek() == '=' { 551 l.toStopRune(' ', true) 552 l.emit(itemAttr) 553 continue 554 } 555 l.backup() 556 l.emit(itemAttr) 557 l.next() 558 l.emit(itemAttrEqual) 559 case r == '!': 560 if l.peek() == '=' { 561 l.backup() 562 l.emit(itemAttr) 563 l.next() 564 l.next() 565 l.emit(itemAttrEqualUn) 566 } 567 case r == ',' || r == '\n': 568 if b1 == 0 && b2 == 0 && b3 == 0 { 569 l.backup() 570 if l.pos > l.start { 571 l.emit(itemAttr) 572 } 573 l.next() 574 l.emit(itemAttrComma) 575 } 576 case r == eof: 577 return l.errorf("lexAttr: expected ')'") 578 } 579 } 580 } 581 582 // 583 // 584 // 585 // 586 // 587 // 588 // 589 // 590 // 591 // 592 593 func (l *lexer) emitWordByType(item itemType) bool { 594 for { 595 if !isAlphaNumeric(l.next()) { 596 l.backup() 597 break 598 } 599 } 600 if l.pos > l.start { 601 l.emit(item) 602 return true 603 } 604 return false 605 } 606 607 func (l *lexer) emitLineByType(item itemType) bool { 608 var r rune 609 for { 610 r = l.next() 611 if r == '\n' || r == '\r' || r == eof { 612 l.backup() 613 if l.pos > l.start { 614 l.emit(item) 615 return true 616 } 617 return false 618 } 619 } 620 } 621 622 // 623 624 func (l *lexer) skipSpaces() (out int) { 625 for { 626 switch l.next() { 627 case ' ', '\t': 628 out += 1 629 case '\n', eof: 630 l.backup() 631 return -1 632 default: 633 l.backup() 634 return 635 } 636 } 637 } 638 639 func (l *lexer) toStopRune(stopRune rune, backup bool) { 640 for { 641 switch r := l.next(); { 642 case r == stopRune: 643 if backup { 644 l.backup() 645 } 646 return 647 case r == eof || r == '\r' || r == '\n': 648 l.backup() 649 return 650 } 651 } 652 } 653 654 func (l *lexer) multiline() { 655 var ( 656 indent int 657 pos Pos 658 ) 659 for { 660 switch r := l.next(); { 661 case r == '\n': 662 l.backup() 663 pos = l.pos 664 l.next() 665 indent = l.indents() 666 if indent != -1 { 667 if indent <= l.depth { 668 l.pos = pos 669 return 670 } 671 } else { 672 l.backup() 673 } 674 case r == eof: 675 l.backup() 676 return 677 } 678 } 679 }