Implemented simple expressions
This commit is contained in:
parent
2569c1bf63
commit
e6462266ef
@ -16,6 +16,7 @@ hello() {
|
|||||||
length -= 20
|
length -= 20
|
||||||
length *= 10
|
length *= 10
|
||||||
length /= 100
|
length /= 100
|
||||||
|
length = (0 + 50 - 20) * 10 / 100
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
syscall(write, stdout, address, length)
|
syscall(write, stdout, address, length)
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
// CompileAssignment compiles an assignment.
|
// CompileAssignment compiles an assignment.
|
||||||
func (f *Function) CompileAssignment(expr *expression.Expression) error {
|
func (f *Function) CompileAssignment(expr *expression.Expression) error {
|
||||||
name := expr.Children[0].Token.Text()
|
name := expr.Children[0].Token.Text()
|
||||||
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
|
|
||||||
register := f.Variables[name].Register
|
register := f.Variables[name].Register
|
||||||
|
|
||||||
switch expr.Token.Text() {
|
switch expr.Token.Text() {
|
||||||
@ -19,15 +18,19 @@ func (f *Function) CompileAssignment(expr *expression.Expression) error {
|
|||||||
f.ExpressionToRegister(expr.Children[1], register)
|
f.ExpressionToRegister(expr.Children[1], register)
|
||||||
|
|
||||||
case "+=":
|
case "+=":
|
||||||
|
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
|
||||||
f.Assembler.RegisterNumber(asm.ADD, register, number)
|
f.Assembler.RegisterNumber(asm.ADD, register, number)
|
||||||
|
|
||||||
case "-=":
|
case "-=":
|
||||||
|
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
|
||||||
f.Assembler.RegisterNumber(asm.SUB, register, number)
|
f.Assembler.RegisterNumber(asm.SUB, register, number)
|
||||||
|
|
||||||
case "*=":
|
case "*=":
|
||||||
|
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
|
||||||
f.Assembler.RegisterNumber(asm.MUL, register, number)
|
f.Assembler.RegisterNumber(asm.MUL, register, number)
|
||||||
|
|
||||||
case "/=":
|
case "/=":
|
||||||
|
number, _ := strconv.Atoi(expr.Children[1].Token.Text())
|
||||||
f.Assembler.RegisterNumber(asm.DIV, register, number)
|
f.Assembler.RegisterNumber(asm.DIV, register, number)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -120,27 +120,61 @@ func (f *Function) CompileInstruction(line token.List) error {
|
|||||||
return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position)
|
return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// isAssignment returns true if the expression is an assignment.
|
|
||||||
func isAssignment(expr *expression.Expression) bool {
|
|
||||||
return expr.Token.Kind == token.Operator && expr.Token.Bytes[len(expr.Token.Bytes)-1] == '='
|
|
||||||
}
|
|
||||||
|
|
||||||
// isFunctionCall returns true if the expression is a function call.
|
|
||||||
func isFunctionCall(expr *expression.Expression) bool {
|
|
||||||
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
|
|
||||||
}
|
|
||||||
|
|
||||||
// isVariableDefinition returns true if the expression is a variable definition.
|
|
||||||
func isVariableDefinition(expr *expression.Expression) bool {
|
|
||||||
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpressionToRegister moves the result of an expression into the given register.
|
// ExpressionToRegister moves the result of an expression into the given register.
|
||||||
func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error {
|
func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error {
|
||||||
if root.IsLeaf() {
|
if root.IsLeaf() {
|
||||||
return f.TokenToRegister(root.Token, register)
|
return f.TokenToRegister(root.Token, register)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
left := root.Children[0]
|
||||||
|
right := root.Children[1]
|
||||||
|
|
||||||
|
f.ExpressionToRegister(left, register)
|
||||||
|
|
||||||
|
if right.IsLeaf() {
|
||||||
|
value := right.Token.Text()
|
||||||
|
n, err := strconv.Atoi(value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch root.Token.Text() {
|
||||||
|
case "+":
|
||||||
|
f.Assembler.RegisterNumber(asm.ADD, register, n)
|
||||||
|
|
||||||
|
case "-":
|
||||||
|
f.Assembler.RegisterNumber(asm.SUB, register, n)
|
||||||
|
|
||||||
|
case "*":
|
||||||
|
f.Assembler.RegisterNumber(asm.MUL, register, n)
|
||||||
|
|
||||||
|
case "/":
|
||||||
|
f.Assembler.RegisterNumber(asm.DIV, register, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
temporary, _ := f.CPU.FindFree()
|
||||||
|
f.CPU.Use(temporary)
|
||||||
|
f.ExpressionToRegister(right, temporary)
|
||||||
|
f.CPU.Free(temporary)
|
||||||
|
|
||||||
|
switch root.Token.Text() {
|
||||||
|
case "+":
|
||||||
|
f.Assembler.RegisterRegister(asm.ADD, register, temporary)
|
||||||
|
|
||||||
|
case "-":
|
||||||
|
f.Assembler.RegisterRegister(asm.SUB, register, temporary)
|
||||||
|
|
||||||
|
case "*":
|
||||||
|
f.Assembler.RegisterRegister(asm.MUL, register, temporary)
|
||||||
|
|
||||||
|
case "/":
|
||||||
|
f.Assembler.RegisterRegister(asm.DIV, register, temporary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errors.New(errors.NotImplemented, f.File, root.Token.Position)
|
return errors.New(errors.NotImplemented, f.File, root.Token.Position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,3 +241,18 @@ func (f *Function) identifierExists(name string) bool {
|
|||||||
_, exists := f.Variables[name]
|
_, exists := f.Variables[name]
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isAssignment returns true if the expression is an assignment.
|
||||||
|
func isAssignment(expr *expression.Expression) bool {
|
||||||
|
return expr.Token.Kind == token.Operator && expr.Token.Bytes[len(expr.Token.Bytes)-1] == '='
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFunctionCall returns true if the expression is a function call.
|
||||||
|
func isFunctionCall(expr *expression.Expression) bool {
|
||||||
|
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
|
||||||
|
}
|
||||||
|
|
||||||
|
// isVariableDefinition returns true if the expression is a variable definition.
|
||||||
|
func isVariableDefinition(expr *expression.Expression) bool {
|
||||||
|
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
|
||||||
|
}
|
||||||
|
@ -6,5 +6,37 @@ import (
|
|||||||
|
|
||||||
// AddRegNum adds a number to the given register.
|
// AddRegNum adds a number to the given register.
|
||||||
func AddRegNum(code []byte, destination cpu.Register, number int) []byte {
|
func AddRegNum(code []byte, destination cpu.Register, number int) []byte {
|
||||||
return numRegReg(code, 0x83, 0x81, 0, byte(destination), number)
|
return numRegReg(code, 0, byte(destination), number, 0x83, 0x81)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRegReg adds a number to the given register.
|
||||||
|
func AddRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
|
||||||
|
return regReg(code, byte(operand), byte(destination), 0x01)
|
||||||
|
}
|
||||||
|
|
||||||
|
func regReg(code []byte, reg byte, rm byte, opCodes ...byte) []byte {
|
||||||
|
w := byte(1) // Indicates a 64-bit register.
|
||||||
|
r := byte(0) // Extension to the "reg" field in ModRM.
|
||||||
|
x := byte(0) // Extension to the SIB index field.
|
||||||
|
b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this).
|
||||||
|
mod := byte(0b11) // Direct addressing mode, no register offsets.
|
||||||
|
|
||||||
|
if reg > 0b111 {
|
||||||
|
r = 1
|
||||||
|
reg &= 0b111
|
||||||
|
}
|
||||||
|
|
||||||
|
if rm > 0b111 {
|
||||||
|
b = 1
|
||||||
|
rm &= 0b111
|
||||||
|
}
|
||||||
|
|
||||||
|
rex := REX(w, r, x, b)
|
||||||
|
modRM := ModRM(mod, reg, rm)
|
||||||
|
|
||||||
|
code = append(code, rex)
|
||||||
|
code = append(code, opCodes...)
|
||||||
|
code = append(code, modRM)
|
||||||
|
|
||||||
|
return code
|
||||||
}
|
}
|
||||||
|
@ -55,3 +55,34 @@ func TestAddRegisterNumber(t *testing.T) {
|
|||||||
assert.DeepEqual(t, code, pattern.Code)
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddRegisterRegister(t *testing.T) {
|
||||||
|
usagePatterns := []struct {
|
||||||
|
Left cpu.Register
|
||||||
|
Right cpu.Register
|
||||||
|
Code []byte
|
||||||
|
}{
|
||||||
|
{x64.RAX, x64.R15, []byte{0x4C, 0x01, 0xF8}},
|
||||||
|
{x64.RCX, x64.R14, []byte{0x4C, 0x01, 0xF1}},
|
||||||
|
{x64.RDX, x64.R13, []byte{0x4C, 0x01, 0xEA}},
|
||||||
|
{x64.RBX, x64.R12, []byte{0x4C, 0x01, 0xE3}},
|
||||||
|
{x64.RSP, x64.R11, []byte{0x4C, 0x01, 0xDC}},
|
||||||
|
{x64.RBP, x64.R10, []byte{0x4C, 0x01, 0xD5}},
|
||||||
|
{x64.RSI, x64.R9, []byte{0x4C, 0x01, 0xCE}},
|
||||||
|
{x64.RDI, x64.R8, []byte{0x4C, 0x01, 0xC7}},
|
||||||
|
{x64.R8, x64.RDI, []byte{0x49, 0x01, 0xF8}},
|
||||||
|
{x64.R9, x64.RSI, []byte{0x49, 0x01, 0xF1}},
|
||||||
|
{x64.R10, x64.RBP, []byte{0x49, 0x01, 0xEA}},
|
||||||
|
{x64.R11, x64.RSP, []byte{0x49, 0x01, 0xE3}},
|
||||||
|
{x64.R12, x64.RBX, []byte{0x49, 0x01, 0xDC}},
|
||||||
|
{x64.R13, x64.RDX, []byte{0x49, 0x01, 0xD5}},
|
||||||
|
{x64.R14, x64.RCX, []byte{0x49, 0x01, 0xCE}},
|
||||||
|
{x64.R15, x64.RAX, []byte{0x49, 0x01, 0xC7}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pattern := range usagePatterns {
|
||||||
|
t.Logf("add %s, %s", pattern.Left, pattern.Right)
|
||||||
|
code := x64.AddRegReg(nil, pattern.Left, pattern.Right)
|
||||||
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,5 +4,10 @@ import "git.akyoto.dev/cli/q/src/build/cpu"
|
|||||||
|
|
||||||
// MulRegNum multiplies a register with a number.
|
// MulRegNum multiplies a register with a number.
|
||||||
func MulRegNum(code []byte, destination cpu.Register, number int) []byte {
|
func MulRegNum(code []byte, destination cpu.Register, number int) []byte {
|
||||||
return numRegReg(code, 0x6B, 0x69, byte(destination), byte(destination), number)
|
return numRegReg(code, byte(destination), byte(destination), number, 0x6B, 0x69)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulRegReg multiplies a register with another register.
|
||||||
|
func MulRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
|
||||||
|
return regReg(code, byte(destination), byte(operand), 0x0F, 0xAF)
|
||||||
}
|
}
|
||||||
|
@ -55,3 +55,34 @@ func TestMulRegisterNumber(t *testing.T) {
|
|||||||
assert.DeepEqual(t, code, pattern.Code)
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMulRegisterRegister(t *testing.T) {
|
||||||
|
usagePatterns := []struct {
|
||||||
|
Left cpu.Register
|
||||||
|
Right cpu.Register
|
||||||
|
Code []byte
|
||||||
|
}{
|
||||||
|
{x64.RAX, x64.R15, []byte{0x49, 0x0F, 0xAF, 0xC7}},
|
||||||
|
{x64.RCX, x64.R14, []byte{0x49, 0x0F, 0xAF, 0xCE}},
|
||||||
|
{x64.RDX, x64.R13, []byte{0x49, 0x0F, 0xAF, 0xD5}},
|
||||||
|
{x64.RBX, x64.R12, []byte{0x49, 0x0F, 0xAF, 0xDC}},
|
||||||
|
{x64.RSP, x64.R11, []byte{0x49, 0x0F, 0xAF, 0xE3}},
|
||||||
|
{x64.RBP, x64.R10, []byte{0x49, 0x0F, 0xAF, 0xEA}},
|
||||||
|
{x64.RSI, x64.R9, []byte{0x49, 0x0F, 0xAF, 0xF1}},
|
||||||
|
{x64.RDI, x64.R8, []byte{0x49, 0x0F, 0xAF, 0xF8}},
|
||||||
|
{x64.R8, x64.RDI, []byte{0x4C, 0x0F, 0xAF, 0xC7}},
|
||||||
|
{x64.R9, x64.RSI, []byte{0x4C, 0x0F, 0xAF, 0xCE}},
|
||||||
|
{x64.R10, x64.RBP, []byte{0x4C, 0x0F, 0xAF, 0xD5}},
|
||||||
|
{x64.R11, x64.RSP, []byte{0x4C, 0x0F, 0xAF, 0xDC}},
|
||||||
|
{x64.R12, x64.RBX, []byte{0x4C, 0x0F, 0xAF, 0xE3}},
|
||||||
|
{x64.R13, x64.RDX, []byte{0x4C, 0x0F, 0xAF, 0xEA}},
|
||||||
|
{x64.R14, x64.RCX, []byte{0x4C, 0x0F, 0xAF, 0xF1}},
|
||||||
|
{x64.R15, x64.RAX, []byte{0x4C, 0x0F, 0xAF, 0xF8}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pattern := range usagePatterns {
|
||||||
|
t.Logf("mul %s, %s", pattern.Left, pattern.Right)
|
||||||
|
code := x64.MulRegReg(nil, pattern.Left, pattern.Right)
|
||||||
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -6,5 +6,10 @@ import (
|
|||||||
|
|
||||||
// SubRegNum subtracts a number from the given register.
|
// SubRegNum subtracts a number from the given register.
|
||||||
func SubRegNum(code []byte, destination cpu.Register, number int) []byte {
|
func SubRegNum(code []byte, destination cpu.Register, number int) []byte {
|
||||||
return numRegReg(code, 0x83, 0x81, 0b101, byte(destination), number)
|
return numRegReg(code, 0b101, byte(destination), number, 0x83, 0x81)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubRegReg subtracts a register value from another register.
|
||||||
|
func SubRegReg(code []byte, destination cpu.Register, operand cpu.Register) []byte {
|
||||||
|
return regReg(code, byte(operand), byte(destination), 0x29)
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,15 @@
|
|||||||
package x64
|
package x64
|
||||||
|
|
||||||
// numRegReg encodes an instruction with up to two registers and a number parameter.
|
// numRegReg encodes an instruction with up to two registers and a number parameter.
|
||||||
func numRegReg(code []byte, opCode8 byte, opCode32 byte, reg byte, rm byte, number int) []byte {
|
func numRegReg(code []byte, reg byte, rm byte, number int, opCode8 byte, opCode32 byte) []byte {
|
||||||
w := byte(1) // Indicates a 64-bit register.
|
|
||||||
r := byte(0) // Extension to the "reg" field in ModRM.
|
|
||||||
x := byte(0) // Extension to the SIB index field.
|
|
||||||
b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this).
|
|
||||||
mod := byte(0b11) // Direct addressing mode, no register offsets.
|
|
||||||
|
|
||||||
if reg > 0b111 {
|
|
||||||
r = 1
|
|
||||||
reg &= 0b111
|
|
||||||
}
|
|
||||||
|
|
||||||
if rm > 0b111 {
|
|
||||||
b = 1
|
|
||||||
rm &= 0b111
|
|
||||||
}
|
|
||||||
|
|
||||||
rex := REX(w, r, x, b)
|
|
||||||
modRM := ModRM(mod, reg, rm)
|
|
||||||
|
|
||||||
if sizeOf(number) == 1 {
|
if sizeOf(number) == 1 {
|
||||||
return append(
|
code = regReg(code, reg, rm, opCode8)
|
||||||
code,
|
return append(code, byte(number))
|
||||||
rex,
|
|
||||||
opCode8,
|
|
||||||
modRM,
|
|
||||||
byte(number),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code = regReg(code, reg, rm, opCode32)
|
||||||
return append(
|
return append(
|
||||||
code,
|
code,
|
||||||
rex,
|
|
||||||
opCode32,
|
|
||||||
modRM,
|
|
||||||
byte(number),
|
byte(number),
|
||||||
byte(number>>8),
|
byte(number>>8),
|
||||||
byte(number>>16),
|
byte(number>>16),
|
||||||
|
@ -31,18 +31,24 @@ func (a *Assembler) Finalize() ([]byte, []byte) {
|
|||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
code = x64.AddRegNum(code, operands.Register, operands.Number)
|
code = x64.AddRegNum(code, operands.Register, operands.Number)
|
||||||
|
case *RegisterRegister:
|
||||||
|
code = x64.AddRegReg(code, operands.Destination, operands.Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SUB:
|
case SUB:
|
||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
code = x64.SubRegNum(code, operands.Register, operands.Number)
|
code = x64.SubRegNum(code, operands.Register, operands.Number)
|
||||||
|
case *RegisterRegister:
|
||||||
|
code = x64.SubRegReg(code, operands.Destination, operands.Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUL:
|
case MUL:
|
||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
code = x64.MulRegNum(code, operands.Register, operands.Number)
|
code = x64.MulRegNum(code, operands.Register, operands.Number)
|
||||||
|
case *RegisterRegister:
|
||||||
|
code = x64.MulRegReg(code, operands.Destination, operands.Source)
|
||||||
}
|
}
|
||||||
|
|
||||||
case DIV:
|
case DIV:
|
||||||
@ -65,6 +71,16 @@ func (a *Assembler) Finalize() ([]byte, []byte) {
|
|||||||
code = x64.PopReg(code, x64.RDX)
|
code = x64.PopReg(code, x64.RDX)
|
||||||
code = x64.PopReg(code, x64.RAX)
|
code = x64.PopReg(code, x64.RAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case *RegisterRegister:
|
||||||
|
code = x64.PushReg(code, x64.RAX)
|
||||||
|
code = x64.PushReg(code, x64.RDX)
|
||||||
|
code = x64.MoveRegReg64(code, x64.RAX, operands.Destination)
|
||||||
|
code = x64.ExtendRAXToRDX(code)
|
||||||
|
code = x64.DivReg(code, operands.Source)
|
||||||
|
code = x64.MoveRegReg64(code, operands.Destination, x64.RAX)
|
||||||
|
code = x64.PopReg(code, x64.RDX)
|
||||||
|
code = x64.PopReg(code, x64.RAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
case CALL:
|
case CALL:
|
||||||
|
@ -13,6 +13,17 @@ func (a *Assembler) RegisterNumber(mnemonic Mnemonic, reg cpu.Register, number i
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterRegister adds an instruction using two registers.
|
||||||
|
func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) {
|
||||||
|
a.Instructions = append(a.Instructions, Instruction{
|
||||||
|
Mnemonic: mnemonic,
|
||||||
|
Data: &RegisterRegister{
|
||||||
|
Destination: left,
|
||||||
|
Source: right,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// MoveRegisterRegister moves a register value into another register.
|
// MoveRegisterRegister moves a register value into another register.
|
||||||
func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) {
|
func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) {
|
||||||
a.Instructions = append(a.Instructions, Instruction{
|
a.Instructions = append(a.Instructions, Instruction{
|
||||||
|
@ -55,13 +55,13 @@ func (expr *Expression) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EachLeaf iterates through all leaves in the tree.
|
// EachLeaf iterates through all leaves in the tree.
|
||||||
func (expr *Expression) EachLeaf(callBack func(*Expression) error) error {
|
func (expr *Expression) EachLeaf(call func(*Expression) error) error {
|
||||||
if expr.IsLeaf() {
|
if expr.IsLeaf() {
|
||||||
return callBack(expr)
|
return call(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, child := range expr.Children {
|
for _, child := range expr.Children {
|
||||||
err := child.EachLeaf(callBack)
|
err := child.EachLeaf(call)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -71,6 +71,28 @@ func (expr *Expression) EachLeaf(callBack func(*Expression) error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EachOperation iterates through all the operations in the tree.
|
||||||
|
func (expr *Expression) EachOperation(call func(*Expression) error) error {
|
||||||
|
if expr.IsLeaf() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't descend into the parameters of function calls
|
||||||
|
if expr.Token.Text() == "λ" {
|
||||||
|
return call(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range expr.Children {
|
||||||
|
err := child.EachOperation(call)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return call(expr)
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveChild removes a child from the expression.
|
// RemoveChild removes a child from the expression.
|
||||||
func (expr *Expression) RemoveChild(child *Expression) {
|
func (expr *Expression) RemoveChild(child *Expression) {
|
||||||
for i, c := range expr.Children {
|
for i, c := range expr.Children {
|
||||||
|
Loading…
Reference in New Issue
Block a user