package main import "math" 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 // 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 (after Expression, child 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 ++ 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) { target.SetRune(offset, 0, '[') if selectedExpression == operation { target.SetColor(offset, 0, stone.ColorBlue) } else { target.SetColor(offset, 0, stone.ColorDim) } moved ++ opcodeSymbol := operation.opcode.String() for _, character := range opcodeSymbol { target.SetRune(offset + moved, 0, character) if selectedExpression == operation { target.SetColor(offset + moved, 0, stone.ColorBlue) } else { target.SetColor(offset + moved, 0, stone.ColorYellow) } moved ++ } for _, operand := range operation.operands { target.SetRune(offset + moved, 0, ' ') moved ++ moved += operand.Render(target, offset + moved) } target.SetRune(offset + moved, 0, ']') if selectedExpression == operation { target.SetColor(offset + moved, 0, stone.ColorBlue) } else { target.SetColor(offset + moved, 0, stone.ColorDim) } moved ++ return } func (operation *Operation) Solution () (solution int64, err error) { var subSolution int64 switch operation.opcode { case OpcodeAdd: for _, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } solution += subSolution } case OpcodeSubtract: if len(operation.operands) == 1 { solution, err = operation.operands[0].Solution() solution *= -1 if err != nil { return } break } for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution -= subSolution } } case OpcodeMultiply: solution = 1 for _, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } solution *= subSolution } case OpcodeDivide: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else if subSolution == 0 { err = ErrorDivideByZero } else { solution /= subSolution } } case OpcodePower: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution = integerPower(solution, subSolution) } } case OpcodeRoot: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution = integerRoot ( solution, subSolution) } } case OpcodeModulo: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution %= subSolution } } case OpcodeOr: for _, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } solution |= subSolution } case OpcodeNot: if len(operation.operands) > 1 { err = ErrorWrongOperandCount return } case OpcodeAnd: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution &= subSolution } } case OpcodeXor: for index, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { return } if index == 0 { solution = subSolution } else { solution ^= subSolution } } case OpcodeLeftShift: if len(operation.operands) != 2 { err = ErrorWrongOperandCount return } var left, right int64 left, err = operation.operands[0].Solution() if err != nil { return } right, err = operation.operands[1].Solution() if err != nil { return } if right < 0 { err = ErrorNegativeShiftAmount return } solution = left << right case OpcodeRightShift: if len(operation.operands) != 2 { err = ErrorWrongOperandCount return } var left, right int64 left, err = operation.operands[0].Solution() if err != nil { return } right, err = operation.operands[1].Solution() if err != nil { return } if right < 0 { err = ErrorNegativeShiftAmount return } solution = left >> right case OpcodeMean: if len(operation.operands) == 0 { break } for _, operand := range operation.operands { subSolution, err = operand.Solution() if err != nil { break } solution += subSolution } solution /= int64(len(operation.operands)) default: err = ErrorUnknownOpcode } return } func (operation *Operation) InexactSolution () (solution float64, err error) { var intSolution int64 intSolution, err = operation.Solution() solution = float64(intSolution) return } func integerPower (x, y int64) (result int64) { if y == 0 { result = 1 return } result = x for index := int64(2); index <= y; index ++ { result *= x } return } func integerRoot (x, y int64) (result int64) { // FIXME: find some algorithm for the nth root of an integer result = int64(math.Pow(float64(x), 1 / float64(y))) 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) if selectedExpression == literal { target.SetColor(offset + moved, 0, stone.ColorBlue) } else { target.SetColor(offset + moved, 0, stone.ColorPurple) } 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 }