Implemented number output
This commit is contained in:
parent
f4dd9004be
commit
123738f88c
5
examples/itoa/itoa.q
Normal file
5
examples/itoa/itoa.q
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
main() {
|
||||||
|
io.printNum(2147483647)
|
||||||
|
}
|
22
lib/io/io.q
Normal file
22
lib/io/io.q
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import mem
|
||||||
|
import sys
|
||||||
|
|
||||||
|
printNum(x) {
|
||||||
|
length := 20
|
||||||
|
buffer := mem.alloc(length)
|
||||||
|
end := buffer + length
|
||||||
|
tmp := end
|
||||||
|
digit := 0
|
||||||
|
|
||||||
|
loop {
|
||||||
|
x, digit = x / 10
|
||||||
|
tmp -= 1
|
||||||
|
tmp[0] = '0' + digit
|
||||||
|
|
||||||
|
if x == 0 {
|
||||||
|
sys.write(1, tmp, end - tmp)
|
||||||
|
mem.free(buffer, length)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -200,6 +200,8 @@ func (a Assembler) Finalize() ([]byte, []byte) {
|
|||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
case *MemoryNumber:
|
case *MemoryNumber:
|
||||||
code = x64.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number)
|
code = x64.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number)
|
||||||
|
case *MemoryRegister:
|
||||||
|
code = x64.StoreRegister(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SYSCALL:
|
case SYSCALL:
|
||||||
|
29
src/build/asm/MemoryRegister.go
Normal file
29
src/build/asm/MemoryRegister.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MemoryRegister operates with a memory address and a number.
|
||||||
|
type MemoryRegister struct {
|
||||||
|
Address Memory
|
||||||
|
Register cpu.Register
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable version.
|
||||||
|
func (data *MemoryRegister) String() string {
|
||||||
|
return fmt.Sprintf("%dB [%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.Offset, data.Register)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryRegister adds an instruction with a memory address and a number.
|
||||||
|
func (a *Assembler) MemoryRegister(mnemonic Mnemonic, address Memory, register cpu.Register) {
|
||||||
|
a.Instructions = append(a.Instructions, Instruction{
|
||||||
|
Mnemonic: mnemonic,
|
||||||
|
Data: &MemoryRegister{
|
||||||
|
Address: address,
|
||||||
|
Register: register,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -4,24 +4,18 @@ import "git.akyoto.dev/cli/q/src/build/arch/x64"
|
|||||||
|
|
||||||
// divide implements the division on x64 machines.
|
// divide implements the division on x64 machines.
|
||||||
func divide(code []byte, data any) []byte {
|
func divide(code []byte, data any) []byte {
|
||||||
code = x64.PushRegister(code, x64.RDX)
|
|
||||||
|
|
||||||
switch operands := data.(type) {
|
switch operands := data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
if operands.Register == x64.RAX {
|
if operands.Register == x64.RAX {
|
||||||
code = x64.PushRegister(code, x64.RCX)
|
|
||||||
code = x64.MoveRegisterNumber32(code, x64.RCX, uint32(operands.Number))
|
code = x64.MoveRegisterNumber32(code, x64.RCX, uint32(operands.Number))
|
||||||
code = x64.ExtendRAXToRDX(code)
|
code = x64.ExtendRAXToRDX(code)
|
||||||
code = x64.DivRegister(code, x64.RCX)
|
code = x64.DivRegister(code, x64.RCX)
|
||||||
code = x64.PopRegister(code, x64.RCX)
|
|
||||||
} else {
|
} else {
|
||||||
code = x64.PushRegister(code, x64.RAX)
|
|
||||||
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register)
|
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register)
|
||||||
code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number))
|
code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number))
|
||||||
code = x64.ExtendRAXToRDX(code)
|
code = x64.ExtendRAXToRDX(code)
|
||||||
code = x64.DivRegister(code, operands.Register)
|
code = x64.DivRegister(code, operands.Register)
|
||||||
code = x64.MoveRegisterRegister64(code, operands.Register, x64.RAX)
|
code = x64.MoveRegisterRegister64(code, operands.Register, x64.RAX)
|
||||||
code = x64.PopRegister(code, x64.RAX)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case *RegisterRegister:
|
case *RegisterRegister:
|
||||||
@ -29,24 +23,18 @@ func divide(code []byte, data any) []byte {
|
|||||||
code = x64.ExtendRAXToRDX(code)
|
code = x64.ExtendRAXToRDX(code)
|
||||||
code = x64.DivRegister(code, operands.Source)
|
code = x64.DivRegister(code, operands.Source)
|
||||||
} else {
|
} else {
|
||||||
code = x64.PushRegister(code, x64.RAX)
|
|
||||||
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Destination)
|
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Destination)
|
||||||
code = x64.ExtendRAXToRDX(code)
|
code = x64.ExtendRAXToRDX(code)
|
||||||
|
|
||||||
code = x64.DivRegister(code, operands.Source)
|
code = x64.DivRegister(code, operands.Source)
|
||||||
code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RAX)
|
code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RAX)
|
||||||
code = x64.PopRegister(code, x64.RAX)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code = x64.PopRegister(code, x64.RDX)
|
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
// modulo calculates the division remainder on x64 machines.
|
// modulo calculates the division remainder on x64 machines.
|
||||||
func modulo(code []byte, data any) []byte {
|
func modulo(code []byte, data any) []byte {
|
||||||
code = x64.PushRegister(code, x64.RDX)
|
|
||||||
code = x64.PushRegister(code, x64.RAX)
|
|
||||||
|
|
||||||
switch operands := data.(type) {
|
switch operands := data.(type) {
|
||||||
case *RegisterNumber:
|
case *RegisterNumber:
|
||||||
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register)
|
code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register)
|
||||||
@ -62,7 +50,5 @@ func modulo(code []byte, data any) []byte {
|
|||||||
code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RDX)
|
code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RDX)
|
||||||
}
|
}
|
||||||
|
|
||||||
code = x64.PopRegister(code, x64.RAX)
|
|
||||||
code = x64.PopRegister(code, x64.RDX)
|
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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/errors"
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
"git.akyoto.dev/cli/q/src/build/token"
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
@ -26,36 +25,11 @@ func (f *Function) CompileAssign(node *ast.Assign) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if left.Token.Kind == token.Array {
|
if left.Token.Kind == token.Array {
|
||||||
name := left.Children[0].Token.Text(f.File.Bytes)
|
return f.CompileAssignArray(node)
|
||||||
variable := f.VariableByName(name)
|
|
||||||
|
|
||||||
if variable == nil {
|
|
||||||
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, left.Children[0].Token.Position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.UseVariable(variable)
|
if left.Token.Kind == token.Separator && right.Token.Kind == token.Div {
|
||||||
|
return f.CompileAssignDivision(node)
|
||||||
index := left.Children[1]
|
|
||||||
offset, _, err := f.Number(index.Token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
number, size, err := f.Number(right.Token)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
elementSize := byte(1)
|
|
||||||
|
|
||||||
if size != elementSize {
|
|
||||||
return errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: elementSize, Size: size}, f.File, right.Token.Position)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.MemoryNumber(asm.STORE, asm.Memory{Base: variable.Register, Offset: byte(offset), Length: elementSize}, number)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(errors.NotImplemented, f.File, left.Token.Position)
|
return errors.New(errors.NotImplemented, f.File, left.Token.Position)
|
||||||
|
37
src/build/core/CompileAssignArray.go
Normal file
37
src/build/core/CompileAssignArray.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileAssignArray compiles an assign statement for array elements.
|
||||||
|
func (f *Function) CompileAssignArray(node *ast.Assign) error {
|
||||||
|
left := node.Expression.Children[0]
|
||||||
|
right := node.Expression.Children[1]
|
||||||
|
|
||||||
|
name := left.Children[0].Token.Text(f.File.Bytes)
|
||||||
|
variable := f.VariableByName(name)
|
||||||
|
|
||||||
|
if variable == nil {
|
||||||
|
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, left.Children[0].Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer f.UseVariable(variable)
|
||||||
|
|
||||||
|
index := left.Children[1]
|
||||||
|
offset, _, err := f.Number(index.Token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
memory := asm.Memory{
|
||||||
|
Base: variable.Register,
|
||||||
|
Offset: byte(offset),
|
||||||
|
Length: byte(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.ExpressionToMemory(right, memory)
|
||||||
|
}
|
40
src/build/core/CompileAssignDivision.go
Normal file
40
src/build/core/CompileAssignDivision.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/arch/x64"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/ast"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompileAssignDivision compiles an assign statement that has quotient and remainder on the left side and division on the right.
|
||||||
|
func (f *Function) CompileAssignDivision(node *ast.Assign) error {
|
||||||
|
left := node.Expression.Children[0]
|
||||||
|
right := node.Expression.Children[1]
|
||||||
|
|
||||||
|
quotient := left.Children[0]
|
||||||
|
name := quotient.Token.Text(f.File.Bytes)
|
||||||
|
quotientVariable := f.VariableByName(name)
|
||||||
|
|
||||||
|
if quotientVariable == nil {
|
||||||
|
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, quotient.Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
remainder := left.Children[1]
|
||||||
|
name = remainder.Token.Text(f.File.Bytes)
|
||||||
|
remainderVariable := f.VariableByName(name)
|
||||||
|
|
||||||
|
if remainderVariable == nil {
|
||||||
|
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, remainder.Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
dividend := right.Children[0]
|
||||||
|
name = dividend.Token.Text(f.File.Bytes)
|
||||||
|
dividendVariable := f.VariableByName(name)
|
||||||
|
|
||||||
|
divisor := right.Children[1]
|
||||||
|
err := f.Execute(right.Token, dividendVariable.Register, divisor)
|
||||||
|
f.RegisterRegister(asm.MOVE, quotientVariable.Register, x64.RAX)
|
||||||
|
f.RegisterRegister(asm.MOVE, remainderVariable.Register, x64.RDX)
|
||||||
|
return err
|
||||||
|
}
|
32
src/build/core/ExpressionToMemory.go
Normal file
32
src/build/core/ExpressionToMemory.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/errors"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/expression"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExpressionToMemory puts the result of an expression into the specified memory address.
|
||||||
|
func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) error {
|
||||||
|
if node.IsLeaf() && (node.Token.Kind == token.Number || node.Token.Kind == token.Rune) {
|
||||||
|
number, size, err := f.Number(node.Token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if size != memory.Length {
|
||||||
|
return errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.MemoryNumber(asm.STORE, memory, number)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := f.NewRegister()
|
||||||
|
defer f.FreeRegister(tmp)
|
||||||
|
err := f.ExpressionToRegister(node, tmp)
|
||||||
|
f.MemoryRegister(asm.STORE, memory, tmp)
|
||||||
|
return err
|
||||||
|
}
|
11
src/build/register/MemoryRegister.go
Normal file
11
src/build/register/MemoryRegister.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package register
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/build/asm"
|
||||||
|
"git.akyoto.dev/cli/q/src/build/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Machine) MemoryRegister(mnemonic asm.Mnemonic, a asm.Memory, b cpu.Register) {
|
||||||
|
f.Assembler.MemoryRegister(mnemonic, a, b)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
@ -12,7 +12,6 @@ const (
|
|||||||
Rune // Rune is a single unicode code point.
|
Rune // Rune is a single unicode code point.
|
||||||
String // String is an uninterpreted series of characters in the source code.
|
String // String is an uninterpreted series of characters in the source code.
|
||||||
Comment // Comment is a comment.
|
Comment // Comment is a comment.
|
||||||
Separator // ,
|
|
||||||
GroupStart // (
|
GroupStart // (
|
||||||
GroupEnd // )
|
GroupEnd // )
|
||||||
BlockStart // {
|
BlockStart // {
|
||||||
@ -50,6 +49,7 @@ const (
|
|||||||
Period // .
|
Period // .
|
||||||
Call // x()
|
Call // x()
|
||||||
Array // [x]
|
Array // [x]
|
||||||
|
Separator // ,
|
||||||
_assignments // <assignments>
|
_assignments // <assignments>
|
||||||
Assign // =
|
Assign // =
|
||||||
AddAssign // +=
|
AddAssign // +=
|
||||||
|
@ -18,6 +18,7 @@ var examples = []struct {
|
|||||||
{"factorial", "", "", 120},
|
{"factorial", "", "", 120},
|
||||||
{"fibonacci", "", "", 55},
|
{"fibonacci", "", "", 55},
|
||||||
{"array", "", "Hello", 0},
|
{"array", "", "Hello", 0},
|
||||||
|
{"itoa", "", "2147483647", 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExamples(t *testing.T) {
|
func TestExamples(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user