Refactored the PCRE2 extension plus adding some regex examples using this extension. (0.41.7)
This commit is contained in:
parent
4825007b97
commit
be590414af
17 changed files with 637 additions and 404 deletions
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
project(fun VERSION 0.41.6 LANGUAGES C)
|
||||
project(fun VERSION 0.41.7 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
|
|
|||
2
Doxyfile
2
Doxyfile
|
|
@ -48,7 +48,7 @@ PROJECT_NAME = "Fun API Documentation"
|
|||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 0.41.5
|
||||
PROJECT_NUMBER = 0.41.7
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewers a
|
||||
|
|
|
|||
|
|
@ -11,23 +11,203 @@
|
|||
* Added: 2025-11-25
|
||||
*/
|
||||
|
||||
/*
|
||||
include <regex/pcre2.fun>
|
||||
// PCRE2 example using VM builtins directly (no class wrapper)
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
rx = Pcre2()
|
||||
print("-- PCRE2 builtins (no class) --")
|
||||
|
||||
text = "E-mails: one@example.com, Two@Example.COM; invalid: x@y"
|
||||
pattern = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
|
||||
pattern = "(\\w+)" // capture a word
|
||||
text = "Hello 123 world"
|
||||
|
||||
print("Has email? ", rx.test(pattern, text, rx.i()))
|
||||
// Flags: 1=I, 2=M, 4=S, 8=U (UTF), 16=X; we’ll use UTF by default
|
||||
flags = 8
|
||||
|
||||
first = rx.match(pattern, text, rx.i())
|
||||
if first != nil {
|
||||
print("First: ", first["full"])
|
||||
}
|
||||
print("test:")
|
||||
print(pcre2_test(pattern, text, flags))
|
||||
|
||||
all = rx.find_all(pattern, text, rx.i())
|
||||
for m in all {
|
||||
print("Found: ", m["full"])
|
||||
}
|
||||
m = pcre2_match(pattern, text, flags)
|
||||
if (m != nil)
|
||||
print("first full:")
|
||||
print(m["full"])
|
||||
print("span:")
|
||||
print(m["start"])
|
||||
print("..")
|
||||
print(m["end"])
|
||||
print("groups count:")
|
||||
print(len(m["groups"]))
|
||||
|
||||
all = pcre2_findall("\\w+", text, flags)
|
||||
for x in all
|
||||
print("all:")
|
||||
print(x["full"])
|
||||
print("@")
|
||||
print(x["start"])
|
||||
print("..")
|
||||
print(x["end"])
|
||||
|
||||
print("")
|
||||
print("-- More regex demos --")
|
||||
|
||||
// Helper: OR flags (uses VM bor opcode)
|
||||
fun OR(a, b)
|
||||
return bor(a, b)
|
||||
|
||||
// Demo 1: E-mail extraction (case-insensitive)
|
||||
email_text = "E-mails: one@example.com, Two@Example.COM; invalid: x@y"
|
||||
email_pat = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
|
||||
email_flags = OR(flags, 1) // UTF | I
|
||||
print("Emails (findall):")
|
||||
emails = pcre2_findall(email_pat, email_text, email_flags)
|
||||
for e in emails
|
||||
print(e["full"])
|
||||
|
||||
// Demo 2: URLs (very simple, for demo purposes)
|
||||
url_text = "See http://example.com and https://fun-lang.xyz/docs?x=1#top"
|
||||
url_pat = "https?://[A-Za-z0-9._~:/?#[@]!$&'()*+,;=%-]+"
|
||||
print("URLs:")
|
||||
for u in pcre2_findall(url_pat, url_text, flags)
|
||||
print(u["full"])
|
||||
|
||||
// Demo 3: IPv4 addresses
|
||||
ip_text = "ping 8.8.8.8 and 192.168.0.1; not 999.999.999.999"
|
||||
ip_pat = "(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)"
|
||||
print("IPv4:")
|
||||
for ip in pcre2_findall(ip_pat, ip_text, flags)
|
||||
print(ip["full"])
|
||||
|
||||
// Demo 4: Dates (YYYY-MM-DD)
|
||||
date_text = "Born 1999-12-31, updated 2025-11-25, bad 2025-13-40"
|
||||
date_pat = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"
|
||||
print("Dates (with groups Y/M/D):")
|
||||
for d in pcre2_findall(date_pat, date_text, flags)
|
||||
print(d["full"])
|
||||
print(join(d["groups"], "/"))
|
||||
|
||||
// Demo 5: Hex colors (#RRGGBB)
|
||||
color_text = "Palette: #FF00FF, #1a2b3c, not #abcd or #12345g"
|
||||
color_pat = "#[0-9A-Fa-f]{6}"
|
||||
print("Hex colors:")
|
||||
for c in pcre2_findall(color_pat, color_text, flags)
|
||||
print(c["full"])
|
||||
|
||||
// Demo 6: Quoted strings with escapes
|
||||
q_text = 'say "hi there" and "indented" plus "quote\\"inside"'
|
||||
q_pat = '"([^"\\\\]|\\\\.)*"'
|
||||
print("Quoted strings (with escapes):")
|
||||
for q in pcre2_findall(q_pat, q_text, flags)
|
||||
print(q["full"])
|
||||
|
||||
// Demo 7: Multiline anchors with /m (M flag)
|
||||
ml_text = "first line\nSecond line\nthird"
|
||||
ml_pat = "^(\\w+)"
|
||||
ml_flags = OR(flags, 2) // UTF | M
|
||||
print("Multiline ^ anchors (first token of each line):")
|
||||
for ml in pcre2_findall(ml_pat, ml_text, ml_flags)
|
||||
print(ml["full"])
|
||||
|
||||
// Demo 8: Dotall vs non-dotall
|
||||
ds_text = "BEGIN\nline1\nline2\nEND"
|
||||
pat_nd = "BEGIN.*END" // default: . does not match newlines
|
||||
pat_ds = "BEGIN.*END" // with DOTALL, it does
|
||||
print("Dotall OFF (should fail):")
|
||||
print(pcre2_test(pat_nd, ds_text, flags))
|
||||
print("Dotall ON (should match):")
|
||||
print(pcre2_test(pat_ds, ds_text, OR(flags, 4)))
|
||||
|
||||
// Demo 9: Word boundaries and case-insensitive find
|
||||
wb_text = "The theater and the THE can differ."
|
||||
wb_pat = "\\bthe\\b"
|
||||
print("Word boundary, case-insensitive:")
|
||||
for w in pcre2_findall(wb_pat, wb_text, OR(flags, 1))
|
||||
print(w["full"])
|
||||
|
||||
// Demo 10: Lookahead — word followed by number
|
||||
la_text = "foo 123, bar, baz 9"
|
||||
la_pat = "\\w+(?=\\s+\\d+)"
|
||||
print("Lookahead (word before number):")
|
||||
for a in pcre2_findall(la_pat, la_text, flags)
|
||||
print(a["full"])
|
||||
|
||||
// Demo 11: Non-greedy vs greedy
|
||||
ng_text = "<a>one</a><a>two</a>"
|
||||
greedy = "<a>.*</a>"
|
||||
lazy = "<a>.*?</a>"
|
||||
print("Greedy:")
|
||||
for g in pcre2_findall(greedy, ng_text, OR(flags, 4)) // DOTALL ensures '.' covers any
|
||||
print(g["full"])
|
||||
print("Non-greedy:")
|
||||
for l in pcre2_findall(lazy, ng_text, OR(flags, 4))
|
||||
print(l["full"])
|
||||
|
||||
/* Expected output:
|
||||
-- PCRE2 builtins (no class) --
|
||||
test:
|
||||
1
|
||||
first full:
|
||||
Hello
|
||||
span:
|
||||
0
|
||||
..
|
||||
5
|
||||
groups count:
|
||||
1
|
||||
all:
|
||||
Hello
|
||||
@
|
||||
0
|
||||
..
|
||||
5
|
||||
all:
|
||||
123
|
||||
@
|
||||
6
|
||||
..
|
||||
9
|
||||
all:
|
||||
world
|
||||
@
|
||||
10
|
||||
..
|
||||
15
|
||||
|
||||
-- More regex demos --
|
||||
Emails (findall):
|
||||
one@example.com
|
||||
Two@Example.COM
|
||||
URLs:
|
||||
IPv4:
|
||||
8.8.8.8
|
||||
192.168.0.1
|
||||
Dates (with groups Y/M/D):
|
||||
1999-12-31
|
||||
1999/12/31
|
||||
2025-11-25
|
||||
2025/11/25
|
||||
Hex colors:
|
||||
#FF00FF
|
||||
#1a2b3c
|
||||
Quoted strings (with escapes):
|
||||
"hi there"
|
||||
"indented"
|
||||
"quote\"inside"
|
||||
Multiline ^ anchors (first token of each line):
|
||||
first
|
||||
Second
|
||||
third
|
||||
Dotall OFF (should fail):
|
||||
0
|
||||
Dotall ON (should match):
|
||||
1
|
||||
Word boundary, case-insensitive:
|
||||
The
|
||||
the
|
||||
THE
|
||||
Lookahead (word before number):
|
||||
foo
|
||||
baz
|
||||
Greedy:
|
||||
<a>one</a><a>two</a>
|
||||
Non-greedy:
|
||||
<a>one</a>
|
||||
<a>two</a>
|
||||
*/
|
||||
|
|
|
|||
34
examples/extensions/pcre2/pcre2_findall.fun
Normal file
34
examples/extensions/pcre2/pcre2_findall.fun
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Minimal example showing only the pcre2_findall opcode
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
pattern = "[A-Za-z]+"
|
||||
text = "One two THREE four"
|
||||
|
||||
// 1 = I (ignore case) so we catch mixed case words uniformly
|
||||
flags = 1
|
||||
|
||||
print("pcre2_findall example:")
|
||||
matches = pcre2_findall(pattern, text, flags)
|
||||
for m in matches
|
||||
print(m["full"]) // prints matched word
|
||||
|
||||
/* Expected output:
|
||||
pcre2_findall example:
|
||||
One
|
||||
two
|
||||
THREE
|
||||
four
|
||||
*/
|
||||
38
examples/extensions/pcre2/pcre2_lib_findall.fun
Normal file
38
examples/extensions/pcre2/pcre2_lib_findall.fun
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Example: Using stdlib PCRE2 class to find all matches
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
#include <regex/pcre2.fun>
|
||||
|
||||
rx = PCRE2()
|
||||
|
||||
pattern = "[A-Za-z]+"
|
||||
text = "One two THREE four"
|
||||
|
||||
// Case-insensitive via class flag helper
|
||||
flags = rx.i()
|
||||
|
||||
print("pcre2_lib_findall example:")
|
||||
matches = rx.find_all(pattern, text, flags)
|
||||
for m in matches
|
||||
print(m["full"]) // prints matched word
|
||||
|
||||
/* Expected output:
|
||||
pcre2_lib_findall examples:
|
||||
One
|
||||
two
|
||||
THREE
|
||||
four
|
||||
*/
|
||||
43
examples/extensions/pcre2/pcre2_lib_match.fun
Normal file
43
examples/extensions/pcre2/pcre2_lib_match.fun
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Example: Using stdlib PCRE2 class to perform a single match
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
#include <regex/pcre2.fun>
|
||||
|
||||
rx = PCRE2()
|
||||
|
||||
pattern = "(foo)-(\\d+)"
|
||||
text = "bar foo-123 baz"
|
||||
|
||||
// Default UTF flag is fine; show combining flags as well (UTF | I)
|
||||
flags = bor(rx.u(), rx.i())
|
||||
|
||||
print("pcre2_lib_match example:")
|
||||
m = rx.match(pattern, text, flags)
|
||||
if (m != nil)
|
||||
print(m["full"]) // foo-123
|
||||
print(m["start"]) // start index
|
||||
print(m["end"]) // end index (exclusive)
|
||||
print(len(m["groups"])) // 2 groups captured
|
||||
else
|
||||
print("no match")
|
||||
|
||||
/* Expected output:
|
||||
pcre2_lib_match examples:
|
||||
foo-123
|
||||
4
|
||||
11
|
||||
2
|
||||
*/
|
||||
36
examples/extensions/pcre2/pcre2_lib_test.fun
Normal file
36
examples/extensions/pcre2/pcre2_lib_test.fun
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Example: Using stdlib PCRE2 class to run a simple test
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
#include <regex/pcre2.fun>
|
||||
|
||||
rx = PCRE2()
|
||||
|
||||
pattern = "^hello$"
|
||||
text = "Hello" // mismatches unless case-insensitive is set
|
||||
|
||||
// Use case-insensitive flag from the class helper
|
||||
flags = rx.i()
|
||||
|
||||
print("pcre2_lib_test example:")
|
||||
print(rx.test(pattern, text, flags)) // prints 1 on match, 0 otherwise
|
||||
|
||||
/* Expected output:
|
||||
pcre2_lib_test example:
|
||||
foo-123
|
||||
4
|
||||
11
|
||||
2
|
||||
*/
|
||||
39
examples/extensions/pcre2/pcre2_match.fun
Normal file
39
examples/extensions/pcre2/pcre2_match.fun
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Minimal example showing only the pcre2_match opcode
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
pattern = "(foo)-(\\d+)"
|
||||
text = "bar foo-123 baz"
|
||||
|
||||
// 8 = UTF flag is harmless here; kept for consistency
|
||||
flags = 8
|
||||
|
||||
print("pcre2_match example:")
|
||||
m = pcre2_match(pattern, text, flags)
|
||||
if (m != nil)
|
||||
print(m["full"]) // foo-123
|
||||
print(m["start"]) // start index
|
||||
print(m["end"]) // end index (exclusive)
|
||||
print(len(m["groups"])) // 2 groups captured: foo and 123
|
||||
else
|
||||
print("no match")
|
||||
|
||||
/* Expected output:
|
||||
pcre2_match example:
|
||||
foo-123
|
||||
4
|
||||
11
|
||||
2
|
||||
*/
|
||||
|
|
@ -1,214 +0,0 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-11-25
|
||||
*/
|
||||
|
||||
// PCRE2 example using VM builtins directly (no class wrapper)
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
print("-- PCRE2 builtins (no class) --")
|
||||
|
||||
pattern = "(\\w+)" // capture a word
|
||||
text = "Hello 123 world"
|
||||
|
||||
// Flags: 1=I, 2=M, 4=S, 8=U (UTF), 16=X; we’ll use UTF by default
|
||||
flags = 8
|
||||
|
||||
print("test:")
|
||||
print(pcre2_test(pattern, text, flags))
|
||||
|
||||
m = pcre2_match(pattern, text, flags)
|
||||
if (m != nil)
|
||||
print("first full:")
|
||||
print(m["full"])
|
||||
print("span:")
|
||||
print(m["start"])
|
||||
print("..")
|
||||
print(m["end"])
|
||||
print("groups count:")
|
||||
print(len(m["groups"]))
|
||||
|
||||
all = pcre2_findall("\\w+", text, flags)
|
||||
for x in all
|
||||
print("all:")
|
||||
print(x["full"])
|
||||
print("@")
|
||||
print(x["start"])
|
||||
print("..")
|
||||
print(x["end"])
|
||||
|
||||
print("")
|
||||
print("-- More regex demos --")
|
||||
|
||||
// Helper: OR flags (uses VM bor opcode)
|
||||
fun OR(a, b)
|
||||
return bor(a, b)
|
||||
|
||||
// Demo 1: E-mail extraction (case-insensitive)
|
||||
email_text = "E-mails: one@example.com, Two@Example.COM; invalid: x@y"
|
||||
email_pat = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
|
||||
email_flags = OR(flags, 1) // UTF | I
|
||||
print("Emails (findall):")
|
||||
emails = pcre2_findall(email_pat, email_text, email_flags)
|
||||
for e in emails
|
||||
print(e["full"])
|
||||
|
||||
// Demo 2: URLs (very simple, for demo purposes)
|
||||
url_text = "See http://example.com and https://fun-lang.xyz/docs?x=1#top"
|
||||
url_pat = "https?://[A-Za-z0-9._~:/?#[@]!$&'()*+,;=%-]+"
|
||||
print("URLs:")
|
||||
for u in pcre2_findall(url_pat, url_text, flags)
|
||||
print(u["full"])
|
||||
|
||||
// Demo 3: IPv4 addresses
|
||||
ip_text = "ping 8.8.8.8 and 192.168.0.1; not 999.999.999.999"
|
||||
ip_pat = "(?:(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)"
|
||||
print("IPv4:")
|
||||
for ip in pcre2_findall(ip_pat, ip_text, flags)
|
||||
print(ip["full"])
|
||||
|
||||
// Demo 4: Dates (YYYY-MM-DD)
|
||||
date_text = "Born 1999-12-31, updated 2025-11-25, bad 2025-13-40"
|
||||
date_pat = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])"
|
||||
print("Dates (with groups Y/M/D):")
|
||||
for d in pcre2_findall(date_pat, date_text, flags)
|
||||
print(d["full"])
|
||||
print(join(d["groups"], "/"))
|
||||
|
||||
// Demo 5: Hex colors (#RRGGBB)
|
||||
color_text = "Palette: #FF00FF, #1a2b3c, not #abcd or #12345g"
|
||||
color_pat = "#[0-9A-Fa-f]{6}"
|
||||
print("Hex colors:")
|
||||
for c in pcre2_findall(color_pat, color_text, flags)
|
||||
print(c["full"])
|
||||
|
||||
// Demo 6: Quoted strings with escapes
|
||||
q_text = 'say "hi there" and "indented" plus "quote\\"inside"'
|
||||
q_pat = '"([^"\\\\]|\\\\.)*"'
|
||||
print("Quoted strings (with escapes):")
|
||||
for q in pcre2_findall(q_pat, q_text, flags)
|
||||
print(q["full"])
|
||||
|
||||
// Demo 7: Multiline anchors with /m (M flag)
|
||||
ml_text = "first line\nSecond line\nthird"
|
||||
ml_pat = "^(\\w+)"
|
||||
ml_flags = OR(flags, 2) // UTF | M
|
||||
print("Multiline ^ anchors (first token of each line):")
|
||||
for ml in pcre2_findall(ml_pat, ml_text, ml_flags)
|
||||
print(ml["full"])
|
||||
|
||||
// Demo 8: Dotall vs non-dotall
|
||||
ds_text = "BEGIN\nline1\nline2\nEND"
|
||||
pat_nd = "BEGIN.*END" // default: . does not match newlines
|
||||
pat_ds = "BEGIN.*END" // with DOTALL, it does
|
||||
print("Dotall OFF (should fail):")
|
||||
print(pcre2_test(pat_nd, ds_text, flags))
|
||||
print("Dotall ON (should match):")
|
||||
print(pcre2_test(pat_ds, ds_text, OR(flags, 4)))
|
||||
|
||||
// Demo 9: Word boundaries and case-insensitive find
|
||||
wb_text = "The theater and the THE can differ."
|
||||
wb_pat = "\\bthe\\b"
|
||||
print("Word boundary, case-insensitive:")
|
||||
for w in pcre2_findall(wb_pat, wb_text, OR(flags, 1))
|
||||
print(w["full"])
|
||||
|
||||
// Demo 10: Lookahead — word followed by number
|
||||
la_text = "foo 123, bar, baz 9"
|
||||
la_pat = "\\w+(?=\\s+\\d+)"
|
||||
print("Lookahead (word before number):")
|
||||
for a in pcre2_findall(la_pat, la_text, flags)
|
||||
print(a["full"])
|
||||
|
||||
// Demo 11: Non-greedy vs greedy
|
||||
ng_text = "<a>one</a><a>two</a>"
|
||||
greedy = "<a>.*</a>"
|
||||
lazy = "<a>.*?</a>"
|
||||
print("Greedy:")
|
||||
for g in pcre2_findall(greedy, ng_text, OR(flags, 4)) // DOTALL ensures '.' covers any
|
||||
print(g["full"])
|
||||
print("Non-greedy:")
|
||||
for l in pcre2_findall(lazy, ng_text, OR(flags, 4))
|
||||
print(l["full"])
|
||||
|
||||
/* Expected output:
|
||||
-- PCRE2 builtins (no class) --
|
||||
test:
|
||||
1
|
||||
first full:
|
||||
Hello
|
||||
span:
|
||||
0
|
||||
..
|
||||
5
|
||||
groups count:
|
||||
1
|
||||
all:
|
||||
Hello
|
||||
@
|
||||
0
|
||||
..
|
||||
5
|
||||
all:
|
||||
123
|
||||
@
|
||||
6
|
||||
..
|
||||
9
|
||||
all:
|
||||
world
|
||||
@
|
||||
10
|
||||
..
|
||||
15
|
||||
|
||||
-- More regex demos --
|
||||
Emails (findall):
|
||||
one@example.com
|
||||
Two@Example.COM
|
||||
URLs:
|
||||
IPv4:
|
||||
8.8.8.8
|
||||
192.168.0.1
|
||||
Dates (with groups Y/M/D):
|
||||
1999-12-31
|
||||
1999/12/31
|
||||
2025-11-25
|
||||
2025/11/25
|
||||
Hex colors:
|
||||
#FF00FF
|
||||
#1a2b3c
|
||||
Quoted strings (with escapes):
|
||||
"hi there"
|
||||
"indented"
|
||||
"quote\"inside"
|
||||
Multiline ^ anchors (first token of each line):
|
||||
first
|
||||
Second
|
||||
third
|
||||
Dotall OFF (should fail):
|
||||
0
|
||||
Dotall ON (should match):
|
||||
1
|
||||
Word boundary, case-insensitive:
|
||||
The
|
||||
the
|
||||
THE
|
||||
Lookahead (word before number):
|
||||
foo
|
||||
baz
|
||||
Greedy:
|
||||
<a>one</a><a>two</a>
|
||||
Non-greedy:
|
||||
<a>one</a>
|
||||
<a>two</a>
|
||||
*/
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2025-11-25
|
||||
*/
|
||||
|
||||
/*
|
||||
include <regex/pcre2.fun>
|
||||
|
||||
re = PCRE2()
|
||||
|
||||
print("Testing PCRE2 showcase...")
|
||||
|
||||
print(re.test("\\d+", "Order #1234"))
|
||||
|
||||
m = re.match("(\\w+)", "hello WORLD", re.i())
|
||||
if m != nil {
|
||||
print(m["full"]) // hello
|
||||
print(len(m["groups"]))
|
||||
}
|
||||
|
||||
for x in re.find_all("[a-z]+", "One two THREE four", re.i()) {
|
||||
print(x["full"]) // one two four
|
||||
}
|
||||
*/
|
||||
29
examples/extensions/pcre2/pcre2_test.fun
Normal file
29
examples/extensions/pcre2/pcre2_test.fun
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env fun
|
||||
|
||||
/*
|
||||
* This file is part of the Fun programming language.
|
||||
* https://fun-lang.xyz/
|
||||
*
|
||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||
* Licensed under the terms of the Apache-2.0 license.
|
||||
* https://opensource.org/license/apache-2-0
|
||||
*
|
||||
* Added: 2026-05-07
|
||||
*/
|
||||
|
||||
// Minimal example showing only the pcre2_test opcode
|
||||
// Requires building Fun with -DFUN_WITH_PCRE2=ON
|
||||
|
||||
pattern = "^hello$"
|
||||
text = "Hello" // mismatches unless case-insensitive is set
|
||||
|
||||
// Flags: 1=I (ignore case), 2=M, 4=S, 8=U (UTF), 16=X
|
||||
flags = 1 // make it case-insensitive so it matches
|
||||
|
||||
print("pcre2_test example:")
|
||||
print(pcre2_test(pattern, text, flags)) // prints 1 on match, 0 otherwise
|
||||
|
||||
/* Expected output:
|
||||
pcre2_test example:
|
||||
1
|
||||
*/
|
||||
|
|
@ -15,14 +15,19 @@
|
|||
// Provides a small class with flags and user-friendly methods.
|
||||
|
||||
class PCRE2()
|
||||
|
||||
fun i(this)
|
||||
return 1
|
||||
|
||||
fun m(this)
|
||||
return 2
|
||||
|
||||
fun s(this)
|
||||
return 4
|
||||
|
||||
fun u(this)
|
||||
return 8
|
||||
|
||||
fun x(this)
|
||||
return 16
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,43 @@
|
|||
|
||||
/**
|
||||
* @file pcre2.c
|
||||
* @brief PCRE2 configuration header and includes for regex-related opcodes.
|
||||
* @brief PCRE2 helpers for Fun VM extension opcodes (conditional build).
|
||||
*
|
||||
* Ensures PCRE2 code unit width is defined consistently prior to including
|
||||
* <pcre2.h> when FUN_WITH_PCRE2 is enabled.
|
||||
* This module centralizes the concrete PCRE2 implementation so VM opcodes in
|
||||
* src/vm/pcre2/*.c only perform stack marshalling and delegate to these
|
||||
* helpers. This mirrors the approach used by other extensions (e.g. SQLite,
|
||||
* XML2) where the heavy lifting lives under src/extensions/ and the opcodes
|
||||
* just call into small C helpers.
|
||||
*
|
||||
* Build-time feature flag:
|
||||
* - The code in this file is compiled only when FUN_WITH_PCRE2 is enabled.
|
||||
* When disabled, PCRE2-dependent opcodes are built with no-op fallbacks.
|
||||
*
|
||||
* PCRE2 width configuration:
|
||||
* - PCRE2 requires defining PCRE2_CODE_UNIT_WIDTH before including <pcre2.h> to
|
||||
* select 8/16/32-bit code units. We select 8-bit here. Because the Fun VM
|
||||
* translates many opcode .c files into the same translation unit, it is
|
||||
* important this macro is defined exactly once before the first <pcre2.h>
|
||||
* inclusion. This file ensures that when FUN_WITH_PCRE2 is enabled.
|
||||
*
|
||||
* Flags mapping used by helpers/opcodes (bitmask in the VM):
|
||||
* - 1 -> PCRE2_CASELESS ("i")
|
||||
* - 2 -> PCRE2_MULTILINE ("m")
|
||||
* - 4 -> PCRE2_DOTALL ("s")
|
||||
* - 8 -> PCRE2_UTF ("u")
|
||||
* - 16 -> PCRE2_EXTENDED ("x")
|
||||
*
|
||||
* Value and memory ownership:
|
||||
* - The Value type and helper functions (make_map_empty, make_array_*, map_set,
|
||||
* array_push, make_int, make_string, make_nil, string_substr, etc.) are
|
||||
* provided by the Fun VM and are declared in the including translation unit.
|
||||
* The arrays/maps returned from this module are owned by the caller (the
|
||||
* VM opcode), consistent with other extension helpers.
|
||||
*
|
||||
* Thread-safety:
|
||||
* - These helpers are not inherently thread-safe, but they do not maintain any
|
||||
* internal state beyond stack-local variables. Coordinate usage externally if
|
||||
* the embedding is multi-threaded.
|
||||
*/
|
||||
|
||||
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
||||
|
|
@ -25,4 +58,180 @@
|
|||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @brief Map Fun VM regex flags to PCRE2 compile options.
|
||||
*
|
||||
* The Fun VM passes a small integer bitmask controlling common regex
|
||||
* behaviours. This function translates those bits into the corresponding
|
||||
* PCRE2 compile options.
|
||||
*
|
||||
* Bit mapping:
|
||||
* - 1 -> PCRE2_CASELESS (case-insensitive)
|
||||
* - 2 -> PCRE2_MULTILINE (^ and $ match start/end of line)
|
||||
* - 4 -> PCRE2_DOTALL (dot matches newlines)
|
||||
* - 8 -> PCRE2_UTF (treat pattern/subject as UTF-8)
|
||||
* - 16 -> PCRE2_EXTENDED (ignore unescaped whitespace and allow comments)
|
||||
*
|
||||
* @param flags Bitmask provided by the VM.
|
||||
* @return uint32_t PCRE2 options suitable for pcre2_compile().
|
||||
*/
|
||||
static uint32_t fun_pcre2_opts_from_flags(int flags) {
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
return opt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test whether a pattern matches a subject at least once.
|
||||
*
|
||||
* Compiles the given pattern with options derived from the flags bitmask and
|
||||
* runs pcre2_match() once starting at offset 0.
|
||||
*
|
||||
* @param pattern NUL-terminated regex pattern string.
|
||||
* @param subject NUL-terminated subject string.
|
||||
* @param flags VM bitmask translated by fun_pcre2_opts_from_flags().
|
||||
* @return int 1 if pcre2_match() returns a non-negative value; 0 if there is
|
||||
* no match or an error occurs (including compile error or OOM).
|
||||
*
|
||||
* @note This helper performs only a single match attempt at offset 0; it does
|
||||
* not search for subsequent matches. Use fun_pcre2_findall() for that.
|
||||
*/
|
||||
static int fun_pcre2_test(const char *pattern, const char *subject, int flags) {
|
||||
if (!pattern || !subject) return 0;
|
||||
int errorcode = 0; PCRE2_SIZE erroff = 0;
|
||||
uint32_t opt = fun_pcre2_opts_from_flags(flags);
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) return 0;
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
return rc >= 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Build a Value API is provided by the VM; declarations are in the including TU. */
|
||||
/**
|
||||
* @brief Match a pattern once and return a structured result map.
|
||||
*
|
||||
* On success, returns a map with the following keys:
|
||||
* - "full" -> string: the matched substring for group 0
|
||||
* - "start" -> int: start index (0-based) of the match in the subject
|
||||
* - "end" -> int: end index (exclusive)
|
||||
* - "groups" -> array: strings for each captured group (1..n), empty if none
|
||||
*
|
||||
* On no match, pattern compile failure, or memory allocation error, returns
|
||||
* Nil.
|
||||
*
|
||||
* @param pattern NUL-terminated regex pattern string.
|
||||
* @param subject NUL-terminated subject string.
|
||||
* @param flags VM bitmask translated by fun_pcre2_opts_from_flags().
|
||||
* @return Value A VM map Value as described above, or Nil on failure.
|
||||
*
|
||||
* @see fun_pcre2_findall()
|
||||
*/
|
||||
static Value fun_pcre2_match(const char *pattern, const char *subject, int flags) {
|
||||
if (!pattern || !subject) return make_nil();
|
||||
int errorcode = 0; PCRE2_SIZE erroff = 0;
|
||||
uint32_t opt = fun_pcre2_opts_from_flags(flags);
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) return make_nil();
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
if (rc <= 0) {
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
return make_nil();
|
||||
}
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
Value res = make_map_empty();
|
||||
int start0 = (int)ov[0];
|
||||
int end0 = (int)ov[1];
|
||||
char *full = string_substr(subject, start0, end0 - start0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(start0));
|
||||
(void)map_set(&res, "end", make_int(end0));
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2 * i];
|
||||
int e = (int)ov[2 * i + 1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find all non-overlapping matches of a pattern in a subject.
|
||||
*
|
||||
* Scans the subject from left to right and appends, for each non-overlapping
|
||||
* match, a map with the same shape as fun_pcre2_match() to the result array.
|
||||
* If the engine reports an empty match (start == end), the scan advances by a
|
||||
* single code unit to prevent infinite loops.
|
||||
*
|
||||
* On pattern compile failure or allocation error, returns an empty array.
|
||||
*
|
||||
* @param pattern NUL-terminated regex pattern string.
|
||||
* @param subject NUL-terminated subject string.
|
||||
* @param flags VM bitmask translated by fun_pcre2_opts_from_flags().
|
||||
* @return Value An array of match maps; may be empty when no matches are found
|
||||
* or on error.
|
||||
*
|
||||
* @see fun_pcre2_match()
|
||||
*/
|
||||
static Value fun_pcre2_findall(const char *pattern, const char *subject, int flags) {
|
||||
Value out = make_array_from_values(NULL, 0);
|
||||
if (!pattern || !subject) return out;
|
||||
int errorcode = 0; PCRE2_SIZE erroff = 0;
|
||||
uint32_t opt = fun_pcre2_opts_from_flags(flags);
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) return out;
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
size_t subj_len = strlen(subject);
|
||||
size_t start_off = 0;
|
||||
while (1) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)subj_len, start_off, 0, mdata, NULL);
|
||||
if (rc <= 0) break;
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
int s0 = (int)ov[0];
|
||||
int e0 = (int)ov[1];
|
||||
Value res = make_map_empty();
|
||||
char *full = string_substr(subject, s0, e0 - s0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(s0));
|
||||
(void)map_set(&res, "end", make_int(e0));
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2 * i];
|
||||
int e = (int)ov[2 * i + 1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
(void)array_push(&out, res);
|
||||
/* Advance safely (guard against empty match). */
|
||||
if (e0 == s0) {
|
||||
if ((size_t)e0 < subj_len) start_off = e0 + 1; else break;
|
||||
} else {
|
||||
start_off = e0;
|
||||
}
|
||||
}
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
return out;
|
||||
}
|
||||
#endif /* FUN_WITH_PCRE2 */
|
||||
|
|
|
|||
|
|
@ -60,68 +60,7 @@ case OP_PCRE2_FINDALL: {
|
|||
push_value(vm, make_array_from_values(NULL, 0));
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode;
|
||||
PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, make_array_from_values(NULL, 0));
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
Value out = make_array_from_values(NULL, 0);
|
||||
size_t subj_len = strlen(subject);
|
||||
size_t start_off = 0;
|
||||
int gcount = 0;
|
||||
while (1) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)subj_len, start_off, 0, mdata, NULL);
|
||||
if (rc <= 0) break;
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
int s0 = (int)ov[0];
|
||||
int e0 = (int)ov[1];
|
||||
/* result map for this match */
|
||||
Value res = make_map_empty();
|
||||
char *full = string_substr(subject, s0, e0 - s0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(s0));
|
||||
(void)map_set(&res, "end", make_int(e0));
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2 * i];
|
||||
int e = (int)ov[2 * i + 1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
(void)array_push(&out, res);
|
||||
/* advance start offset; guard against empty match */
|
||||
if (e0 == s0) {
|
||||
if ((size_t)e0 < subj_len) {
|
||||
start_off = e0 + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
start_off = e0;
|
||||
}
|
||||
gcount = rc;
|
||||
}
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
Value out = fun_pcre2_findall(pattern, subject, flags);
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, out);
|
||||
|
|
|
|||
|
|
@ -58,58 +58,7 @@ case OP_PCRE2_MATCH: {
|
|||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode;
|
||||
PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
if (rc <= 0) {
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, make_nil());
|
||||
break;
|
||||
}
|
||||
PCRE2_SIZE *ov = pcre2_get_ovector_pointer(mdata);
|
||||
/* Build result map */
|
||||
Value res = make_map_empty();
|
||||
int start0 = (int)ov[0];
|
||||
int end0 = (int)ov[1];
|
||||
char *full = string_substr(subject, start0, end0 - start0);
|
||||
(void)map_set(&res, "full", make_string(full ? full : ""));
|
||||
if (full) free(full);
|
||||
(void)map_set(&res, "start", make_int(start0));
|
||||
(void)map_set(&res, "end", make_int(end0));
|
||||
/* groups array (excluding group 0) */
|
||||
Value groups = make_array_from_values(NULL, 0);
|
||||
for (int i = 1; i < rc; ++i) {
|
||||
int s = (int)ov[2 * i];
|
||||
int e = (int)ov[2 * i + 1];
|
||||
char *gstr = (s >= 0 && e >= s) ? string_substr(subject, s, e - s) : NULL;
|
||||
Value gv = make_string(gstr ? gstr : "");
|
||||
if (gstr) free(gstr);
|
||||
(void)array_push(&groups, gv);
|
||||
}
|
||||
(void)map_set(&res, "groups", groups);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
Value res = fun_pcre2_match(pattern, subject, flags);
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, res);
|
||||
|
|
|
|||
|
|
@ -53,32 +53,10 @@ case OP_PCRE2_TEST: {
|
|||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
#ifndef PCRE2_CODE_UNIT_WIDTH
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#endif
|
||||
#include <pcre2.h>
|
||||
int errorcode;
|
||||
PCRE2_SIZE erroff;
|
||||
uint32_t opt = 0;
|
||||
if (flags & 1) opt |= PCRE2_CASELESS; /* I */
|
||||
if (flags & 2) opt |= PCRE2_MULTILINE; /* M */
|
||||
if (flags & 4) opt |= PCRE2_DOTALL; /* S */
|
||||
if (flags & 8) opt |= PCRE2_UTF; /* U */
|
||||
if (flags & 16) opt |= PCRE2_EXTENDED; /* X */
|
||||
pcre2_code *re = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, opt, &errorcode, &erroff, NULL);
|
||||
if (!re) {
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, make_int(0));
|
||||
break;
|
||||
}
|
||||
pcre2_match_data *mdata = pcre2_match_data_create_from_pattern(re, NULL);
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)subject, (PCRE2_SIZE)strlen(subject), 0, 0, mdata, NULL);
|
||||
pcre2_match_data_free(mdata);
|
||||
pcre2_code_free(re);
|
||||
int rc = fun_pcre2_test(pattern, subject, flags);
|
||||
free(pattern);
|
||||
free(subject);
|
||||
push_value(vm, make_int(rc >= 0 ? 1 : 0));
|
||||
push_value(vm, make_int(rc));
|
||||
#else
|
||||
/* pop args and return 0 when PCRE2 disabled */
|
||||
Value a = pop_value(vm);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue