From e235738cb01d79cd9dd7f904806b6966941fe8eb Mon Sep 17 00:00:00 2001 From: Giorgis Georgakoudis Date: Thu, 12 Mar 2026 14:33:38 -0700 Subject: [PATCH 1/2] Refactor openmp grammar for LALR parsing --- src/numba/openmp/omp_grammar.py | 46 +++++++++++++++++++++++++-------- src/numba/openmp/omp_lower.py | 9 +++++++ src/numba/openmp/parser.py | 8 ++++-- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/numba/openmp/omp_grammar.py b/src/numba/openmp/omp_grammar.py index 97fcb38d7447..ac4ef021c48f 100644 --- a/src/numba/openmp/omp_grammar.py +++ b/src/numba/openmp/omp_grammar.py @@ -54,12 +54,41 @@ | ordered_construct for_simd_construct: for_simd_directive for_simd_directive: FOR SIMD [for_simd_clause*] - for_simd_clause: for_clause - | simd_clause + // LALR: avoid ambiguity from overlapping expansions in for_clause vs simd_clause + for_simd_clause: ORDERED + | schedule_clause + | collapse_clause + | private_clause + | copyprivate_clause + | firstprivate_clause + | lastprivate_clause + | data_sharing_clause + | data_default_clause + | copyin_clause + | reduction_clause + | NOWAIT + | aligned_clause + | linear_clause + | uniform_clause + | inbranch_clause parallel_for_simd_construct: parallel_for_simd_directive parallel_for_simd_directive: PARALLEL FOR SIMD [parallel_for_simd_clause*] - parallel_for_simd_clause: parallel_for_clause - | simd_clause + // LALR: avoid ambiguity from overlapping expansions in parallel_for_clause vs simd_clause + parallel_for_simd_clause: if_clause + | num_threads_clause + | ORDERED + | schedule_clause + | collapse_clause + | data_default_clause + | private_clause + | firstprivate_clause + | lastprivate_clause + | data_sharing_clause + | reduction_clause + | aligned_clause + | linear_clause + | uniform_clause + | inbranch_clause distribute_construct: distribute_directive distribute_simd_construct: distribute_simd_directive distribute_directive: DISTRIBUTE [distribute_clause*] @@ -509,10 +538,9 @@ | device_clause | if_clause motion_clause: update_motion_type "(" variable_array_section_list ")" - variable_array_section_list: PYTHON_NAME + // LALR: name_slice already covers bare PYTHON_NAME, so avoid the ambiguous PYTHON_NAME alternatives. + variable_array_section_list: name_slice // | array_section - | name_slice - | variable_array_section_list "," PYTHON_NAME | variable_array_section_list "," name_slice // | variable_array_section_list "," array_section //array_section: PYTHON_NAME array_section_subscript @@ -690,7 +718,3 @@ %import common.WS %ignore WS """ - -""" - name_slice: PYTHON_NAME [ "[" slice ["," slice]* "]" ] -""" diff --git a/src/numba/openmp/omp_lower.py b/src/numba/openmp/omp_lower.py index d87c2d124355..955fe81e8d7a 100644 --- a/src/numba/openmp/omp_lower.py +++ b/src/numba/openmp/omp_lower.py @@ -2867,6 +2867,15 @@ def name_slice(self, args): else: return NameSlice(args[0], args[1:]) + def variable_array_section_list(self, args): + if DEBUG_OPENMP >= 1: + print("visit variable_array_section_list", args, type(args)) + if len(args) == 1: + return args + else: + args[0].append(args[1]) + return args[0] + def var_list(self, args): if DEBUG_OPENMP >= 1: print("visit var_list", args, type(args)) diff --git a/src/numba/openmp/parser.py b/src/numba/openmp/parser.py index 85a545fdc404..3758d2af23e3 100644 --- a/src/numba/openmp/parser.py +++ b/src/numba/openmp/parser.py @@ -2,5 +2,9 @@ from .omp_grammar import openmp_grammar -openmp_parser = Lark(openmp_grammar, start="openmp_statement") -var_collector_parser = Lark(openmp_grammar, start="openmp_statement") +# Use Lark's contextual lexer with LALR to resolve keyword-vs-identifier +# overlaps (e.g. `TO` vs `PYTHON_NAME`) without reserving words. +_LARK_KWARGS = dict(parser="lalr", lexer="contextual") + +openmp_parser = Lark(openmp_grammar, start="openmp_statement", **_LARK_KWARGS) +var_collector_parser = Lark(openmp_grammar, start="openmp_statement", **_LARK_KWARGS) From d71c8e29e8b4fd9f18b962a58b4eaedf7e53e19d Mon Sep 17 00:00:00 2001 From: Giorgis Georgakoudis Date: Thu, 12 Mar 2026 22:44:06 -0700 Subject: [PATCH 2/2] Fixup: make expected error robust across lark versions --- src/numba/openmp/tests/test_openmp.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/numba/openmp/tests/test_openmp.py b/src/numba/openmp/tests/test_openmp.py index 5805778e2941..c3aee25fa37c 100644 --- a/src/numba/openmp/tests/test_openmp.py +++ b/src/numba/openmp/tests/test_openmp.py @@ -1926,7 +1926,14 @@ def test_impl(nt): with self.assertRaises(Exception) as raises: test_impl(12) - self.assertIn("No terminal matches", str(raises.exception)) + # Lark error text differs across versions: lexer failure ("No terminal matches") + # vs parser failure ("Unexpected token") for unsupported clauses like NOWAIT here. + err = str(raises.exception) + self.assertIn("nowait", err.lower()) + self.assertTrue( + "No terminal matches" in err or "Unexpected token" in err, + err, + ) def test_parallel_double_num_threads(self): @njit