-
I'm trying to write a grammar for Inkle's Ink language for interactive fiction. Here's my test case: {blofeld and old: I saw that old git,|Dunno,} I said.
{blofeld is old: and that's a fact,|I already told you about his age,} I said. These two lines do different things:
The distinguishing factor between these two cases is that I thought I could resolve this by declaring a conflict, essentially saying "try both, then pick the survivor". But tree-sitter just tells me "Warning: unnecessary conflicts", so I'm clearly not understanding something important here. Is there any way to get this to work? Minimal Grammarmodule.exports = grammar({
name: 'ink',
inline: $ => [$._expr],
conflicts: $ => [
// If I enable any of these, tree-sitter tells me 'unnecessary conflicts' 🤷
[$.alternatives, $.conditional_text],
[$._expr, $.text],
[$.identifier, $.text],
],
rules: {
ink: $ => seq(repeat($._line)),
_line: $ => seq($.flow, alias(/\n/, '\\n')),
// positive or negative precedence here determines if the flow rule tries conditional_text or alternatives,
// but it doesn't get it right in context.
text: _ => token(prec(-10, /[^\n\{\}|]+/)),
flow: $ => prec.right(repeat1(choice(
$.conditional_text,
$.alternatives,
$.text,
))),
alternatives: $ => seq(
'{',
repeat1(choice('|', $.flow)),
'}',
),
conditional_text: $ => seq(
'{',
field('condition', $._expr),
":",
field('iftrue', $.flow),
"|",
field('else', $.flow),
'}',
),
_expr: $ => choice(
$.identifier,
$.binary,
),
binary: $ => prec.left(seq($._expr, field('op', $._binary_operator), $._expr)),
_binary_operator: _ => choice("and", "or"),
identifier: _ => /[a-zA-z_][a-zA-Z0-9_]*/,
},
}); Parse TreesMassaging the grammar (by commenting out and What I want is the left branch to have the Bonus Question: While I'd love to understand what I'm missing here, the underlying problem seems to be that the whole language basically works like "try to match a control structure and if that fails, it's text". Is there a pattern in tree-sitter to implement such an "ultimate fallback rule"? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
OK, I've cracked it (I think): IDENT_REGEX = /[a-zA-z_][a-zA-Z0-9_]*/
module.exports = grammar({
// …
conflicts: $ => [
[$.identifier, $.text],
],
rules: {
// …
text: $ => prec.right(repeat1(choice(
IDENT_REGEX, // this needs to come first, because the next regex is more general and tree-sitter goes by rule order. If the next regex matched an identifier, it wouldn't trigger the conflict.
/[^\s\{\}|:]+/,
':',
$._binary_operator,
))),
// …
identifier: _ => IDENT_REGEX,
},
}); My mistakes:
The first two are pretty obvious. The third one is sort of understandable, but the specifics are a bit mystifying: I can reference Anyway; I think I now understand the basics of it. Let's see if I can make it work with the actual Ink syntax (which is of course more complicated than this minimal example 🤯). |
Beta Was this translation helpful? Give feedback.
OK, I've cracked it (I think):
My mistakes:
token(prec(…))
) for the text._.expr