MiniZinc grammar for tree-sitter
at develop 8.6 kB view raw
1const PREC = { 2 call: 15, 3 annotation: 14, 4 unary: 13, 5 exponent: 12, 6 multiplicative: 11, 7 additive: 10, 8 intersect: 9, 9 dotdot: 8, 10 symdiff: 7, 11 diff: 6, 12 union: 5, 13 comparative: 4, 14 and: 3, 15 xor: 2, 16 or: 1, 17 implication: 2, 18 equivalence: 1, 19}; 20 21const primitive_types = ["ann", "bool", "float", "int", "string"]; 22 23module.exports = grammar({ 24 name: "minizinc", 25 26 extras: ($) => [/\s/, $.line_comment, $.block_comment], 27 28 word: ($) => $.identifier, 29 30 conflicts: ($) => [ 31 [$._expression, $.generator], 32 [$._expression, $.assignment], 33 ], 34 35 supertypes: ($) => [$._expression, $._item, $._type], 36 37 rules: { 38 source_file: ($) => seq(sepBy(";", $._item)), 39 40 _item: ($) => 41 choice( 42 $.annotation, 43 $.assignment, 44 $.constraint, 45 $.declaration, 46 $.enumeration, 47 $.function_item, 48 $.goal, 49 $.include, 50 $.output, 51 $.predicate 52 ), 53 54 annotation: ($) => 55 seq( 56 "annotation", 57 field("name", $.identifier), 58 optional(field("parameters", $._parameters)), 59 optional(seq("=", field("expr", $._expression))) 60 ), 61 62 assignment: ($) => 63 seq(field("name", $.identifier), "=", field("expr", $._expression)), 64 65 constraint: ($) => seq("constraint", $._expression), 66 67 declaration: ($) => 68 seq( 69 field("type", $._type), 70 ":", 71 field("name", $.identifier), 72 optional(field("annotations", $._annotations)), 73 optional(seq("=", field("expr", $._expression))) 74 ), 75 76 enumeration: ($) => 77 seq( 78 "enum", 79 field("name", $.identifier), 80 optional(field("annotations", $._annotations)), 81 optional(seq("=", "{", field("members", sepBy(",", $.identifier)), "}")) 82 ), 83 84 function_item: ($) => 85 seq( 86 "function", 87 field("type", $._type), 88 ":", 89 field("name", $.identifier), 90 field("parameters", $._parameters), 91 optional(field("annotations", $._annotations)), 92 optional(seq("=", field("expr", $._expression))) 93 ), 94 95 goal: ($) => 96 seq( 97 "solve", 98 field( 99 "strategy", 100 choice( 101 "satisfy", 102 seq("maximize", $._expression), 103 seq("minimize", $._expression) 104 ) 105 ) 106 ), 107 108 include: ($) => seq("include", $.string_literal), 109 110 output: ($) => seq("output", $._expression), 111 112 predicate: ($) => 113 seq( 114 field("type", choice("predicate", "test")), 115 field("name", $.identifier), 116 field("parameters", $._parameters), 117 optional(field("annotations", $._annotations)), 118 optional(seq("=", field("expr", $._expression))) 119 ), 120 121 _annotations: ($) => repeat1(seq("::", $._expression)), 122 123 _parameters: ($) => 124 seq("(", sepBy(",", seq($._type, optional(seq(":", $.identifier)))), ")"), 125 _expression: ($) => 126 choice( 127 $.identifier, 128 $._literal, 129 130 $.array_comprehension, 131 $.call, 132 $.generator_call, 133 $.if_then_else, 134 $.indexed_access, 135 $.infix_operator, 136 $.let_expression, 137 $.prefix_operator, 138 $.set_comprehension, 139 $.string_interpolation, 140 $.parenthesised_expression 141 ), 142 143 parenthesised_expression: ($) => seq("(", $._expression, ")"), 144 145 array_comprehension: ($) => 146 seq("[", $._expression, "|", sepBy1(",", $.generator), "]"), 147 148 call: ($) => 149 prec( 150 PREC.call, 151 seq( 152 field("name", $.identifier), 153 "(", 154 field("arguments", sepBy(",", $._expression)), 155 ")" 156 ) 157 ), 158 159 generator_call: ($) => 160 prec( 161 PREC.call, 162 seq( 163 field("name", $.identifier), 164 "(", 165 field("generators", sepBy1(",", $.generator)), 166 ")", 167 "(", 168 field("template", $._expression), 169 ")" 170 ) 171 ), 172 173 generator: ($) => 174 seq( 175 $.identifier, 176 "in", 177 $._expression, 178 optional(seq("where", $._expression)) 179 ), 180 181 if_then_else: ($) => 182 seq( 183 "if", 184 $._expression, 185 "then", 186 $._expression, 187 repeat(seq("elseif", $._expression, "then", $._expression)), 188 optional(seq("else", $._expression)), 189 "endif" 190 ), 191 192 indexed_access: ($) => 193 prec( 194 PREC.call, 195 seq( 196 field("collection", $._expression), 197 "[", 198 field("indices", seq($._expression, repeat(seq(",", $._expression)))), 199 "]" 200 ) 201 ), 202 203 infix_operator: ($) => { 204 const table = [ 205 [prec.left, PREC.equivalence, "<->"], 206 [prec.left, PREC.implication, choice("->", "<-")], 207 [prec.left, PREC.or, "\\/"], 208 [prec.left, PREC.xor, "xor"], 209 [prec.left, PREC.and, "/\\"], 210 // TODO: Should really be nonassoc 211 // prettier-ignore 212 [prec.left, PREC.comparative, choice( 213 "=", "==", "!=", "<", "<=", ">", ">=", "in", "subset", "superset" 214 )], 215 [prec.left, PREC.union, "union"], 216 [prec.left, PREC.diff, "diff"], 217 [prec.left, PREC.symdiff, "symdiff"], 218 [prec.left, PREC.intersect, "intersect"], 219 // TODO: Could be nonassoc, will always give type error 220 [prec.left, PREC.dotdot, ".."], 221 [prec.left, PREC.additive, choice("+", "-", "++")], 222 [prec.left, PREC.multiplicative, choice("*", "/", "div", "mod")], 223 [prec.left, PREC.exponent, "^"], 224 [prec.left, PREC.annotation, "::"], 225 ]; 226 227 return choice( 228 ...table.map(([assoc, precedence, operator]) => 229 assoc( 230 precedence, 231 seq( 232 field("left", $._expression), 233 field("operator", operator), 234 field("right", $._expression) 235 ) 236 ) 237 ) 238 ); 239 }, 240 241 let_expression: ($) => 242 seq( 243 "let", 244 "{", 245 field( 246 "let", 247 sepBy(choice(",", ";"), choice($.declaration, $.constraint)) 248 ), 249 "}", 250 "in", 251 field("in", $._expression) 252 ), 253 254 prefix_operator: ($) => 255 prec( 256 PREC.unary, 257 seq(field("operator", choice("-", "not", "¬")), $._expression) 258 ), 259 260 set_comprehension: ($) => 261 seq("{", $._expression, "|", sepBy1(",", $.generator), "}"), 262 263 // TODO: Decide if string_literal and string_interpolation should be combined 264 string_interpolation: ($) => 265 seq( 266 '"', 267 optional($.string_content), 268 repeat1(seq("\\(", $._expression, ")", optional($.string_content))), 269 '"' 270 ), 271 272 _type: ($) => choice($.array_type, $.type_base), 273 array_type: ($) => 274 seq("array", "[", sepBy1(",", $.type_base), "]", "of", $._type), 275 type_base: ($) => 276 seq( 277 optional(field("var_par", choice("var", "par"))), 278 optional(field("opt", "opt")), 279 optional(field("set", seq("set", "of"))), 280 choice($.primitive_type, $._expression) 281 ), 282 primitive_type: ($) => choice(...primitive_types), 283 284 _literal: ($) => 285 choice( 286 $.absent, 287 $.array_literal, 288 $.boolean_literal, 289 $.float_literal, 290 $.integer_literal, 291 $.set_literal, 292 $.string_literal 293 ), 294 295 absent: ($) => "<>", 296 array_literal: ($) => seq("[", sepBy(",", $._expression), "]"), 297 boolean_literal: ($) => choice("true", "false"), 298 float_literal: ($) => 299 token( 300 choice( 301 /\d+\.\d+/, 302 /\d+(\.\d+)?[Ee][+-]?\d+/ 303 // TODO: Hexadecimal floating point numbers 304 ) 305 ), 306 integer_literal: ($) => 307 token(choice(/[0-9]+/, /0x[0-9a-fA-F]+/, /0b[01]+/, /0o[0-7]+/)), 308 set_literal: ($) => seq("{", sepBy(",", $._expression), "}"), 309 310 string_literal: ($) => 311 seq('"', alias(optional($.string_content), "content"), '"'), 312 string_content: ($) => 313 repeat1(choice(token.immediate(prec(1, /[^"\n\\]+/)), $.escape_sequence)), 314 escape_sequence: ($) => 315 token.immediate( 316 seq( 317 "\\", 318 choice( 319 /[^xuU]/, 320 /\d{2,3}/, 321 /x[0-9a-fA-F]{2,}/, 322 /u[0-9a-fA-F]{4}/, 323 /U[0-9a-fA-F]{8}/ 324 ) 325 ) 326 ), 327 328 identifier: ($) => /[A-Za-z][A-Za-z0-9_]*/, 329 330 line_comment: ($) => token(seq("%", /.*/)), 331 block_comment: ($) => token(seq("/*", /([^*]|\*[^\/]|\n)*?\*?/, "*/")), 332 }, 333}); 334 335function sepBy(sep, rule) { 336 return seq(repeat(seq(rule, sep)), optional(rule)); 337} 338 339function sepBy1(sep, rule) { 340 return seq(rule, repeat(seq(sep, rule)), optional(sep)); 341}