UNPKG

2.91 kBJavaScriptView Raw
1'use strict'
2const ParserEND = Symbol('END')
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 parse (str) {
35 /* istanbul ignore next */
36 if (str.length === 0) return
37
38 this._buf = String(str)
39 this.ii = 0
40 this.char = this._buf[0]
41 let getNext
42 do {
43 getNext = this.runOne()
44 } while (getNext === false || this.nextChar())
45 this._buf = null
46 }
47 nextChar () {
48 if (this.char === '\n') {
49 ++this.line
50 this.col = -1
51 }
52 ++this.ii
53 this.char = this._buf[this.ii]
54 ++this.pos
55 ++this.col
56 return this.haveBuffer()
57 }
58 haveBuffer () {
59 return this.ii < this._buf.length
60 }
61 runOne () {
62 return this.state.parser.call(this, this.state.returned)
63 }
64 finish () {
65 this.char = ParserEND
66 let last
67 do {
68 last = this.state.parser
69 this.runOne()
70 } while (this.state.parser !== last)
71
72 this.ctx = null
73 this.state = null
74 this._buf = null
75
76 return this.obj
77 }
78 next (fn) {
79 /* istanbul ignore next */
80 if (typeof fn !== 'function') throw new ParserError('Tried to set state to non-existent state: ' + JSON.stringify(fn))
81 this.state.parser = fn
82 }
83 goto (fn) {
84 this.next(fn)
85 return this.runOne()
86 }
87 call (fn, returnWith) {
88 if (returnWith) this.next(returnWith)
89 this.stack.push(this.state)
90 this.state = new State(fn)
91 }
92 callNow (fn, returnWith) {
93 this.call(fn, returnWith)
94 return this.runOne()
95 }
96 return (value) {
97 /* istanbul ignore next */
98 if (!this.stack.length) throw this.error(new ParserError('Stack underflow'))
99 if (value === undefined) value = this.state.buf
100 this.state = this.stack.pop()
101 this.state.returned = value
102 }
103 returnNow (value) {
104 this.return(value)
105 return this.runOne()
106 }
107 consume () {
108 /* istanbul ignore next */
109 if (this.char === ParserEND) throw this.error(new ParserError('Unexpected end-of-buffer'))
110 this.state.buf += this.char
111 }
112 error (err) {
113 err.line = this.line
114 err.col = this.col
115 err.pos = this.pos
116 return err
117 }
118 /* istanbul ignore next */
119 parseStart () {
120 throw new ParserError('Must declare a parseStart method')
121 }
122}
123Parser.END = ParserEND
124Parser.Error = ParserError
125module.exports = Parser