Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 4e8b5fc

Browse files
committed
vmod: Add vmod_math code generator
1 parent 3e1a235 commit 4e8b5fc

5 files changed

Lines changed: 969 additions & 0 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ cscope.*out
8181
/vmod/vcc_*_if.h
8282
/vmod/vmod_*.rst
8383
/vmod/vmod_vcs_version.txt
84+
/vmod/vmod_math.vcc
85+
/vmod/vmod_math.c
8486

8587
# Man-files and binaries
8688
/man/*.1

vmod/Makefile.am

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ include $(srcdir)/automake_boilerplate_h2.am
3333
include $(srcdir)/automake_boilerplate_cookie.am
3434
include $(srcdir)/automake_boilerplate_debug.am
3535
include $(srcdir)/automake_boilerplate_directors.am
36+
include $(srcdir)/automake_boilerplate_math.am
3637
include $(srcdir)/automake_boilerplate_proxy.am
3738
include $(srcdir)/automake_boilerplate_purge.am
3839
include $(srcdir)/automake_boilerplate_std.am
@@ -55,3 +56,14 @@ vmod_debug_symbols_regex = 'Vmod_.*_Data'
5556

5657
# not --strict, not installed
5758
vmodtoolargs_debug = --boilerplate --noinst -o vcc_debug_if
59+
60+
### vmod_math specific
61+
# the specs are deliberately checked in to not make the build
62+
# dependend on external connectivity
63+
vmod_math_spec.html:
64+
curl -o $@ https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/math.h.html
65+
66+
vmod_math.c: vmod_math.vcc
67+
68+
vmod_math.vcc: vmod_math_spec.html vmod_math_gen.py
69+
@PYTHON@ vmod_math_gen.py $< vmod_math.vcc vmod_math.c

vmod/automake_boilerplate_math.am

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Generated by vmodtool.py --boilerplate.
2+
3+
vmod_LTLIBRARIES += libvmod_math.la
4+
dist_vcc_DATA += $(srcdir)/vmod_math.vcc
5+
6+
libvmod_math_la_SOURCES = \
7+
vmod_math.c
8+
9+
libvmod_math_la_CFLAGS =
10+
11+
vmodtoolargs_math ?= --strict --boilerplate -o vcc_math_if
12+
vmod_math_symbols_regex ?= Vmod_math_Data
13+
14+
libvmod_math_la_LDFLAGS = \
15+
-export-symbols-regex $(vmod_math_symbols_regex) \
16+
$(AM_LDFLAGS) \
17+
$(VMOD_LDFLAGS) \
18+
-rpath $(vmoddir)
19+
20+
nodist_libvmod_math_la_SOURCES = vcc_math_if.c vcc_math_if.h
21+
22+
EXTRA_libvmod_math_la_DEPENDENCIES = $(nodist_libvmod_math_la_SOURCES)
23+
24+
EXTRA_DIST += automake_boilerplate_math.am
25+
26+
$(libvmod_math_la_OBJECTS): vcc_math_if.h
27+
28+
vcc_math_if.h vmod_math.rst vmod_math.man.rst: vcc_math_if.c
29+
30+
# A doc-change will not update mtime on the .h and .c files, so a
31+
# touch(1) is necessary to signal that vmodtool was in fact run.
32+
vcc_math_if.c: $(VMODTOOL) $(srcdir)/vmod_math.vcc
33+
@PYTHON@ $(VMODTOOL) $(vmodtoolargs_math) $(srcdir)/vmod_math.vcc
34+
touch vcc_math_if.c
35+
36+
clean-local: clean-vmod-math
37+
38+
clean-vmod-math:
39+
rm -f $(nodist_libvmod_math_la_SOURCES)
40+
rm -f vmod_math.rst vmod_math.man.rst

vmod/vmod_math_gen.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2025 UPLEX - Nils Goroll Systemoptimierung
4+
# All rights reserved.
5+
#
6+
# Author: Nils Goroll <nils.goroll@uplex.de>
7+
#
8+
# SPDX-License-Identifier: BSD-2-Clause
9+
#
10+
# Redistribution and use in source and binary forms, with or without
11+
# modification, are permitted provided that the following conditions
12+
# are met:
13+
# 1. Redistributions of source code must retain the above copyright
14+
# notice, this list of conditions and the following disclaimer.
15+
# 2. Redistributions in binary form must reproduce the above copyright
16+
# notice, this list of conditions and the following disclaimer in the
17+
# documentation and/or other materials provided with the distribution.
18+
#
19+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22+
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29+
# SUCH DAMAGE.
30+
#
31+
# generate vmod_math vcc and c files from opengroup spec html
32+
33+
import re
34+
import sys
35+
36+
37+
vcc_type_map = {
38+
"int": "INT",
39+
"double": "REAL",
40+
"long": "INT",
41+
"real-floating": "REAL",
42+
"const char *": "STRING",
43+
}
44+
45+
def_arg_names = ["x", "y", "z"]
46+
arg_names_map = {
47+
"atan2": ["y", "x"],
48+
"ldexp": ["x", "exp"],
49+
"nan": ["tag"],
50+
"scalbln": ["x", "exp"],
51+
"yn": ["n", "x"],
52+
}
53+
54+
# type name
55+
re_arg = re.compile(r"^(.+?)(\s+[xy])?$")
56+
# argument separator
57+
re_argsep = re.compile(r",\s*")
58+
59+
60+
def vcc_arg(arg, n):
61+
match = re_arg.search(arg)
62+
if match is None:
63+
print(f"Error: no re_arg match on '{arg}'")
64+
sys.exit(1)
65+
# print(match)
66+
67+
atype = vcc_type_map[match.group(1)]
68+
name = match.group(2)
69+
70+
if name is None:
71+
name = n
72+
73+
return f"{atype} {name}"
74+
75+
76+
def gen_func_vcc(match, vcc_fh):
77+
rtype = match.group(1)
78+
name = match.group(2)
79+
args = match.group(3)
80+
81+
# print(match)
82+
83+
rtype = vcc_type_map[rtype]
84+
args = re_argsep.split(args)
85+
86+
argnames = def_arg_names[: len(args)]
87+
if name in arg_names_map:
88+
argnames = arg_names_map[name]
89+
90+
args = ", ".join([vcc_arg(arg, name) for arg, name in zip(args, argnames)])
91+
92+
tpl = f"""
93+
$Function {rtype} {name}({args})
94+
"""
95+
96+
vcc_fh.write(tpl)
97+
98+
99+
def c_arg(arg, n):
100+
match = re_arg.search(arg)
101+
if match is None:
102+
print(f"Error: no re_arg match on '{arg}'")
103+
sys.exit(1)
104+
# print(match)
105+
106+
atype = "VCL_" + vcc_type_map[match.group(1)]
107+
name = match.group(2)
108+
109+
if name is None:
110+
name = n
111+
112+
return f"{atype} {name}"
113+
114+
115+
def gen_func_c(match, c_fh):
116+
rtype = match.group(1)
117+
name = match.group(2)
118+
args = match.group(3)
119+
120+
# print(match)
121+
122+
rtype = "VCL_" + vcc_type_map[rtype]
123+
args = re_argsep.split(args)
124+
125+
argnames = def_arg_names[: len(args)]
126+
if name in arg_names_map:
127+
argnames = arg_names_map[name]
128+
129+
args = ", ".join([c_arg(arg, name) for arg, name in zip(args, argnames)])
130+
argnames = ", ".join(argnames)
131+
132+
tpl = f"""
133+
{rtype}
134+
vmod_{name}(VRT_CTX, {args})
135+
{{
136+
(void)ctx;
137+
return ({name}({argnames}));
138+
}}
139+
"""
140+
141+
c_fh.write(tpl)
142+
143+
144+
def gen_func(func, vcc_fh, c_fh):
145+
gen_func_vcc(func, vcc_fh)
146+
gen_func_c(func, c_fh)
147+
148+
149+
# rtype name (args...)
150+
re_spec_funcs = re.compile(r"\b([a-z0-9 ]+?)\s+([a-z0-9]+)\((.*)\)(?=;)")
151+
# disregard variants for types we do not need or can not support
152+
# not support: frexp(double, int *) etc returning two values
153+
re_func_disregard = re.compile(r"long double|float|long long|int \*|double \*")
154+
155+
156+
def gen_funcs(spec, vcc_fh, c_fh):
157+
for match in re_spec_funcs.finditer(spec):
158+
f = match.group(0)
159+
#print("XXX " + match.group(0))
160+
if re_func_disregard.search(f):
161+
#print(f"disregard: {f}")
162+
continue
163+
gen_func(match, vcc_fh, c_fh)
164+
165+
166+
vcc_top = '''
167+
# this code is auto-generated. Do not edit
168+
$ABI vrt
169+
$Module math 3 "VMOD wrapping math.h"
170+
171+
DESCRIPTION
172+
===========
173+
174+
This VMOD wraps the functions in math.h.
175+
176+
For documentation, see ``man <function>``.
177+
178+
'''
179+
180+
c_top = '''
181+
/* this code is auto-generated. Do not edit */
182+
#include "config.h"
183+
184+
#include <math.h>
185+
186+
#include "vdef.h"
187+
#include "vrt.h"
188+
#include "vcc_math_if.h"
189+
'''
190+
191+
def main():
192+
# Check command line arguments
193+
if len(sys.argv) != 4:
194+
print("Usage: python3 script.py <spec_file> <vcc_file> <c_file>")
195+
sys.exit(1)
196+
197+
spec_file = sys.argv[1]
198+
vcc_file = sys.argv[2]
199+
c_file = sys.argv[3]
200+
201+
spec = ""
202+
203+
try:
204+
with open(spec_file, "r") as f:
205+
spec = f.read()
206+
except FileNotFoundError:
207+
print(f"Error: Input file '{spec_file}' not found")
208+
sys.exit(1)
209+
210+
with open(vcc_file, "w") as vcc_fh:
211+
vcc_fh.write(vcc_top)
212+
with open(c_file, "w") as c_fh:
213+
c_fh.write(c_top)
214+
gen_funcs(spec, vcc_fh, c_fh)
215+
print(f"Output written to '{vcc_file}' and '{c_file}'")
216+
217+
218+
if __name__ == "__main__":
219+
main()

0 commit comments

Comments
 (0)