158 lines
4.4 KiB
JavaScript
158 lines
4.4 KiB
JavaScript
/**
|
|
* @file Kerolox grammar for tree-sitter
|
|
* @license AGPL-3.0-or-later
|
|
*
|
|
* Copyright (C) 2025-2026 Marceline Cramer
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*
|
|
* Kerolox is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License as published by the Free
|
|
* Software Foundation, either version 3 of the License, or (at your option) any
|
|
* later version.
|
|
*
|
|
* Kerolox is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Kerolox. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/// <reference types="tree-sitter-cli/dsl" />
|
|
// @ts-check
|
|
|
|
const list = el => seq(el, repeat(seq(",", el)))
|
|
const listComma = el => seq(el, choice(",", repeat1(seq(",", el))))
|
|
const parenList = (el) => seq("(", list(el), ")");
|
|
const parenListComma = (el) => seq("(", listComma(el), ")");
|
|
|
|
/// Shorthand to write a binary expression with left precedence of the given priority.
|
|
const expr_prec = (expr, precedence, op) => prec.left(precedence,
|
|
seq(field("lhs", expr), field("op", op), field("rhs", expr)))
|
|
|
|
export default grammar({
|
|
name: "kerolox",
|
|
|
|
extras: $ => [$._whitespace, $.comment],
|
|
|
|
rules: {
|
|
file: $ => repeat(choice(
|
|
// explicitly parse newlines and comments separately
|
|
// this helps determine which comments are doc comments
|
|
$.newline, $.comment,
|
|
// the actual items
|
|
$.type_alias, $.import, $.definition, $.rule, $.assumption
|
|
)),
|
|
|
|
comment: $ => seq(";", $.commentInner),
|
|
commentInner: _ => /[^\n]*\n/,
|
|
newline: _ => /[\n\r]/,
|
|
_whitespace: _ => /[ \n\r\t]/,
|
|
docs: _ => /(?:;.*\n)*/,
|
|
|
|
variable: _ => /[a-z][a-zA-Z0-9_]*/,
|
|
symbol: _ => /[A-Z][a-zA-Z0-9]*/,
|
|
_ident: $ => choice($.variable, $.symbol),
|
|
|
|
integer: _ => choice("0", /-?[1-9][0-9]*/),
|
|
|
|
type_alias: $ => seq(
|
|
"type",
|
|
field("name", $.symbol),
|
|
"=",
|
|
field("type", $.type),
|
|
),
|
|
|
|
type: $ => choice(
|
|
field("named", $.symbol),
|
|
parenListComma(field("tuple", $.type)),
|
|
),
|
|
|
|
import: $ => seq(
|
|
"import",
|
|
field("path", $.symbol),
|
|
repeat(seq(".", field("path", $.symbol))),
|
|
".",
|
|
choice(
|
|
field("item", $.symbol),
|
|
parenList(field("item", $.symbol))
|
|
),
|
|
),
|
|
|
|
definition: $ => seq(
|
|
"define",
|
|
field("input", optional("input")),
|
|
field("output", optional("output")),
|
|
field("decision", optional("decision")),
|
|
field("relation", $.symbol),
|
|
field("type", $.type),
|
|
),
|
|
|
|
rule: $ => seq(
|
|
optional(field("negate", "-")),
|
|
field("relation", $.symbol),
|
|
field("head", $.expr),
|
|
optional(seq(":-", field("body", $.rule_body))),
|
|
"."
|
|
),
|
|
|
|
assumption: $ => seq(
|
|
optional(seq("soft", "(", field("soft", $.integer), ")")),
|
|
":-",
|
|
field("body", $.rule_body),
|
|
"."
|
|
),
|
|
|
|
rule_body: $ => list(field("clause", $.expr)),
|
|
|
|
expr: $ => choice(
|
|
field("hole", "_"),
|
|
field("atom", $.atom),
|
|
field("tuple", $.tuple),
|
|
field("value", $.value),
|
|
field("variable", $.variable),
|
|
field("aggregate", $.aggregate),
|
|
field("unary", $.unary_expr),
|
|
field("binary", $.binary_expr),
|
|
seq('(', field("parens", $.expr), ')'),
|
|
),
|
|
|
|
atom: $ => prec.right(2, seq(field("head", $.symbol), field("body", $.expr))),
|
|
|
|
tuple: $ => parenListComma(field("el", $.expr)),
|
|
|
|
value: $ => choice(
|
|
field("true", "True"),
|
|
field("false", "False"),
|
|
field("symbol", $.symbol),
|
|
field("integer", $.integer),
|
|
),
|
|
|
|
aggregate: $ => seq(
|
|
field("op", $.aggregate_op),
|
|
choice(
|
|
field("atom", $.atom),
|
|
seq("{", field("body", $.rule_body ), "}"),
|
|
),
|
|
),
|
|
|
|
aggregate_op: $ => /@[a-z]+/,
|
|
|
|
unary_expr: $ => prec.left(5, seq(
|
|
field("op", $.unary_op),
|
|
field("term", $.expr)
|
|
)),
|
|
|
|
unary_op: _ => choice("!", "-"),
|
|
|
|
binary_expr: $ => choice(
|
|
expr_prec($.expr, 4, choice("*", "/")),
|
|
expr_prec($.expr, 3, choice("+", "-")),
|
|
expr_prec($.expr, 2, ".."),
|
|
expr_prec($.expr, 1, choice("=", "!=", ">=", "<=", "<", ">")),
|
|
expr_prec($.expr, 0, choice("&&", "||")),
|
|
),
|
|
}
|
|
});
|