mathpan/tree.go

391 lines
8.8 KiB
Go

package main
import "git.tebibyte.media/sashakoshka/stone"
var selectedExpression Expression
type Opcode int
const (
OpcodeUnknown Opcode = iota
OpcodeAdd
OpcodeSubtract
OpcodeMultiply
OpcodeDivide
OpcodePower
OpcodeRoot
OpcodeModulo
OpcodeOr
OpcodeNot
OpcodeAnd
OpcodeXor
OpcodeLeftShift
OpcodeRightShift
OpcodeMean
)
func (opcode Opcode) String () (output string) {
switch opcode {
case OpcodeUnknown: output = "?"
case OpcodeAdd: output = "+"
case OpcodeSubtract: output = "-"
case OpcodeMultiply: output = "*"
case OpcodeDivide: output = "/"
case OpcodePower: output = "p"
case OpcodeRoot: output = "r"
case OpcodeModulo: output = "%"
case OpcodeOr: output = "|"
case OpcodeNot: output = "~"
case OpcodeAnd: output = "&"
case OpcodeXor: output = "^"
case OpcodeLeftShift: output = "<<"
case OpcodeRightShift: output = ">>"
case OpcodeMean: output = "mean"
}
return
}
type Expression interface {
Parent () (parent *Operation)
Next () (next Expression)
Previous () (previous Expression)
Render (target stone.Buffer, offset int) (moved int)
Solution () (solution int64, err error)
InexactSolution () (solution float64, err error)
}
type Operation struct {
parent *Operation
opcode Opcode
operands []Expression
floating bool
// TODO: add bool to make this into a float
// when it is a float:
// - displays with parentheses instead of brackets
// - compute solution in floating point manner
// - return floating point solution for both Solution and
// InexactSolution, instead of the other way around, casting as
// necessary
// - ask children for InexactSolution instead of Solution
//
// TODO: have some control to make the readout call InexactSolution
// instead of Solution and display the floating point data properly
}
func (operation *Operation) Parent () (parent *Operation) {
parent = operation.parent
return
}
func (operation *Operation) Adopt (child Expression) {
operation.operands = append(operation.operands, child)
operation.adopt(child)
}
func (operation *Operation) adopt (child Expression) {
switch child.(type) {
case *Operation:
child.(*Operation).parent = operation
case *IntegerLiteral:
child.(*IntegerLiteral).parent = operation
}
}
func (operation *Operation) Insert (around, child Expression, after bool) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == around { found = true; break }
}
if !found { return }
if after { index ++ }
operation.operands = append(operation.operands, nil)
copy(operation.operands[index + 1:], operation.operands[index:])
operation.operands[index] = child
operation.adopt(child)
}
func (operation *Operation) Disown (child Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == child { found = true; break }
}
if !found { return }
operation.operands = append (
operation.operands[:index],
operation.operands[index + 1:]...)
}
func (operation *Operation) Swap (child Expression, with Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == child { found = true; break }
}
if !found { return }
operation.operands[index] = with
operation.adopt(with)
}
func (operation *Operation) Next () (next Expression) {
if operation == selectedExpression && len(operation.operands) > 0 {
// selection descends into this operation
next = operation.operands[0]
return
}
next = operation.next(selectedExpression)
return
}
func (operation *Operation) next (after Expression) (next Expression) {
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == after { found = true; break }
}
if !found { return }
index ++
if index >= len(operation.operands) {
// selection goes out of bounds to the right, ascending
// out of this operation
if operation.parent != nil {
next = operation.parent.next(operation)
}
} else {
// selection moves to the right within this operation
next = operation.operands[index]
}
return
}
func (operation *Operation) Previous () (previous Expression) {
if operation == selectedExpression {
if operation.parent != nil {
previous = operation.parent.Previous()
}
return
}
var operand Expression
var index int
var found bool
for index, operand = range operation.operands {
if operand == selectedExpression { found = true; break }
}
if !found {
if operation.parent != nil {
previous = operation.parent.Next()
}
} else {
index --
if index < 0 {
previous = operation
} else {
previous = operation.operands[index]
subOperation, isOperation := previous.(*Operation)
if isOperation {
previous = subOperation.Last()
if previous == nil {
previous = subOperation
}
}
}
}
return
}
func (operation *Operation) Last () (last Expression) {
if len(operation.operands) < 1 { return }
last = operation.operands[len(operation.operands) - 1]
return
}
func (operation *Operation) Render (target stone.Buffer, offset int) (moved int) {
if operation.floating {
target.SetRune(offset, 0, '(')
} else {
target.SetRune(offset, 0, '[')
}
if selectedExpression == operation {
target.SetColor(offset, 0, stone.ColorYellow)
target.SetStyle(offset, 0, stone.StyleHighlight)
} else {
target.SetColor(offset, 0, stone.ColorDim)
target.SetStyle(offset, 0, stone.StyleNormal)
}
moved ++
opcodeSymbol := operation.opcode.String()
for _, character := range opcodeSymbol {
target.SetRune(offset + moved, 0, character)
target.SetColor(offset + moved, 0, stone.ColorYellow)
if selectedExpression == operation {
target.SetStyle(offset + moved, 0, stone.StyleHighlight)
} else {
target.SetStyle(offset + moved, 0, stone.StyleNormal)
}
moved ++
}
for _, operand := range operation.operands {
target.SetRune(offset + moved, 0, ' ')
moved ++
moved += operand.Render(target, offset + moved)
}
if operation.floating {
target.SetRune(offset + moved, 0, ')')
} else {
target.SetRune(offset + moved, 0, ']')
}
if selectedExpression == operation {
target.SetColor(offset + moved, 0, stone.ColorYellow)
target.SetStyle(offset + moved, 0, stone.StyleHighlight)
} else {
target.SetColor(offset + moved, 0, stone.ColorDim)
target.SetStyle(offset + moved, 0, stone.StyleNormal)
}
moved ++
return
}
func (operation *Operation) Solution () (solution int64, err error) {
if operation.floating {
var floatSolution float64
floatSolution, err = operation.inexactSolution()
solution = int64(floatSolution)
} else {
solution, err = operation.solution()
}
return
}
func (operation *Operation) InexactSolution () (solution float64, err error) {
if operation.floating {
solution, err = operation.inexactSolution()
} else {
var intSolution int64
intSolution, err = operation.solution()
solution = float64(intSolution)
}
return
}
type IntegerLiteral struct {
parent *Operation
displayRadix int
value int64
}
func (literal *IntegerLiteral) Parent () (parent *Operation) {
parent = literal.parent
return
}
func (literal *IntegerLiteral) Next () (next Expression) {
if literal.parent != nil {
next = literal.parent.Next()
}
return
}
func (literal *IntegerLiteral) Previous () (previous Expression) {
if literal.parent != nil {
previous = literal.parent.Previous()
}
return
}
func (literal *IntegerLiteral) Render (target stone.Buffer, offset int) (moved int) {
var output []rune
value := literal.value
negative := false
if value < 0 {
negative = true
value *= -1
}
for value > 0 {
digit := rune(value % int64(literal.displayRadix))
value /= int64(literal.displayRadix)
if digit < 10 {
digit += '0'
} else {
digit += 'A' - 10
}
output = append([]rune { digit }, output...)
}
if len(output) == 0 {
output = []rune { '0' }
}
switch literal.displayRadix {
case 2:
output = append([]rune("0b"), output...)
case 8:
output = append([]rune("0"), output...)
case 10:
case 16:
output = append([]rune("0x"), output...)
default:
output = append([]rune("0?"), output...)
}
if negative {
output = append([]rune { '-' }, output...)
}
for _, character := range output {
target.SetRune(offset + moved, 0, character)
target.SetColor(offset + moved, 0, stone.ColorPurple)
if selectedExpression == literal {
target.SetStyle(offset + moved, 0, stone.StyleHighlight)
} else {
target.SetStyle(offset + moved, 0, stone.StyleNormal)
}
moved ++
}
return
}
func (literal *IntegerLiteral) Solution () (solution int64, err error) {
solution = literal.value
return
}
func (literal *IntegerLiteral) InexactSolution () (solution float64, err error) {
solution = float64(literal.value)
return
}