解析、匹配和关键字
Parsing, matching and keywords
我正在使用 Java15 语法,有几个问题是关于 Rascal 的解析器如何工作以及为什么有些东西不工作。给出具体语法:
module tests::Concrete
start syntax CompilationUnit =
compilationUnit: TypeDec* LAYOUTLIST
;
syntax TypeDec =
ClassDec
;
syntax ClassDec =
\class: ClassDecHead ClassBody
;
syntax ClassDecHead =
"class" Id
;
syntax ClassBody =
"{" ClassBodyDec* "}"
;
syntax ClassBodyDec =
ClassMemberDec
;
syntax ClassMemberDec =
MethodDec
;
syntax MethodDec =
\method: MethodDecHead
;
syntax MethodDecHead =
ResultType Id
;
syntax ResultType =
\void: "void"
;
syntax Id =
\id: [A-Z_a-z] !<< ID \ IDKeywords !>> [0-9A-Z_a-z]
;
keyword Keyword =
"void"
;
keyword IDKeywords =
"null"
| Keyword
;
lexical LAYOUT =
[\t-\n \a0C-\a0D \ ]
;
lexical ID =
[A-Z_a-z] [0-9A-Z_a-z]*
;
layout LAYOUTLIST =
LAYOUT* !>> [\t-\n \a0C-\a0D \ ] !>> ( [/] [*] ) !>> ( [/] [/] ) !>> "/*" !>> "//"
;
AST 定义:
module tests::Abstract
data Declaration =
\compilationUnit(list[Declaration] body)
| \package(ID name)
| \import(ID name)
| \class(ID name, list[Declaration] body)
| \method(Type ret, ID name)
;
data Type =
\void()
;
data ID =
\id(str id)
;
和加载文件的驱动程序:
module tests::Load
import Prelude;
import tests::Concrete;
import tests::Abstract;
public Declaration load(loc l) = implode(#Declaration, parse(#CompilationUnit, l));
我发现实际有效和无效的一些奇怪之处。如果我参加这个项目:
class A {
}
这按预期解析为:compilationUnit([ class(id("A"),[]) ])
但事实证明,为 class 内部的方法解析和构建 AST 节点有点棘手。给定程序:
class A {
void f
}
这会产生 "Cannot find a constructor for Declaration"
错误。如果我将语法修改为:
syntax MethodDecHead =
ResultType
;
要成为的 AST:
| \method(Type ret)
我能够得到我期望的树:compilationUnit([class(id("A"),[method(void())])])
我对这里发生的事情、如何处理关键字以及导致这种行为的原因感到很困惑。
除此之外,如果我不将 LAYOUTLIST
添加到 start syntax
生产的末尾,我每次尝试读取文件时都会得到 ParseError
。
我不是 implode
方面的专家,所以我暂时不说了,但是 LAYOUTLIST 的问题是由于 parse
的调用方式。
每个由start Something =
定义的start
非终结符产生两种类型,即:
* 非终结符本身 Something
和
* 一个名为 start[Something]
的非终端包装器。
包装器 automatically/implicitly 定义如下:
syntax start[Something] = LAYOUTLIST before Something top LAYOUTLIST after;
所以,如果你想在你的程序前后有空格和注释,你可以像这样调用解析:
parse(#start[Something], yourLocation)
如果您不想保留注释或空白以备后用,那么您可以像这样投影出顶层树:
Something mySomething = parse(#start[Something], myLocation).top;
ClassDec
的产生式规则与 AST 节点 class
不兼容。
将其更改为:
syntax ClassDec =
\class: "class" Id "{" ClassBodyDec* "}"
;
使其与 AST 节点更加规则和同构 class(ID name, list[Declaration])
但是:名称应该始终对应,所以我建议在语法中将 ID
更改为 Id
。此外,您的 AST 节点需要 Declaration
s,但在语法中您有 ClassBodyDec
s.
implode
的一般规则是:
- 非终结符对应ADT类型
- 生产标签对应ADT构造函数
- 关键字、运算符、布局等被跳过。
- 未标记的词汇产生式映射到原语(str、int、real)。
- 如果需要,带标签的词法可以映射到构造函数:
lexical Id = id: [a-z]+
,可以映射到 data Id = id(str x)
;
- 如果你不标记上下文无关的产品,
implode
"looks over them":所以如果我有 syntax A = B; syntax B = cons: "bla"
,那么我可以使用 ADT:data A = cons()
。
(这些规则记录在 Parsetree.rsc、https://github.com/cwi-swat/rascal/blob/master/src/org/rascalmpl/library/ParseTree.rsc)
我正在使用 Java15 语法,有几个问题是关于 Rascal 的解析器如何工作以及为什么有些东西不工作。给出具体语法:
module tests::Concrete
start syntax CompilationUnit =
compilationUnit: TypeDec* LAYOUTLIST
;
syntax TypeDec =
ClassDec
;
syntax ClassDec =
\class: ClassDecHead ClassBody
;
syntax ClassDecHead =
"class" Id
;
syntax ClassBody =
"{" ClassBodyDec* "}"
;
syntax ClassBodyDec =
ClassMemberDec
;
syntax ClassMemberDec =
MethodDec
;
syntax MethodDec =
\method: MethodDecHead
;
syntax MethodDecHead =
ResultType Id
;
syntax ResultType =
\void: "void"
;
syntax Id =
\id: [A-Z_a-z] !<< ID \ IDKeywords !>> [0-9A-Z_a-z]
;
keyword Keyword =
"void"
;
keyword IDKeywords =
"null"
| Keyword
;
lexical LAYOUT =
[\t-\n \a0C-\a0D \ ]
;
lexical ID =
[A-Z_a-z] [0-9A-Z_a-z]*
;
layout LAYOUTLIST =
LAYOUT* !>> [\t-\n \a0C-\a0D \ ] !>> ( [/] [*] ) !>> ( [/] [/] ) !>> "/*" !>> "//"
;
AST 定义:
module tests::Abstract
data Declaration =
\compilationUnit(list[Declaration] body)
| \package(ID name)
| \import(ID name)
| \class(ID name, list[Declaration] body)
| \method(Type ret, ID name)
;
data Type =
\void()
;
data ID =
\id(str id)
;
和加载文件的驱动程序:
module tests::Load
import Prelude;
import tests::Concrete;
import tests::Abstract;
public Declaration load(loc l) = implode(#Declaration, parse(#CompilationUnit, l));
我发现实际有效和无效的一些奇怪之处。如果我参加这个项目:
class A {
}
这按预期解析为:compilationUnit([ class(id("A"),[]) ])
但事实证明,为 class 内部的方法解析和构建 AST 节点有点棘手。给定程序:
class A {
void f
}
这会产生 "Cannot find a constructor for Declaration"
错误。如果我将语法修改为:
syntax MethodDecHead =
ResultType
;
要成为的 AST:
| \method(Type ret)
我能够得到我期望的树:compilationUnit([class(id("A"),[method(void())])])
我对这里发生的事情、如何处理关键字以及导致这种行为的原因感到很困惑。
除此之外,如果我不将 LAYOUTLIST
添加到 start syntax
生产的末尾,我每次尝试读取文件时都会得到 ParseError
。
我不是 implode
方面的专家,所以我暂时不说了,但是 LAYOUTLIST 的问题是由于 parse
的调用方式。
每个由start Something =
定义的start
非终结符产生两种类型,即:
* 非终结符本身 Something
和
* 一个名为 start[Something]
的非终端包装器。
包装器 automatically/implicitly 定义如下:
syntax start[Something] = LAYOUTLIST before Something top LAYOUTLIST after;
所以,如果你想在你的程序前后有空格和注释,你可以像这样调用解析:
parse(#start[Something], yourLocation)
如果您不想保留注释或空白以备后用,那么您可以像这样投影出顶层树:
Something mySomething = parse(#start[Something], myLocation).top;
ClassDec
的产生式规则与 AST 节点 class
不兼容。
将其更改为:
syntax ClassDec =
\class: "class" Id "{" ClassBodyDec* "}"
;
使其与 AST 节点更加规则和同构 class(ID name, list[Declaration])
但是:名称应该始终对应,所以我建议在语法中将 ID
更改为 Id
。此外,您的 AST 节点需要 Declaration
s,但在语法中您有 ClassBodyDec
s.
implode
的一般规则是:
- 非终结符对应ADT类型
- 生产标签对应ADT构造函数
- 关键字、运算符、布局等被跳过。
- 未标记的词汇产生式映射到原语(str、int、real)。
- 如果需要,带标签的词法可以映射到构造函数:
lexical Id = id: [a-z]+
,可以映射到data Id = id(str x)
; - 如果你不标记上下文无关的产品,
implode
"looks over them":所以如果我有syntax A = B; syntax B = cons: "bla"
,那么我可以使用 ADT:data A = cons()
。
(这些规则记录在 Parsetree.rsc、https://github.com/cwi-swat/rascal/blob/master/src/org/rascalmpl/library/ParseTree.rsc)