From a3f1a7320ba0c68c9e76ce5e5691ac180105c3c2 Mon Sep 17 00:00:00 2001 From: Faiyam Rahman Date: Mon, 15 Oct 2018 03:48:53 -0400 Subject: [PATCH] Added not reserved keyword. Works for both not in and not like (#68) * Added not reserved keyword. Functions for both not in and not like * removed all vestiges of T_NOT_OR_T_LIKE * Deleting IDE files (1) * Deleting IDE files (2) * Deleting IDE files (3) * Deleting IDE files (4) * Deleting IDE files (5) * Fixing spaces on tokens.go --- lexical/lexemes.go | 1 + lexical/lexical.go | 2 + lexical/lexical_test.go | 5 +- lexical/tokens.go | 2 + parser/ast.go | 8 +++ parser/parser.go | 17 ++++- parser/parser_test.go | 148 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 181 insertions(+), 2 deletions(-) diff --git a/lexical/lexemes.go b/lexical/lexemes.go index 600cfd7..1379c83 100644 --- a/lexical/lexemes.go +++ b/lexical/lexemes.go @@ -12,3 +12,4 @@ const L_IN = "in" const L_ASC = "asc" const L_DESC = "desc" const L_LIKE = "like" +const L_NOT = "not" diff --git a/lexical/lexical.go b/lexical/lexical.go index 1435254..8b3c3cb 100644 --- a/lexical/lexical.go +++ b/lexical/lexical.go @@ -220,6 +220,8 @@ func lexemeToToken(lexeme string) uint8 { return T_DESC case L_LIKE: return T_LIKE + case L_NOT: + return T_NOT } return T_ID } diff --git a/lexical/lexical_test.go b/lexical/lexical_test.go index 0907945..fa89dc6 100644 --- a/lexical/lexical_test.go +++ b/lexical/lexical_test.go @@ -127,7 +127,7 @@ func TestErrorUnrecognizeChar(t *testing.T) { func TestReservedWords(t *testing.T) { setUp() - source = "SELECT from WHEre in " + source = "SELECT from WHEre in not" char = nextChar() var token uint8 @@ -144,6 +144,9 @@ func TestReservedWords(t *testing.T) { token, _ = Token() assertToken(t, token, T_IN) + token, _ = Token() + assertToken(t, token, T_NOT) + token, _ = Token() assertToken(t, token, T_EOF) } diff --git a/lexical/tokens.go b/lexical/tokens.go index 3658347..6a51461 100644 --- a/lexical/tokens.go +++ b/lexical/tokens.go @@ -26,6 +26,7 @@ const T_PARENTH_R = 23 const T_IN = 24 const T_ASC = 25 const T_LIKE = 26 +const T_NOT = 27 const T_EOF = 0 const T_FUCK = 66 @@ -58,6 +59,7 @@ func allocMapTokenNames() { T_IN: "T_IN", T_EOF: "T_EOF", T_ASC: "T_ASC", + T_NOT: "T_NOT", } } } diff --git a/parser/ast.go b/parser/ast.go index 43c1fc3..0d61381 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -57,6 +57,7 @@ type NodeAdapterBinToConst struct { type NodeIn struct { leftValue NodeExpr rightValue NodeExpr + Not bool } type NodeEqual struct { @@ -73,6 +74,7 @@ type NodeLike struct { leftValue NodeExpr rightValue NodeExpr Pattern *regexp.Regexp + Not bool } type NodeGreater struct { @@ -129,6 +131,9 @@ func (e *NodeEmpty) Run() { } func (n *NodeIn) Assertion(lvalue string, rvalue string) bool { + if n.Not { + return !strings.Contains(rvalue, lvalue) + } return strings.Contains(rvalue, lvalue) } @@ -210,6 +215,9 @@ func (n *NodeNotEqual) LeftValue() NodeExpr { // LIKE func (n *NodeLike) Assertion(lvalue string, rvalue string) bool { + if n.Not { + return !n.Pattern.MatchString(lvalue) + } return n.Pattern.MatchString(lvalue) } diff --git a/parser/parser.go b/parser/parser.go index 7ca9e2f..b260b6e 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -341,7 +341,7 @@ func gWC3(eating bool) (NodeExpr, error) { return expr, nil } -// where cond 'equal', 'in', 'like' and 'not equal' +// where cond 'equal', 'in', 'not in', 'like', 'not like' and 'not equal' func gWC4(eating bool) (NodeExpr, error) { if eating { token, err := lexical.Token() @@ -355,6 +355,19 @@ func gWC4(eating bool) (NodeExpr, error) { return nil, err } + var notBool bool + if look_ahead == lexical.T_NOT { + notBool = true + token, err := lexical.Token() + if err != nil { + return nil, err + } + look_ahead = token + if look_ahead != lexical.T_LIKE && look_ahead != lexical.T_IN { + return nil, throwSyntaxError(lexical.T_NOT, look_ahead) + } + } + switch look_ahead { case lexical.T_EQUAL: op := new(NodeEqual) @@ -387,6 +400,7 @@ func gWC4(eating bool) (NodeExpr, error) { // we don't need to compile for every row rx := strings.Replace(expr2.(*NodeLiteral).Value(), "%", "(.*)", -1) op.Pattern, err2 = regexp.Compile(rx) + op.Not = notBool return op, err2 case lexical.T_IN: op := new(NodeIn) @@ -396,6 +410,7 @@ func gWC4(eating bool) (NodeExpr, error) { return nil, err2 } op.SetRightValue(expr2) + op.Not = notBool return op, nil } diff --git a/parser/parser_test.go b/parser/parser_test.go index 66b89ab..0c4535b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -230,6 +230,154 @@ func TestWhereWithNotEqualCompare(t *testing.T) { } } +func TestWhereWithIn(t *testing.T) { + New("Select message from commits where 'react' in message") + + ast, err := AST() + if err != nil { + t.Errorf(err.Error()) + return + } + + selectNode := ast.Child.(*NodeSelect) + w := selectNode.Where + if w == nil { + t.Errorf("should have where node") + } + + if reflect.TypeOf(w) != reflect.TypeOf(new(NodeIn)) { + t.Errorf("should be a NodeIn") + } + + notBool := w.(*NodeIn).Not + if notBool == true { + t.Errorf("Not bool should be set to false") + } + + lValue := w.LeftValue().(*NodeLiteral) + rValue := w.RightValue().(*NodeId) + if lValue.Value() != "react" { + t.Errorf("LValue should be 'react'") + } + + if rValue.Value() != "message" { + t.Errorf("RValue should be 'message'") + } + + + +} + +func TestWhereWithNotIn(t *testing.T) { + New("Select message from commits where 'react' not in message") + + ast, err := AST() + if err != nil { + t.Errorf(err.Error()) + return + } + + selectNode := ast.Child.(*NodeSelect) + w := selectNode.Where + if w == nil { + t.Errorf("should have where node") + } + + if reflect.TypeOf(w) != reflect.TypeOf(new(NodeIn)) { + t.Errorf("should be a NodeIn") + } + + notBool := w.(*NodeIn).Not + if notBool == false { + t.Errorf("Not bool should be set to true") + } + + lValue := w.LeftValue().(*NodeLiteral) + rValue := w.RightValue().(*NodeId) + if lValue.Value() != "react" { + t.Errorf("LValue should be 'react'") + } + + if rValue.Value() != "message" { + t.Errorf("RValue should be 'message'") + } + +} + +func TestWhereWithLike(t *testing.T) { + New("Select author, message from commits where message like '%B'") + + ast, err := AST() + if err != nil { + t.Errorf(err.Error()) + return + } + + selectNode := ast.Child.(*NodeSelect) + w := selectNode.Where + if w == nil { + t.Errorf("should have where node") + } + + if reflect.TypeOf(w) != reflect.TypeOf(new(NodeLike)) { + t.Errorf("should be a NodeLike") + } + + notBool := w.(*NodeLike).Not + if notBool == true { + t.Errorf("Not bool should be set to false") + } + + lValue := w.LeftValue().(*NodeId) + rValue := w.RightValue().(*NodeLiteral) + if lValue.Value() != "message" { + t.Errorf("LValue should be 'message'") + } + + es := `Rvalue should be &B` + if rValue.Value() != "%B" { + t.Errorf(es) + } + +} + +func TestWhereWithNotLike(t *testing.T) { + New("Select author, message from commits where message not like '%B'") + + ast, err := AST() + if err != nil { + t.Errorf(err.Error()) + return + } + + selectNode := ast.Child.(*NodeSelect) + w := selectNode.Where + if w == nil { + t.Errorf("should have where node") + } + + if reflect.TypeOf(w) != reflect.TypeOf(new(NodeLike)) { + t.Errorf("should be a NodeLike") + } + + notBool := w.(*NodeLike).Not + if notBool == false { + t.Errorf("Not bool should be set to true") + } + + lValue := w.LeftValue().(*NodeId) + rValue := w.RightValue().(*NodeLiteral) + if lValue.Value() != "message" { + t.Errorf("LValue should be 'message'") + } + + es := `Rvalue should be &B` + if rValue.Value() != "%B" { + t.Errorf(es) + } + +} + func TestWhereWithGreater(t *testing.T) { New("select * from commits where date > '2014-05-12 00:00:00' ")