Solo  当前访客:0 开始使用

使用词法语法解析完成JSON.parse的功能


本节为前几节的一个练习, 主要是用我们已知的词法语法解析方法,对一个json字符串转换成JSON对象的过程

目标:
实现一个parseJson(jsonString)的方法

第一步

知道一个json包含了哪些token

{"a": [1,2,"k",{"b":true},{"c":false}], "d": null,"f": 1.2}

上述例子中,我们可以知道一个JSON字符串应该是这样的

  1. 键名由"包裹
  2. 键值常量包括由字符串数字浮点数truefalsenull
  3. 键值还可以是一个JSON对象或JSON数组
  4. 整体应是一个JSON对象或JSON数组

第二步 词法确认

确定词法, 简化如下

JSONString:
	" JsonStringChars "

JsonStringChar
	这边其实定义了哪些字符可用,咱们先支持所有的字符

JSONNumber:
	DecimalIntegerLiteral
        . DecimalDigits
JSONBoolean:
	true
        false

JSONNull:
	null

第三步 文法确认

确定文法:

JSONValue:
	JSONString
	JSONNumber
	JSONNull
	JSONBoolean
	JSONObject
	JSONArray

JSONObject:
	{}
	{ JSONMember }

JSONMember:
	JSONString: JSONValue
	JSONMember, JSONString: JSONValue

JSONArray:
	[]
	[JSONElements]

JSONElements:
	JSONValue
	JSONElements, JSONValue

代码编写

1. 词法解析

switch(state) {
                case this.DfaState.Initial:
                    state = this.toInitial(ch)
                    break;
                // 首先处理字符串, 以"开始
                case this.DfaState.String:
                    if(ch === '"') {
                        chIndex++;
                        state = this.toInitial(code.charAt(chIndex))
                    } else if(Utils.isAlpha(ch) || Utils.isNum(ch)) {
                        state = this.switchState(this.DfaState.String, ch)
                    } else {
                        throw Error(`String类型中不支持该字符: ${ch}`)
                    }
                    break;
                case this.DfaState.Null_1:
                    if(ch === 'u') {
                        state = this.switchState(this.DfaState.Null_2, ch)
                    } else {
                        throw Error(`您是想输入null吗? `)
                    }
                    break;
                case this.DfaState.Null_2:
                    if(ch === 'l') {
                        state = this.switchState(this.DfaState.Null_3, ch)
                    } else {
                        throw Error(`您是想输入null吗? `)
                    }
                    break;
                case this.DfaState.Null_3:
                    if(ch === 'l') {
                        state = this.switchState(this.DfaState.Null, ch)
                    } else {
                        throw Error(`您是想输入null吗? `)
                    }
                    break;
                case this.DfaState.Null:
                    state = this.toInitial(ch)
                    break;
                case this.DfaState.True_1:
                    if(ch === 'r') {
                        state = this.switchState(this.DfaState.True_2, ch)
                    } else {
                        throw Error(`您是想输入true吗? `)
                    }
                    break;
                case this.DfaState.True_2:
                    if(ch === 'u') {
                        state = this.switchState(this.DfaState.True_3, ch)
                    } else {
                        throw Error(`您是想输入true吗? `)
                    }
                    break;
                case this.DfaState.True_3:
                    if(ch === 'e') {
                        state = this.switchState(this.DfaState.True, ch)
                    } else {
                        throw Error(`您是想输入true吗? `)
                    }
                    break;
                case this.DfaState.True:
                    state = this.toInitial(ch)
                    break;
                case this.DfaState.False_1:
                    if(ch === 'a') {
                        state = this.switchState(this.DfaState.False_2, ch)
                    } else {
                        throw Error(`您是想输入false吗? `)
                    }
                    break;
                case this.DfaState.False_2:
                    if(ch === 'l') {
                        state = this.switchState(this.DfaState.False_3, ch)
                    } else {
                        throw Error(`您是想输入false吗? `)
                    }
                    break;
                case this.DfaState.False_3:
                    if(ch === 's') {
                        state = this.switchState(this.DfaState.False_4, ch)
                    } else {
                        throw Error(`您是想输入false吗? `)
                    }
                    break;
                case this.DfaState.False_4:
                    if(ch === 'e') {
                        state = this.switchState(this.DfaState.False, ch)
                    } else {
                        throw Error(`您是想输入false吗? `)
                    }
                    break;
                case this.DfaState.False:
                    state = this.toInitial(ch)
                    break;
                case this.DfaState.Num:
                    if(Utils.isNum(ch) || ch === "."){
                        state = this.switchState(this.DfaState.Num, ch)
                    } else if(!Utils.isNum(ch)) {
                        state = this.toInitial(ch)
                    }
                    break;
                case this.DfaState.LeftParen:
                case this.DfaState.RightParen:
                case this.DfaState.LeftBrace:
                case this.DfaState.RightBrace:
                case this.DfaState.LeftBracket:
                case this.DfaState.RightBracket:
                case this.DfaState.SemiColon:
                case this.DfaState.Comma:
                case this.DfaState.Colon:
                case this.DfaState.Dot:
                    state = this.toInitial(ch)
                    break;
                default:

            }
            chIndex++;

        }

我们按词法描述实现了上述代码, 从源代码中解析出了相关token, 同时对一些常见的错误做出提示

2. 实现语法解析部分

  1. JSONValue部分
    我们将在这部分实现对文法中JSONValue部分的解析
jsonValue() {
        
        let nextToken = this.lexer.tokenPeek()
        let node = null;
        if(nextToken) {
            if(nextToken.type === this.lexer.DfaState.Num || nextToken.type === this.lexer.DfaState.String || nextToken.type === this.lexer.DfaState.Null || nextToken.type === this.lexer.DfaState.True || nextToken.type === this.lexer.DfaState.False) {
                nextToken = this.lexer.tokenRead();
                node = new JsonValueAstNode(nextToken.type, nextToken.value)
            } else if(nextToken.type === this.lexer.DfaState.LeftBrace){
                node = this.jsonObject()
            } else if(nextToken.type === this.lexer.DfaState.LeftBracket){
                node = this.jsonArray()
            }
        }

        return node;
        
    }
  1. 实现JSONObject
jsonObject() {
        let nextToken = this.lexer.tokenPeek()
        let node = null;
        if(nextToken) {
            if(nextToken.type === this.lexer.DfaState.LeftBrace) {
                // 
                this.lexer.tokenRead();
                node = new JsonObjectAstNode("jsonObject", '');
                while(nextToken.type != this.lexer.DfaState.RightBrace){

                    let child = this.jsonMember()
                    if(child) {
                        node.addChild(child)
                    }
                    nextToken = this.lexer.tokenPeek()
                    if(nextToken.type == this.lexer.DfaState.Comma) {
                        this.lexer.tokenRead();
                        nextToken = this.lexer.tokenPeek()
                    }
                }
                
                if(nextToken.type === this.lexer.DfaState.RightBrace) {
                    this.lexer.tokenRead();
                }
            }
        }


        return node;
    }

    jsonMember() {
        let child = this.jsonValue();
        let node = child;
        let nextToken = this.lexer.tokenPeek()
        if(child && nextToken) {
            if(nextToken.type === this.lexer.DfaState.Colon) {
                // 
                this.lexer.tokenRead()
                node = new JsonMemberAstNode("jsonMember", "")
                node.addLeftChild(child)
                let child2 = this.jsonValue()
                if(child2) {
                    node.addRightChild(child2)
                }
                nextToken = this.lexer.tokenPeek()
            }
        }
        return node;
    }
  1. 实现JSONArray的部分
jsonArray() {
        let nextToken = this.lexer.tokenPeek()
        let node = null;
        if(nextToken) {
            if(nextToken.type === this.lexer.DfaState.LeftBracket) {
                this.lexer.tokenRead();
                node = new JsonArrayAstNode("jsonArray", '');
                while(nextToken.type != this.lexer.DfaState.RightBracket){
                    //

                    let child = this.jsonValue()
                    if(child) {
                        node.addChild(child)
                    }

                    nextToken = this.lexer.tokenPeek()
                    if(nextToken.type == this.lexer.DfaState.Comma) {
                        this.lexer.tokenRead();
                        nextToken = this.lexer.tokenPeek()
                    }
                }

                if(nextToken.type === this.lexer.DfaState.RightBracket) {
                    this.lexer.tokenRead();
                }
            } 
        }
        
        return node;
    }
  1. 解析入口
 jsonParse() {
        let node = this.jsonArray()
        if(!node) {
            node = this.jsonObject();
        }
        return node
    }

测试

  1. 测试parse
 let obj = {a:1, b: "1", c:[1,3.3, {a:1}]}
    let synxtax = new SyntaxForJson(JSON.stringify(obj))

    console.log("====源字符串:",JSON.stringify(obj),"===parse结果",synxtax.jsonParse().getValue())

结果

====源字符串: {"a":1,"b":"1","c":[1,3.3,{"a":1}]} ===parse结果 { a: 1, b: '1', c: [ 1, 3.3, { a: 1 } ] }
  1. 测试错误提示
  let lexer2 = new LexerForJson('{"a" : tru}')
    lexer2.dfaParse()
    console.log(lexer2.tokens)

结果

Error: 您是想输入true吗? 
    at LexerForJson.dfaParse (/Users/yixin/Desktop/my/projects/learn/demo_json/LexerForJson.js:220:31)
    at main (/Users/yixin/Desktop/my/projects/learn/demo_json/LexerForJson.js:302:12)


标题:使用词法语法解析完成JSON.parse的功能
作者:hugh0524
地址:https://newblog.uproject.cn/articles/2020/02/28/1582898914361.html

, , , 0 2