diff --git a/_future/ipel/README.md b/_future/ipel/README.md index dceed675..88399cd5 100644 --- a/_future/ipel/README.md +++ b/_future/ipel/README.md @@ -41,7 +41,7 @@ app.Int.Set("min", minValidator) ```go isVersionStrValidator := func() func(string) bool { - versions := []string("v1","v2") + versions := []string{"v1","v2"} return func(paramValue string) bool { for _, s := range versions { if s == paramValue { diff --git a/_future/ipel/ast/param.go b/_future/ipel/ast/param.go index db51c216..3384452b 100644 --- a/_future/ipel/ast/param.go +++ b/_future/ipel/ast/param.go @@ -11,14 +11,14 @@ const ( // /myparam ParamTypeAlphabetical // /myparam1/myparam2 - ParamPath + ParamTypePath ) var paramTypes = map[string]ParamType{ "int": ParamTypeInt, "string": ParamTypeString, "alphabetical": ParamTypeAlphabetical, - "path": ParamPath, + "path": ParamTypePath, // could be named also: // "tail": // "wild" diff --git a/_future/ipel/parser/parser.go b/_future/ipel/parser/parser.go index 188b2220..b5059dcd 100644 --- a/_future/ipel/parser/parser.go +++ b/_future/ipel/parser/parser.go @@ -2,6 +2,7 @@ package parser import ( "fmt" + "strconv" "strings" "gopkg.in/kataras/iris.v6/_future/ipel/ast" @@ -26,8 +27,20 @@ func (p *Parser) appendErr(format string, a ...interface{}) { p.errors = append(p.errors, fmt.Sprintf(format, a...)) } +const DefaultParamErrorCode = 404 + +func parseParamFuncArg(t token.Token) (a ast.ParamFuncArg, err error) { + if t.Type == token.INT { + return ast.ParamFuncArgToInt(t.Literal) + } + return t.Literal, nil +} + func (p *Parser) Parse() (*ast.ParamStatement, error) { stmt := new(ast.ParamStatement) + stmt.ErrorCode = DefaultParamErrorCode + // let's have them nilled stmt.Funcs = make([]ast.ParamFunc, 0) + lastParamFunc := ast.ParamFunc{} for { t := p.l.NextToken() if t.Type == token.EOF { @@ -45,11 +58,52 @@ func (p *Parser) Parse() (*ast.ParamStatement, error) { paramType := ast.LookupParamType(nextTok.Literal) if paramType == ast.ParamTypeUnExpected { p.appendErr("[%d:%d] unexpected parameter type: %s", t.Start, t.End, nextTok.Literal) + continue } + stmt.Type = paramType + // param func + case token.IDENT: + lastParamFunc.Name = t.Literal + case token.LPAREN: + argValTok := p.l.NextToken() + argVal, err := parseParamFuncArg(argValTok) + if err != nil { + p.appendErr("[%d:%d] expected param func argument to be an integer but got %s", t.Start, t.End, argValTok.Literal) + continue + } + + lastParamFunc.Args = append(lastParamFunc.Args, argVal) + case token.COMMA: + argValTok := p.l.NextToken() + argVal, err := parseParamFuncArg(argValTok) + if err != nil { + p.appendErr("[%d:%d] expected param func argument to be an integer but got %s", t.Start, t.End, argValTok.Literal) + continue + } + + lastParamFunc.Args = append(lastParamFunc.Args, argVal) + case token.RPAREN: + stmt.Funcs = append(stmt.Funcs, lastParamFunc) + lastParamFunc = ast.ParamFunc{} // reset + case token.ELSE: + errCodeTok := p.l.NextToken() + if errCodeTok.Type != token.INT { + p.appendErr("[%d:%d] expected error code to be an integer but got %s", t.Start, t.End, errCodeTok.Literal) + continue + } + errCode, err := strconv.Atoi(errCodeTok.Literal) + if err != nil { + // this is a bug on lexer if throws because we already check for token.INT + p.appendErr("[%d:%d] unexpected lexer error while trying to convert error code to an integer, %s", t.Start, t.End, err.Error()) + continue + } + stmt.ErrorCode = errCode + case token.RBRACE: + break case token.ILLEGAL: p.appendErr("[%d:%d] illegal token: %s", t.Start, t.End, t.Literal) default: - p.appendErr("[%d:%d] unexpected token type: %q", t.Start, t.End, t.Type) + p.appendErr("[%d:%d] unexpected token type: %q with value %s", t.Start, t.End, t.Type, t.Literal) } } diff --git a/_future/ipel/parser/parser_test.go b/_future/ipel/parser/parser_test.go index 8c328779..cc94d909 100644 --- a/_future/ipel/parser/parser_test.go +++ b/_future/ipel/parser/parser_test.go @@ -2,14 +2,14 @@ package parser import ( "fmt" + "reflect" "strings" "testing" + "gopkg.in/kataras/iris.v6/_future/ipel/ast" "gopkg.in/kataras/iris.v6/_future/ipel/lexer" ) -// Test is failing because we are not finished with the Parser yet -// 27/03 func TestParseError(t *testing.T) { // fail illegalChar := '$' @@ -46,3 +46,79 @@ func TestParseError(t *testing.T) { } // } + +func TestParse(t *testing.T) { + tests := []struct { + input string + valid bool + expectedStatement ast.ParamStatement + }{ + {"{id:int min(1) max(5) else 404}", true, + ast.ParamStatement{ + Name: "id", + Type: ast.ParamTypeInt, + Funcs: []ast.ParamFunc{ + ast.ParamFunc{ + Name: "min", + Args: []ast.ParamFuncArg{1}}, + ast.ParamFunc{ + Name: "max", + Args: []ast.ParamFuncArg{5}}, + }, + ErrorCode: 404, + }}, // 0 + {"{id:int range(1,5)}", true, + ast.ParamStatement{ + Name: "id", + Type: ast.ParamTypeInt, + Funcs: []ast.ParamFunc{ + ast.ParamFunc{ + Name: "range", + Args: []ast.ParamFuncArg{1, 5}}, + }, + ErrorCode: 404, + }}, // 1 + {"{file:path contains(.)}", true, + ast.ParamStatement{ + Name: "file", + Type: ast.ParamTypePath, + Funcs: []ast.ParamFunc{ + ast.ParamFunc{ + Name: "contains", + Args: []ast.ParamFuncArg{"."}}, + }, + ErrorCode: 404, + }}, // 2 + {"{username:alphabetical", true, + ast.ParamStatement{ + Name: "username", + Type: ast.ParamTypeAlphabetical, + ErrorCode: 404, + }}, // 3 + {"{username:thisianunexpected", false, + ast.ParamStatement{ + Name: "username", + Type: ast.ParamTypeUnExpected, + ErrorCode: 404, + }}, // 4 + } + + for i, tt := range tests { + l := lexer.New(tt.input) + p := New(l) + resultStmt, err := p.Parse() + + if tt.valid && err != nil { + t.Fatalf("tests[%d] - error %s", i, err.Error()) + } else if !tt.valid && err == nil { + t.Fatalf("tests[%d] - expected to be a failure", i) + } + + if resultStmt != nil { // is valid here + if !reflect.DeepEqual(tt.expectedStatement, *resultStmt) { + t.Fatalf("tests[%d] - wrong statement, expected and result differs. Details:\n%#v\n%#v", i, tt.expectedStatement, *resultStmt) + } + } + + } +}