/** * @file SCL grammar for tree-sitter * @author Jacob Signorovitch * @license MIT */ /// // @ts-check module.exports = grammar({ name: "scl", conflicts: ($) => [[$.params, $._exp]], rules: { source_file: ($) => repeat($._exp), _exp: ($) => choice( $.num, $.bool, $.word, $.binexp, $.callexp, $.parexp, $.block, $.vardef, $.lambda, $.funcdef, $.ifexp, ), num: (_) => /\d+/, bool: (_) => choice("TRUE", "T", "FALSE", "F"), word: (_) => /[a-zA-Z_]\w*/, binexp: ($) => prec.left( 1, seq( field("left", $._exp), field("op", choice("+", "-", "*", "/", "eq")), field("right", $._exp), ), ), callexp: ($) => prec.left( 1, // lower than funcdef seq(field("fn", $.word), "(", optional(commaSep($._exp)), ")"), ), parexp: ($) => seq("(", $._exp, ")"), 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: ($) => prec.right( 3, // higher precedence than call seq( field("name", $.word), field("params", $.params), field("body", $._exp), ), ), params: ($) => seq("(", optional(commaSep($.word)), ")"), ifexp: ($) => 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), ), ), }, }); function commaSep(rule) { return seq(rule, repeat(seq(",", rule))); }