function caseInsensitive(word) {
return alias(new RegExp(word.split('')
.map(letter => `[${letter.toLowerCase()}${letter.toUpperCase()}]`)
.join('')), word);
}
const PN_CHAR_BASE = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}'
const PN_CHARS_U = PN_CHAR_BASE + '_'
const PN_CHARS = PN_CHARS_U + '\\-0-9\u00B7\u0300-\u036F\u203F-\u2040'
const PLX = '%[0-9a-fA-F]{2}|\\\\[_~.\\-!$&\'()*+,;=/?#@%]'
module.exports = grammar({
name: 'turtle',
extras: $ => [/\s*/, $.comment],
rules: {
turtle_doc: $ => repeat($._statement),
_statement: $ => choice($._directive, $.triples),
_directive: $ => choice($.prefix, $.base),
prefix: $ => choice(seq('@prefix', field('label', $._pname_ns), field('iri', $._iriref), '.'), seq(caseInsensitive('PREFIX'), field('label', $._pname_ns), field('iri', $._iriref))),
base: $ => choice(seq('@base', field('iri', $._iriref), '.'), seq(caseInsensitive('BASE'), field('iri', $._iriref))),
triples: $ => choice(seq(field('subject', $._subject), $._predicate_object_list, '.'), seq(field('subject', $.blank_node_property_list), optional($._predicate_object_list), '.')),
_predicate_object_list: $ => seq($.predicate_objects, repeat(seq(';', optional($.predicate_objects)))),
predicate_objects: $ => seq(field('predicate', $._verb), $._object_list),
_object_list: $ => seq($._object, repeat(seq(',', $._object))),
_verb: $ => choice($._predicate, $.a),
a: $ => 'a',
_subject: $ => choice($._iri, $._blank_node, $.collection),
_predicate: $ => $._iri,
_object: $ => choice($._iri, $._blank_node, $.collection, $.blank_node_property_list, $._literal),
_literal: $ => choice($.literal, $._numeric_literal, $.boolean),
blank_node_property_list: $ => seq('[', $._predicate_object_list, ']'),
collection: $ => seq('(', repeat($._object), ')'),
_numeric_literal: $ => choice($.integer, $.decimal, $.double),
literal: $ => seq(field('value', $.string), optional(choice(field('language', $._langtag), seq('^^', field('datatype', $._iri))))),
boolean: $ => token(choice('true', 'false')),
string: $ => choice(
/"([^"\\\r\n]|\\[tbnrf"'\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8})*"/,
/'([^'\\\r\n]|\\[tbnrf"'\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8})*'/,
/'''('?'?([^'\\]|\\[tbnrf"'\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}))*'''/,
/"""("?"?([^"\\]|\\[tbnrf"'\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}))*"""/,
),
_iri: $ => choice($._iriref, $.prefixed_name),
prefixed_name: $ => new RegExp("([" + PN_CHAR_BASE + "]([" + PN_CHARS + ".]*[" + PN_CHARS + "])?)?:(([" + PN_CHARS_U + ":0-9]|" + PLX + ")(([" + PN_CHARS + ".:]|" + PLX + ")*([" + PN_CHARS + ":]|" + PLX + "))?)?", 'u'),
_blank_node: $ => choice($._blank_node_label, $.anon),
_iriref: $ => seq('<', $.iriref, token.immediate('>')),
iriref: $ => token.immediate(/([^\x00-\x20<>"{}|^`\\]|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8})*/),
_pname_ns: $ => seq(optional($.pn_prefix), ':'),
_blank_node_label: $ => seq('_:', $.blank_node_label),
blank_node_label: $ => token.immediate(new RegExp("[" + PN_CHARS_U + "0-9]([" + PN_CHARS + ".]*[" + PN_CHARS + "])?", 'u')),
_langtag: $ => seq('@', $.langtag),
langtag: $ => token.immediate(/[a-zA-Z]+(-[a-zA-Z0-9]+)*/),
integer: $ => /[+-]?[0-9]+/,
decimal: $ => /[+-]?[0-9]*\.[0-9]+/,
double: $ => /[+-]?([0-9]+\.[0-9]*[eE][+-]?[0-9]+|\.[0-9]+[eE][+-]?[0-9]+|[0-9]+[eE][+-]?[0-9]+)/,
anon: $ => /\[\s*]/,
pn_prefix: $ => new RegExp("[" + PN_CHAR_BASE + "]([" + PN_CHARS + ".]*[" + PN_CHARS + "])?", 'u'),
pn_local: $ => token.immediate(new RegExp("([" + PN_CHARS_U + ":0-9]|" + PLX + ")(([" + PN_CHARS + ".:]|" + PLX + ")*([" + PN_CHARS + ":]|" + PLX + "))?", 'u')),
comment: $ => token(prec(-1, /#[^\r\n]*/)),
}
});