构建特定领域的编程语言:Irony框架介绍

Irony是一个强大的框架,它允许开发者在C#中创建全新的编程语言。通过定义语法规则并提供新关键字、函数的实现,可以轻松构建出满足特定需求的编程语言。本文将介绍如何使用Irony框架实现与WWW服务器的交互,包括发送GET和POST请求、匹配响应内容等功能。

Irony框架概述

Irony框架的核心组件包括扫描器、解析器和解释器,它们都是用C#编写的。开发者需要定义语言的语法规则(同样使用C#),并提供新关键字和函数的实现。CodeProject网站上有一些入门文章,例如《编写第一个领域特定语言》、《JSBasic - 一个BASIC到JavaScript的编译器》、《编写第一个Visual Studio语言服务》以及《Irony - .NET编译器构建工具包》等,这些文章展示了Irony的一些可能性。

语法定义

为了实现基本的WWW操作,需要定义以下语法规则:

program ::= * stmt ::= getStmt | postStmt | matchStmt | caseStmt | gotoStmt | labelStmt | assignmentStmt | expr

其中,最重要的语法元素是"get"、"post"和"match"语句。"get"和"post"语句允许向Web服务器发送请求(GET或POST),并将结果存储在变量中。例如:

get "http://www.google.com/" into @variable log(@variable) end

"log"是WWW DSL中的一个函数,它允许将信息存储到文件或控制台中。变量不需要显式声明,地址也可以来自变量:

@addr = "http://www.google.com/" get @addr into @variable log(@variable) end

错误处理也是可能的:

@addr = "http://www.google.com/" get @addr into @variable log(@variable) :error log("Error appear!") end

同样,可以向服务器发送POST请求(例如,登录Gmail):

@addr = "https://www.google.com/accounts/ClientLogin" post @addr referer = "https://www.google.com/accounts/ClientLogin" postdata "accountType"="GOOGLE" "Email"="account@gmail.com" "Passwd"="putpasswordhere" "service"="mail" "source"="Pol-WWWDSL-1.0" end into @variable log(@variable) :error log("Error appear!") end

从服务器获得响应后,可以使用"match"语句进行处理。例如:

@addr = "https://www.google.com/accounts/ClientLogin" post @addr referer = "https://www.google.com/accounts/ClientLogin" postdata "accountType"="GOOGLE" "Email"="account@gmail.com" "Passwd"="putpasswordhere" "service"="mail" "source"="Pol-WWWDSL-1.0" end into @variable log(@variable) match @variable using >>> SID=(?[^\\s]+)\\s+LSID=(?[^\\s]+)\\s+Auth=(?[^\\s]+) >>> log(@sid) log(@lsid) log(@auth) :error log("Match failed :(") end :error log("Error appear!") end

当有多个选择时,"switch"语句比"match"更好,例如:

get "http://www.google.pl/search?q=Irony" into @variable switch @variable case >>> Wikipedia, the free encyclopedia >>> log("Wikipedia result") end case >>> definition | Dictionary.com >>> log("Dictionary result, no wikipedia result") end default log("Other results, no wikipedia nor dictionary result") end end

最后一个语法元素是"goto"跳转。这种"丑陋"的构造在简单的脚本中非常方便,例如:

:restart get "http://www.google.pl/search?q=Irony" into @variable switch @variable case >>> Wikipedia, the free encyclopedia >>> log("Wikipedia result") goto restart end default log("Other results, no wikipedia result") end end

在这个项目中使用的Irony版本缺少"goto"实现。因此,准备了一个简单的解决方案来提供这个功能。

它工作如下:

protected override void DoEvaluate(EvaluationContext context) { throw new GotoJumpException(labelNode_); }

这允许从调用堆栈返回。然后,执行重新开始,但从跳转目的地标签的适当点开始。

while (nodes != null) { try { foreach (var node in nodes) { node.Evaluate(evalContext); } nodes = null; } catch (GotoJumpException e) { nodes = m_gotoNodes[e.LabelId.ValueString]; } }

这些执行点在脚本执行之前准备好。语法树在标签点被修剪,得到的分支存储在m_gotoNodes字典中,标签名称作为键。

foreach (var labelStmt in labels) { var nodes = labelStmt.Parent.ChildNodes.Skip( labelStmt.Parent.ChildNodes.IndexOf(labelStmt)); var parent = labelStmt.Parent; while (parent.Parent != null) { var upper = parent.Parent; if ((upper.Term.Name == "stmt+") || (upper.Term.Name == "program")) { var upperNextNodes = upper.ChildNodes.Skip(upper.ChildNodes.IndexOf(parent) + 1); nodes = nodes.Concat(upperNextNodes); } parent = upper; } m_gotoNodes.Add(((Token)labelStmt.ChildNodes[0]).ValueString, nodes); }

函数

WWW DSL的"标准库"由四个函数组成。"wait"函数允许等待指定的秒数,例如:

get @addr into @variable match @variable using >>> you have to wait (?\d+) minutes >>> wait(int(@minutes)*60) end end

上述示例使用了额外的函数"int",它允许从字符串转换为整数。基本的数值操作(如上面的乘法)由Irony本身提供。

最后两个函数是"log"(在前面的示例中已经展示)和"download",这是一个允许从WWW服务器下载文件的函数。以下是一个使用WWW DSL创建的最简单的文件下载器示例:

download(@arg1, @arg2)

在这个示例中,有两个变量(@arg1和@arg2),它们等同于程序"main"函数中的"args"。@arg1是第一个,@arg2是第二个传递给WWW DSL脚本的参数。

Irony语法探索器

脚本准备好后,需要检查其正确性。Irony有一个非常好的应用程序来检查语法和脚本,即Irony语法探索器。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485