From b247f025d592f625e178a747bf2eeb7b61fcfa11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C5=99emysl=20Janouch?=
Date: Thu, 20 Oct 2016 01:31:15 +0200
Subject: [PATCH] General cleanup
---
assembler.go | 168 +++++++++++++++++++++++----------------------------
machine.go | 30 ++++-----
2 files changed, 93 insertions(+), 105 deletions(-)
diff --git a/assembler.go b/assembler.go
index 7efce84..5221040 100644
--- a/assembler.go
+++ b/assembler.go
@@ -10,10 +10,11 @@ import (
)
const (
- WORD = iota // [A-Za-z_-]+
- NUMBER // [0-9]+
- NEWLINE // \n
- ERROR // Error
+ WORD = iota // [A-Za-z_-]+
+ INSTRUCTION // Instruction word
+ NUMBER // [0-9]+
+ NEWLINE // \n
+ ERROR // Error
)
type location struct {
@@ -22,9 +23,10 @@ type location struct {
}
type token struct {
- location location // Position of the token
- value string // Text content of the token
- kind int // Kind of the token
+ location location // Position of the token
+ value string // Text content of the token
+ instruction int // INSTRUCTION ID
+ kind int // Kind of the token
}
type tokenizer struct {
@@ -57,19 +59,62 @@ func isWordTail(c byte) bool {
// -----------------------------------------------------------------------------
+const (
+ IHALT = iota * 100
+ IADD
+ ISUBTRACT
+ ISTORE
+ _
+ ILOAD
+ IBRANCH
+ IBRANCH_IF_ZERO
+ IBRANCH_IF_POSITIVE
+ IIO
+)
+
+const (
+ _ = iota
+ IO_INPUT
+ IO_OUTPUT
+)
+
+var instructions = map[string]int{
+ "HLT": IHALT,
+ "COB": IHALT,
+ "ADD": IADD,
+ "SUB": ISUBTRACT,
+ "STA": ISTORE,
+ "LDA": ILOAD,
+ "BRA": IBRANCH,
+ "BRZ": IBRANCH_IF_ZERO,
+ "BRP": IBRANCH_IF_POSITIVE,
+ "INP": IIO + IO_INPUT,
+ "OUT": IIO + IO_OUTPUT,
+ "DAT": 0,
+}
+
+// -----------------------------------------------------------------------------
+
func (t *tokenizer) send(start location, kind int) {
- t.tokens <- token{start, string(t.value), kind}
+ tok := token{start, strings.ToUpper(string(t.value)), 0, kind}
+ if kind == WORD {
+ if instr, found := instructions[tok.value]; found {
+ tok.kind = INSTRUCTION
+ tok.instruction = instr
+ }
+ }
+ t.tokens <- tok
t.value = []byte{}
}
// XXX: the handling could probably be simplified by extending the "byte"
// to also include a special out-of-band value for errors
func (t *tokenizer) peek() (byte, error) {
- buf, err := t.reader.Peek(1)
- if err != nil {
+ if buf, err := t.reader.Peek(1); err != nil {
return '?', err
+ } else {
+ return buf[0], nil
}
- return buf[0], err
}
func (t *tokenizer) eat() (byte, error) {
@@ -170,13 +215,11 @@ func tokenize(r io.Reader, tokens chan<- token) {
reader: bufio.NewReader(r),
}
for {
- err := t.step()
- if err == io.EOF {
+ if err := t.step(); err == io.EOF {
break
- }
- if err != nil {
+ } else if err != nil {
t.tokens <- token{t.location, fmt.Sprintf("line %d, column %d: %s",
- t.location.line, t.location.column, err.Error()), ERROR}
+ t.location.line, t.location.column, err.Error()), 0, ERROR}
break
}
}
@@ -185,44 +228,12 @@ func tokenize(r io.Reader, tokens chan<- token) {
// -----------------------------------------------------------------------------
-const (
- IHALT = iota
- IADD
- ISUBTRACT
- ISTORE
- ILOAD
- _
- IBRANCH
- IBRANCH_IF_ZERO
- IBRANCH_IF_POSITIVE
- IINPUT
- IOUTPUT
- IDATA
-)
-
-var instructions = map[string]int{
- "HLT": IHALT,
- "COB": IHALT,
- "ADD": IADD,
- "SUB": ISUBTRACT,
- "STA": ISTORE,
- "LDA": ILOAD,
- "BRA": IBRANCH,
- "BRZ": IBRANCH_IF_ZERO,
- "BRP": IBRANCH_IF_POSITIVE,
- "INP": IINPUT,
- "OUT": IOUTPUT,
- "DAT": IDATA,
-}
-
type instruction struct {
id int // What instruction this is
target string // Label name
number int // Immediate value
}
-// -----------------------------------------------------------------------------
-
type assembler struct {
tokens chan token // Where tokens come from
output []instruction // The assembled program
@@ -236,37 +247,22 @@ func (a *assembler) step() (bool, error) {
}
// TODO: add token location information to returned errors
-
switch token.kind {
case WORD:
- canonical := strings.ToUpper(token.value)
- instr, found := instructions[canonical]
-
- // Not found in the instruction list
- // Assume it is a label
- if !found {
- if _, dup := a.labels[canonical]; dup {
- return false, fmt.Errorf("Duplicate label: %s", canonical)
- }
- a.labels[canonical] = len(a.output)
-
- token, ok = <-a.tokens
- if !ok {
- return false, errors.New("Unexpected end of file")
- }
- if token.kind != WORD {
- return false, errors.New("Expected word")
- }
-
- // XXX: it might be better to classify this in the lexer
- canonical = strings.ToUpper(token.value)
- instr, found = instructions[canonical]
- }
- if !found {
- return false, fmt.Errorf("Unknown instruction: %s", canonical)
+ if _, dup := a.labels[token.value]; dup {
+ return false, fmt.Errorf("Duplicate label: %s", token.value)
}
+ a.labels[token.value] = len(a.output)
- instrHolder := instruction{id: instr}
+ if token, ok = <-a.tokens; !ok {
+ return false, errors.New("Unexpected end of file")
+ }
+ if token.kind != INSTRUCTION {
+ return false, errors.New("Expected instruction name after label")
+ }
+ fallthrough
+ case INSTRUCTION:
+ instrHolder := instruction{id: token.instruction}
token, ok := <-a.tokens
eol := false
@@ -274,6 +270,7 @@ func (a *assembler) step() (bool, error) {
case token.kind == WORD:
instrHolder.target = strings.ToUpper(token.value)
case token.kind == NUMBER:
+ // TODO: we should check the number
instrHolder.number, _ = strconv.Atoi(token.value)
case token.kind == ERROR:
return false, errors.New(token.value)
@@ -289,9 +286,7 @@ func (a *assembler) step() (bool, error) {
token, ok := <-a.tokens
switch {
case !ok:
- break
case token.kind == NEWLINE:
- break
case token.kind == ERROR:
return false, errors.New(token.value)
default:
@@ -313,11 +308,9 @@ func Assemble(r io.Reader) (code []int16, err error) {
go tokenize(r, a.tokens)
for {
- cont, err := a.step()
- if err != nil {
+ if cont, err := a.step(); err != nil {
return nil, err
- }
- if !cont {
+ } else if !cont {
break
}
}
@@ -327,17 +320,10 @@ func Assemble(r io.Reader) (code []int16, err error) {
if i >= len(code) {
return nil, errors.New("Program too long")
}
- n := x.id * 100
- // XXX: this also stinks
- if x.id == IDATA {
- n = 0
- }
- // XXX: we should be able to handle the strange INP and OUT better
+ n := x.id
switch {
- case x.id == IINPUT:
- n = 901
- case x.id == IOUTPUT:
- n = 902
+ case x.id%100 != 0:
+ // TODO: we could complain that arguments aren't allowed
case len(x.target) != 0:
// Resolve targets to code locations
if resolved, ok := a.labels[x.target]; !ok {
diff --git a/machine.go b/machine.go
index cca2aa2..c43ccae 100644
--- a/machine.go
+++ b/machine.go
@@ -20,39 +20,41 @@ func Run(code []int16) error {
pc := 0
for pc >= 0 && pc < len(code) {
i := code[pc]
+ arg := i % 100
pc++
- switch i / 100 {
+
+ switch i - arg {
case IHALT:
return nil
case IADD:
- accumulator += code[i%100]
+ accumulator += code[arg]
case ISUBTRACT:
- accumulator -= code[i%100]
+ accumulator -= code[arg]
case ISTORE:
- code[i%100] = accumulator
+ code[arg] = accumulator
case ILOAD:
- accumulator = code[i%100]
+ accumulator = code[arg]
case IBRANCH:
pc = int(i % 100)
case IBRANCH_IF_ZERO:
if accumulator == 0 {
- pc = int(i % 100)
+ pc = int(arg)
}
case IBRANCH_IF_POSITIVE:
if accumulator > 0 {
- pc = int(i % 100)
+ pc = int(arg)
}
- default:
- switch i {
- case 901:
+ case IIO:
+ switch arg {
+ case IO_INPUT:
fmt.Printf("Input: ")
fmt.Scanf("%d\n", &accumulator)
- case 902:
+ case IO_OUTPUT:
fmt.Printf("Output: %d\n", accumulator)
- default:
- e := fmt.Sprintf("Unsupported instruction %d at %d", i, pc)
- return errors.New(e)
}
+ default:
+ e := fmt.Sprintf("Unsupported instruction %d at %d", i, pc)
+ return errors.New(e)
}
}
return errors.New("Program counter ran away")