File: Parser.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (355 lines) | stat: -rw-r--r-- 12,722 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
import WasmParser

internal struct Parser {
    var lexer: Lexer

    init(_ input: String) {
        self.lexer = Lexer(input: input)
    }

    init(_ lexer: Lexer) {
        self.lexer = lexer
    }

    func peek(_ kind: TokenKind? = nil) throws -> Token? {
        var lexer = lexer
        guard let token = try lexer.lex() else { return nil }
        if let kind {
            guard token.kind == kind else { return nil }
        }
        return token
    }

    func peekKeyword() throws -> String? {
        guard let token = try peek(.keyword) else {
            return nil
        }
        return token.text(from: lexer)
    }

    mutating func take(_ kind: TokenKind) throws -> Bool {
        guard try peek(kind) != nil else { return false }
        try consume()
        return true
    }

    mutating func takeKeyword(_ keyword: String) throws -> Bool {
        guard let token = try peek(.keyword), token.text(from: lexer) == keyword else {
            return false
        }
        try consume()
        return true
    }

    /// Consume a `(keyword` sequence, returning whether the tokens were consumed.
    mutating func takeParenBlockStart(_ keyword: String) throws -> Bool {
        let original = lexer
        guard try take(.leftParen), try takeKeyword(keyword) else {
            lexer = original
            return false
        }
        return true
    }

    mutating func takeUnsignedInt<IntegerType: UnsignedInteger & FixedWidthInteger>(_: IntegerType.Type = IntegerType.self) throws -> IntegerType? {
        guard let token = try peek() else { return nil }
        guard case let .integer(nil, pattern) = token.kind else {
            return nil
        }
        try consume()
        switch pattern {
        case .hexPattern(let pattern):
            guard let index = IntegerType(pattern, radix: 16) else {
                throw WatParserError("invalid index \(pattern)", location: token.location(in: lexer))
            }
            return index
        case .decimalPattern(let pattern):
            guard let index = IntegerType(pattern) else {
                throw WatParserError("invalid index \(pattern)", location: token.location(in: lexer))
            }
            return index
        }
    }

    mutating func takeSignedInt<IntegerType: FixedWidthInteger, UnsignedType: FixedWidthInteger & UnsignedInteger>(
        fromBitPattern: (UnsignedType) -> IntegerType
    ) throws -> IntegerType? {
        guard let token = try peek() else { return nil }
        guard case let .integer(sign, pattern) = token.kind else {
            return nil
        }
        try consume()
        let value: UnsignedType
        let makeError = { [lexer] in
            WatParserError("invalid literal \(token.text(from: lexer))", location: token.location(in: lexer))
        }
        switch pattern {
        case .hexPattern(let pattern):
            guard let index = UnsignedType(pattern, radix: 16) else { throw makeError() }
            value = index
        case .decimalPattern(let pattern):
            guard let index = UnsignedType(pattern) else { throw makeError() }
            value = index
        }
        switch sign {
        case .plus, nil: return fromBitPattern(value)
        case .minus:
            let casted = fromBitPattern(~value &+ 1)
            guard casted <= 0 else { throw makeError() }
            return casted
        }
    }

    mutating func takeStringBytes() throws -> [UInt8]? {
        guard let token = try peek(), case .string(let bytes) = token.kind else { return nil }
        try consume()
        return bytes
    }

    mutating func takeString() throws -> String? {
        guard let bytes = try takeStringBytes() else { return nil }
        return String(decoding: bytes, as: UTF8.self)
    }

    mutating func takeIndexOrId() throws -> IndexOrId? {
        let location = lexer.location()
        if let index: UInt32 = try takeUnsignedInt() {
            return .index(index, location)
        } else if let id = try takeId() {
            return .id(id, location)
        }
        return nil
    }

    @discardableResult
    mutating func expect(_ kind: TokenKind) throws -> Token {
        guard let token = try lexer.lex() else {
            throw WatParserError("expected \(kind)", location: lexer.location())
        }
        guard token.kind == kind else {
            throw WatParserError("expected \(kind)", location: token.location(in: lexer))
        }
        return token
    }

    @discardableResult
    mutating func expectKeyword(_ keyword: String? = nil) throws -> String {
        let token = try expect(.keyword)
        let text = token.text(from: lexer)
        if let keyword {
            guard text == keyword else {
                throw WatParserError("expected \(keyword)", location: token.location(in: lexer))
            }
        }
        return text
    }

    mutating func expectStringBytes() throws -> [UInt8] {
        guard let token = try lexer.lex() else {
            throw WatParserError("expected string", location: lexer.location())
        }
        guard case .string(let text) = token.kind else {
            throw WatParserError("expected string but got \(token.kind)", location: token.location(in: lexer))
        }
        return text
    }
    mutating func expectString() throws -> String {
        // TODO: Use SE-0405 once we can upgrade minimum-supported Swift version to 6.0
        let bytes = try expectStringBytes()
        return try bytes.withUnsafeBufferPointer {
            guard let value = String._tryFromUTF8($0) else {
                throw WatParserError("invalid UTF-8 string", location: lexer.location())
            }
            return value
        }
    }

    mutating func expectStringList() throws -> [UInt8] {
        var data: [UInt8] = []
        while try !take(.rightParen) {
            data += try expectStringBytes()
        }
        return data
    }

    mutating func expectUnsignedInt<IntegerType: UnsignedInteger & FixedWidthInteger>(_: IntegerType.Type = IntegerType.self) throws -> IntegerType {
        guard let value: IntegerType = try takeUnsignedInt() else {
            throw WatParserError("expected decimal index without sign", location: lexer.location())
        }
        return value
    }

    mutating func expectSignedInt<IntegerType: FixedWidthInteger, UnsignedType: FixedWidthInteger & UnsignedInteger>(
        fromBitPattern: (UnsignedType) -> IntegerType
    ) throws -> IntegerType {
        guard let value: IntegerType = try takeSignedInt(fromBitPattern: fromBitPattern) else {
            throw WatParserError("expected decimal index with sign", location: lexer.location())
        }
        return value
    }

    mutating func expectFloatingPoint<F: BinaryFloatingPoint & LosslessStringConvertible, BitPattern: FixedWidthInteger>(
        _: F.Type, toBitPattern: (F) -> BitPattern, isNaN: (BitPattern) -> Bool,
        buildBitPattern: (
            _ sign: FloatingPointSign,
            _ exponentBitPattern: UInt,
            _ significandBitPattern: UInt
        ) -> BitPattern
    ) throws -> BitPattern {
        let token = try consume()

        var infinityExponent: UInt {
            return 1 &<< UInt(F.exponentBitCount) - 1
        }

        let makeError = { [lexer] in
            WatParserError("invalid float literal \(token.text(from: lexer))", location: token.location(in: lexer))
        }
        let parse = { (pattern: String) throws -> F in
            // Swift's Float{32,64} initializer returns +/- infinity for too large/too small values.
            // We know that the given pattern will not be expected to be parsed as infinity,
            // so we can check if the parsing succeeded by checking if the returned value is infinite.
            guard let value = F(pattern), !value.isInfinite else { throw makeError() }
            return value
        }
        switch token.kind {
        case let .float(sign, pattern):
            let float: F
            switch pattern {
            case .decimalPattern(let pattern):
                float = try parse(pattern)
            case .hexPattern(let pattern):
                float = try parse("0x" + pattern)
            case .inf:
                float = .infinity
            case .nan(hexPattern: nil):
                float = .nan
            case .nan(let hexPattern?):
                guard let significandBitPattern = BitPattern(hexPattern, radix: 16) else { throw makeError() }
                let bitPattern = buildBitPattern(sign ?? .plus, infinityExponent, UInt(significandBitPattern))
                // Ensure that the given bit pattern is a NaN.
                guard isNaN(bitPattern) else { throw makeError() }
                return bitPattern
            }
            return toBitPattern(sign == .minus ? -float : float)
        case let .integer(sign, pattern):
            let float: F
            switch pattern {
            case .hexPattern(let pattern):
                float = try parse("0x" + pattern)
            case .decimalPattern(let pattern):
                float = try parse(pattern)
            }
            return toBitPattern(sign == .minus ? -float : float)
        default:
            throw WatParserError("expected float but got \(token.kind)", location: token.location(in: lexer))
        }
    }

    mutating func expectFloat32() throws -> IEEE754.Float32 {
        let bitPattern = try expectFloatingPoint(
            Float32.self, toBitPattern: \.bitPattern,
            isNaN: { Float32(bitPattern: $0).isNaN },
            buildBitPattern: {
                UInt32(
                    ($0 == .minus ? 1 : 0) << (Float32.exponentBitCount + Float32.significandBitCount)
                        + ($1 << Float32.significandBitCount) + $2
                )
            }
        )
        return IEEE754.Float32(bitPattern: bitPattern)
    }

    mutating func expectFloat64() throws -> IEEE754.Float64 {
        let bitPattern = try expectFloatingPoint(
            Float64.self, toBitPattern: \.bitPattern,
            isNaN: { Float64(bitPattern: $0).isNaN },
            buildBitPattern: {
                UInt64(
                    ($0 == .minus ? 1 : 0) << (Float64.exponentBitCount + Float64.significandBitCount)
                        + ($1 << Float64.significandBitCount) + $2
                )
            }
        )
        return IEEE754.Float64(bitPattern: bitPattern)
    }

    mutating func expectIndex() throws -> UInt32 { try expectUnsignedInt(UInt32.self) }

    mutating func expectParenBlockStart(_ keyword: String) throws {
        guard try takeParenBlockStart(keyword) else {
            throw WatParserError("expected \(keyword)", location: lexer.location())
        }
    }

    enum IndexOrId {
        case index(UInt32, Location)
        case id(Name, Location)
        var location: Location {
            switch self {
            case .index(_, let location), .id(_, let location):
                return location
            }
        }
    }

    mutating func expectIndexOrId() throws -> IndexOrId {
        guard let indexOrId = try takeIndexOrId() else {
            throw WatParserError("expected index or id", location: lexer.location())
        }
        return indexOrId
    }

    func isEndOfParen() throws -> Bool {
        guard let token = try peek() else { return true }
        return token.kind == .rightParen
    }

    @discardableResult
    mutating func consume() throws -> Token {
        guard let token = try lexer.lex() else {
            throw WatParserError("unexpected EOF", location: lexer.location())
        }
        return token
    }

    mutating func takeId() throws -> Name? {
        guard let token = try peek(.id) else { return nil }
        try consume()
        return Name(value: token.text(from: lexer), location: token.location(in: lexer))
    }

    mutating func skipParenBlock() throws {
        var depth = 1
        while depth > 0 {
            let token = try consume()
            switch token.kind {
            case .leftParen:
                depth += 1
            case .rightParen:
                depth -= 1
            default:
                break
            }
        }
    }
}

public struct WatParserError: Error, CustomStringConvertible {
    public let message: String
    public let location: Location?

    public var description: String {
        if let location {
            let (line, column) = location.computeLineAndColumn()
            return "\(line):\(column): \(message)"
        } else {
            return message
        }
    }

    init(_ message: String, location: Location?) {
        self.message = message
        self.location = location
    }
}