From 87d0ae47aa0c0faaeb2a0859f8320795a03d00bd Mon Sep 17 00:00:00 2001 From: aaldevrn Date: Mon, 10 Aug 2026 19:50:20 -0400 Subject: [PATCH] parser: support Python-style implicit string concatenation Adjacent string literals ("a" "b") are folded into one string ("ab"), matching Jinja semantics. Some model chat templates (e.g. Gemma-4-E2B) embed multi-line error messages this way inside raise_exception(...) call args, which previously failed to parse with 'Expected closing parenthesis in call args'. Concatenation is only folded between string literals, so operator expressions and identifiers are unaffected. --- include/minja/minja.hpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/include/minja/minja.hpp b/include/minja/minja.hpp index 5ed0556..583f5bc 100644 --- a/include/minja/minja.hpp +++ b/include/minja/minja.hpp @@ -1806,7 +1806,28 @@ class Parser { if (it == end) return nullptr; if (*it == '"' || *it == '\'') { auto str = parseString(); - if (str) return std::make_shared(*str); + if (str) { + // Python-style implicit string concatenation: adjacent string + // literals ("a" "b") fold into one ("ab"). Jinja (and the Gemini/ + // Gemma templates that embed tool-call error messages) relies on it; + // Minja's parser rejected the multi-line call-arg form. + while (it != end) { + auto save = it; + consumeSpaces(); + if (it != end && (*it == '"' || *it == '\'')) { + auto next = parseString(); + if (!next) { + it = save; + break; + } + *str += *next; + } else { + it = save; + break; + } + } + return std::make_shared(*str); + } } static std::regex prim_tok(R"(true\b|True\b|false\b|False\b|None\b)"); auto token = consumeToken(prim_tok);