C#实现自己的Json解析器(LALR(1)+miniDFA)
Json是一个用处广泛、文法简单的数据格式。本文介绍如何用bitParser(拥有自己的解析器(C#实现LALR(1)语法解析器和miniDFA词法分析器的生成器)迅速实现一个简单高效的Json解析器。
读者可在(https://gitee.com/bitzhuwei/bitParser-demos/tree/master/bitzhuwei.JsonFormat.TestConsole)查看、下载完整代码。
Json格式的文法
我们可以在(https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf )找到Json格式的详细说明。据此,可得如下文法:- // Json grammar according to ECMA-404 2nd Edition / December 2017
- Json = Object | Array ;
- Object = '{' '}' | '{' Members '}' ;
- Array = '[' ']' | '[' Elements ']' ;
- Members = Members ',' Member | Member ;
- Elements = Elements ',' Element | Element ;
- Member = 'string' ':' Value ;
- Element = Value ;
- Value = 'null' | 'true' | 'false' | 'number' | 'string'
- | Object | Array ;
- %%"([^"\\\u0000-\u001F]|\\["\\/bfnrt]|\\u[0-9A-Fa-f]{4})*"%% 'string'
- %%[-]?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?%% 'number'
复制代码 实际上这个文法是我用AI写出来后再整理成的。
此文法说明:
- 一个Json要么是一个Object,要么是一个Array。
- 一个Object包含0-多个键值对("key" : value),用{ }括起来。
- 一个Array包含0-多个value,用[ ]括起来。
- 一个value有如下几种类型:null、true、false、number、string、Object、Array。
其中:
null、true、false就是字面意思,因而可以省略不写。如果要在文法中显式地书写,就是这样:- %%null%% 'null'
- %%true%% 'true'
- %%false%% 'false'
复制代码 {、}、[、]、,、:也都是字面意思,因而可以省略不写。如果要在文法中显式地书写,就是这样:- %%\{%% '{'
- %%}%% '}'
- %%\[%% '['
- %%]%% ']'
- %%,%% ','
- %%:%% ':'
复制代码 number可由下图描述:
图上直观地说明了number这个token的正则表达式由4个依次排列的部分组成:- [-]? (0|[1-9][0-9]*) ([.][0-9]+)? ([eE][+-]?[0-9]+)?
复制代码 string可由下图描述:
图上直观地说明了string这个token的正则表达式是用"包裹起来的某些字符或转义字符:- " ( [^"\\\u0000-\u001F] | \\["\\/bfnrt] | \\u[0-9A-Fa-f]{4} )* "
- /*
- 实际含义为:
- 非"、非\、非控制字符(\u0000-\u001F)
- "、\\、\/、\b、\f、\n、\r、\t
- \uNNNN
- */
复制代码 Value = Object | Array;说明Json中的数据是可以嵌套的。
将此文法作为输入,提供给bitParser,就可以一键生成下述章节介绍的Json解析器代码和文档了。
生成的词法分析器
DFA
DFA文件夹下是依据确定的有限自动机原理生成的词法分析器的全部词法状态。
初始状态lexicalState0[code]using System;using bitzhuwei.Compiler;namespace bitzhuwei.JsonFormat { partial class CompilerJson { private static readonly Action lexicalState0 = static (context, c, wrap) => { if (false) { /* for simpler code generation purpose. */ } /* user-input condition code */ /* [1-9] */ else if (/* possible Vt : 'number' */ /* no possible signal */ /* [xxx] scope */ '1'/*'\u0031'(49)*/ |