2023-12-09 03:12:45 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type lexer struct {
|
|
|
|
r *bufio.Reader
|
|
|
|
lineno int
|
|
|
|
|
|
|
|
peek Token
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLexer(src io.Reader) *lexer {
|
|
|
|
return &lexer{
|
|
|
|
r: bufio.NewReader(src),
|
|
|
|
lineno: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lexer) Peek() (Token, error) {
|
|
|
|
tok, err := l.Next()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
l.peek = tok
|
|
|
|
return tok, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *lexer) Next() (Token, error) {
|
|
|
|
if l.peek != nil {
|
|
|
|
ret := l.peek
|
|
|
|
l.peek = nil
|
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
l.lineno++
|
|
|
|
line, err := l.r.ReadString('\n')
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Strip leading spaces
|
|
|
|
line = strings.TrimLeft(line, " \t\r\n")
|
|
|
|
|
|
|
|
// Strip trailing line-comments (;)
|
|
|
|
line, _, _ = strings.Cut(line, `;`)
|
|
|
|
|
|
|
|
if len(line) == 0 {
|
|
|
|
// This line only contained comments
|
|
|
|
// Continue to the next line
|
|
|
|
return l.Next()
|
|
|
|
}
|
|
|
|
|
|
|
|
fields := strings.Fields(line)
|
|
|
|
// FIXME commas!?
|
|
|
|
|
|
|
|
switch strings.ToLower(fields[0]) {
|
|
|
|
case "section":
|
|
|
|
return SectionToken{fields[1]}, nil
|
|
|
|
|
|
|
|
case "global":
|
|
|
|
return LabelToken{strings.TrimRight(fields[1], `:`), true}, nil
|
|
|
|
|
|
|
|
case "mov":
|
|
|
|
for i, _ := range fields {
|
|
|
|
fields[i] = strings.TrimRight(fields[i], `,`)
|
|
|
|
}
|
|
|
|
return MovInstrToken{fields[1:]}, nil
|
|
|
|
|
|
|
|
case "syscall":
|
|
|
|
return SyscallInstrToken{}, nil
|
|
|
|
|
2023-12-11 00:14:13 +00:00
|
|
|
case "ret":
|
|
|
|
return RetInstrToken{}, nil
|
|
|
|
|
2023-12-09 03:12:45 +00:00
|
|
|
default:
|
|
|
|
// If the field ends with `:`, it's a (local) label
|
|
|
|
if strings.HasSuffix(fields[0], `:`) {
|
|
|
|
return LabelToken{strings.TrimRight(fields[0], `:`), false}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the field starts with `$`, it's a "variable"
|
|
|
|
if strings.HasPrefix(fields[0], `$`) {
|
|
|
|
// 1: =
|
|
|
|
if fields[1] != `=` {
|
|
|
|
return nil, fmt.Errorf("Invalid syntax at line %d (expected = in declaration)", l.lineno)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2: sizeclass
|
|
|
|
// 3+++: literal initializer
|
|
|
|
return DataVariableInstrToken{
|
|
|
|
VarName: fields[0][1:],
|
|
|
|
Sizeclass: fields[2],
|
|
|
|
Value: strings.Join(fields[3:], " "), // FIXME consecutive spaces are ruined
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("Unknown token '%s' at line %d", fields[0], l.lineno)
|
|
|
|
}
|