~madcapjake/rhi

c912700b2203de7ade39c9d96f5b1345b996c8ba — Jake Russo 1 year, 8 months ago 0eaf7eb
Integrate object system into chunk execution
M internal/generator/visitor.go => internal/generator/visitor.go +7 -7
@@ 104,7 104,7 @@ func (v *RhumbVisitor) VisitLabelLiteral(ctx *P.LabelLiteralContext) interface{}
	logger.Println("label:", text)
	v.vm.WriteCodeToMain(
		ctx.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		word.FromAddress(ra.Id()),
		vm.NewLocalRequest,
	)
	return v.VisitChildren(ctx)


@@ 127,7 127,7 @@ func (v *RhumbVisitor) VisitAssignment(ctx *P.AssignmentContext) interface{} {

		v.vm.WriteCodeToMain(
			addr.GetLine(),
			word.FromAddress(int(ra.Id())),
			word.FromAddress(ra.Id()),
			vm.NewLocalRequest,
		)
	} else {


@@ 139,7 139,7 @@ func (v *RhumbVisitor) VisitAssignment(ctx *P.AssignmentContext) interface{} {
		ra = vm.NewRuneArray(&v.vm, word.FromAddress(0), []rune(text)...)
		v.vm.WriteCodeToMain(
			addrRef.GetStart().GetLine(),
			word.FromAddress(int(ra.Id())),
			word.FromAddress(ra.Id()),
			vm.NewLocalRequest,
		)
	}


@@ 155,7 155,7 @@ func (v *RhumbVisitor) VisitAssignment(ctx *P.AssignmentContext) interface{} {
	)
	v.vm.WriteCodeToMain(
		op.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		word.FromAddress(ra.Id()),
		vm.NewOuterRequest,
	)
	return nil


@@ 240,7 240,7 @@ func (v *RhumbVisitor) VisitMultiplicative(ctx *P.MultiplicativeContext) interfa

	v.vm.WriteCodeToMain(
		mulOp.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		word.FromAddress(ra.Id()),
		vm.NewOuterRequest, // FIXME: re-implement as NewInnerRequest
	)
	return nil


@@ 264,7 264,7 @@ func (v *RhumbVisitor) VisitAdditive(ctx *P.AdditiveContext) interface{} {

	v.vm.WriteCodeToMain(
		addOp.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		word.FromAddress(ra.Id()),
		vm.NewOuterRequest, // FIXME: re-implement as NewInnerRequest
	)
	return nil


@@ 321,7 321,7 @@ func (v *RhumbVisitor) VisitPower(ctx *P.PowerContext) interface{} {
	}
	v.vm.WriteCodeToMain(
		powOp.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		word.FromAddress(ra.Id()),
		vm.NewOuterRequest, // FIXME: re-implement as NewInnerRequest
	)
	return nil

M internal/vm/chunk.go => internal/vm/chunk.go +18 -19
@@ 26,8 26,8 @@ func NewChunk(vm *VirtualMachine) Chunk {
		wa WordArray    = NewWordArray(vm, word.FromAddress(0))
	)
	ch[0] = word.Word(word.CMP_UNIT)
	ch[1] = word.FromAddress(int(ca.id))
	ch[2] = word.FromAddress(int(wa.id))
	ch[1] = word.FromAddress(ca.id)
	ch[2] = word.FromAddress(wa.id)
	id, err := vm.ReAllocate(ch[:]...)
	if err != nil {
		panic("failed to reallocate onto heap")


@@ 59,25 59,26 @@ func (ch *Chunk) WriteCode(vm *VirtualMachine, line int, codes []Code) {
}

func (ch Chunk) SetInstructionsAddress(vm *VirtualMachine, addr uint64) {
	vm.heap[ch.id+chunk_ca_offset] = word.FromAddress(int(addr))
	vm.heap[ch.id+chunk_ca_offset] = word.FromAddress(addr)
}

func (ch Chunk) SetLiteralsAddress(vm *VirtualMachine, addr uint64) {
	vm.heap[ch.id+chunk_wa_offset] = word.FromAddress(int(addr))
	vm.heap[ch.id+chunk_wa_offset] = word.FromAddress(addr)
}

func (ch *Chunk) AddLiteral(vm *VirtualMachine, lit word.Word) (uint64, error) {
	literals := ch.ReviveLits(vm)
	existingIndex, err := literals.IndexOf(vm, lit)
	if err != nil {
		id, err := literals.Append(vm, lit)
		waID, err := literals.Append(vm, lit)
		if err != nil {
			panic("literals append failed")
		}
		if id != ch.Literals(vm).AsAddr() {
			ch.SetLiteralsAddress(vm, id)
		if waID != ch.Literals(vm).AsAddr() {
			ch.SetLiteralsAddress(vm, waID)
		}
		return id, err
		newLastID := uint64(literals.Length(vm)) - 1
		return newLastID, err
	} else {
		return uint64(existingIndex), nil
	}


@@ 131,12 132,8 @@ func (ch Chunk) Execute(vm *VirtualMachine) {
	}
}

func (ch Chunk) execTagIndex(vm *VirtualMachine, b byte, idx int) {
	var (
		code     Code  = Code(b)
		tag      uint8 = code.Tag()
		literals       = ch.ReviveLits(vm)
	)
func (ch Chunk) execTagIndex(vm *VirtualMachine, tag byte, idx int) {
	literals := ch.ReviveLits(vm)
	fmt.Println("Executing chunk tag:", tag)
	switch tag {
	case TAG_VALUE_LITERAL:


@@ 149,10 146,12 @@ func (ch Chunk) execTagIndex(vm *VirtualMachine, b byte, idx int) {
		vm.SubmitUnderRequest(literals.Get(vm, idx))
	case TAG_OUTER_REQUEST:
		vm.SubmitOuterRequest(literals.Get(vm, idx))
		// case TAG_EVENT_REQUEST:
		// 	vm.SubmitEventRequest(literals.Get(idx))
		// case TAG_REPLY_REQUEST:
		// 	vm.SubmitReplyRequest(literals.Get(idx))
	// case TAG_EVENT_REQUEST:
	// 	vm.SubmitEventRequest(literals.Get(idx))
	// case TAG_REPLY_REQUEST:
	// 	vm.SubmitReplyRequest(literals.Get(idx))
	default:
		panic("not a valid tag")
	}
}



@@ 188,7 187,7 @@ func (ch Chunk) DisassembleCode(vm *VirtualMachine, currentLine, currentOffset i
			}
		default:
			idx := uint32(i + int(Code(buf[o]).Index()))
			fmt.Printf("%s %d '%v'\n",
			fmt.Printf("%s %d %v\n",
				Code(buf[o]).String(),
				idx,
				ch.ReviveLits(vm).Get(vm, int(idx)).Debug(),

M internal/vm/code_array.go => internal/vm/code_array.go +28 -22
@@ 119,27 119,33 @@ func (ca *CodeArray) SetCodes(
	cs ...Code,
) {
	var (
		origLen  uint64 = uint64(ca.Length(vm))
		newLen   uint32 = uint32(len(cs))
		byteSz   uint32 = 8
		codeID   int
		code     Code
		initSz   uint32
		bs       []byte      = make([]byte, 0, byteSz)
		ws       []word.Word = make([]word.Word, 0, newLen/8)
		lastWord word.Word   = vm.heap[ca.id+uint64(ca.Size(vm))-1]
		origLen    uint64 = uint64(ca.Length(vm))
		origSz     uint32 = uint32(ca.Size(vm))
		newLen     uint32 = uint32(len(cs))
		byteSz     uint32 = 8
		codeID     int
		code       Code
		initSz     uint32
		bs         []byte      = make([]byte, 0, byteSz)
		ws         []word.Word = make([]word.Word, 0, newLen/8)
		lastWordID uint64      = ca.id + uint64(ca.Size(vm)) - 1
	)
	if newLine {
		ws = append(ws, word.Sentinel())
	} else {
		if ca.lastWordVacancy(vm) {
			ca.getCodesFromWord(vm, lastWord, &bs)
			ca.getCodesFromWord(vm, vm.heap[lastWordID], &bs)
			vm.free[lastWordID] = true
			origSz--
			initSz = uint32(len(bs))
			for codeID, code = range cs {
			tempCS := make([]Code, len(cs))
			copy(tempCS, cs)
			for codeID, code = range tempCS {
				bs = append(bs, byte(code))
				cs = cs[codeID+1:] // shift from main cs var
				if initSz+uint32(codeID)+1 == byteSz {
					vm.heap[ca.id+ca.Size(vm)] = word.Word(
						binary.BigEndian.Uint64(bs))
					ws = append(ws,
						word.Word(binary.BigEndian.Uint64(bs)))
					bs = make([]byte, 0, byteSz)
					break
				}


@@ 160,15 166,16 @@ func (ca *CodeArray) SetCodes(
		for range bs[len(bs):byteSz] {
			bs = append(bs, 0x0)
		}
		ws = append(ws, word.Word(binary.BigEndian.Uint64(bs)))
		bsWord := word.Word(binary.BigEndian.Uint64(bs))
		ws = append(ws, bsWord)
	}
	wordsLen := uint32(len(ws))
	finalLength := uint32(origLen) + wordsLen
	ca.SetSize(vm, finalLength+4)
	ca.SetLength(vm, wordsLen)
	finalLength := uint32(origLen) + newLen
	ca.SetSize(vm, origSz+wordsLen)
	ca.SetLength(vm, finalLength)
	newId, _ := vm.Allocate(
		int(ca.id),
		int(origLen)+int(code_arr_offset),
		int(origSz),
		ws...,
	)
	if newId != ca.id {


@@ 179,9 186,8 @@ func (ca *CodeArray) SetCodes(

func (ca *CodeArray) lastWordVacancy(vm *VirtualMachine) bool {
	var (
		lastIndex uint64    = uint64(ca.Length(vm) - 1)
		id        uint64    = ca.id + code_arr_offset + lastIndex
		word      word.Word = vm.heap[id]
		lastIndex uint64    = uint64(ca.Size(vm) - 1)
		word      word.Word = vm.heap[ca.id+lastIndex]
	)
	if word.IsSentinel() {
		return false


@@ 230,7 236,7 @@ func (ca CodeArray) getWordCodeCount(vm *VirtualMachine, word word.Word) (count 

func (ca CodeArray) GetCodes(vm *VirtualMachine) []byte {
	var (
		cwLen uint64 = uint64(ca.Length(vm))
		cwLen uint64 = uint64(ca.Size(vm))
		buf   []byte = make([]byte, 0, cwLen*8)
	)
	for wIndex := code_arr_offset; wIndex < cwLen; wIndex++ {

M internal/vm/primitives.go => internal/vm/primitives.go +1 -1
@@ 90,7 90,7 @@ func (vm *VirtualMachine) expTwoInts() {

// New scope and add a sentinel to the stack
func (vm *VirtualMachine) beginRoutine() {
	vm.scope = append(vm.scope, make(map[string]int))
	vm.scope = append(vm.scope, make(map[string]uint64))
	vm.stack = append(vm.stack, word.Sentinel())
}


M internal/vm/rune_array.go => internal/vm/rune_array.go +25 -23
@@ 47,35 47,33 @@ func NewRuneArray(
) RuneArray {
	var (
		runesLen  uint32      = uint32(len(runes))
		runesSize uint32      = uint32(code_arr_offset) + runesLen
		raWords   []word.Word = make([]word.Word, 0, runesSize)
		runesRem  uint32      = uint32(runesLen % 2)
		runesCap  uint32      = uint32(code_arr_offset) + (runesLen / 2) + runesRem
		raWords   []word.Word = make([]word.Word, 0, runesCap)
		runeBytes []byte
		wordBytes []byte
		offset    uint32
	)
	raWords = append(raWords,
		/* Mark:   */ word.Word(word.RUNE_ARR),
		/* Legend: */ legAddr,
		/* Size:   */ word.FromInt(runesSize),
		/* Size:   */ word.FromInt(runesCap),
		/* Length: */ word.FromInt(runesLen),
	)
	wordBytes = make([]byte, 8)
	for i, r := range runes {
		offset = uint32(i) % 2
		runeBytes = make([]byte, 4)
		binary.LittleEndian.PutUint32(runeBytes, uint32(r))
		if offset == 0 {
		if i%2 == 0 {
			copy(wordBytes, runeBytes)
			if i == len(runes)-1 {
				raWords = append(raWords,
					word.Word(binary.LittleEndian.Uint64(wordBytes)))
				newWord := word.Word(binary.BigEndian.Uint64(wordBytes))
				raWords = append(raWords, word.Word(newWord))
			}
		} else {
			for i, rb := range runeBytes {
				wordBytes[i+4] = rb
			}
			raWords = append(raWords,
				word.Word(binary.LittleEndian.Uint64(wordBytes)))
			copy(wordBytes[4:], runeBytes)
			newWord := word.Word(binary.BigEndian.Uint64(wordBytes))
			raWords = append(raWords, newWord)
			wordBytes = make([]byte, 8)
		}

	}


@@ 91,21 89,20 @@ func NewRuneArray(

// }

func ReviveRuneArray(vm *VirtualMachine, addr word.Word) RuneArray {
	i := addr.AsAddr()
	mark := vm.heap[i]
func ReviveRuneArray(vm *VirtualMachine, addr uint64) RuneArray {
	mark := vm.heap[addr]
	if !(mark.IsRuneArrayMark()) {
		panic("not a rune array mark")
	}
	legend := vm.heap[i+rune_lgd_offset]
	legend := vm.heap[addr+rune_lgd_offset]
	if !(legend.IsAddress()) {
		panic("rune array legend word is not an address")
	}
	size := vm.heap[i+rune_sze_offset]
	size := vm.heap[addr+rune_sze_offset]
	if !(size.IsInteger()) {
		panic("rune array object size word is not an integer")
	}
	return RuneArray{i}
	return RuneArray{addr}
}

func (ra RuneArray) Id() uint64 { return ra.id }


@@ 120,12 117,17 @@ func (ra RuneArray) Length(vm *VirtualMachine) uint32 {
	return vm.heap[ra.id+rune_len_offset].AsInt()
}
func (ra RuneArray) Runes(vm *VirtualMachine) []rune {
	buf := make([]rune, 0, ra.Length(vm))
	for i := range vm.heap[ra.id+rune_arr_offset:] {
	raLen := uint64(ra.Length(vm))
	buf := make([]rune, 0, raLen)
	start := ra.id + rune_arr_offset
	for i := range vm.heap[start : ra.id+ra.Size(vm)] {
		bytes := make([]byte, 8)
		binary.LittleEndian.PutUint64(bytes, uint64(vm.heap[ra.id+uint64(i)]))
		binary.BigEndian.PutUint64(bytes, uint64(vm.heap[start+uint64(i)]))
		buf = append(buf, rune(binary.LittleEndian.Uint32(bytes[:4])))
		buf = append(buf, rune(binary.LittleEndian.Uint32(bytes[4:])))
		secondRune := rune(binary.LittleEndian.Uint32(bytes[4:]))
		if secondRune != 0 {
			buf = append(buf, secondRune)
		}
	}
	return buf
}

M internal/vm/vm.go => internal/vm/vm.go +82 -33
@@ 21,18 21,64 @@ type VirtualMachine struct {
	heap  []word.Word
	free  []bool
	stack []word.Word
	scope []map[string]int
	scope []map[string]uint64
	main  Chunk
}

var DEBUG_WIDTH int = 10

func incCheckNL(inc *int) {
	val := *inc
	val++
	if val%DEBUG_WIDTH == 0 {
		fmt.Println()
	}
	*inc = val
}

func (vm VirtualMachine) DebugHeap() {
	width := 4
	for i := range vm.heap {
		if i%width == 0 {
	for i := 0; i < len(vm.heap); i++ {
		if i%DEBUG_WIDTH == 0 {
			fmt.Println()
		}
		if vm.free[i] {
			fmt.Printf("{%3v:           }", i)
		} else if vm.heap[i].IsRuneArrayMark() {
			j := i
			fmt.Printf("[%3v: %-9s ]", j, vm.heap[j].Debug())
			incCheckNL(&j)
			fmt.Printf("[%3v: %-9s ]", j, vm.heap[j].Debug())
			incCheckNL(&j)
			fmt.Printf("[%3v: %-9s ]", j, vm.heap[j].Debug())
			incCheckNL(&j)
			fmt.Printf("[%3v: %-9s ]", j, vm.heap[j].Debug())
			incCheckNL(&j)
			fmt.Printf("[%3v: ", j)
			ra := ReviveRuneArray(&vm, uint64(i))
			runes := ra.Runes(&vm)
			if len(runes) <= 2 {
				for _, r := range runes {
					fmt.Printf("'%s'", string(r))
					fmt.Print("  ")
				}
			} else {
				for i, r := range runes {
					fmt.Printf("'%s'", string(r))
					if i%2 == 1 {
						fmt.Print("  |")
						incCheckNL(&j)
						fmt.Print("|     ")
					} else {
						fmt.Print("  ")
					}
				}
				for range make([]int, (ra.Length(&vm) % 2)) {
					fmt.Print("     ")
				}

			}
			fmt.Print("]")
			i += int(ra.Size(&vm)) - 1
		} else {
			fmt.Printf("[%3v: %-9s ]", i, vm.heap[i].Debug())
		}


@@ 47,8 93,8 @@ func NewVirtualMachine() *VirtualMachine {
	vm.free = make([]bool, 0, init_heap_len)
	vm.ReAllocate(word.Word(word.SENTINEL))
	vm.stack = make([]word.Word, 0)
	vm.scope = make([]map[string]int, 0)
	vm.scope = append(vm.scope, make(map[string]int))
	vm.scope = make([]map[string]uint64, 0)
	vm.scope = append(vm.scope, make(map[string]uint64))
	vm.main = NewChunk(vm)
	return vm
}


@@ 136,8 182,8 @@ func (vm *VirtualMachine) Allocate(
			*vm = obj
			return uint64(loc), nil
		} else {
			last := i + newSize
			for i = i + 1; i < last; i++ {
			last := i + newSize - 1
			for i = i + 1; i <= last; i++ {
				if !(obj.free[i]) {
					break
				} else if i == last {


@@ 179,7 225,7 @@ func appendRhumb(vm VirtualMachine, ws []word.Word) VirtualMachine {
// Only run this if you are absolutely sure there is room
// to allocate in the heap. Overwriting memory is possible.
func (vm VirtualMachine) allocInPlace(x, y int, ws ...word.Word) {
	for hID, wID := x, 0; hID < y; hID, wID = hID+1, wID+1 {
	for hID, wID := x, 0; hID <= y; hID, wID = hID+1, wID+1 {
		vm.heap[hID], vm.free[hID] = ws[wID], false
	}
}


@@ 206,7 252,7 @@ func (vm *VirtualMachine) WriteCodeToMain(
}

func (vm *VirtualMachine) Disassemble() {
	// vm.main.Disassemble()
	vm.main.Disassemble(vm)
}

func (vm *VirtualMachine) Execute() {


@@ 227,9 273,9 @@ func logAddedToStack(stack []word.Word, txt string) {
}

func locateScopeLabel(
	scopes []map[string]int, lbl string,
	scopes []map[string]uint64, lbl string,
) (
	idx int, ok bool,
	idx uint64, ok bool,
) {
	topScope := len(scopes) - 1
	for i := topScope; i >= 0; i-- {


@@ 275,25 321,25 @@ func (vm *VirtualMachine) AddLiteralToStack(literal word.Word) {
	logAddedToStack(vm.stack, literal.Debug())
}

func (vm *VirtualMachine) getStringFromRuneArray(labelAddr word.Word) {

}

// Currently just for lexically traversing the scope
func (vm *VirtualMachine) SubmitLocalRequest(label word.Word) {
	// labelValue := vm.getStringFromRuneArray(label)
	// idx, ok := locateScopeLabel(vm.scope, label)
	// if ok {
	// 	// TODO: Invoke address, skip addrRef
	// 	vm.stack = append(vm.stack, word.FromAddress(idx))
	// 	logAddedToStack(vm.stack, ir.text)
	// 	return
	// }
	// vm.heap = append(vm.heap, EmptyWord())
	// idx = len(vm.heap) - 1
	// vm.scope[len(vm.scope)-1][ir.text] = idx
	// vm.stack = append(vm.stack, word.FromAddress(idx))
	// logAddedToStack(vm.stack, ir.text)
func (vm *VirtualMachine) SubmitLocalRequest(addr word.Word) {
	target := vm.heap[addr.AsAddr()]
	if target.IsRuneArrayMark() {
		label := ReviveRuneArray(vm, addr.AsAddr()).String(vm)
		idx, ok := locateScopeLabel(vm.scope, label)
		if ok {
			// TODO: Invoke address, skip addrRef
			vm.stack = append(vm.stack, word.FromAddress(idx))
			logAddedToStack(vm.stack, label)
		} else {
			vm.scope[len(vm.scope)-1][label] = addr.AsAddr()
			vm.stack = append(vm.stack, addr)
			logAddedToStack(vm.stack, label)
		}
	} else {
		vm.stack = append(vm.stack, target)
		logAddedToStack(vm.stack, target.Debug())
	}
}

// Used for traversing maps and legends


@@ 308,15 354,18 @@ func (vm *VirtualMachine) SubmitUnderRequest(label word.Word) {
// Used for traversing primitives and compilations
func (vm *VirtualMachine) SubmitOuterRequest(label word.Word) {
	// FIXME: locate text
	addr, err := vm.main.ReviveLits(vm).IndexOf(vm, label)
	lits := vm.main.ReviveLits(vm)
	addr, err := lits.IndexOf(vm, label)
	if err != nil {
		panic("unable to find word for outer request")
	}
	ref := vm.heap[vm.main.ReviveInstrs(vm).id+rune_arr_offset+uint64(addr)]
	refId := lits.id + word_arr_offset + uint64(addr)
	refAddr := vm.heap[refId]
	ref := vm.heap[refAddr.AsAddr()]
	if !(ref.IsRuneArrayMark()) {
		panic("outer request submitted with non-ra value")
	}
	text := ReviveRuneArray(vm, ref).String(vm)
	text := ReviveRuneArray(vm, refAddr.AsAddr()).String(vm)
	fmt.Println(text)
	switch text {
	case ".=", ":=":

M internal/vm/word_array.go => internal/vm/word_array.go +6 -2
@@ 71,13 71,13 @@ func (wa WordArray) IndexOf(vm *VirtualMachine, x word.Word) (
	if x.IsAddress() {
		mkX := vm.heap[x.AsAddr()]
		if mkX.IsRuneArrayMark() {
			rax := ReviveRuneArray(vm, x)
			rax := ReviveRuneArray(vm, x.AsAddr())
			for i := range make([]int, wa.Length(vm)) {
				y := wa.Get(vm, i)
				if y.IsAddress() {
					mkY := vm.heap[y.AsAddr()]
					if mkY.IsRuneArrayMark() {
						ray := ReviveRuneArray(vm, y)
						ray := ReviveRuneArray(vm, y.AsAddr())
						if rax.String(vm) == ray.String(vm) {
							return i, nil
						}


@@ 112,6 112,10 @@ func (wa *WordArray) Append(vm *VirtualMachine, newWords ...word.Word) (uint64, 
}

func (wa WordArray) Get(vm *VirtualMachine, i int) word.Word {
	waLen := wa.Length(vm)
	if i < 0 || i >= waLen {
		panic("index out of bounds")
	}
	return vm.heap[wa.id+word_arr_offset+uint64(i)]
}


M internal/word/word.go => internal/word/word.go +6 -6
@@ 117,10 117,10 @@ func FromBool(b bool) Word {
		return Word(VAL_FALS)
	}
}
func FromInt(i uint32) Word  { return Word(VAL_NUMB | uint64(i)) }
func FromRune(r rune) Word   { return Word(VAL_RUNE | uint64(r)) }
func FromSym(a int) Word     { return Word(VAL_SYMB | uint64(a)) }
func FromAddress(a int) Word { return Word(VAL_ADDR | uint64(a)) }
func FromInt(i uint32) Word     { return Word(VAL_NUMB | uint64(i)) }
func FromRune(r rune) Word      { return Word(VAL_RUNE | uint64(r)) }
func FromSym(a int) Word        { return Word(VAL_SYMB | uint64(a)) }
func FromAddress(a uint64) Word { return Word(VAL_ADDR | a) }

func (w Word) isVal(v uint64) bool { return uint64(w)&MASK_ONE == v }



@@ 191,7 191,7 @@ func (w Word) Debug() string {
	} else if w.IsSym() {
		return "S" // FIXME: Implement symbols
	} else if w.IsAddress() { // must trigger before IsFloat for unknown reasons
		return fmt.Sprint("A ", w.AsAddr(), "    ")
		return fmt.Sprint("A  ", w.AsAddr(), "   ")
	} else if w.IsFloat() {
		// return fmt.Sprintf("F %.1e", w.AsFloat())
		return ". . . . ."


@@ 215,7 215,7 @@ func (w Word) Debug() string {
}

func (x Word) Equals(y Word) bool {
	if x.IsNAN() {
	if x.IsNAN() || x.IsSentinel() {
		return false
	} else if x.IsTrue() {
		return y.IsTrue()