├── .gitignore ├── go.mod ├── docs ├── 实验报告.docx └── 题目要求.docx ├── bin ├── maxif.txt └── while.txt ├── parser ├── error.go ├── selectset │ ├── declare.go │ ├── factor.go │ ├── statement.go │ └── expression.go ├── ident │ └── ident.go ├── fct │ └── fct.go ├── printout.go └── parser.go ├── asm └── asm.go ├── README.md ├── interpret ├── rw.go └── interpret.go ├── fileio └── fileio.go ├── token ├── token_test.go └── token.go ├── main.go ├── LICENSE └── scanner ├── error.go └── scanner.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zu1k/pl0complier 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /docs/实验报告.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zu1k/pl0compiler/HEAD/docs/实验报告.docx -------------------------------------------------------------------------------- /docs/题目要求.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zu1k/pl0compiler/HEAD/docs/题目要求.docx -------------------------------------------------------------------------------- /bin/maxif.txt: -------------------------------------------------------------------------------- 1 | var x,y,r; 2 | 3 | procedure max; 4 | begin 5 | r:=x; 6 | if x 1 { 19 | config.filepath = args[1] 20 | } 21 | log.Printf("SourceCode FilePath: %s\n", config.filepath) 22 | parser := parser2.Parser{} 23 | parser.Init(config.filepath) 24 | parser.Start() 25 | parser.Sout() 26 | parser.Interpret() 27 | } 28 | 29 | func testparser(filepath string) { 30 | scanner := scanner.Scanner{} 31 | scanner.Init(filepath) 32 | for { 33 | tok, lit, num := scanner.Scan() 34 | if tok == token.EOFSYM { 35 | break 36 | } 37 | if lit == "" { 38 | lit = "NULL" 39 | } 40 | if tok != token.BADTOKEN { 41 | log.Printf("\t类型说明:%s", tok, lit, num, tok.String()) 42 | } else { 43 | log.Printf("Error: \t类型说明:%s", tok, lit, num, tok.String()) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 zu1k 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scanner/error.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @auther: zu1k 3 | * @date: 2019/11/10 4 | */ 5 | package scanner 6 | 7 | import ( 8 | "log" 9 | ) 10 | 11 | var error = [...]string{ 12 | /* 0 */ "", 13 | /* 1 */ "找到‘:=’,但是期望的是‘=’。", 14 | /* 2 */ "‘=’后面必须接一个数字。", 15 | /* 3 */ "标识符后面必须是‘=’。", 16 | /* 4 */ "在‘const’, ‘var’, 或‘procedure’后面必须是一个标识符。", 17 | /* 5 */ "缺少‘,’或‘;’。", 18 | /* 6 */ "过程名错误,未找到该过程,或者连同名的常量变量都没有", 19 | /* 7 */ "期待一个语句。", 20 | /* 8 */ "语句后面是错误的符号。", 21 | /* 9 */ "期待‘.’。", 22 | /* 10 */ "期待‘.’。", 23 | /* 11 */ "未声明的标识符。", 24 | /* 12 */ "非法声明。", 25 | /* 13 */ "期待赋值号‘:=’。", 26 | /* 14 */ "在‘call’后面必须接一个标识符(过程)。", 27 | /* 15 */ "常量和变量(非过程标识符)不能被call调用。", 28 | /* 16 */ "'then' expected.", 29 | /* 17 */ "';' or 'end' expected.", 30 | /* 18 */ "'do' expected.", 31 | /* 19 */ "Incorrect symbol.", 32 | /* 20 */ "Relative operators expected.", 33 | /* 21 */ "Procedure identifier can not be in an expression.", 34 | /* 22 */ "Missing ')'.", 35 | /* 23 */ "The symbol can not be followed by a factor.", 36 | /* 24 */ "The symbol can not be as the beginning of an expression.", 37 | /* 25 */ "不能改变常量的值", 38 | /* 26 */ "变量未定义,无法赋值", 39 | /* 27 */ "call后面应该是一个过程类型的标识符,这里连标识符都不是", 40 | /* 28 */ "不能输出一个过程", 41 | /* 29 */ "", 42 | /* 30 */ "", 43 | /* 31 */ "数太大了。", 44 | /* 32 */ "层次太深了。", 45 | } 46 | 47 | func (s *Scanner) Error(t int) { 48 | log.Printf("Error %d: %s\tON Line: %d", t, error[t], s.line) 49 | } 50 | -------------------------------------------------------------------------------- /interpret/interpret.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @auther: zu1k 3 | * @date: 2019/11/12 4 | */ 5 | package interpret 6 | 7 | import ( 8 | "log" 9 | "github.com/zu1k/pl0complier/asm" 10 | "github.com/zu1k/pl0complier/parser/fct" 11 | ) 12 | 13 | var s [200]int 14 | 15 | func base(b int, l int) (c int) { 16 | c = b 17 | for l > 0 { 18 | c = s[c] 19 | l-- 20 | } 21 | return 22 | } 23 | 24 | func Interpret(asms []asm.Asm) { 25 | var p, b, t int // 程序寄存器PC、基地址寄存器、栈顶寄存器 26 | var i asm.Asm // 指令寄存器 27 | 28 | log.Printf("Start to run") 29 | t = 0 30 | b = 1 31 | p = 0 32 | 33 | s[1] = 0 34 | s[2] = 0 35 | s[3] = 0 36 | 37 | for { 38 | i = asms[p] 39 | p++ 40 | 41 | switch i.Fct { 42 | case fct.Lit: 43 | t++ 44 | s[t] = i.A 45 | case fct.Opr: //运算 46 | switch i.A { 47 | case 0: //返回指令 48 | t = b - 1 49 | p = s[t+3] 50 | b = s[t+2] 51 | case 1: //负号 52 | s[t] = -s[t] 53 | case 2: //加法 54 | t-- 55 | s[t] = s[t] + s[t+1] 56 | case 3: //减法 57 | t-- 58 | s[t] = s[t] - s[t+1] 59 | case 4: // 乘法 60 | t-- 61 | s[t] = s[t] * s[t+1] 62 | case 5: // 除法 63 | t-- 64 | s[t] = s[t] / s[t+1] 65 | case 6: // odd 66 | s[t] = s[t] % 2 67 | case 7: 68 | 69 | case 8: // == 70 | t-- 71 | bb := s[t] == s[t+1] 72 | if bb { 73 | s[t] = 1 74 | } else { 75 | s[t] = 0 76 | } 77 | case 9: // != 78 | t-- 79 | bb := s[t] != s[t+1] 80 | if bb { 81 | s[t] = 1 82 | } else { 83 | s[t] = 0 84 | } 85 | case 10: //< 86 | t-- 87 | bb := s[t] < s[t+1] 88 | if bb { 89 | s[t] = 1 90 | } else { 91 | s[t] = 0 92 | } 93 | case 11: //> 94 | t-- 95 | bb := s[t] > s[t+1] 96 | if bb { 97 | s[t] = 1 98 | } else { 99 | s[t] = 0 100 | } 101 | case 12: //<= 102 | t-- 103 | bb := s[t] <= s[t+1] 104 | if bb { 105 | s[t] = 1 106 | } else { 107 | s[t] = 0 108 | } 109 | case 13: //>= 110 | t-- 111 | bb := s[t] >= s[t+1] 112 | if bb { 113 | s[t] = 1 114 | } else { 115 | s[t] = 0 116 | } 117 | case 14: //read 118 | t++ 119 | s[t] = read() 120 | case 15: //write 121 | write(s[t]) 122 | t-- 123 | } 124 | case fct.Lod: // 调用变量值指令 125 | t = t + 1 126 | s[t] = s[base(b, i.L)+i.A] 127 | case fct.Sto: // 将值存入变量指令 128 | s[base(b, i.L)+i.A] = s[t] 129 | //log.Printf("%10d\n", s[t]) 130 | t = t - 1 131 | case fct.Cal: // 过程调用,产生新的块标记 132 | s[t+1] = base(b, i.L) 133 | s[t+2] = b 134 | s[t+3] = p // 记录返回地址等参数 135 | b = t + 1 136 | p = i.A 137 | case fct.Int: // 开内存空间 138 | t = t + i.A 139 | case fct.Jmp: // 无条件跳转指令 140 | p = i.A 141 | case fct.Jpc: // 栈顶为0跳转 142 | if s[t] == 0 { 143 | p = i.A 144 | } 145 | t-- 146 | } 147 | 148 | if p == 0 { 149 | break 150 | } 151 | } 152 | log.Printf("Completed\n") 153 | } 154 | -------------------------------------------------------------------------------- /scanner/scanner.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | import ( 4 | "log" 5 | "github.com/zu1k/pl0complier/fileio" 6 | "github.com/zu1k/pl0complier/token" 7 | "unicode" 8 | ) 9 | 10 | const ( 11 | MAX_TOKEN_LENGTH = 10 12 | ) 13 | 14 | type Scanner struct { 15 | file fileio.File 16 | line int 17 | } 18 | 19 | func (s *Scanner) Init(filepath string) { 20 | s.file = fileio.File{} 21 | s.file.Open(filepath) 22 | s.line = 1 23 | } 24 | 25 | type Symbol struct { 26 | SID token.Token 27 | VALUE string 28 | NUM int 29 | } 30 | 31 | var runecache []rune 32 | var c = ' ' 33 | var end = false 34 | 35 | func (s *Scanner) Scan() (sid token.Token, value string, num int) { 36 | runecache = make([]rune, 10) 37 | //去除空格、制表符、换行与回车等空白符 38 | for { 39 | if !isSpace(c) || end { 40 | break 41 | } 42 | s.getCh() 43 | } 44 | if end { 45 | sid = token.EOFSYM 46 | return 47 | } 48 | 49 | //出现字母,可能是保留字或者标识符 50 | if isLetter(c) { 51 | off := 0 52 | for { 53 | runecache[off] = c 54 | off++ 55 | if off > MAX_TOKEN_LENGTH-1 { 56 | //TODO 标识符长度超过10 57 | log.Printf("ERROR: 标识符长度超限,等待解决的地方") 58 | break 59 | } 60 | s.getCh() 61 | if !isLetter(c) && !isDecimal(c) { 62 | break 63 | } 64 | } 65 | //现在是一个标识符或者关键字了,判断一下是什么 66 | value = string(runecache[:off]) 67 | sid = token.Lookup(value) 68 | return 69 | } else 70 | //数字打头,只可能是一个数 71 | if isDecimal(c) { 72 | n := 0 73 | for { 74 | n = n*10 + int(c) - '0' 75 | s.getCh() 76 | if !isDecimal(c) { 77 | break 78 | } 79 | } 80 | //现在是一个数 81 | num = n 82 | sid = token.NUMBERSYM 83 | return 84 | } else 85 | //可能会成对出现的符号 86 | if maybeDouble(c) { 87 | var opt = [2]rune{c} 88 | var opts string 89 | s.getCh() 90 | if c == '=' { 91 | s.getCh() 92 | opt[1] = '=' 93 | opts = string(opt[:]) 94 | } else { 95 | opts = string(opt[:1]) 96 | } 97 | sid = token.OptParse(opts) 98 | value = opts 99 | return 100 | } else { 101 | //一些可能出现的孤立的符号 102 | switch c { 103 | case '=', '+', '-', '*', '/', '#', '(', ')', ';', '.', ',': 104 | sid = token.OptParse(string(c)) 105 | value = string(c) 106 | default: 107 | //TODO 这里是出现了不应该出现的字符,应该处理错误 108 | log.Printf("这里是出现了不应该出现的字符: %s", string(c)) 109 | } 110 | s.getCh() 111 | return 112 | } 113 | } 114 | 115 | /* 116 | * 读取一个字符 117 | */ 118 | func (s *Scanner) getCh() { 119 | c, end = s.file.ReadRune() 120 | if c == '\n' { 121 | s.line++ 122 | } 123 | //log.Println(string(c)) 124 | } 125 | 126 | /* 127 | * 判断是不是空白符 128 | */ 129 | func isSpace(ch rune) (spcae bool) { 130 | return unicode.IsSpace(ch) 131 | } 132 | 133 | func isLetter(ch rune) bool { 134 | return 'a' <= lower(ch) && lower(ch) <= 'z' && unicode.IsLetter(ch) 135 | } 136 | 137 | func lower(ch rune) rune { return ('a' - 'A') | ch } // returns lower-case ch iff ch is ASCII letter 138 | func isDecimal(ch rune) bool { return '0' <= ch && ch <= '9' } 139 | 140 | func parseNumber(intrune []rune) (n int) { 141 | return 142 | } 143 | 144 | func maybeDouble(ch rune) bool { 145 | return ch == '<' || ch == '>' || ch == ':' 146 | } 147 | -------------------------------------------------------------------------------- /token/token.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @auther: zu1k 3 | * @date: 2019/11/2 4 | */ 5 | package token 6 | 7 | import ( 8 | "strconv" 9 | "unicode" 10 | ) 11 | 12 | // Token is the set of lexical tokens of the Go programming language. 13 | type Token int 14 | 15 | /* 16 | * Sym类型的枚举类型 17 | * The list of tokens. 18 | */ 19 | const ( 20 | BADTOKEN = iota // 无效的单词 21 | 22 | literal_beg 23 | IDENTSYM // 标识符 24 | NUMBERSYM // 数 25 | literal_end 26 | 27 | operator_beg 28 | // Operators 29 | PLUSSYM // + 30 | MINUSYM // - 31 | MULSYM // * 32 | SLASHSYM // / 33 | 34 | relation_optr_beg 35 | EQLSYM // = 36 | NEQSYM // # 37 | LESSYM // < 38 | LEQSYM // <= 39 | GTRSYM // > 40 | GEQSYM // >= 41 | relation_optr_end 42 | 43 | LPARENTSYM // ( 44 | RPARENTSYM // ) 45 | COMMASYM // , 46 | SEMICOLOMSYM // ; 47 | PERIODSYM // . 48 | BECOMESSYM // := 49 | operator_end 50 | 51 | keyword_beg 52 | // Keywords 53 | BEGINSYM // begin 54 | ENDSYM // end 55 | IFSYM // if 56 | ELSESYM // else 57 | THENSYM // then 58 | WHILESYM // while 59 | DOSYM // do 60 | CALLSYM // call 61 | CONSTSYM // const 62 | VARSYM // var 63 | PROCSYM // procedure 64 | ODDSYM // odd 65 | WRITESYM // write 66 | READSYM // read 67 | keyword_end 68 | 69 | EOFSYM // EOF 70 | ) 71 | 72 | var tokens = [...]string{ 73 | CONSTSYM: "const", 74 | VARSYM: "var", 75 | PROCSYM: "procedure", 76 | CALLSYM: "call", 77 | BEGINSYM: "begin", 78 | ENDSYM: "end", 79 | IFSYM: "if", 80 | THENSYM: "then", 81 | ELSESYM: "else", 82 | WHILESYM: "while", 83 | DOSYM: "do", 84 | READSYM: "read", 85 | WRITESYM: "write", 86 | ODDSYM: "odd", 87 | 88 | PLUSSYM: "+", 89 | MINUSYM: "-", 90 | MULSYM: "*", 91 | SLASHSYM: "/", 92 | LPARENTSYM: "(", 93 | RPARENTSYM: ")", 94 | COMMASYM: ",", 95 | PERIODSYM: ".", 96 | 97 | EQLSYM: "=", 98 | LESSYM: "<", 99 | GTRSYM: ">", 100 | NEQSYM: "#", 101 | LEQSYM: "<=", 102 | GEQSYM: ">=", 103 | 104 | SEMICOLOMSYM: ";", 105 | BECOMESSYM: ":=", 106 | 107 | BADTOKEN: "无效字符", 108 | NUMBERSYM: "数字", 109 | IDENTSYM: "变量标识符", 110 | 111 | EOFSYM: "文档已结束", 112 | } 113 | 114 | var Tokens = tokens 115 | 116 | // String returns the string corresponding to the token tok. 117 | // For operators, delimiters, and keywords the string is the actual 118 | // token character sequence (e.g., for the token ADD, the string is 119 | // "+"). For all other tokens the string corresponds to the token 120 | // constant name (e.g. for the token IDENT, the string is "IDENT"). 121 | // 122 | func (tok Token) String() string { 123 | s := "" 124 | if 0 <= tok && tok < Token(len(tokens)) { 125 | s = tokens[tok] 126 | } 127 | if s == "" { 128 | s = "token(" + strconv.Itoa(int(tok)) + ")" 129 | } 130 | return s 131 | } 132 | 133 | var keywords map[string]Token 134 | 135 | func init() { 136 | keywords = make(map[string]Token) 137 | for i := keyword_beg + 1; i < keyword_end; i++ { 138 | keywords[tokens[i]] = Token(i) //TODO ?what? 139 | } 140 | } 141 | 142 | // Lookup maps an identifier to its keyword token or IDENT (if not a keyword). 143 | // 144 | func Lookup(ident string) Token { 145 | if tok, is_keyword := keywords[ident]; is_keyword { 146 | return tok 147 | } 148 | return IDENTSYM 149 | } 150 | 151 | // Predicates 152 | 153 | // IsLiteral returns true for tokens corresponding to identifiers 154 | // and basic type literals; it returns false otherwise. 155 | // 156 | func (tok Token) IsLiteral() bool { return literal_beg < tok && tok < literal_end } 157 | 158 | func (tok Token) IsIdent() bool { return tok == IDENTSYM } 159 | func (tok Token) IsNumber() bool { return tok == NUMBERSYM } 160 | func (tok Token) IsBecome() bool { return tok == BECOMESSYM } 161 | func (tok Token) IsCall() bool { return tok == CALLSYM } 162 | func (tok Token) IsThen() bool { return tok == THENSYM } 163 | func (tok Token) IsSemicolom() bool { return tok == SEMICOLOMSYM } 164 | func (tok Token) IsEnd() bool { return tok == ENDSYM } 165 | func (tok Token) IsDo() bool { return tok == DOSYM } 166 | func (tok Token) IsConst() bool { return tok == CONSTSYM } 167 | func (tok Token) IsComma() bool { return tok == COMMASYM } 168 | func (tok Token) IsVar() bool { return tok == VARSYM } 169 | func (tok Token) IsProc() bool { return tok == PROCSYM } 170 | func (tok Token) IsLparent() bool { return tok == LPARENTSYM } 171 | func (tok Token) IsRparent() bool { return tok == RPARENTSYM } 172 | 173 | // IsOperator returns true for tokens corresponding to operators and 174 | // delimiters; it returns false otherwise. 175 | // 176 | func (tok Token) IsOperator() bool { return operator_beg < tok && tok < operator_end } 177 | 178 | // IsKeyword returns true for tokens corresponding to keywords; 179 | // it returns false otherwise. 180 | // 181 | func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end } 182 | 183 | func (tok Token) IsRelationOpr() bool { return relation_optr_beg < tok && tok < relation_optr_end } 184 | 185 | // IsKeyword reports whether name is a PL/0 keyword, such as "const" or "read". 186 | // 187 | func IsKeyword(name string) bool { 188 | // TODO: opt: use a perfect hash function instead of a global map. 189 | _, ok := keywords[name] 190 | return ok 191 | } 192 | 193 | // IsIdentifier reports whether name is a Go identifier, that is, a non-empty 194 | // string made up of letters, digits, and underscores, where the first character 195 | // is not a digit. Keywords are not identifiers. 196 | // 197 | func IsIdentifier(name string) bool { 198 | for i, c := range name { 199 | if !unicode.IsLetter(c) && (i == 0 || !unicode.IsDigit(c)) { 200 | return false 201 | } 202 | } 203 | return name != "" && !IsKeyword(name) 204 | } 205 | 206 | /* 207 | * PL/0 保留字 - type 208 | */ 209 | var ReversedWordMap = map[string]int{ 210 | "const": CONSTSYM, 211 | "var": VARSYM, 212 | "procedure": PROCSYM, 213 | "call": CALLSYM, 214 | "begin": BEGINSYM, 215 | "end": ENDSYM, 216 | "if": IFSYM, 217 | "then": THENSYM, 218 | "else": ELSESYM, 219 | "while": WHILESYM, 220 | "do": DOSYM, 221 | "read": READSYM, 222 | "write": WRITESYM, 223 | "odd": ODDSYM, 224 | } 225 | 226 | /* 227 | * 特殊字符列表 228 | */ 229 | var SpecialSymbol = [...]string{"+", "-", "*", "/", "(", ")", "=", ",", ".", "<", ">", ";", ":"} 230 | 231 | /* 232 | * 特殊符号 - type 233 | */ 234 | var SpecialSymbolMap = map[string]Token{ 235 | "+": PLUSSYM, 236 | "-": MINUSYM, 237 | "*": MULSYM, 238 | "/": SLASHSYM, 239 | "(": LPARENTSYM, 240 | ")": RPARENTSYM, 241 | "=": EQLSYM, 242 | ",": COMMASYM, 243 | ".": PERIODSYM, 244 | "<": LESSYM, 245 | ">": GTRSYM, 246 | "#": NEQSYM, 247 | "<=": LEQSYM, 248 | ">=": GEQSYM, 249 | ";": SEMICOLOMSYM, 250 | ":=": BECOMESSYM, 251 | } 252 | 253 | func OptParse(opt string) Token { 254 | if tok, is_opt := SpecialSymbolMap[opt]; is_opt { 255 | return tok 256 | } 257 | return BADTOKEN 258 | } 259 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | /** 2 | * @auther: zu1k 3 | * @date: 2019/11/8 4 | */ 5 | package parser 6 | 7 | import ( 8 | "log" 9 | "github.com/zu1k/pl0complier/asm" 10 | "github.com/zu1k/pl0complier/interpret" 11 | "github.com/zu1k/pl0complier/parser/fct" 12 | "github.com/zu1k/pl0complier/parser/ident" 13 | "github.com/zu1k/pl0complier/parser/selectset" 14 | "github.com/zu1k/pl0complier/scanner" 15 | "github.com/zu1k/pl0complier/token" 16 | ) 17 | 18 | type Parser struct { 19 | scanner scanner.Scanner 20 | // Next token 21 | tok token.Token 22 | lit string 23 | num int 24 | 25 | //嵌套层数 26 | lev int 27 | 28 | //中间代码 29 | codes []asm.Asm 30 | cidx int 31 | 32 | //符号表 33 | table []identitem 34 | tx int 35 | id string //最近一次识别出来的ident 36 | 37 | dx int 38 | } 39 | 40 | func (p *Parser) Interpret() { 41 | interpret.Interpret(p.codes) 42 | } 43 | 44 | func (p *Parser) Scan() { 45 | p.tok, p.lit, p.num = p.scanner.Scan() 46 | if p.tok.IsIdent() { 47 | p.id = p.lit 48 | } 49 | } 50 | 51 | func (p *Parser) Init(filepath string) { 52 | p.scanner = scanner.Scanner{} 53 | p.scanner.Init(filepath) 54 | p.Scan() 55 | p.lev = 0 56 | p.codes = make([]asm.Asm, 0) 57 | p.cidx = 0 58 | p.table = make([]identitem, 100) 59 | p.tx = 0 60 | p.dx = 0 61 | } 62 | 63 | const ( 64 | MAX_LEV = 3 65 | ) 66 | 67 | //符号表里的符号 68 | type identitem struct { 69 | name string 70 | kind ident.Ident 71 | lev int 72 | value int //变量,且只可能是无符号整数 73 | addr int //变量,在数据栈里的地址,也就是dx 74 | } 75 | 76 | /* 77 | * 符号表里加一项 78 | * 可能是常亮 a=2 79 | * 可能是变量 var v 80 | * 可能是过程 procedure p 81 | */ 82 | func (p *Parser) enter(k ident.Ident) { 83 | p.tx++ 84 | tmp := identitem{ 85 | name: p.id, 86 | lev: p.lev, 87 | kind: k, 88 | value: p.num, 89 | addr: p.dx, 90 | } 91 | if k == ident.Variable { 92 | //变量需要在数据栈里留空间 93 | p.dx++ 94 | } 95 | p.table[p.tx] = tmp 96 | } 97 | 98 | // 生成中间代码 99 | func (p *Parser) gen(fct fct.Fct, y, z int) { 100 | p.codes = append(p.codes, asm.Asm{ 101 | Fct: fct, 102 | L: y, 103 | A: z, 104 | }) 105 | p.cidx++ 106 | } 107 | 108 | //返回标识符在符号表的位置 109 | //检查标识符是否已经存在 110 | //注意不同lev的不应该相同 111 | func (p *Parser) checkintable(lit string) (id identitem, in bool) { 112 | for _, v := range p.table { 113 | if lit == v.name { 114 | return v, true 115 | } 116 | } 117 | return identitem{}, false 118 | } 119 | 120 | func tokintoks(toks []token.Token, tok token.Token) bool { 121 | for _, v := range toks { 122 | if v == tok { 123 | return true 124 | } 125 | } 126 | return false 127 | } 128 | 129 | /* 130 | * 检查终结符是不是在select集中 131 | */ 132 | func (p *Parser) test(toks1 []token.Token, toks2 []token.Token, t int) { 133 | toks1 = append(toks1, toks2...) 134 | if !tokintoks(toks1, p.tok) { 135 | p.scanner.Error(t) 136 | log.Println("出错的当前单词:" + p.tok.String() + p.lit) 137 | for !tokintoks(toks1, p.tok) && p.tok != token.PERIODSYM { 138 | p.Scan() 139 | } 140 | } 141 | } 142 | 143 | /* 144 | * 出现了const后进入这个过程,理应是常量 145 | * 常量声明 146 | */ 147 | func (p *Parser) constdeclaration() { 148 | if p.tok.IsIdent() { 149 | p.Scan() //看下一个 150 | if p.tok == token.EQLSYM || p.tok == token.BECOMESSYM { //等号或赋值号 151 | if p.tok == token.BECOMESSYM { 152 | //这里是容错处理,报错,但是当做等号使用 153 | error() 154 | } 155 | 156 | p.Scan() 157 | //数字就加入符号表 158 | if p.tok.IsNumber() { 159 | p.enter(ident.Constant) 160 | p.Scan() 161 | } else { 162 | //TODO 等号后面不是数字,类型不可接受 163 | error() 164 | } 165 | } else { 166 | //TODO 不是等号或赋值号,非常量声明部分 167 | error() 168 | } 169 | } else { 170 | error() 171 | } 172 | } 173 | 174 | /* 175 | * 变量声明部分 176 | */ 177 | func (p *Parser) vardeclaration() { 178 | if p.tok.IsIdent() { 179 | p.enter(ident.Variable) 180 | p.Scan() 181 | } else { 182 | //TODO 不是变量名,报错 183 | error() 184 | } 185 | } 186 | 187 | /* 188 | * 因子的产生式 189 | * <因子> → <标识符>|<无符号整数>|(<表达式>) 190 | */ 191 | // Checked 192 | func (p *Parser) factor(toks []token.Token) { 193 | p.test(selectset.FactorSelect, toks, 24) 194 | for tokintoks(selectset.FactorSelect, p.tok) { 195 | switch p.tok { 196 | case token.IDENTSYM: //是标识符 197 | //可能是常量或者变量,需要查询符号表确定位置 198 | it, in := p.checkintable(p.id) 199 | if in { //标识符定义了 200 | switch it.kind { 201 | case ident.Constant: 202 | p.gen(fct.Lit, 0, it.value) 203 | case ident.Variable: 204 | p.gen(fct.Lod, p.lev-it.lev, it.addr) 205 | case ident.Proc: 206 | p.scanner.Error(21) 207 | //TODO 不允许接受过程 208 | } 209 | } else { 210 | //TODO 标识符未定义 211 | p.scanner.Error(11) 212 | } 213 | p.Scan() 214 | case token.NUMBERSYM: //是无符号整数 215 | //TODO 判断数字有没有溢出 216 | p.gen(fct.Lit, 0, p.num) 217 | p.Scan() 218 | case token.LPARENTSYM: //是左括号 219 | p.Scan() 220 | p.expression(append(toks, token.RPARENTSYM)) 221 | //表达式结束后是右括号 222 | if p.tok == token.RPARENTSYM { 223 | p.Scan() 224 | } else { 225 | //TODO 缺少右括号 226 | p.scanner.Error(22) 227 | } 228 | 229 | } 230 | p.test(toks, []token.Token{token.LPARENTSYM}, 23) 231 | } 232 | } 233 | 234 | /* 235 | * 项 236 | * <项> → <因子>{<乘除运算符><因子>} 237 | */ 238 | // Checked 239 | func (p *Parser) term(toks []token.Token) { 240 | ttoks := append(toks, []token.Token{token.MULSYM, token.SLASHSYM}...) 241 | //第一个是因子 242 | p.factor(ttoks) 243 | 244 | //后面可以接无限个 乘除 因子 245 | for p.tok == token.MULSYM || p.tok == token.SLASHSYM { 246 | opt := p.tok 247 | p.Scan() 248 | p.factor(ttoks) 249 | if opt == token.MULSYM { 250 | p.gen(fct.Opr, 0, 4) //乘法 251 | } else { 252 | p.gen(fct.Opr, 0, 5) //除法 253 | } 254 | } 255 | } 256 | 257 | /* 258 | * 表达式 259 | * <表达式> → [+|-]<项>{<加减运算符><项>} 260 | */ 261 | func (p *Parser) expression(toks []token.Token) { 262 | var addop token.Token 263 | ttoks := append(toks, []token.Token{token.PLUSSYM, token.MINUSYM}...) 264 | if p.tok == token.PLUSSYM || p.tok == token.MINUSYM { //处理可能出现的正负号 265 | addop = p.tok 266 | p.Scan() 267 | p.term(ttoks) 268 | if addop == token.MINUSYM { 269 | p.gen(fct.Opr, 0, 1) 270 | } 271 | } else { 272 | p.term(ttoks) 273 | } 274 | //后面可以重复的加减运算和项 275 | for p.tok == token.PLUSSYM || p.tok == token.MINUSYM { 276 | addop = p.tok 277 | p.Scan() 278 | 279 | p.term(ttoks) 280 | if addop == token.PLUSSYM { 281 | p.gen(fct.Opr, 0, 2) //加 282 | } else { 283 | p.gen(fct.Opr, 0, 3) //减 284 | } 285 | } 286 | } 287 | 288 | /* 289 | * 条件 290 | * <条件> → <表达式><关系运算符><表达式>|ood<表达式> 291 | */ 292 | func (p *Parser) condition(toks []token.Token) { 293 | if p.tok == token.ODDSYM { //odd <表达式> 294 | p.Scan() 295 | p.expression(toks) 296 | p.gen(fct.Opr, 0, 6) 297 | } else { 298 | //表达式 299 | p.expression(append(toks, selectset.ExpressionSelect...)) 300 | if p.tok.IsRelationOpr() { //是关系运算符 301 | relop := p.tok 302 | p.Scan() 303 | 304 | p.expression(toks) 305 | switch relop { 306 | case token.EQLSYM: 307 | p.gen(fct.Opr, 0, 8) // = 308 | case token.NEQSYM: 309 | p.gen(fct.Opr, 0, 9) // # 310 | case token.LESSYM: 311 | p.gen(fct.Opr, 0, 10) // < 312 | case token.GTRSYM: 313 | p.gen(fct.Opr, 0, 11) // > 314 | case token.LEQSYM: 315 | p.gen(fct.Opr, 0, 12) // <= 316 | case token.GEQSYM: 317 | p.gen(fct.Opr, 0, 13) // >= 318 | } 319 | } 320 | } 321 | } 322 | 323 | /* 324 | * 语句 325 | * <语句> → <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空> 326 | * <赋值语句> → <标识符>:=<表达式> 327 | * <复合语句> → begin<语句>{ ;<语句>} 328 | * <条件语句> → if<条件>then<语句> 329 | * <过程调用语句> → call<标识符> 330 | * <当型循环语句> → while<条件>do<语句> 331 | * <读语句> → read(<标识符>{ ,<标识符>}) 332 | * <写语句> → write(<标识符>{,<标识符>}) 333 | */ 334 | func (p *Parser) statement(toks []token.Token) { 335 | switch p.tok { 336 | case token.IDENTSYM: //赋值语句 337 | // <赋值语句> → <标识符>:=<表达式> 338 | id, in := p.checkintable(p.id) 339 | if in { 340 | if id.kind.IsConsttant() { 341 | // 不能改变常量的值 342 | p.scanner.Error(25) 343 | in = false 344 | } 345 | } else { 346 | p.scanner.Error(26) 347 | //变量未定义,不能赋值 348 | } 349 | p.Scan() 350 | if p.tok.IsBecome() { 351 | p.Scan() 352 | } else { 353 | //不是赋值语句??! 354 | p.scanner.Error(13) 355 | } 356 | p.expression(toks) 357 | if in { 358 | p.gen(fct.Sto, p.lev-id.lev, id.addr) 359 | } 360 | case token.CALLSYM: //过程调用语句 361 | // call<标识符> 362 | p.Scan() 363 | if p.tok.IsIdent() { 364 | id, in := p.checkintable(p.lit) 365 | if in { 366 | if id.kind.IsProcedure() { 367 | p.gen(fct.Cal, p.lev-id.lev, id.addr) 368 | } else { 369 | // 调用的对象不是一个过程 370 | p.scanner.Error(15) 371 | } 372 | } else { 373 | p.scanner.Error(6) 374 | //未找到调用的过程 375 | } 376 | p.Scan() 377 | } else { 378 | //不是ident, 不是过程调用语句 379 | p.scanner.Error(27) 380 | } 381 | case token.IFSYM: //条件语句 382 | // if<条件>then<语句> 383 | p.Scan() 384 | p.condition(append(toks, []token.Token{token.THENSYM, token.DOSYM}...)) 385 | if p.tok.IsThen() { 386 | p.Scan() 387 | } else { 388 | //TODO if条件之后未找到then 389 | error() 390 | } 391 | cidx := p.cidx //挖坑,false集的 a 需要时then后面的语句? 392 | p.gen(fct.Jpc, 0, 0) 393 | p.statement(toks) 394 | p.codes[cidx].A = p.cidx //false集,也就是else的部分?没有else? 395 | case token.BEGINSYM: // 复合语句 396 | // <复合语句> → begin<语句>{ ;<语句>} 397 | p.Scan() 398 | ttoks := append(toks, []token.Token{token.SEMICOLOMSYM, token.ENDSYM}...) 399 | p.statement(ttoks) 400 | for p.tok.IsSemicolom() { 401 | p.Scan() 402 | p.statement(ttoks) 403 | } 404 | if p.tok.IsEnd() { 405 | p.Scan() 406 | } else { 407 | //TODO 过程没有结束符号 408 | error() 409 | } 410 | case token.WHILESYM: //while循环 411 | // <当型循环语句> → while<条件>do<语句> 412 | cidx1 := p.cidx //判断前面,循环体结束后需要跳过来 413 | p.Scan() 414 | p.condition(append(toks, token.DOSYM)) 415 | cidx2 := p.cidx //退出循环体的地址后面分配好代码后回填 416 | p.gen(fct.Jpc, 0, 0) 417 | if p.tok.IsDo() { 418 | p.Scan() 419 | } else { 420 | //TODO 缺少do,可能忘写了 421 | error() 422 | } 423 | p.statement(toks) 424 | p.gen(fct.Jmp, 0, cidx1) 425 | p.codes[cidx2].A = p.cidx 426 | // * <读语句> → read(<标识符>{ ,<标识符>}) 427 | // * <写语句> → write(<标识符>{,<标识符>}) 428 | case token.READSYM: 429 | p.Scan() 430 | if p.tok.IsLparent() { 431 | p.Scan() 432 | if p.tok.IsIdent() { 433 | //检查变量表,应该是一个已经定义的变量 434 | id, in := p.checkintable(p.lit) 435 | if in { 436 | if !id.kind.IsVariable() { 437 | // 不能改变常量的值 438 | p.scanner.Error(25) 439 | in = false 440 | } 441 | } else { 442 | p.scanner.Error(26) 443 | //变量未定义,不能赋值 444 | } 445 | if in { 446 | p.gen(fct.Opr, 0, 14) //读入数字放栈顶 447 | p.gen(fct.Sto, p.lev-id.lev, id.addr) //从栈顶放到相应位置 448 | } 449 | p.Scan() 450 | } else { 451 | //TODO 应该是一个标识符,但是这里不是 452 | error() 453 | } 454 | } 455 | for p.tok.IsComma() { 456 | p.Scan() 457 | if p.tok.IsIdent() { 458 | //检查变量表,应该是一个已经定义的变量 459 | id, in := p.checkintable(p.lit) 460 | if in { 461 | if id.kind.IsConsttant() { 462 | // 不能改变常量的值 463 | p.scanner.Error(25) 464 | in = false 465 | } 466 | } else { 467 | p.scanner.Error(26) 468 | //变量未定义,不能赋值 469 | } 470 | if in { 471 | p.gen(fct.Opr, 0, 14) //读入数字放栈顶 472 | p.gen(fct.Sto, p.lev-id.lev, id.addr) //从栈顶放到相应位置 473 | } 474 | } 475 | p.Scan() 476 | } 477 | if p.tok.IsRparent() { 478 | p.Scan() 479 | } else { 480 | error() 481 | } 482 | case token.WRITESYM: 483 | p.Scan() 484 | if p.tok.IsLparent() { 485 | p.Scan() 486 | if p.tok.IsIdent() { 487 | //检查变量表,应该是一个已经定义的变量 488 | id, in := p.checkintable(p.id) 489 | if in { 490 | if id.kind.IsProcedure() { 491 | // 不能读过程 492 | p.scanner.Error(28) 493 | in = false 494 | } else if id.kind.IsConsttant() { 495 | p.gen(fct.Lit, 0, id.value) //从相应位置读到栈顶 496 | p.gen(fct.Opr, 0, 15) //从栈顶显示出来 497 | in = false 498 | } 499 | } else { 500 | p.scanner.Error(26) 501 | //变量未定义,不能赋值 502 | } 503 | if in { 504 | p.gen(fct.Lod, p.lev-id.lev, id.addr) //从相应位置读到栈顶 505 | p.gen(fct.Opr, 0, 15) //从栈顶显示出来 506 | } 507 | p.Scan() 508 | } else { 509 | //TODO 应该是一个标识符,但是这里不是 510 | error() 511 | } 512 | } 513 | for p.tok.IsComma() { 514 | p.Scan() 515 | if p.tok.IsIdent() { 516 | //检查变量表,应该是一个已经定义的变量 517 | id, in := p.checkintable(p.lit) 518 | if in { 519 | if id.kind.IsProcedure() { 520 | // 不能改变常量的值 521 | p.scanner.Error(28) 522 | in = false 523 | } else if id.kind.IsConsttant() { 524 | p.gen(fct.Lit, 0, id.value) //从相应位置读到栈顶 525 | p.gen(fct.Opr, 0, 15) //从栈顶显示出来 526 | in = false 527 | } 528 | } else { 529 | p.scanner.Error(26) 530 | //变量未定义,不能赋值 531 | } 532 | if in { 533 | p.gen(fct.Lod, p.lev-id.lev, id.addr) //把数据放到栈顶 534 | p.gen(fct.Opr, 0, 15) //读入数字放栈顶 535 | } 536 | } 537 | p.Scan() 538 | } 539 | if p.tok.IsRparent() { 540 | p.Scan() 541 | } else { 542 | error() 543 | } 544 | } 545 | 546 | p.test(toks, []token.Token{}, 19) 547 | } 548 | 549 | /* 550 | *〈程序〉→〈分程序>. 551 | *〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]〈语句〉 552 | * <常量说明部分> → CONST<常量定义>{ ,<常量定义>}; 553 | * <变量说明部分> → VAR<标识符>{ ,<标识符>}; 554 | * <过和说明部分> → <过程首部><分程度>;{<过程说明部分>} 555 | * <过程首部> → procedure<标识符>; 556 | */ 557 | func (p *Parser) block(toks []token.Token) { 558 | p.dx = 3 559 | tx0 := p.tx 560 | p.table[p.tx].addr = p.cidx 561 | p.gen(fct.Jmp, 0, 0) 562 | 563 | if p.lev > MAX_LEV { 564 | //TODO 嵌套层次太大 565 | error() 566 | } 567 | for { //声明部分 568 | //常量声明部分 569 | // * <常量说明部分> → CONST<常量定义>{ ,<常量定义>}; 570 | // * <常量定义> → <标识符>=<无符号整数> 571 | if p.tok.IsConst() { 572 | p.Scan() 573 | for p.tok.IsIdent() { 574 | //第一个常量声明 575 | p.constdeclaration() 576 | //逗号打头的多个常量的声明 577 | for p.tok.IsComma() { 578 | p.Scan() 579 | p.constdeclaration() 580 | } 581 | //分号结束 582 | if p.tok.IsSemicolom() { 583 | p.Scan() 584 | } else { 585 | //TODO 缺少分号 586 | error() 587 | } 588 | } 589 | } 590 | 591 | //变量声明部分 592 | // * <变量说明部分> → VAR<标识符>{ ,<标识符>}; 593 | if p.tok.IsVar() { 594 | p.Scan() 595 | for p.tok.IsIdent() { 596 | p.vardeclaration() 597 | for p.tok.IsComma() { 598 | p.Scan() 599 | p.vardeclaration() 600 | } 601 | //分号结束 602 | if p.tok.IsSemicolom() { 603 | p.Scan() 604 | } else { 605 | //TODO 缺少分号 606 | error() 607 | } 608 | } 609 | } 610 | 611 | //过程声明部分 612 | // * <过和说明部分> → <过程首部><分程度>;{<过程说明部分>} 613 | // * <过程首部> → procedure<标识符>; 614 | for p.tok.IsProc() { 615 | p.Scan() 616 | if p.tok.IsIdent() { 617 | p.enter(ident.Proc) 618 | p.Scan() 619 | } else { 620 | //TODO proc后面需要紧跟一个标识符 621 | error() 622 | } 623 | if p.tok.IsSemicolom() { 624 | p.Scan() 625 | } else { 626 | //TODO 过程首部里面缺少分号 627 | error() 628 | } 629 | 630 | p.lev++ 631 | tx1 := p.tx 632 | dx1 := p.dx 633 | p.block(append(toks, token.SEMICOLOMSYM)) 634 | p.lev-- 635 | p.tx = tx1 636 | p.dx = dx1 637 | 638 | if p.tok.IsSemicolom() { 639 | p.Scan() 640 | p.test(append(selectset.StatementSelect, []token.Token{token.IDENTSYM, token.PROCSYM}...), toks, 6) 641 | } else { 642 | //TODO 缺少分号 643 | error() 644 | } 645 | } 646 | 647 | p.test(append(selectset.StatementSelect, token.IDENTSYM), selectset.DeclareSelect, 7) 648 | if !tokintoks(selectset.DeclareSelect, p.tok) { 649 | break 650 | } 651 | } 652 | 653 | p.codes[p.table[tx0].addr].A = p.cidx 654 | p.table[tx0].addr = p.cidx 655 | //cx0 := p.cidx 656 | p.gen(fct.Int, 0, p.dx) 657 | p.statement(append(toks, []token.Token{token.SEMICOLOMSYM, token.ENDSYM}...)) 658 | p.gen(fct.Opr, 0, 0) 659 | p.test(toks, []token.Token{}, 8) 660 | } 661 | 662 | func (p *Parser) Start() { 663 | p.block(append(append(selectset.DeclareSelect, selectset.StatementSelect...), token.PERIODSYM)) 664 | log.Printf("结束\n") 665 | } 666 | --------------------------------------------------------------------------------