General cleanup

This commit is contained in:
Přemysl Eric Janouch 2016-10-20 01:31:15 +02:00
parent b1e11ece87
commit b247f025d5
Signed by: p
GPG Key ID: B715679E3A361BE6
2 changed files with 93 additions and 105 deletions

View File

@ -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 {

View File

@ -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")