Skip to content

Commit

Permalink
add logic and comparison operators
Browse files Browse the repository at this point in the history
  • Loading branch information
bobertlo committed Nov 22, 2024
1 parent 1d4e301 commit 9de8622
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 16 deletions.
8 changes: 7 additions & 1 deletion expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ 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++ {
Expand Down Expand Up @@ -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
20 changes: 20 additions & 0 deletions expr_test.go
Original file line number Diff line number Diff line change
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 Down
70 changes: 59 additions & 11 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 @@ -189,13 +191,19 @@ func lexInput(l *lexer) lexStateFn {
case '{':
fallthrough
case '}':
fallthrough
return l.emitConsume(token{tokAddressMode, 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{tokExprOp, "=="}, 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{tokExprOp, "||"}, 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{tokExprOp, "&&"}, 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{tokExprOp, ">="}, lexInput)
} else {
l.tokens <- token{tokAddressMode, ">"}
return lexInput
}
}

func lexLt(l *lexer) lexStateFn {
if l.nextRune == '=' {
return l.emitConsume(token{tokExprOp, "<="}, lexInput)
} else {
l.tokens <- token{tokAddressMode, "<"}
return lexInput
}
}
57 changes: 55 additions & 2 deletions lex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ import (
type lexTestCase struct {
input string
expected []token
err bool
}

func runLexTests(t *testing.T, setName string, testCases []lexTestCase) {
for i, test := range testCases {
l := newLexer(strings.NewReader(test.input))
out, err := l.Tokens()
require.NoError(t, err, fmt.Errorf("%s test %d: error: %s", setName, i, err))
assert.Equal(t, test.expected, out, fmt.Sprintf("%s test %d", setName, i))
if test.err {
require.Error(t, err, fmt.Sprintf("%s test %d", setName, i))
require.Equal(t, out, test.expected, fmt.Sprintf("%s test %d", setName, i))
} else {
require.NoError(t, err, fmt.Errorf("%s test %d: error: %s", setName, i, err))
assert.Equal(t, test.expected, out, fmt.Sprintf("%s test %d", setName, i))
}
}
}

Expand Down Expand Up @@ -122,11 +128,58 @@ func TestLexer(t *testing.T) {
{tokEOF, ""},
},
},
{
input: "for CORESIZE==1\n",
expected: []token{
{tokText, "for"},
{tokText, "CORESIZE"},
{tokExprOp, "=="},
{tokNumber, "1"},
{tokNewline, ""},
{tokEOF, ""},
},
},
{
input: "CORESIZE==8000||CORESIZE==800\n",
expected: []token{
{tokText, "CORESIZE"},
{tokExprOp, "=="},
{tokNumber, "8000"},
{tokExprOp, "||"},
{tokText, "CORESIZE"},
{tokExprOp, "=="},
{tokNumber, "800"},
{tokNewline, ""},
{tokEOF, ""},
},
},
{
input: "1&&2\n",
expected: []token{
{tokNumber, "1"},
{tokExprOp, "&&"},
{tokNumber, "2"},
{tokNewline, ""},
{tokEOF, ""},
},
},
}

runLexTests(t, "TestLexer", testCases)
}

func TestLexNegative(t *testing.T) {
inputs := []string{
"1 =! 0",
}

for _, input := range inputs {
tokens, err := LexInput(strings.NewReader(input))
require.NoError(t, err)
require.Equal(t, tokError, tokens[len(tokens)-1].typ)
}
}

func TestLexEnd(t *testing.T) {
l := newLexer(strings.NewReader("test mov 0, 1\n"))

Expand Down
5 changes: 3 additions & 2 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,14 @@ func parseOp(p *parser) parseStateFn {

p.next()

if p.nextToken.typ == tokAddressMode {
return parseModeA
}
if p.nextToken.IsExpressionTerm() && p.nextToken.val != "*" {
return parseExprA
}

switch p.nextToken.typ {
case tokAddressMode:
return parseModeA
case tokExprOp:
if p.nextToken.val == "*" {
return parseModeA
Expand Down
5 changes: 5 additions & 0 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,10 @@ func (t token) IsExpressionTerm() bool {
if t.typ == tokExprOp || t.typ == tokNumber || t.typ == tokText || t.typ == tokParenL || t.typ == tokParenR {
return true
}
if t.typ == tokAddressMode {
if t.val == ">" || t.val == "<" {
return true
}
}
return false
}

0 comments on commit 9de8622

Please sign in to comment.