346 lines
7.2 KiB
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, nil:
|
|
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()
|
|
}
|