Implemented negation
This commit is contained in:
parent
6861ae9a90
commit
bb74c0cf50
8
src/build/arch/x64/Negate.go
Normal file
8
src/build/arch/x64/Negate.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package x64
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
|
||||||
|
// NegateRegister negates the value in the register.
|
||||||
|
func NegateRegister(code []byte, register cpu.Register) []byte {
|
||||||
|
return encode(code, AddressDirect, 0b011, register, 8, 0xF7)
|
||||||
|
}
|
39
src/build/arch/x64/Negate_test.go
Normal file
39
src/build/arch/x64/Negate_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package x64_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
"git.akyoto.dev/go/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNegateRegister(t *testing.T) {
|
||||||
|
usagePatterns := []struct {
|
||||||
|
Register cpu.Register
|
||||||
|
Code []byte
|
||||||
|
}{
|
||||||
|
{x64.RAX, []byte{0x48, 0xF7, 0xD8}},
|
||||||
|
{x64.RCX, []byte{0x48, 0xF7, 0xD9}},
|
||||||
|
{x64.RDX, []byte{0x48, 0xF7, 0xDA}},
|
||||||
|
{x64.RBX, []byte{0x48, 0xF7, 0xDB}},
|
||||||
|
{x64.RSP, []byte{0x48, 0xF7, 0xDC}},
|
||||||
|
{x64.RBP, []byte{0x48, 0xF7, 0xDD}},
|
||||||
|
{x64.RSI, []byte{0x48, 0xF7, 0xDE}},
|
||||||
|
{x64.RDI, []byte{0x48, 0xF7, 0xDF}},
|
||||||
|
{x64.R8, []byte{0x49, 0xF7, 0xD8}},
|
||||||
|
{x64.R9, []byte{0x49, 0xF7, 0xD9}},
|
||||||
|
{x64.R10, []byte{0x49, 0xF7, 0xDA}},
|
||||||
|
{x64.R11, []byte{0x49, 0xF7, 0xDB}},
|
||||||
|
{x64.R12, []byte{0x49, 0xF7, 0xDC}},
|
||||||
|
{x64.R13, []byte{0x49, 0xF7, 0xDD}},
|
||||||
|
{x64.R14, []byte{0x49, 0xF7, 0xDE}},
|
||||||
|
{x64.R15, []byte{0x49, 0xF7, 0xDF}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pattern := range usagePatterns {
|
||||||
|
t.Logf("neg %s", pattern.Register)
|
||||||
|
code := x64.NegateRegister(nil, pattern.Register)
|
||||||
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
|
}
|
||||||
|
}
|
@ -165,6 +165,12 @@ func (a Assembler) Finalize() ([]byte, []byte) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case NEGATE:
|
||||||
|
switch operands := x.Data.(type) {
|
||||||
|
case *Register:
|
||||||
|
code = x64.NegateRegister(code, operands.Register)
|
||||||
|
}
|
||||||
|
|
||||||
case OR:
|
case OR:
|
||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
|
@ -4,12 +4,31 @@ type Mnemonic uint8
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
NONE Mnemonic = iota
|
NONE Mnemonic = iota
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
ADD
|
ADD
|
||||||
AND
|
|
||||||
CALL
|
|
||||||
COMMENT
|
|
||||||
COMPARE
|
|
||||||
DIV
|
DIV
|
||||||
|
MODULO
|
||||||
|
MUL
|
||||||
|
NEGATE
|
||||||
|
SUB
|
||||||
|
|
||||||
|
// Bitwise operations
|
||||||
|
AND
|
||||||
|
OR
|
||||||
|
SHIFTL
|
||||||
|
SHIFTRS
|
||||||
|
XOR
|
||||||
|
|
||||||
|
// Data movement
|
||||||
|
MOVE
|
||||||
|
LOAD
|
||||||
|
POP
|
||||||
|
PUSH
|
||||||
|
STORE
|
||||||
|
|
||||||
|
// Control flow
|
||||||
|
CALL
|
||||||
JE
|
JE
|
||||||
JNE
|
JNE
|
||||||
JG
|
JG
|
||||||
@ -17,21 +36,13 @@ const (
|
|||||||
JL
|
JL
|
||||||
JLE
|
JLE
|
||||||
JUMP
|
JUMP
|
||||||
MUL
|
|
||||||
LABEL
|
LABEL
|
||||||
LOAD
|
|
||||||
MODULO
|
|
||||||
MOVE
|
|
||||||
OR
|
|
||||||
POP
|
|
||||||
PUSH
|
|
||||||
RETURN
|
RETURN
|
||||||
SHIFTL
|
|
||||||
SHIFTRS
|
|
||||||
STORE
|
|
||||||
SUB
|
|
||||||
SYSCALL
|
SYSCALL
|
||||||
XOR
|
|
||||||
|
// Others
|
||||||
|
COMMENT
|
||||||
|
COMPARE
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns a human readable version.
|
// String returns a human readable version.
|
||||||
@ -73,6 +84,8 @@ func (m Mnemonic) String() string {
|
|||||||
return "move"
|
return "move"
|
||||||
case MUL:
|
case MUL:
|
||||||
return "mul"
|
return "mul"
|
||||||
|
case NEGATE:
|
||||||
|
return "negate"
|
||||||
case OR:
|
case OR:
|
||||||
return "or"
|
return "or"
|
||||||
case POP:
|
case POP:
|
||||||
|
21
src/build/core/ExecuteRegister.go
Normal file
21
src/build/core/ExecuteRegister.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecuteRegister performs an operation on a single register.
|
||||||
|
func (f *Function) ExecuteRegister(operation token.Token, register cpu.Register) error {
|
||||||
|
switch operation.Kind {
|
||||||
|
case token.Negate:
|
||||||
|
f.Register(asm.NEGATE, register)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return errors.New(&errors.InvalidOperator{Operator: operation.Text(f.File.Bytes)}, f.File, operation.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/build/asm"
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
"git.akyoto.dev/cli/q/src/build/ast"
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
@ -24,10 +26,21 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(node.Children) < 2 {
|
if len(node.Children) == 1 {
|
||||||
|
if !node.Token.IsUnaryOperator() {
|
||||||
|
fmt.Println(node.String(f.File.Bytes), node.Token.Kind)
|
||||||
return errors.New(errors.MissingOperand, f.File, node.Token.End())
|
return errors.New(errors.MissingOperand, f.File, node.Token.End())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := f.ExpressionToRegister(node.Children[0], register)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.ExecuteRegister(node.Token, register)
|
||||||
|
}
|
||||||
|
|
||||||
left := node.Children[0]
|
left := node.Children[0]
|
||||||
right := node.Children[1]
|
right := node.Children[1]
|
||||||
final := register
|
final := register
|
||||||
|
@ -38,8 +38,6 @@ const (
|
|||||||
Shr // >>
|
Shr // >>
|
||||||
LogicalAnd // &&
|
LogicalAnd // &&
|
||||||
LogicalOr // ||
|
LogicalOr // ||
|
||||||
Not // ! (unary)
|
|
||||||
Negate // - (unary)
|
|
||||||
Equal // ==
|
Equal // ==
|
||||||
Less // <
|
Less // <
|
||||||
Greater // >
|
Greater // >
|
||||||
@ -51,6 +49,10 @@ const (
|
|||||||
Call // x()
|
Call // x()
|
||||||
Array // [x]
|
Array // [x]
|
||||||
Separator // ,
|
Separator // ,
|
||||||
|
_unary // <unary>
|
||||||
|
Not // ! (unary)
|
||||||
|
Negate // - (unary)
|
||||||
|
_unaryEnd // </unary>
|
||||||
_assignments // <assignments>
|
_assignments // <assignments>
|
||||||
Assign // =
|
Assign // =
|
||||||
AddAssign // +=
|
AddAssign // +=
|
||||||
|
@ -43,6 +43,11 @@ func (t Token) IsOperator() bool {
|
|||||||
return t.Kind > _operators && t.Kind < _operatorsEnd
|
return t.Kind > _operators && t.Kind < _operatorsEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsUnaryOperator returns true if the token is a unary operator.
|
||||||
|
func (t Token) IsUnaryOperator() bool {
|
||||||
|
return t.Kind > _unary && t.Kind < _unaryEnd
|
||||||
|
}
|
||||||
|
|
||||||
// Reset resets the token to default values.
|
// Reset resets the token to default values.
|
||||||
func (t *Token) Reset() {
|
func (t *Token) Reset() {
|
||||||
t.Position = 0
|
t.Position = 0
|
||||||
|
@ -27,7 +27,7 @@ func Tokenize(buffer []byte) List {
|
|||||||
case '\n':
|
case '\n':
|
||||||
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
|
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
|
||||||
case '-':
|
case '-':
|
||||||
if len(tokens) == 0 || tokens[len(tokens)-1].IsOperator() || tokens[len(tokens)-1].IsExpressionStart() {
|
if len(tokens) == 0 || tokens[len(tokens)-1].IsOperator() || tokens[len(tokens)-1].IsExpressionStart() || tokens[len(tokens)-1].IsKeyword() {
|
||||||
tokens = append(tokens, Token{Kind: Negate, Position: i, Length: 1})
|
tokens = append(tokens, Token{Kind: Negate, Position: i, Length: 1})
|
||||||
} else {
|
} else {
|
||||||
if i+1 < Position(len(buffer)) && buffer[i+1] == '=' {
|
if i+1 < Position(len(buffer)) && buffer[i+1] == '=' {
|
||||||
|
7
tests/programs/negation.q
Normal file
7
tests/programs/negation.q
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
main() {
|
||||||
|
syscall(60, f(-32))
|
||||||
|
}
|
||||||
|
|
||||||
|
f(x) {
|
||||||
|
return -x
|
||||||
|
}
|
@ -43,6 +43,7 @@ var programs = []struct {
|
|||||||
{"loop-lifetime", "", "", 0},
|
{"loop-lifetime", "", "", 0},
|
||||||
{"assert", "", "", 1},
|
{"assert", "", "", 1},
|
||||||
{"negative", "", "", 32},
|
{"negative", "", "", 32},
|
||||||
|
{"negation", "", "", 32},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrograms(t *testing.T) {
|
func TestPrograms(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user