定从句文法简介(2004)

2020-08-16 02:37:04

本文档简要介绍了逻辑语法,作为以逻辑语法形式表示XSD模式的背景信息。

逻辑文法是用来表示在逻辑程序设计系统中编写的文法的。本文描述了定子句文法(DCGS)和定子句翻译文法(DCTGs),DCGS是大多数PROLOG系统的内置部分,DCTG采用了类似的形式。DCTG与[Knuth 1968]和后来的作者所描述的属性文法非常相似,在DCTG中处理复杂的或大量的属性值函数要容易得多。在DCTG中,DCTG与[Knuth 1968]和后来的作者所描述的属性文法非常相似,而且在DCTG中处理复杂的或大量的属性值函数要容易得多。

DCGS和DCTGS都可以看作是straightProlog的语法糖;在执行之前,这两个符号都被翻译成通常符号中的序言从句。

这一部分回顾了定从句语法的基础知识和它们的注释。我将以两个或三个版本介绍一个简单的语法,以说明DCG表示法的使用。

如果读者觉得这篇介绍不够详细,可以参考参考文献中列出的序言书。

下面是一小段英语的简单DCG,我称它为E1。它取自[König/Seiffert 1989]中的例子8.1,《语言学家序言:%语法E1》中的例子8.1,它是英语的一个小片段-->;NP,VP。句子是名词短语(NP)%加上动词短语enp-->;det,n.%名词短语是限定词加上名词-->;n.%...。或者只是一个名词。vp-->;v,np。动词短语是动词及其直接的%宾语,是npvp-->;v.%...。或者只用动词(表示不及物)。N-->;[Mary]。%';玛丽,##39;约翰##39;女性,##39;男性,#39;苹果;%都是名词。n-->;[John].N-->;[女性].N-->;[男性].N-->;[苹果].det-->;[the]。%';The';是一个限定词v-->;[Love]。%';Love';,';Eats';和';Sings';都是动词。v-->;[eats].v-->;[sings]。

这个语法产生了诸如“约翰爱玛丽”、“男人唱歌”和“女人吃苹果”这样的句子,还有一些不太可信的句子“苹果唱玛丽”。

当PROLOG系统查询包含此语法的文件时,语法规则使用差异列表自动转换为规范的PROLOG子句:?-LISTING([s,np,vp,n,det,v]).s(A,B):-np(A,C),VP(C,B).np(A,B):-det(A,C),n(C,B).np(A,B):-n(A,B).vp(A,B。B).vp(A,B):-v(A,B).N([Mary|A],A).N([John|A],A).N([女人|A],A).N([man|A],A).N([the|A],A).det([the|A],A).v([爱|A],A).v([吃|A],A).v([唱|A],A)

差异列表是这样的列表对,使得第二列表是第一列表的后缀(即,从某一点或另一列表到末尾相同),例如,对[a,b,c,d]和[c,d],它们一起表示列表[a,b],或者(在上面的终端规则中)[Love|A]和A,它们一起表示列表[Love]。基于PROLOG的解析器通常使用差异列表来表示输入;它们允许识别输入列表中的结构的PROLOG子句根据需要使用尽可能多的输入列表(对中的第一个列表),并将输入的其余部分(对中的第二个列表)传递给下一个子句。如果DCG谓词成功,其最后两个参数统一到差异列表对[a,b,c,d]和[c,d],则意味着语法规则识别列表[a,b]。

在s的子句中,我们可以看到解析器的一部分是如何在另一部分离开的地方继续工作的。然后,谓词VP尝试在列表C的开头识别动词短语。动词短语之后的剩余部分又被分配给列表B,列表B既是“动词短语之后的输入列表的一部分”(在谓词VP中),(因为动词短语可以是句子中的最后一项),也是“s之后的输入部分”(因为动词短语可以是句子中的最后一项),并且(因为动词短语可以是句子中的最后一项)也是“在s之后的输入部分”(在谓词VP中),并且(因为动词短语可以是句子中的最后一项)也是“s之后的输入部分”(在谓词VP中),并且(因为动词短语可以是句子中的最后一项)也是“s之后的输入部分”(。

例如,给定句子[the,Women,eats,the,apple],将调用NP的第一个规则,并将其第一个参数绑定到整个输入字符串;在它识别初始名词短语后,第二个参数将绑定到名词短语后面的句子部分,即[eats,the,apple]。谓词VP随后将识别动词短语[eats,the,apple],将空列表[]作为句子后面的输入部分。[1]。

下面的Prolog会话日志片段显示了如何使用该语法来测试句子的文法性;它还表明该语法可以使用一些改进。为了测试给定的记号序列是否为s,我们使用两个不同的-listargument来调用prolog谓词s。如果我们指定空列表作为第二个参数,我们实际上要求整个输入序列是一个合法语句。

我们可以使用变量作为第二个参数,而不是空列表;在这种情况下,Prolog将告诉我们输入列表的每个前缀,这些前缀可以根据我们的语法被解析为一个句子:在一次解析中,整个输入被解析为一个句子,其余的就是空列表。然而,另一种分析方法是将[the,Women,eats]作为一个句子,留下[the,apple]作为剩余的句子。

属性语法是上下文无关的语法,其中解析树的节点携带属性(名称-值对形式的注释)。根据与上下文无关文法的产生式规则相关联的规范来计算属性值。属性值可以在解析树中自上而下(继承属性)或自下而上(合成属性)传播。

[Alblas 1991]给出了属性文法的一种可访问的形式描述。非正式地,属性文法有时被描述为具有非终结符上的参数的上下文无关文法。

通过向产生式添加参数,我们可以将简单的上下文无关文法E1转换为属性文法;我将称之为属性文法E2。在E2中,一个参数用于生成句子的结构描述,另一个参数用于检查语法数字中的主谓一致。

因此,用参数和词典中的更多单词进行扩展,语法看起来如下:%E2:用于%英语片段的平凡属性语法,具有用于结构%描述和数字协议实施的合成属性。s(S,P))-->;NP(S,num),VP(P,num).np(NP(D,N),num)-->;det(D,num),n(N。N(N,pl).np(np(N),sg)-->;pn(N,sg).vp(Vp(V,O),Num)-->;v(V,Num),Np(O,_).vp(Vp(V),Num)-->;v(V,Num).N(n(L),Num)-->;[L],{Lex(L,n。[l],{lex(L,pn,num)}.v(v(L),num)-->;[L],{lex(L,v,num)}.det(det(L),num)-->;[l],{lex(L,Det,num)}.lex(Mary,pn,sg).lex(John,pn,sg).lex(女性,n,sg).lex(女性,n,pl).lex(man,n,sg).lex(man,n,pl).lex(Apple,n,sg).lex(apples,n,pl).lex(the,Det,_).lex(ome,det,pl).lex(ome,det,pl)。Lex(eats,v,sg).lex(eat,v,pl).lex(sings,v,sg).lex(sing,v,pl)。

当用这个语法分析时,句子“John Love Mary”被分配结构s(NP(pn(John)),VP(v(Love),NP(pn(Mary)-或者,用漂亮的印刷术:这里,语法规则左边的每个非末端的语法结构是从该规则右边的非末端的语法结构合成的。“约翰爱玛丽”被分配给结构s(NP(pn(John)),VP(v(Love),NP(pn(Mary)-或者,用漂亮的印刷体:这里,语法规则左边的每个非末端的语法结构是从规则右边的非末端的语法结构合成的。所以它是一个综合属性。

在一些作品中,第二个参数被用来强制主语和动词以及限定词和名词在数量上的一致。在这里,Prolog的统一机制使得没有必要区分合成属性和继承属性。当且仅当NP和VP的数量(Num)一致时,句子才是语法的;属性是自下而上地在这两个上流动,还是自下而上地在一个上流动,而在另一个上自上而下地流动,这与我们无关。

在这个版本的语法中,值得读者注意一些规则右侧的大括号内的序言从句,例如,在这些序言从句中,表达了对语法结构的约束,而这些约束本身并不是语法成分。它们有时被称为“卫士”(例如,[Brown/Blair 1990]);它们直接对应于[Alblas 1991]所描述的定语语法的一个组成部分--“语义条件”。如果(A)L(代表‘词汇化’)是输入列表[L]中的单个项,并且(B)关系lex包含三元组(L,n,num),则该规则可以解释为“A noun(An N),具有结构n(L)和语法数字num。如果(A)L(代表‘licialform’)是输入列表[L]中的单个项,并且(B)关系lex包含三元组(L,n,num)。

这样的保护将出现在我们用逻辑语法表示XML Schema的许多规则中。

通过调用语法规则,我们可以实例化一个具有句子结构描述的变量。?-s(strucc,[John,Love,Mary],[]).Struc=s(NP(pn(John)),VP(v(Love),NP(pn(Mary)是?-s(S,[the,Women,eats,the,Apple],[]).s=s(NP(the(The),n(Women)),VP(v(Eats),NP(det(The),n(Apple)是?-s。[]).s=s(np(det(The),n(Man)),vp(v(Sings))是?-s(S,[apple,sings,the,Mary],[]).no?-s(S,[the,Apple,sings,Mary],[]).s=s(np(det(The),n(Apple)),vp(v(Sings),np(pn(Mary)是?-。

注意,在DCG的“常规”使用中,用户或应用程序只在顶层调用语法一次,解析器本身将为嵌套的语法构造生成递归调用(例如,在我们的示例中为np和vp)。但是,请注意,如果我们希望从任意Prolog代码调用任何级别的语法,我们都可以。还要注意的是,我们可以将任意的序言代码包含在规则的右侧,方法是将其放在大括号中。通过这样做,我们可以将任意复杂的计算作为语法规则的一部分;这是DCG不限于上下文无关语言的一个原因。

Henk Alblas在他的属性文法导论([Alblas 1991])中提供了两个简单的例子,这里将以定从句的形式重现这两个例子。这两个例子都说明了如何使用属性来记录编程语言的表达式子语言中的类型信息,并检测类型错误。

第一个示例(Alblas标记为AG1)通过合成的(自下而上)属性来推断数值表达式的总体类型。

赋值由变量名、赋值符号(‘get’)和表达式组成。如果变量的类型是整数,并且表达式是实数类型,则会引发错误。<;1AG1以Dcg形式[Fileag1.dcg]>;≡/*在Dcg的版本中介绍了其语法属性:AG1已在[Alblas,1991].**//*中提供。*//*A1.更新语法*/赋值变量-->;变量(Vtype),获取,表达式(Etype),*{(vtype=t,*etype=)}{(vtype=t,*etype=.){(vtype=t,*etype=t),*etype=.{0}{(vtype=,*etype=)。)更多的信息;更真实的信息}。

在<;表达式2,<;术语3,<;简单术语4&>,<;因子5,<;加法运算符6>;,<;乘法运算符7&>,<;标点符号8>;,<;变量9>;,<;错误消息10>;,<;测试数据11>;中继续。

表达式是由加法运算符连接的一系列项。请注意,[Alblas 1991]中的语法是左结合的,但为了简单起见,我将其重新转换为左结合的,因为定从句语法在进行左递归时有问题。[2]如果其中一个操作数是实数,则求和的类型是实数;如果两个操作数都是整数,则表达式也是实数。<;2表达式[以dcg形式继续1 AG1]>;≡Expr(T)0-->;Term(T1),Gaddop(_Op),&Expr(T2),{eX1t;=3real=>;GT=3real 1;eXT=3T2}.Expr(T)3-->;Term(T)。

项是一系列因子,由乘法运算符连接。有三个运算符,具有不同的类型规则:<;3个项[以dcg形式继续1ag1]>;≡项(T)因子(T1)、运算符(Op)、项(T2)、{(运算运算符=nmul)}->;(t1项=1实数->;*T项=1实数);*T。*(Op=Tmod)t->;rt;=1int,t(t1;=1个实数->1个操作数错误(';左操作数不应为整数类型);TRUE),2个操作数(t2=1个实数->1错误(';右操作数数不应为1个整数类型););.(tt)(t)(tt);t。

如果该系列中只有一个因素,则术语的类型与该因素的类型相同。

因子可以是变量,也可以是带括号的表达式。它们的类型是从变量或子表达式的类型复制而来的。

这种语言中使用的简单标点符号可以很容易地直接写到它们出现的地方,但是我们将遵循为每个标点符号定义前缀符号的做法。

Alblas的语法不处理将类型赋给变量;类型(整数或实数)与每个变量相关联,但该机制是带外的。在大多数语言中,声明都会用于此目的。出于测试目的,我们将使用一种简单的机制,就像旧的Fortran规则那样,类型取决于变量名的初始字符。<;9变量[以dcg形式继续1 AG1]>;≡变量(INT)*-->;[i].Variable(INT)*[j].Variable(INT)*-->;T[k].Variable(REAL)*-->;T[x].Variable(REAL)*-->;L[y].Variable(REAL)[y].Variable(REAL)--&z].Variable(INT。*[a].变量(Int)*-->;*[b].变量(Int)*-->;*[c]。

同样,仅出于测试目的,我们将定义一个简单的错误消息谓词。<;10错误消息[以dcg形式继续1 ag1]>;≡/*2.其他实用程序代码(如真实度)*/错误(E):-NL,写入(';!),写入_错误_消息(E),写入(';)!!';),WRITE_ERROR_MESSAGE(E):-A ATOM(E),WRITE_ERROR_MESSAGE([H|T]):-A WRITE_ERROR_MESSAGE(H),A WRITE_ERROR_MESSAGE(T).A WRITE_ERROR_MESSAGE([]).WRITE_ERROR_MESSAGE([])。

最后,拥有一些与语法相关的测试数据是很有用的,这些数据根本不是一组详尽的测试,而是一些有用的健全检查。<;11测试数据[继续Dcg形式的1AG1]>;≡/*测试工具*//*在前言提示符后,逐一浏览这些测试数据,输入*测试数据(测试)、测试作业(节点、测试、[])、写入(测试)、测试NL**、测试*//*,然后继续命中测试;请查看**新的测试数据。2*/testdata([a,b:=,11b]).testdata([a,b:=,7y]).testdata([z,11:=,7y]).testdata([z,11:=,a]).testdata([z,18:=,3a]).testdata((';,11b,+,yy,t';)&#。[]).testdata([z,x:=,cb,t+,y,';)';).testdata([z,n:=,p a,t mod,t';(';,b,c+,c,';)';]).testdata([z,n:=,p,z,d mod]).testdata([z,n:=,c,';)';d]).testdata([z,n:=,t,z,t mod]).testdata([z,c:=,t,z,#mod]).testdata([z,x:=,#z,#mod])。,.testdata([z,u:=,rz,t+,c,';)';).testdata([a,n:=,t,a,dmod,';)';(';,b,c+,c,';)';]).testdata([a,b:=,c,';)';(';,rb,c+,c,';)';).testdata([a,b:=,c,';)';]。

对于某些应用程序,用与应用程序相关的信息注释解析树的注释是很有用的。正如刚才所显示的,可以使用DCG表示法来提供这样的注释,但是它很快就变得很麻烦。DCTG表示法被设计成比DCG更方便地处理语法属性,并且更有效地将语义从语法中分离出来[Abramson 1984]。它也被应用于SGML语义的讨论([Brown/Blair 1990],[Brown/Wakayama/Blair 1992])。

下面是DCTG表示法的一个简单示例。考虑以下二进制字符串的上下文无关文法:bit::=';0';.bit::=';1';.bitstring::==';';/*Nothing*/.bitstring::=bit,bitstring.number::=bitstring,fraction.fraction.分数::=';.';,bitString::=';';

我们可能希望计算位串的长度和(无符号基数2)值作为属性。使用类似yacc的符号,可能如下所示。请注意,Scale是自上而下的属性,value和fractionalvalue是自下而上的属性。Bit::=';0';{$0.bitvalue=0;}.bit::=';1';{$0.bitvalue=power(2,$0.scale);}.bitstring::=&';';{$0.value=0;$0.length=0;/*scale';此处不重要*/}.bitstring::=bit,bitstring{$0.length=$2.length+1;$1.scale=$0.scale。$2.scale=$0.scale-1;$0.value=$1.value+$2.value;}.number::=位串,分数{$1.scale=$1.length-1;$0.value=$1.value+$2.fractionalvalue;}.分数::=';.';,位串{$2.scale=-1;$0.fractionalvalue=$2.value;}.分数::=';';{$0.fractionalvalue=。

在DCTG表示法中,此语法如下:[3]bit::=[0]<;:>;bitval(0,_).bit::=[1]<;:>;bitval(V,Scale)::-V is**(2,scale).bitstring::=[]<;:>;length(0)&;&;value(0,_).bitstring::=bit^^B,bit^B。Length(Length)::-B1^Length(Length1),Length is Length1+1&;&;Value(Value,Scaleb)::-B^bitval(Vb,Scaleb),S1为Scaleb-1,B1^值(V1,S1),值为VB+V1。数字::=位串^B,分数^F<;:>;值(V)::-B^LENGTH(LENGTH),S是长度-1,B^VALUE(VB,S),F^FRACTIONAL_VALUE(VF),V是VB+VF.FRATION::=[';.';],位串^B<;:>;FRACTIONAL_VALUE(V)::-S是-1,B^VALUE(V,S)。FRAMETION::=[]<;:>;FRANT;

可以看出,DCTG中的每个规则都由左侧、右侧以及可选的属性列表组成。右侧与左侧之间由运算符::=分隔,与以下属性列表(如果有的话)之间由运算符<;:>;分隔。右侧的表达式是一系列非终端符号,每个符号可选后缀为运算符^^和变量名(例如,位串^^B)。每个属性由序言结构标识,例如值(V),其函数器是属性的名称和谁

DCTG表示法[4]的部分EBNF语法是:语法::=(子句|指令|规则)*规则::=lhs';::=';rhs(';<;:>;';att-spec(';&;&;';att-spec)*)?lhs::=Termrhs::=Term(';,';Term)*attspee。,';目标)*)?复合术语::=ATOM';(';Term(';,';Term)*';)';子句::=Head';:-';Body。

这里有另一个简单的例子:上面给出的简单的英语语法片段,直接翻译成DCTG表示法,如下所示。与DCG形式的唯一区别是,每个产品的左右两侧之间的分隔符是“::=”,而不是“-->;”。%E1(英语片段的普通上下文无关文法)%,采用DCTG记号。s::=np,vp.np::=det,n.np::==v,np.vp::=V.N::=[Mary].N::=[John].N::=[Women].N::=[man].N::=[Apple].det::=[the].v::=[the].v::=[the].v::=。

到标准PROLOG的转换类似于用于DCGS的转换,但是DCTG转换没有向eachnon-Terminal添加两个参数,而是添加了三个参数。第一个附加变元是具有函数器节点的序言结构,下面将进一步描述。第二个和第三个是差异列表(就像DCG翻译中添加的两个参数一样)。

节点结构有三个参数:与语法规则右侧的项相关联的节点结构列表。

将琐碎的语法翻译成标准的PROLOG是这样的:?-dctg_reconsult(';ks81dctg.pl';).是?-LISTING([s,np,vp,n,det,v]).:-dynamic s/3.s(node(s,[A,B],[]),C,D):-np(A,C,E),VP(B,E,D).:-dynamic np/3.np(node(np,[A,B],[]),C,D):-d。

.