PluginPenguin
PluginPenguin

Reputation: 1950

Are there configuration languages with classes?

I have a project where some common configurations are specified in json files which are processed by a simple python parser which generates source files in various languages which contain a bunch of constants specified in the json file. As a simplified example, one of the config files specify a bunch of parameters:

{
  "param1":
  {
    "type": "bool",
    "displayName": "Name 1",
    "defaultValue": false
  },

  "param2":
  {
    "type": "float",
    "displayName": "Name 2",
    "defaultValue": 0.0,
    "valueRange": [-24.0, 24.0],
    "interval": 0.1
  },

  "param3":
  {
    "type": "float",
    "displayName": "Name 3",
    "defaultValue": 0.0,
    "valueRange": [-24.0, 24.0],
    "interval": 0.1
  },

  "param4":
  {
    "type": "float",
    "displayName": "Name 4",  
    "defaultValue": 0.0,
    "valueRange": [-24.0, 24.0],
    "interval": 0.1
  }

Now in my real-world case, there can be up to 50 parameters with each single parameter having a lot more properties. As you see in the example above, param2, param3 and param4 have the same properties, except for their displayName.

I'd love to find some alternative configuration language that can be used here instead of json, which would allow me to specify something like classes, maybe like this pseudo-code snippet:

class GenericBoolParam (name_, defaultValue_)
{
    "type": "bool",
    "displayName": name_,
    "defaultValue": defaultValue_
}

class SpecialFloatParam (name_)
{
    "type": "float",
    "displayName": name_,
    "defaultValue": 0.0,
    "valueRange": [-24.0, 24.0],
    "interval": 0.1
}

{
   "param1" : GenericBoolParam ("Name1, false),
   "param2" : SpecialFloatParam ("Name2),
   "param3" : SpecialFloatParam ("Name3),
   "param4" : SpecialFloatParam ("Name4)
}

The exact syntax is not as important as the possibility to express these repeated pattern. A ready-to-use implementation to interpret the language with python would be a plus but is not mandatory. I am not aware of any suitable language, so I'm hoping to find some hints for a suitable language here.

Upvotes: 1

Views: 107

Answers (2)

Ciaran McHale
Ciaran McHale

Reputation: 2244

Config4*, which I developed, is available in C++ and Java, and its @copyFrom statement copies all the values from the specified scope into the current scope.

One way to rewrite your example configuration file in Config4* format is as follows:

default.float {
    type         = "float";
    defaultValue = "0.0";
    vaueRange    = ["-24.0", "24.0"];
    interval     = "0.1";
}

param1 {
    type         = "bool";
    displayName  = "Name 1";
    defaultValue = "false";
}

param2 {
    @copyFrom "default.float";
    displayName = "Name 2";
}

param3 {
    @copyFrom "default.float";
    displayName = "Name 3";
}

param4 {
    @copyFrom "default.float";
    displayName = "Name 4";
}

If you want to read an overview of Config4*, then I suggest you read Chapter 2 ("Overview of Config4* Syntax") and Chapter 3 ("Overview of Config4* API") of the "Config4* Getting Started" guide (PDF of manual, HTML Chapter 2, HTML Chapter 3).

A Python implementation of Config4* does not exist. If you decide that Config4* is a good fit for your needs, then rather than porting it to Python (which I guestimate would take more than 1 month of effort), it would be simpler to write a utility in Java or C++ that uses the API to parse a configuration file, navigates the parsed information and uses print statements to write out the details in, say, Python or JSON format. Your Python application could execute the utility and parse the file it outputs.

If you decide to write such a utility, here are some hints to get you started in the right direction:

  • After parsing a configuration file, start at the root scope, and use the listFullyScopedNames() or listLocallyScopedNames() operation to obtain a list of the entries within that scope.

  • Use the type() operation to determine whether an entry is a string variable, a list variable or a sub-scope. If it is a sub-scope, then use recursion to examine the sub-scope. When recursing, the mergeNames() operation will be useful to append the name of a sub-scope onto the name of the current scope..

  • Call lookupString() to retrieve the value of string variables, and lookupList() to retrieve the value of list variables.

My guestimate is that it would take less than a day to write such a utility.

Upvotes: 0

Wander Nauta
Wander Nauta

Reputation: 19675

Jsonnet comes to mind: it has almost exactly what you describe, calling them 'functions' rather than 'classes'. Multiple implementations are available, one in C++ and one in Go; the former has a simple Python binding.

Taking an example from the homepage:

local Person(name='Alice') = {
  name: name,
  welcome: 'Hello ' + name + '!',
};
{
  person1: Person(),
  person2: Person('Bob'),
}

becomes:

{
  "person1": {
    "name": "Alice",
    "welcome": "Hello Alice!"
  },
  "person2": {
    "name": "Bob",
    "welcome": "Hello Bob!"
  }
}

Since the output of a Jsonnet program is a JSON document, you can feed that to your existing program.

Upvotes: 1

Related Questions