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 }