Skip to content

carlinigraphy/conflang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

conflang

Strongly typed configuration language, featuring a small but robust feature set.

Written in Bash, compiles to Bash.

What works

  • ✓ Parsing

  • ✓ Typechecking

  • ✓ Evaluation

  • ✓ Imports

  • ❏ VM, FFI, post-compilation validation

What it is

Aiming to solve common problems with shell configuration files.

conflang is strongly typed. Worry less if users pass in invalid data. Defining clear types removes both a category of errors, and a fair amount of error handling code.

conflang is written in bash. Dependencies are only realpath and bash 4+. No need to install Python3, or other heavy depdencies, just to parse a config file.

conflang compiles to bash. Run it, source the output, everything you need is included.

conflang is fast. (The times below are recorded on my very modest laptop.) Compiling a reasonably sized file takes ~0.100ms. Though there’s no need to re-compile every time. The query function can access an element nested 10 level deep 12,000 times per second.

How it look

Variable declarations
# Untyped key/value pairs.
name : "Marcus";
age  : 30;


# Primitive types.
name @str: "Marcus";
age  @int: 30;


# Complex types.
full_name @rec[str, str]: [
   "Marcus",
   "Aurelius",
];
Types
@str     Surrounded with double-quotes:  "this is a string"
@path    Surrounded with single-quotes:  './relative', '~/absolute', '/absolute'
@int     Negative & positive integers:   -1, 0, 1, ...
@bool    Boolean values:                 true, false
@list    Any length, same type:          [1, 2, 3], ["one", "two", "three"]
@rec     Predetermined length, any type: {"Marcus", "Aurelius", 30}
Typedefs
typedef rec[
   str,        # First name
   str,        # Last name
   int,        # Age
] as Person;

me @Person: {"Marcus", "Aurelius", 30};
Variables
red    @str: "#AF5F5F";
yellow @str: "#DFAD83";
green  @str: "#5F8787";

good  : green;
uh_oh : yellow;
bad   : red;
Sections
key  : "val";
key2 : key;

Section {
   key2: key;
   #     ^-- relative reference,
   #         shadows global `key2`

   key3: Section.key2;
   #     ^-- absolute reference
}
Others
# Environment variables.
homedir   : $HOME;
configdir : $XDG_CONFIG_HOME;

# String interpolation.
project_dir : f'{$HOME}/projects/';
sub_dir     : f'{project_dir}/sub/';

# Type casting.
shell_level: $SHLVL -> int;

Example input/output

input
common {
   colors {
      white @str: "#C5C5C8";
      black @str: "#202121";
   }
}

window {
   geometry {
      height @int: 100;
      width  @int: 80;
   }

   theme {
      foreground : common.colors.white;
      background : common.colors.black;
   }
}
output
declare -- _SKELLY_ROOT="_SKELLY_2"
declare -A _SKELLY_2=([window]="_SKELLY_10" [common]="_SKELLY_4" )
declare -A _SKELLY_4=([colors]="_SKELLY_6" )
declare -A _SKELLY_6=([black]="#202121" [white]="#C5C5C8" )
declare -A _SKELLY_10=([geometry]="_SKELLY_12" [theme]="_SKELLY_16" )
declare -A _SKELLY_12=([width]="80" [height]="100" )
declare -A _SKELLY_16=([foreground]="#C5C5C8" [background]="#202121" )

# Query function is included in output.
conf ()
{
    declare -g RV="$_SKELLY_ROOT";
    for arg in "$@";
    do
        local -n d=$RV;
        if [[ ! -n "${d[$arg]+_}" ]]; then
            raise index_error "$arg";
        fi;
        RV="${d[$arg]}";
    done
}

What I hope to add

Validation

Declare requirements after any expression. These may be in the form of tests, or directives.

config @path: '~/.config/hre-utils/conflang' {
   is_directory.
}

is_directory here is a directive. It tests if the directory exist. If not, runs mkdir -p and returns the exit status.

config @path: '~/.config/hre-utils/conflang/config' {
   is_file?
   exists?
}

is_file and exists are tests. If their conditions fail, an error is raised.

Some useful things I can imagine:

  1. File/directory operations

    1. exists

    2. is_file, is_dir, is_link

    3. can_read, can_write, can_execute

  2. String operations

    1. non_empty

    2. option [ <opt1>..<optN> ]

      1. Throws error if text is not present in list of opts

  3. List operations

    1. non_empty

    2. each <function> <args>

      1. Applies a function to each element of the list

Virtual machine

Thinking of writing a simple VM to run the validation steps. Compile things to a simple set of common instructions.

FFI

Write functions in bash that can be used as validation tests/directives. Hopefully will have a core standard library with things like is_dir, is_file, exists. Anything additional should be extensible without undue burden.

Why write this

Learning.

Around 2020 I began learning to write a "real" programming language. Turns out it’s hard. I found myself consistently hitting conceptual sticking points. Approaching these in the context of something I already understand very well (bash) is easier.

Many of my recent projects (mkconf, conf, shql) were building towards this goal.

I do not intend for anyone to actually use conflang, just as I didn’t shql. Journey over destination and all that.

About

Better bash config files

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages