Skip to content

Commit 37d0086

Browse files
committed
Add support for native xml parsing. (#4252)
1 parent 709c677 commit 37d0086

21 files changed

+672
-6
lines changed

.github/opam/liquidsoap-core-windows.opam

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ depends: [
3535
"fileutils"
3636
"fileutils-windows"
3737
"curl-windows"
38+
"xml-light-windows"
3839
"mem_usage-windows" {>= "0.1.1"}
3940
"metadata-windows" {>= "0.3.0"}
4041
"dune-site-windows"

.github/scripts/build-posix.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ echo "::endgroup::"
3939

4040
echo "::group::Setting up specific dependencies"
4141

42+
opam install -y xml-light
43+
4244
cd /tmp/liquidsoap-full/liquidsoap
4345

4446
./.github/scripts/checkout-deps.sh

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ jobs:
121121
cd /tmp/liquidsoap-full/liquidsoap
122122
eval "$(opam config env)"
123123
opam update
124-
opam install -y saturn_lockfree.0.4.1
124+
opam install -y xml-light
125125
dune build --profile release ./src/js/interactive_js.bc.js
126126
127127
tree_sitter_parse:

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ repos:
2626
exclude: dune.inc
2727

2828
- repo: https://github.com/savonet/pre-commit-liquidsoap
29-
rev: c5eab8dceed09fa985b3cf0ba3fe7f398fc00c04
29+
rev: 056cf2da9d985e1915a069679f126a461206504a
3030
hooks:
3131
- id: liquidsoap-prettier
3232

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
New:
44

5+
- Added support for parsing and rendering XML natively (#4252)
56
- Added support for `WAVE_FORMAT_EXTENSIBLE` to the internal
67
wav dexcoder.
78
- Added optional `buffer_size` parameter to `input.alsa` and

doc/content/xml.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
## Importing/exporting XML values
2+
3+
Support for XML parsing and rendering was first added in liquidsoap `2.3.1`.
4+
5+
You can parse XML strings using a decorator and type annotation. There are two different representations of XML you can use.
6+
7+
### Record access representation
8+
9+
This is the easiest representation. It is intended for quick access to parsed value via
10+
record and tuples.
11+
12+
Here's an example:
13+
14+
```liquidsoap
15+
s =
16+
'<bla param="1" bla="true">
17+
<foo opt="12.3">gni</foo>
18+
<bar />
19+
<bar>bla</bar>
20+
<blo>1.23</blo>
21+
<blu>false</blu>
22+
<ble>123</ble>
23+
</bla>'
24+
25+
let xml.parse (x :
26+
{
27+
bla: {
28+
foo: string.{ xml_params: {opt: float} },
29+
bar: (unit * string),
30+
blo: float,
31+
blu: bool,
32+
ble: int,
33+
xml_params: { bla: bool }
34+
}
35+
}
36+
) = s
37+
38+
print("The value for blu is: #{x.bla.ble}")
39+
```
40+
41+
Things to note:
42+
43+
- The basic mappings are: `<tag name> -> <tag content>`
44+
- Tag content maps tag parameters to a `xml_params` method.
45+
- When multiple tags are present, their values are collected as tuple (`bar` tag in the example)
46+
- When a tag contains a single ground value (`string`, `bool`, `float` or `integer`), the mapping is from tag name to the corresponding value, with xml attributes attached as methods
47+
- Tag parameters can be converted to ground values and omitted.
48+
49+
The parsing is driven by the type annotation and is intended to be permissive. For instance, this will work:
50+
51+
```liquidsoaop
52+
s = '<bla>foo</bla>'
53+
54+
# Here, `foo` is omitted.
55+
let xml.parse (x: { bla: unit }) = s
56+
57+
# x contains: { bla = () }
58+
59+
# Here, `foo` is made optional
60+
let xml.parse (x: { bla: string? }) = s
61+
62+
# x contains: { bla = "foo" }
63+
```
64+
65+
### Formal representation
66+
67+
Because XML format can result in complex values, the parser can also use a generic representation.
68+
69+
Here's an example:
70+
71+
```liquidsoap
72+
s =
73+
'<bla param="1" bla="true">
74+
<foo opt="12.3">gni</foo>
75+
<bar />
76+
<bar>bla</bar>
77+
<blo>1.23</blo>
78+
<blu>false</blu>
79+
<ble>123</ble>
80+
</bla>'
81+
82+
let xml.parse (x :
83+
(
84+
string
85+
*
86+
{
87+
xml_params: [(string * string)],
88+
xml_children: [
89+
(
90+
string
91+
*
92+
{
93+
xml_params: [(string * string)],
94+
xml_children: [(string * {xml_text: string})]
95+
}
96+
)
97+
]
98+
}
99+
)
100+
) = s
101+
102+
# x contains:
103+
(
104+
"bla",
105+
{
106+
xml_children=
107+
[
108+
(
109+
"foo",
110+
{
111+
xml_children=[("xml_text", {xml_text="gni"})],
112+
xml_params=[("opt", "12.3")]
113+
}
114+
),
115+
("bar", {xml_children=[], xml_params=[]}),
116+
(
117+
"bar",
118+
{
119+
xml_children=[("xml_text", {xml_text="bla"})],
120+
xml_params=[("option", "aab")]
121+
}
122+
),
123+
(
124+
"blo",
125+
{xml_children=[("xml_text", {xml_text="1.23"})], xml_params=[]}
126+
),
127+
(
128+
"blu",
129+
{xml_children=[("xml_text", {xml_text="false"})], xml_params=[]}
130+
),
131+
(
132+
"ble",
133+
{xml_children=[("xml_text", {xml_text="123"})], xml_params=[]}
134+
)
135+
],
136+
xml_params=[("param", "1"), ("bla", "true")]
137+
}
138+
)
139+
```
140+
141+
This representation is much less convenient to manipulate but allows an exact representation of all XML values.
142+
143+
Things to note:
144+
145+
- XML nodes are represented by a pair of the form: `(<tag name>, <tag properties>)`
146+
- `<tag properties>` is a record containing the following methods:
147+
- `xml_params`, represented as a list of pairs `(string * string)`
148+
- `xml_children`, containing a list of the XML node's children. Each entry in the list is a node in the formal XML representation.
149+
- `xml_text`, present when the node is a text node. In this case, `xml_params` and `xm_children` are empty.
150+
- By convention, text nodes are labelled `xml_text` and are of the form: `{ xml_text: "node content" }`
151+
152+
### Rendering XML values
153+
154+
XML values can be converted back to strings using `xml.stringify`.
155+
156+
Both the formal and record-access form can be rendered back into XML strings however, with the record-access representations, if a node has multiple children with the same tag, the conversion to XML string will fail.
157+
158+
More generally, if the values you want to convert to XML strings are complex, for instance if they use several times the same tag as child node or if the order of child nodes matters, we recommend using the formal representation to make sure that children ordering is properly preserved.
159+
160+
This is because record methods are not ordered in the language so we make no guarantee that the child nodes they represent be rendered in a specific order.

doc/dune.inc

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9281,6 +9281,134 @@
92819281
)
92829282
)
92839283

9284+
(rule
9285+
(alias doc)
9286+
(package liquidsoap)
9287+
(enabled_if (not %{bin-available:pandoc}))
9288+
(deps (:no_pandoc no-pandoc))
9289+
(target xml.html)
9290+
(action (run cp %{no_pandoc} %{target}))
9291+
)
9292+
9293+
(rule
9294+
(alias doc)
9295+
(package liquidsoap)
9296+
(enabled_if %{bin-available:pandoc})
9297+
(deps
9298+
liquidsoap.xml
9299+
language.dtd
9300+
template.html
9301+
content/liq/append-silence.liq
9302+
content/liq/archive-cleaner.liq
9303+
content/liq/basic-radio.liq
9304+
content/liq/beets-amplify.liq
9305+
content/liq/beets-protocol-short.liq
9306+
content/liq/beets-protocol.liq
9307+
content/liq/beets-source.liq
9308+
content/liq/blank-detect.liq
9309+
content/liq/blank-sorry.liq
9310+
content/liq/complete-case.liq
9311+
content/liq/cross.custom.liq
9312+
content/liq/crossfade.liq
9313+
content/liq/decoder-faad.liq
9314+
content/liq/decoder-flac.liq
9315+
content/liq/decoder-metaflac.liq
9316+
content/liq/dump-hourly.liq
9317+
content/liq/dump-hourly2.liq
9318+
content/liq/dynamic-source.liq
9319+
content/liq/external-output.file.liq
9320+
content/liq/fallback.liq
9321+
content/liq/ffmpeg-filter-dynamic-volume.liq
9322+
content/liq/ffmpeg-filter-flanger-highpass.liq
9323+
content/liq/ffmpeg-filter-hflip.liq
9324+
content/liq/ffmpeg-filter-hflip2.liq
9325+
content/liq/ffmpeg-filter-parallel-flanger-highpass.liq
9326+
content/liq/ffmpeg-live-switch.liq
9327+
content/liq/ffmpeg-relay-ondemand.liq
9328+
content/liq/ffmpeg-relay.liq
9329+
content/liq/ffmpeg-shared-encoding-rtmp.liq
9330+
content/liq/ffmpeg-shared-encoding.liq
9331+
content/liq/fixed-time1.liq
9332+
content/liq/fixed-time2.liq
9333+
content/liq/frame-size.liq
9334+
content/liq/harbor-auth.liq
9335+
content/liq/harbor-dynamic.liq
9336+
content/liq/harbor-insert-metadata.liq
9337+
content/liq/harbor-metadata.liq
9338+
content/liq/harbor-redirect.liq
9339+
content/liq/harbor-simple.liq
9340+
content/liq/harbor-usage.liq
9341+
content/liq/harbor.http.register.liq
9342+
content/liq/harbor.http.response.liq
9343+
content/liq/hls-metadata.liq
9344+
content/liq/hls-mp4.liq
9345+
content/liq/http-input.liq
9346+
content/liq/icy-update.liq
9347+
content/liq/input.mplayer.liq
9348+
content/liq/jingle-hour.liq
9349+
content/liq/json-ex.liq
9350+
content/liq/json-stringify.liq
9351+
content/liq/json1.liq
9352+
content/liq/live-switch.liq
9353+
content/liq/medialib-predicate.liq
9354+
content/liq/medialib.liq
9355+
content/liq/medialib.sqlite.liq
9356+
content/liq/multitrack-add-video-track.liq
9357+
content/liq/multitrack-add-video-track2.liq
9358+
content/liq/multitrack-default-video-track.liq
9359+
content/liq/multitrack.liq
9360+
content/liq/multitrack2.liq
9361+
content/liq/multitrack3.liq
9362+
content/liq/output.file.hls.liq
9363+
content/liq/playlists.liq
9364+
content/liq/prometheus-callback.liq
9365+
content/liq/prometheus-settings.liq
9366+
content/liq/radiopi.liq
9367+
content/liq/re-encode.liq
9368+
content/liq/regular.liq
9369+
content/liq/replaygain-metadata.liq
9370+
content/liq/replaygain-playlist.liq
9371+
content/liq/request.dynamic.liq
9372+
content/liq/rtmp.liq
9373+
content/liq/samplerate3.liq
9374+
content/liq/scheduling.liq
9375+
content/liq/seek-telnet.liq
9376+
content/liq/settings.liq
9377+
content/liq/shoutcast.liq
9378+
content/liq/single.liq
9379+
content/liq/source-cue.liq
9380+
content/liq/space_overhead.liq
9381+
content/liq/split-cue.liq
9382+
content/liq/sqlite.liq
9383+
content/liq/srt-receiver.liq
9384+
content/liq/srt-sender.liq
9385+
content/liq/switch-show.liq
9386+
content/liq/transcoding.liq
9387+
content/liq/video-anonymizer.liq
9388+
content/liq/video-bluescreen.liq
9389+
content/liq/video-canvas-example.liq
9390+
content/liq/video-default-canvas.liq
9391+
content/liq/video-in-video.liq
9392+
content/liq/video-logo.liq
9393+
content/liq/video-osc.liq
9394+
content/liq/video-simple.liq
9395+
content/liq/video-static.liq
9396+
content/liq/video-text.liq
9397+
content/liq/video-transition.liq
9398+
content/liq/video-weather.liq
9399+
content/liq/video-webcam.liq
9400+
(:md content/xml.md)
9401+
)
9402+
(target xml.html)
9403+
(action
9404+
(pipe-stdout
9405+
(run pandoc %{md} -t json)
9406+
(run pandoc-include --directory content/liq)
9407+
(run pandoc -f json --syntax-definition=liquidsoap.xml --highlight=pygments --metadata pagetitle=xml --template=template.html -o %{target})
9408+
)
9409+
)
9410+
)
9411+
92849412
(rule
92859413
(alias doc)
92869414
(package liquidsoap)
@@ -10496,6 +10624,7 @@
1049610624
(strings_encoding.html as html/strings_encoding.html)
1049710625
(video-static.html as html/video-static.html)
1049810626
(video.html as html/video.html)
10627+
(xml.html as html/xml.html)
1049910628
(yaml.html as html/yaml.html)
1050010629
)
1050110630
)

dune-project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
(ppx_hash :build)
157157
(sedlex (>= 3.2))
158158
(menhir (>= 20240715))
159+
xml-light
159160
)
160161
(sites (share libs) (share bin) (share cache) (lib_root lib_root))
161162
(synopsis "Liquidsoap language library"))

liquidsoap-lang.opam

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ depends: [
1717
"ppx_hash" {build}
1818
"sedlex" {>= "3.2"}
1919
"menhir" {>= "20240715"}
20+
"xml-light"
2021
"odoc" {with-doc}
2122
]
2223
build: [

0 commit comments

Comments
 (0)