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 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 stone.KeyUp: 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 { }, modifiers.Alt) redraw() application.Draw() case '+', '-', '*', '/', 'p', 'r', '%', '|', '~', '&', '^', 'm', '<', '>': insertOperation(rune(button), modifiers.Alt) redraw() application.Draw() case stone.KeyTab: showEndResult = !showEndResult 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 ' ': insertGeneric ( &IntegerLiteral { displayRadix: 10 }, modifiers.Alt) 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: insertGeneric (&IntegerLiteral { displayRadix: 10, value: int64(value), }, modifiers.Alt) case *IntegerLiteral: integer := selectedExpression.(*IntegerLiteral) integer.value *= int64(integer.displayRadix) 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() } } func insertOperation (symbol rune, swap bool) { var opcode Opcode switch (symbol) { case '+': opcode = OpcodeAdd case '-': opcode = OpcodeSubtract case '*': opcode = OpcodeMultiply case '/': opcode = OpcodeDivide case 'p': opcode = OpcodePower case 'r': opcode = OpcodeRoot case '%': opcode = OpcodeModulo case '|': opcode = OpcodeOr case '~': opcode = OpcodeNot case '&': opcode = OpcodeAnd case '^': opcode = OpcodeXor case '<': opcode = OpcodeLeftShift case '>': opcode = OpcodeRightShift case 'm': opcode = OpcodeMean } operation, isOperation := selectedExpression.(*Operation) if isOperation { if operation.opcode == OpcodeUnknown || swap { operation.opcode = opcode return } } newExpression := &Operation { opcode: opcode } insertGeneric(newExpression, swap) } func insertGeneric (expression Expression, swap bool) { defer func () { selectedExpression = expression } () if expressionRoot == nil { // if there are no expressions, place this one down in the empty // void expressionRoot = expression return } parent := selectedExpression.Parent() if swap { // if alt is held, swap the selected expression for the new one if parent == nil { expressionRoot = expression } else { selectedExpression.Parent().Swap(selectedExpression, expression) } return } operation, isOperation := selectedExpression.(*Operation) if isOperation && (len(operation.operands) < 1 || parent == nil) { // if we have an empty operation selected, always insert the // new expression into it operation.Adopt(expression) return } // else, insert the new expression after the selected expression if parent == nil { expressionRoot = expression } else { selectedExpression.Parent().Insert(selectedExpression, expression) } } func onStart () { redraw() }