From b9a1ed1bccee2fde21f13fc52e85a38828df6fb5 Mon Sep 17 00:00:00 2001 From: NikolajDanger Date: Wed, 22 Apr 2026 09:08:36 +0200 Subject: [PATCH] :goat: QVAERE --- README.md | 24 +- centvrion/ast_nodes.py | 13 ++ centvrion/compiler/emit_expr.py | 3 + centvrion/compiler/runtime/cent_runtime.c | 32 +++ centvrion/compiler/runtime/cent_runtime.h | 1 + centvrion/lexer.py | 3 +- language/main.aux | 2 - language/main.log | 269 ---------------------- language/main.pdf | Bin 25220 -> 25934 bytes snippets/scripta.cent | 1 + snippets/scripta.png | Bin 0 -> 4830 bytes tests.py | 17 ++ 12 files changed, 84 insertions(+), 281 deletions(-) delete mode 100644 language/main.aux delete mode 100644 language/main.log create mode 100644 snippets/scripta.cent create mode 100644 snippets/scripta.png diff --git a/README.md b/README.md index a54d537..f1247f3 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,11 @@ Returns the type of `value` as a string: `NVMERVS` (integer), `LITTERA` (string) Sleeps for `n` seconds, where `n` is an integer, fraction, or NVLLVS (treated as 0). Returns nothing meaningful. +### QVAERE +`QVAERE(pattern, string)` + +Returns an array of all non-overlapping matches of the regex `pattern` in `string`. Both arguments must be strings. Patterns use extended regular expression syntax. Returns an empty array if there are no matches. Raises an error if the pattern is invalid. + ## Modules Modules are additions to the base `CENTVRION` syntax. They add or change certain features. Modules are included in your code by having @@ -361,15 +366,15 @@ Vnlike many other programming languages with modules, the modules in `CENTVRION` ### FORS ![CVM FORS](snippets/fors.png) -The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 4 new built-in functions: `FORTVITVS_NVMERVS int int`, `FORTVITA_ELECTIO ['a]`, `DECIMATIO ['a]`, and `SEMEN int`. +The `FORS` module allows you to add randomness to your `CENTVRION` program. It adds 4 new built-in functions: `FORTVITVS_NVMERVS(int, int)`, `FORTVITA_ELECTIO(['a])`, `DECIMATIO(['a])`, and `SEMEN(int)`. -`FORTVITVS_NVMERVS int int` picks a random int in the (inclusive) range of the two given ints. +`FORTVITVS_NVMERVS(int, int)` picks a random int in the (inclusive) range of the two given ints. -`FORTVITA_ELECTIO ['a]` picks a random element from the given array. `FORTVITA_ELECTIO array` is identical to ```array[FORTVITVS_NVMERVS NVLLVS ((LONGITVDO array)-I)]```. +`FORTVITA_ELECTIO(['a])` picks a random element from the given array. `FORTVITA_ELECTIO(array)` is identical to ```array[FORTVITVS_NVMERVS NVLLVS ((LONGITVDO array)-I)]```. -`DECIMATIO ['a]` returns a copy of the given array with a random tenth of its elements removed. Arrays with fewer than 10 elements are returned unchanged. +`DECIMATIO(['a])` returns a copy of the given array with a random tenth of its elements removed. Arrays with fewer than 10 elements are returned unchanged. -`SEMEN int` seeds the random number generator for reproducibility. +`SEMEN(int)` seeds the random number generator for reproducibility. ### FRACTIO ![CVM FRACTIO](snippets/fractio.png) @@ -394,14 +399,15 @@ When `_` is added _after_ a numeric symbol, the symbol becomes 1.000 times large All integer symbols except `I` may be given a `_`. ### SCRIPTA +![CVM SCRIPTA](snippets/scripta.png) -The `SCRIPTA` module adds file I/O to your `CENTVRION` program. It adds 3 new built-in functions: `LEGE string`, `SCRIBE string string`, and `ADIVNGE string string`. +The `SCRIPTA` module adds file I/O to your `CENTVRION` program. It adds 3 new built-in functions: `LEGE`, `SCRIBE`, and `ADIVNGE`. -`LEGE string` reads the contents of the file at the given path and returns them as a string. +`LEGE(string)` reads the contents of the file at the given path and returns them as a string. -`SCRIBE string string` writes the second argument to the file at the path given by the first argument, overwriting any existing content. +`SCRIBE(string, string)` writes the second argument to the file at the path given by the first argument, overwriting any existing content. -`ADIVNGE string string` appends the second argument to the file at the path given by the first argument. +`ADIVNGE(string, string)` appends the second argument to the file at the path given by the first argument. ### SVBNVLLA ![CVM SVBNVLLA](snippets/svbnvlla.png) diff --git a/centvrion/ast_nodes.py b/centvrion/ast_nodes.py index 4ce7087..2f58de1 100644 --- a/centvrion/ast_nodes.py +++ b/centvrion/ast_nodes.py @@ -1277,6 +1277,19 @@ class BuiltIn(Node): with open(path, "a") as f: f.write(content) return vtable, ValNul() + case "QVAERE": + pattern = params[0] + text = params[1] + if not isinstance(pattern, ValStr) or not isinstance(text, ValStr): + raise CentvrionError("QVAERE requires two strings") + try: + matches = [ + ValStr(m.group(0)) + for m in re.finditer(pattern.value(), text.value()) + ] + except re.error as e: + raise CentvrionError(f"Invalid regex: {e}") + return vtable, ValList(matches) case _: raise NotImplementedError(self.builtin) diff --git a/centvrion/compiler/emit_expr.py b/centvrion/compiler/emit_expr.py index c93ffea..c3a5cb0 100644 --- a/centvrion/compiler/emit_expr.py +++ b/centvrion/compiler/emit_expr.py @@ -297,6 +297,9 @@ def _emit_builtin(node, ctx): lines.append(f"cent_adivnge({param_vars[0]}, {param_vars[1]});") lines.append(f"CentValue {tmp} = cent_null();") + case "QVAERE": + lines.append(f"CentValue {tmp} = cent_qvaere({param_vars[0]}, {param_vars[1]});") + case _: raise NotImplementedError(node.builtin) diff --git a/centvrion/compiler/runtime/cent_runtime.c b/centvrion/compiler/runtime/cent_runtime.c index 874d276..449ba0f 100644 --- a/centvrion/compiler/runtime/cent_runtime.c +++ b/centvrion/compiler/runtime/cent_runtime.c @@ -3,6 +3,7 @@ #include #include #include +#include /* ------------------------------------------------------------------ */ /* Global arena */ @@ -870,6 +871,37 @@ CentValue cent_dict_keys(CentValue dict) { return result; } +/* ------------------------------------------------------------------ */ +/* Regex */ +/* ------------------------------------------------------------------ */ + +CentValue cent_qvaere(CentValue pattern, CentValue text) { + if (pattern.type != CENT_STR || text.type != CENT_STR) + cent_type_error("'QVAERE' requires two strings"); + regex_t re; + int rc = regcomp(&re, pattern.sval, REG_EXTENDED); + if (rc != 0) { + char errbuf[256]; + regerror(rc, &re, errbuf, sizeof(errbuf)); + regfree(&re); + cent_runtime_error(errbuf); + } + CentValue result = cent_list_new(8); + const char *cursor = text.sval; + regmatch_t match; + while (*cursor && regexec(&re, cursor, 1, &match, 0) == 0) { + int len = match.rm_eo - match.rm_so; + char *buf = cent_arena_alloc(cent_arena, len + 1); + memcpy(buf, cursor + match.rm_so, len); + buf[len] = '\0'; + cent_list_push(&result, cent_str(buf)); + cursor += match.rm_eo; + if (len == 0) cursor++; // avoid infinite loop on zero-length match + } + regfree(&re); + return result; +} + /* ------------------------------------------------------------------ */ /* Initialisation */ /* ------------------------------------------------------------------ */ diff --git a/centvrion/compiler/runtime/cent_runtime.h b/centvrion/compiler/runtime/cent_runtime.h index 9234a5a..4565c35 100644 --- a/centvrion/compiler/runtime/cent_runtime.h +++ b/centvrion/compiler/runtime/cent_runtime.h @@ -232,6 +232,7 @@ CentValue cent_ordina(CentValue lst); /* ORDINA */ CentValue cent_lege(CentValue path); /* LEGE */ void cent_scribe(CentValue path, CentValue content); /* SCRIBE */ void cent_adivnge(CentValue path, CentValue content); /* ADIVNGE */ +CentValue cent_qvaere(CentValue pattern, CentValue text); /* QVAERE */ /* ------------------------------------------------------------------ */ /* Array helpers */ diff --git a/centvrion/lexer.py b/centvrion/lexer.py index 823a8f6..a2c0a9b 100644 --- a/centvrion/lexer.py +++ b/centvrion/lexer.py @@ -57,7 +57,8 @@ builtin_tokens = [("BUILTIN", i) for i in [ "TYPVS", "LEGE", "SCRIBE", - "ADIVNGE" + "ADIVNGE", + "QVAERE" ]] data_tokens = [ diff --git a/language/main.aux b/language/main.aux deleted file mode 100644 index d490c35..0000000 --- a/language/main.aux +++ /dev/null @@ -1,2 +0,0 @@ -\relax -\gdef \@abspage@last{2} diff --git a/language/main.log b/language/main.log deleted file mode 100644 index d08eed8..0000000 --- a/language/main.log +++ /dev/null @@ -1,269 +0,0 @@ -This is XeTeX, Version 3.141592653-2.6-0.999998 (TeX Live 2026/Arch Linux) (preloaded format=xelatex 2026.4.8) 21 APR 2026 22:51 -entering extended mode - restricted \write18 enabled. - %&-line parsing enabled. -**main.tex -(./main.tex -LaTeX2e <2025-11-01> -L3 programming layer <2026-01-19> -(/usr/share/texmf-dist/tex/latex/base/article.cls -Document Class: article 2025/01/22 v1.4n Standard LaTeX document class -(/usr/share/texmf-dist/tex/latex/base/size10.clo -File: size10.clo 2025/01/22 v1.4n Standard LaTeX file (size option) -) -\c@part=\count271 -\c@section=\count272 -\c@subsection=\count273 -\c@subsubsection=\count274 -\c@paragraph=\count275 -\c@subparagraph=\count276 -\c@figure=\count277 -\c@table=\count278 -\abovecaptionskip=\skip49 -\belowcaptionskip=\skip50 -\bibindent=\dimen148 -) -(/usr/share/texmf-dist/tex/latex/geometry/geometry.sty -Package: geometry 2020/01/02 v5.9 Page Geometry - -(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty -Package: keyval 2022/05/29 v1.15 key=value parser (DPC) -\KV@toks@=\toks17 -) -(/usr/share/texmf-dist/tex/generic/iftex/ifvtex.sty -Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead. - -(/usr/share/texmf-dist/tex/generic/iftex/iftex.sty -Package: iftex 2024/12/12 v1.0g TeX engine tests -)) -\Gm@cnth=\count279 -\Gm@cntv=\count280 -\c@Gm@tempcnt=\count281 -\Gm@bindingoffset=\dimen149 -\Gm@wd@mp=\dimen150 -\Gm@odd@mp=\dimen151 -\Gm@even@mp=\dimen152 -\Gm@layoutwidth=\dimen153 -\Gm@layoutheight=\dimen154 -\Gm@layouthoffset=\dimen155 -\Gm@layoutvoffset=\dimen156 -\Gm@dimlist=\toks18 -) -(/usr/share/texmf-dist/tex/latex/fontspec/fontspec.sty -(/usr/share/texmf-dist/tex/latex/l3packages/xparse/xparse.sty -(/usr/share/texmf-dist/tex/latex/l3kernel/expl3.sty -Package: expl3 2026-01-19 L3 programming layer (loader) - -(/usr/share/texmf-dist/tex/latex/l3backend/l3backend-xetex.def -File: l3backend-xetex.def 2025-10-09 L3 backend support: XeTeX -\g__graphics_track_int=\count282 -\g__pdfannot_backend_int=\count283 -\g__pdfannot_backend_link_int=\count284 -)) -Package: xparse 2025-10-09 L3 Experimental document command parser -) -Package: fontspec 2025/09/29 v2.9g Font selection for XeLaTeX and LuaLaTeX - -(/usr/share/texmf-dist/tex/latex/fontspec/fontspec-xetex.sty -Package: fontspec-xetex 2025/09/29 v2.9g Font selection for XeLaTeX and LuaLaTe -X -\l__fontspec_script_int=\count285 -\l__fontspec_language_int=\count286 -\l__fontspec_strnum_int=\count287 -\l__fontspec_tmp_int=\count288 -\l__fontspec_tmpa_int=\count289 -\l__fontspec_tmpb_int=\count290 -\l__fontspec_tmpc_int=\count291 -\l__fontspec_em_int=\count292 -\l__fontspec_emdef_int=\count293 -\l__fontspec_strong_int=\count294 -\l__fontspec_strongdef_int=\count295 -\l__fontspec_tmpa_dim=\dimen157 -\l__fontspec_tmpb_dim=\dimen158 -\l__fontspec_tmpc_dim=\dimen159 - -(/usr/share/texmf-dist/tex/latex/base/fontenc.sty -Package: fontenc 2025/07/18 v2.1d Standard LaTeX package -) -(/usr/share/texmf-dist/tex/latex/fontspec/fontspec.cfg))) - -Package fontspec Info: -(fontspec) Hurmit Nerd Font Mono scale = 0.7. - - -Package fontspec Info: -(fontspec) Hurmit Nerd Font Mono scale = 0.7. - - -Package fontspec Info: -(fontspec) Hurmit Nerd Font Mono/B scale = 0.7. - - -Package fontspec Info: -(fontspec) Hurmit Nerd Font Mono/I scale = 0.7. - - -Package fontspec Info: -(fontspec) Hurmit Nerd Font Mono/BI scale = 0.7. - - -Package fontspec Info: -(fontspec) Font family 'HurmitNerdFontMono(0)' created for font -(fontspec) 'Hurmit Nerd Font Mono' with options -(fontspec) [WordSpace={1,0,0},HyphenChar=None,PunctuationSpace=Word -Space,Scale=0.7]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: <->s*[0.7]"Hurmit -(fontspec) Nerd Font Mono/OT:script=DFLT;language=dflt;" -(fontspec) - 'bold' (b/n) with NFSS spec.: <->s*[0.7]"Hurmit Nerd -(fontspec) Font Mono/B/OT:script=DFLT;language=dflt;" -(fontspec) - 'italic' (m/it) with NFSS spec.: <->s*[0.7]"Hurmit -(fontspec) Nerd Font Mono/I/OT:script=DFLT;language=dflt;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.7]"Hurmit Nerd Font -(fontspec) Mono/BI/OT:script=DFLT;language=dflt;" - - -No file main.aux. -\openout1 = `main.aux'. - -LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for TU/lmr/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 11. -LaTeX Font Info: ... okay on input line 11. -*geometry* driver: auto-detecting -*geometry* detected driver: xetex -*geometry* verbose mode - [ preamble ] result: -* driver: xetex -* paper: a4paper -* layout: -* layoutoffset:(h,v)=(0.0pt,0.0pt) -* modes: -* h-part:(L,W,R)=(72.26999pt, 452.9679pt, 72.26999pt) -* v-part:(T,H,B)=(72.26999pt, 700.50687pt, 72.26999pt) -* \paperwidth=597.50787pt -* \paperheight=845.04684pt -* \textwidth=452.9679pt -* \textheight=700.50687pt -* \oddsidemargin=0.0pt -* \evensidemargin=0.0pt -* \topmargin=-37.0pt -* \headheight=12.0pt -* \headsep=25.0pt -* \topskip=10.0pt -* \footskip=30.0pt -* \marginparwidth=57.0pt -* \marginparsep=11.0pt -* \columnsep=10.0pt -* \skip\footins=9.0pt plus 4.0pt minus 2.0pt -* \hoffset=0.0pt -* \voffset=0.0pt -* \mag=1000 -* \@twocolumnfalse -* \@twosidefalse -* \@mparswitchfalse -* \@reversemarginfalse -* (1in=72.27pt=25.4mm, 1cm=28.453pt) - - -Package fontspec Info: -(fontspec) Adjusting the maths setup (use [no-math] to avoid -(fontspec) this). - -\symlegacymaths=\mathgroup4 -LaTeX Font Info: Overwriting symbol font `legacymaths' in version `bold' -(Font) OT1/cmr/m/n --> OT1/cmr/bx/n on input line 11. -LaTeX Font Info: Redeclaring math accent \acute on input line 11. -LaTeX Font Info: Redeclaring math accent \grave on input line 11. -LaTeX Font Info: Redeclaring math accent \ddot on input line 11. -LaTeX Font Info: Redeclaring math accent \tilde on input line 11. -LaTeX Font Info: Redeclaring math accent \bar on input line 11. -LaTeX Font Info: Redeclaring math accent \breve on input line 11. -LaTeX Font Info: Redeclaring math accent \check on input line 11. -LaTeX Font Info: Redeclaring math accent \hat on input line 11. -LaTeX Font Info: Redeclaring math accent \dot on input line 11. -LaTeX Font Info: Redeclaring math accent \mathring on input line 11. -LaTeX Font Info: Redeclaring math symbol \colon on input line 11. -LaTeX Font Info: Redeclaring math symbol \Gamma on input line 11. -LaTeX Font Info: Redeclaring math symbol \Delta on input line 11. -LaTeX Font Info: Redeclaring math symbol \Theta on input line 11. -LaTeX Font Info: Redeclaring math symbol \Lambda on input line 11. -LaTeX Font Info: Redeclaring math symbol \Xi on input line 11. -LaTeX Font Info: Redeclaring math symbol \Pi on input line 11. -LaTeX Font Info: Redeclaring math symbol \Sigma on input line 11. -LaTeX Font Info: Redeclaring math symbol \Upsilon on input line 11. -LaTeX Font Info: Redeclaring math symbol \Phi on input line 11. -LaTeX Font Info: Redeclaring math symbol \Psi on input line 11. -LaTeX Font Info: Redeclaring math symbol \Omega on input line 11. -LaTeX Font Info: Redeclaring math symbol \mathdollar on input line 11. -LaTeX Font Info: Redeclaring symbol font `operators' on input line 11. -LaTeX Font Info: Encoding `OT1' has changed to `TU' for symbol font -(Font) `operators' in the math version `normal' on input line 11. -LaTeX Font Info: Overwriting symbol font `operators' in version `normal' -(Font) OT1/cmr/m/n --> TU/lmr/m/n on input line 11. -LaTeX Font Info: Encoding `OT1' has changed to `TU' for symbol font -(Font) `operators' in the math version `bold' on input line 11. -LaTeX Font Info: Overwriting symbol font `operators' in version `bold' -(Font) OT1/cmr/bx/n --> TU/lmr/m/n on input line 11. -LaTeX Font Info: Overwriting symbol font `operators' in version `normal' -(Font) TU/lmr/m/n --> TU/lmr/m/n on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' -(Font) OT1/cmr/m/it --> TU/lmr/m/it on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' -(Font) OT1/cmr/bx/n --> TU/lmr/b/n on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' -(Font) OT1/cmss/m/n --> TU/lmss/m/n on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' -(Font) OT1/cmtt/m/n --> TU/HurmitNerdFontMono(0)/m/n on input -line 11. -LaTeX Font Info: Overwriting symbol font `operators' in version `bold' -(Font) TU/lmr/m/n --> TU/lmr/b/n on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' -(Font) OT1/cmr/bx/it --> TU/lmr/b/it on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' -(Font) OT1/cmss/bx/n --> TU/lmss/b/n on input line 11. -LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' -(Font) OT1/cmtt/m/n --> TU/HurmitNerdFontMono(0)/b/n on input -line 11. -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <7> on input line 14. -LaTeX Font Info: External font `cmex10' loaded for size -(Font) <5> on input line 14. -LaTeX Font Info: Font shape `TU/HurmitNerdFontMono(0)/m/n' will be -(Font) scaled to size 6.99997pt on input line 22. - -LaTeX Warning: Float too large for page by 44.293pt on input line 93. - -[1 - -] [2] (./main.aux) - *********** -LaTeX2e <2025-11-01> -L3 programming layer <2026-01-19> - *********** - ) -Here is how much of TeX's memory you used: - 3526 strings out of 470191 - 106539 string characters out of 5479698 - 562689 words of memory out of 5000000 - 32135 multiletter control sequences out of 15000+600000 - 627857 words of font info for 57 fonts, out of 8000000 for 9000 - 14 hyphenation exceptions out of 8191 - 73i,9n,93p,432b,328s stack positions out of 10000i,1000n,20000p,200000b,200000s - -Output written on main.pdf (2 pages). diff --git a/language/main.pdf b/language/main.pdf index 28d0d6e35c3049a3907ca01f9e217832091acaff..d99df9fdf240c7417ba24ebce0a01ab7831c12eb 100644 GIT binary patch delta 13777 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

uO6Y8L8~*d^48JSp$*bd{vE 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 H1^0geoo-ST delta 13100 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

e=P4QdHwft_2dK*!Xdu^Y6F-5jYF*1!?%m85 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 Lw>|vi@2CF-{$tl% literal 0 HcmV?d00001 diff --git a/tests.py b/tests.py index 2b5b9da..8c564f6 100644 --- a/tests.py +++ b/tests.py @@ -606,6 +606,20 @@ builtin_tests = [ ("TYPVS(FVNCTIO () VT { REDI(I) })", Program([], [ExpressionStatement(BuiltIn("TYPVS", [Fvnctio([], [Redi([Numeral("I")])])]))]), ValStr("FVNCTIO")), # TYPVS: null ("TYPVS(NVLLVS)", Program([], [ExpressionStatement(BuiltIn("TYPVS", [Nullus()]))]), ValStr("NVLLVS")), + # QVAERE: basic literal match + ('QVAERE("ab", "abcabc")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("ab"), String("abcabc")]))]), ValList([ValStr("ab"), ValStr("ab")])), + # QVAERE: no match → empty list + ('QVAERE("xyz", "abc")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("xyz"), String("abc")]))]), ValList([])), + # QVAERE: regex character class + ('QVAERE("[a-z]+", "abc123def")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("[a-z]+"), String("abc123def")]))]), ValList([ValStr("abc"), ValStr("def")])), + # QVAERE: empty text → empty list + ('QVAERE("a", "")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("a"), String("")]))]), ValList([])), + # QVAERE: capture groups still return full match + ('QVAERE("(a)(b)", "ab")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("(a)(b)"), String("ab")]))]), ValList([ValStr("ab")])), + # QVAERE: empty pattern matches between every character + ('QVAERE("", "ab")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String(""), String("ab")]))]), ValList([ValStr(""), ValStr(""), ValStr("")])), + # QVAERE: dot matches any character + ('QVAERE(".", "ab")', Program([], [ExpressionStatement(BuiltIn("QVAERE", [String("."), String("ab")]))]), ValList([ValStr("a"), ValStr("b")])), ] class TestBuiltins(unittest.TestCase): @@ -687,6 +701,9 @@ error_tests = [ ("CVM FRACTIO\n[I, II, III][I VSQVE IIIS]", CentvrionError), # slice with fractional upper bound ("CVM FRACTIO\n[I, II, III][I / II VSQVE III]", CentvrionError), # slice with division-fraction lower bound ("TEMPTA {\nDESIGNA x VT I / NVLLVS\n} CAPE e {\nDESIGNA y VT I / NVLLVS\n}", CentvrionError), # uncaught error in catch block propagates + ('QVAERE(I, "abc")', CentvrionError), # QVAERE requires strings, not int + ('QVAERE("abc", I)', CentvrionError), # QVAERE requires strings, not int + ('QVAERE("[", "abc")', CentvrionError), # QVAERE invalid regex ] class TestErrors(unittest.TestCase):