Skip to content
/ mconf Public

my own configuration language, made mostly for fun

License

Notifications You must be signed in to change notification settings

marzeq/mconf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mconf

what and why?

to start - i am a big fan of the general idea of yaml, the easy-to-read nature of it is very appealing to me. however, it has many obvious pitfalls like a (somewhat) undefined syntax with multiple ways to do the same thing, no official standard, and the glaring issue that it's indentation based

so, i decided to make my own one that is more strict and has a more defined syntax while still being easy to read, avoiding the problems of yaml, while adding some creature comforts

it was originally made as a recreational programming exercise and a way to learn more about tokenisation and parsing, however, i decided to flesh it out a bit more to make it more usable

getting the binary

pre-compiled binaries

go onto the releases page and download the binary for your platform (compiled from stable branch)

building from source

you can clone the repo and build the project yourself

git clone
git checkout stable
cd mconf
just build
# optionally:
cp build/mconf ~/.local/usr/bin/ # or /usr/local/bin

see the building & running section for more info

editor support

see editors.md for more info

building & running

this repo comes with a justfile, so you can use just to run the commands (i refuse to add a makefile for any project that is small or medium sized, it's simply unnecessary)

just run        # runs the project, equivalent to `go run .`
just build      # builds the project in build/, equivalent to `go build .`
just build-all  # builds the project for every combination of: windows, darwin (macos) and linux & amd64 and arm64

if you don't want to use just, you can always use the go build and go run commands directly

command line usage

Usage:
  %s <filename> [-- property1 property2 ...]

Arguments:
  <filename>                    Path to the configuration file. Use '-' to read from stdin.
  [-- property1 property2 ...]  List of properties to access. Multiple properties are used to access nested objects or lists. If no properties are provided, the global object is printed. '--' is simply there for readability.

Options:
  -h, --help        Show this message
  -v, --version     Show version
  -j, --json        Output as JSON (in a compact format, prettyfication is up to the user)
  -d, --dotenv      Load .env file in current directory
  --envfile <file>  Load specified enviorment variables file
  -c, --constants   Show constants (only displayed when no properties are provided)

Examples:
  %s config.mconf -- property1 property2
  cat config.mconf | %s - -- property1 property2

spec

mconf fully suppports unicode, so a letter means any unicode latin letter and not just ascii letters, and string values can contain any unicode character

note: if you just one want a one-file example, look at the examples/example.mconf file

mconf is (almost) a superset of JSON, so any valid JSON file (base spec) that starts with an object can be parsed by mconf

comments

# this is a comment
a = 1 # this is a comment as well

keys

key = "value"
"strings as keys" = "are allowed"
óóóó_unicode = true
test: 1 # colon is also valid for JSON compatibility reasons

23abc = false # illegal, keys must start with a letter or underscore

if a key is defined many times, the last one will shadow the previous ones

string values

a_str = "bar"
no_quotes = bar
no_quotes2 = foo bar baz # illegal, no quotes only allowed if it's one word
multiline_str = "123
456"
escapes = "\"escaped quotes\""
unicode = "😊"
single_quotes = 'single quotes'

formatted strings

user_and_port = "${USER}:${PORT}"

keep in mind, the {} are required

if the value substituted is not a string, it will be converted to one

numerical values

# integer value
an_int = 123
# signed integer value
a_uint = -123
# float value
a_float = 123.456
# fancy floats
fancy_float = .5

# hexadecimal value
hex = 0x123
# binary value
bin = 0b1010

# scientific notation
sci = 1.23e3
scineg = 1.23e-3

boolean values

a_bool = true
also_a_bool = false

yes_are_bools_too = yes
and_nos_as_well = no
and_on = on
and_off = off

null values

only really added for JSON compatibility

null_value = null

list values

list = [1, 2, 3, "abc", true, false]
two_dimensional_list = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

commas in lists are required

object values

object = {
  foo = "bar"
  bar = 123
  baz = false
}

nested_object_and_list = {
  foo = {
    bar = "baz"
  }
  list = [1, 2, 3]
}

commas in objects are optional, but you can use them if you want

object = {
  foo = "bar",
  bar = 123,
  baz = false,
}

top level objects

this is a a neat way to organise your file, where if you put an object at the top level, it's keys will be merged with the top level keys

you can think of this as a way to split the file into sections of multiple related keys

{
  foo = 123
  bar = 123
}

{
  baz = 123
}

is equivalent to

foo = 123
bar = 123
baz = 123

if a value is defined many times, the last one will shadow the previous ones, just like if they were all defined at the top level

constants

$some_constant = 123 
abc = $some_constant

if a constant is redefined, the previous references will not be affected, but the following ones will be

$a = 123
foo = $a
$a = 456
bar = $a

will result in

foo = 123
bar = 456

environment variables

enviorment variables are automatically loaded as any other constant

user = $USER

default values

if a constant is not defined, you can put a ? after it and then another constant or value that will be used as the default value

$default_user = "some_user"
user = $USER?$default_user

# OR

user = $USER?"some_user"

chaining is allowed

something = $a?$b?$c?123 # will evaluate to 123 because neither $a, $b or $c are defined

$x = "x"
something2 = $a?$x?$c?456 # will evaluate to "x" because $x is defined (everything after $x is ignored)

ternary operator

you can use the ternary operator to choose between two values based on a defined constant with a boolean value or a straight up boolean value

$use_https = true
protocol = $use_https ~ "https" | "http" # evaluates to "https"

import

files can import other files, and the imported file will be parsed and merged with the current file (constants are shared between the files as well)

@import "other_file.mconf"

in the case of an import cycle, the file that is second in the chain will only have access to the properties of the first file that were defined before the import

specific import

you can specify exactly what you want to import from a file

a.mconf:

@import { foo, $bar } "b.mconf"
a = $bar

b.mconf:

foo = 123
$bar = 456

will result in:

foo = 123
a = 456

todo:

  • support for formatted strings
  • merge current env vars with the constants
  • allow for specifying what exactly to import from a file
  • allow specyfing default values for constants/env vars if they are not set
  • hexadecimal and binary numbers
  • add a --json flag to convert mconf to json

license

do whatever with this, i don't care

About

my own configuration language, made mostly for fun

Resources

License

Stars

Watchers

Forks

Packages

No packages published