go-modlike is a Go library for parsing, building, and serializing configuration files in the Modlike format.
Modlike is inspired by the go.mod syntax: data is written as key value lines, while nested structures are represented with parenthesized blocks.
- parsing from
[]byte,string, andio.Reader; - document serialization to
[]byte,string, orio.Writer; - ordered data model: key order and list item order are preserved;
- support for lists, maps, and string values;
- string literals with escape sequences;
- line and block comments at the lexical analysis stage;
- strict per-key typing: once a key is first created as a list, it cannot later become a map.
go get github.com/80lk/modlikeThe minimum Go version is specified in go.mod.
project (
name project_name
version 1.0.0
)
project author 80LK
meta my_data "string value"
tags (
tag1
tag2
tag3
tag4
)
The root document is a map. Root key values can be:
- a list, when the value is written as a single string or as a block without keys;
- a map, when a nested key follows the root key or when a block contains
key valuepairs.
When a key appears more than once, values of the same type are merged: lists are appended, and maps are merged recursively.
package main
import (
"fmt"
"log"
"github.com/80lk/modlike"
)
func main() {
doc, err := modlike.ParseString(`
project (
name project_name
version 1.0.0
)
tags (
tag1
tag2
)
`)
if err != nil {
log.Fatal(err)
}
project, err := doc.GetMap("project")
if err != nil {
log.Fatal(err)
}
nameList, err := project.GetList("name")
if err != nil {
log.Fatal(err)
}
name, err := nameList.GetStringFirst()
if err != nil {
log.Fatal(err)
}
fmt.Println(name)
out, err := modlike.SerializeString(doc)
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
}func buildDocument() (modlike.Document, error) {
doc := modlike.New()
require, err := doc.CreateMap("require")
if err != nil {
return nil, err
}
module, err := require.CreateList("github.com/example/module")
if err != nil {
return nil, err
}
module.AppendString("v1.0.0")
replace, err := doc.CreateMap("replace")
if err != nil {
return nil, err
}
oldModule, err := replace.CreateList("github.com/old/module")
if err != nil {
return nil, err
}
oldModule.AppendString("github.com/new/module")
return doc, nil
}Main package functions:
modlike.New() Document- creates an empty document;modlike.Parse(data []byte) (Document, error)- parses a document from bytes;modlike.ParseString(src string) (Document, error)- parses a document from a string;modlike.ParseReader(r io.Reader) (Document, error)- parses a document from a stream;modlike.Serialize(doc Document) ([]byte, error)- serializes a document;modlike.SerializeString(doc Document) (string, error)- serializes a document to a string;modlike.WriteTo(w io.Writer, doc Document) error- writes a serialized document to a stream.
Marshal and Unmarshal are declared, but currently return modlike: not implemented.
Document is the root map with the following methods:
Has(key string) bool;GetKind(key string) Kind;Keys() []string;Get(key string) Value;GetList(key string) (*List, error);CreateList(key string) (*List, error);GetMap(key string) (*Map, error);CreateMap(key string) (*Map, error);Delete(key string);All(func(string, Value) bool).
Map stores values by string keys and supports the same operations for nested lists and maps.
List stores an ordered sequence of values. Strings, maps, and nested lists can be added to a list:
AppendString,PrependString,InsertString;AppendNewMap,PrependNewMap,InsertNewMap;AppendNewList,PrependNewList,InsertNewList;GetString,GetStringFirst,GetStringLast;GetMap,GetMapFirst,GetMapLast;GetList,GetListFirst,GetListLast;Delete,DeleteFirst,DeleteLast;All(func(int, Value) bool).
Value kind can be checked with Kind:
modlike.K_NONE;modlike.K_LIST;modlike.K_MAP;modlike.K_STRING.
- identifiers are used as unquoted keys and values;
- strings with spaces are written in double quotes;
- supported escape sequences are
\n,\r,\t,\",\\; - a block starts with
(and ends with); //starts a line comment;/* ... */defines a block comment;- empty lines are allowed.
Detailed grammar and format description are available in docs/spec.en.md, docs/modlike.ebnf, and docs/modlike.g4.
go test ./...The project is under active development. The parser, model transformer, and serializer are covered by tests, but the Marshal/Unmarshal API is not implemented yet.