Skip to content

Commit

Permalink
First test passed
Browse files Browse the repository at this point in the history
  • Loading branch information
ziflex committed Oct 25, 2024
1 parent a15e95c commit a627598
Show file tree
Hide file tree
Showing 12 changed files with 515 additions and 453 deletions.
46 changes: 26 additions & 20 deletions pkg/compiler/allocator.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package compiler

import "github.com/MontFerret/ferret/pkg/runtime"

type (
// RegisterStatus tracks register usage
RegisterStatus struct {
Expand All @@ -18,35 +20,39 @@ type (

// RegisterAllocator manages register allocation
RegisterAllocator struct {
registers map[Register]*RegisterStatus
nextRegister Register
registers map[runtime.Operand]*RegisterStatus
nextRegister runtime.Operand
currentInstr int
lifetimes map[string]*RegisterLifetime
usageGraph map[Register]map[Register]bool
usageGraph map[runtime.Operand]map[runtime.Operand]bool
}
)

func NewRegisterAllocator() *RegisterAllocator {
return &RegisterAllocator{
registers: make(map[Register]*RegisterStatus),
registers: make(map[runtime.Operand]*RegisterStatus),
nextRegister: 0,
lifetimes: make(map[string]*RegisterLifetime),
usageGraph: make(map[Register]map[Register]bool),
usageGraph: make(map[runtime.Operand]map[runtime.Operand]bool),
}
}

func (ra *RegisterAllocator) AllocateLocalVarRegister(name string) Register {
func (ra *RegisterAllocator) AllocateLocalVar(name string) runtime.Operand {
// Allocate register
reg := ra.AllocateRegister(VarLocal)
reg := ra.Allocate(VarLocal)

// Update register status
ra.registers[reg].VarName = name

return reg
}

// AllocateRegister assigns a register based on variable type
func (ra *RegisterAllocator) AllocateRegister(varType VarType) Register {
func (ra *RegisterAllocator) AllocateTempVar() runtime.Operand {
return ra.Allocate(VarTemporary)
}

// Allocate assigns a register based on variable type
func (ra *RegisterAllocator) Allocate(varType VarType) runtime.Operand {
// Try to find a free register first
reg, found := ra.findFreeRegister()

Expand All @@ -70,8 +76,8 @@ func (ra *RegisterAllocator) AllocateRegister(varType VarType) Register {
return newReg
}

// FreeRegister marks a register as available
func (ra *RegisterAllocator) FreeRegister(reg Register) {
// Free marks a register as available
func (ra *RegisterAllocator) Free(reg runtime.Operand) {
if status, exists := ra.registers[reg]; exists {
status.IsAllocated = false
status.Lifetime.End = ra.currentInstr
Expand All @@ -86,7 +92,7 @@ func (ra *RegisterAllocator) FreeRegister(reg Register) {
}

// findFreeRegister looks for an unused register
func (ra *RegisterAllocator) findFreeRegister() (Register, bool) {
func (ra *RegisterAllocator) findFreeRegister() (runtime.Operand, bool) {
// First, try to find a completely free register
for reg, status := range ra.registers {
if !status.IsAllocated {
Expand All @@ -95,7 +101,7 @@ func (ra *RegisterAllocator) findFreeRegister() (Register, bool) {
}

// If no free registers, try to find one that's no longer needed
var candidate Register
var candidate runtime.Operand
var found bool
maxLastUse := -1

Expand All @@ -109,16 +115,16 @@ func (ra *RegisterAllocator) findFreeRegister() (Register, bool) {

if found {
// Free the candidate register
ra.FreeRegister(candidate)
ra.Free(candidate)

return candidate, true
}

return 0, false
}

// UpdateRegisterUse updates the usage information for a register
func (ra *RegisterAllocator) UpdateRegisterUse(reg Register) {
// UpdateUse updates the usage information for a register
func (ra *RegisterAllocator) UpdateUse(reg runtime.Operand) {
status := ra.registers[reg]

if status == nil {
Expand All @@ -139,7 +145,7 @@ func (ra *RegisterAllocator) UpdateRegisterUse(reg Register) {
}

// registersInterfere checks if two registers have overlapping lifetimes
func (ra *RegisterAllocator) registersInterfere(reg1, reg2 Register) bool {
func (ra *RegisterAllocator) registersInterfere(reg1, reg2 runtime.Operand) bool {
status1 := ra.registers[reg1]
status2 := ra.registers[reg2]

Expand All @@ -153,12 +159,12 @@ func (ra *RegisterAllocator) registersInterfere(reg1, reg2 Register) bool {
}

// addInterference records that two registers interfere
func (ra *RegisterAllocator) addInterference(reg1, reg2 Register) {
func (ra *RegisterAllocator) addInterference(reg1, reg2 runtime.Operand) {
if ra.usageGraph[reg1] == nil {
ra.usageGraph[reg1] = make(map[Register]bool)
ra.usageGraph[reg1] = make(map[runtime.Operand]bool)
}
if ra.usageGraph[reg2] == nil {
ra.usageGraph[reg2] = make(map[Register]bool)
ra.usageGraph[reg2] = make(map[runtime.Operand]bool)
}

ra.usageGraph[reg1][reg2] = true
Expand Down
5 changes: 2 additions & 3 deletions pkg/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ func (c *Compiler) Compile(query string) (program *runtime.Program, err error) {
}

program = &runtime.Program{}
program.Bytecode = l.instructions
program.Constants = l.constants
//program.Locations = l.locations
program.Bytecode = l.emitter.instructions
program.Constants = l.symbols.constants
program.CatchTable = l.catchTable

return program, err
Expand Down
106 changes: 53 additions & 53 deletions pkg/compiler/compiler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ import (

func TestVariables(t *testing.T) {
RunUseCases(t, []UseCase{
//{
// `LET i = NONE RETURN i`,
// nil,
// nil,
//},
{
`LET i = NONE RETURN i`,
nil,
nil,
},
//{
// `LET a = TRUE RETURN a`,
// true,
Expand Down Expand Up @@ -109,12 +109,12 @@ func TestVariables(t *testing.T) {
// []int{1, 2, 3},
// ShouldEqualJSON,
//},
{
`LET _ = (FOR i IN 1..100 RETURN NONE)
RETURN TRUE`,
true,
ShouldEqualJSON,
},
//{
// `LET _ = (FOR i IN 1..100 RETURN NONE)
// RETURN TRUE`,
// true,
// ShouldEqualJSON,
//},
})

//
Expand Down Expand Up @@ -182,39 +182,39 @@ func TestVariables(t *testing.T) {
// So(string(out), ShouldEqual, "true")
//})

Convey("Should not compile FOR foo IN foo", t, func() {
c := compiler.New()

_, err := c.Compile(`
FOR foo IN foo
RETURN foo
`)

So(err, ShouldNotBeNil)
})

Convey("Should not compile if a variable not defined", t, func() {
c := compiler.New()

_, err := c.Compile(`
RETURN foo
`)

So(err, ShouldNotBeNil)
})

Convey("Should not compile if a variable is not unique", t, func() {
c := compiler.New()
//Convey("Should not compile FOR foo IN foo", t, func() {
// c := compiler.New()
//
// _, err := c.Compile(`
// FOR foo IN foo
// RETURN foo
// `)
//
// So(err, ShouldNotBeNil)
//})

_, err := c.Compile(`
LET foo = "bar"
LET foo = "baz"
RETURN foo
`)
//Convey("Should not compile if a variable not defined", t, func() {
// c := compiler.New()
//
// _, err := c.Compile(`
// RETURN foo
// `)
//
// So(err, ShouldNotBeNil)
//})

So(err, ShouldNotBeNil)
})
//Convey("Should not compile if a variable is not unique", t, func() {
// c := compiler.New()
//
// _, err := c.Compile(`
// LET foo = "bar"
// LET foo = "baz"
//
// RETURN foo
// `)
//
// So(err, ShouldNotBeNil)
//})

//SkipConvey("Should use value returned from WAITFOR EVENT", t, func() {
// out, err := newCompilerWithObservable().MustCompile(`
Expand Down Expand Up @@ -256,17 +256,17 @@ func TestVariables(t *testing.T) {
//})
//

Convey("Should not allow to use ignorable variable name", t, func() {
c := compiler.New()

_, err := c.Compile(`
LET _ = (FOR i IN 1..100 RETURN NONE)
RETURN _
`)

So(err, ShouldNotBeNil)
})
//Convey("Should not allow to use ignorable variable name", t, func() {
// c := compiler.New()
//
// _, err := c.Compile(`
// LET _ = (FOR i IN 1..100 RETURN NONE)
//
// RETURN _
// `)
//
// So(err, ShouldNotBeNil)
//})
}

func TestMathOperators(t *testing.T) {
Expand Down
40 changes: 40 additions & 0 deletions pkg/compiler/emitter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package compiler

import "github.com/MontFerret/ferret/pkg/runtime"

type Emitter struct {
instructions []runtime.Instruction
}

func NewEmitter() *Emitter {
return &Emitter{
instructions: make([]runtime.Instruction, 0, 8),
}
}

func (e *Emitter) Size() int {
return len(e.instructions)
}

// Emit emits an opcode with no arguments.
func (e *Emitter) Emit(op runtime.Opcode) {
e.EmitABC(op, 0, 0, 0)
}

// EmitA emits an opcode with a single destination register argument.
func (e *Emitter) EmitA(op runtime.Opcode, dest runtime.Operand) {
e.EmitABC(op, dest, 0, 0)
}

// EmitAB emits an opcode with a destination register and a single source register argument.
func (e *Emitter) EmitAB(op runtime.Opcode, dest, src1 runtime.Operand) {
e.EmitABC(op, dest, src1, 0)
}

// EmitABC emits an opcode with a destination register and two source register arguments.
func (e *Emitter) EmitABC(op runtime.Opcode, dest, src1, src2 runtime.Operand) {
e.instructions = append(e.instructions, runtime.Instruction{
Opcode: op,
Operands: [3]runtime.Operand{dest, src1, src2},
})
}
1 change: 1 addition & 0 deletions pkg/compiler/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ var (
ErrInvalidToken = errors.New("invalid token")
ErrUnexpectedToken = errors.New("unexpected token")
ErrInvalidDataSource = errors.New("invalid data source")
ErrUnknownOpcode = errors.New("unknown opcode")
)
Loading

0 comments on commit a627598

Please sign in to comment.