Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

create-tokenizer.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. 'use strict'
  2. var assert = require('assert')
  3. var createDebug = require('debug')
  4. var assign = require('../constant/assign.js')
  5. var codes = require('../character/codes.js')
  6. var markdownLineEnding = require('../character/markdown-line-ending.js')
  7. var chunkedPush = require('./chunked-push.js')
  8. var chunkedSplice = require('./chunked-splice.js')
  9. var miniflat = require('./miniflat.js')
  10. var resolveAll = require('./resolve-all.js')
  11. var serializeChunks = require('./serialize-chunks.js')
  12. var shallow = require('./shallow.js')
  13. var sliceChunks = require('./slice-chunks.js')
  14. function _interopDefaultLegacy(e) {
  15. return e && typeof e === 'object' && 'default' in e ? e : {default: e}
  16. }
  17. var assert__default = /*#__PURE__*/ _interopDefaultLegacy(assert)
  18. var createDebug__default = /*#__PURE__*/ _interopDefaultLegacy(createDebug)
  19. var debug = createDebug__default['default']('micromark')
  20. // Create a tokenizer.
  21. // Tokenizers deal with one type of data (e.g., containers, flow, text).
  22. // The parser is the object dealing with it all.
  23. // `initialize` works like other constructs, except that only its `tokenize`
  24. // function is used, in which case it doesn’t receive an `ok` or `nok`.
  25. // `from` can be given to set the point before the first character, although
  26. // when further lines are indented, they must be set with `defineSkip`.
  27. function createTokenizer(parser, initialize, from) {
  28. var point = from ? shallow(from) : {line: 1, column: 1, offset: 0}
  29. var columnStart = {}
  30. var resolveAllConstructs = []
  31. var chunks = []
  32. var stack = []
  33. var consumed = true
  34. // Tools used for tokenizing.
  35. var effects = {
  36. consume: consume,
  37. enter: enter,
  38. exit: exit,
  39. attempt: constructFactory(onsuccessfulconstruct),
  40. check: constructFactory(onsuccessfulcheck),
  41. interrupt: constructFactory(onsuccessfulcheck, {interrupt: true}),
  42. lazy: constructFactory(onsuccessfulcheck, {lazy: true})
  43. }
  44. // State and tools for resolving and serializing.
  45. var context = {
  46. previous: codes.eof,
  47. events: [],
  48. parser: parser,
  49. sliceStream: sliceStream,
  50. sliceSerialize: sliceSerialize,
  51. now: now,
  52. defineSkip: skip,
  53. write: write
  54. }
  55. // The state function.
  56. var state = initialize.tokenize.call(context, effects)
  57. // Track which character we expect to be consumed, to catch bugs.
  58. var expectedCode
  59. if (initialize.resolveAll) {
  60. resolveAllConstructs.push(initialize)
  61. }
  62. // Store where we are in the input stream.
  63. point._index = 0
  64. point._bufferIndex = -1
  65. return context
  66. function write(slice) {
  67. chunks = chunkedPush(chunks, slice)
  68. main()
  69. // Exit if we’re not done, resolve might change stuff.
  70. if (chunks[chunks.length - 1] !== codes.eof) {
  71. return []
  72. }
  73. addResult(initialize, 0)
  74. // Otherwise, resolve, and exit.
  75. context.events = resolveAll(resolveAllConstructs, context.events, context)
  76. return context.events
  77. }
  78. //
  79. // Tools.
  80. //
  81. function sliceSerialize(token) {
  82. return serializeChunks(sliceStream(token))
  83. }
  84. function sliceStream(token) {
  85. return sliceChunks(chunks, token)
  86. }
  87. function now() {
  88. return shallow(point)
  89. }
  90. function skip(value) {
  91. columnStart[value.line] = value.column
  92. accountForPotentialSkip()
  93. debug('position: define skip: `%j`', point)
  94. }
  95. //
  96. // State management.
  97. //
  98. // Main loop (note that `_index` and `_bufferIndex` in `point` are modified by
  99. // `consume`).
  100. // Here is where we walk through the chunks, which either include strings of
  101. // several characters, or numerical character codes.
  102. // The reason to do this in a loop instead of a call is so the stack can
  103. // drain.
  104. function main() {
  105. var chunkIndex
  106. var chunk
  107. while (point._index < chunks.length) {
  108. chunk = chunks[point._index]
  109. // If we’re in a buffer chunk, loop through it.
  110. if (typeof chunk === 'string') {
  111. chunkIndex = point._index
  112. if (point._bufferIndex < 0) {
  113. point._bufferIndex = 0
  114. }
  115. while (
  116. point._index === chunkIndex &&
  117. point._bufferIndex < chunk.length
  118. ) {
  119. go(chunk.charCodeAt(point._bufferIndex))
  120. }
  121. } else {
  122. go(chunk)
  123. }
  124. }
  125. }
  126. // Deal with one code.
  127. function go(code) {
  128. assert__default['default'].equal(
  129. consumed,
  130. true,
  131. 'expected character to be consumed'
  132. )
  133. consumed = undefined
  134. debug('main: passing `%s` to %s', code, state.name)
  135. expectedCode = code
  136. state = state(code)
  137. }
  138. // Move a character forward.
  139. function consume(code) {
  140. assert__default['default'].equal(
  141. code,
  142. expectedCode,
  143. 'expected given code to equal expected code'
  144. )
  145. debug('consume: `%s`', code)
  146. assert__default['default'].equal(
  147. consumed,
  148. undefined,
  149. 'expected code to not have been consumed'
  150. )
  151. assert__default['default'](
  152. code === null
  153. ? !context.events.length ||
  154. context.events[context.events.length - 1][0] === 'exit'
  155. : context.events[context.events.length - 1][0] === 'enter',
  156. 'expected last token to be open'
  157. )
  158. if (markdownLineEnding(code)) {
  159. point.line++
  160. point.column = 1
  161. point.offset += code === codes.carriageReturnLineFeed ? 2 : 1
  162. accountForPotentialSkip()
  163. debug('position: after eol: `%j`', point)
  164. } else if (code !== codes.virtualSpace) {
  165. point.column++
  166. point.offset++
  167. }
  168. // Not in a string chunk.
  169. if (point._bufferIndex < 0) {
  170. point._index++
  171. } else {
  172. point._bufferIndex++
  173. // At end of string chunk.
  174. if (point._bufferIndex === chunks[point._index].length) {
  175. point._bufferIndex = -1
  176. point._index++
  177. }
  178. }
  179. // Expose the previous character.
  180. context.previous = code
  181. // Mark as consumed.
  182. consumed = true
  183. }
  184. // Start a token.
  185. function enter(type, fields) {
  186. var token = fields || {}
  187. token.type = type
  188. token.start = now()
  189. assert__default['default'].equal(
  190. typeof type,
  191. 'string',
  192. 'expected string type'
  193. )
  194. assert__default['default'].notEqual(
  195. type.length,
  196. 0,
  197. 'expected non-empty string'
  198. )
  199. debug('enter: `%s`', type)
  200. context.events.push(['enter', token, context])
  201. stack.push(token)
  202. return token
  203. }
  204. // Stop a token.
  205. function exit(type) {
  206. assert__default['default'].equal(
  207. typeof type,
  208. 'string',
  209. 'expected string type'
  210. )
  211. assert__default['default'].notEqual(
  212. type.length,
  213. 0,
  214. 'expected non-empty string'
  215. )
  216. assert__default['default'].notEqual(
  217. stack.length,
  218. 0,
  219. 'cannot close w/o open tokens'
  220. )
  221. var token = stack.pop()
  222. token.end = now()
  223. assert__default['default'].equal(
  224. type,
  225. token.type,
  226. 'expected exit token to match current token'
  227. )
  228. assert__default['default'](
  229. !(
  230. token.start._index === token.end._index &&
  231. token.start._bufferIndex === token.end._bufferIndex
  232. ),
  233. 'expected non-empty token (`' + type + '`)'
  234. )
  235. debug('exit: `%s`', token.type)
  236. context.events.push(['exit', token, context])
  237. return token
  238. }
  239. // Use results.
  240. function onsuccessfulconstruct(construct, info) {
  241. addResult(construct, info.from)
  242. }
  243. // Discard results.
  244. function onsuccessfulcheck(construct, info) {
  245. info.restore()
  246. }
  247. // Factory to attempt/check/interrupt.
  248. function constructFactory(onreturn, fields) {
  249. return hook
  250. // Handle either an object mapping codes to constructs, a list of
  251. // constructs, or a single construct.
  252. function hook(constructs, returnState, bogusState) {
  253. var listOfConstructs
  254. var constructIndex
  255. var currentConstruct
  256. var info
  257. return constructs.tokenize || 'length' in constructs
  258. ? handleListOfConstructs(miniflat(constructs))
  259. : handleMapOfConstructs
  260. function handleMapOfConstructs(code) {
  261. if (code in constructs || codes.eof in constructs) {
  262. return handleListOfConstructs(
  263. constructs.null
  264. ? /* c8 ignore next */
  265. miniflat(constructs[code]).concat(miniflat(constructs.null))
  266. : constructs[code]
  267. )(code)
  268. }
  269. return bogusState(code)
  270. }
  271. function handleListOfConstructs(list) {
  272. listOfConstructs = list
  273. constructIndex = 0
  274. return handleConstruct(list[constructIndex])
  275. }
  276. function handleConstruct(construct) {
  277. return start
  278. function start(code) {
  279. // To do: not nede to store if there is no bogus state, probably?
  280. // Currently doesn’t work because `inspect` in document does a check
  281. // w/o a bogus, which doesn’t make sense. But it does seem to help perf
  282. // by not storing.
  283. info = store()
  284. currentConstruct = construct
  285. if (!construct.partial) {
  286. context.currentConstruct = construct
  287. }
  288. if (
  289. construct.name &&
  290. context.parser.constructs.disable.null.indexOf(construct.name) > -1
  291. ) {
  292. return nok(code)
  293. }
  294. return construct.tokenize.call(
  295. fields ? assign({}, context, fields) : context,
  296. effects,
  297. ok,
  298. nok
  299. )(code)
  300. }
  301. }
  302. function ok(code) {
  303. assert__default['default'].equal(code, expectedCode, 'expected code')
  304. consumed = true
  305. onreturn(currentConstruct, info)
  306. return returnState
  307. }
  308. function nok(code) {
  309. assert__default['default'].equal(code, expectedCode, 'expected code')
  310. consumed = true
  311. info.restore()
  312. if (++constructIndex < listOfConstructs.length) {
  313. return handleConstruct(listOfConstructs[constructIndex])
  314. }
  315. return bogusState
  316. }
  317. }
  318. }
  319. function addResult(construct, from) {
  320. if (construct.resolveAll && resolveAllConstructs.indexOf(construct) < 0) {
  321. resolveAllConstructs.push(construct)
  322. }
  323. if (construct.resolve) {
  324. chunkedSplice(
  325. context.events,
  326. from,
  327. context.events.length - from,
  328. construct.resolve(context.events.slice(from), context)
  329. )
  330. }
  331. if (construct.resolveTo) {
  332. context.events = construct.resolveTo(context.events, context)
  333. }
  334. assert__default['default'](
  335. construct.partial ||
  336. !context.events.length ||
  337. context.events[context.events.length - 1][0] === 'exit',
  338. 'expected last token to end'
  339. )
  340. }
  341. function store() {
  342. var startPoint = now()
  343. var startPrevious = context.previous
  344. var startCurrentConstruct = context.currentConstruct
  345. var startEventsIndex = context.events.length
  346. var startStack = Array.from(stack)
  347. return {restore: restore, from: startEventsIndex}
  348. function restore() {
  349. point = startPoint
  350. context.previous = startPrevious
  351. context.currentConstruct = startCurrentConstruct
  352. context.events.length = startEventsIndex
  353. stack = startStack
  354. accountForPotentialSkip()
  355. debug('position: restore: `%j`', point)
  356. }
  357. }
  358. function accountForPotentialSkip() {
  359. if (point.line in columnStart && point.column < 2) {
  360. point.column = columnStart[point.line]
  361. point.offset += columnStart[point.line] - 1
  362. }
  363. }
  364. }
  365. module.exports = createTokenizer