Repository: jg-rp/python-jsonpath

Issue:
unexpected token 'and' / 'or' for properties
This works:
`jsonpath.findall('$.orders', json_object)`
`jsonpath.findall('$.test', json_object)`
`jsonpath.findall('test', json_object)`

This does not work: 
`jsonpath.findall('orders', json_object)` -> `JSONPathSyntaxError: unexpected token 'or', line 1, column 0`
`jsonpath.findall('andy', json_object)` -> `JSONPathSyntaxError: unexpected token 'and', line 1, column 0`

According to the documentation the '$.' part is optional.

Hints from issue comments:
@jg-rp: Thank you, @torbenw. Should be fixed in version 1.2.2, now released.

Name/property selectors that match a reserved word exactly, and where we can't tell the difference using a preceding `.`, will still need to use quoted syntax, like `["and"]`. I've added a list of the default reserved words to the docs [here](https://jg-rp.github.io/python-jsonpath/syntax/#properties-thing-thing-or-thing).@torbenw: Thanks for the quick fix!

Solution patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 08d7e4c..2de5ae7 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -8,7 +8,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest]
+        os: [ubuntu-22.04, windows-latest, macos-latest]
         python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
         exclude:
           - os: macos-latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73ba386..d597096 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Python JSONPath Change Log
 
+## Version 1.2.2 (unreleased)
+
+**Fixes**
+
+- Fixed parsing of bare name selectors that start with a reserved word. See [issue #72](https://github.com/jg-rp/python-jsonpath/issues/72).
+
 ## Version 1.2.1
 
 **Fixes**
diff --git a/docs/syntax.md b/docs/syntax.md
index 5ed7236..2aa7e10 100644
--- a/docs/syntax.md
+++ b/docs/syntax.md
@@ -42,6 +42,8 @@ $.categories[0][name]
 $.categories[0]['name']
 ```
 
+By default, `or`, `and`, `in`, `true`, `True`, `false`, `False`, `nil`, `Nil`, `null`, `Null`, `none`, `None`, `contains`, `undefined`, and `missing` are considered _reserved words_. In some cases you will need to use quoted property/name selector syntax if you're selecting a name that matches any of these words exactly. For example, `["and"]`.
+
 ### Array indices (`[0]` or `[-1]`)
 
 Select an item from an array by its index. Indices are zero-based and enclosed in brackets. If the index is negative, items are selected from the end of the array. Considering example data from the top of this page, the following examples are equivalent.
diff --git a/jsonpath/__about__.py b/jsonpath/__about__.py
index 3369473..433a7d9 100644
--- a/jsonpath/__about__.py
+++ b/jsonpath/__about__.py
@@ -1,4 +1,4 @@
 # SPDX-FileCopyrightText: 2023-present James Prior <jamesgr.prior@gmail.com>
 #
 # SPDX-License-Identifier: MIT
-__version__ = "1.2.1"
+__version__ = "1.2.2"
diff --git a/jsonpath/lex.py b/jsonpath/lex.py
index 0399631..4c4422d 100644
--- a/jsonpath/lex.py
+++ b/jsonpath/lex.py
@@ -88,13 +88,13 @@ class attributes. Then setting `lexer_class` on a `JSONPathEnvironment`.
     key_pattern = r"[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*"
 
     # `not` or !
-    logical_not_pattern = r"(?:not|!)"
+    logical_not_pattern = r"(?:not\b)|!"
 
     # && or `and`
-    logical_and_pattern = r"(?:&&|and)"
+    logical_and_pattern = r"&&|(?:and\b)"
 
     # || or `or`
-    logical_or_pattern = r"(?:\|\||or)"
+    logical_or_pattern = r"\|\||(?:or\b)"
 
     def __init__(self, *, env: JSONPathEnvironment) -> None:
         self.env = env
@@ -153,15 +153,15 @@ def compile_rules(self) -> Pattern[str]:
             ],
             (TOKEN_WILD, r"\*"),
             (TOKEN_FILTER, r"\?"),
-            (TOKEN_IN, r"in"),
-            (TOKEN_TRUE, r"[Tt]rue"),
-            (TOKEN_FALSE, r"[Ff]alse"),
-            (TOKEN_NIL, r"[Nn]il"),
-            (TOKEN_NULL, r"[Nn]ull"),
-            (TOKEN_NONE, r"[Nn]one"),
-            (TOKEN_CONTAINS, r"contains"),
-            (TOKEN_UNDEFINED, r"undefined"),
-            (TOKEN_MISSING, r"missing"),
+            (TOKEN_IN, r"in\b"),
+            (TOKEN_TRUE, r"[Tt]rue\b"),
+            (TOKEN_FALSE, r"[Ff]alse\b"),
+            (TOKEN_NIL, r"[Nn]il\b"),
+            (TOKEN_NULL, r"[Nn]ull\b"),
+            (TOKEN_NONE, r"[Nn]one\b"),
+            (TOKEN_CONTAINS, r"contains\b"),
+            (TOKEN_UNDEFINED, r"undefined\b"),
+            (TOKEN_MISSING, r"missing\b"),
             (TOKEN_LIST_START, r"\["),
             (TOKEN_RBRACKET, r"]"),
             (TOKEN_COMMA, r","),


Test patch:
diff --git a/tests/test_find.py b/tests/test_find.py
index b2175e2..6059641 100644
--- a/tests/test_find.py
+++ b/tests/test_find.py
@@ -127,6 +127,30 @@ class Case:
             }
         ],
     ),
+    Case(
+        description="issue 72, orders",
+        path="orders",
+        data={"orders": [1, 2, 3]},
+        want=[[1, 2, 3]],
+    ),
+    Case(
+        description="issue 72, andy",
+        path="andy",
+        data={"andy": [1, 2, 3]},
+        want=[[1, 2, 3]],
+    ),
+    Case(
+        description="quoted reserved word, and",
+        path="['and']",
+        data={"and": [1, 2, 3]},
+        want=[[1, 2, 3]],
+    ),
+    Case(
+        description="quoted reserved word, or",
+        path="['or']",
+        data={"or": [1, 2, 3]},
+        want=[[1, 2, 3]],
+    ),
 ]
 
 
diff --git a/tests/test_lex.py b/tests/test_lex.py
index 51df997..14727ac 100644
--- a/tests/test_lex.py
+++ b/tests/test_lex.py
@@ -1375,6 +1375,85 @@ class Case:
             Token(kind=TOKEN_RBRACKET, value="]", index=9, path="$.thing[~]"),
         ],
     ),
+    Case(
+        description="implicit root selector, name selector starts with `and`",
+        path="anderson",
+        want=[
+            Token(kind=TOKEN_BARE_PROPERTY, value="anderson", index=0, path="anderson"),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `or`",
+        path="order",
+        want=[
+            Token(kind=TOKEN_BARE_PROPERTY, value="order", index=0, path="order"),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `true`",
+        path="trueblue",
+        want=[
+            Token(kind=TOKEN_BARE_PROPERTY, value="trueblue", index=0, path="trueblue"),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `false`",
+        path="falsehood",
+        want=[
+            Token(
+                kind=TOKEN_BARE_PROPERTY, value="falsehood", index=0, path="falsehood"
+            ),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `not`",
+        path="nottingham",
+        want=[
+            Token(
+                kind=TOKEN_BARE_PROPERTY, value="nottingham", index=0, path="nottingham"
+            ),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `null`",
+        path="nullable",
+        want=[
+            Token(kind=TOKEN_BARE_PROPERTY, value="nullable", index=0, path="nullable"),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `none`",
+        path="nonexpert",
+        want=[
+            Token(
+                kind=TOKEN_BARE_PROPERTY, value="nonexpert", index=0, path="nonexpert"
+            ),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `undefined`",
+        path="undefinedness",
+        want=[
+            Token(
+                kind=TOKEN_BARE_PROPERTY,
+                value="undefinedness",
+                index=0,
+                path="undefinedness",
+            ),
+        ],
+    ),
+    Case(
+        description="implicit root selector, name selector starts with `missing`",
+        path="missingly",
+        want=[
+            Token(
+                kind=TOKEN_BARE_PROPERTY,
+                value="missingly",
+                index=0,
+                path="missingly",
+            ),
+        ],
+    ),
 ]