Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expand for loops in preprocessor #90

Merged
merged 12 commits into from
Dec 5, 2024
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
Loading