diff --git a/pkg/logql/expr.go b/pkg/logql/expr.go
index 70c2cd7a8d97a188695b88fb9546a72e2d5ad2e9..16e485923e8b9ee7cd75c54fe823cf846fd3d009 100644
--- a/pkg/logql/expr.go
+++ b/pkg/logql/expr.go
@@ -15,6 +15,8 @@ import (
 type exprSymType struct {
 	yys      int
 	Expr     Expr
+	Filter   labels.MatchType
+	Selector []*labels.Matcher
 	Matchers []*labels.Matcher
 	Matcher  *labels.Matcher
 	str      string
@@ -61,56 +63,59 @@ const exprEofCode = 1
 const exprErrCode = 2
 const exprInitialStackSize = 16
 
-//line pkg/logql/expr.y:51
+//line pkg/logql/expr.y:65
 
 //line yacctab:1
 var exprExca = [...]int{
 	-1, 1,
 	1, -1,
 	-2, 0,
+	-1, 2,
+	1, 1,
+	-2, 0,
 }
 
 const exprPrivate = 57344
 
-const exprLast = 26
+const exprLast = 30
 
 var exprAct = [...]int{
 
-	8, 10, 16, 17, 7, 3, 6, 18, 19, 20,
-	21, 4, 5, 26, 25, 24, 23, 15, 14, 22,
-	13, 12, 11, 9, 2, 1,
+	6, 13, 20, 4, 29, 18, 28, 10, 14, 9,
+	21, 22, 23, 24, 7, 8, 17, 19, 27, 16,
+	26, 25, 15, 12, 11, 14, 3, 5, 2, 1,
 }
 var exprPact = [...]int{
 
-	-7, -1000, -5, 18, 16, 15, 13, 12, -1000, -11,
-	-1000, -1, -1000, -1000, -1000, -1000, -1000, 18, 11, 10,
-	9, 8, -1000, -1000, -1000, -1000, -1000,
+	-9, -1000, -2, -1000, 21, 17, -1000, -1000, -1000, -1000,
+	-1000, 3, -11, -1000, 2, -1000, -1000, -1000, -1000, 4,
+	-1000, 15, 13, 1, -1, -1000, -1000, -1000, -1000, -1000,
 }
 var exprPgo = [...]int{
 
-	0, 25, 24, 23, 1,
+	0, 29, 28, 27, 26, 24, 1,
 }
 var exprR1 = [...]int{
 
-	0, 1, 2, 2, 2, 2, 2, 2, 2, 3,
-	3, 4, 4, 4, 4,
+	0, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+	4, 4, 4, 5, 5, 6, 6, 6, 6,
 }
 var exprR2 = [...]int{
 
-	0, 1, 3, 3, 3, 3, 3, 2, 2, 1,
-	3, 3, 3, 3, 3,
+	0, 1, 1, 3, 3, 2, 1, 1, 1, 1,
+	3, 3, 3, 1, 3, 3, 3, 3, 3,
 }
 var exprChk = [...]int{
 
-	-1000, -1, -2, 12, 16, 17, 11, 9, 5, -3,
-	-4, 4, 5, 5, 5, 5, 13, 14, 8, 9,
-	10, 11, -4, 5, 5, 5, 5,
+	-1000, -1, -2, -4, 12, -3, 2, 16, 17, 11,
+	9, -5, 2, -6, 4, 5, 2, 13, 2, 14,
+	13, 8, 9, 10, 11, -6, 5, 5, 5, 5,
 }
 var exprDef = [...]int{
 
-	0, -2, 1, 0, 7, 0, 0, 0, 8, 0,
-	9, 0, 3, 4, 5, 6, 2, 0, 0, 0,
-	0, 0, 10, 11, 12, 13, 14,
+	0, -2, -2, 2, 0, 0, 5, 6, 7, 8,
+	9, 0, 0, 13, 0, 3, 4, 10, 11, 0,
+	12, 0, 0, 0, 0, 14, 15, 16, 17, 18,
 }
 var exprTok1 = [...]int{
 
@@ -464,85 +469,96 @@ exprdefault:
 
 	case 1:
 		exprDollar = exprS[exprpt-1 : exprpt+1]
-//line pkg/logql/expr.y:28
+//line pkg/logql/expr.y:32
 		{
 			exprlex.(*lexer).expr = exprDollar[1].Expr
 		}
 	case 2:
-		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:31
+		exprDollar = exprS[exprpt-1 : exprpt+1]
+//line pkg/logql/expr.y:35
 		{
-			exprVAL.Expr = &matchersExpr{matchers: exprDollar[2].Matchers}
+			exprVAL.Expr = &matchersExpr{matchers: exprDollar[1].Selector}
 		}
 	case 3:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:32
+//line pkg/logql/expr.y:36
 		{
-			exprVAL.Expr = NewFilterExpr(exprDollar[1].Expr, labels.MatchRegexp, exprDollar[3].str)
+			exprVAL.Expr = NewFilterExpr(exprDollar[1].Expr, exprDollar[2].Filter, exprDollar[3].str)
 		}
-	case 4:
-		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:33
+	case 6:
+		exprDollar = exprS[exprpt-1 : exprpt+1]
+//line pkg/logql/expr.y:42
 		{
-			exprVAL.Expr = NewFilterExpr(exprDollar[1].Expr, labels.MatchEqual, exprDollar[3].str)
+			exprVAL.Filter = labels.MatchRegexp
 		}
-	case 5:
-		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:34
+	case 7:
+		exprDollar = exprS[exprpt-1 : exprpt+1]
+//line pkg/logql/expr.y:43
 		{
-			exprVAL.Expr = NewFilterExpr(exprDollar[1].Expr, labels.MatchNotRegexp, exprDollar[3].str)
+			exprVAL.Filter = labels.MatchEqual
 		}
-	case 6:
+	case 8:
+		exprDollar = exprS[exprpt-1 : exprpt+1]
+//line pkg/logql/expr.y:44
+		{
+			exprVAL.Filter = labels.MatchNotRegexp
+		}
+	case 9:
+		exprDollar = exprS[exprpt-1 : exprpt+1]
+//line pkg/logql/expr.y:45
+		{
+			exprVAL.Filter = labels.MatchNotEqual
+		}
+	case 10:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:35
+//line pkg/logql/expr.y:49
 		{
-			exprVAL.Expr = NewFilterExpr(exprDollar[1].Expr, labels.MatchNotEqual, exprDollar[3].str)
+			exprVAL.Selector = exprDollar[2].Matchers
 		}
-	case 7:
-		exprDollar = exprS[exprpt-2 : exprpt+1]
-//line pkg/logql/expr.y:36
+	case 11:
+		exprDollar = exprS[exprpt-3 : exprpt+1]
+//line pkg/logql/expr.y:50
 		{
-			exprlex.(*lexer).Error("unexpected end of query, expected string")
+			exprVAL.Selector = exprDollar[2].Matchers
 		}
-	case 8:
-		exprDollar = exprS[exprpt-2 : exprpt+1]
-//line pkg/logql/expr.y:37
+	case 12:
+		exprDollar = exprS[exprpt-3 : exprpt+1]
+//line pkg/logql/expr.y:51
 		{
-			exprlex.(*lexer).Error("unexpected string, expected pipe")
 		}
-	case 9:
+	case 13:
 		exprDollar = exprS[exprpt-1 : exprpt+1]
-//line pkg/logql/expr.y:41
+//line pkg/logql/expr.y:55
 		{
 			exprVAL.Matchers = []*labels.Matcher{exprDollar[1].Matcher}
 		}
-	case 10:
+	case 14:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:42
+//line pkg/logql/expr.y:56
 		{
 			exprVAL.Matchers = append(exprDollar[1].Matchers, exprDollar[3].Matcher)
 		}
-	case 11:
+	case 15:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:46
+//line pkg/logql/expr.y:60
 		{
 			exprVAL.Matcher = mustNewMatcher(labels.MatchEqual, exprDollar[1].str, exprDollar[3].str)
 		}
-	case 12:
+	case 16:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:47
+//line pkg/logql/expr.y:61
 		{
 			exprVAL.Matcher = mustNewMatcher(labels.MatchNotEqual, exprDollar[1].str, exprDollar[3].str)
 		}
-	case 13:
+	case 17:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:48
+//line pkg/logql/expr.y:62
 		{
 			exprVAL.Matcher = mustNewMatcher(labels.MatchRegexp, exprDollar[1].str, exprDollar[3].str)
 		}
-	case 14:
+	case 18:
 		exprDollar = exprS[exprpt-3 : exprpt+1]
-//line pkg/logql/expr.y:49
+//line pkg/logql/expr.y:63
 		{
 			exprVAL.Matcher = mustNewMatcher(labels.MatchNotRegexp, exprDollar[1].str, exprDollar[3].str)
 		}
diff --git a/pkg/logql/expr.y b/pkg/logql/expr.y
index 0a9588163fad58a80198aa9137bf021a953a9946..cbe8ded5cb72f7dcb042ea99d6cd1797c4a10d6c 100644
--- a/pkg/logql/expr.y
+++ b/pkg/logql/expr.y
@@ -8,6 +8,8 @@ import (
 
 %union{
   Expr         Expr
+  Filter       labels.MatchType
+  Selector     []*labels.Matcher
   Matchers     []*labels.Matcher
   Matcher      *labels.Matcher
   str          string
@@ -16,9 +18,11 @@ import (
 
 %start root
 
-%type  <Expr>         expr
-%type  <Matchers>     matchers
-%type  <Matcher>      matcher
+%type <Expr>         expr
+%type <Filter>       filter
+%type <Selector>     selector
+%type <Matchers>     matchers
+%type <Matcher>      matcher
 
 %token <str>  IDENTIFIER STRING
 %token <val>  MATCHERS LABELS EQ NEQ RE NRE OPEN_BRACE CLOSE_BRACE COMMA DOT PIPE_MATCH PIPE_EXACT
@@ -28,13 +32,23 @@ import (
 root: expr { exprlex.(*lexer).expr = $1 };
 
 expr:
-      OPEN_BRACE matchers CLOSE_BRACE  { $$ = &matchersExpr{ matchers: $2 } }
-    | expr PIPE_MATCH STRING           { $$ = NewFilterExpr( $1, labels.MatchRegexp, $3 ) }
-    | expr PIPE_EXACT STRING           { $$ = NewFilterExpr( $1, labels.MatchEqual, $3 ) }
-    | expr NRE STRING                  { $$ = NewFilterExpr( $1, labels.MatchNotRegexp, $3 ) }
-    | expr NEQ STRING                  { $$ = NewFilterExpr( $1, labels.MatchNotEqual, $3 ) }
-    | expr PIPE_MATCH                 { exprlex.(*lexer).Error("unexpected end of query, expected string") }
-    | expr STRING                     { exprlex.(*lexer).Error("unexpected string, expected pipe") }
+      selector                         { $$ = &matchersExpr{ matchers: $1 } }
+    | expr filter STRING               { $$ = NewFilterExpr( $1, $2, $3 ) }
+    | expr filter error
+    | expr error
+    ;
+
+filter:
+      PIPE_MATCH                       { $$ = labels.MatchRegexp }
+    | PIPE_EXACT                       { $$ = labels.MatchEqual }
+    | NRE                              { $$ = labels.MatchNotRegexp }
+    | NEQ                              { $$ = labels.MatchNotEqual }
+    ;
+
+selector:
+      OPEN_BRACE matchers CLOSE_BRACE  { $$ = $2 }
+    | OPEN_BRACE matchers error        { $$ = $2 }
+    | OPEN_BRACE error CLOSE_BRACE     { }
     ;
 
 matchers:
diff --git a/pkg/logql/parser.go b/pkg/logql/parser.go
index de0c4499d6eeaf440ace006ff880deb0dce8db7c..7d34c8820b23e6c70f605f2fcd2f3008fe4cad77 100644
--- a/pkg/logql/parser.go
+++ b/pkg/logql/parser.go
@@ -7,18 +7,28 @@ import (
 	"text/scanner"
 )
 
+func init() {
+	// Improve the error messages coming out of yacc.
+	exprErrorVerbose = true
+	for str, tok := range tokens {
+		exprToknames[tok-exprPrivate+1] = str
+	}
+}
+
 // ParseExpr parses a string and returns an Expr.
 func ParseExpr(input string) (Expr, error) {
-	var l lexer
+	var l = lexer{
+		parser: exprNewParser().(*exprParserImpl),
+	}
 	l.Init(strings.NewReader(input))
 	//l.Scanner.Mode = scanner.SkipComments | scanner.ScanStrings | scanner.ScanInts
 	l.Scanner.Error = func(_ *scanner.Scanner, msg string) {
 		l.Error(msg)
 	}
 
-	e := exprParse(&l)
-	if e != 0 || l.err != nil {
-		return nil, l.err
+	e := l.parser.Parse(&l)
+	if e != 0 || len(l.errs) > 0 {
+		return nil, l.errs[0]
 	}
 	return l.expr, nil
 }
@@ -38,8 +48,9 @@ var tokens = map[string]int{
 
 type lexer struct {
 	scanner.Scanner
-	err  error
-	expr Expr
+	errs   []ParseError
+	expr   Expr
+	parser *exprParserImpl
 }
 
 func (l *lexer) Lex(lval *exprSymType) int {
@@ -73,16 +84,11 @@ func (l *lexer) Lex(lval *exprSymType) int {
 }
 
 func (l *lexer) Error(msg string) {
-	// We want to return the first error (from the lexer), and ignore subsequent ones.
-	if l.err != nil {
-		return
-	}
-
-	l.err = ParseError{
+	l.errs = append(l.errs, ParseError{
 		msg:  msg,
 		line: l.Line,
 		col:  l.Column,
-	}
+	})
 }
 
 // ParseError is what is returned when we failed to parse.
diff --git a/pkg/logql/parser_test.go b/pkg/logql/parser_test.go
index cf53433d454af050430359019a8e2ffb4bc24b9e..1b889500ee591d1e97f38d217c2b3302f430e114 100644
--- a/pkg/logql/parser_test.go
+++ b/pkg/logql/parser_test.go
@@ -119,18 +119,36 @@ func TestParse(t *testing.T) {
 				col:  6,
 			},
 		},
+		{
+			in: `{foo="bar"`,
+			err: ParseError{
+				msg:  "syntax error: unexpected $end, expecting } or ,",
+				line: 1,
+				col:  11,
+			},
+		},
+
 		{
 			in: `{foo="bar"} |~`,
 			err: ParseError{
-				msg:  "unexpected end of query, expected string",
+				msg:  "syntax error: unexpected $end, expecting STRING",
 				line: 1,
 				col:  15,
 			},
 		},
+
 		{
 			in: `{foo="bar"} "foo"`,
 			err: ParseError{
-				msg:  "unexpected string, expected pipe",
+				msg:  "syntax error: unexpected STRING, expecting != or !~ or |~ or |=",
+				line: 1,
+				col:  13,
+			},
+		},
+		{
+			in: `{foo="bar"} foo`,
+			err: ParseError{
+				msg:  "syntax error: unexpected IDENTIFIER, expecting != or !~ or |~ or |=",
 				line: 1,
 				col:  13,
 			},