2022-11-20 22:51:09 -07:00
|
|
|
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
|
2022-11-20 23:07:46 -07:00
|
|
|
OpcodeLeftShift
|
|
|
|
OpcodeRightShift
|
2022-11-20 22:51:09 -07:00
|
|
|
|
|
|
|
OpcodeMean
|
|
|
|
)
|
|
|
|
|
|
|
|
func (opcode Opcode) String () (output string) {
|
|
|
|
switch opcode {
|
2022-11-20 23:07:46 -07:00
|
|
|
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"
|
2022-11-20 22:51:09 -07:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
2022-11-21 11:49:36 -07:00
|
|
|
if len(operation.operands) == 1 {
|
|
|
|
solution, err = operation.operands[0].Solution()
|
|
|
|
solution *= -1
|
|
|
|
if err != nil { return }
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
for index, operand := range operation.operands {
|
2022-11-20 22:51:09 -07:00
|
|
|
subSolution, err = operand.Solution()
|
|
|
|
if err != nil { return }
|
2022-11-21 11:49:36 -07:00
|
|
|
if index == 0 {
|
|
|
|
solution = subSolution
|
|
|
|
} else {
|
|
|
|
solution -= subSolution
|
|
|
|
}
|
2022-11-20 22:51:09 -07:00
|
|
|
}
|
|
|
|
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 = integerPower (
|
|
|
|
solution, subSolution * -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2022-11-20 23:07:46 -07:00
|
|
|
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
|
2022-11-20 22:51:09 -07:00
|
|
|
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 integerPower (x, y int64) (result int64) {
|
|
|
|
if y == 0 {
|
|
|
|
result = 1
|
|
|
|
return
|
|
|
|
} else if y < 0 {
|
|
|
|
// FIXME: find some algorithm for the nth root of an integer
|
|
|
|
result = int64(math.Pow(float64(x), float64(y)))
|
|
|
|
}
|
|
|
|
|
|
|
|
result = x
|
|
|
|
for index := int64(2); index <= y; index ++ {
|
|
|
|
result *= x
|
|
|
|
}
|
|
|
|
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
|
2022-11-21 23:19:35 -07:00
|
|
|
|
|
|
|
negative := false
|
|
|
|
if value < 0 {
|
|
|
|
negative = true
|
|
|
|
value *= -1
|
|
|
|
}
|
|
|
|
|
2022-11-20 22:51:09 -07:00
|
|
|
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...)
|
|
|
|
}
|
|
|
|
|
2022-11-21 23:19:35 -07:00
|
|
|
if negative {
|
|
|
|
output = append([]rune { '-' }, output...)
|
|
|
|
}
|
|
|
|
|
2022-11-20 22:51:09 -07:00
|
|
|
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
|
|
|
|
}
|