[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:

7 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

Thanks for releasing this! scfg went from ISC to AGPL license transitively through the addition of the prelude 0.5 (latest) · OCaml Package package. I can pin scfg to earlier versions (ie. 0.3 from 4 months ago), but it would be great if its license could be clarified.

Related: Is there a way for someone from the public to create an issue on your GitLab Issues - prelude - Zapashcanon server?

1 Like

Thanks for releasing this! scfg went from ISC to AGPL license transitively through the addition of the prelude 0.5 (latest) · OCaml Package package. I can pin scfg to earlier versions (ie. 0.3 from 4 months ago), but it would be great if its license could be clarified.

Sorry about this. The prelude library will only be used in development mode in the short term. Would that solve the issue for you? In the beginning, it was exposing new functions names, but then I changed the design so that it is only hiding parts of the stdlib. Which makes it possible to remove it from dependencies and only use it in development mode and in CI.

Out of curiosity, in which projects are you using scfg?

Related: Is there a way for someone from the public to create an issue on your GitLab Issues - prelude - Zapashcanon server?

I can create an account for you if you want. Registrations used to be opened, but I was getting too much spam and closed it as a quick solution.

Hello,

First, I just want to say that the concrete syntax of a config language is important. We can just look at the success of YAML, whose core is expressly stated to be equivalent to JSON, to see that concrete syntax matters. To that end, I wonder if your friend has thought about the relationship between scfg and JSON ? It would seem tight, and I think I could sketch it out here, but I thought I’d suggest it, b/c it might go some way to helping others see whether and why to use scfg.

Just a suggestion.

P.S. Again, I am -not- suggesting using JSON instead. Rather, that understanding scfg in terms of its mapping to the JSON -datatype- might be helpful for some to understand why to use it. It might also be a way to encourage such use: if an alternate demarshaller produced JSON data in-memory, it might make it easier to get people to use it.

1 Like

I’m sorry to say that, but don’t you have enough key on your keyboard to write plain words. You are frequently using this kind of abbreviation but, honestly, I don’t know how to translate them. Did you mean “because” ?

Sorry, yes, “because”.

Thanks! For now, I can keep the pin. (see next answer)

I’m testing it in MlFront/DkCoder for:

  • (Now stuff) Configuring human level knobs for the build caching. Typically I use JSONC for this but …

  • (Future stuff) Editing project config files from a CLI like what yarn add (etc.) does for you. The big advantage of scfg is that it is line-oriented. Line orientation removes a ton of ambiguity and should make it easy to edit programmatically. I have a round-trip editor for JSONC that mostly works but is way too complex for my liking.

I’m not committed to it yet. I’m not happy about the lack of semantics for scfg (is a config file a tree with merge semantics for multiple toplevel items, or a DAG? are comments associated with the directive above or below? etc.), and I’m not committed to mixing human editing and programmatic editing. Those are things I’ll noodle over. But I’m considering it for inclusion in the standard library for DkCoder scripts. My technical requirements are a) they are liberally licensed, b) I have used the libraries enough to recommend them, and c) they work on Windows and Android.

On “b” right now.


Account? I use GitLab myself but don’t self-host, so I was curious. I don’t need an account. Thx!

What sway tools have you written in OCaml? I am a sway user and I’m curious to try them.

I’m not happy about the lack of semantics for scfg (is a config file a tree with merge semantics for multiple toplevel items, or a DAG?

I guess it can be both and the author of the program reading scfg files has to choose the one that makes sense in its application.

are comments associated with the directive above or below?

I don’t remember scfg to have any notion of comments. (Of course you can easily emulate them)

Account? I use GitLab myself but don’t self-host, so I was curious. I don’t need an account. Thx!

I was offering it simply so you can open issues! Let me know if you ever want one. Actually, this is not GitLab but Gitea (which is much easier to deploy and maintain).

What sway tools have you written in OCaml? I am a sway user and I’m curious to try them.

The only two that are usable are ccbg, to manage wallpapers, and sun for screenshots. They are simple wrappers over existing tools such as grim or slurp.

2 Likes