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:
|
||||
switch operands := x.Data.(type) {
|
||||
case *RegisterNumber:
|
||||
|
@ -4,12 +4,31 @@ type Mnemonic uint8
|
||||
|
||||
const (
|
||||
NONE Mnemonic = iota
|
||||
|
||||
// Arithmetic
|
||||
ADD
|
||||
AND
|
||||
CALL
|
||||
COMMENT
|
||||
COMPARE
|
||||
DIV
|
||||
MODULO
|
||||
MUL
|
||||
NEGATE
|
||||
SUB
|
||||
|
||||
// Bitwise operations
|
||||
AND
|
||||
OR
|
||||
SHIFTL
|
||||
SHIFTRS
|
||||
XOR
|
||||
|
||||
// Data movement
|
||||
MOVE
|
||||
LOAD
|
||||
POP
|
||||
PUSH
|
||||
STORE
|
||||
|
||||
// Control flow
|
||||
CALL
|
||||
JE
|
||||
JNE
|
||||
JG
|
||||
@ -17,21 +36,13 @@ const (
|
||||
JL
|
||||
JLE
|
||||
JUMP
|
||||
MUL
|
||||
LABEL
|
||||
LOAD
|
||||
MODULO
|
||||
MOVE
|
||||
OR
|
||||
POP
|
||||
PUSH
|
||||
RETURN
|
||||
SHIFTL
|
||||
SHIFTRS
|
||||
STORE
|
||||
SUB
|
||||
SYSCALL
|
||||
XOR
|
||||
|
||||
// Others
|
||||
COMMENT
|
||||
COMPARE
|
||||
)
|
||||
|
||||
// String returns a human readable version.
|
||||
@ -73,6 +84,8 @@ func (m Mnemonic) String() string {
|
||||
return "move"
|
||||
case MUL:
|
||||
return "mul"
|
||||
case NEGATE:
|
||||
return "negate"
|
||||
case OR:
|
||||
return "or"
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/build/asm"
|
||||
"git.akyoto.dev/cli/q/src/build/ast"
|
||||
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||
@ -24,8 +26,19 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
||||
return err
|
||||
}
|
||||
|
||||
if len(node.Children) < 2 {
|
||||
return errors.New(errors.MissingOperand, f.File, node.Token.End())
|
||||
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())
|
||||
}
|
||||
|
||||
err := f.ExpressionToRegister(node.Children[0], register)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return f.ExecuteRegister(node.Token, register)
|
||||
}
|
||||
|
||||
left := node.Children[0]
|
||||
|
@ -38,8 +38,6 @@ const (
|
||||
Shr // >>
|
||||
LogicalAnd // &&
|
||||
LogicalOr // ||
|
||||
Not // ! (unary)
|
||||
Negate // - (unary)
|
||||
Equal // ==
|
||||
Less // <
|
||||
Greater // >
|
||||
@ -51,6 +49,10 @@ const (
|
||||
Call // x()
|
||||
Array // [x]
|
||||
Separator // ,
|
||||
_unary // <unary>
|
||||
Not // ! (unary)
|
||||
Negate // - (unary)
|
||||
_unaryEnd // </unary>
|
||||
_assignments // <assignments>
|
||||
Assign // =
|
||||
AddAssign // +=
|
||||
|
@ -43,6 +43,11 @@ func (t Token) IsOperator() bool {
|
||||
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.
|
||||
func (t *Token) Reset() {
|
||||
t.Position = 0
|
||||
|
@ -27,7 +27,7 @@ func Tokenize(buffer []byte) List {
|
||||
case '\n':
|
||||
tokens = append(tokens, Token{Kind: NewLine, Position: i, Length: 1})
|
||||
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})
|
||||
} else {
|
||||
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},
|
||||
{"assert", "", "", 1},
|
||||
{"negative", "", "", 32},
|
||||
{"negation", "", "", 32},
|
||||
}
|
||||
|
||||
func TestPrograms(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user