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.
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 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.
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}