Skip to content

Commit 6f3ed05

Browse files
authored
Fix regex literal AOT compilation (#132) (#133)
Add support for *regexp.Regexp values in code generator by serializing them as regexp.MustCompile() calls. The regex pattern string is preserved using the Regexp.String() method. This fixes the "unsupported value type *regexp.Regexp" error when using regex literals (#"...") in AOT-compiled code. The re-pattern function already worked because it returns the compiled regexp at runtime rather than embedding it as a constant. Added comprehensive test cases covering various regex patterns including special characters, escapes, quotes, and newlines.
1 parent 4d2f8d2 commit 6f3ed05

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

pkg/runtime/codegen.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"path/filepath"
1010
"reflect"
11+
"regexp"
1112
"sort"
1213
"strings"
1314
"time"
@@ -484,6 +485,10 @@ func (g *Generator) generateValue(value any) string {
484485
case time.Duration:
485486
alias := g.addImportWithAlias("time")
486487
return fmt.Sprintf("%s.Duration(%d)", alias, int64(v))
488+
case *regexp.Regexp:
489+
alias := g.addImportWithAlias("regexp")
490+
// Use MustCompile since we know the pattern is valid (it compiled successfully in the reader)
491+
return fmt.Sprintf("%s.MustCompile(%#v)", alias, v.String())
487492
case *lang.BigDecimal:
488493
return g.generateBigDecimalValue(v)
489494
case bool:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
(ns codegen.test.regex-literal)
2+
3+
;; Test regex literal compilation with various patterns
4+
(def pattern-literal #"[a-z]+")
5+
6+
;; Test with special characters that need escaping
7+
(def pattern-with-backslash #"\\d+")
8+
(def pattern-with-quotes #"\"quoted\"")
9+
(def pattern-with-newline #"line1\nline2")
10+
(def pattern-complex #"^(?:foo|bar)\s*=\s*(['\"]?)(.+?)$")
11+
12+
;; Test re-pattern function call
13+
(def pattern-function (re-pattern "[0-9]+"))
14+
15+
;; Test using the patterns
16+
(defn test-match [s]
17+
(and (re-matches pattern-literal "hello")
18+
(re-matches pattern-function "12345")))
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Code generated by glojure codegen. DO NOT EDIT.
2+
3+
package regex_DASH_literal
4+
5+
import (
6+
fmt "fmt"
7+
lang "github.com/glojurelang/glojure/pkg/lang"
8+
runtime "github.com/glojurelang/glojure/pkg/runtime"
9+
reflect "reflect"
10+
regexp4 "regexp"
11+
)
12+
13+
func init() {
14+
runtime.RegisterNSLoader("codegen/test/regex_literal", LoadNS)
15+
}
16+
17+
func checkDerefVar(v *lang.Var) any {
18+
if v.IsMacro() {
19+
panic(lang.NewIllegalArgumentError(fmt.Sprintf("can't take value of macro: %v", v)))
20+
}
21+
return v.Get()
22+
}
23+
24+
func checkArity(args []any, expected int) {
25+
if len(args) != expected {
26+
panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")"))
27+
}
28+
}
29+
30+
func checkArityGTE(args []any, min int) {
31+
if len(args) < min {
32+
panic(lang.NewIllegalArgumentError("wrong number of arguments (" + fmt.Sprint(len(args)) + ")"))
33+
}
34+
}
35+
36+
// LoadNS initializes the namespace "codegen.test.regex-literal"
37+
func LoadNS() {
38+
sym_clojure_DOT_core := lang.NewSymbol("clojure.core")
39+
sym_codegen_DOT_test_DOT_regex_DASH_literal := lang.NewSymbol("codegen.test.regex-literal")
40+
sym_pattern_DASH_complex := lang.NewSymbol("pattern-complex")
41+
sym_pattern_DASH_function := lang.NewSymbol("pattern-function")
42+
sym_pattern_DASH_literal := lang.NewSymbol("pattern-literal")
43+
sym_pattern_DASH_with_DASH_backslash := lang.NewSymbol("pattern-with-backslash")
44+
sym_pattern_DASH_with_DASH_newline := lang.NewSymbol("pattern-with-newline")
45+
sym_pattern_DASH_with_DASH_quotes := lang.NewSymbol("pattern-with-quotes")
46+
sym_re_DASH_matches := lang.NewSymbol("re-matches")
47+
sym_s := lang.NewSymbol("s")
48+
sym_test_DASH_match := lang.NewSymbol("test-match")
49+
kw_arglists := lang.NewKeyword("arglists")
50+
kw_column := lang.NewKeyword("column")
51+
kw_end_DASH_column := lang.NewKeyword("end-column")
52+
kw_end_DASH_line := lang.NewKeyword("end-line")
53+
kw_file := lang.NewKeyword("file")
54+
kw_line := lang.NewKeyword("line")
55+
kw_ns := lang.NewKeyword("ns")
56+
kw_rettag := lang.NewKeyword("rettag")
57+
// var clojure.core/re-matches
58+
var_clojure_DOT_core_re_DASH_matches := lang.InternVarName(sym_clojure_DOT_core, sym_re_DASH_matches)
59+
// var codegen.test.regex-literal/pattern-complex
60+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_complex := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_complex)
61+
// var codegen.test.regex-literal/pattern-function
62+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_function := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_function)
63+
// var codegen.test.regex-literal/pattern-literal
64+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_literal := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_literal)
65+
// var codegen.test.regex-literal/pattern-with-backslash
66+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_backslash := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_with_DASH_backslash)
67+
// var codegen.test.regex-literal/pattern-with-newline
68+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_newline := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_with_DASH_newline)
69+
// var codegen.test.regex-literal/pattern-with-quotes
70+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_quotes := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_pattern_DASH_with_DASH_quotes)
71+
// var codegen.test.regex-literal/test-match
72+
var_codegen_DOT_test_DOT_regex_DASH_literal_test_DASH_match := lang.InternVarName(sym_codegen_DOT_test_DOT_regex_DASH_literal, sym_test_DASH_match)
73+
// reference fmt to avoid unused import error
74+
_ = fmt.Printf
75+
// reference reflect to avoid unused import error
76+
_ = reflect.TypeOf
77+
ns := lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal)
78+
_ = ns
79+
// pattern-complex
80+
{
81+
tmp0 := sym_pattern_DASH_complex.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(10), kw_column, int(6), kw_end_DASH_line, int(10), kw_end_DASH_column, int(20), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
82+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_complex = ns.InternWithValue(tmp0, regexp4.MustCompile("^(?:foo|bar)\\s*=\\s*(['\\\"]?)(.+?)$"), true)
83+
if tmp0.Meta() != nil {
84+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_complex.SetMeta(tmp0.Meta().(lang.IPersistentMap))
85+
}
86+
}
87+
// pattern-function
88+
{
89+
tmp0 := sym_pattern_DASH_function.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(13), kw_column, int(6), kw_end_DASH_line, int(13), kw_end_DASH_column, int(21), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
90+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_function = ns.InternWithValue(tmp0, regexp4.MustCompile("[0-9]+"), true)
91+
if tmp0.Meta() != nil {
92+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_function.SetMeta(tmp0.Meta().(lang.IPersistentMap))
93+
}
94+
}
95+
// pattern-literal
96+
{
97+
tmp0 := sym_pattern_DASH_literal.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(4), kw_column, int(6), kw_end_DASH_line, int(4), kw_end_DASH_column, int(20), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
98+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_literal = ns.InternWithValue(tmp0, regexp4.MustCompile("[a-z]+"), true)
99+
if tmp0.Meta() != nil {
100+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_literal.SetMeta(tmp0.Meta().(lang.IPersistentMap))
101+
}
102+
}
103+
// pattern-with-backslash
104+
{
105+
tmp0 := sym_pattern_DASH_with_DASH_backslash.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(7), kw_column, int(6), kw_end_DASH_line, int(7), kw_end_DASH_column, int(27), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
106+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_backslash = ns.InternWithValue(tmp0, regexp4.MustCompile("\\\\d+"), true)
107+
if tmp0.Meta() != nil {
108+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_backslash.SetMeta(tmp0.Meta().(lang.IPersistentMap))
109+
}
110+
}
111+
// pattern-with-newline
112+
{
113+
tmp0 := sym_pattern_DASH_with_DASH_newline.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(9), kw_column, int(6), kw_end_DASH_line, int(9), kw_end_DASH_column, int(25), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
114+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_newline = ns.InternWithValue(tmp0, regexp4.MustCompile("line1\\nline2"), true)
115+
if tmp0.Meta() != nil {
116+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_newline.SetMeta(tmp0.Meta().(lang.IPersistentMap))
117+
}
118+
}
119+
// pattern-with-quotes
120+
{
121+
tmp0 := sym_pattern_DASH_with_DASH_quotes.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(8), kw_column, int(6), kw_end_DASH_line, int(8), kw_end_DASH_column, int(24), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
122+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_quotes = ns.InternWithValue(tmp0, regexp4.MustCompile("\\\"quoted\\\""), true)
123+
if tmp0.Meta() != nil {
124+
var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_with_DASH_quotes.SetMeta(tmp0.Meta().(lang.IPersistentMap))
125+
}
126+
}
127+
// test-match
128+
{
129+
tmp0 := sym_test_DASH_match.WithMeta(lang.NewMap(kw_file, "codegen/test/regex_literal.glj", kw_line, int(16), kw_column, int(7), kw_end_DASH_line, int(16), kw_end_DASH_column, int(16), kw_arglists, lang.NewList(lang.NewVector(sym_s)), kw_ns, lang.FindOrCreateNamespace(sym_codegen_DOT_test_DOT_regex_DASH_literal))).(*lang.Symbol)
130+
var tmp1 lang.FnFunc
131+
tmp1 = lang.NewFnFunc(func(args ...any) any {
132+
checkArity(args, 1)
133+
v2 := args[0]
134+
_ = v2
135+
var tmp3 any
136+
{ // let
137+
// let binding "and__0__auto__"
138+
tmp4 := checkDerefVar(var_clojure_DOT_core_re_DASH_matches)
139+
tmp5 := checkDerefVar(var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_literal)
140+
tmp6 := lang.Apply(tmp4, []any{tmp5, "hello"})
141+
var v7 any = tmp6
142+
_ = v7
143+
var tmp8 any
144+
if lang.IsTruthy(v7) {
145+
tmp9 := checkDerefVar(var_clojure_DOT_core_re_DASH_matches)
146+
tmp10 := checkDerefVar(var_codegen_DOT_test_DOT_regex_DASH_literal_pattern_DASH_function)
147+
tmp11 := lang.Apply(tmp9, []any{tmp10, "12345"})
148+
tmp8 = tmp11
149+
} else {
150+
tmp8 = v7
151+
}
152+
tmp3 = tmp8
153+
} // end let
154+
return tmp3
155+
})
156+
tmp1 = tmp1.WithMeta(lang.NewMap(kw_rettag, nil)).(lang.FnFunc)
157+
var_codegen_DOT_test_DOT_regex_DASH_literal_test_DASH_match = ns.InternWithValue(tmp0, tmp1, true)
158+
if tmp0.Meta() != nil {
159+
var_codegen_DOT_test_DOT_regex_DASH_literal_test_DASH_match.SetMeta(tmp0.Meta().(lang.IPersistentMap))
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)