摘要本文旨在简要介绍如何在Go中构建LL(1)解析器,在本例中用于解析SQL查询。为简单起见,我们将处理子选择、函数、复杂的嵌套表达式以及所有SQL风格都支持的其他特性。这些属性与我们将使用的策略密切相关。1分钟理论解析器由两部分组成:词法分析:又名“Tokeniser”语法分析:AST创建词法分析让我们用一个例子来定义它。“标记化”以下查询:SELECTid,nameFROM'users.csv'表示提取构成此查询的“标记”。tokeniser的结果是这样的:[]string{"SELECT","id",",","name","FROM","'users.csv'"}语法分析这部分实际上是我们看的地方在tokens处,确保它们有意义并解析它们以构建某种结构,该结构以一种对将要使用它的应用程序更方便的方式表示查询(例如,用于执行查询,用颜色突出显示)。经过这一步,我们会得到这样的结果:query{Type:"Select",TableName:"users.csv",Fields:["id","name"],}解析失败的原因有很多,So同时执行这两个步骤可能会很方便,如果发生错误则立即停止。策略我们将这样定义一个解析器:typeparserstruct{sqlstring//Thequerytoparseiint//Wherewearnthequeryqueryquery.Query//我们将构建的“querystruct”stepstep//这是什么?继续阅读...}//Mainfunctionthatreturnsthe"querystruct"oranerrorfunc(p*parser)Parse()(query.Query,error){}//返回sthenexttokentoparsefunc(p*parser)peek()(string){}//sameaspeek(),butadvancingour"i"indexfunc(p*parser)pop()(string){}直观上,我们做的第一件事是“peek()***tokens”。在基本的SQL语法中,只有几个有效的初始标记:SELECT、UPDATE、DELETE等;其他一切都是错误的。像这样的代码:switchstrings.ToUpper(parser.peek()){case"SELECT":parser.query.type="SELECT"//startbuildingthe"querystruct"parser.pop()//TODOcontinuewithSELECTqueryparsing...case"UPDATE"://TODOhandleUPDATE//TODOothercases...default:returnparser.query,fmt.Errorf("invalidquerytype")}我们基本上可以填入TODO让它跑起来!然而,精明的读者会注意到解析整个SELECT查询的代码很快就会变得混乱,而且我们有许多类型的查询需要解析。所以我们需要一些结构。有限状态机(FSM)是一个非常有趣的话题,但我们不在这里讨论它,所以我们不会深入探讨。让我们专注于我们需要的东西。在我们的解析过程中,在任何给定的点(与其说是“点”,不如说是“节点”),只有少数几个token是有效的,找到这些token后,我们将进入新的节点,不同的token是有效的,依此类推,直到完成查询的解析。我们可以将这些节点关系可视化为一个有向图:点转换可以用一个更简单的表来定义,但是:我们可以直接将这个表转换成一个非常大的switch语句。我们将使用我们之前定义的parser.step属性:func(p*parser)Parse()(query.Query,error){parser.step=stepType//initialstepforparser.i
