Another one.

This commit is contained in:
2025-08-27 01:29:54 -04:00
parent 7cad33405e
commit 542d36f266
4 changed files with 4254 additions and 1008 deletions

View File

@@ -7,78 +7,64 @@
/// <reference types="tree-sitter-cli/dsl" /> /// <reference types="tree-sitter-cli/dsl" />
// @ts-check // @ts-check
// grammar.js
const PREC = {
assign: 0, // right-assoc
add: 1, // left-assoc
mul: 2, // left-assoc
call: 3,
funcdef: 4,
lambda: 5,
};
module.exports = grammar({ module.exports = grammar({
name: "scl", name: "scl",
// Whitespace is of no consequence.
extras: ($) => [/\s/],
conflicts: ($) => [[$.params, $._exp]], conflicts: ($) => [[$.params, $._exp]],
rules: { rules: {
// A file is zero or more expressions.
source_file: ($) => repeat($._exp), source_file: ($) => repeat($._exp),
// Everything is expressions. Makes this a lot easier.
_exp: ($) => _exp: ($) =>
choice( choice(
$.num, $.num,
$.bool,
$.word, $.word,
$.binexp, $.binexp,
$.callexp, $.callexp,
$.funcdef,
$.lambda,
$.vardef,
$.parexp, $.parexp,
$.block,
$.vardef,
$.lambda,
$.funcdef,
$.ifexp,
), ),
// Literals / identifiers.
num: (_) => /\d+/, num: (_) => /\d+/,
bool: (_) => choice("TRUE", "T", "FALSE", "F"),
word: (_) => /[a-zA-Z_]\w*/, word: (_) => /[a-zA-Z_]\w*/,
// Binary expressions with precedence and left associativity.
binexp: ($) => binexp: ($) =>
choice( prec.left(
prec.left( 1,
PREC.add, seq(
seq( field("left", $._exp),
field("left", $._exp), field("op", choice("+", "-", "*", "/", "eq")),
field("op", choice("+", "-")), field("right", $._exp),
field("right", $._exp),
),
),
prec.left(
PREC.mul,
seq(
field("left", $._exp),
field("op", choice("*", "/")),
field("right", $._exp),
),
), ),
), ),
// Function call: prefer this over plain `word` via precedence.
callexp: ($) => callexp: ($) =>
prec( prec.left(
PREC.call, 1, // lower than funcdef
seq(field("fn", $.word), "(", optional(commaSep($._exp)), ")"), seq(field("fn", $.word), "(", optional(commaSep($._exp)), ")"),
), ),
// Convenient function definition (sugar): `f(x, y, ...) body`. parexp: ($) => seq("(", $._exp, ")"),
// Give it higher precedence than calls to resolve the shared prefix. block: ($) => seq("{", repeat($._exp), "}"),
vardef: ($) =>
prec.right(1, seq(field("name", $.word), "=", field("value", $._exp))),
lambda: ($) =>
prec.right(
2,
seq("\\", field("params", $.params), field("body", $._exp)),
),
funcdef: ($) => funcdef: ($) =>
prec( prec.right(
PREC.funcdef, 3, // higher precedence than call
seq( seq(
field("name", $.word), field("name", $.word),
field("params", $.params), field("params", $.params),
@@ -86,26 +72,33 @@ module.exports = grammar({
), ),
), ),
// Lambda: `\(x, y, ...) body`.
lambda: ($) =>
prec(
PREC.lambda,
seq("\\", field("params", $.params), field("body", $._exp)),
),
// Variable definition / assignment: `x = expr`.
// Lowest precedence, right-associative: `a = b = c` → `a = (b = c)`.
vardef: ($) =>
prec.right(
PREC.assign,
seq(field("name", $.word), "=", field("value", $._exp)),
),
// Parameter list node used by funcdef and lambda.
params: ($) => seq("(", optional(commaSep($.word)), ")"), params: ($) => seq("(", optional(commaSep($.word)), ")"),
// Parenthesized expression. ifexp: ($) =>
parexp: ($) => seq("(", $._exp, ")"), choice(
seq(
"_if",
"(",
field("cond", $._exp),
",",
field("then", $._exp),
",",
field("else", $._exp),
")",
),
seq(
"if",
field("cond", $._exp),
field("then", $._exp),
choice(seq("else", field("else", $._exp)), field("else", $._exp)),
),
seq(
"?",
field("cond", $._exp),
field("then", $._exp),
field("else", $._exp),
),
),
}, },
}); });

390
src/grammar.json generated
View File

@@ -16,6 +16,10 @@
"type": "SYMBOL", "type": "SYMBOL",
"name": "num" "name": "num"
}, },
{
"type": "SYMBOL",
"name": "bool"
},
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "word" "name": "word"
@@ -30,11 +34,11 @@
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "funcdef" "name": "parexp"
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "lambda" "name": "block"
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@@ -42,7 +46,15 @@
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "parexp" "name": "lambda"
},
{
"type": "SYMBOL",
"name": "funcdef"
},
{
"type": "SYMBOL",
"name": "ifexp"
} }
] ]
}, },
@@ -50,102 +62,88 @@
"type": "PATTERN", "type": "PATTERN",
"value": "\\d+" "value": "\\d+"
}, },
"bool": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "TRUE"
},
{
"type": "STRING",
"value": "T"
},
{
"type": "STRING",
"value": "FALSE"
},
{
"type": "STRING",
"value": "F"
}
]
},
"word": { "word": {
"type": "PATTERN", "type": "PATTERN",
"value": "[a-zA-Z_]\\w*" "value": "[a-zA-Z_]\\w*"
}, },
"binexp": { "binexp": {
"type": "CHOICE", "type": "PREC_LEFT",
"members": [ "value": 1,
{ "content": {
"type": "PREC_LEFT", "type": "SEQ",
"value": 1, "members": [
"content": { {
"type": "SEQ", "type": "FIELD",
"members": [ "name": "left",
{ "content": {
"type": "FIELD", "type": "SYMBOL",
"name": "left", "name": "_exp"
"content": { }
"type": "SYMBOL", },
"name": "_exp" {
"type": "FIELD",
"name": "op",
"content": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "+"
},
{
"type": "STRING",
"value": "-"
},
{
"type": "STRING",
"value": "*"
},
{
"type": "STRING",
"value": "/"
},
{
"type": "STRING",
"value": "eq"
} }
}, ]
{ }
"type": "FIELD", },
"name": "op", {
"content": { "type": "FIELD",
"type": "CHOICE", "name": "right",
"members": [ "content": {
{ "type": "SYMBOL",
"type": "STRING", "name": "_exp"
"value": "+" }
},
{
"type": "STRING",
"value": "-"
}
]
}
},
{
"type": "FIELD",
"name": "right",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
}
]
} }
}, ]
{ }
"type": "PREC_LEFT",
"value": 2,
"content": {
"type": "SEQ",
"members": [
{
"type": "FIELD",
"name": "left",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "FIELD",
"name": "op",
"content": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "*"
},
{
"type": "STRING",
"value": "/"
}
]
}
},
{
"type": "FIELD",
"name": "right",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
}
]
}
}
]
}, },
"callexp": { "callexp": {
"type": "PREC", "type": "PREC_LEFT",
"value": 3, "value": 1,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@@ -201,9 +199,46 @@
] ]
} }
}, },
"funcdef": { "parexp": {
"type": "PREC", "type": "SEQ",
"value": 4, "members": [
{
"type": "STRING",
"value": "("
},
{
"type": "SYMBOL",
"name": "_exp"
},
{
"type": "STRING",
"value": ")"
}
]
},
"block": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "{"
},
{
"type": "REPEAT",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "STRING",
"value": "}"
}
]
},
"vardef": {
"type": "PREC_RIGHT",
"value": 1,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@@ -216,16 +251,12 @@
} }
}, },
{ {
"type": "FIELD", "type": "STRING",
"name": "params", "value": "="
"content": {
"type": "SYMBOL",
"name": "params"
}
}, },
{ {
"type": "FIELD", "type": "FIELD",
"name": "body", "name": "value",
"content": { "content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "_exp" "name": "_exp"
@@ -235,8 +266,8 @@
} }
}, },
"lambda": { "lambda": {
"type": "PREC", "type": "PREC_RIGHT",
"value": 5, "value": 2,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@@ -263,9 +294,9 @@
] ]
} }
}, },
"vardef": { "funcdef": {
"type": "PREC_RIGHT", "type": "PREC_RIGHT",
"value": 0, "value": 3,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
@@ -278,12 +309,16 @@
} }
}, },
{ {
"type": "STRING", "type": "FIELD",
"value": "=" "name": "params",
"content": {
"type": "SYMBOL",
"name": "params"
}
}, },
{ {
"type": "FIELD", "type": "FIELD",
"name": "value", "name": "body",
"content": { "content": {
"type": "SYMBOL", "type": "SYMBOL",
"name": "_exp" "name": "_exp"
@@ -338,20 +373,145 @@
} }
] ]
}, },
"parexp": { "ifexp": {
"type": "SEQ", "type": "CHOICE",
"members": [ "members": [
{ {
"type": "STRING", "type": "SEQ",
"value": "(" "members": [
{
"type": "STRING",
"value": "_if"
},
{
"type": "STRING",
"value": "("
},
{
"type": "FIELD",
"name": "cond",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "STRING",
"value": ","
},
{
"type": "FIELD",
"name": "then",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "STRING",
"value": ","
},
{
"type": "FIELD",
"name": "else",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "STRING",
"value": ")"
}
]
}, },
{ {
"type": "SYMBOL", "type": "SEQ",
"name": "_exp" "members": [
{
"type": "STRING",
"value": "if"
},
{
"type": "FIELD",
"name": "cond",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "FIELD",
"name": "then",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "else"
},
{
"type": "FIELD",
"name": "else",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
}
]
},
{
"type": "FIELD",
"name": "else",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
}
]
}
]
}, },
{ {
"type": "STRING", "type": "SEQ",
"value": ")" "members": [
{
"type": "STRING",
"value": "?"
},
{
"type": "FIELD",
"name": "cond",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "FIELD",
"name": "then",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
},
{
"type": "FIELD",
"name": "else",
"content": {
"type": "SYMBOL",
"name": "_exp"
}
}
]
} }
] ]
} }

360
src/node-types.json generated
View File

@@ -11,6 +11,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -19,6 +27,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -60,6 +72,10 @@
{ {
"type": "/", "type": "/",
"named": false "named": false
},
{
"type": "eq",
"named": false
} }
] ]
}, },
@@ -71,6 +87,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -79,6 +103,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -103,6 +131,66 @@
} }
} }
}, },
{
"type": "block",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "binexp",
"named": true
},
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{
"type": "callexp",
"named": true
},
{
"type": "funcdef",
"named": true
},
{
"type": "ifexp",
"named": true
},
{
"type": "lambda",
"named": true
},
{
"type": "num",
"named": true
},
{
"type": "parexp",
"named": true
},
{
"type": "vardef",
"named": true
},
{
"type": "word",
"named": true
}
]
}
},
{
"type": "bool",
"named": true,
"fields": {}
},
{ {
"type": "callexp", "type": "callexp",
"named": true, "named": true,
@@ -126,6 +214,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -134,6 +230,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -169,6 +269,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -177,6 +285,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -221,6 +333,162 @@
} }
} }
}, },
{
"type": "ifexp",
"named": true,
"fields": {
"cond": {
"multiple": false,
"required": true,
"types": [
{
"type": "binexp",
"named": true
},
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{
"type": "callexp",
"named": true
},
{
"type": "funcdef",
"named": true
},
{
"type": "ifexp",
"named": true
},
{
"type": "lambda",
"named": true
},
{
"type": "num",
"named": true
},
{
"type": "parexp",
"named": true
},
{
"type": "vardef",
"named": true
},
{
"type": "word",
"named": true
}
]
},
"else": {
"multiple": false,
"required": true,
"types": [
{
"type": "binexp",
"named": true
},
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{
"type": "callexp",
"named": true
},
{
"type": "funcdef",
"named": true
},
{
"type": "ifexp",
"named": true
},
{
"type": "lambda",
"named": true
},
{
"type": "num",
"named": true
},
{
"type": "parexp",
"named": true
},
{
"type": "vardef",
"named": true
},
{
"type": "word",
"named": true
}
]
},
"then": {
"multiple": false,
"required": true,
"types": [
{
"type": "binexp",
"named": true
},
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{
"type": "callexp",
"named": true
},
{
"type": "funcdef",
"named": true
},
{
"type": "ifexp",
"named": true
},
{
"type": "lambda",
"named": true
},
{
"type": "num",
"named": true
},
{
"type": "parexp",
"named": true
},
{
"type": "vardef",
"named": true
},
{
"type": "word",
"named": true
}
]
}
}
},
{ {
"type": "lambda", "type": "lambda",
"named": true, "named": true,
@@ -233,6 +501,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -241,6 +517,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -302,6 +582,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -310,6 +598,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -346,6 +638,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -354,6 +654,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -399,6 +703,14 @@
"type": "binexp", "type": "binexp",
"named": true "named": true
}, },
{
"type": "block",
"named": true
},
{
"type": "bool",
"named": true
},
{ {
"type": "callexp", "type": "callexp",
"named": true "named": true
@@ -407,6 +719,10 @@
"type": "funcdef", "type": "funcdef",
"named": true "named": true
}, },
{
"type": "ifexp",
"named": true
},
{ {
"type": "lambda", "type": "lambda",
"named": true "named": true
@@ -463,10 +779,46 @@
"type": "=", "type": "=",
"named": false "named": false
}, },
{
"type": "?",
"named": false
},
{
"type": "F",
"named": false
},
{
"type": "FALSE",
"named": false
},
{
"type": "T",
"named": false
},
{
"type": "TRUE",
"named": false
},
{ {
"type": "\\", "type": "\\",
"named": false "named": false
}, },
{
"type": "_if",
"named": false
},
{
"type": "else",
"named": false
},
{
"type": "eq",
"named": false
},
{
"type": "if",
"named": false
},
{ {
"type": "num", "type": "num",
"named": true "named": true
@@ -474,5 +826,13 @@
{ {
"type": "word", "type": "word",
"named": true "named": true
},
{
"type": "{",
"named": false
},
{
"type": "}",
"named": false
} }
] ]

4397
src/parser.c generated

File diff suppressed because it is too large Load Diff