parser.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package parser
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "github.com/kataras/iris/v12/macro/interpreter/ast"
  7. "github.com/kataras/iris/v12/macro/interpreter/lexer"
  8. "github.com/kataras/iris/v12/macro/interpreter/token"
  9. )
  10. // Parse takes a route "fullpath"
  11. // and returns its param statements
  12. // or an error if failed.
  13. func Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement, error) {
  14. if len(paramTypes) == 0 {
  15. return nil, fmt.Errorf("empty parameter types")
  16. }
  17. pathParts := strings.Split(fullpath, "/")
  18. p := new(ParamParser)
  19. statements := make([]*ast.ParamStatement, 0)
  20. for i, s := range pathParts {
  21. if s == "" { // if starts with /
  22. continue
  23. }
  24. // if it's not a named path parameter of the new syntax then continue to the next
  25. // if s[0] != lexer.Begin || s[len(s)-1] != lexer.End {
  26. // continue
  27. // }
  28. // Modified to show an error on a certain invalid action.
  29. if s[0] != lexer.Begin {
  30. continue
  31. }
  32. if s[len(s)-1] != lexer.End {
  33. if idx := strings.LastIndexByte(s, lexer.End); idx > 2 && idx < len(s)-1 /* at least {x}*/ {
  34. // Do NOT allow something more than a dynamic path parameter in the same path segment,
  35. // e.g. /{param}-other-static-part/. See #2024.
  36. // this allows it but NO (see trie insert): s = s[0 : idx+1]
  37. return nil, fmt.Errorf("%s: invalid path part: dynamic path parameter and other parameters or static parts are not allowed in the same exact request path part, use the {regexp} function alone instead", s)
  38. } else {
  39. continue
  40. }
  41. }
  42. p.Reset(s)
  43. stmt, err := p.Parse(paramTypes)
  44. if err != nil {
  45. // exit on first error
  46. return nil, err
  47. }
  48. // if we have param type path but it's not the last path part
  49. if ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {
  50. return nil, fmt.Errorf("%s: parameter type \"%s\" should be registered to the very end of a path once", s, stmt.Type.Indent())
  51. }
  52. statements = append(statements, stmt)
  53. }
  54. return statements, nil
  55. }
  56. // ParamParser is the parser
  57. // which is being used by the Parse function
  58. // to parse path segments one by one
  59. // and return their parsed parameter statements (param name, param type its functions and the inline route's functions).
  60. type ParamParser struct {
  61. src string
  62. errors []string
  63. }
  64. // NewParamParser receives a "src" of a single parameter
  65. // and returns a new ParamParser, ready to Parse.
  66. func NewParamParser(src string) *ParamParser {
  67. p := new(ParamParser)
  68. p.Reset(src)
  69. return p
  70. }
  71. // Reset resets this ParamParser,
  72. // reset the errors and set the source to the input "src".
  73. func (p *ParamParser) Reset(src string) {
  74. p.src = src
  75. p.errors = []string{}
  76. }
  77. func (p *ParamParser) appendErr(format string, a ...interface{}) {
  78. p.errors = append(p.errors, fmt.Sprintf(format, a...))
  79. }
  80. const (
  81. // DefaultParamErrorCode is the default http error code, 404 not found,
  82. // per-parameter. An error code can be set via
  83. // the "else" keyword inside a route's path.
  84. DefaultParamErrorCode = 404
  85. )
  86. func (p ParamParser) Error() error {
  87. if len(p.errors) > 0 {
  88. return fmt.Errorf(strings.Join(p.errors, "\n"))
  89. }
  90. return nil
  91. }
  92. // Parse parses the p.src based on the given param types and returns its param statement
  93. // and an error on failure.
  94. func (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, error) {
  95. l := lexer.New(p.src)
  96. stmt := &ast.ParamStatement{
  97. ErrorCode: DefaultParamErrorCode,
  98. Type: ast.GetMasterParamType(paramTypes...),
  99. Src: p.src,
  100. }
  101. lastParamFunc := ast.ParamFunc{}
  102. for {
  103. t := l.NextToken()
  104. if t.Type == token.EOF {
  105. if stmt.Name == "" {
  106. p.appendErr("[1:] parameter name is missing")
  107. }
  108. break
  109. }
  110. switch t.Type {
  111. case token.LBRACE:
  112. // can accept only letter or number only.
  113. nextTok := l.NextToken()
  114. stmt.Name = nextTok.Literal
  115. case token.COLON:
  116. // type can accept both letters and numbers but not symbols ofc.
  117. nextTok := l.NextToken()
  118. paramType, found := ast.LookupParamType(nextTok.Literal, paramTypes...)
  119. if !found {
  120. p.appendErr("[%d:%d] unexpected parameter type: %s", t.Start, t.End, nextTok.Literal)
  121. }
  122. stmt.Type = paramType
  123. // param func
  124. case token.IDENT:
  125. lastParamFunc.Name = t.Literal
  126. case token.LPAREN:
  127. // param function without arguments ()
  128. if l.PeekNextTokenType() == token.RPAREN {
  129. // do nothing, just continue to the RPAREN
  130. continue
  131. }
  132. argValTok := l.NextDynamicToken() // catch anything from "(" and forward, until ")", because we need to
  133. // be able to use regex expression as a macro type's func argument too.
  134. // fmt.Printf("argValTok: %#v\n", argValTok)
  135. // fmt.Printf("argVal: %#v\n", argVal)
  136. lastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)
  137. case token.COMMA:
  138. argValTok := l.NextToken()
  139. lastParamFunc.Args = append(lastParamFunc.Args, argValTok.Literal)
  140. case token.RPAREN:
  141. stmt.Funcs = append(stmt.Funcs, lastParamFunc)
  142. lastParamFunc = ast.ParamFunc{} // reset
  143. case token.ELSE:
  144. errCodeTok := l.NextToken()
  145. if errCodeTok.Type != token.INT {
  146. p.appendErr("[%d:%d] expected error code to be an integer but got %s", t.Start, t.End, errCodeTok.Literal)
  147. continue
  148. }
  149. errCode, err := strconv.Atoi(errCodeTok.Literal)
  150. if err != nil {
  151. // this is a bug on lexer if throws because we already check for token.INT
  152. p.appendErr("[%d:%d] unexpected lexer error while trying to convert error code to an integer, %s", t.Start, t.End, err.Error())
  153. continue
  154. }
  155. stmt.ErrorCode = errCode
  156. case token.RBRACE:
  157. // check if } but not {
  158. if stmt.Name == "" {
  159. p.appendErr("[%d:%d] illegal token: }, forgot '{' ?", t.Start, t.End)
  160. }
  161. case token.ILLEGAL:
  162. p.appendErr("[%d:%d] illegal token: %s", t.Start, t.End, t.Literal)
  163. default:
  164. p.appendErr("[%d:%d] unexpected token type: %q with value %s", t.Start, t.End, t.Type, t.Literal)
  165. }
  166. }
  167. return stmt, p.Error()
  168. }