Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kccqzy/qcl
An experimental configuration language that produces JSON
https://github.com/kccqzy/qcl
Last synced: about 1 month ago
JSON representation
An experimental configuration language that produces JSON
- Host: GitHub
- URL: https://github.com/kccqzy/qcl
- Owner: kccqzy
- Created: 2024-04-21T23:05:53.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2024-05-13T01:33:54.000Z (8 months ago)
- Last Synced: 2024-05-13T02:35:36.008Z (8 months ago)
- Language: Haskell
- Size: 60.5 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Introduction
QCL is an experimental, dynamically typed "little language" (as defined by
Chapter 6 of *The AWK Programming Language*) that produces JSON output. It is
JSON but more convenient to write, and it allows a little bit of computation.# Motivation
This project mainly exists because I want to play with a natural and intuitive
(to me) syntax. I also want to play with [the Earley
parser](https://en.wikipedia.org/wiki/Earley_parser) which is capable of parsing
context-free languages (I want my language to be strictly context-free after
lexing). I also want to play with making named tuples (aka dictionaries,
JS-style objects) a first-class citizen of the language, such that almost
everything is built on top of it. The abstract tuple is a powerful feature
whereby the evaluation of the tuple is delayed until such time intended by the
user through the `eval` keyword. It can be thought of as providing a *template*
tuple with values to be filled in later; it can also be thought of as
introducing a lambda function where the values to be filled in are function
arguments. These lambdas are closures and capture their environment.I also want to produce extremely helpful and detailed error messages, which
sadly I usually don't get to do too often.Don't use this in production. This is my playground.
# Demo
This shows real examples. Feel free to run it using the *configlang-examples*
executable.QCL:
```
true
```JSON result:
```
true
```------------
QCL:
```
1.234
```JSON result:
```
1.234
```------------
QCL:
```
1+2+3+4+5
```JSON result:
```
15
```------------
QCL:
```
17.5 / 5.5
```JSON result:
```
3.1818181818181817
```------------
QCL:
```
17.5 // 5.5
```JSON result:
```
3
```------------
QCL:
```
17.5 % 5.5
```JSON result:
```
1
```------------
QCL:
```
1 + 2 * 3 + 4 < 5
```JSON result:
```
false
```------------
QCL:
```
a+b.c.d
```Error message:
```
error:
variable reference "a" must be inside a tuple
|
1 | a+b.c.d
| ^ top-level variable reference```
------------
QCL:
```
{}
```JSON result:
```
{}
```------------
QCL:
```
{} =
```Error message:
```
parse error:
|
1 | {} =
| ^ expecting "||", "&&", "!=", "==", ">=", ">", "<=", "<", "-", "+", "//", "%", "/", "*", ".", "{", found "="```
------------
QCL:
```
{} { }
```JSON result:
```
{}
```------------
QCL:
```
{x = true}
```JSON result:
```
{"x":true}
```------------
QCL:
```
{x = true, }
```JSON result:
```
{"x":true}
```------------
QCL:
```
{x = true,
y = false
}
```JSON result:
```
{"x":true,"y":false}
```------------
QCL:
```
{x = true,
y = false,
}
```JSON result:
```
{"x":true,"y":false}
```------------
QCL:
```
{x = true,
y = x + 1}
```JSON result:
```
{"x":true,"y":2}
```------------
QCL:
```
{x = true,
y }
```Error message:
```
parse error:
|
2 | y }
| ^ expecting "%=", "//=", "/=", "*=", "-=", "+=", "{", "=", found "}"```
------------
QCL:
```
{private x = true,
y = x + 1}
```JSON result:
```
{"y":2}
```------------
QCL:
```
{private x = true, y = x + 1} { x = false }
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | {private x = true, y = x + 1} { x = false }
| ^ access of private field here
|
1 | {private x = true, y = x + 1} { x = false }
| ^ defined here
|
1 | {private x = true, y = x + 1} { x = false }
| ^^^^^^^ marked as private here```
------------
QCL:
```
{private x = true, y = x + 1} { delete x }
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | {private x = true, y = x + 1} { delete x }
| ^ access of private field here
|
1 | {private x = true, y = x + 1} { delete x }
| ^ defined here
|
1 | {private x = true, y = x + 1} { delete x }
| ^^^^^^^ marked as private here```
------------
QCL:
```
{private x = true, y = x + 1}.x
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | {private x = true, y = x + 1}.x
| ^ access of private field here
|
1 | {private x = true, y = x + 1}.x
| ^ defined here
|
1 | {private x = true, y = x + 1}.x
| ^^^^^^^ marked as private here```
------------
QCL:
```
{private x = true} { private x = false}
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | {private x = true} { private x = false}
| ^ access of private field here
|
1 | {private x = true} { private x = false}
| ^ defined here
|
1 | {private x = true} { private x = false}
| ^^^^^^^ marked as private here```
------------
QCL:
```
{x = true,
assert(true), }
```JSON result:
```
{"x":true}
```------------
QCL:
```
{x = 5, assert(x % 2
==
0), }
```Error message:
```
error:
assertion failed
|
1 | {x = 5, assert(x % 2
| ^^^^^
2 | ==
| ^^
3 | 0), }
| ^ evaluates to false
|
1 | {x = 5, assert(x % 2
| ^ this has value 5```
------------
QCL:
```
{x = 2,
x = 1}
```Error message:
```
error:
duplicate field label "x" in tuple
|
2 | x = 1}
| ^ this definition
|
1 | {x = 2,
| ^ earlier definition```
------------
QCL:
```
1 + { a = 2, b = a + 3}.b
```JSON result:
```
6
```------------
QCL:
```
1 + { a = 2, b = a + 3}
```Error message:
```
error:
unexpected type for expression
|
1 | 1 + { a = 2, b = a + 3}
| ^^^^^^^^^^^^^^^^^^^ expecting number or boolean, found tuple```
------------
QCL:
```
{
a = 1,
b = a + a,
c = a + b + c
}.c
```Error message:
```
error:
variable reference "c" does not exist
|
4 | c = a + b + c
| ^ undefined variable reference```
------------
QCL:
```
[]
```JSON result:
```
[]
```------------
QCL:
```
[1, true]
```JSON result:
```
[1,true]
```------------
QCL:
```
{a = {b = {c=1}}, ret = a.b{x=1}}.ret
```JSON result:
```
{"c":1,"x":1}
```------------
QCL:
```
{a = {b = {c=1}}, ret = a{x=1}.b}.ret
```JSON result:
```
{"c":1}
```------------
QCL:
```
{ x=1, y=2, z=3 } {z = 4}
```JSON result:
```
{"x":1,"y":2,"z":4}
```------------
QCL:
```
{ x=1, y=2, z=3 } {z += 4}
```JSON result:
```
{"x":1,"y":2,"z":7}
```------------
QCL:
```
{ x=1, y=2, z=3 } {z *= z}
```JSON result:
```
{"x":1,"y":2,"z":9}
```------------
QCL:
```
{ x=1, y=2, z=3 } {delete z}
```JSON result:
```
{"x":1,"y":2}
```------------
QCL:
```
{ x=1, y=2, z=3 } .x
```JSON result:
```
1
```------------
QCL:
```
{ x=1, y=2, z=3 }.wwww
```Error message:
```
error:
label "wwww" does not exist in tuple
|
1 | { x=1, y=2, z=3 }.wwww
| ^^^^ tuple has labels "x", "y", "z"```
------------
QCL:
```
{ x=1, y=2, z=y }
```JSON result:
```
{"x":1,"y":2,"z":2}
```------------
QCL:
```
{ x=1, y=2, z={a=1, b=y} }
```JSON result:
```
{"x":1,"y":2,"z":{"a":1,"b":2}}
```------------
QCL:
```
{ x=1, y=2, z={a=1, b=a} }
```JSON result:
```
{"x":1,"y":2,"z":{"a":1,"b":1}}
```------------
QCL:
```
{ x=1, y=2, z={x=1, y=x} }
```Error message:
```
error:
variable reference "x" is ambiguous
|
1 | { x=1, y=2, z={x=1, y=x} }
| ^ variable reference used here
|
1 | { x=1, y=2, z={x=1, y=x} }
| ^ possible referent
|
1 | { x=1, y=2, z={x=1, y=x} }
| ^ another possible referent```
------------
QCL:
```
{ inner = { private x = 1, y = 2 }, result = inner.x }
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | { inner = { private x = 1, y = 2 }, result = inner.x }
| ^ access of private field here
|
1 | { inner = { private x = 1, y = 2 }, result = inner.x }
| ^ defined here
|
1 | { inner = { private x = 1, y = 2 }, result = inner.x }
| ^^^^^^^ marked as private here```
------------
QCL:
```
{ private x = 1, inner = { y = 2 + x } }
```JSON result:
```
{"inner":{"y":3}}
```------------
QCL:
```
# A convenient syntax for nested tuple updates.
{ a=1, b={ x=2, y=3 } } { b { y = 100 } }
```JSON result:
```
{"a":1,"b":{"x":2,"y":100}}
```------------
QCL:
```
{ z = { irrelevant = 1000 },
inner = {
z = { x = 0 }
} {
z { x += 1 } # This z updates the field in the current tuple, not the outside one.
}
}
```JSON result:
```
{"inner":{"z":{"x":1}},"z":{"irrelevant":1000}}
```------------
QCL:
```
{ x = 1, y = 2, z = { a = x + 1, b = y + a + 2}}.z.b
```JSON result:
```
6
```------------
QCL:
```
{ a=1, b=2 } { a=b+1 } { b=a+1 } { a=b+1 } { b=a+1 }
```JSON result:
```
{"a":5,"b":6}
```------------
QCL:
```
{ final meaningOfLife = 42 } { meaningOfLife = 43 }
```Error message:
```
error:
field marked as final cannot be overridden
|
1 | { final meaningOfLife = 42 } { meaningOfLife = 43 }
| ^^^^^^^^^^^^^ override here
|
1 | { final meaningOfLife = 42 } { meaningOfLife = 43 }
| ^^^^^^^^^^^^^ defined here
|
1 | { final meaningOfLife = 42 } { meaningOfLife = 43 }
| ^^^^^ marked as final here```
------------
QCL:
```
{ final
meaningOfLife = 42 } { delete meaningOfLife }
```Error message:
```
error:
field marked as final cannot be overridden
|
2 | meaningOfLife = 42 } { delete meaningOfLife }
| ^^^^^^^^^^^^^ override here
|
2 | meaningOfLife = 42 } { delete meaningOfLife }
| ^^^^^^^^^^^^^ defined here
|
1 | { final
| ^^^^^ marked as final here```
------------
QCL:
```
{
# Perl-style boolean operators.
oneOrTwo = 1 || 2,
zeroOrTwo = 0 || 2,
oneAndTwo = 1 && 2,
zeroAndTwo = 0 && 2,
# Substitute for the conditional operator.
ifTrue = 1 && 100 || 200,
ifFalse = 0 && 100 || 200,
# Short-circuiting. Even type errors are not found.
simplyFalse = false && (1+{}),
simplyTrue = true || (1+{}),
}
```JSON result:
```
{"ifFalse":200,"ifTrue":100,"oneAndTwo":2,"oneOrTwo":1,"simplyFalse":false,"simplyTrue":true,"zeroAndTwo":0,"zeroOrTwo":2}
```------------
QCL:
```
{ abstract a }
```Error message:
```
error:
abstract field cannot be used in non-abstract tuples
|
1 | { abstract a }
| ^^^^^^^^ abstract field```
------------
QCL:
```
{}.eval
```Error message:
```
error:
unexpected type for expression
|
1 | {}.eval
| ^^ expecting abstract tuple, found tuple```
------------
QCL:
```
# Abstract tuples are not automatically evaluated. It is not evaluated here.
abstract {
abstract a,
assert (a % 2 == 0),
ret = a / 2,
}
```JSON result:
```
null
```------------
QCL:
```
# Abstract tuple updates are also not automatically evaluated. It is not evaluated here.
abstract {
abstract a,
assert (a % 2 == 0),
ret = a / 2,
} { a = 42 }
```JSON result:
```
null
```------------
QCL:
```
# The abstract tuple must be evaluated explicitly using the eval keyword.
abstract {
abstract a,
assert (a % 2 == 0),
ret = a / 2,
} { a = 42 }.eval
```JSON result:
```
{"a":42,"ret":21}
```------------
QCL:
```
{
private checkEven = abstract {
abstract a,
assert(a % 2 == 0),
ret = a / 2,
},
e1 = checkEven { a = 100 },
} { e1 = e1.eval.ret }
```JSON result:
```
{"e1":50}
```------------
QCL:
```
{
private checkEven = abstract {
abstract a,
# This will fail.
assert(a % 2 == 0),
ret = a / 2,
},
e1 = checkEven { a = 105 },
} { e1 = e1.eval.ret }
```Error message:
```
error:
assertion failed
|
5 | assert(a % 2 == 0),
| ^^^^^^^^^^ evaluates to false
|
5 | assert(a % 2 == 0),
| ^ this has value 105```
------------
QCL:
```
abstract {
abstract a,
assert (a % 2 == 0),
ret = a / 2,
} {
a = 42
} {
a = 64 # This override fails because abstract rows must be overridden exactly once.
}.eval
```Error message:
```
error:
non-abstract field cannot be overridden in an abstract tuple
(consider first evaluating the abstract tuple into a tuple,
and then overriding this field)
|
8 | a = 64 # This override fails because abstract rows must be overridden exactly once.
| ^ override of field here
|
6 | a = 42
| ^ previous value defined here
|
1 | abstract {
| ^^^^^^^^ tuple marked as abstract here```
------------
QCL:
```
# Variables in abstract tuples can refer to the surrounding scope (lexical scope).
{ a = 1,
b = abstract {
c = a
}.eval,
assert (b.c==a)
}
```JSON result:
```
{"a":1,"b":{"c":1}}
```------------
QCL:
```
# Variables in abstract tuple updates can also refer to the surrounding scope.
{ a = 1,
b = abstract {
abstract c
}
} {
b = b {c = a}.eval,
assert (b.c==a)
}
```JSON result:
```
{"a":1,"b":{"c":1}}
```------------
QCL:
```
# Variables in abstract tuple updates refer to the value upon the update, not during evaluation.
{ a = 1,
b = abstract {
abstract c
}
} {
b {c = a}, # This `a` is 1.
a = a + a, # `a` becomes 2
b = b.eval, # Evaluation of `b` uses the `a` whose value is 1.
assert (b.c!=a)
}
```JSON result:
```
{"a":2,"b":{"c":1}}
```------------
QCL:
```
abstract{
abstract a,
b = abstract{
abstract x,
ret = x*10,
},
ret = a + b { x = a * 100}.eval.ret
} { a = 5 }.eval.ret
```JSON result:
```
5005
```------------
QCL:
```
# Mutual reference is not allowed
abstract { abstract a, b = a+1 } { a = b+1 } .eval
```Error message:
```
error:
variable reference "b" does not exist
|
2 | abstract { abstract a, b = a+1 } { a = b+1 } .eval
| ^ undefined variable reference```
------------
QCL:
```
# Abstract tuples delay evaluation. They allow but do not require abstract fields.
abstract {
x = 0,
y = 1,
z = 2,
}.eval {
x = 2,
y = 4,
}
```JSON result:
```
{"x":2,"y":4,"z":2}
```------------
QCL:
```
abstract {
x = 2,
y = 4,
ret = x * y,
} {
# Will not work because non-abstract fields in abstract tuples cannot be overridden.
x += 1,
}.eval
```Error message:
```
error:
non-abstract field cannot be overridden in an abstract tuple
(consider first evaluating the abstract tuple into a tuple,
and then overriding this field)
|
7 | x += 1,
| ^ override of field here
|
2 | x = 2,
| ^ previous value defined here
|
1 | abstract {
| ^^^^^^^^ tuple marked as abstract here```
------------
QCL:
```
abstract {
x = 2,
y = 4,
ret = x * y,
}.eval {
# Works.
x += 1,
}
```JSON result:
```
{"ret":8,"x":3,"y":4}
```------------
QCL:
```
{a=2, b = abstract { abstract c, assert (c%a == 0) }} { b = b { c = 10 }.eval }
```JSON result:
```
{"a":2,"b":{"c":10}}
```------------
QCL:
```
{a=2, b = abstract { abstract c, assert (c%a == 0) }} { b = b { c = 11 }.eval }
```Error message:
```
error:
assertion failed
|
1 | {a=2, b = abstract { abstract c, assert (c%a == 0) }} { b = b { c = 11 }.eval }
| ^^^^^^^^ evaluates to false
|
1 | {a=2, b = abstract { abstract c, assert (c%a == 0) }} { b = b { c = 11 }.eval }
| ^ this has value 2
|
1 | {a=2, b = abstract { abstract c, assert (c%a == 0) }} { b = b { c = 11 }.eval }
| ^ this has value 11```
------------
QCL:
```
abstract { abstract x, private y = x + x, z = y * y } { x = 10 }.eval
```JSON result:
```
{"x":10,"z":400}
```------------
QCL:
```
abstract { abstract x, private y = x + x } { x = 10 }.eval.y
```Error message:
```
error:
field marked as private cannot be accessed outside its enclosing tuple
|
1 | abstract { abstract x, private y = x + x } { x = 10 }.eval.y
| ^ access of private field here
|
1 | abstract { abstract x, private y = x + x } { x = 10 }.eval.y
| ^ defined here
|
1 | abstract { abstract x, private y = x + x } { x = 10 }.eval.y
| ^^^^^^^ marked as private here```
------------
QCL:
```
# It is possible to mark an overridden field private. It is orthogonal
# to the evaluation order.
abstract {
abstract x,
ret = x * x,
} {
private x = 10,
}.eval
```JSON result:
```
{"ret":100}
```------------
QCL:
```
# It is also possible to mark an overridden field final.
abstract {
abstract x,
ret = x * x,
} {
final x = 10,
}.eval
```JSON result:
```
{"ret":100,"x":10}
```------------
QCL:
```
# This example showcases Church-encoded booleans in an untyped lambda calculus.
{
private t = abstract { abstract a, abstract b, ret = a },
private f = abstract { abstract a, abstract b, ret = b },# Boolean and
private and = abstract { abstract p, abstract q, ret = p { a = q, b = p }},
trueAndFalse = and { p = t, q = f }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
falseAndTrue = and { p = f, q = t }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
trueAndTrue = and { p = t, q = t }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
falseAndFalse = and { p = f, q = f }.eval.ret.eval.ret{a=true,b=false}.eval.ret,# Boolean or
private or = abstract { abstract p, abstract q, ret = p { a = p, b = q }},
trueOrFalse = or { p = t, q = f }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
falseOrTrue = or { p = f, q = t }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
trueOrTrue = or { p = t, q = t }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
falseOrFalse = or { p = f, q = f }.eval.ret.eval.ret{a=true,b=false}.eval.ret,
}
```JSON result:
```
{"falseAndFalse":false,"falseAndTrue":false,"falseOrFalse":false,"falseOrTrue":true,"trueAndFalse":false,"trueAndTrue":true,"trueOrFalse":true,"trueOrTrue":true}
```------------
QCL:
```
# The omega combinator. In lambda calculus, the omega combinator diverges (infinite
# loop). But in this language, every abstract tuple evaluation must be explicit.
# Therefore, by placing the eval at the right place, it is possible to see each
# stage of applying the omega combinator.
{
omega = abstract {
abstract x,
ret = x { x = x } # This program would loop if the eval keyword is here.
},
} {
omega { x = omega },
o1 = omega.eval.ret,
o2 = omega.eval.ret.eval.ret,
o3 = omega.eval.ret.eval.ret.eval.ret,
o4 = omega.eval.ret.eval.ret.eval.ret.eval.ret,
o5 = omega.eval.ret.eval.ret.eval.ret.eval.ret.eval.ret,
o6 = omega.eval.ret.eval.ret.eval.ret.eval.ret.eval.ret.eval.ret,
# Ad infitinum.
}
```JSON result:
```
{"o1":null,"o2":null,"o3":null,"o4":null,"o5":null,"o6":null,"omega":null}
```------------
QCL:
```
{
# This implements a recursive function call using the well-known trick of
# having an argument to refer to the recursion and passing the function itself.
factorial = abstract {
abstract x,
abstract rec,
ret = x == 0 && 1 || x * rec { x = x - 1, rec = rec }.eval.ret
},
} {
final factorial { rec = factorial }
} {
f10 = factorial { x = 10 }.eval.ret
}
```JSON result:
```
{"f10":3628800,"factorial":null}
```------------