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

Implement assert #86

Merged
merged 3 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (c *compiler) expandExpression(expr []token, line int) ([]token, error) {
if labelOk {
val := (label - line) % int(c.m)
if val < 0 {
output = append(output, token{tokExprOp, "-"}, token{tokNumber, fmt.Sprintf("%d", -val)})
output = append(output, token{tokSymbol, "-"}, token{tokNumber, fmt.Sprintf("%d", -val)})
} else {
output = append(output, token{tokNumber, fmt.Sprintf("%d", val)})
}
Expand All @@ -140,6 +140,43 @@ func (c *compiler) expandExpression(expr []token, line int) ([]token, error) {
return output, nil
}

func (c *compiler) evaluateAssertion(assertText string) error {

assertTokens, err := LexInput(strings.NewReader(assertText))
if err != nil {
return err
}
assertTokens = assertTokens[:len(assertTokens)-1]
exprTokens, err := c.expandExpression(assertTokens, 0)
if err != nil {
return err
}
exprVal, err := evaluateExpression(exprTokens)
if err != nil {
return err
}
if exprVal == 0 {
return fmt.Errorf("assertion '%s' failed", assertText)
}
return nil
}

func (c *compiler) evaluateAssertions() error {
for _, line := range c.lines {
if line.typ != lineComment {
continue
}
if strings.HasPrefix(line.comment, ";assert") {
assertText := line.comment[7:]
err := c.evaluateAssertion(assertText)
if err != nil {
return err
}
}
}
return nil
}

func (c *compiler) assembleLine(in sourceLine) (Instruction, error) {
opLower := strings.ToLower(in.op)
var aMode, bMode AddressMode
Expand Down Expand Up @@ -289,7 +326,7 @@ func (c *compiler) expandFor(start, end int) error {
if j == 1 {
newValue = []token{{tokNumber, "0"}}
} else {
newValue = []token{{tokExprOp, "-"}, {tokNumber, fmt.Sprintf("%d", -(1 - j))}}
newValue = []token{{tokSymbol, "-"}, {tokNumber, fmt.Sprintf("%d", -(1 - j))}}
}
}
thisLine = thisLine.subSymbol(label, newValue)
Expand Down Expand Up @@ -351,9 +388,13 @@ func (c *compiler) expandForLoops() error {
}

func (c *compiler) compile() (WarriorData, error) {

c.loadSymbols()

err := c.evaluateAssertions()
if err != nil {
return WarriorData{}, err
}

graph := buildReferenceGraph(c.values)
cyclic, cyclicKey := graphContainsCycle(graph)
if cyclic {
Expand Down
28 changes: 28 additions & 0 deletions compile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,31 @@ func TestCompileDoubleForLoop(t *testing.T) {
}, w.Code)
assert.Equal(t, 7, w.Start)
}

func TestAssertPositive(t *testing.T) {
config := ConfigNOP94

input := `
;assert CORESIZE == 8000
dat.f $123, $123
`

w, err := CompileWarrior(strings.NewReader(input), config)
require.NoError(t, err)
assert.Equal(t, []Instruction{
{Op: DAT, OpMode: F, AMode: DIRECT, A: 123, BMode: DIRECT, B: 123},
}, w.Code)
}

func TestAssertNegative(t *testing.T) {
config := ConfigNOP94

input := `
;assert CORESIZE == 8192
dat.f $123, $123
`

w, err := CompileWarrior(strings.NewReader(input), config)
require.Error(t, err)
require.Equal(t, WarriorData{}, w)
}
14 changes: 10 additions & 4 deletions expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ func expandExpressions(values map[string][]token, graph map[string][]string) (ma

func combineSigns(expr []token) []token {
out := make([]token, 0, len(expr))
lastOut := token{tokEOF, ""}
lastOut := token{typ: tokEOF}

// please forgive me for this lol
for i := 0; i < len(expr); i++ {
if lastOut.typ == tokExprOp {
if lastOut.typ == tokSymbol {
negativeFound := false
for ; i < len(expr); i++ {
if !(expr[i].val == "-" || expr[i].val == "+") {
Expand All @@ -90,7 +90,7 @@ func combineSigns(expr []token) []token {
}
}
if negativeFound {
out = append(out, token{tokExprOp, "-"})
out = append(out, token{tokSymbol, "-"})
}
if i < len(expr) {
out = append(out, expr[i])
Expand All @@ -111,7 +111,7 @@ func flipDoubleNegatives(expr []token) []token {
for i := 0; i < len(expr); i++ {
if expr[i].val == "-" {
if i+1 < len(expr) && expr[i+1].val == "-" {
out = append(out, token{tokExprOp, "+"})
out = append(out, token{tokSymbol, "+"})
i += 1
continue
}
Expand All @@ -122,6 +122,12 @@ func flipDoubleNegatives(expr []token) []token {
}

func evaluateExpression(expr []token) (int, error) {
for _, tok := range expr {
if tok.typ == tokText || !tok.IsExpressionTerm() {
return 0, fmt.Errorf("unexpected token in expressoin: '%s'", tok)
}
}

combinedExpr := combineSigns(expr)
flippedExpr := flipDoubleNegatives(combinedExpr)

Expand Down
39 changes: 30 additions & 9 deletions expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
func TestExpandExpressions(t *testing.T) {
values := map[string][]token{
"a": {{tokNumber, "1"}},
"c": {{tokText, "a"}, {tokExprOp, "*"}, {tokText, "b"}},
"b": {{tokText, "a"}, {tokExprOp, "+"}, {tokNumber, "2"}},
"c": {{tokText, "a"}, {tokSymbol, "*"}, {tokText, "b"}},
"b": {{tokText, "a"}, {tokSymbol, "+"}, {tokNumber, "2"}},
}
graph := map[string][]string{
"b": {"a"},
Expand All @@ -23,8 +23,8 @@ func TestExpandExpressions(t *testing.T) {
require.NoError(t, err)
require.Equal(t, map[string][]token{
"a": {{tokNumber, "1"}},
"b": {{tokNumber, "1"}, {tokExprOp, "+"}, {tokNumber, "2"}},
"c": {{tokNumber, "1"}, {tokExprOp, "*"}, {tokNumber, "1"}, {tokExprOp, "+"}, {tokNumber, "2"}},
"b": {{tokNumber, "1"}, {tokSymbol, "+"}, {tokNumber, "2"}},
"c": {{tokNumber, "1"}, {tokSymbol, "*"}, {tokNumber, "1"}, {tokSymbol, "+"}, {tokNumber, "2"}},
}, output)
}

Expand All @@ -37,17 +37,17 @@ func TestCombineSigns(t *testing.T) {
input: "1++-2",
output: []token{
{tokNumber, "1"},
{tokExprOp, "+"},
{tokExprOp, "-"},
{tokSymbol, "+"},
{tokSymbol, "-"},
{tokNumber, "2"},
},
},
{
input: "1-+-2",
output: []token{
{tokNumber, "1"},
{tokExprOp, "-"},
{tokExprOp, "-"},
{tokSymbol, "-"},
{tokSymbol, "-"},
{tokNumber, "2"},
},
},
Expand All @@ -73,7 +73,7 @@ func TestFlipDoubleNegatives(t *testing.T) {
input: "1--1",
output: []token{
{tokNumber, "1"},
{tokExprOp, "+"},
{tokSymbol, "+"},
{tokNumber, "1"},
},
},
Expand Down Expand Up @@ -103,13 +103,33 @@ func TestEvaluateExpressionPositive(t *testing.T) {

// handle signs
"1 - -1": 2,

// logic
"1 > 2": 0,
"2 > 1": 1,
"1 < 2": 1,
"2 < 1": 0,
"1 >= 1": 1,
"2 <= 2": 1,
"8000 == 8000": 1,
"8000 == 800": 0,
// hmmm, these need to be fixed
// "1 && 1": 1,
// "1 && 0": 0,
// "1 || 1": 1,
// "1 || 0": 0,
"2 == 1 || 2 == 2": 1,
"2 == 1 || 2 == 3": 0,
}

for input, expected := range testCases {
lexer := newLexer(strings.NewReader(input))
tokens, err := lexer.Tokens()
require.NoError(t, err)

// trim EOF from input
tokens = tokens[:len(tokens)-1]

val, err := evaluateExpression(tokens)
require.NoError(t, err)
assert.Equal(t, expected, val)
Expand All @@ -120,6 +140,7 @@ func TestEvaluateExpressionNegative(t *testing.T) {
cases := []string{
")21",
"2^3",
"2{2",
}

for _, input := range cases {
Expand Down
72 changes: 60 additions & 12 deletions lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ func newLexer(r io.Reader) *lexer {
return lex
}

func LexInput(r io.Reader) ([]token, error) {
lexer := newLexer(r)
return lexer.Tokens()
}

func (l *lexer) next() (rune, bool) {
if l.atEOF {
return '\x00', true
Expand Down Expand Up @@ -155,15 +160,12 @@ func lexInput(l *lexer) lexStateFn {
return lexNumber
}

// handle comments
if l.nextRune == ';' {
return lexComment
}

// dispatch based on next rune, or error
switch l.nextRune {
case '\x00':
l.tokens <- token{tokEOF, ""}
case ';':
return lexComment
case ',':
return l.emitConsume(token{tokComma, ","}, lexInput)
case '(':
Expand All @@ -179,7 +181,7 @@ func lexInput(l *lexer) lexStateFn {
case '/':
fallthrough
case '%':
return l.emitConsume(token{tokExprOp, string(l.nextRune)}, lexInput)
return l.emitConsume(token{tokSymbol, string(l.nextRune)}, lexInput)
case '$':
fallthrough
case '#':
Expand All @@ -189,13 +191,19 @@ func lexInput(l *lexer) lexStateFn {
case '{':
fallthrough
case '}':
fallthrough
return l.emitConsume(token{tokSymbol, string(l.nextRune)}, lexInput)
case '<':
fallthrough
return l.consume(lexLt)
case '>':
return l.emitConsume(token{tokAddressMode, string(l.nextRune)}, lexInput)
return l.consume(lexGt)
case ':':
return l.emitConsume(token{tokColon, ":"}, lexInput)
case '=':
return l.consume(lexEquals)
case '|':
return l.consume(lexPipe)
case '&':
return l.consume(lexAnd)
case '\x1a':
return l.consume(lexInput)
default:
Expand Down Expand Up @@ -276,7 +284,47 @@ func lexComment(l *lexer) lexStateFn {
return lexInput
}

func LexInput(r io.Reader) ([]token, error) {
lexer := newLexer(r)
return lexer.Tokens()
func lexEquals(l *lexer) lexStateFn {
if l.nextRune == '=' {
return l.emitConsume(token{tokSymbol, "=="}, lexInput)
} else {
l.tokens <- token{tokError, fmt.Sprintf("expected '=' after '=', got '%s'", string(l.nextRune))}
return nil
}
}

func lexPipe(l *lexer) lexStateFn {
if l.nextRune == '|' {
return l.emitConsume(token{tokSymbol, "||"}, lexInput)
} else {
l.tokens <- token{tokError, fmt.Sprintf("expected '|' after '|', got '%s'", string(l.nextRune))}
return nil
}
}

func lexAnd(l *lexer) lexStateFn {
if l.nextRune == '&' {
return l.emitConsume(token{tokSymbol, "&&"}, lexInput)
} else {
l.tokens <- token{tokError, fmt.Sprintf("expected '&' after '&', got '%s'", string(l.nextRune))}
return nil
}
}

func lexGt(l *lexer) lexStateFn {
if l.nextRune == '=' {
return l.emitConsume(token{tokSymbol, ">="}, lexInput)
} else {
l.tokens <- token{tokSymbol, ">"}
return lexInput
}
}

func lexLt(l *lexer) lexStateFn {
if l.nextRune == '=' {
return l.emitConsume(token{tokSymbol, "<="}, lexInput)
} else {
l.tokens <- token{tokSymbol, "<"}
return lexInput
}
}
Loading
Loading