您现在的位置是:网站首页> 编程资料编程资料
golang 四则运算计算器yacc归约手写实现_Golang_
2023-05-26
390人已围观
简介 golang 四则运算计算器yacc归约手写实现_Golang_
缘起
最近拜读前桥和弥[日]的<<自制编程语言>>
开头一章便是教读者使用lex/yacc工具
制作四则运算器
其中yacc的移进/归约/梯度下降的思想很有启发
于是使用golang练习之
目标
- 制作一个四则运算器, 从os.Stdin读入一行表达式, 然后输出计算过程和结果
- 支持+ - * /
- 支持左右括号
- 支持负数
难点
记号扫描(lexer)
- 逐字符扫描记号
- 单字符记号可直接识别, + - * / ( )
多字符记号, 此处只有浮点数, 通过有限状态的转换进行识别
+ '-' = INT_STATUS
+ 'd' = INT_STATUS
+ '.' = DOT_STATUS
+ 'SPACE | + | - | * | / | ( | )' = INITIAL
+ 'd' = FRAG_STATUS
+ 'SPACE | + | - | * | / | ( | )' = INITIAL
运算优先级
- /优先级最高, 可以立即归约计算
- 括号次之, 遇到右括号, 应当触发+ -归约
- 程序末尾, 没有新的记号剩余, 对+ -进行归约
识别负数
- 简单起见, 本程序总是使用浮点数作为基本计算单位
- 把负号识别为浮点数的可选部分: 浮点数 = -?d+(.d+)?
总体流程
- 从os.Stdin读入一行表达式字符串
- 逐字符扫描记号流, 放入记号队列
- 逐记号出队, 置入计算栈
- 判断栈顶是否符合归约条件, 是则进行计算
- 记号队列空, 对计算栈进行最终计算
- 输出结果
main.go
从os.Stdin循环读入行
调用lexer.Parse获得记号流
调用parser.Parse进行计算
func main() { reader := bufio.NewReader(os.Stdin) for { fmt.Printf("=> ") arrBytes, _, err := reader.ReadLine() if err != nil { panic(err.Error()) } line := strings.TrimSpace(string(arrBytes)) expression := line tokens, e := lexer.Parse(expression) if e != nil { println(e.Error()) } else { e,v := parser.Parse(tokens) if e != nil { println(e.Error()) } fmt.Println(strconv.FormatFloat(v, 'f', 10, 64)) } } }tokens/tokens.go
定义记号
package tokens type TOKENS string const IntLiteral TOKENS = "INT" const DoubleLiteral TOKENS = "DBL" const ADD TOKENS = "ADD" const SUB TOKENS = "SUB" const MUL TOKENS = "MUL" const DIV TOKENS = "DIV" const LB TOKENS = "LB" const RB TOKENS = "RB" const UNKNOWN TOKENS = "UNKNOWN" type Token struct { Token TOKENS Value string Position int } func OfRune(t TOKENS, r rune, from int) *Token { return &Token { Token: t, Value : string(r), Position: from, } } func OfString(t TOKENS, s string, from int) *Token { return &Token { Token: t, Value : s, Position: from, } }states/states.go
定义lexer的状态
type STATES int const INITIAL STATES = 1 const INT_STATUS STATES = 11 const DOT_STATUS STATES = 12 const FRAG_STATUS STATES = 13
lexer/lexer.go
记号扫描
type tLexerState struct { state states.STATES tokens []*tokens.Token buffer []rune i0 int i1 int d0 int d1 int } func (me *tLexerState) AppendToken(t *tokens.Token) { me.tokens = append(me.tokens, t) } func (me *tLexerState) AppendChar(it rune) { me.buffer = append(me.buffer, it) } func (me *tLexerState) BufferSize() int { return len(me.buffer) } func (me *tLexerState) IntSize() int { return me.i1 - me.i0 + 1 } func (me *tLexerState) FragSize() int { return me.d1 - me.d0 + 1 } func Parse(line string) ([]*tokens.Token, error) { var state = &(tLexerState{ state: states.INITIAL, tokens: make([]*tokens.Token, 0), buffer: make([]rune, 0), i0 : 0, i1 : 0, d0: 0, d1: 0, }) for i, it := range line + "\n" { e := parseChar(state, i, it) if e != nil { return nil, e } } return state.tokens, nil } func parseChar(state *tLexerState, i int, it rune) error { var e error = nil switch state.state { case states.INITIAL: e = parseCharWhenInitial(state, i, it) break case states.INT_STATUS: e = parseCharWhenInt(state, i, it) break case states.DOT_STATUS: e = parseCharWhenDot(state, i, it) break case states.FRAG_STATUS: e = parseCharWhenFrag(state, i, it) break } return e } func parseCharWhenInitial(state *tLexerState, i int, it rune) error { if is_minus(it) || is_0_to_9(it) { state.state = states.INT_STATUS state.buffer = make([]rune, 0) state.buffer = append(state.buffer, it) state.i0 = i state.i1 = i } else if is_space(it){ return nil } else if is_add(it) { state.AppendToken(tokens.OfRune(tokens.ADD, it, i)) } else if is_sub(it) { state.AppendToken(tokens.OfRune(tokens.SUB, it, i)) } else if is_mul(it) { state.AppendToken(tokens.OfRune(tokens.MUL, it, i)) } else if is_div(it) { state.AppendToken(tokens.OfRune(tokens.DIV, it, i)) } else if is_lb(it) { state.AppendToken(tokens.OfRune(tokens.LB, it, i)) } else if is_rb(it) { state.AppendToken(tokens.OfRune(tokens.RB, it, i)) } else { return errors.New(fmt.Sprintf("parseCharWhenInitial, invalid char %c at %d", it, i)) } return nil } func parseCharWhenInt(state *tLexerState, i int, it rune) error { if is_0_to_9(it) { if state.BufferSize() >= 10 { return errors.New(fmt.Sprintf("too large int number at %v", i)) } else { state.AppendChar(it) state.i1 = i } } else if is_dot(it) { state.AppendChar(it) state.state = states.DOT_STATUS } else if is_space(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.state = states.INITIAL } else if is_rb(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.RB, it, i)) state.state = states.INITIAL } else if is_add(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.ADD, it, i)) state.state = states.INITIAL } else if is_sub(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.SUB, it, i)) state.state = states.INITIAL } else if is_mul(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.MUL, it, i)) state.state = states.INITIAL } else if is_div(it) { state.AppendToken(tokens.OfString(tokens.IntLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.DIV, it, i)) state.state = states.INITIAL } else { return errors.New(fmt.Sprintf("parseCharWhenInt, invalid char %c at %d", it, i)) } return nil } func parseCharWhenDot(state *tLexerState, i int, it rune) error { if is_0_to_9(it) { state.state = states.FRAG_STATUS state.AppendChar(it) state.d0 = i state.d1 = i } else { return errors.New(fmt.Sprintf("parseCharWhenDot, invalid char %c at %d", it, i)) } return nil } func parseCharWhenFrag(state *tLexerState, i int, it rune) error { if is_0_to_9(it) { if state.FragSize() >= 10 { return errors.New(fmt.Sprintf("too many chars for a double value at %d", i)) } else { state.AppendChar(it) state.d1 = i } } else if is_space(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.state = states.INITIAL } else if is_add(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.ADD, it, i)) state.state = states.INITIAL } else if is_sub(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.SUB, it, i)) state.state = states.INITIAL } else if is_mul(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.MUL, it, i)) state.state = states.INITIAL } else if is_div(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.DIV, it, i)) state.state = states.INITIAL } else if is_rb(it) { state.AppendToken(tokens.OfString(tokens.DoubleLiteral, string(state.buffer), state.i1)) state.AppendToken(tokens.OfRune(tokens.RB, it, i)) state.state = states.INITIAL } else { return errors.New(fmt.Sprintf("parseCharWhenFrag, invalid char %c at %d", it, i)) } return nil }parser/tStackNode.go
定义计算栈的一个节点. 计算栈中有两种节点: 已归约的值节点, 和尚未计算的记号节点
type tStackNodeType int const NODE_VALUE tStackNodeType = 1 const NODE_TOKEN tStackNodeType = 2 type tStackNode struct { flag tStackNodeType token *tokens.Token value float64 } func newValueNode(value float64) *tStackNode { return &tStackNode{ flag: NODE_VALUE, value: value, token: nil, } } func newTokenNode(token *tokens.Token) *tStackNode { return &tStackNode{ flag: NODE_TOKEN, token: token, value: 0, } } func (me *tStackNode) getTokenType() tokens.TOKENS { switch me.flag { case NODE_VALUE: return tokens.DoubleLiteral case NODE_TOKEN: switch me.token.Token { case tokens.IntLiteral: fallthrough case tokens.DoubleLiteral: return tokens.DoubleLiteral default: return me.token.Token } } return tokens.UNKNOWN } func (me *tStackNode) getDoubleValue() float64 { switch me.flag { case NODE_VALUE: return me.value case NODE_TOKEN: switch me.token.Token { case tokens.IntLiteral: fallthrough case tokens.DoubleLiteral: v1,e1 := strconv.ParseFloat(me.token.Value, 64) if e1 != nil { panic(e1) } return v1 } } panic("value not avaiable") }parser/parser.go
提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
点击排行
本栏推荐
