Better error messages
This commit is contained in:
parent
b247f025d5
commit
17e7453fff
31
assembler.go
31
assembler.go
|
@ -125,7 +125,7 @@ func (t *tokenizer) eat() (byte, error) {
|
||||||
|
|
||||||
if c == '\n' {
|
if c == '\n' {
|
||||||
t.location.line++
|
t.location.line++
|
||||||
t.location.column = 0
|
t.location.column = 1
|
||||||
} else {
|
} else {
|
||||||
t.location.column++
|
t.location.column++
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ 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{
|
||||||
location: location{line: 1, column: 0},
|
location: location{line: 1, column: 1},
|
||||||
tokens: tokens,
|
tokens: tokens,
|
||||||
reader: bufio.NewReader(r),
|
reader: bufio.NewReader(r),
|
||||||
}
|
}
|
||||||
|
@ -218,8 +218,7 @@ func tokenize(r io.Reader, tokens chan<- token) {
|
||||||
if err := t.step(); err == io.EOF {
|
if err := t.step(); err == io.EOF {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.tokens <- token{t.location, fmt.Sprintf("line %d, column %d: %s",
|
t.tokens <- token{t.location, err.Error(), 0, ERROR}
|
||||||
t.location.line, t.location.column, err.Error()), 0, ERROR}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,19 +245,23 @@ func (a *assembler) step() (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add token location information to returned errors
|
mkerr := func(format string, a ...interface{}) error {
|
||||||
|
prefix := fmt.Sprintf("line %d, column %d: ",
|
||||||
|
token.location.line, token.location.column)
|
||||||
|
return errors.New(prefix + fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
switch token.kind {
|
switch token.kind {
|
||||||
case WORD:
|
case WORD:
|
||||||
if _, dup := a.labels[token.value]; dup {
|
if _, dup := a.labels[token.value]; dup {
|
||||||
return false, fmt.Errorf("Duplicate label: %s", token.value)
|
return false, mkerr("duplicate label: %s", token.value)
|
||||||
}
|
}
|
||||||
a.labels[token.value] = len(a.output)
|
a.labels[token.value] = len(a.output)
|
||||||
|
|
||||||
if token, ok = <-a.tokens; !ok {
|
if token, ok = <-a.tokens; !ok {
|
||||||
return false, errors.New("Unexpected end of file")
|
return false, mkerr("unexpected end of file")
|
||||||
}
|
}
|
||||||
if token.kind != INSTRUCTION {
|
if token.kind != INSTRUCTION {
|
||||||
return false, errors.New("Expected instruction name after label")
|
return false, mkerr("expected instruction name after label")
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case INSTRUCTION:
|
case INSTRUCTION:
|
||||||
|
@ -288,17 +291,17 @@ func (a *assembler) step() (bool, error) {
|
||||||
case !ok:
|
case !ok:
|
||||||
case token.kind == NEWLINE:
|
case token.kind == NEWLINE:
|
||||||
case token.kind == ERROR:
|
case token.kind == ERROR:
|
||||||
return false, errors.New(token.value)
|
return false, mkerr("%s", token.value)
|
||||||
default:
|
default:
|
||||||
return false, errors.New("Expected end of line")
|
return false, mkerr("expected end of line")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case NEWLINE:
|
case NEWLINE:
|
||||||
// Ignore empty lines
|
// Ignore empty lines
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
return false, errors.New("Unexpected number")
|
return false, mkerr("unexpected number")
|
||||||
case ERROR:
|
case ERROR:
|
||||||
return false, errors.New(token.value)
|
return false, mkerr("%s", token.value)
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -318,7 +321,7 @@ func Assemble(r io.Reader) (code []int16, err error) {
|
||||||
code = make([]int16, 100)
|
code = make([]int16, 100)
|
||||||
for i, x := range a.output {
|
for i, x := range a.output {
|
||||||
if i >= len(code) {
|
if i >= len(code) {
|
||||||
return nil, errors.New("Program too long")
|
return nil, errors.New("program too long")
|
||||||
}
|
}
|
||||||
n := x.id
|
n := x.id
|
||||||
switch {
|
switch {
|
||||||
|
@ -327,7 +330,7 @@ func Assemble(r io.Reader) (code []int16, err error) {
|
||||||
case len(x.target) != 0:
|
case len(x.target) != 0:
|
||||||
// Resolve targets to code locations
|
// Resolve targets to code locations
|
||||||
if resolved, ok := a.labels[x.target]; !ok {
|
if resolved, ok := a.labels[x.target]; !ok {
|
||||||
return nil, errors.New("Unknown label")
|
return nil, errors.New("unknown label")
|
||||||
} else {
|
} else {
|
||||||
n += resolved
|
n += resolved
|
||||||
}
|
}
|
||||||
|
|
25
main.go
25
main.go
|
@ -7,22 +7,15 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
fmt.Printf("usage: %s file", os.Args[0])
|
fmt.Printf("usage: %s file\n", os.Args[0])
|
||||||
os.Exit(1)
|
} else if file, err := os.Open(os.Args[1]); err != nil {
|
||||||
|
fmt.Printf("Cannot open file: %s\n", err)
|
||||||
|
} else if code, err := Assemble(file); err != nil {
|
||||||
|
fmt.Printf("Assembly failed: %s\n", err)
|
||||||
|
} else if err = Run(code); err != nil {
|
||||||
|
fmt.Printf("Runtime error: %s\n", err)
|
||||||
|
} else {
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
file, err := os.Open(os.Args[1])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Cannot open file: %s", err)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
|
||||||
code, err := Assemble(file)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Assembly failed: %s", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
err = Run(code)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Runtime error: %s", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue