From 2f44d04fe044fcf1b577a4fc3eed4619d3bf9b92 Mon Sep 17 00:00:00 2001 From: "J.J. Tolton" Date: Thu, 20 Nov 2025 16:43:05 -0500 Subject: [PATCH] Fix malformed list syntax bug in curly braces Fixes #3161 HeadTailSeparator tokens were incorrectly accepted at term positions. Added explicit check to reject them. --- src/parser/parser.rs | 4 ++ .../issue_3161_malformed_list_syntax.md | 68 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/scryer/cli/issues/issue_3161_malformed_list_syntax.md diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 61e85c7ec..2777efebd 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -645,6 +645,10 @@ impl<'a, R: CharRead> Parser<'a, R> { for (i, desc) in self.stack.iter().rev().enumerate() { if i % 2 == 0 { // expect a term or non-comma operator. + // HeadTailSeparator is NEVER valid as a term, even if its spec suggests otherwise + if let TokenType::HeadTailSeparator = desc.tt { + return None; + } if let TokenType::Comma = desc.tt { return None; } else if is_term!(desc.spec) || is_op!(desc.spec) || is_negate!(desc.spec) { diff --git a/tests/scryer/cli/issues/issue_3161_malformed_list_syntax.md b/tests/scryer/cli/issues/issue_3161_malformed_list_syntax.md new file mode 100644 index 000000000..ff4f52e8e --- /dev/null +++ b/tests/scryer/cli/issues/issue_3161_malformed_list_syntax.md @@ -0,0 +1,68 @@ +# Issue 3161: Malformed List Syntax in Curly Braces + +Tests for the parser bug where malformed list syntax like `[|]` was incorrectly +accepted inside curly braces when preceded by specific operator patterns. + +## Test that {!*[|]*} throws syntax error + +Before the fix, this incorrectly evaluated to `T = {*}`. +After the fix, it should throw a syntax error. + +```trycmd +$ scryer-prolog -f --no-add-history +?- use_module(library(dcgs)). + true. +?- {!*[|]*}=T. +error(syntax_error(incomplete_reduction),read_term/3:1). +?- halt. +``` + +## Test that {!+[|]+} throws syntax error + +```trycmd +$ scryer-prolog -f --no-add-history +?- use_module(library(dcgs)). + true. +?- {!+[|]+}=T. +error(syntax_error(incomplete_reduction),read_term/3:1). +?- halt. +``` + +## Test that {[|]} throws syntax error + +```trycmd +$ scryer-prolog -f --no-add-history +?- {[|]}=T. +error(syntax_error(incomplete_reduction),read_term/3:1). +?- halt. +``` + +## Test that valid list syntax still works + +```trycmd +$ scryer-prolog -f --no-add-history +?- {[a,b,c]}=T, write(T), nl. +{[a,b,c]} + T = {"abc"}. +?- halt. +``` + +## Test that empty list in curly braces works + +```trycmd +$ scryer-prolog -f --no-add-history +?- {[]}=T, write(T), nl. +{[]} + T = {[]}. +?- halt. +``` + +## Test that cut in curly braces works + +```trycmd +$ scryer-prolog -f --no-add-history +?- {!}=T, write(T), nl. +{!} + T = {!}. +?- halt. +```