Skip to content

Commit

Permalink
expand for loops in preprocessor (#90)
Browse files Browse the repository at this point in the history
* sybol_scanner: stop and report forSeen on for input

* load: accept slt # b-field in 88 mode

* implement forExpander with enought code to pass-through tokens

* finish for implementation, remove old one
  • Loading branch information
bobertlo authored Dec 5, 2024
1 parent 6f9d992 commit be7a66e
Show file tree
Hide file tree
Showing 12 changed files with 611 additions and 189 deletions.
161 changes: 22 additions & 139 deletions compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand All @@ -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 {
Expand Down Expand Up @@ -451,11 +317,28 @@ func CompileWarrior(r io.Reader, config SimulatorConfig) (WarriorData, error) {
return WarriorData{}, err
}

// scanner := newSymbolScanner(newBufTokenReader(tokens))
// _, err = scanner.ScanInput()
// if err != nil {
// return WarriorData{}, fmt.Errorf("symbol scanner: %s", err)
// }
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()
Expand Down
6 changes: 3 additions & 3 deletions compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
28 changes: 28 additions & 0 deletions expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ import (
"strconv"
)

func ExpandAndEvaluate(expr []token, symbols map[string][]token) (int, error) {
graph := buildReferenceGraph(symbols)

cyclic, key := graphContainsCycle(graph)
if cyclic {
return 0, fmt.Errorf("symbol graph contains cycles: %s", key)
}

resolved, err := expandExpressions(symbols, graph)
if err != nil {
return 0, err
}

expanded := make([]token, 0)
for _, tok := range expr {
if tok.typ == tokText {
symVal, ok := resolved[tok.val]
if ok {
expanded = append(expanded, symVal...)
continue
}
}
expanded = append(expanded, tok)
}

return evaluateExpression(expanded)
}

func expandValue(key string, values, resolved map[string][]token, graph map[string][]string) ([]token, error) {
// load key value or error
value, valOk := values[key]
Expand Down
Loading

0 comments on commit be7a66e

Please sign in to comment.