Thanks to ℝaphink's answer, I started with the lvm.aug
lens, which was a good starting point, and improved it a little.
Here's what I've got at the moment, which supports only a subset of libconfuse
syntax—if you test it with the libconfuse
example test.conf
, it will fail in several places. But it works for the sub-set of the syntax in the config files I'm currently using, so it's "good enough" for my current purposes. Although I'd like to figure out how to get indentation of nested blocks looking nice (like the json.aug
lens does; I haven't figured out how it does it yet).
(*
Module: LibconfuseSimple
Based on Module LVM
*)
module LibconfuseSimple =
(* See lvm2/libdm/libdm-config.c for tokenisation;
* libdm uses a blacklist but I prefer the safer whitelist approach. *)
(* View: identifier
* The left hand side of a definition *)
let identifier = /[a-zA-Z0-9_-]+/
(* strings can contain backslash-escaped dquotes, but I don't know
* how to get the message across to augeas *)
let str = [label "str". Quote.do_quote (store /[^"]*/)]
let int = [label "int". store Rx.integer]
let env = [label "env". del "${" "${" . store /[^}]*/ . del "}" "}"]
let const (r:regexp) = [ label "const" . store r ]
let rawstr = [label "rawstr". store Rx.space_in]
(* View: flat_literal
* A literal without structure *)
let flat_literal = int|str|env|const /true|false|null/|rawstr
(* allow multiline and mixed int/str, used for raids and stripes *)
(* View: list
* A list containing flat literals *)
let list = [
label "list" . counter "list"
. del /\[[ \t\n]*/ "["
.([seq "list". flat_literal . del /,[ \t\n]*/ ", "]*
. [seq "list". flat_literal . del /[ \t\n]*/ ""])?
. Util.del_str "]"]
(* View: val
* Any value that appears on the right hand side of an assignment *)
let val = flat_literal | list
(* View: comments
* Comments of various sorts *)
let comments =
Util.comment
| Util.comment_c_style
| Util.comment_multiline
(* View: nondef
* A line that doesn't contain a statement *)
let nondef =
Util.empty
| comments
(* View: indent
* Remove any input indentation; output 4 spaces indentation. *)
let indent = del /[ \t]*/ " "
(* Build.block couldn't be reused, because of recursion and
* a different philosophy of whitespace handling. *)
(* View: def
* An assignment, or a block containing definitions *)
let rec def = [
key identifier . (
(del /[ \t]*/ " " . [label "title" . store identifier])? . del /[ \t]*\{\n?/ " {\n"
.[label "dict" . (Util.empty | indent . comments | indent . def)*]
. Util.indent . Util.del_str "}\n"
|Sep.space_equal . val . Util.comment_or_eol)]
(* View: lns
* The main lens *)
let lns = (nondef | (Util.indent . def))*