安康免费做网站公司,文化类网站建设,个人站长做什么类型的网站,企业关键词大全上一节我们研究了增强语法#xff0c;本节我们看看何为属性语法。属性语法实则是在语法规则上附带上一些重要的解析信息#xff0c;随着语法解析的进行#xff0c;我们可以利用附带的解析信息去进行一系列操作#xff0c;例如利用解析信息实现代码生成。我们先看属性语法的…上一节我们研究了增强语法本节我们看看何为属性语法。属性语法实则是在语法规则上附带上一些重要的解析信息随着语法解析的进行我们可以利用附带的解析信息去进行一系列操作例如利用解析信息实现代码生成。我们先看属性语法的一个实例
NUMBER(156, 156)NUMBER 是语法解析中的终结符他附带有两个属性一个是该标签对应字符串的内容“156”另一个是他对应的数值也就是 156如果符号是 ID也就是变量那么它可以附带一个属性就是一个指针指向符号表的入口该符号表包含了该变量的字符串名称该变量对应的数据等等。
属性信息分为两种一种是继承属性也就是属性从语法表达式箭头左边的符号传递给右边的符号另一种是综合属性属性信息从箭头右边符号汇总后传递给左边符号。从前面代码中我们看到语法解析本质上就是函数的调用例如语法
expr - term expr_prime对应的代码实现就是
expr() {term()expr_prime()}对于继承属性那就是父函数expr 在调用是被输入了某些参数这些参数再传递给里面的 term,和 expr_prime例如
expr(param) {term(param)expr_prime(param)
}而综合属性就是子函数有返回值父函数获取子函数的返回值后综合起来处理例如
expr() {val_term : term()val_expr_prime : expr_prime(param)do_something(val_term, val_expr_prime)}在上一节我们使用增强语法来生成代码时代码生成所需要的信息例如寄存器等是从全局函数或全局变量例如全局寄存器数组等中获取在属性语法中我们就可以把这些信息作为参数传递给特定的语法解析函数这样在生成代码时就能更灵活。我们看具体的实现你就能更明白什么叫属性语法我们还是利用上一节识别算术表达式的语法
stmt - epsilon | expr SEMI stmt
expr - term expr_prime
expr_prime - PLUS term expr_prime
term - factor term_prime
term_prime - MUL factor term_prime | epsilon
factor - NUMBER | LEFT_PAREN expr RIGHT_PAREN在原有项目中创建新文件夹 attribute_parser,在里面创建文件 attribute_parser.go添加代码如下
package attribute_parserimport (fmtlexer
)type AttributeParser struct {parserLexer lexer.LexerreverseToken []lexer.Token//用于存储虚拟寄存器的名字registerNames []string//存储当前已分配寄存器的名字regiserStack []string//当前可用寄存器名字的下标registerNameIdx int
}func NewAttributeParser(parserLexer lexer.Lexer) *AttributeParser {return AttributeParser{parserLexer: parserLexer,reverseToken: make([]lexer.Token, 0),registerNames: []string{t0, t1, t2, t3, t4, t5, t6, t7},regiserStack: make([]string, 0),registerNameIdx: 0,}
}func (a *AttributeParser) putbackToken(token lexer.Token) {a.reverseToken append(a.reverseToken, token)
}func (a *AttributeParser) getToken() lexer.Token {//先看看有没有上次退回去的 tokenif len(a.reverseToken) 0 {token : a.reverseToken[len(a.reverseToken)-1]a.reverseToken a.reverseToken[0 : len(a.reverseToken)-1]return token}token, err : a.parserLexer.Scan()if err ! nil token.Tag ! lexer.EOF {sErr : fmt.Sprintf(get token with err:%s\n, err)panic(sErr)}return token
}func (a *AttributeParser) match(tag lexer.Tag) bool {token : a.getToken()if token.Tag ! tag {a.putbackToken(token)return false}return true
}func (a *AttributeParser) newName() string {//返回一个寄存器的名字if a.registerNameIdx len(a.registerNames) {//没有寄存器可用panic(register name running out)}name : a.registerNames[a.registerNameIdx]a.registerNameIdx 1return name
}func (a *AttributeParser) freeName(name string) {//释放当前寄存器名字if a.registerNameIdx len(a.registerNames) {panic(register name index out of bound)}if a.registerNameIdx 0 {panic(register name is full)}a.registerNameIdx - 1a.registerNames[a.registerNameIdx] name
}func (a *AttributeParser) Parse() {a.stmt()
}func (a *AttributeParser) stmt() {for a.match(lexer.EOF) ! true {t : a.newName()a.expr(t)a.freeName(t)if a.match(lexer.SEMI) ! true {panic(missing ; at the end of expression)}}
}func (a *AttributeParser) expr(t string) {a.term(t)a.expr_prime(t)
}func (a *AttributeParser) expr_prime(t string) {if a.match(lexer.PLUS) {t2 : a.newName()a.term(t2)fmt.Printf(%s %s\n, t, t2)a.freeName(t2)a.expr_prime(t)}
}func (a *AttributeParser) term(t string) {a.factor(t)a.term_prime(t)
}func (a *AttributeParser) term_prime(t string) {if a.match(lexer.MUL) {t2 : a.newName()a.factor(t2)fmt.Printf(%s * %s\n, t, t2)a.freeName(t2)a.term_prime(t)}
}func (a *AttributeParser) factor(t string) {if a.match(lexer.NUM) {fmt.Printf(%s %s\n, t, a.parserLexer.Lexeme)} else if a.match(lexer.LEFT_BRACKET) {a.expr(t)if a.match(lexer.RIGHT_BRACKET) ! true {panic(missing ) for expr)}}
}
我们可以看到 AttributeParser 跟我们前面实现的 AugmentedParser 区别不大一个明显区别是解析函数接受一个传进来的参数这个参数可以看做是语法属性他由语法表达式左边符号对应的函数创建然后传递给右边符号对应的函数。我们看如下代码
func (a *AttributeParser) stmt() {for a.match(lexer.EOF) ! true {t : a.newName()a.expr(t)a.freeName(t)if a.match(lexer.SEMI) ! true {panic(missing ; at the end of expression)}}
}stmt 函数在调用时创建了一个寄存器名称然后调用 expr 时将该名称作为参数传入在语法表达上相当于
stmt_(t) - expr_(t) SEMI stmt其中 t 是左边 stmt 符号附带的参数他将该参数传递给右边符号 exprexpr 利用该传过来的符号在语法解析时进行代码生成。从上面代码我们也能看出它实际上是增强语法和属性语法的结合体例如代码将属性作为参数传入同时在解析的过程中又在特定位置执行特定步骤因此上面的解析过程其实可以对应成如下的“增强属性语法”
stmt - epsilon | {tnewName()} expt_(t) SEMI stmt
expr_(t) - term_(t) expr_prime_(t)
expr_prime_(t) - PLUS {t2 newName()} term_(t2) {print(%s%s\n,t,t2) freenName(t2)} expr_prime_(t) | epsilon
term_(t) - factor term_prime
term_prime_(t) - MUL {t2 newName()} factor_(t2) {print(%s%s\n,t,t2) freeName(t2)} term_prime_(t)
factor_(t) - NUM {print(%s*%s\n,t, lexeme)} | LEFT_PAREN expr_(t) RIGHT_PAREN最后我们在 main.go 中调用属性语法解析器看看运行结果
package mainimport (attribute_parserlexer
)func main() {exprLexer : lexer.NewLexer(12*(43);)attributeParser : attribute_parser.NewAttributeParser(exprLexer)attributeParser.Parse()
}
上面代码运行后结果如下
t0 1
t1 2
t2 4
t3 3
t2 t3
t1 * t2
t0 t1可以看到生成的结果跟我们上一节一样。更多内容请在 b 站搜索 coding 迪斯尼。代码下载 https://github.com/wycl16514/compiler-attribute-grammar.git