mathpan/main.go

346 lines
7.2 KiB
Go

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()
}