sparql.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineMode('sparql', function (config) {
  15. var indentUnit = config.indentUnit
  16. var curPunc
  17. function wordRegexp(words) {
  18. return new RegExp('^(?:' + words.join('|') + ')$', 'i')
  19. }
  20. var ops = wordRegexp([
  21. 'str',
  22. 'lang',
  23. 'langmatches',
  24. 'datatype',
  25. 'bound',
  26. 'sameterm',
  27. 'isiri',
  28. 'isuri',
  29. 'iri',
  30. 'uri',
  31. 'bnode',
  32. 'count',
  33. 'sum',
  34. 'min',
  35. 'max',
  36. 'avg',
  37. 'sample',
  38. 'group_concat',
  39. 'rand',
  40. 'abs',
  41. 'ceil',
  42. 'floor',
  43. 'round',
  44. 'concat',
  45. 'substr',
  46. 'strlen',
  47. 'replace',
  48. 'ucase',
  49. 'lcase',
  50. 'encode_for_uri',
  51. 'contains',
  52. 'strstarts',
  53. 'strends',
  54. 'strbefore',
  55. 'strafter',
  56. 'year',
  57. 'month',
  58. 'day',
  59. 'hours',
  60. 'minutes',
  61. 'seconds',
  62. 'timezone',
  63. 'tz',
  64. 'now',
  65. 'uuid',
  66. 'struuid',
  67. 'md5',
  68. 'sha1',
  69. 'sha256',
  70. 'sha384',
  71. 'sha512',
  72. 'coalesce',
  73. 'if',
  74. 'strlang',
  75. 'strdt',
  76. 'isnumeric',
  77. 'regex',
  78. 'exists',
  79. 'isblank',
  80. 'isliteral',
  81. 'a',
  82. 'bind',
  83. ])
  84. var keywords = wordRegexp([
  85. 'base',
  86. 'prefix',
  87. 'select',
  88. 'distinct',
  89. 'reduced',
  90. 'construct',
  91. 'describe',
  92. 'ask',
  93. 'from',
  94. 'named',
  95. 'where',
  96. 'order',
  97. 'limit',
  98. 'offset',
  99. 'filter',
  100. 'optional',
  101. 'graph',
  102. 'by',
  103. 'asc',
  104. 'desc',
  105. 'as',
  106. 'having',
  107. 'undef',
  108. 'values',
  109. 'group',
  110. 'minus',
  111. 'in',
  112. 'not',
  113. 'service',
  114. 'silent',
  115. 'using',
  116. 'insert',
  117. 'delete',
  118. 'union',
  119. 'true',
  120. 'false',
  121. 'with',
  122. 'data',
  123. 'copy',
  124. 'to',
  125. 'move',
  126. 'add',
  127. 'create',
  128. 'drop',
  129. 'clear',
  130. 'load',
  131. ])
  132. var operatorChars = /[*+\-<>=&|\^\/!\?]/
  133. function tokenBase(stream, state) {
  134. var ch = stream.next()
  135. curPunc = null
  136. if (ch == '$' || ch == '?') {
  137. if (ch == '?' && stream.match(/\s/, false)) {
  138. return 'operator'
  139. }
  140. stream.match(
  141. /^[A-Za-z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][A-Za-z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*/
  142. )
  143. return 'variable-2'
  144. } else if (ch == '<' && !stream.match(/^[\s\u00a0=]/, false)) {
  145. stream.match(/^[^\s\u00a0>]*>?/)
  146. return 'atom'
  147. } else if (ch == '"' || ch == "'") {
  148. state.tokenize = tokenLiteral(ch)
  149. return state.tokenize(stream, state)
  150. } else if (/[{}\(\),\.;\[\]]/.test(ch)) {
  151. curPunc = ch
  152. return 'bracket'
  153. } else if (ch == '#') {
  154. stream.skipToEnd()
  155. return 'comment'
  156. } else if (ch === '^') {
  157. ch = stream.peek()
  158. if (ch === '^') stream.eat('^')
  159. else stream.eatWhile(operatorChars)
  160. return 'operator'
  161. } else if (operatorChars.test(ch)) {
  162. stream.eatWhile(operatorChars)
  163. return 'operator'
  164. } else if (ch == ':') {
  165. eatPnLocal(stream)
  166. return 'atom'
  167. } else if (ch == '@') {
  168. stream.eatWhile(/[a-z\d\-]/i)
  169. return 'meta'
  170. } else {
  171. stream.eatWhile(/[_\w\d]/)
  172. if (stream.eat(':')) {
  173. eatPnLocal(stream)
  174. return 'atom'
  175. }
  176. var word = stream.current()
  177. if (ops.test(word)) return 'builtin'
  178. else if (keywords.test(word)) return 'keyword'
  179. else return 'variable'
  180. }
  181. }
  182. function eatPnLocal(stream) {
  183. stream.match(/(\.(?=[\w_\-\\%])|[:\w_-]|\\[-\\_~.!$&'()*+,;=/?#@%]|%[a-f\d][a-f\d])+/i)
  184. }
  185. function tokenLiteral(quote) {
  186. return function (stream, state) {
  187. var escaped = false,
  188. ch
  189. while ((ch = stream.next()) != null) {
  190. if (ch == quote && !escaped) {
  191. state.tokenize = tokenBase
  192. break
  193. }
  194. escaped = !escaped && ch == '\\'
  195. }
  196. return 'string'
  197. }
  198. }
  199. function pushContext(state, type, col) {
  200. state.context = { prev: state.context, indent: state.indent, col: col, type: type }
  201. }
  202. function popContext(state) {
  203. state.indent = state.context.indent
  204. state.context = state.context.prev
  205. }
  206. return {
  207. startState: function () {
  208. return { tokenize: tokenBase, context: null, indent: 0, col: 0 }
  209. },
  210. token: function (stream, state) {
  211. if (stream.sol()) {
  212. if (state.context && state.context.align == null) state.context.align = false
  213. state.indent = stream.indentation()
  214. }
  215. if (stream.eatSpace()) return null
  216. var style = state.tokenize(stream, state)
  217. if (style != 'comment' && state.context && state.context.align == null && state.context.type != 'pattern') {
  218. state.context.align = true
  219. }
  220. if (curPunc == '(') pushContext(state, ')', stream.column())
  221. else if (curPunc == '[') pushContext(state, ']', stream.column())
  222. else if (curPunc == '{') pushContext(state, '}', stream.column())
  223. else if (/[\]\}\)]/.test(curPunc)) {
  224. while (state.context && state.context.type == 'pattern') popContext(state)
  225. if (state.context && curPunc == state.context.type) {
  226. popContext(state)
  227. if (curPunc == '}' && state.context && state.context.type == 'pattern') popContext(state)
  228. }
  229. } else if (curPunc == '.' && state.context && state.context.type == 'pattern') popContext(state)
  230. else if (/atom|string|variable/.test(style) && state.context) {
  231. if (/[\}\]]/.test(state.context.type)) pushContext(state, 'pattern', stream.column())
  232. else if (state.context.type == 'pattern' && !state.context.align) {
  233. state.context.align = true
  234. state.context.col = stream.column()
  235. }
  236. }
  237. return style
  238. },
  239. indent: function (state, textAfter) {
  240. var firstChar = textAfter && textAfter.charAt(0)
  241. var context = state.context
  242. if (/[\]\}]/.test(firstChar)) while (context && context.type == 'pattern') context = context.prev
  243. var closing = context && firstChar == context.type
  244. if (!context) return 0
  245. else if (context.type == 'pattern') return context.col
  246. else if (context.align) return context.col + (closing ? 0 : 1)
  247. else return context.indent + (closing ? 0 : indentUnit)
  248. },
  249. lineComment: '#',
  250. }
  251. })
  252. CodeMirror.defineMIME('application/sparql-query', 'sparql')
  253. })