我需要解析一段看起来像这样的代码:
* Block
| Line 1
| Line 2
| ...
很容易做到:
block : head lines;
head : '*' line;
lines : lines '|' line
| '|' line
;
现在我想知道,如何添加嵌套块,例如:
* Block
| Line 1
| * Subblock
| | Line 1.1
| | ...
| Line 2
| ...
这可以表示为LALR
语法吗?
当然,我可以解析顶级块,然后再次运行我的解析器来处理这些顶级块。但是,我只是在学习这个主题,所以对我来说避免这种方法很有意思。
答案 0 :(得分:3)
嵌套块语言不是无上下文[注2],因此无法使用LALR(k)解析器进行解析。
然而,嵌套的括号语言当然是无上下文的,通过替换词法扫描程序中的初始 | 序列,将输入转换为括号形式相对容易。转型很简单:
当 | 的初始序列比上一行长时,插入BEGIN_BLOCK
。 (初始序列必须正好一个 | ;否则可能是语法错误。)
当 | 的初始序列比前一行短时,插入足够的END_BLOCK
以使预期长度达到正确的值。
| 本身不会传递给解析器。
这与用于解析布局感知语言(如Python和Haskell)的INDENT
/ DEDENT
策略非常相似。主要的区别在于,我们不需要一堆缩进级别。
完成转换后,语法将如下所示:
content: /* empty */
| content line
| content block
block : head BEGIN_BLOCK content END_BLOCK
| head
head : '*' line
flex实现的大致轮廓如下:(见下面注1)。
%x INDENT CONTENT
%%
static int depth = 0, new_depth = 0;
/* Handle pending END_BLOCKs */
send_end:
if (new_depth < depth) {
--depth;
return END_BLOCK;
}
^"|"[[:blank:]]* { new_depth = 1; BEGIN(INDENT); }
^. { new_depth = 0; yyless(0); BEGIN(CONTENT);
goto send_end; }
^\n /* Ignore blank lines */
<INDENT>{
"|"[[:blank:]]* ++new_depth;
. { yyless(0); BEGIN(CONTENT);
if (new_depth > depth) {
++depth;
if (new_depth > depth) { /* Report syntax error */ }
return BEGIN_BLOCK;
} else goto send_end;
}
\n BEGIN(INITIAL); /* Maybe you care about this blank line? */
}
/* Put whatever you use here to lexically scan the lines */
<CONTENT>{
\n BEGIN(INITIAL);
}
并非所有人都对goto
感到满意,但它可以节省一些代码重复。状态变量(depth
和new_depth
)是本地static
变量的事实使得词法分析器不可重入且不可重新启动(在出错之后)。这只对玩具代码有用;对于任何真实的东西,你应该让词法扫描器重新进入并将状态变量放入extra
数据结构。
术语“无上下文”和“上下文敏感”是语法的技术描述,因此有点误导。基于单词似乎意味着什么的直觉通常是错误的。上下文敏感性的一个非常常见的来源是一种语言,其中有效性取决于产生相同令牌序列的相同非终端的两个不同派生。 (假设非终端可以导出多个令牌序列;否则,可以消除非终端。)
在普通编程语言中有很多这种上下文敏感的例子;通常,语法将允许这些构造,并且稍后将在一些语义分析阶段执行检查。这些包括要求声明标识符(IDENTIFIER
的两个派生产生相同的字符串)或要求使用正确数量的参数调用函数(这里,只需要推导的长度)非终端匹配,但这足以触发上下文敏感度。)
在这种情况下,要求是连续行中可能被称为bar-prefix
的两个实例产生相同的 | 字符串。在这种情况下,由于效果确实是语法上的,因此推迟到后来的语义分析会使解析失败。上下文敏感性的其他例子是“句法上的”还是“语义上的”是一场辩论,它产生了惊人的热量而没有对讨论产生太多的影响。
答案 1 :(得分:0)
这个答案实际上是一个评论(......我的评论难以阅读)
如果你编写一个明确的块尾令牌,事情会变得更加清晰
import urllib.request as urllib
from http.cookiejar import CookieJar
from os.path import isfile
from os.path import join as joinPath
from sys import exc_info
from traceback import print_tb
from urllib.parse import urlencode
# constant
APPLICATION_PATH = '/srv/path/'
ALERT_POINT_PATH = joinPath(APPLICATION_PATH, 'alert_contact')
URL_REQUEST_TIMEOUT = 42
SMS_BOX_URL = 'xx.xxxx.xxx.xxx'
def initWebConnection(): # init web connection
response = 0
initUrlLibResponse = initUrlLib() # init urllib
if initUrlLibResponse:
response = 1
return response
def initUrlLib(): # init urllib
response = 0
try:
cookieJar = CookieJar() # cookies
opener = urllib.build_opener(urllib.HTTPCookieProcessor(cookieJar))
urllib.install_opener(opener)
except Exception as e:
response = 1
# ex_type, ex, tb = exc_info()
return response
def urlRequest(url, data=None): # make url request
contentResponse = None
try:
request = None
if data:
dataRequest = urlencode(data)
dataRequest = dataRequest.encode('UTF-8')
request = urllib.Request(url, dataRequest)
else:
request = urllib.Request(url)
response = urllib.urlopen(url=request, timeout=URL_REQUEST_TIMEOUT) # make request
# get response
contentResponse = response.read()
except Exception as e:
contentResponse = None
# ex_type, ex, tb = exc_info()
return contentResponse
try:
evt.data = 'Some name'
# check production state
isInProduction = False
if evt.prodState == 1000:
isInProduction = True
if isInProduction:
initWebConnection()
# check alert point'
if isfile(ALERT_POINT_PATH):
alertContactContent = None
with open(ALERT_POINT_PATH, 'r') as alertContactFile:
alertContactContent = alertContactFile.read()
alertContactContent = alertContactContent.splitlines()
if alertContactContent:
evt.summary = '#[ DNS: ALERT ]# {}'.format(evt.summary)
for alertContactContentLine in alertContactContent:
webRequestData = dict(
## TO DO: set the url parameters appropriately
phone=alertContactContentLine,
message='NEW ALERT: {}'.format(evt.ipAddress),
)
webRequestResponse = urlRequest(SMS_BOX_URL, webRequestData)
else:
evt.summary = '#[ ERROR: SMS ALERT NO CONTACT ]# {}'.format(evt.summary)
except Exception as e:
ex_type, ex, tb = exc_info()
print('\n #[ERROR]#exception: {ex}\n'.format(ex=e))
print('\n #[ERROR]#exception traceback: {trace}\n'.format(trace=print_tb(tb)))
evt.summary = '#[ DNS:ERROR traceback in event message ]# {}'.format(evt.summary)
evt.message = '#[ DNS:ERROR ex_type:\n {} \nex: {} \n traceback:\n {} \n]# {}'.format(ex_type, ex,
print_tb(tb),
evt.message)
和语法成为
*Block{
|Line 1
*SubBlock{
| line 1.1
| line 1.2
}
|Line 2
|...
}