[ANN] First release of scfg

Hi,

I’m pleased to announce the first release of scfg on opam.

It provides a library and an executable to work with the scfg configuration file format. (disclaimer: scfg has been created by my good friend @emersion)

Here’s an example of an scfg file taken from the specification:

train "Shinkansen" {
	model "E5" {
		max-speed 320km/h
		weight 453.5t

		lines-served "Tōhoku" "Hokkaido"
	}

	model "E7" {
		max-speed 275km/h
		weight 540t

		lines-served "Hokuriku" "Jōetsu"
	}
}

Scfg is a file format designed to be simple and indeed the implementation was really straightforward. I’m planning to use it in small tools I wrote (mostly sway tools written in OCaml) but never released because I couldn’t stand having to use TOML, YAML or JSON for them…

The library provides an executable to validate and pretty-print an scfg file. It’ll indent it properly, remove useless quoting and whitespaces:

$ scfg spec.scfg
train Shinkansen {
  model E5 {
    max-speed 320km/h
    weight 453.5t
    lines-served Tōhoku Hokkaido
  }
  model E7 {
    max-speed 275km/h
    weight 540t
    lines-served Hokuriku Jōetsu
  }
}

The library is made of four modules : Types, Parse, Pp and Query.

The Types module simply defines the following types, which are all you need to deal with scfg:

(** A directive has a name, a list of parameters and children (a list of directive). *)
type directive =
  { name : string
  ; params : string list
  ; children : directive list
  }

(** A config is a list of directives. *)
type config = directive list

The others modules can be used as follow:

let file = {|train A-Train {
    bla bla bla
  }
  train "John Col Train" {
    tut tut tut
  }
|}

(* parsing the file *)
let config =
  (* there's also a `Parse.from_file` function that should be more useful *)
  match Scfg.Parse.from_string file with
  | Error e ->
    Format.eprintf "error: %s@." e;
    exit 1
  | Ok config -> config

(* printing the file *)
let () =
  Format.printf "```scfg@.%a@.```@." Scfg.Pp.config config

(* querying the file *)
let () =
  (* gets the first directive with the name `train` *)
  match Scfg.Query.get_dir "train" config with
  | None -> Format.printf "No train found.@."
  | Some train -> (
    (* get the parameter at index 0 in the `train` directive *)
    match Scfg.Query.get_param 0 train with
    | Error _e -> Format.printf "Train has no name.@."
    | Ok name -> Format.printf "The first train is `%s`.@." name )

For more have a look at the project’s README, the documentation or feel free to ask here ! :partying_face:

3 Likes

the config wire format looks like s-expressions in disguise. Are {} really a substancial benefit to accessibility compared to ()?

Choosing () instead of {} would have been possible. I’m not sure there’s a benefit to accessibility to one or the other. Maybe one could say () are generally more used than {} so they’re more likely to end-up in directive name/parameters and thus you prefer avoid having to escape them, e.g. : dir p(0) p(1) instead of dir p\(0\) p\(1\) or dir "p(0)" "p(1)".

But anyway, I fail to see how changing {} to () makes the format looks like s-expression. In scfg, new lines have meaning.

a b c is not:

a
b
c

This first one is the directive a with parameters b and c. The last one is made of three distincts directives a, b and c with no parameter.

1 Like