Repository: tursodatabase/limbo (language: rust)

Issue:
Parser does not stop on first syntax error in shell
We should probably just stop on the first syntax error...

SQLite3:

```
sqlite> SELECT FROM foo;
Parse error: near "FROM": syntax error
  SELECT FROM foo;
         ^--- error here
sqlite>
```

Limbo:

```
limbo> SELECT FROM foo;

  × near "FROM": syntax error at (1, 12)
   ╭────
 1 │ SELECT FROM foo;
   ·           ▲
   ·           ╰── syntax error
   ╰────


  × near "foo": syntax error at (1, 16)
   ╭────
 1 │ SELECT FROM foo;
   ·               ▲
   ·               ╰── syntax error
   ╰────
```


Discussion / hints:

Fix patch:
diff --git a/vendored/sqlite3-parser/src/lexer/sql/mod.rs b/vendored/sqlite3-parser/src/lexer/sql/mod.rs
index dc672e9f5..862d4c4ee 100644
--- a/vendored/sqlite3-parser/src/lexer/sql/mod.rs
+++ b/vendored/sqlite3-parser/src/lexer/sql/mod.rs
@@ -30,6 +30,7 @@ pub struct Parser<'input> {
     scanner: Scanner<Tokenizer>,
     /// lemon parser
     parser: yyParser<'input>,
+    had_error: bool,
 }
 
 impl<'input> Parser<'input> {
@@ -43,12 +44,14 @@ impl<'input> Parser<'input> {
             input,
             scanner,
             parser,
+            had_error: false,
         }
     }
     /// Parse new `input`
     pub fn reset(&mut self, input: &'input [u8]) {
         self.input = input;
         self.scanner.reset();
+        self.had_error = false;
     }
     /// Current line position in input
     pub fn line(&self) -> u64 {
@@ -182,6 +185,10 @@ impl FallibleIterator for Parser<'_> {
 
     fn next(&mut self) -> Result<Option<Cmd>, Error> {
         //print!("line: {}, column: {}: ", self.scanner.line(), self.scanner.column());
+        // if we have already encountered an error, return None to signal that to fallible_iterator that we are done parsing
+        if self.had_error {
+            return Ok(None);
+        }
         self.parser.ctx.reset();
         let mut last_token_parsed = TK_EOF;
         let mut eof = false;
@@ -197,6 +204,7 @@ impl FallibleIterator for Parser<'_> {
             if token_type == TK_ILLEGAL {
                 //  break out of parsing loop and return error
                 self.parser.sqlite3ParserFinalize();
+                self.had_error = true;
                 return Err(Error::UnrecognizedToken(
                     Some((self.scanner.line(), self.scanner.column())),
                     Some(start.into()),
@@ -242,12 +250,18 @@ impl FallibleIterator for Parser<'_> {
                     self.parser
                         .sqlite3Parser(TK_SEMI, sentinel(self.input.len()))
                 );
+                if self.parser.ctx.error().is_some() {
+                    self.had_error = true;
+                }
             }
             try_with_position!(
                 self.scanner,
                 self.parser
                     .sqlite3Parser(TK_EOF, sentinel(self.input.len()))
             );
+            if self.parser.ctx.error().is_some() {
+                self.had_error = true;
+            }
         }
         self.parser.sqlite3ParserFinalize();
         if let Some(e) = self.parser.ctx.error() {
@@ -256,6 +270,7 @@ impl FallibleIterator for Parser<'_> {
                 Some((self.scanner.line(), self.scanner.column())),
                 Some((self.offset() - 1).into()),
             );
+            self.had_error = true;
             return Err(err);
         }
         let cmd = self.parser.ctx.cmd();
@@ -266,6 +281,7 @@ impl FallibleIterator for Parser<'_> {
                     Some((self.scanner.line(), self.scanner.column())),
                     Some((self.offset() - 1).into()),
                 );
+                self.had_error = true;
                 return Err(err);
             }
         }


Test patch:
diff --git a/vendored/sqlite3-parser/src/lexer/sql/test.rs b/vendored/sqlite3-parser/src/lexer/sql/test.rs
index 46cca8af3..2ba066bdd 100644
--- a/vendored/sqlite3-parser/src/lexer/sql/test.rs
+++ b/vendored/sqlite3-parser/src/lexer/sql/test.rs
@@ -338,6 +338,21 @@ fn qualified_table_name_within_triggers() {
     );
 }
 
+#[test]
+fn select_from_error_stops_at_first_error() {
+    let mut parser = Parser::new(b"SELECT FROM foo;");
+
+    // First next() call should return the first syntax error
+    let err = parser.next().unwrap_err();
+    assert!(matches!(err, Error::ParserError(_, _, _)));
+
+    // Second next() call should return Ok(None) since parsing should have stopped
+    assert_eq!(parser.next().unwrap(), None);
+
+    // Third next() call should also return Ok(None)
+    assert_eq!(parser.next().unwrap(), None);
+}
+
 #[test]
 fn indexed_by_clause_within_triggers() {
     expect_parser_err_msg(