HTML DSL in V
Published .. 31-07-2024
Type ....... article
Tags ....... v, dsl
Type ....... article
Tags ....... v, dsl
V is expressive enough to support creating DSL (Domain Specific Language). It's not Lisp, but it will do. Here are the beginning of a DSL to express HTML in V.
Here is how you can write HTML using the DSL:
fn main() {
// vfmt off
x := html(atts(),
meta(atts("keywords","demo"))
body(atts(),
div(atts("class","card"),
a(atts('href', '/demo/', 'target', '_blank'), 'Click me'),
p(atts(), 'More text'))))
// vfmt on
println(x)
}
The HTML DSL. This is much nicer than the same written in Go because of the sum-type in V:
module main
import strings
type Element = Node | string
fn (e Element) str() string {
return match e {
Node { e.str() }
string { e.str() }
}
}
struct Node {
tag string
tag_omission bool
attributes Attributes
children []Element
}
fn (n Node) str() string {
mut b := strings.new_builder(1024)
b.write_string('<${n.tag}')
if n.attributes.len > 0 {
b.write_string(' ')
for i := 0; i < n.attributes.len; i++ {
a := n.attributes[i]
b.write_string('${a.name}="${a.value}"')
if i < n.attributes.len - 1 {
b.write_string(' ')
}
}
}
b.write_string('>')
for c in n.children {
b.write_string(c.str())
}
if !n.tag_omission {
b.write_string('</${n.tag}>\n')
}
return b.str()
}
struct Attribute {
name string
value string
}
type Attributes = []Attribute
// atts take a list of name and values and converts these
// to an array of Attribute elements.
fn atts(alist ...string) []Attribute {
mut res := []Attribute{}
for i := 0; i < alist.len; i += 2 {
a := Attribute{
name: alist[i]
value: alist[i + 1]
}
res << a
}
return res
}
fn construct(tag string, tagomit bool, attribs Attributes, children ...Element) Node {
n := Node{
tag: tag
tag_omission: tagomit
attributes: attribs
children: children
}
return n
}
fn meta(attribs Attributes, children ...Element) Node {
return construct('meta', true, attribs, ...children)
}
fn html(attribs Attributes, children ...Element) Node {
return construct('html', false, attribs, ...children)
}
fn body(attribs Attributes, children ...Element) Node {
return construct('body', false, attribs, ...children)
}
fn p(attribs Attributes, children ...Element) Node {
return construct('p', false, attribs, ...children)
}
fn a(attribs Attributes, children ...Element) Node {
return construct('a', false, attribs, ...children)
}
fn div(attribs Attributes, children ...Element) Node {
return construct('div', false, attribs, ...children)
}
// etc...