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  }