UNPKG

2.94 kBJavaScriptView Raw
1'use strict'
2const ParserEND = 0x110000
3class ParserError extends Error {
4 /* istanbul ignore next */
5 constructor (msg, filename, linenumber) {
6 super('[ParserError] ' + msg, filename, linenumber)
7 this.code = 'ParserError'
8 if (Error.captureStackTrace) Error.captureStackTrace(this, ParserError)
9 }
10}
11class State {
12 constructor (parser) {
13 this.parser = parser
14 this.buf = ''
15 this.returned = null
16 this.result = null
17 this.resultTable = null
18 this.resultArr = null
19 }
20}
21class Parser {
22 constructor () {
23 this.pos = 0
24 this.col = 0
25 this.line = 0
26 this.obj = {}
27 this.ctx = this.obj
28 this.stack = []
29 this._buf = ''
30 this.char = null
31 this.ii = 0
32 this.state = new State(this.parseStart)
33 }
34
35 parse (str) {
36 /* istanbul ignore next */
37 if (str.length === 0 || str.length == null) return
38
39 this._buf = String(str)
40 this.ii = -1
41 this.char = -1
42 let getNext
43 while (getNext === false || this.nextChar()) {
44 getNext = this.runOne()
45 }
46 this._buf = null
47 }
48 nextChar () {
49 if (this.char === 0x0A) {
50 ++this.line
51 this.col = -1
52 }
53 ++this.ii
54 this.char = this._buf.codePointAt(this.ii)
55 ++this.pos
56 ++this.col
57 return this.haveBuffer()
58 }
59 haveBuffer () {
60 return this.ii < this._buf.length
61 }
62 runOne () {
63 return this.state.parser.call(this, this.state.returned)
64 }
65 finish () {
66 this.char = ParserEND
67 let last
68 do {
69 last = this.state.parser
70 this.runOne()
71 } while (this.state.parser !== last)
72
73 this.ctx = null
74 this.state = null
75 this._buf = null
76
77 return this.obj
78 }
79 next (fn) {
80 /* istanbul ignore next */
81 if (typeof fn !== 'function') throw new ParserError('Tried to set state to non-existent state: ' + JSON.stringify(fn))
82 this.state.parser = fn
83 }
84 goto (fn) {
85 this.next(fn)
86 return this.runOne()
87 }
88 call (fn, returnWith) {
89 if (returnWith) this.next(returnWith)
90 this.stack.push(this.state)
91 this.state = new State(fn)
92 }
93 callNow (fn, returnWith) {
94 this.call(fn, returnWith)
95 return this.runOne()
96 }
97 return (value) {
98 /* istanbul ignore next */
99 if (!this.stack.length) throw this.error(new ParserError('Stack underflow'))
100 if (value === undefined) value = this.state.buf
101 this.state = this.stack.pop()
102 this.state.returned = value
103 }
104 returnNow (value) {
105 this.return(value)
106 return this.runOne()
107 }
108 consume () {
109 /* istanbul ignore next */
110 if (this.char === ParserEND) throw this.error(new ParserError('Unexpected end-of-buffer'))
111 this.state.buf += this._buf[this.ii]
112 }
113 error (err) {
114 err.line = this.line
115 err.col = this.col
116 err.pos = this.pos
117 return err
118 }
119 /* istanbul ignore next */
120 parseStart () {
121 throw new ParserError('Must declare a parseStart method')
122 }
123}
124Parser.END = ParserEND
125Parser.Error = ParserError
126module.exports = Parser