Created
July 30, 2025 19:00
-
-
Save luoling8192/0abd8b5187557390e7f40c43a02cae95 to your computer and use it in GitHub Desktop.
JSON Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { describe, expect, it } from 'vitest' | |
| function jsonParse(json: string) { | |
| let currentIndex = 0 | |
| function token() { | |
| return json[currentIndex] | |
| } | |
| function nextToken() { | |
| currentIndex++ | |
| } | |
| function parseValue() { | |
| skipSpace() | |
| const token = json[currentIndex] | |
| switch (token) { | |
| case '{': | |
| return parseObject() | |
| case '"': | |
| return parseString() | |
| case '[': | |
| return parseArray() | |
| default: | |
| if (isNumber()) { | |
| return parseNumber() | |
| } | |
| return parseKeyword() | |
| } | |
| } | |
| function parseObject(): any { | |
| let obj = {} | |
| nextToken() | |
| skipSpace() | |
| if (token() === '}') { | |
| nextToken() | |
| return {} | |
| } | |
| skipSpace() | |
| while (currentIndex < json.length) { | |
| let key = '' | |
| if (token() === '"') | |
| key = parseString() | |
| skipSpace() | |
| if (token() === ':') | |
| nextToken() // next | |
| skipSpace() | |
| const value = parseValue() | |
| obj = Object.assign(obj, { [key]: value }) | |
| skipSpace() | |
| if (token() === '}') { | |
| nextToken() | |
| return obj | |
| } | |
| if (token() === ',') { | |
| nextToken() // next | |
| continue | |
| } | |
| throw new Error(`Unexpected token : ${token()}`) | |
| } | |
| } | |
| // [1, 2, 3] | |
| function parseArray(): any { | |
| nextToken() // next | |
| skipSpace() | |
| if (token() === ']') { | |
| nextToken() | |
| return [] | |
| } | |
| const array = [] | |
| while (currentIndex < json.length) { | |
| skipSpace() | |
| const value = parseValue() | |
| array.push(value) | |
| if (token() === ']') { | |
| nextToken() | |
| return array | |
| } | |
| if (token() === ',') { | |
| nextToken() | |
| continue | |
| } | |
| throw new Error(`Unexpected token ${token()}`) | |
| } | |
| } | |
| function parseString() { | |
| nextToken() | |
| let result = '' | |
| while (currentIndex < json.length) { | |
| const token = json[currentIndex++] | |
| if (token === '"') | |
| break | |
| result += token | |
| } | |
| return result | |
| } | |
| function isNumber() { | |
| const token = json[currentIndex] | |
| return (token >= '0' && token <= '9') || token === '-' | |
| } | |
| function skipSpace() { | |
| while (currentIndex < json.length | |
| && (json[currentIndex] === ' ' | |
| || json[currentIndex] === '\n' | |
| || json[currentIndex] === '\t')) { | |
| nextToken() | |
| } | |
| } | |
| function parseKeyword() { | |
| if (token() === 't' && json.slice(currentIndex, currentIndex + 4) === 'true') { | |
| currentIndex += 4 | |
| return true | |
| } | |
| if (token() === 'f' && json.slice(currentIndex, currentIndex + 5) === 'false') { | |
| currentIndex += 5 | |
| return false | |
| } | |
| if (token() === 'n' && json.slice(currentIndex, currentIndex + 4) === 'null') { | |
| currentIndex += 4 | |
| return null | |
| } | |
| throw new Error(`Unexpected token ${token()}`) | |
| } | |
| function parseNumber() { | |
| let result = '' | |
| while (currentIndex < json.length) { | |
| if (!isNumber()) | |
| return Number(result) | |
| result += token() | |
| nextToken() | |
| } | |
| } | |
| return parseValue() | |
| } | |
| // function expectJSON(json: string) { | |
| // expect(JSON.stringify(jsonParse(json))).toBe(json) | |
| // } | |
| function expectObject(obj: any) { | |
| const str = JSON.stringify(obj) | |
| const res = JSON.stringify(jsonParse(str)) | |
| expect(res).toBe(str) | |
| } | |
| describe('json parser', () => { | |
| it('should parse {}', () => { | |
| expectObject({}) | |
| }) | |
| it('should parse {"a":1}', () => { | |
| expectObject({ a: 1 }) | |
| }) | |
| it('should parse { "a": 1 }', () => { | |
| expect(JSON.stringify(jsonParse(`{ "a": 1 }`))).toBe(`{"a":1}`) | |
| }) | |
| it('should parse {"a":{}}', () => { | |
| expectObject({ a: {} }) | |
| }) | |
| it('should parse {"a":{"b":1}}', () => { | |
| expectObject({ a: { b: 1 } }) | |
| }) | |
| it('should parse {"a":{"b":1,"c":"hello"}}', () => { | |
| expectObject({ a: { b: 1, c: 'hello' } }) | |
| }) | |
| it('should parse array', () => { | |
| expectObject({ a: { b: 1, c: 'hello', d: [1, 2, 3] } }) | |
| }) | |
| it('should parse boolean and null', () => { | |
| expectObject({ a: true, b: false, c: null, d: [false, null, 1] }) | |
| }) | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment