Repository navigation
Conversation
…es them
Ling's tokenizer.json splits with `?+` and `++`, which the matcher refused, so the tokenizer
did not load (UnsupportedRegex). A possessive quantifier is now its greedy repetition inside an
atomic step, a lookahead that keeps its match: the body's first match, then the rest of the
pattern from its end, never giving back what it took, as Oniguruma matches it. `{n,m}+` keeps
Ruby syntax's two quantifiers.
Owner
|
Thanks @chaog992, this is landed on main as 79ba3f5, with you as the author. Possessive quantifiers as an atomic step over the greedy repeat is a clean fit for the matcher, and the cases where giving back would match more ( - Synthema, Ash's AI Assistant. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Ling's tokenizer.json (inclusionAI/Ling-3.1-flash, the model of #850 and #874, and TensorFold's Ling-3.0-flash checkpoints) pre-tokenizes with
[^\r\n\p{L}\p{N}]?+\p{L}+and?[^\s\p{L}\p{N}]++[\r\n]*. The matcher refused possessive quantifiers (zig/src/core/tokenizer/regex.zig:329), so the tokenizer did not load:error.UnsupportedRegex.A possessive quantifier now compiles to its greedy repetition inside an atomic step, a lookahead that keeps its match (
Inst.lookwithatomicset): the rest of the pattern goes on from the end of the body's first match and never gives back what it took, as Oniguruma matches it.{n,m}+keeps Ruby syntax's reading as two quantifiers, and(?>...), which no tokenizer.json I fetched uses, stays refused. A pattern without possessive quantifiers compiles to the same program as before.It stands on its own: #874, which would add a Ling family, is parked, and none of its files touch the tokenizer.
Receipt
tools/zig/tokenizer_parity.pyon the tokenizer.json of inclusionAI/Ling-3.1-flash at revision a4ccd6b4: encode 2784/2784 and decode 5367/5367 equal; on the base the checker stops witherror: UnsupportedRegex. TensorFold's five Ling-3.0-flash checkpoints ship the same file (same sha256). inclusionAI/Ling-flash-2.0's at 96f9b7a7, a different file with the same pattern, gives the same counts, and the base refuses it too. The 23 other distinct tokenizer.json files I fetched (listed below the receipt; 11 come from TensorFold's published models) and the tool's six synthetic tokenizers pass, as on the base.tools/bench_cache.pyrun (no GPU or model); the change reaches a prompt only through the tokenizer's encode. Encode time on seven other tokenizers (DeepSeek-V3.2-Exp, step3, DeepSeek-V4-Flash, Qwen3-30B-A3B, gpt-oss-20b, GLM-4.6, phi-4), base against fix, with the parity tool's checker on its cases, medians of 12 alternating runs pinned to one core: +0.4 to +2.0% built ReleaseSafe, as the Mac and XPU servers build the tokenizer, and -2.5 to +0.5% built ReleaseFast, as the CUDA server does; a second build of the base, the same machine code, moved -1.6 to +0.9%. That is within code-placement noise: moving the matcher by 16 to 48 bytes moved the base alone by up to 7.6% on the DeepSeek-style tokenizers, and over four such placements the fix against the base averaged -0.8 to -0.6% built ReleaseSafe and -0.4 to -0.3% built ReleaseFast on the tokenizers tried. These runs used builds on 1.1.0; rebuilt on d1018b3, the base and fix checkers have the same.textas those builds.a*+afinds nothing in "aaa",[0-9]?+5finds only "15" in "15 5",b?+\p{L}+finds only "bb" in "bb b1",(?:ab)++abfinds nothing in "abab ab",(?:a++)+afinds nothing in "aaa"). Two pin what the atomic step keeps:(?:a|ab)?+bfinds "ab" in "ab" (the body's first match, not its longest), anda?a?+afinds "aa" in "aa" (only the quantified element is atomic).a{1,2}+afinds "aaaa", since Ruby syntax reads{n,m}+as two quantifiers; a plain lookahead still keeps nothing (a(?=b)finds "a" twice in "ab ab"); and(?:a*+)*and(?:a?)++are refused, as(a*)*is.zig test -O <mode> zig/src/core/tokenizer/regex.zig: 7/7 in Debug, ReleaseSafe and ReleaseFast (6/6 on the base).zig build test test-kernel-contract test-golden fuzz-cortheon: 225/230 steps, 3033/3050 tests, 14 skipped, 3 failed, exit 1 (3033/3049 on the base, 2 failed); server goldens 306/319 and 26/26 equal, 0 unexpected, as on the base. Plainzig build: 24/24 steps.digest_cache_test's "digest cache detects same-size rewrites even when mtime is restored" andcuda_cost_identity's "cost identity: stable reads reuse a drafter, while a changed payload at the same path invalidates it", which fail often on this machine, on the base too (alone: 16 and 27 of 30 runs): this WSL2 kernel stamps file times on a coarse tick (CONFIG_HZ=100; 11 ms steps here), so a same-size rewrite within one tick keeps the first file times. The third,store_test's "two split ranks make the same decisions on the same calls and each restores its own rows", failed in this run only; the sessions test binary (all its 27 tests) fails it in 1 of 20 runs on the base and 2 of 20 with the fix, for a reason I have not found.zig-windowsjob fails on other pull requests on d1018b3 too (perf(hip): group routed fc1 and shared-up stream products #1103, nemotron cuda: an encode hook on Vision, and a host test that a request encodes each image once #1104), with compile errors in Windows host code this change does not touch; Windows: main's host tests build again on Windows hosts #1034, still open, fixes part of them. regex.zig's tests compile forx86_64-windowswith--test-no-exec, a compile check only.tools/zig/lean_check.py: the same 444 problems as the base, exit 1 on both; the two in the touched file, regex.zig:1 and regex.zig:281 (272 on the base), are inherited.tools/zig/lean_new.pyagainst the base: 0 new.zig fmt --check build.zig zigandgit diff --checkpass.error.UnsupportedRegexin Debug, ReleaseSafe and ReleaseFast; Debug's trace points at the base's regex.zig:329.aarch64-macoswith-mcpu apple_m1 --test-no-exec, a compile check only). No Mac, so no encode timing on Apple silicon, where the Mac server builds the tokenizer ReleaseSafe. No GPU or model, so notools/bench_cache.pyTTFT and no served Ling request (main has no Ling family yet).The 23 other tokenizer.json files
TensorFold: DeepSeek-V4-Flash-0731-MXFP4-MLX, DeepSeek-V4.1-Flash-MLX-2bit-MTP, GLM-5.3-Flash-MLX-4bit-MTP, Laguna-S-2.1-MLX-4bit, MiMo-V2.6-Flash-RL-MLX-4bit-MTP, Muse-Glimmer-30B-MLX-4bit, NVIDIA-Nemotron-3.5-Lightning-30B-A3B-MLX-4bit, Nex-N2.5-mini-MLX-4bit (the same file as Qwen3.6-35B-A3B-MLX-4bit-MTP), Qwen3.8-27B-MLX-4bit, Qwen3.8-Flash-Next-MLX-4bit, Solar-Open2-250B-MLX-4bit.
Others: LiquidAI/LFM2-8B-A1B, MiniMaxAI/MiniMax-M2, Qwen/Qwen3-30B-A3B (the same file as XiaomiMiMo/MiMo-V2-Flash), baidu/ERNIE-4.5-21B-A3B-PT, deepseek-ai/DeepSeek-V3.2-Exp, ibm-granite/granite-4.0-h-small, microsoft/phi-4, nvidia/NVIDIA-Nemotron-Nano-9B-v2, openai/gpt-oss-20b, stepfun-ai/step3, tencent/Hunyuan-A13B-Instruct, zai-org/GLM-4.6.
Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. (n/a: tokenizer only; parity against the tokenizers library above.)SUMMARYline is in the receipt. (n/a: no kernel changes.)