goon
goon
https://git.tonybtw.com/goon.git
git://git.tonybtw.com/goon.git
Goon Language Specification
Version: 0.1.0
Goon is an embeddable configuration language with Nix-like syntax. It evaluates to JSON and is designed for window manager configs and similar applications.
Lexical Structure
Comments
// Single line comment
/* Multi-line
comment */
Identifiers
IDENT = [a-zA-Z_][a-zA-Z0-9_]*
Reserved keywords: let, if, then, else, true, false, import
Integers
INT = -?[0-9]+
Examples: 0, 42, -10
Strings
Double-quoted with escape sequences and interpolation:
"hello world"
"line1\nline2"
"value is ${x}"
Escape sequences:
\n- newline\t- tab\r- carriage return\\- backslash\"- double quote\$- literal dollar sign (escape interpolation)
Interpolation:
${identifier}is replaced with the string value of the variable- Integers and booleans are converted to strings automatically
Booleans
true
false
Operators and Punctuation
| Token | Description |
|---|---|
= |
Assignment |
=> |
Arrow (lambda) |
.. |
Range |
... |
Spread |
? |
Ternary condition |
: |
Ternary separator |
; |
Statement terminator |
, |
List/argument separator |
. |
Field access |
{} |
Record delimiters |
[] |
List delimiters |
() |
Grouping / parameters |
Grammar
program = statement* expression? ;
statement = let_binding ;
let_binding = "let" IDENT "=" expression ";" ;
expression = if_expr
| ternary ;
if_expr = "if" expression "then" expression "else" expression ;
ternary = primary ("?" expression ":" expression)? ;
primary = INT
| STRING
| "true" | "false"
| IDENT ("(" args? ")")? (* variable or function call *)
| IDENT ("." IDENT)* (* field access *)
| record
| list
| lambda
| import_expr
| "(" expression ")" ;
lambda = "(" params? ")" "=>" expression ;
params = IDENT ("," IDENT)* ;
args = expression ("," expression)* ;
record = "{" (record_item (";" record_item)* ";"?)? "}" ;
record_item = IDENT "=" expression
| "..." expression ;
list = "[" (list_item ("," list_item)* ","?)? "]" ;
list_item = range
| "..." expression
| expression ;
range = INT ".." INT ;
import_expr = "import" "(" STRING ")" ;
Types
Goon has the following value types:
| Type | Description | JSON Output |
|---|---|---|
nil |
Null value | null |
bool |
Boolean | true / false |
int |
64-bit integer | Number |
string |
UTF-8 string | String |
list |
Ordered collection | Array |
record |
Key-value map | Object |
lambda |
Function | (not serializable) |
Semantics
Let Bindings
Bind a name to a value. Semicolon is required.
let x = 42;
let name = "goon";
Bindings are visible after their definition in the same scope.
Records
Records are key-value maps with string keys.
{
name = "goon";
version = 1;
nested = {
foo = "bar";
};
}
Field access with dot notation:
let config = { name = "test"; };
let n = config.name; // "test"
Lists
Ordered collections of values.
[1, 2, 3]
["a", "b", "c"]
[{ x = 1; }, { x = 2; }]
Ranges
Ranges expand to inclusive integer sequences inside lists.
[1..5] // [1, 2, 3, 4, 5]
[1..1] // [1]
Spread Operator
Spread (...) merges values into lists or records.
In lists:
let a = [1, 2];
let b = [...a, 3, 4]; // [1, 2, 3, 4]
In records:
let defaults = { gap = 10; border = 2; };
let config = { ...defaults; gap = 20; }; // { gap = 20; border = 2; }
Later values override earlier ones.
Arrow Functions (Lambdas)
Anonymous functions for creating templates.
let double = (x) => { value = x; doubled = x; };
let make_key = (mod, key, cmd) => { mod = mod; key = key; cmd = cmd; };
Functions are called with parentheses:
let result = double(5); // { value = 5; doubled = 5; }
let k = make_key("super", "a", "app");
Functions capture their lexical environment (closures).
String Interpolation
Variables can be embedded in strings:
let name = "world";
let greeting = "hello ${name}"; // "hello world"
let n = 42;
let msg = "value is ${n}"; // "value is 42"
Conditionals
If-then-else:
let x = if true then 1 else 2; // 1
Ternary operator:
let x = true ? 1 : 2; // 1
Both require an else branch.
Imports
Import other goon files:
let colors = import("./colors.goon");
let theme = colors.dark;
- Paths are relative to the importing file
.goonextension is optional- Imported files are evaluated and their final expression is returned
Built-in Functions
map(list, function)
Apply a function to each element of a list.
let nums = [1..3];
let doubled = map(nums, (n) => n); // [1, 2, 3] (identity)
let keys = map([1..9], (n) => {
mod = "super";
key = n;
cmd = "workspace ${n}";
});
Constraints
- No arithmetic: Goon does not have
+,-,*,/operators - No comparison: No
==,<,>operators - No recursion: Functions cannot call themselves
- Immutable: Values cannot be reassigned after binding
Output
Goon evaluates to a single value, typically a record, which is serialized to JSON.
let name = "myapp";
let version = 1;
{
name = name;
version = version;
enabled = true;
}
Output:
{
"name": "myapp",
"version": 1,
"enabled": true
}
CLI Usage
# Evaluate and output JSON
goon eval config.goon
# Pretty-print output
goon eval config.goon --pretty
# Check syntax without evaluating
goon check config.goon
# Show version
goon --version
C API
#include "goon.h"
// Create context
Goon_Ctx *ctx = goon_create();
// Load and evaluate
if (!goon_load_file(ctx, "config.goon")) {
const Goon_Error *err = goon_get_error_info(ctx);
goon_error_print(err);
return 1;
}
// Get result
Goon_Value *result = goon_eval_result(ctx);
// Convert to JSON
char *json = goon_to_json_pretty(result, 2);
printf("%s\n", json);
free(json);
// Cleanup
goon_destroy(ctx);
Registering Built-in Functions
Goon_Value *my_builtin(Goon_Ctx *ctx, Goon_Value **args, size_t argc) {
// Implementation
return goon_int(ctx, 42);
}
goon_register(ctx, "my_func", my_builtin);
Future Considerations
The following features may be added in future versions:
- Arithmetic operators (
+,-,*,/,%) - Comparison operators (
==,!=,<,>,<=,>=) - Logical operators (
&&,||,!) - Optional type annotations
- Hex integer literals (
0xFF) - Filter in map:
map(list, fn, predicate)