typstuff/misc/uml/parser.typ
2024-12-13 23:04:02 +01:00

94 lines
2.4 KiB
Typst

#import "uml.typ"
#let parse-args(data) = {
let args = data.split(",").map(a => a.trim()).filter(a => a.len() != 0)
let res
if ":" in data {
res = (:)
} else {
res = ()
}
for arg in args {
let parts = arg.split(":")
let arg-name = none
let arg-type
if parts.len() == 1 {
if type(res) == dictionary {
panic("Cannot mix named and unnamed arguments")
}
arg-type = parts.first().trim()
res.push(arg-type)
} else {
arg-name = parts.first().trim()
arg-type = parts.slice(1).join(":").trim()
res.insert(arg-name, arg-type)
}
}
return res
}
#let parse(data) = {
let elements = ()
if type(data) == raw or type(data) == content {
data = data.text
}
let classes = data.matches(regex("(?ms)(abstract class|class|interface) (.*?)\s*\{(.*?)\}"))
for class in classes {
let family-name = class.captures.first()
let family = (
class: none,
"abstract class": uml.abstract,
"interface": uml.interface,
"enum": uml.enum,
).at(family-name)
let name = class.captures.at(1)
let class-data = class.captures.last().trim()
let members = class-data.matches(regex("(?m)^\s*(.*?)\s*(:\s*([^:]*?))?$")).filter(m => m.text.len() != 0)
let attributes = ()
let methods = ()
for member in members {
let member-name = member.captures.first()
let val-type = member.captures.last()
let visibility = none
if member-name.starts-with(regex("\+|-|~|#")) {
let vis = member-name.first()
member-name = member-name.slice(1)
visibility = (
"+": uml.public,
"-": uml.private,
"~": uml.package,
"#": uml.protected
).at(vis)
}
if member-name.ends-with(")") {
let func = member-name.match(regex("^(.*?)\((.*)\)$"))
member-name = func.captures.first()
let args = parse-args(func.captures.last())
methods += uml.method(member-name, visibility, val-type, args)
} else {
attributes += uml.attr(member-name, visibility, val-type)
}
}
elements += uml.class(name, family, attributes + methods)
}
let defaults = (:)
let params = data.matches(regex("(?m)^\s*@\s*(.*?)\s*=\s*(.*)\s*$"))
for param in params {
let key = param.captures.first()
let value = eval(param.captures.last(), scope: (uml: uml))
defaults.insert(key, value)
}
return uml.diagram(elements, defaults)
}