Arbitrary checkpoint

Apparently a gofmt happened in the meantime.
This commit is contained in:
Přemysl Eric Janouch 2016-10-19 20:20:43 +02:00
parent 04639942af
commit 17279e3e6e
Signed by: p
GPG Key ID: B715679E3A361BE6
2 changed files with 84 additions and 56 deletions

View File

@ -1,39 +1,37 @@
package main package main
import ( import (
"errors"
"io"
"fmt"
"bufio" "bufio"
"strings" "errors"
"fmt"
"io"
"strconv" "strconv"
"strings"
) )
const ( const (
WORD = iota // [A-Za-z_-]+ WORD = iota // [A-Za-z_-]+
NUMBER // [0-9]+ NUMBER // [0-9]+
NEWLINE // \n NEWLINE // \n
ERROR // Error ERROR // Error
) )
type location struct { type location struct {
line int line int
column int column int
} }
type token struct { type token struct {
location location // Position of the token location location // Position of the token
value string // Text content of the token value string // Text content of the token
kind int // Kind of the token kind int // Kind of the token
} }
type tokenizer struct { type tokenizer struct {
line int // Current line location location // Current position
column int // Current column value []byte // Current token string
reader *bufio.Reader // Reader
value []byte // Current token string tokens chan<- token // Output token channel
reader *bufio.Reader // Reader
tokens chan<- token // Token channel
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -47,7 +45,9 @@ func isNumber(c byte) bool {
} }
func isWordHead(c byte) bool { func isWordHead(c byte) bool {
if c >= 'a' && c <= 'z' { c -= 32 } if c >= 'a' && c <= 'z' {
c -= 32
}
return c >= 'a' && c <= 'z' || c == '_' return c >= 'a' && c <= 'z' || c == '_'
} }
@ -57,12 +57,13 @@ func isWordTail(c byte) bool {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
func (t *tokenizer) send(kind int) { func (t *tokenizer) send(start location, kind int) {
// FIXME: track the beginning of the token t.tokens <- token{start, string(t.value), kind}
t.tokens <- token{location{t.line, t.column}, string(t.value), kind}
t.value = []byte{} t.value = []byte{}
} }
// XXX: the handling could probably be simplified by extending the "byte"
// to also include a special value for io.EOF and other errors
func (t *tokenizer) peek() (byte, error) { func (t *tokenizer) peek() (byte, error) {
buf, err := t.reader.Peek(1) buf, err := t.reader.Peek(1)
return buf[0], err return buf[0], err
@ -70,13 +71,15 @@ func (t *tokenizer) peek() (byte, error) {
func (t *tokenizer) eat() (byte, error) { func (t *tokenizer) eat() (byte, error) {
c, err := t.reader.ReadByte() c, err := t.reader.ReadByte()
if err != nil { return 0, err } if err != nil {
return 0, err
}
if c == '\n' { if c == '\n' {
t.line++ t.location.line++
t.column = 0 t.location.column = 0
} else { } else {
t.column++ t.location.column++
} }
return c, nil return c, nil
} }
@ -87,63 +90,88 @@ func (t *tokenizer) step() error {
t.value = []byte{} t.value = []byte{}
c, err := t.peek() c, err := t.peek()
if err == io.EOF { return nil } if err == io.EOF {
if err != nil { return err } return nil
}
if err != nil {
return err
}
start := t.location
switch { switch {
case isSpace(c): case isSpace(c):
t.eat() c, err = t.eat()
case c == '\n': case c == '\n':
c, err = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
t.send(NEWLINE) t.send(start, NEWLINE)
case isNumber(c): case isNumber(c):
c, err = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
for { for {
c, err = t.peek() c, err = t.peek()
if err == io.EOF { break } if err == io.EOF {
if err != nil { return err } break
}
if err != nil {
return err
}
if !isNumber(c) { break } if !isNumber(c) {
break
}
c, _ = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
} }
t.send(NUMBER) t.send(start, NUMBER)
case isWordHead(c): case isWordHead(c):
c, err = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
for { for {
c, err = t.peek() c, err = t.peek()
if err == io.EOF { break } if err == io.EOF {
if err != nil { return err } break
}
if err != nil {
return err
}
if !isWordTail(c) { break } if !isWordTail(c) {
break
}
c, _ = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
} }
t.send(WORD) t.send(start, WORD)
case c == '/': case c == '/':
c, err = t.eat() c, err = t.eat()
t.value = append(t.value, c) t.value = append(t.value, c)
c, err = t.peek() c, err = t.peek()
if err != nil { return err } if err != nil {
return err
}
if c != '/' { if c != '/' {
return errors.New("unrecognized input") return errors.New("unrecognized input")
} }
for { for {
c, err = t.peek() c, err = t.peek()
if err == io.EOF { break } if err == io.EOF {
if err != nil { return err } break
}
if err != nil {
return err
}
if c == '\n' { break } if c == '\n' {
break
}
t.eat() t.eat()
} }
default: default:
@ -154,10 +182,9 @@ func (t *tokenizer) step() error {
func tokenize(r io.Reader, tokens chan<- token) { func tokenize(r io.Reader, tokens chan<- token) {
t := tokenizer{ t := tokenizer{
line: 1, location: location{line: 1, column: 0},
column: 0, tokens: tokens,
tokens: tokens, reader: bufio.NewReader(r),
reader: bufio.NewReader(r),
} }
for { for {
err := t.step() err := t.step()
@ -165,9 +192,8 @@ func tokenize(r io.Reader, tokens chan<- token) {
break break
} }
if err != nil { if err != nil {
t.tokens <- token{location{t.line, t.column}, t.tokens <- token{t.location, fmt.Sprintf("line %d, column %d: %s",
fmt.Sprintf("line %d, column %d: %s", t.location.line, t.location.column, err.Error()), ERROR}
t.line, t.column, err.Error()), ERROR}
break break
} }
} }
@ -190,7 +216,7 @@ const (
IDATA IDATA
) )
var instructions = map[string]int { var instructions = map[string]int{
"HLT": IHALT, "HLT": IHALT,
"COB": IHALT, "COB": IHALT,
"ADD": IADD, "ADD": IADD,
@ -206,7 +232,7 @@ var instructions = map[string]int {
} }
type instruction struct { type instruction struct {
id int id int
target string target string
number int number int
} }
@ -221,7 +247,9 @@ type assembler struct {
func (a *assembler) step() (bool, error) { func (a *assembler) step() (bool, error) {
token, ok := <-a.tokens token, ok := <-a.tokens
if !ok { return false, nil } if !ok {
return false, nil
}
// TODO: add token location information to returned errors // TODO: add token location information to returned errors
@ -297,7 +325,7 @@ func Assemble(r io.Reader) (code []int16, err error) {
} }
} }
for _, x := range(a.output) { for _, x := range a.output {
n := x.id * 100 n := x.id * 100
if len(x.target) != 0 { if len(x.target) != 0 {
if resolved, ok := a.labels[x.target]; !ok { if resolved, ok := a.labels[x.target]; !ok {

View File

@ -1,8 +1,8 @@
package main package main
import ( import (
"os"
"fmt" "fmt"
"os"
) )
func main() { func main() {