package main import "bytes" import "image" import _ "embed" import _ "image/png" import "git.tebibyte.media/sashakoshka/stone" import _ "git.tebibyte.media/sashakoshka/stone/backends/x" //go:embed icon/icon64.png var iconBytes []byte var application = &stone.Application { } var expressionRoot Expression var inputBuffer stone.DamageBuffer var showLeftColumn bool var showEndResult bool = true var showSigned bool = true var showFloat bool func main () { application.SetTitle("MathPan") application.SetSize(64, 12) icon, _, err := image.Decode(bytes.NewReader(iconBytes)) if err != nil { panic(err) } application.SetIcon([]image.Image { icon }) application.OnStart(onStart) application.OnResize(redraw) application.OnPress(onPress) err = application.Run() if err != nil { panic(err) } } func onPress (button stone.Button, modifiers stone.Modifiers) { switch button { case ',': if selectedExpression == nil { break } newSelection := selectedExpression.Parent() if newSelection != nil { selectedExpression = newSelection } redraw() application.Draw() case stone.KeyLeft: if selectedExpression == nil { break } newSelection := selectedExpression.Previous() if newSelection != nil { selectedExpression = newSelection } redraw() application.Draw() case stone.KeyRight: if selectedExpression == nil { break } newSelection := selectedExpression.Next() if newSelection != nil { selectedExpression = newSelection } redraw() application.Draw() case '[': insertGeneric (&Operation { floating: showFloat, }, modifiers.Alt, true, false) redraw() application.Draw() case ']': insertGeneric (&Operation { floating: showFloat, }, modifiers.Alt, false, false) redraw() application.Draw() case '\\': insertOperation('?', false, true) redraw() application.Draw() case '+', '-', '*', '/', 'p', 'P', '%', '|', '~', '&', '^', 'm', 'l', 'r': insertOperation(rune(button), modifiers.Alt, false) redraw() application.Draw() case stone.KeyDelete: if selectedExpression == nil { break } parent := selectedExpression.Parent() if parent == nil { expressionRoot = nil selectedExpression = nil } else { previous := selectedExpression.Parent().Previous() next := selectedExpression.Parent().Next() selectedExpression.Parent().Disown(selectedExpression) if next == nil { selectedExpression = previous } else { selectedExpression = next } } redraw() application.Draw() case stone.KeyBackspace: if selectedExpression == nil { break } parent := selectedExpression.Parent() switch selectedExpression.(type) { case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) if integer.value == 0 { break } integer.value /= int64(integer.displayRadix) goto stopBackspacing } if parent == nil { expressionRoot = nil selectedExpression = nil } else { previous := selectedExpression.Parent().Previous() selectedExpression.Parent().Disown(selectedExpression) if previous != nil { selectedExpression = previous } } stopBackspacing: redraw() application.Draw() case ' ': insertNumber(0, modifiers.Alt, modifiers.Shift) redraw() application.Draw() case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f': value := rune(button) if value >= 'a' { value -= 'a' value += 10 } else { value -= '0' } switch selectedExpression.(type) { case *Operation, nil: insertNumber(int64(value), modifiers.Alt, false) case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) integer.value *= int64(integer.displayRadix) if integer.value < 0 { integer.value -= int64(value) } else { integer.value += int64(value) } } redraw() application.Draw() case 'x': switch selectedExpression.(type) { case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) switch integer.displayRadix { case 2: integer.displayRadix = 8 case 8: integer.displayRadix = 10 case 10: integer.displayRadix = 16 case 16: integer.displayRadix = 2 } } redraw() application.Draw() case 'X': switch selectedExpression.(type) { case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) switch integer.displayRadix { case 2: integer.displayRadix = 16 case 8: integer.displayRadix = 2 case 10: integer.displayRadix = 8 case 16: integer.displayRadix = 10 } } redraw() application.Draw() case 's': switch selectedExpression.(type) { case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) integer.value *= -1 } redraw() application.Draw() case 'S': showSigned = !showSigned redraw() application.Draw() case 'q': showEndResult = !showEndResult redraw() application.Draw() case 'z': switch selectedExpression.(type) { case *Operation: operation := selectedExpression.(*Operation) operation.floating = !operation.floating } redraw() application.Draw() case 'Z': showFloat = !showFloat redraw() application.Draw() } } func insertOperation (symbol rune, swap, encompass bool) { var opcode Opcode switch (symbol) { case '?': opcode = OpcodeUnknown case '+': opcode = OpcodeAdd case '-': opcode = OpcodeSubtract case '*': opcode = OpcodeMultiply case '/': opcode = OpcodeDivide case 'p': opcode = OpcodePower case 'P': opcode = OpcodeRoot case '%': opcode = OpcodeModulo case '|': opcode = OpcodeOr case '~': opcode = OpcodeNot case '&': opcode = OpcodeAnd case '^': opcode = OpcodeXor case 'l': opcode = OpcodeLeftShift case 'r': opcode = OpcodeRightShift case 'm': opcode = OpcodeMean } newExpression := Operation { opcode: opcode, floating: showFloat } if encompass { if selectedExpression == nil { return } parent := selectedExpression.Parent() newExpression.Adopt(selectedExpression) if parent == nil { expressionRoot = &newExpression } else { parent.Swap(selectedExpression, &newExpression) } selectedExpression = &newExpression return } operation, isOperation := selectedExpression.(*Operation) if isOperation { if operation.opcode == OpcodeUnknown || swap { operation.opcode = opcode return } } insertGeneric(&newExpression, swap, false, true) } func insertNumber (value int64, swap, before bool) { insertGeneric (&IntegerLiteral { displayRadix: 10, value: value, }, swap, before, true) } func insertGeneric (expression Expression, swap, before, fillIn bool) { // if there are no expressions, place this at root if selectedExpression == nil || expressionRoot == nil { expressionRoot = expression selectedExpression = expression return } // prefer filling an empty operation operation, isOperation := selectedExpression.(*Operation) if fillIn && isOperation && len(operation.operands) == 0 { operation.Adopt(expression) selectedExpression = expression return } // insert expression into the parent operation parent := selectedExpression.Parent() if parent == nil { return } if swap { parent.Swap(selectedExpression, expression) } else { parent.Insert(selectedExpression, expression, !before) } selectedExpression = expression } func onStart () { redraw() }