From 17e7453fff5e60763a1fab88038f1253790e34a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C5=99emysl=20Janouch?= 
Date: Thu, 20 Oct 2016 01:47:07 +0200
Subject: [PATCH] Better error messages
---
 assembler.go | 31 +++++++++++++++++--------------
 main.go      | 27 ++++++++++-----------------
 2 files changed, 27 insertions(+), 31 deletions(-)
diff --git a/assembler.go b/assembler.go
index 5221040..a366929 100644
--- a/assembler.go
+++ b/assembler.go
@@ -125,7 +125,7 @@ func (t *tokenizer) eat() (byte, error) {
 
 	if c == '\n' {
 		t.location.line++
-		t.location.column = 0
+		t.location.column = 1
 	} else {
 		t.location.column++
 	}
@@ -210,7 +210,7 @@ func (t *tokenizer) step() error {
 
 func tokenize(r io.Reader, tokens chan<- token) {
 	t := tokenizer{
-		location: location{line: 1, column: 0},
+		location: location{line: 1, column: 1},
 		tokens:   tokens,
 		reader:   bufio.NewReader(r),
 	}
@@ -218,8 +218,7 @@ func tokenize(r io.Reader, tokens chan<- token) {
 		if err := t.step(); err == io.EOF {
 			break
 		} else if err != nil {
-			t.tokens <- token{t.location, fmt.Sprintf("line %d, column %d: %s",
-				t.location.line, t.location.column, err.Error()), 0, ERROR}
+			t.tokens <- token{t.location, err.Error(), 0, ERROR}
 			break
 		}
 	}
@@ -246,19 +245,23 @@ func (a *assembler) step() (bool, error) {
 		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 {
 	case WORD:
 		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)
 
 		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 {
-			return false, errors.New("Expected instruction name after label")
+			return false, mkerr("expected instruction name after label")
 		}
 		fallthrough
 	case INSTRUCTION:
@@ -288,17 +291,17 @@ func (a *assembler) step() (bool, error) {
 			case !ok:
 			case token.kind == NEWLINE:
 			case token.kind == ERROR:
-				return false, errors.New(token.value)
+				return false, mkerr("%s", token.value)
 			default:
-				return false, errors.New("Expected end of line")
+				return false, mkerr("expected end of line")
 			}
 		}
 	case NEWLINE:
 		// Ignore empty lines
 	case NUMBER:
-		return false, errors.New("Unexpected number")
+		return false, mkerr("unexpected number")
 	case ERROR:
-		return false, errors.New(token.value)
+		return false, mkerr("%s", token.value)
 	}
 	return true, nil
 }
@@ -318,7 +321,7 @@ func Assemble(r io.Reader) (code []int16, err error) {
 	code = make([]int16, 100)
 	for i, x := range a.output {
 		if i >= len(code) {
-			return nil, errors.New("Program too long")
+			return nil, errors.New("program too long")
 		}
 		n := x.id
 		switch {
@@ -327,7 +330,7 @@ func Assemble(r io.Reader) (code []int16, err error) {
 		case len(x.target) != 0:
 			// Resolve targets to code locations
 			if resolved, ok := a.labels[x.target]; !ok {
-				return nil, errors.New("Unknown label")
+				return nil, errors.New("unknown label")
 			} else {
 				n += resolved
 			}
diff --git a/main.go b/main.go
index 6fbd3f9..d08e114 100644
--- a/main.go
+++ b/main.go
@@ -7,22 +7,15 @@ import (
 
 func main() {
 	if len(os.Args) != 2 {
-		fmt.Printf("usage: %s file", os.Args[0])
-		os.Exit(1)
-	}
-	file, err := os.Open(os.Args[1])
-	if err != nil {
-		fmt.Printf("Cannot open file: %s", err)
-		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)
+		fmt.Printf("usage: %s file\n", os.Args[0])
+	} 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)
 	}
+	os.Exit(1)
 }