gitee.com/quant1x/num@v0.3.2/asm/asm2plan9/asm2plan9s.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2016-2017 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "bufio" 21 "fmt" 22 "io" 23 "log" 24 "os" 25 "strings" 26 ) 27 28 type Instruction struct { 29 instruction string 30 lineno int 31 commentPos int 32 inDefine bool 33 assembled string 34 opcodes []byte 35 } 36 37 type Assembler struct { 38 Prescan bool 39 Instructions []Instruction 40 Compact bool 41 } 42 43 // assemble assembles an array of lines into their 44 // resulting plan9 equivalents 45 func (a *Assembler) assemble(lines []string) ([]string, error) { 46 47 result := make([]string, 0) 48 49 for lineno, line := range lines { 50 startsWithTab := strings.HasPrefix(line, "\t") 51 line := strings.Replace(line, "\t", " ", -1) 52 fields := strings.Split(line, "//") 53 if len(fields) == 2 && (startsAfterLongWordByteSequence(fields[0]) || len(fields[0]) == 65) { 54 55 // test whether string before instruction is terminated with a backslash (so used in a #define) 56 trimmed := strings.TrimSpace(fields[0]) 57 inDefine := len(trimmed) > 0 && string(trimmed[len(trimmed)-1]) == `\` 58 59 // While prescanning collect the instructions 60 if a.Prescan { 61 ins := Instruction{instruction: fields[1], lineno: lineno, commentPos: len(fields[0]), inDefine: inDefine} 62 a.Instructions = append(a.Instructions, ins) 63 continue 64 } 65 66 var ins *Instruction 67 for i := range a.Instructions { 68 if lineno == a.Instructions[i].lineno { 69 ins = &a.Instructions[i] 70 } 71 } 72 if ins == nil { 73 if a.Compact { 74 continue 75 } 76 panic("failed to find entry with correct line number") 77 } 78 if startsWithTab { 79 ins.assembled = strings.Replace(ins.assembled, " ", "\t", 1) 80 } 81 result = append(result, ins.assembled) 82 } else if !a.Prescan { 83 if startsWithTab { 84 line = strings.Replace(line, " ", "\t", 1) 85 } 86 result = append(result, line) 87 } 88 } 89 90 return result, nil 91 } 92 93 // startsAfterLongWordByteSequence determines if an assembly instruction 94 // starts on a position after a combination of LONG, WORD, BYTE sequences 95 func startsAfterLongWordByteSequence(prefix string) bool { 96 97 if len(strings.TrimSpace(prefix)) != 0 && !strings.HasPrefix(prefix, " LONG $0x") && 98 !strings.HasPrefix(prefix, " WORD $0x") && !strings.HasPrefix(prefix, " BYTE $0x") { 99 return false 100 } 101 102 length := 4 + len(prefix) + 1 103 104 for objcodes := 3; objcodes <= 8; objcodes++ { 105 106 ls, ws, bs := 0, 0, 0 107 108 oc := objcodes 109 110 for ; oc >= 4; oc -= 4 { 111 ls++ 112 } 113 if oc >= 2 { 114 ws++ 115 oc -= 2 116 } 117 if oc == 1 { 118 bs++ 119 } 120 size := 4 + ls*(len("LONG $0x")+8) + ws*(len("WORD $0x")+4) + bs*(len("BYTE $0x")+2) + (ls+ws+bs-1)*len("; ") 121 122 if length == size+6 { // comment starts after a space 123 return true 124 } 125 } 126 return false 127 } 128 129 // combineLines shortens the output by combining consecutive lines into a larger list of opcodes 130 func (a *Assembler) combineLines() { 131 startIndex, startLine, opcodes := -1, -1, make([]byte, 0, 1024) 132 combined := make([]Instruction, 0, 100) 133 for i, ins := range a.Instructions { 134 if startIndex == -1 { 135 startIndex, startLine = i, ins.lineno 136 } 137 if ins.lineno != startLine+(i-startIndex) { // we have found a non-consecutive line 138 combiAssem, _ := toPlan9s(opcodes, "", 0, false) 139 combiIns := Instruction{assembled: combiAssem, lineno: startLine, inDefine: false} 140 141 combined = append(combined, combiIns) 142 opcodes = opcodes[:0] 143 startIndex, startLine = i, ins.lineno 144 } 145 opcodes = append(opcodes, ins.opcodes...) 146 } 147 if len(opcodes) > 0 { 148 combiAssem, _ := toPlan9s(opcodes, "", 0, false) 149 ins := Instruction{assembled: combiAssem, lineno: startLine, inDefine: false} 150 151 combined = append(combined, ins) 152 } 153 154 a.Instructions = combined 155 } 156 157 // readLines reads a whole file into memory 158 // and returns a slice of its lines. 159 func readLines(path string, in io.Reader) ([]string, error) { 160 if in == nil { 161 file, err := os.Open(path) 162 if err != nil { 163 return nil, err 164 } 165 defer file.Close() 166 in = file 167 } 168 169 var lines []string 170 scanner := bufio.NewScanner(in) 171 for scanner.Scan() { 172 lines = append(lines, scanner.Text()) 173 } 174 return lines, scanner.Err() 175 } 176 177 // writeLines writes the lines to the given file. 178 func writeLines(lines []string, path string, out io.Writer) error { 179 if path != "" { 180 file, err := os.Create(path) 181 if err != nil { 182 return err 183 } 184 defer file.Close() 185 out = file 186 } 187 188 w := bufio.NewWriter(out) 189 for _, line := range lines { 190 fmt.Fprintln(w, line) 191 } 192 return w.Flush() 193 } 194 195 func assemble(lines []string, compact bool) (result []string, err error) { 196 197 // TODO: Make compaction configurable 198 a := Assembler{Prescan: true, Compact: compact} 199 200 _, err = a.assemble(lines) 201 if err != nil { 202 return result, err 203 } 204 205 err = as(a.Instructions) 206 if err != nil { 207 return result, err 208 } 209 210 if a.Compact { 211 a.combineLines() 212 } 213 214 a.Prescan = false 215 result, err = a.assemble(lines) 216 if err != nil { 217 return result, err 218 } 219 220 return result, nil 221 } 222 223 func main() { 224 225 file := "" 226 if len(os.Args) >= 2 { 227 file = os.Args[1] 228 } 229 230 var lines []string 231 var err error 232 if len(file) > 0 { 233 fmt.Println("Processing file", file) 234 lines, err = readLines(file, nil) 235 } else { 236 lines, err = readLines("", os.Stdin) 237 } 238 if err != nil { 239 log.Fatalf("readLines: %s", err) 240 } 241 242 result, err := assemble(lines, false) 243 if err != nil { 244 fmt.Print(err) 245 os.Exit(-1) 246 } 247 248 err = writeLines(result, file, os.Stdout) 249 if err != nil { 250 log.Fatalf("writeLines: %s", err) 251 } 252 }