~madcapjake/rhi

38be5b2c7d5962aa9b5561a9615d7def9bfbedc9 — Jake Russo 1 year, 8 months ago 18a392f
Nearly functional memory system
M .vscode/launch.json => .vscode/launch.json +1 -1
@@ 25,7 25,7 @@
                "foo ** bar // 6"
            ],
            "env": {
                "RHUMB_VISITOR_DEBUG": "1",
                "RHUMB_VISITOR_DEBUG": "0",
                "RHUMB_CODE_ARRAY_DEBUG": "1",
                "RHUMB_VM_DEBUG": "1"
            }

M internal/generator/visitor.go => internal/generator/visitor.go +159 -117
@@ 9,8 9,9 @@ import (
	"reflect"
	"strconv"

	"git.sr.ht/~madcapjake/grhumb/internal/parser"
	P "git.sr.ht/~madcapjake/grhumb/internal/parser"
	"git.sr.ht/~madcapjake/grhumb/internal/vm"
	"git.sr.ht/~madcapjake/grhumb/internal/word"
	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
)



@@ 43,7 44,7 @@ type RhumbReturn struct {
}

type RhumbVisitor struct {
	parser.BaseRhumbParserVisitor
	P.BaseRhumbParserVisitor
	vm vm.VirtualMachine
}



@@ 79,45 80,58 @@ func (v *RhumbVisitor) VisitChildren(node antlr.RuleNode) interface{} {
	return nil
}

func (v *RhumbVisitor) VisitSequence(ctx *parser.SequenceContext) interface{} {
func (v *RhumbVisitor) VisitSequence(ctx *P.SequenceContext) interface{} {
	logger.Println("sequence!")
	// logger.Println(ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitSimple(ctx *parser.SimpleContext) interface{} {
func (v *RhumbVisitor) VisitSimple(ctx *P.SimpleContext) interface{} {
	logger.Println("simple:", ctx.GetText())
	return v.Visit(ctx.GetChild(0).(antlr.ParseTree))
}

func (v *RhumbVisitor) VisitMutableLabel(ctx *parser.MutableLabelContext) interface{} {
func (v *RhumbVisitor) VisitMutableLabel(ctx *P.MutableLabelContext) interface{} {
	logger.Println("mutable label:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLabelLiteral(ctx *parser.LabelLiteralContext) interface{} {
	text := ctx.GetText()
func (v *RhumbVisitor) VisitLabelLiteral(ctx *P.LabelLiteralContext) interface{} {
	var (
		text string       = ctx.GetText()
		ra   vm.RuneArray = vm.NewRuneArray(
			&v.vm,
			word.FromAddress(0),
			vm.RuneWords(text)...,
		)
	)
	logger.Println("label:", text)
	v.vm.WriteCodeToMain(
		ctx.GetStart().GetLine(),
		vm.NewInstrRef(vm.RefLabel, text, nil),
		word.FromAddress(int(ra.Id())),
		vm.NewLocalRequest,
	)
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitAssignment(ctx *parser.AssignmentContext) interface{} {
func (v *RhumbVisitor) VisitAssignment(ctx *P.AssignmentContext) interface{} {
	logger.Println("assignment!")
	// TODO: implement map assignment
	var text string
	var (
		text string
		ra   vm.RuneArray
	)
	if addr := ctx.GetAddress(); addr != nil {
		text = addr.GetText()
		logger.Println("Address:", text)
		// TODO: check for a matching subroutine and invoke
		// TODO: check for matching outer scoped label

		ra = vm.NewRuneArray(&v.vm, word.FromAddress(0), vm.RuneWords(text)...)

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


@@ 126,9 140,10 @@ func (v *RhumbVisitor) VisitAssignment(ctx *parser.AssignmentContext) interface{
		logger.Printf("addrRef.Accept(v): %v\n", addrRef.Accept(v))
		text = addrRef.GetText()
		logger.Println("AddressRef:", text)
		ra = vm.NewRuneArray(&v.vm, word.FromAddress(0), vm.RuneWords(text)...)
		v.vm.WriteCodeToMain(
			addrRef.GetStart().GetLine(),
			vm.NewInstrRef(vm.RefLabel, text, nil),
			word.FromAddress(int(ra.Id())),
			vm.NewLocalRequest,
		)
	}


@@ 136,22 151,27 @@ func (v *RhumbVisitor) VisitAssignment(ctx *parser.AssignmentContext) interface{
	logger.Println("ctx.Expression().Accept...")
	logger.Println("ctx.Expression().Accept:", ctx.Expression().Accept(v))
	logger.Println("INNER:")
	assignOp := ctx.AssignmentOp()
	op := ctx.AssignmentOp()
	ra = vm.NewRuneArray(
		&v.vm,
		word.FromAddress(0),
		vm.RuneWords(op.GetText())...,
	)
	v.vm.WriteCodeToMain(
		assignOp.GetStart().GetLine(),
		vm.NewInstrRef(vm.RefLabel, assignOp.GetText(), nil),
		op.GetStart().GetLine(),
		word.FromAddress(int(ra.Id())),
		vm.NewOuterRequest,
	)
	return nil
}

func (v *RhumbVisitor) VisitFloatLiteral(ctx *parser.FloatLiteralContext) interface{} {
func (v *RhumbVisitor) VisitFloatLiteral(ctx *P.FloatLiteralContext) interface{} {
	// logger.Println("float-lit!")
	logger.Println(ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIntegerLiteral(ctx *parser.IntegerLiteralContext) interface{} {
func (v *RhumbVisitor) VisitIntegerLiteral(ctx *P.IntegerLiteralContext) interface{} {
	text := ctx.GetText()
	val, err := strconv.ParseInt(text, 10, 32)
	if err != nil {


@@ 160,462 180,484 @@ func (v *RhumbVisitor) VisitIntegerLiteral(ctx *parser.IntegerLiteralContext) in
	logger.Println("VALUE:", val)
	v.vm.WriteCodeToMain(
		ctx.GetStart().GetLine(),
		vm.NewInstrRef(vm.RefInt, text, val),
		word.FromInt(uint32(val)),
		vm.NewValueLiteral,
	)
	return RhumbReturn{val, nil}
}

func (v *RhumbVisitor) VisitStringLiteral(ctx *parser.StringLiteralContext) interface{} {
func (v *RhumbVisitor) VisitStringLiteral(ctx *P.StringLiteralContext) interface{} {
	logger.Println("string-lit:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitReferenceLiteral(ctx *parser.ReferenceLiteralContext) interface{} {
func (v *RhumbVisitor) VisitReferenceLiteral(ctx *P.ReferenceLiteralContext) interface{} {
	logger.Println("ref-lit:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitConjunctive(ctx *parser.ConjunctiveContext) interface{} {
func (v *RhumbVisitor) VisitConjunctive(ctx *P.ConjunctiveContext) interface{} {
	logger.Println("conj:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitAccess(ctx *parser.AccessContext) interface{} {
func (v *RhumbVisitor) VisitAccess(ctx *P.AccessContext) interface{} {
	logger.Println("access:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitApplicative(ctx *parser.ApplicativeContext) interface{} {
func (v *RhumbVisitor) VisitApplicative(ctx *P.ApplicativeContext) interface{} {
	logger.Println("applicative:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitConditional(ctx *parser.ConditionalContext) interface{} {
func (v *RhumbVisitor) VisitConditional(ctx *P.ConditionalContext) interface{} {
	logger.Println("cond:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitPrefix(ctx *parser.PrefixContext) interface{} {
func (v *RhumbVisitor) VisitPrefix(ctx *P.PrefixContext) interface{} {
	logger.Println("prefix:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitComparative(ctx *parser.ComparativeContext) interface{} {
func (v *RhumbVisitor) VisitComparative(ctx *P.ComparativeContext) interface{} {
	logger.Println("compare:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitMultiplicative(ctx *parser.MultiplicativeContext) interface{} {
func (v *RhumbVisitor) VisitMultiplicative(ctx *P.MultiplicativeContext) interface{} {
	logger.Println("multiply:", ctx.GetText())
	exprs := ctx.AllExpression()
	var (
		exprs []P.IExpressionContext     = ctx.AllExpression()
		mulOp P.IMultiplicativeOpContext = ctx.MultiplicativeOp()
		ra    vm.RuneArray               = vm.NewRuneArray(
			&v.vm,
			word.FromAddress(0),
			vm.RuneWords(mulOp.GetText())...,
		)
	)

	for i := range exprs {
		exprs[i].Accept(v)
	}
	mulOp := ctx.MultiplicativeOp()

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

func (v *RhumbVisitor) VisitAdditive(ctx *parser.AdditiveContext) interface{} {
func (v *RhumbVisitor) VisitAdditive(ctx *P.AdditiveContext) interface{} {
	logger.Println("additive:", ctx.GetText())
	exprs := ctx.AllExpression()
	var (
		exprs []P.IExpressionContext = ctx.AllExpression()
		addOp P.IAdditiveOpContext   = ctx.AdditiveOp()
		ra    vm.RuneArray           = vm.NewRuneArray(
			&v.vm,
			word.FromAddress(0),
			vm.RuneWords(addOp.GetText())...,
		)
	)

	for i := range exprs {
		exprs[i].Accept(v)
	}
	addOp := ctx.AdditiveOp()

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

func (v *RhumbVisitor) VisitInvocation(ctx *parser.InvocationContext) interface{} {
func (v *RhumbVisitor) VisitInvocation(ctx *P.InvocationContext) interface{} {
	logger.Println("invoke:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitRoutine(ctx *parser.RoutineContext) interface{} {
func (v *RhumbVisitor) VisitRoutine(ctx *P.RoutineContext) interface{} {
	logger.Println("routine:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitDisjunctive(ctx *parser.DisjunctiveContext) interface{} {
func (v *RhumbVisitor) VisitDisjunctive(ctx *P.DisjunctiveContext) interface{} {
	logger.Println("disjunct:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIdentity(ctx *parser.IdentityContext) interface{} {
func (v *RhumbVisitor) VisitIdentity(ctx *P.IdentityContext) interface{} {
	logger.Println("identify:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitEffect(ctx *parser.EffectContext) interface{} {
func (v *RhumbVisitor) VisitEffect(ctx *P.EffectContext) interface{} {
	logger.Println("effect:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitMember(ctx *parser.MemberContext) interface{} {
func (v *RhumbVisitor) VisitMember(ctx *P.MemberContext) interface{} {
	logger.Println("member:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitSelector(ctx *parser.SelectorContext) interface{} {
func (v *RhumbVisitor) VisitSelector(ctx *P.SelectorContext) interface{} {
	logger.Println("selector:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitPower(ctx *parser.PowerContext) interface{} {
func (v *RhumbVisitor) VisitPower(ctx *P.PowerContext) interface{} {
	logger.Println("power:", ctx.GetText())
	exprs := ctx.AllExpression()
	var (
		exprs []P.IExpressionContext     = ctx.AllExpression()
		powOp P.IExponentiationOpContext = ctx.ExponentiationOp()
		ra    vm.RuneArray               = vm.NewRuneArray(
			&v.vm,
			word.FromAddress(0),
			vm.RuneWords(powOp.GetText())...,
		)
	)
	for i := range exprs {
		exprs[i].Accept(v)
	}
	powOp := ctx.ExponentiationOp()
	v.vm.WriteCodeToMain(
		powOp.GetStart().GetLine(),
		vm.NewInstrRef(vm.RefLabel, powOp.GetText(), nil),
		// FIXME: re-implement as NewInnerRequest
		vm.NewOuterRequest,
		word.FromAddress(int(ra.Id())),
		vm.NewOuterRequest, // FIXME: re-implement as NewInnerRequest
	)
	return nil
}

func (v *RhumbVisitor) VisitMap(ctx *parser.MapContext) interface{} {
func (v *RhumbVisitor) VisitMap(ctx *P.MapContext) interface{} {
	logger.Println("map:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitFreeze(ctx *parser.FreezeContext) interface{} {
func (v *RhumbVisitor) VisitFreeze(ctx *P.FreezeContext) interface{} {
	logger.Println("freeze:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitInner(ctx *parser.InnerContext) interface{} {
func (v *RhumbVisitor) VisitInner(ctx *P.InnerContext) interface{} {
	logger.Println("inner:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLength(ctx *parser.LengthContext) interface{} {
func (v *RhumbVisitor) VisitLength(ctx *P.LengthContext) interface{} {
	logger.Println("length:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitFunction(ctx *parser.FunctionContext) interface{} {
func (v *RhumbVisitor) VisitFunction(ctx *P.FunctionContext) interface{} {
	logger.Println("function:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitJunction(ctx *parser.JunctionContext) interface{} {
func (v *RhumbVisitor) VisitJunction(ctx *P.JunctionContext) interface{} {
	logger.Println("junction:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitGreaterThan(ctx *parser.GreaterThanContext) interface{} {
func (v *RhumbVisitor) VisitGreaterThan(ctx *P.GreaterThanContext) interface{} {
	logger.Println("greater-than:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitGreaterThanOrEqualTo(ctx *parser.GreaterThanOrEqualToContext) interface{} {
func (v *RhumbVisitor) VisitGreaterThanOrEqualTo(ctx *P.GreaterThanOrEqualToContext) interface{} {
	logger.Println("greater-than-or-equal-to:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLessThan(ctx *parser.LessThanContext) interface{} {
func (v *RhumbVisitor) VisitLessThan(ctx *P.LessThanContext) interface{} {
	logger.Println("less-than:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLessThanOrEqualTo(ctx *parser.LessThanOrEqualToContext) interface{} {
func (v *RhumbVisitor) VisitLessThanOrEqualTo(ctx *P.LessThanOrEqualToContext) interface{} {
	logger.Println("less-than-or-equal-to:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIsEqual(ctx *parser.IsEqualContext) interface{} {
func (v *RhumbVisitor) VisitIsEqual(ctx *P.IsEqualContext) interface{} {
	logger.Println("is-equal:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIsLike(ctx *parser.IsLikeContext) interface{} {
func (v *RhumbVisitor) VisitIsLike(ctx *P.IsLikeContext) interface{} {
	logger.Println("is-like:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIsIn(ctx *parser.IsInContext) interface{} {
func (v *RhumbVisitor) VisitIsIn(ctx *P.IsInContext) interface{} {
	logger.Println("is-in:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIsOverlayed(ctx *parser.IsOverlayedContext) interface{} {
func (v *RhumbVisitor) VisitIsOverlayed(ctx *P.IsOverlayedContext) interface{} {
	logger.Println("is-overlayed:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIsTopmost(ctx *parser.IsTopmostContext) interface{} {
func (v *RhumbVisitor) VisitIsTopmost(ctx *P.IsTopmostContext) interface{} {
	logger.Println("is-topmost:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNotEqual(ctx *parser.NotEqualContext) interface{} {
func (v *RhumbVisitor) VisitNotEqual(ctx *P.NotEqualContext) interface{} {
	logger.Println("not-equal:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNotLike(ctx *parser.NotLikeContext) interface{} {
func (v *RhumbVisitor) VisitNotLike(ctx *P.NotLikeContext) interface{} {
	logger.Println("not-like:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNotIn(ctx *parser.NotInContext) interface{} {
func (v *RhumbVisitor) VisitNotIn(ctx *P.NotInContext) interface{} {
	logger.Println("not-in:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNotOverlayed(ctx *parser.NotOverlayedContext) interface{} {
func (v *RhumbVisitor) VisitNotOverlayed(ctx *P.NotOverlayedContext) interface{} {
	logger.Println("not-overlayed:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNotTopmost(ctx *parser.NotTopmostContext) interface{} {
func (v *RhumbVisitor) VisitNotTopmost(ctx *P.NotTopmostContext) interface{} {
	logger.Println("not-topmost:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitConjunctiveOp(ctx *parser.ConjunctiveOpContext) interface{} {
func (v *RhumbVisitor) VisitConjunctiveOp(ctx *P.ConjunctiveOpContext) interface{} {
	logger.Println("conj-op:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitDisjunctiveOp(ctx *parser.DisjunctiveOpContext) interface{} {
func (v *RhumbVisitor) VisitDisjunctiveOp(ctx *P.DisjunctiveOpContext) interface{} {
	logger.Println("disjunct-op:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitOtherwise(ctx *parser.OtherwiseContext) interface{} {
func (v *RhumbVisitor) VisitOtherwise(ctx *P.OtherwiseContext) interface{} {
	logger.Println("otherwise:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitDefault(ctx *parser.DefaultContext) interface{} {
func (v *RhumbVisitor) VisitDefault(ctx *P.DefaultContext) interface{} {
	logger.Println("default:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitForeach(ctx *parser.ForeachContext) interface{} {
func (v *RhumbVisitor) VisitForeach(ctx *P.ForeachContext) interface{} {
	logger.Println("for-each:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitWhile(ctx *parser.WhileContext) interface{} {
func (v *RhumbVisitor) VisitWhile(ctx *P.WhileContext) interface{} {
	logger.Println("while:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitThen(ctx *parser.ThenContext) interface{} {
func (v *RhumbVisitor) VisitThen(ctx *P.ThenContext) interface{} {
	logger.Println("then:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitElse(ctx *parser.ElseContext) interface{} {
func (v *RhumbVisitor) VisitElse(ctx *P.ElseContext) interface{} {
	logger.Println("else:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitAddition(ctx *parser.AdditionContext) interface{} {
func (v *RhumbVisitor) VisitAddition(ctx *P.AdditionContext) interface{} {
	logger.Println("addition:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitDeviation(ctx *parser.DeviationContext) interface{} {
func (v *RhumbVisitor) VisitDeviation(ctx *P.DeviationContext) interface{} {
	logger.Println("deviation:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitSubtraction(ctx *parser.SubtractionContext) interface{} {
func (v *RhumbVisitor) VisitSubtraction(ctx *P.SubtractionContext) interface{} {
	logger.Println("subtraction:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitMultiplication(ctx *parser.MultiplicationContext) interface{} {
func (v *RhumbVisitor) VisitMultiplication(ctx *P.MultiplicationContext) interface{} {
	logger.Println("mupltication:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitDivision(ctx *parser.DivisionContext) interface{} {
func (v *RhumbVisitor) VisitDivision(ctx *P.DivisionContext) interface{} {
	logger.Println("division:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitIntegerDivision(ctx *parser.IntegerDivisionContext) interface{} {
func (v *RhumbVisitor) VisitIntegerDivision(ctx *P.IntegerDivisionContext) interface{} {
	logger.Println("int-division:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitModulo(ctx *parser.ModuloContext) interface{} {
func (v *RhumbVisitor) VisitModulo(ctx *P.ModuloContext) interface{} {
	logger.Println("modulo:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitBind(ctx *parser.BindContext) interface{} {
func (v *RhumbVisitor) VisitBind(ctx *P.BindContext) interface{} {
	logger.Println("bind:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitExponent(ctx *parser.ExponentContext) interface{} {
func (v *RhumbVisitor) VisitExponent(ctx *P.ExponentContext) interface{} {
	logger.Println("exponenet:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitRootExtraction(ctx *parser.RootExtractionContext) interface{} {
func (v *RhumbVisitor) VisitRootExtraction(ctx *P.RootExtractionContext) interface{} {
	logger.Println("root-extract:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitScientific(ctx *parser.ScientificContext) interface{} {
func (v *RhumbVisitor) VisitScientific(ctx *P.ScientificContext) interface{} {
	logger.Println("scientific:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitImmutablePair(ctx *parser.ImmutablePairContext) interface{} {
func (v *RhumbVisitor) VisitImmutablePair(ctx *P.ImmutablePairContext) interface{} {
	logger.Println("immutable-pair:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitImmutableLabel(ctx *parser.ImmutableLabelContext) interface{} {
func (v *RhumbVisitor) VisitImmutableLabel(ctx *P.ImmutableLabelContext) interface{} {
	logger.Println("immutable-label:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitMutablePair(ctx *parser.MutablePairContext) interface{} {
func (v *RhumbVisitor) VisitMutablePair(ctx *P.MutablePairContext) interface{} {
	logger.Println("mutable-pair:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNumericalNegate(ctx *parser.NumericalNegateContext) interface{} {
func (v *RhumbVisitor) VisitNumericalNegate(ctx *P.NumericalNegateContext) interface{} {
	logger.Println("numerical-negate:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitOuterScope(ctx *parser.OuterScopeContext) interface{} {
func (v *RhumbVisitor) VisitOuterScope(ctx *P.OuterScopeContext) interface{} {
	logger.Println("outer-scope:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLogicalNegate(ctx *parser.LogicalNegateContext) interface{} {
func (v *RhumbVisitor) VisitLogicalNegate(ctx *P.LogicalNegateContext) interface{} {
	logger.Println("logical-negate:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitAssert(ctx *parser.AssertContext) interface{} {
func (v *RhumbVisitor) VisitAssert(ctx *P.AssertContext) interface{} {
	logger.Println("assert:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitArgument(ctx *parser.ArgumentContext) interface{} {
func (v *RhumbVisitor) VisitArgument(ctx *P.ArgumentContext) interface{} {
	logger.Println("argument:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitSlurpSpread(ctx *parser.SlurpSpreadContext) interface{} {
func (v *RhumbVisitor) VisitSlurpSpread(ctx *P.SlurpSpreadContext) interface{} {
	logger.Println("slurp-spread:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitBaseClone(ctx *parser.BaseCloneContext) interface{} {
func (v *RhumbVisitor) VisitBaseClone(ctx *P.BaseCloneContext) interface{} {
	logger.Println("base-clone:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNumericalPosit(ctx *parser.NumericalPositContext) interface{} {
func (v *RhumbVisitor) VisitNumericalPosit(ctx *P.NumericalPositContext) interface{} {
	logger.Println("numerical-posit:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLogicalPosit(ctx *parser.LogicalPositContext) interface{} {
func (v *RhumbVisitor) VisitLogicalPosit(ctx *P.LogicalPositContext) interface{} {
	logger.Println("logical-posit:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitOverlay(ctx *parser.OverlayContext) interface{} {
func (v *RhumbVisitor) VisitOverlay(ctx *P.OverlayContext) interface{} {
	logger.Println("overlay:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitExistentialPosit(ctx *parser.ExistentialPositContext) interface{} {
func (v *RhumbVisitor) VisitExistentialPosit(ctx *P.ExistentialPositContext) interface{} {
	logger.Println("existential-posit:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitImmutableDestruct(ctx *parser.ImmutableDestructContext) interface{} {
func (v *RhumbVisitor) VisitImmutableDestruct(ctx *P.ImmutableDestructContext) interface{} {
	logger.Println("immutable-destruct:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitMutableDestruct(ctx *parser.MutableDestructContext) interface{} {
func (v *RhumbVisitor) VisitMutableDestruct(ctx *P.MutableDestructContext) interface{} {
	logger.Println("mutable-destruct:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNestedLabel(ctx *parser.NestedLabelContext) interface{} {
func (v *RhumbVisitor) VisitNestedLabel(ctx *P.NestedLabelContext) interface{} {
	logger.Println("nested-label:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNestedOverlay(ctx *parser.NestedOverlayContext) interface{} {
func (v *RhumbVisitor) VisitNestedOverlay(ctx *P.NestedOverlayContext) interface{} {
	logger.Println("nested-overlay:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitRange(ctx *parser.RangeContext) interface{} {
func (v *RhumbVisitor) VisitRange(ctx *P.RangeContext) interface{} {
	logger.Println("range:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitString(ctx *parser.StringContext) interface{} {
func (v *RhumbVisitor) VisitString(ctx *P.StringContext) interface{} {
	logger.Println("string:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitLabelInterp(ctx *parser.LabelInterpContext) interface{} {
func (v *RhumbVisitor) VisitLabelInterp(ctx *P.LabelInterpContext) interface{} {
	logger.Println("label-interp!")
	logger.Println(ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitRoutineInterp(ctx *parser.RoutineInterpContext) interface{} {
func (v *RhumbVisitor) VisitRoutineInterp(ctx *P.RoutineInterpContext) interface{} {
	logger.Println("routine-interp:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitSelectorInterp(ctx *parser.SelectorInterpContext) interface{} {
func (v *RhumbVisitor) VisitSelectorInterp(ctx *P.SelectorInterpContext) interface{} {
	logger.Println("selector-interp:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitNamedRef(ctx *parser.NamedRefContext) interface{} {
func (v *RhumbVisitor) VisitNamedRef(ctx *P.NamedRefContext) interface{} {
	logger.Println("named-ref:", ctx.GetText())
	return ctx.Label().GetText()
}

func (v *RhumbVisitor) VisitFunctionRef(ctx *parser.FunctionRefContext) interface{} {
func (v *RhumbVisitor) VisitFunctionRef(ctx *P.FunctionRefContext) interface{} {
	logger.Println("function-ref:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitComputedRef(ctx *parser.ComputedRefContext) interface{} {
func (v *RhumbVisitor) VisitComputedRef(ctx *P.ComputedRefContext) interface{} {
	logger.Println("computed-ref:", ctx.GetText())
	return v.VisitChildren(ctx)
}

func (v *RhumbVisitor) VisitJunctionRef(ctx *parser.JunctionRefContext) interface{} {
func (v *RhumbVisitor) VisitJunctionRef(ctx *P.JunctionRefContext) interface{} {
	logger.Println("junction-ref:", ctx.GetText())
	return v.VisitChildren(ctx)
}

M internal/vm/chunk.go => internal/vm/chunk.go +92 -81
@@ 2,99 2,107 @@ package vm

import (
	"fmt"
)

type RefType uint8
	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

const (
	RefInt RefType = iota
	RefFloat
	RefRune
	RefBool
	RefAddr
	RefLabel
	chunk_ca_offset uint64 = 1
	chunk_wa_offset uint64 = 2
)

type InstrRef struct {
	kind  RefType
	text  string
	value interface{}
}

func NewInstrRef(kind RefType, text string, value interface{}) InstrRef {
	return InstrRef{kind, text, value}
}

func (ir InstrRef) equals(other InstrRef) bool {
	return ir.text == other.text
}

type Chunk struct {
	instructions CodeArray

	// FIXME: Find a less memory intensive line tracking system
	lines []int
	// address in vm's heap
	id uint64

	// FIXME: Convert to array of refs & literals for true compilation
	prestack []InstrRef
	// temp line tracking
	line int
}

func NewChunk(codes, refs []Word) Chunk {
	return Chunk{
		instructions: NewCodeArray(codes...),
		prestack:     make([]InstrRef, len(codes)/2),
		lines:        make([]int, len(codes)),
func NewChunk(vm *VirtualMachine) Chunk {
	var (
		ch [3]word.Word = [3]word.Word{}
		ca CodeArray    = NewCodeArray(vm, word.FromAddress(0))
		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))
	id, err := vm.ReAllocate(ch[:]...)
	if err != nil {
		panic("failed to reallocate onto heap")
	}
	return Chunk{id, 0}
}

func (ch *Chunk) WriteCode(line int, codes []Code) {
	for c := range codes {
		ch.instructions.SetCodes(codes[c])
		ch.lines = append(ch.lines, line)
func ReviveChunk(vm *VirtualMachine, addr word.Word) Chunk {
	i := addr.AsAddr()
	mark := vm.heap[i]
	if !(mark.IsCodeArrayMark()) {
		panic("not a chunk mark")
	}
	return Chunk{i, 0}
}

func (ch *Chunk) AddReference(ref InstrRef) uint32 {
	for r := range ch.prestack {
		if ch.prestack[r].equals(ref) {
			return uint32(r)
		}
func (ch *Chunk) WriteCode(vm *VirtualMachine, line int, codes []Code) {

	instructions := ch.Instructions(vm)
	if line > ch.line {
		instructions.SetCodes(vm, true, codes...)
		ch.line = line
	} else {
		instructions.SetCodes(vm, false, codes...)
	}
	caId := ch.id + chunk_ca_offset
	if instructions.id != vm.heap[caId].AsAddr() {
		vm.heap[caId] = word.FromAddress(int(instructions.id))
	}
	newId := len(ch.prestack)
	ch.prestack = append(ch.prestack, ref)
	return uint32(newId)
}

func (ch Chunk) CodeWords() []Word {
	return ch.instructions.CodeWords
func (ch *Chunk) AddLiteral(vm *VirtualMachine, lit word.Word) (uint64, error) {
	literals := ch.Literals(vm)
	existingIndex, err := literals.IndexOf(vm, lit)
	if err != nil {
		id, err := literals.Append(vm, lit)
		if err != nil {
			panic("literals append failed")
		}
		if id != vm.heap[ch.id+chunk_wa_offset].AsAddr() {
			vm.heap[ch.id+chunk_wa_offset] = word.FromAddress(int(id))
		}
		return id, err
	} else {
		return uint64(existingIndex), nil
	}
}

func (ch Chunk) CodeWordsCount() int {
	return len(ch.instructions.CodeWords)
func (ch Chunk) Instructions(vm *VirtualMachine) CodeArray {
	return ReviveCodeArray(vm, vm.heap[ch.id+chunk_ca_offset])
}

func (ch Chunk) CodesFromWord(wordIndex int, buf *[]byte) {
	ch.instructions.getCodesFromWord(wordIndex, buf)
func (ch Chunk) Literals(vm *VirtualMachine) WordArray {
	return ReviveWordArray(vm, vm.heap[ch.id+chunk_wa_offset])
}

func (ch Chunk) Execute(vm *VirtualMachine) {
	var (
		cwLen int    = len(ch.instructions.CodeWords)
		idx   int    = 0
		buf   []byte = make([]byte, 0, cwLen*8)
		instructions        = ch.Instructions(vm)
		cwLen        int    = int(instructions.Length(vm))
		idx          uint64 = 0
		buf          []byte = make([]byte, 0, cwLen*8)
	)
	for wordIndex := 0; wordIndex < cwLen; wordIndex++ {
		ch.instructions.getCodesFromWord(wordIndex, &buf)
		instructions.getCodesFromWord(vm, uint64(wordIndex), &buf)
		for b := range buf {
			if buf[b] == 0 {
				break
			}
			code := Code(buf[b])
			idx += int(code.Index())
			idx += uint64(code.Index())
			if code.IsIndexExtension() {
				continue
			} else {
				ch.execTagIndex(vm, code.Tag(), idx)
				ch.execTagIndex(vm, code.Tag(), int(idx))
				idx = 0
			}
		}


@@ 103,45 111,48 @@ func (ch Chunk) Execute(vm *VirtualMachine) {
}

func (ch Chunk) execTagIndex(vm *VirtualMachine, b byte, idx int) {
	code := Code(b)
	tag := code.Tag()
	var (
		code     Code  = Code(b)
		tag      uint8 = code.Tag()
		literals       = ch.Literals(vm)
	)
	switch tag {
	case TAG_VALUE_LITERAL:
		vm.AddValueToStack(ch.prestack[idx])
		vm.AddLiteralToStack(literals.Get(vm, idx))
	case TAG_LOCAL_REQUEST:
		vm.SubmitLocalRequest(ch.prestack[idx])
		vm.SubmitLocalRequest(literals.Get(vm, idx))
	case TAG_INNER_REQUEST:
		vm.SubmitInnerRequest(ch.prestack[idx])
		vm.SubmitInnerRequest(literals.Get(vm, idx))
	case TAG_UNDER_REQUEST:
		vm.SubmitUnderRequest(ch.prestack[idx])
		vm.SubmitUnderRequest(literals.Get(vm, idx))
	case TAG_OUTER_REQUEST:
		vm.SubmitOuterRequest(ch.prestack[idx])
	case TAG_EVENT_REQUEST:
		vm.SubmitEventRequest(ch.prestack[idx])
	case TAG_REPLY_REQUEST:
		vm.SubmitReplyRequest(ch.prestack[idx])
		vm.SubmitOuterRequest(literals.Get(vm, idx))
		// case TAG_EVENT_REQUEST:
		// 	vm.SubmitEventRequest(literals.Get(idx))
		// case TAG_REPLY_REQUEST:
		// 	vm.SubmitReplyRequest(literals.Get(idx))
	}
}

func (ch Chunk) Disassemble() {
func (ch Chunk) Disassemble(vm *VirtualMachine) {
	fmt.Println("============= Chunk =============")
	var line int
	buf := ch.instructions.GetCodes()
	for offset := 0; offset < ch.instructions.Len(); {
		line, offset = ch.DisassembleCode(line, offset, &buf)
	instructions := ch.Instructions(vm)
	buf := instructions.GetCodes(vm)
	for offset := 0; offset < int(instructions.Length(vm)); {
		if line < instructions.GetLine(vm, offset) {
			line += 1
			fmt.Printf("%4d ", line)
		} else {
			fmt.Printf("   | ")
		}
		line, offset = ch.DisassembleCode(vm, line, offset, &buf)
	}
}

func (ch Chunk) DisassembleCode(currentLine, currentOffset int, bufPtr *[]byte) (int, int) {
func (ch Chunk) DisassembleCode(vm *VirtualMachine, currentLine, currentOffset int, bufPtr *[]byte) (int, int) {
	buf := *bufPtr
	fmt.Printf("%04d ", currentOffset)
	if currentOffset > 0 &&
		ch.lines[currentOffset] == ch.lines[currentOffset-1] {
		fmt.Printf("   | ")
	} else {
		fmt.Printf("%4d ", ch.lines[currentOffset])
	}

	var recurse func(l, o, i int) (int, int)
	recurse = func(l, o, i int) (int, int) {
		tag := Code(buf[o]).Tag()


@@ 158,7 169,7 @@ func (ch Chunk) DisassembleCode(currentLine, currentOffset int, bufPtr *[]byte) 
			fmt.Printf("%s %d '%v'\n",
				Code(buf[o]).String(),
				idx,
				ch.prestack[idx].text,
				ch.Literals(vm).Get(vm, int(idx)).Debug(),
			)
			return l, o + 1
		}

M internal/vm/code.go => internal/vm/code.go +9 -9
@@ 11,7 11,7 @@ const TAG_UNDER_REQUEST uint8 = 0b10000000
const TAG_OUTER_REQUEST uint8 = 0b10100000
const TAG_EVENT_REQUEST uint8 = 0b11000000
const TAG_REPLY_REQUEST uint8 = 0b11100000
const MAX_IDX_SIZE uint32 = 31
const MAX_IDX_SIZE uint64 = 31

func (c Code) String() (result string) {
	switch c.Tag() {


@@ 35,7 35,7 @@ func (c Code) String() (result string) {
	return
}

func generateCodes(tag uint8, idx uint32) []Code {
func generateCodes(tag uint8, idx uint64) []Code {
	codeWithExtensions := make([]Code, (idx/MAX_IDX_SIZE)+1)
	remaining := idx
	for i := range codeWithExtensions {


@@ 67,7 67,7 @@ func (c Code) IsIndexExtension() bool {
	return c.Tag() == TAG_IDX_EXTENSION
}

func NewValueLiteral(idx uint32) []Code {
func NewValueLiteral(idx uint64) []Code {
	return generateCodes(TAG_VALUE_LITERAL, idx)
}



@@ 75,7 75,7 @@ func (c Code) IsValueLiteral() bool {
	return c.Tag() == TAG_VALUE_LITERAL
}

func NewLocalRequest(idx uint32) []Code {
func NewLocalRequest(idx uint64) []Code {
	return generateCodes(TAG_LOCAL_REQUEST, idx)
}



@@ 83,7 83,7 @@ func (c Code) IsLocalRequest() bool {
	return c.Tag() == TAG_LOCAL_REQUEST
}

func NewInnerRequest(idx uint32) []Code {
func NewInnerRequest(idx uint64) []Code {
	return generateCodes(TAG_INNER_REQUEST, idx)
}



@@ 91,7 91,7 @@ func (c Code) IsInnerRequest() bool {
	return c.Tag() == TAG_INNER_REQUEST
}

func NewUnderRequest(idx uint32) []Code {
func NewUnderRequest(idx uint64) []Code {
	return generateCodes(TAG_UNDER_REQUEST, idx)
}



@@ 99,7 99,7 @@ func (c Code) IsUnderRequest() bool {
	return c.Tag() == TAG_UNDER_REQUEST
}

func NewOuterRequest(idx uint32) []Code {
func NewOuterRequest(idx uint64) []Code {
	return generateCodes(TAG_OUTER_REQUEST, idx)
}



@@ 107,7 107,7 @@ func (c Code) IsOuterRequest() bool {
	return c.Tag() == TAG_OUTER_REQUEST
}

func NewEventRequest(idx uint32) []Code {
func NewEventRequest(idx uint64) []Code {
	return generateCodes(TAG_EVENT_REQUEST, idx)
}



@@ 115,7 115,7 @@ func (c Code) IsEventRequest() bool {
	return c.Tag() == TAG_EVENT_REQUEST
}

func NewReplyRequest(idx uint32) []Code {
func NewReplyRequest(idx uint64) []Code {
	return generateCodes(TAG_REPLY_REQUEST, idx)
}


M internal/vm/code_array.go => internal/vm/code_array.go +218 -61
@@ 5,6 5,8 @@ import (
	"io"
	"log"
	"os"

	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

var caLogger = log.New(io.Discard, "", log.LstdFlags)


@@ 15,79 17,183 @@ func init() {
	}
}

// type CodeArray struct {
// 	Mark      word.Word
// 	Legend    word.Word // Address
//  Size      word.Word
// 	Length    word.Word
// 	CodeWords []word.Word // Words are packed 8-elem Codes
// }

const (
	code_lgd_offset uint64 = 1
	code_sze_offset uint64 = 2
	code_len_offset uint64 = 3
	code_arr_offset uint64 = 4
)

type CodeArray struct {
	Mark      Word
	Legend    Word // Address
	Length    Word
	CodeWords []Word // Words are packed 8-elem Codes
}

func NewCodeArray(words ...Word) CodeArray {
	return CodeArray{
		Mark:      Word(CODE_ARR),
		Legend:    Word(VAL_ADDR),
		Length:    WordFromInt(uint32(len(words))),
		CodeWords: words,
	}
}

func (ca *CodeArray) SetCodes(cs ...Code) {
	origLen := ca.Length.AsInt()
	newLen := uint32(len(cs))
	byteSize := uint32(8)
	bs := make([]byte, 0, byteSize)
	currWordVacancy := ca.currentVacancy()
	if currWordVacancy {
		ca.getCodesFromWord(ca.currentIndex(), &bs)
	}
	for c := uint32(0); c < newLen; c++ {
		bs = append(bs, byte(cs[c]))
		if (c+1)%byteSize == 0 {
			ca.CodeWords = append(
				ca.CodeWords,
				Word(binary.BigEndian.Uint64(bs)),
			)
	// address in vm's heap
	id uint64
}

func NewCodeArray(
	vm *VirtualMachine,
	legAddr word.Word,
	words ...word.Word,
) CodeArray {
	wordsLen := uint32(len(words))
	wordsSize := uint32(code_arr_offset) + wordsLen
	caWords := make([]word.Word, 0, wordsSize)
	caWords = append(caWords,
		/* Mark:   */ word.Word(word.CODE_ARR),
		/* Legend: */ legAddr,
		/* Size:   */ word.FromInt(wordsSize),
		/* Length: */ word.FromInt(wordsLen),
	)
	caWords = append(caWords, words...)

	loc, err := vm.ReAllocate(caWords...)
	if err != nil {
		panic("allocation failed")
	}
	return CodeArray{uint64(loc)}
}

func ReviveCodeArray(vm *VirtualMachine, addr word.Word) CodeArray {
	i := addr.AsAddr()
	mark := vm.heap[i]
	if !(mark.IsCodeArrayMark()) {
		panic("not a code array mark")
	}
	legend := vm.heap[i+code_lgd_offset]
	if !(legend.IsAddress()) {
		panic("code array legend word is not an address")
	}
	size := vm.heap[i+code_sze_offset]
	if !(size.IsInteger()) {
		panic("code array object size word is not an integer")
	}
	return CodeArray{i}
}

// Given a possible new line and some new codes, what
// will the new total word size of this object?
func (ca CodeArray) NewSize(vm *VirtualMachine, newLine bool, cs ...Code) (size uint64) {
	var (
		origSize    uint64 = ca.Size(vm)
		newLength   int    = len(cs)
		freshLength int
		wholeWords  int
		remainder   int
	)
	if newLine {
		size = origSize + 1
		wholeWords = newLength / 8
		remainder = newLength % 8
		if remainder == 0 {
			size += uint64(wholeWords)
		} else {
			size += uint64(wholeWords + 1)
		}
	} else {
		size = origSize
		remainder = ca.getWordCodeCount(vm, uint64(ca.Size(vm)-1))
		freshLength = newLength - remainder
		wholeWords = freshLength / 8
		remainder = freshLength % 8
		if remainder == 0 {
			size += uint64(wholeWords)
		} else {
			size += uint64(wholeWords + 1)
		}
	}
	return
}

func (ca *CodeArray) SetCodes(
	vm *VirtualMachine,
	newLine bool,
	cs ...Code,
) {
	var (
		origLen  uint64 = uint64(ca.Length(vm))
		newLen   uint32 = uint32(len(cs))
		byteSize uint32 = 8
		codeID   uint32
		initSize uint32
		bs       []byte      = make([]byte, 0, byteSize)
		ws       []word.Word = make([]word.Word, 0, newLen/8)
	)
	if newLine {
		ws = append(ws, word.Sentinel())
	} else {
		if ca.lastWordVacancy(vm) {
			ca.getCodesFromWord(vm, uint64(ca.Length(vm))-1, &bs)
			initSize = uint32(len(bs))
			for ; codeID < newLen; codeID++ {
				bs = append(bs, byte(cs[codeID]))
				if initSize+codeID+1 == byteSize {
					vm.heap[ca.id+ca.Size(vm)] = word.Word(
						binary.BigEndian.Uint64(bs))
					bs = make([]byte, 0, byteSize)
					break
				}
			}

		}
	}
	for ; codeID < newLen; codeID++ {
		bs = append(bs, byte(cs[codeID]))
		if codeID+1 == byteSize {
			ws = append(ws, word.Word(binary.BigEndian.Uint64(bs)))
			bs = make([]byte, 0, byteSize)
		}
	}

	if len(bs) > 0 {
		caLogger.Println(bs)
		for range bs[len(bs):byteSize] {
			bs = append(bs, 0x0)
		}
		newCodeWord := Word(binary.BigEndian.Uint64(bs))
		if currWordVacancy {
			ca.CodeWords[ca.currentIndex()] = newCodeWord
		} else {
			ca.CodeWords = append(ca.CodeWords, newCodeWord)
		}
		ws = append(ws, word.Word(binary.BigEndian.Uint64(bs)))
	}
	ca.Length = WordFromInt(origLen + newLen)
}

func (ca *CodeArray) vacancy() uint32 {
	caLen := ca.Length.AsInt()
	modByte := caLen % 8
	return modByte
	finalLength := uint32(origLen) + uint32(len(ws))
	ca.SetSize(vm, finalLength+4)
	ca.SetLength(vm, finalLength)
	newId, _ := vm.Allocate(
		int(ca.id),
		int(origLen)+int(code_arr_offset),
		ws...,
	)
	if newId != ca.id {
		// vm.UpdateAddresses(ca.id, newId)
		ca.id = newId
	}
}

func (ca *CodeArray) currentVacancy() bool {
	return ca.vacancy() != 0
func (ca *CodeArray) lastWordVacancy(vm *VirtualMachine) bool {
	lastIndex := uint64(ca.Length(vm) - 1)
	return ca.getWordCodeCount(vm, lastIndex) != 0
}

func (ca *CodeArray) currentIndex() int {
	if ca.currentVacancy() {
		return len(ca.CodeWords) - 1
	} else {
		return len(ca.CodeWords)
	}
}
// func (ca *CodeArray) currentIndex(vm *VirtualMachine) int {
// 	if ca.lastWordVacancy(vm) {
// 		return int(ca.Length(vm)) - 1
// 	} else {
// 		return int(ca.Length(vm))
// 	}
// }

func (ca *CodeArray) getCodesFromWord(wi int, b *[]byte) {
func (ca CodeArray) getCodesFromWord(vm *VirtualMachine, wi uint64, b *[]byte) {
	var (
		buf  []byte = *b
		word Word   = ca.CodeWords[wi]
		buf  []byte    = *b
		word word.Word = vm.heap[ca.id+code_arr_offset+wi]
	)
	if word.IsSentinel() {
		panic("cannot get codes from sentinel")
	}
	for offset := 56; offset >= 0; offset -= 8 {
		i := uint64(word) >> offset
		subByte := byte(i)


@@ 99,15 205,66 @@ func (ca *CodeArray) getCodesFromWord(wi int, b *[]byte) {
	*b = buf
}

func (ca *CodeArray) GetCodes() []byte {
	cwLen := len(ca.CodeWords)
func (ca CodeArray) getWordCodeCount(vm *VirtualMachine, wi uint64) int {
	var (
		count int
		id    uint64    = ca.id + code_arr_offset + wi
		word  word.Word = vm.heap[id]
	)
	if word.IsSentinel() {
		panic("cannot get codes from sentinel")
	}
	for offset := 56; offset >= 0; offset -= 8 {
		i := uint64(word) >> offset
		subByte := byte(i)
		if subByte == 0 {
			break
		}
		count = count + 1
	}
	return count
}

func (ca CodeArray) GetCodes(vm *VirtualMachine) []byte {
	cwLen := uint64(ca.Length(vm))
	buf := make([]byte, 0, cwLen*8)
	for wordIndex := 0; wordIndex < cwLen; wordIndex++ {
		ca.getCodesFromWord(wordIndex, &buf)
	for wIndex := code_arr_offset; wIndex < cwLen; wIndex++ {
		if !(vm.heap[ca.id+wIndex].IsSentinel()) {
			ca.getCodesFromWord(vm, wIndex, &buf)
		}
	}
	return buf
}

func (ca *CodeArray) Len() int {
	return int(ca.Length.AsInt())
func (ca *CodeArray) GetLine(vm *VirtualMachine, codeIndex int) (lines int) {
	codes, cwLen, cwSize := 0, ca.Length(vm), ca.Size(vm)
	if uint32(codeIndex) > cwLen {
		panic("index greater than length")
	}
	for i := uint64(0); i < cwSize && codes <= codeIndex; i++ {
		if vm.heap[ca.id+code_arr_offset+i].IsSentinel() {
			lines += 1
		} else {
			codes += ca.getWordCodeCount(vm, i)
		}
	}
	return
}

func (ca CodeArray) Legend(vm *VirtualMachine) uint64 {
	return vm.heap[ca.id+code_lgd_offset].AsAddr()
}
func (ca CodeArray) Size(vm *VirtualMachine) uint64 {
	return uint64(vm.heap[ca.id+code_sze_offset].AsInt())
}
func (ca CodeArray) Length(vm *VirtualMachine) uint32 {
	return vm.heap[ca.id+code_len_offset].AsInt()
}

func (ca *CodeArray) SetSize(vm *VirtualMachine, s uint32) {
	vm.heap[ca.id+code_sze_offset] = word.FromInt(s)
}

func (ca *CodeArray) SetLength(vm *VirtualMachine, l uint32) {
	vm.heap[ca.id+code_len_offset] = word.FromInt(l)
}

A internal/vm/descriptor.go => internal/vm/descriptor.go +47 -0
@@ 0,0 1,47 @@
package vm

import "git.sr.ht/~madcapjake/grhumb/internal/word"

//	type Descriptor struct {
//		Mark Word // immutable, mutable, submap
//		Name Word // address to TextMap
//		Data Word // constant, field offset, or
//	}

const (
	desc_name_offset uint64 = 1
	desc_data_offset uint64 = 2
)

type Descriptor struct {
	vm *VirtualMachine
	id uint64
	at []word.Word
}

func NewDescriptor(
	vm *VirtualMachine,
	name word.Word, // string address
	kind uint64,
	value word.Word,
) Descriptor {
	descWords := make([]word.Word, 0, 2)
	descWords = append(descWords, word.Word(kind))
	descWords = append(descWords, name)
	descWords = append(descWords, value)
	loc, _ := vm.ReAllocate(descWords...)
	return Descriptor{vm, loc, vm.heap[loc : loc+2]}
}

func ReviveDescriptor(vm *VirtualMachine, addr word.Word) Descriptor {
	i := addr.AsAddr()
	mark := vm.heap[i]
	if !(mark.IsDescMark()) {
		panic("not a descriptor mark")
	}
	name := vm.heap[i+desc_name_offset]
	if !(name.IsAddress()) {
		panic("desciptor name word is not an address")
	}
	return Descriptor{vm, i, vm.heap[i : i+2]}
}

A internal/vm/legend.go => internal/vm/legend.go +180 -0
@@ 0,0 1,180 @@
package vm

import "git.sr.ht/~madcapjake/grhumb/internal/word"

// type Legend struct {
// 	Mark       Word
// 	MetaLegend Word
// }

// type BaseMapLegend struct {
// 	Legend
// 	TrashSweep Word
// 	Size       Word
// 	Length     Word
// 	ReqLink    Word // pointer, circular dependency list
// 	DepLink    Word // pointer, circular dependency list
// }

// type MainMapLegend struct {
// 	BaseMapLegend
// 	Field []Descriptor
// }

// type ListMapLegend struct {
// 	BaseMapLegend
// 	Items Word
// 	Field []Descriptor
// }

// type TextMapLegend struct {
// 	BaseMapLegend
// 	Runes Word
// 	Field []Descriptor
// }

// type FuncMapLegend struct {
// 	BaseMapLegend
// 	Chunk Word
// 	Field []Descriptor
// }

// type MetaMapLegend struct {
// 	BaseMapLegend
// 	Field []Descriptor
// }

type MainMapLegend []word.Word
type ListMapLegend []word.Word
type TextMapLegend []word.Word
type FuncMapLegend []word.Word
type MetaMapLegend []word.Word

const (
	base_lgd_offset uint64 = 1
	base_swp_offset uint64 = 2
	base_sze_offset uint64 = 3
	base_len_offset uint64 = 3
	base_req_offset uint64 = 4
	base_dep_offset uint64 = 5
	main_fld_offset uint64 = 6
	list_arr_offset uint64 = 6
	list_fld_offset uint64 = 7
	text_arr_offset uint64 = 6
	text_fld_offset uint64 = 7
	func_chu_offset uint64 = 6
	func_fld_offset uint64 = 7
)

func NewBaseMapLegend(
	mark word.Word,
	legAddr word.Word,
	length uint32,
	dWords []word.Word,
) []word.Word {
	words := make([]word.Word, 0, 8)
	words = append(words,
		/* Mark:    */ mark,
		/* Legend:  */ legAddr,
		/* Sweep:   */ word.FromAddress(0),
		/* Size:    */ word.FromInt(7+length),
		/* Length:  */ word.FromInt(length),
		/* ReqLink: */ word.FromAddress(0),
		/* DepLink: */ word.FromAddress(0),
	)
	words = append(words, dWords...)
	return words
}

func wordsFromDescriptors(
	d ...Descriptor,
) (buf []word.Word) {
	// buf = make([]word.Word, 0, len(d)*3)
	// for descIndex := range d {
	// 	buf = append(buf, d[descIndex]...)
	// }
	return
}

func NewMainMapLegend(
	legAddr word.Word,
	descs ...Descriptor,
) MainMapLegend {
	descCount := uint32(len(descs))
	dWords := wordsFromDescriptors(descs...)
	return NewBaseMapLegend(
		word.Word(word.MAIN_LGD),
		legAddr,
		descCount,
		dWords,
	)
}

func NewListMapLegend(
	legAddr word.Word,
	descs ...Descriptor,
) ListMapLegend {
	descCount := uint32(len(descs))
	dWords := wordsFromDescriptors(descs...)
	return NewBaseMapLegend(
		word.Word(word.LIST_LGD),
		legAddr,
		descCount,
		dWords,
	)
}

func NewTextMapLegend(
	legAddr word.Word,
	descs ...Descriptor,
) TextMapLegend {
	descCount := uint32(len(descs))
	dWords := wordsFromDescriptors(descs...)
	return NewBaseMapLegend(
		word.Word(word.TEXT_LGD),
		legAddr,
		descCount,
		dWords,
	)
}

func NewFuncMapLegend(
	legAddr word.Word,
	descs ...Descriptor,
) FuncMapLegend {
	descCount := uint32(len(descs))
	dWords := wordsFromDescriptors(descs...)
	return NewBaseMapLegend(
		word.Word(word.FUNC_LGD),
		legAddr,
		descCount,
		dWords,
	)
}

func NewMetaMapLegend(descs ...Descriptor) MetaMapLegend {
	descCount := uint32(len(descs))
	dWords := wordsFromDescriptors(descs...)
	return NewBaseMapLegend(
		word.Word(word.META_LGD),
		word.FromAddress(0),
		descCount,
		dWords,
	)
}

type ArrayLegend struct {
	Mark       word.Word
	MetaLegend word.Word
	TrashSweep word.Word
	Field      []Descriptor
}

func NewArrayLegend(mark word.Word) ArrayLegend {
	return ArrayLegend{
		Mark:       mark,
		MetaLegend: word.FromAddress(0),
		TrashSweep: word.Empty(),
		Field:      make([]Descriptor, 0),
	}
}

M internal/vm/map.go => internal/vm/map.go +24 -52
@@ 1,66 1,38 @@
package vm

type Map struct {
	Mark   Word
	Legend Word // Address
	Field  []Word
}
import "git.sr.ht/~madcapjake/grhumb/internal/word"

func NewMap(count uint32, legAddr Word) Map {
	return Map{
		Word(MAIN_MAP),
		WordFromAddress(0),
		make([]Word, count),
	}
}
// type RhumbMap struct {
// 	Mark   word.Word
// 	Legend word.Word
// 	Field  []word.Word
// }

type MapLegend struct {
	Mark       Word
	MetaLegend Word
	TrashSweep Word
	Length     Word
	Count      Word
	PrevLegend Word // pointer, circular dependency list
	NextLegend Word // pointer, circular dependency list
	Field      []LegendFieldDescriptor
type RhumbMap []word.Word

func NewMap(mark word.Word, legend word.Word, fields ...word.Word) RhumbMap {
	rmap := make([]word.Word, 0, 2+len(fields))
	rmap = append(rmap, mark, legend)
	rmap = append(rmap, fields...)
	return rmap
}

func NewMapLegend() MapLegend {
	return MapLegend{
		Word(MAIN_LGD),
		WordFromAddress(0),
		EmptyWord(),
		WordFromInt(0),
		WordFromInt(0),
		WordFromAddress(0),
		WordFromAddress(0),
		make([]LegendFieldDescriptor, 0),
	}
func NewMainMap(count uint32, legAddr word.Word) RhumbMap {
	return NewMap(word.Word(word.MAIN_MAP), legAddr)
}

type RoutineLegend struct {
	Mark       Word
	MetaLegend Word // pointer to the toplevel MapLegend
	TrashSweep Word
	Length     Word
	Count      Word
	PrevLegend Word // pointer, circular dependency list
	NextLegend Word // pointer, circular dependency list
	Code       Word // pointer to CodeArray
	Field      []LegendFieldDescriptor
func NewListMap(count uint32, legAddr word.Word) RhumbMap {
	return NewMap(word.Word(word.LIST_MAP), legAddr)
}

type ArrayLegend struct {
	Mark       Word
	MetaLegend Word
	TrashSweep Word
	Parent     LegendFieldDescriptor
func NewTextMap(count uint32, legAddr word.Word) RhumbMap {
	return NewMap(word.Word(word.TEXT_MAP), legAddr)
}

// T
func NewFuncMap(count uint32, legAddr word.Word) RhumbMap {
	return NewMap(word.Word(word.FUNC_MAP), legAddr)
}

type LegendFieldDescriptor struct {
	Mark Word // immutable, mutable, subfield
	Name Word // address to TextMap
	Data Word // constant, field offset, or
func NewMetaMap(count uint32, legAddr word.Word) RhumbMap {
	return NewMap(word.Word(word.META_MAP), legAddr)
}

M internal/vm/primitives.go => internal/vm/primitives.go +9 -22
@@ 2,6 2,8 @@ package vm

import (
	"fmt"

	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

/* Phase 1


@@ 9,21 11,6 @@ import (
 * _-/_
 */

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

func expBySquaring(x, n uint32) uint32 {
	if n == 0 {
		return 1


@@ 42,7 29,7 @@ func expBySquaring(x, n uint32) uint32 {
	return x * y
}

func (vm *VirtualMachine) popStack() (popped Word) {
func (vm *VirtualMachine) popStack() (popped word.Word) {
	idx := len(vm.stack) - 1
	popped = vm.stack[idx]
	vm.stack = vm.stack[:idx]


@@ 73,38 60,38 @@ func (vm *VirtualMachine) assignLabel() {

func (vm *VirtualMachine) addTwoInts() {
	val1, val2 := vm.gatherTwoInts()
	vm.stack = append(vm.stack, WordFromInt(val1+val2))
	vm.stack = append(vm.stack, word.FromInt(val1+val2))
	logAddedToStack(vm.stack, fmt.Sprint(val1, " + ", val2))
}

func (vm *VirtualMachine) subTwoInts() {
	val1, val2 := vm.gatherTwoInts()
	vm.stack = append(vm.stack, WordFromInt(val1-val2))
	vm.stack = append(vm.stack, word.FromInt(val1-val2))
	logAddedToStack(vm.stack, fmt.Sprint(val1, " - ", val2))
}

func (vm *VirtualMachine) mulTwoInts() {
	val1, val2 := vm.gatherTwoInts()
	vm.stack = append(vm.stack, WordFromInt(val1*val2))
	vm.stack = append(vm.stack, word.FromInt(val1*val2))
	logAddedToStack(vm.stack, fmt.Sprint(val1, " x ", val2))
}

func (vm *VirtualMachine) divTwoInts() {
	val1, val2 := vm.gatherTwoInts()
	vm.stack = append(vm.stack, WordFromInt(val1/val2))
	vm.stack = append(vm.stack, word.FromInt(val1/val2))
	logAddedToStack(vm.stack, fmt.Sprint(val1, " / ", val2))
}

func (vm *VirtualMachine) expTwoInts() {
	val1, val2 := vm.gatherTwoInts()
	vm.stack = append(vm.stack, WordFromInt(expBySquaring(val1, val2)))
	vm.stack = append(vm.stack, word.FromInt(expBySquaring(val1, val2)))
	logAddedToStack(vm.stack, fmt.Sprint(val1, " ^ ", val2))
}

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

// Same as routine

M internal/vm/rune_array.go => internal/vm/rune_array.go +130 -36
@@ 1,53 1,147 @@
package vm

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"unicode/utf8"

	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

// type RuneArray struct {
// 	Mark   word.Word
// 	Legend word.Word // Address
// 	Size   word.Word
// 	Length word.Word
// 	Runes  []word.Word // Words are packed 2-elem runes
// }

const (
	rune_lgd_offset uint64 = 1
	rune_sze_offset uint64 = 2
	rune_len_offset uint64 = 3
	rune_arr_offset uint64 = 4
)

type RuneArray struct {
	Mark   Word
	Legend Word // Address
	Length Word
	Runes  []Word // Words are packed 2-elem runes
	// address in vm's heap
	id uint64
}

func NewRuneArray(addr Word, words ...Word) RuneArray {
	return RuneArray{
		Word(RUNE_ARR),
		addr,
		WordFromInt(uint32(len(words))),
		words,
func RuneWords(str string) (words []word.Word) {
	var (
		rn         rune
		sz, length int
	)
	for id := 0; id < len(str); id += sz {
		rn, sz = utf8.DecodeRuneInString(str[id:])
		words = append(words, word.FromRune(rn))
		length++
	}
	return
}

func (ra *RuneArray) SetRunes(rs []rune) {
	var buf bytes.Buffer
	ra.Runes = nil
	for r := range rs {
		_, err := buf.WriteRune(rs[r])
		if err != nil {
			fmt.Println("buf.WriteRune failed:", err)
		}
		if (r+1)%2 == 0 {
			ra.Runes = append(
				ra.Runes,
				Word(binary.LittleEndian.Uint64(buf.Bytes())),
			)
			buf.Reset()
		}
func NewRuneArray(
	vm *VirtualMachine,
	legAddr word.Word,
	words ...word.Word,
) RuneArray {
	wordsLen := uint32(len(words))
	wordsSize := uint32(code_arr_offset) + wordsLen
	raWords := make([]word.Word, 0, wordsSize)
	raWords = append(raWords,
		/* Mark:   */ word.Word(word.RUNE_ARR),
		/* Legend: */ legAddr,
		/* Size:   */ word.FromInt(wordsSize),
		/* Length: */ word.FromInt(wordsLen),
	)
	raWords = append(raWords, words...)

	loc, err := vm.ReAllocate(raWords...)
	if err != nil {
		panic("allocation failed")
	}
	ra.Length = WordFromInt(uint32(len(ra.Runes)))
	return RuneArray{uint64(loc)}
}

func (ra *RuneArray) GetRunes() []rune {
	b := make([]byte, 8)
	buf := new(bytes.Buffer)
	for word := range ra.Runes {
		binary.LittleEndian.PutUint64(b, uint64(word))
		buf.Write(b)
		b = nil
func ReviveRuneArray(vm *VirtualMachine, addr word.Word) RuneArray {
	i := addr.AsAddr()
	mark := vm.heap[i]
	if !(mark.IsRuneArrayMark()) {
		panic("not a rune array mark")
	}
	legend := vm.heap[i+rune_lgd_offset]
	if !(legend.IsAddress()) {
		panic("rune array legend word is not an address")
	}
	size := vm.heap[i+rune_sze_offset]
	if !(size.IsInteger()) {
		panic("rune array object size word is not an integer")
	}
	return RuneArray{i}
}

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

func (ra RuneArray) Legend(vm *VirtualMachine) uint64 {
	return vm.heap[ra.id+rune_lgd_offset].AsAddr()
}
func (ra RuneArray) Size(vm *VirtualMachine) uint64 {
	return uint64(vm.heap[ra.id+rune_sze_offset].AsInt())
}
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:] {
		bytes := make([]byte, 8)
		binary.LittleEndian.PutUint64(bytes, uint64(vm.heap[ra.id+uint64(i)]))
		buf = append(buf, rune(binary.LittleEndian.Uint32(bytes[:4])))
		buf = append(buf, rune(binary.LittleEndian.Uint32(bytes[4:])))
	}
	return buf
}

func (ra RuneArray) String(vm *VirtualMachine) string {
	return string(ra.Runes(vm))
}

func (ra RuneArray) Rune(vm *VirtualMachine, i uint32) rune {
	addr := i / 2
	offset := i % 2
	bytes := make([]byte, 8)
	binary.LittleEndian.PutUint64(bytes, uint64(vm.heap[ra.id+uint64(addr)]))
	if offset == 0 {
		return rune(binary.LittleEndian.Uint32(bytes[:4]))
	} else {
		return rune(binary.LittleEndian.Uint32(bytes[4:]))
	}
}

func (ra RuneArray) SetSize(vm *VirtualMachine, s uint32) {
	vm.heap[ra.id+rune_sze_offset] = word.FromInt(s)
}

func (ra RuneArray) SetLength(vm *VirtualMachine, l uint32) {
	vm.heap[ra.id+rune_len_offset] = word.FromInt(l)
}

func (ra RuneArray) SetRune(vm *VirtualMachine, i uint32, r rune) {
	var (
		addr      uint64 = uint64(i) / 2
		id        uint64 = ra.id + addr
		offset    uint32 = i % 2
		runeBytes []byte = make([]byte, 4)
		wordBytes []byte = make([]byte, 8)
	)
	binary.LittleEndian.PutUint32(runeBytes, uint32(r))
	binary.LittleEndian.PutUint64(wordBytes, uint64(vm.heap[id]))
	if offset == 0 {
		copy(wordBytes, runeBytes)
	} else {
		for i := range runeBytes {
			wordBytes[i+4] = runeBytes[i]
		}
	}
	return []rune(buf.String())
	vm.heap[id] = word.Word(binary.LittleEndian.Uint64(wordBytes))
}

M internal/vm/vm.go => internal/vm/vm.go +250 -38
@@ 5,6 5,8 @@ import (
	"io"
	"log"
	"os"

	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

var vmLogger = log.New(io.Discard, "", log.LstdFlags)


@@ 16,43 18,202 @@ func init() {
}

type VirtualMachine struct {
	heap  []Word
	heap  []word.Word
	free  []bool
	stack []Word
	stack []word.Word
	scope []map[string]int
	main  Chunk
}

func (vm VirtualMachine) DebugHeap() {
	width := 4
	for i := range vm.heap {
		if i%width == 0 {
			fmt.Println()
		}
		if vm.free[i] {
			fmt.Printf("{%3v:           }", i)
		} else {
			fmt.Printf("[%3v: %-9s ]", i, vm.heap[i].Debug())
		}
	}
	fmt.Println()
}

func NewVirtualMachine() *VirtualMachine {
	vm := new(VirtualMachine)
	vm.heap = make([]Word, 0)
	vm.free = make([]bool, 0)
	vm.stack = make([]Word, 0)
	const init_heap_len int = 10
	vm.heap = make([]word.Word, 0, init_heap_len)
	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.main = NewChunk(nil, nil)
	vm.main = NewChunk(vm)
	return vm
}

// Traverses the free list until a sufficiently-sized chunk
// of free slots is available to place the word arguments.
//
// Returns the index of the final heap location.
func (vm *VirtualMachine) ReAllocate(ws ...word.Word) (uint64, error) {
	obj := *vm
	size := len(ws)
	if size == 0 {
		return 0, fmt.Errorf("no words provided")
	}
	var first, next int
	for first = 0; first < len(obj.free); first += next {
		next = 0
		if obj.free[first] {
			if size == 1 {
				vmLogger.Println("adding word to index:", first)
				obj.heap[first] = ws[0]
				obj.free[first] = false
				obj.DebugHeap()
				*vm = obj
				return uint64(first), nil
			} else {
				last := first + size
				for next = range obj.free[first+1 : last] {
					if !(obj.free[next]) {
						next++
						break
					} else if next == last {
						vmLogger.Println("appending words to index:", first)
						obj.allocInPlace(first, last, ws...)
						obj.DebugHeap()
						*vm = obj
						return uint64(first), nil
					}
				}
			}
		} else {
			next = 1
		}
	}
	// no available chunk in existing memory locations.
	first = len(obj.heap)
	vmLogger.Println("appending words to end:", first)
	obj = appendRhumb(obj, ws)
	*vm = obj
	return uint64(first), nil
}

// Traverses the free list directly after the current
// used memory for this object. If there is sufficient free
// slots, then allocInPlace. Otherwise, call ReAllocate.
//
// Returns the index of the final heap location.
func (vm *VirtualMachine) Allocate(
	loc int,
	oldSize int,
	ws ...word.Word,
) (uint64, error) {
	var (
		obj       VirtualMachine = *vm
		newSize   int            = len(ws)
		lastOldId int            = loc + oldSize
	)
	if newSize == 0 {
		return 0, fmt.Errorf("no words provided")
	}
	var i int = lastOldId
	if i == len(obj.free) {
		vmLogger.Println("appending words to end:", i)
		obj = appendRhumb(obj, ws)
		*vm = obj
		return uint64(loc), nil
	}

	if obj.free[i] {
		if newSize == 1 {
			vmLogger.Println("appending word to index:", i)
			obj.heap[i] = ws[0]
			obj.free[i] = false
			obj.DebugHeap()
			*vm = obj
			return uint64(loc), nil
		} else {
			last := i + newSize
			for i = i + 1; i < last; i++ {
				if !(obj.free[i]) {
					break
				} else if i == last {
					first := lastOldId
					vmLogger.Println("appending words to index:", first)
					obj.allocInPlace(first, last, ws...)
					obj.DebugHeap()
					*vm = obj
					return uint64(loc), nil
				}
			}
		}
	}
	// Subsequent memory spots unavailable, search for any
	// available memory spot across heap
	totalWords := make([]word.Word, 0, oldSize+newSize)
	totalWords = append(totalWords, obj.heap[loc:lastOldId]...)
	totalWords = append(totalWords, ws...)

	for f := range obj.free[loc:lastOldId] {
		obj.free[loc+f] = true
	}

	id, err := obj.ReAllocate(totalWords...)
	*vm = obj
	return id, err
}

// Appends to heap and free slices, used in allocate and reallocate
func appendRhumb(vm VirtualMachine, ws []word.Word) VirtualMachine {
	for i := range ws {
		vm.heap = append(vm.heap, ws[i])
		vm.free = append(vm.free, false)
	}
	vm.DebugHeap()
	return vm
}

// 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 {
		vm.heap[hID], vm.free[hID] = ws[wID], false
	}
}

// func (vm VirtualMachine) UpdateAddresses(oldId, newId uint64) {
// 	for i, free := range vm.free {
// 		if !(free) {
// 			w := vm.heap[i]
// 			if w.IsAddress() && w.AsAddr() == oldId {
// 				vm.heap[i] = word.FromAddress(int(newId))
// 			}
// 		}
// 	}
// }

func (vm *VirtualMachine) WriteCodeToMain(
	line int,
	ref InstrRef,
	codeFactory func(i uint32) []Code,
	lit word.Word,
	codeFactory func(i uint64) []Code,
) {
	id := vm.main.AddReference(ref)
	id, _ := vm.main.AddLiteral(vm, lit)
	codes := codeFactory(id)
	vm.main.WriteCode(line, codes)
	vm.main.WriteCode(vm, line, codes)
}

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

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

func logAddedToStack(stack []Word, txt string) {
func logAddedToStack(stack []word.Word, txt string) {
	logStr := fmt.Sprintf("▏ %-7s ⇾ [", txt)
	for s := range stack {
		logStr = fmt.Sprint(logStr, " ")


@@ 65,39 226,90 @@ func logAddedToStack(stack []Word, txt string) {
	vmLogger.Println(logStr, " ]")
}

func (vm *VirtualMachine) AddValueToStack(ir InstrRef) {
	vm.stack = append(vm.stack, NewWord(ir.value))
	logAddedToStack(vm.stack, ir.text)
func locateScopeLabel(
	scopes []map[string]int, lbl string,
) (
	idx int, ok bool,
) {
	topScope := len(scopes) - 1
	for i := topScope; i >= 0; i-- {
		idx, ok = scopes[i][lbl]
		if ok {
			return
		}
	}
	return
}

// Currently just for traversing the scope outwardly
func (vm *VirtualMachine) SubmitLocalRequest(ir InstrRef) {
	idx, ok := locateScopeLabel(vm.scope, ir.text)
	if ok {
		// TODO: Invoke address, skip addrRef
		vm.stack = append(vm.stack, WordFromAddress(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, WordFromAddress(idx))
	logAddedToStack(vm.stack, ir.text)
// func (vm *VirtualMachine) TextMapFromString(s string) word.Word {
// 	var first word.Word
// 	var rn rune
// 	var sz, runeCount int
// 	for si := 0; si < len(s); si += sz {
// 		rn, sz = utf8.DecodeRuneInString(s[si:])
// 		vm.heap = append(vm.heap, word.FromRune(rn))
// 		if si == 0 {
// 			first = word.FromAddress(len(vm.heap) - 1)
// 		}
// 		runeCount += 1
// 	}
// 	last := int(first.AsInt()) + runeCount
// 	legend := TextMapLegend{
// 		BaseMapLegend: BaseMapLegend{
// 			Legend: Legend{
// 				Mark:       word.Word(word.TEXT_LGD),
// 				MetaLegend: word.FromAddress(0),
// 			},
// 			TrashSweep: Empty(),
// 			Length:     word.FromInt(0),
// 			Count:      word.FromInt(0),
// 			ReqLink:    word.FromAddress(0),
// 			DepLink:    word.FromAddress(0),
// 		},
// 		Runes: NewRuneArray(first, vm.heap[first.AsInt():last]...),
// 	}
// }

func (vm *VirtualMachine) AddLiteralToStack(literal word.Word) {
	vm.stack = append(vm.stack, literal)
	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)
}

// Used for traversing maps and legends
func (vm *VirtualMachine) SubmitInnerRequest(ir InstrRef) {
func (vm *VirtualMachine) SubmitInnerRequest(label word.Word) {

}

// Used for traversing submaps and legends
func (vm *VirtualMachine) SubmitUnderRequest(ir InstrRef) {
func (vm *VirtualMachine) SubmitUnderRequest(label word.Word) {
}

// Used for traversing primitives and compilations
func (vm *VirtualMachine) SubmitOuterRequest(ir InstrRef) {
	switch ir.text {
func (vm *VirtualMachine) SubmitOuterRequest(label word.Word) {
	// FIXME: locate text
	text := ""
	switch text {
	case ".=", ":=":
		vm.assignLabel()
	case "++":


@@ 124,8 336,8 @@ func (vm *VirtualMachine) SubmitOuterRequest(ir InstrRef) {
	}
}

// Used for signalling across Rhumb
func (vm *VirtualMachine) SubmitEventRequest(ir InstrRef) {}
// // Used for signalling across Rhumb
// func (vm *VirtualMachine) SubmitEventRequest(ir InstrRef) {}

// Used for replying to signals across Rhumb
func (vm *VirtualMachine) SubmitReplyRequest(ir InstrRef) {}
// // Used for replying to signals across Rhumb
// func (vm *VirtualMachine) SubmitReplyRequest(ir InstrRef) {}

M internal/vm/word_array.go => internal/vm/word_array.go +88 -28
@@ 1,45 1,105 @@
package vm

import "fmt"
import (
	"fmt"

	"git.sr.ht/~madcapjake/grhumb/internal/word"
)

// type WordArray struct {
// 	Mark   word.Word
// 	Legend word.Word // Address
// 	Length word.Word // Integer
// 	Words  []word.Word
// }

const (
	word_lgd_offset uint64 = 1
	word_len_offset uint64 = 2
	word_arr_offset uint64 = 3
)

type WordArray struct {
	Mark   Word
	Legend Word // Address
	Length Word
	Words  []Word // Words are normal single words
}

func NewWordArray(words ...Word) WordArray {
	return WordArray{
		Word(MAIN_ARR),
		Word(VAL_ADDR),
		WordFromInt(uint32(len(words))),
		words,
	// address in vm's heap
	id uint64
}

func (wa WordArray) Legend(vm *VirtualMachine, i int) word.Word {
	return vm.heap[i]
}

func (wa WordArray) Length(vm *VirtualMachine) int {
	return int(vm.heap[wa.id+word_arr_offset].AsInt())
}

func NewWordArray(
	vm *VirtualMachine,
	legAddr word.Word,
	words ...word.Word,
) WordArray {
	wordsLen := uint32(len(words))
	wordsSize := uint32(code_arr_offset) + wordsLen
	waWords := make([]word.Word, 0, wordsSize)
	waWords = append(waWords,
		/* Mark:   */ word.Word(word.MAIN_ARR),
		/* Legend: */ legAddr,
		/* Length: */ word.FromInt(wordsLen),
	)
	waWords = append(waWords, words...)

	loc, err := vm.ReAllocate(waWords...)
	if err != nil {
		panic("allocation failed")
	}
	return WordArray{uint64(loc)}
}

func (wa *WordArray) IndexOf(w Word) (idx int, err error) {
	for i := range wa.Words {
		if wa.Words[i] == w {
			return i, nil
		}
func ReviveWordArray(vm *VirtualMachine, addr word.Word) WordArray {
	i := addr.AsAddr()
	mark := vm.heap[i]
	if !(mark.IsMainArrayMark()) {
		panic("not a word array mark")
	}
	legend := vm.heap[i+word_lgd_offset]
	if !(legend.IsAddress()) {
		// fmt.Println(legend.Debug())
		panic("word array legend word is not an address")
	}
	return 0, fmt.Errorf("couldn't find word")
	length := vm.heap[i+word_len_offset]
	if !(length.IsInteger()) {
		panic("word array object length word is not an integer")
	}
	return WordArray{i}
}

func (wa *WordArray) Add(w Word) {
	wa.Words = append(wa.Words, w)
	wa.Length = WordFromInt(uint32(len(wa.Words)))
func (wa WordArray) IndexOf(vm *VirtualMachine, w word.Word) (idx int, err error) {
	len := wa.Length(vm)
	for i := range make([]int, len) {
		found := w.Equals(wa.Get(vm, i))
		if found {
			return i, nil
		}
	}
	return -1, fmt.Errorf("couldn't find word")
}

func (wa *WordArray) Get(i uint32) Word {
	return wa.Words[i]
func (wa *WordArray) Append(vm *VirtualMachine, newWords ...word.Word) (uint64, error) {
	id, err := vm.Allocate(int(wa.id), wa.Size(vm), newWords...)
	if err != nil {
		panic("word array append failed")
	} else {
		if id != wa.id {
			// vm.UpdateAddresses(wa.id, id)
			wa.id = id
		}
		return id, err
	}
}

func (wa *WordArray) All() []Word {
	return wa.Words
func (wa WordArray) Get(vm *VirtualMachine, i int) word.Word {
	return vm.heap[wa.id+word_arr_offset+uint64(i)]
}

func (wa *WordArray) Count() int {
	return int(wa.Length.AsInt())
func (wa WordArray) Size(vm *VirtualMachine) int {
	return wa.Length(vm) + 3
}

R internal/vm/word.go => internal/word/word.go +107 -52
@@ 1,4 1,4 @@
package vm
package word

import (
	"bytes"


@@ 41,7 41,7 @@ const MARK_ARR uint64 = 0x7F_FE_80_00_00_00_00_00

const MAIN_ARR uint64 = 0x7F_FE_80_20_00_00_00_00
const RUNE_ARR uint64 = 0x7F_FE_80_30_00_00_00_00
const CODE_ARR uint64 = 0x7F_FE_40_40_00_00_00_00
const CODE_ARR uint64 = 0x7F_FE_80_40_00_00_00_00
const META_ARR uint64 = 0x7F_FE_80_F0_00_00_00_00

const MARK_LGD uint64 = 0x7F_FE_C0_00_00_00_00_00


@@ 50,6 50,7 @@ const MAIN_LGD uint64 = 0x7F_FE_C0_10_00_00_00_00
const LIST_LGD uint64 = 0x7F_FE_C0_20_00_00_00_00
const TEXT_LGD uint64 = 0x7F_FE_C0_30_00_00_00_00
const FUNC_LGD uint64 = 0x7F_FE_C0_40_00_00_00_00
const ARRA_LGD uint64 = 0x7F_FE_C0_50_00_00_00_00
const META_LGD uint64 = 0x7F_FE_C0_F0_00_00_00_00

const MARK_DES uint64 = 0x7F_FF_00_00_00_00_00_00


@@ 75,26 76,27 @@ const MASK_TWO uint64 = 0x7F_FF_C0_F0_00_00_00_00
func NewWord(a any) Word {
	switch a := a.(type) {
	case float64:
		return WordFromFloat(a)
		return FromFloat(a)
	case float32:
		return WordFromFloat(float64(a))
		return FromFloat(float64(a))
	case bool:
		return WordFromBool(a)
		return FromBool(a)
	case int:
		return WordFromInt(uint32(a))
		return FromInt(uint32(a))
	case int64:
		return WordFromInt(uint32(a))
		return FromInt(uint32(a))
	case rune:
		return WordFromRune(a)
		return FromRune(a)
	default:
		return EmptyWord()
		return Empty()
	}
}

func EmptyWord() Word      { return Word(VAL_EMPT) }
func NotANumberWord() Word { return Word(ERR_NUMB) }
func Empty() Word    { return Word(VAL_EMPT) }
func NAN() Word      { return Word(ERR_NUMB) }
func Sentinel() Word { return Word(SENTINEL) }

func WordFromFloat(f float64) Word {
func FromFloat(f float64) Word {
	var bytes [8]byte
	fbits := math.Float64bits(f)
	bytes[0] = byte(fbits >> 56)


@@ 108,17 110,17 @@ func WordFromFloat(f float64) Word {
	return Word(binary.LittleEndian.Uint64(bytes[:]))
}

func WordFromBool(b bool) Word {
func FromBool(b bool) Word {
	if b {
		return Word(VAL_TRUE)
	} else {
		return Word(VAL_FALS)
	}
}
func WordFromInt(i uint32) Word  { return Word(VAL_NUMB | uint64(i)) }
func WordFromRune(r rune) Word   { return Word(VAL_RUNE | uint64(r)) }
func WordFromSym(a int) Word     { return Word(VAL_SYMB | uint64(a)) }
func WordFromAddress(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 int) Word { return Word(VAL_ADDR | uint64(a)) }

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



@@ 135,43 137,45 @@ func (w Word) IsSym() bool     { return w.isVal(VAL_SYMB) }

func (w Word) IsSentinel() bool { return uint64(w) == SENTINEL }

func (w Word) isMark(m uint64) bool { return uint64(w)&MASK_ONE == m }
func (w Word) isMark(m uint64) bool  { return uint64(w)&MASK_ONE == m }
func (w Word) isMark2(m uint64) bool { return uint64(w)&MASK_TWO == m }

func (w Word) IsNAN() bool { return w.isMark(ERR_NUMB) }

func (w Word) IsAnyMark() bool     { return uint64(w)&MASK_ONE > MARK_MAP }
func (w Word) IsMapMark() bool     { return w.isMark(MARK_MAP) }
func (w Word) IsMainMapMark() bool { return w.isMark(MAIN_MAP) }
func (w Word) IsListMapMark() bool { return w.isMark(LIST_MAP) }
func (w Word) IsTextMapMark() bool { return w.isMark(TEXT_MAP) }
func (w Word) IsFuncMapMark() bool { return w.isMark(FUNC_MAP) }
func (w Word) IsMetaMapMark() bool { return w.isMark(META_MAP) }
func (w Word) IsMainMapMark() bool { return w.isMark2(MAIN_MAP) }
func (w Word) IsListMapMark() bool { return w.isMark2(LIST_MAP) }
func (w Word) IsTextMapMark() bool { return w.isMark2(TEXT_MAP) }
func (w Word) IsFuncMapMark() bool { return w.isMark2(FUNC_MAP) }
func (w Word) IsMetaMapMark() bool { return w.isMark2(META_MAP) }

func (w Word) IsArrayMark() bool     { return w.isMark(MARK_ARR) }
func (w Word) IsMainArrayMark() bool { return w.isMark(MAIN_ARR) }
func (w Word) IsRuneArrayMark() bool { return w.isMark(RUNE_ARR) }
func (w Word) IsCodeArrayMark() bool { return w.isMark(CODE_ARR) }
func (w Word) IsMetaArrayMark() bool { return w.isMark(META_ARR) }
func (w Word) IsMainArrayMark() bool { return w.isMark2(MAIN_ARR) }
func (w Word) IsRuneArrayMark() bool { return w.isMark2(RUNE_ARR) }
func (w Word) IsCodeArrayMark() bool { return w.isMark2(CODE_ARR) }
func (w Word) IsMetaArrayMark() bool { return w.isMark2(META_ARR) }

func (w Word) IsLegendMark() bool     { return w.isMark(MARK_LGD) }
func (w Word) IsMainLegendMark() bool { return w.isMark(MAIN_LGD) }
func (w Word) IsListLegendMark() bool { return w.isMark(LIST_LGD) }
func (w Word) IsTextLegendMark() bool { return w.isMark(TEXT_LGD) }
func (w Word) IsFuncLegendMark() bool { return w.isMark(FUNC_LGD) }
func (w Word) IsMetaLegendMark() bool { return w.isMark(META_LGD) }
func (w Word) IsMainLegendMark() bool { return w.isMark2(MAIN_LGD) }
func (w Word) IsListLegendMark() bool { return w.isMark2(LIST_LGD) }
func (w Word) IsTextLegendMark() bool { return w.isMark2(TEXT_LGD) }
func (w Word) IsFuncLegendMark() bool { return w.isMark2(FUNC_LGD) }
func (w Word) IsArraLegendMark() bool { return w.isMark2(ARRA_LGD) }
func (w Word) IsMetaLegendMark() bool { return w.isMark2(META_LGD) }

func (w Word) IsDescMark() bool        { return w.isMark(MARK_DES) }
func (w Word) IsIntegerDescMark() bool { return w.isMark(DES_NUMB) }
func (w Word) IsFloatDescMark() bool   { return w.isMark(DES_FLOA) }
func (w Word) IsBoolDescMark() bool    { return w.isMark(DES_BOOL) }
func (w Word) IsRuneDescMark() bool    { return w.isMark(DES_RUNE) }
func (w Word) IsSymbolDescMark() bool  { return w.isMark(DES_SYMB) }
func (w Word) IsDateDescMark() bool    { return w.isMark(DES_DATE) }
func (w Word) IsAddressDescMark() bool { return w.isMark(DES_ADDR) }
func (w Word) IsIntegerDescMark() bool { return w.isMark2(DES_NUMB) }
func (w Word) IsFloatDescMark() bool   { return w.isMark2(DES_FLOA) }
func (w Word) IsBoolDescMark() bool    { return w.isMark2(DES_BOOL) }
func (w Word) IsRuneDescMark() bool    { return w.isMark2(DES_RUNE) }
func (w Word) IsSymbolDescMark() bool  { return w.isMark2(DES_SYMB) }
func (w Word) IsDateDescMark() bool    { return w.isMark2(DES_DATE) }
func (w Word) IsAddressDescMark() bool { return w.isMark2(DES_ADDR) }

func (w Word) IsChunk() bool { return w.isMark(CMP_UNIT) }
func (w Word) IsCmpUnit() bool { return w.isMark(CMP_UNIT) }

func (w Word) Debug() any {
func (w Word) Debug() string {
	if w.IsNAN() {
		return "NAN"
	} else if w.IsTrue() {


@@ 181,25 185,76 @@ func (w Word) Debug() any {
	} else if w.IsEmpty() {
		return "EMPTY"
	} else if w.IsInteger() {
		return fmt.Sprint("{INT: ", w.AsInt(), "}")
		return fmt.Sprint("N  ", w.AsInt(), "    ")
	} else if w.IsRune() {
		return fmt.Sprint("{RUNE: ", w.AsRune(), "}")
		return fmt.Sprint("R  '", string(w.AsRune()), "'")
	} else if w.IsSym() {
		return "SYMBOL" // FIXME: Implement symbols
		return "S" // FIXME: Implement symbols
	} else if w.IsAddress() { // must trigger before IsFloat for unknown reasons
		return fmt.Sprint("{ADDR: ", w.AsAddr(), "}")
		return fmt.Sprint("A ", w.AsAddr(), "    ")
	} else if w.IsFloat() {
		return fmt.Sprint("{FLOAT: ", w.AsFloat(), "}")
		// return fmt.Sprintf("F %.1e", w.AsFloat())
		return ". . . . ."
	} else if w.IsMapMark() {
		return "MAP"
	} else if w.IsArrayMark() {
		return "ARRAY"
		return "MAP MARK"
	} else if w.IsCodeArrayMark() {
		return "CODE MARK"
	} else if w.IsMainArrayMark() {
		return "LIST MARK"
	} else if w.IsRuneArrayMark() {
		return "RUNE MARK"
	} else if w.IsFuncMapMark() {
		return "ROUTINE"
	} else if w.IsChunk() {
		return "CHUNK"
	} else if w.IsCmpUnit() {
		return "CMPUNIT"
	} else if w.IsSentinel() {
		return "SENTINEL"
	} else {
		return nil
		return "..."
	}
}

func (x Word) Equals(y Word) bool {
	if x.IsNAN() {
		return false
	} else if x.IsTrue() {
		return y.IsTrue()
	} else if x.IsFalse() {
		return y.IsFalse()
	} else if x.IsEmpty() {
		return y.IsEmpty()
	} else if x.IsInteger() {
		if y.IsInteger() {
			return x.IsInteger() == y.IsInteger()
		} else {
			return false
		}
	} else if x.IsRune() {
		if y.IsRune() {
			return x.AsRune() == y.AsRune()
		} else {
			return false
		}
	} else if x.IsSym() {
		panic("symbols are not yet implemented")
	} else if x.IsAddress() { // must trigger before IsFloat for unknown reasons
		if y.IsAddress() {
			return x.AsAddr() == y.AsAddr()
		} else {
			return false
		}
	} else if x.IsFloat() {
		if y.IsFloat() {
			return x.AsFloat() == y.AsFloat()
		} else {
			return false
		}
	} else if x.IsAnyMark() {
		panic("should not compare marks")
	} else if x.IsCmpUnit() {
		panic("should not compare chunks")
	} else {
		panic("unknown object comparison")
	}
}