diff --git a/compile.go b/compile.go index 31dff6a..5359b7d 100644 --- a/compile.go +++ b/compile.go @@ -75,34 +75,6 @@ func (c *compiler) loadSymbols() { } } -func (c *compiler) reloadReferences() error { - c.labels = make(map[string]int) - - var curPseudoLine int - for _, line := range c.lines { - if line.typ == lineInstruction { - for _, label := range line.labels { - _, ok := c.labels[label] - if ok { - return fmt.Errorf("line %d: label '%s' redefined", line.line, label) - } - c.labels[label] = line.codeLine - curPseudoLine = line.codeLine + 1 - } - } else if line.typ == linePseudoOp { - for _, label := range line.labels { - _, ok := c.labels[label] - if ok { - return fmt.Errorf("line %d: label '%s' redefined", line.line, label) - } - c.labels[label] = curPseudoLine - } - } - } - - return nil -} - func (c *compiler) expandExpression(expr []token, line int) ([]token, error) { input := expr var output []token @@ -286,107 +258,6 @@ func (c *compiler) assembleLine(in sourceLine) (Instruction, error) { }, nil } -func (c *compiler) expandFor(start, end int) error { - output := make([]sourceLine, 0) - codeLineIndex := 0 - - // concatenate lines preceding start - for i := 0; i < start; i++ { - // curLine := c.lines[i] - if c.lines[i].typ == lineInstruction { - // curLine.line = codeLineIndex - codeLineIndex++ - } - output = append(output, c.lines[i]) - } - - // get labels and count from for line - labels := c.lines[start].labels - - countExpr, err := c.expandExpression(c.lines[start].a, start) - if err != nil { - return err - } - count, err := evaluateExpression(countExpr) - if err != nil { - return fmt.Errorf("line %d: invalid for count '%s", c.lines[start].line, c.lines[start].a) - } - - for j := 1; j <= count; j++ { - for i := start + 1; i < end; i++ { - if c.lines[i].typ == lineInstruction { - thisLine := c.lines[i] - - // subtitute symbols in line - for iLabel, label := range labels { - var newValue []token - if iLabel == len(labels)-1 { - newValue = []token{{tokNumber, fmt.Sprintf("%d", j)}} - } else { - if j == 1 { - newValue = []token{{tokNumber, "0"}} - } else { - newValue = []token{{tokSymbol, "-"}, {tokNumber, fmt.Sprintf("%d", -(1 - j))}} - } - } - thisLine = thisLine.subSymbol(label, newValue) - } - - // update codeLine - thisLine.codeLine = codeLineIndex - codeLineIndex++ - - output = append(output, thisLine) - } else { - output = append(output, c.lines[i]) - } - } - - } - - // continue appending lines until the end of the file - for i := end + 1; i < len(c.lines); i++ { - if c.lines[i].typ == lineInstruction { - thisLine := c.lines[i] - thisLine.codeLine = codeLineIndex - codeLineIndex++ - output = append(output, thisLine) - } else { - output = append(output, c.lines[i]) - } - } - - c.lines = output - return c.reloadReferences() -} - -// look for for statements from the bottom up. if one is found it is expanded -// and the function calls itself again. -func (c *compiler) expandForLoops() error { - rofSourceIndex := -1 - for i := len(c.lines) - 1; i >= 0; i-- { - if c.lines[i].typ == linePseudoOp { - lop := strings.ToLower(c.lines[i].op) - if lop == "rof" { - rofSourceIndex = i - } else if lop == "for" { - if rofSourceIndex == -1 { - return fmt.Errorf("line %d: unmatched for", c.lines[i].codeLine) - } - err := c.expandFor(i, rofSourceIndex) - if err != nil { - return err - } - return c.expandForLoops() - } - } - } - if rofSourceIndex != -1 { - return fmt.Errorf("line %d: unmatched rof", c.lines[rofSourceIndex].line) - } - return nil -} - func (c *compiler) compile() (WarriorData, error) { c.loadSymbols() @@ -407,11 +278,6 @@ func (c *compiler) compile() (WarriorData, error) { } c.values = resolved - err = c.expandForLoops() - if err != nil { - return WarriorData{}, err - } - code := make([]Instruction, 0) for _, line := range c.lines { if line.typ != lineInstruction { @@ -451,23 +317,28 @@ func CompileWarrior(r io.Reader, config SimulatorConfig) (WarriorData, error) { return WarriorData{}, err } - // for { - // symbols, forSeen, err := ScanInput(newBufTokenReader(tokens)) - // if err != nil { - // return WarriorData{}, fmt.Errorf("symbol scanner: %s", err) - // } - // if forSeen { - // expandedTokens, err := ForExpand(newBufTokenReader(tokens), symbols) - // if err != nil { - // return WarriorData{}, fmt.Errorf("for: %s", err) - // } - // tokens = expandedTokens - // // oops the embedded for loops are not implemented - // break - // } else { - // break - // } - // } + depth := 0 + for { + symbols, forSeen, err := ScanInput(newBufTokenReader(tokens)) + if err != nil { + return WarriorData{}, fmt.Errorf("symbol scanner: %s", err) + } + if forSeen { + expandedTokens, err := ForExpand(newBufTokenReader(tokens), symbols) + if err != nil { + return WarriorData{}, fmt.Errorf("for: %s", err) + } + tokens = expandedTokens + // oops the embedded for loops are not implemented + // break + } else { + break + } + depth++ + if depth > 12 { + return WarriorData{}, fmt.Errorf("for loop depth exceeded") + } + } parser := newParser(newBufTokenReader(tokens)) sourceLines, metadata, err := parser.parse() diff --git a/compile_test.go b/compile_test.go index 1d6628d..2b2c9c1 100644 --- a/compile_test.go +++ b/compile_test.go @@ -43,12 +43,12 @@ func runWarriorLoadFileTests(t *testing.T, tests []warriorTestCase) { if test.err { assert.Error(t, err, fmt.Sprintf("%s: error should be present", test.filename)) } else { - require.NoError(t, err) + require.NoError(t, err, test.loadFilename) loadInput, err := os.Open(test.loadFilename) - require.NoError(t, err) + require.NoError(t, err, test.loadFilename) defer loadInput.Close() expectedData, err := ParseLoadFile(loadInput, test.config) - require.NoError(t, err) + require.NoError(t, err, test.loadFilename) assert.Equal(t, expectedData.Code, warriorData.Code) } } diff --git a/forexpand.go b/forexpand.go index bf0797f..255be61 100644 --- a/forexpand.go +++ b/forexpand.go @@ -15,12 +15,13 @@ type forExpander struct { atEOF bool // for state fields - forCountLabel string - forLineLabels []string - forCount int - forIndex int - forContent []token - forDepth int + forCountLabel string + forLineLabels []string + forLineLabelsToWrite []string + forCount int + forIndex int + forContent []token + forDepth int symbols map[string][]token @@ -116,7 +117,7 @@ func forLine(f *forExpander) forStateFn { f.labelBuf = make([]string, 0) return forConsumeLabels default: - return f.emitConsume(forConsumeEmitLine) + return forConsumeEmitLine } } @@ -128,7 +129,6 @@ func forLine(f *forExpander) forStateFn { // other: nil func forConsumeLabels(f *forExpander) forStateFn { if f.nextToken.typ == tokText { - if f.nextToken.IsPseudoOp() { opLower := strings.ToLower(f.nextToken.val) if opLower == "for" { @@ -191,6 +191,9 @@ func forConsumeExpression(f *forExpander) forStateFn { case tokNewline: f.next() return forFor + case tokComment: + f.next() + return forConsumeExpression case tokError: return f.emitConsume(nil) case tokEOF: @@ -235,6 +238,11 @@ func forFor(f *forExpander) forStateFn { f.forLineLabels = []string{} } + f.forLineLabelsToWrite = make([]string, len(f.forLineLabels)) + for i, label := range f.forLineLabels { + f.forLineLabelsToWrite[i] = fmt.Sprintf("__for_%s_%s", f.forCountLabel, label) + } + f.forCount = val f.forIndex = 0 // should not be necessary f.forContent = make([]token, 0) @@ -263,7 +271,6 @@ func forInnerLabels(f *forExpander) forStateFn { if f.nextToken.IsPseudoOp() { opLower := strings.ToLower(f.nextToken.val) if opLower == "for" { - fmt.Println("inner:", f.labelBuf, "for") f.forDepth += 1 return forInnerEmitLabels } else if opLower == "rof" { @@ -277,7 +284,12 @@ func forInnerLabels(f *forExpander) forStateFn { return forInnerEmitLabels } } else if f.nextToken.IsOp() { - // write labels and op into emit buffer then emitcomsume line + if f.forLineLabelsToWrite != nil { + for _, label := range f.forLineLabelsToWrite { + f.tokens <- token{tokText, label} + } + f.forLineLabelsToWrite = nil + } return forInnerEmitLabels } else { f.labelBuf = append(f.labelBuf, f.nextToken.val) @@ -300,12 +312,11 @@ func forInnerEmitLabels(f *forExpander) forStateFn { func forInnerEmitConsumeLine(f *forExpander) forStateFn { switch f.nextToken.typ { case tokError: - // TODO + f.tokens <- f.nextToken return nil case tokEOF: return nil case tokNewline: - // f.tokens <- f.nextToken f.forContent = append(f.forContent, f.nextToken) f.next() return forInnerLine @@ -319,21 +330,31 @@ func forInnerEmitConsumeLine(f *forExpander) forStateFn { func forRof(f *forExpander) forStateFn { for f.nextToken.typ != tokNewline { if f.nextToken.typ == tokEOF || f.nextToken.typ == tokError { + f.tokens <- f.nextToken return nil } f.next() } f.next() - fmt.Println(f.forContent) - for i := 1; i <= f.forCount; i++ { for _, tok := range f.forContent { if tok.typ == tokText { if tok.val == f.forCountLabel { f.tokens <- token{tokNumber, fmt.Sprintf("%d", i)} } else { - f.tokens <- tok + found := false + for _, label := range f.forLineLabels { + forLabel := fmt.Sprintf("__for_%s_%s", f.forCountLabel, label) + if tok.val == label { + f.tokens <- token{tokText, forLabel} + found = true + break + } + } + if !found { + f.tokens <- tok + } } } else { f.tokens <- tok