diff --git a/pkg/compiler/allocator.go b/pkg/compiler/allocator.go index b934184c..0087a9f9 100644 --- a/pkg/compiler/allocator.go +++ b/pkg/compiler/allocator.go @@ -31,7 +31,7 @@ type ( func NewRegisterAllocator() *RegisterAllocator { return &RegisterAllocator{ registers: make(map[runtime.Operand]*RegisterStatus), - nextRegister: 0, + nextRegister: runtime.ResultOperand + 1, // we start at 1 to avoid ResultOperand lifetimes: make(map[string]*RegisterLifetime), usageGraph: make(map[runtime.Operand]map[runtime.Operand]bool), } diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index 97f36ece..2f536566 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -19,31 +19,31 @@ func TestVariables(t *testing.T) { nil, nil, }, - //{ - // `LET a = TRUE RETURN a`, - // true, - // nil, - //}, - //{ - // `LET a = 1 RETURN a`, - // 1, - // nil, - //}, - //{ - // `LET a = 1.1 RETURN a`, - // 1.1, - // nil, - //}, - //{ - // `LET i = 'foo' RETURN i`, - // "foo", - // nil, - //}, - //{ - // `LET i = [] RETURN i`, - // []any{}, - // ShouldEqualJSON, - //}, + { + `LET a = TRUE RETURN a`, + true, + nil, + }, + { + `LET a = 1 RETURN a`, + 1, + nil, + }, + { + `LET a = 1.1 RETURN a`, + 1.1, + nil, + }, + { + `LET i = 'foo' RETURN i`, + "foo", + nil, + }, + { + `LET i = [] RETURN i`, + []any{}, + ShouldEqualJSON, + }, //{ // `LET i = [1, 2, 3] RETURN i`, // []any{1, 2, 3}, diff --git a/pkg/compiler/emitter.go b/pkg/compiler/emitter.go index 5ee4ba89..4cd67cc4 100644 --- a/pkg/compiler/emitter.go +++ b/pkg/compiler/emitter.go @@ -31,6 +31,11 @@ func (e *Emitter) EmitAB(op runtime.Opcode, dest, src1 runtime.Operand) { e.EmitABC(op, dest, src1, 0) } +// EmitABx emits an opcode with a destination register and a custom argument. +func (e *Emitter) EmitABx(op runtime.Opcode, dest runtime.Operand, arg int) { + e.EmitABC(op, dest, runtime.Operand(arg), 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{ diff --git a/pkg/compiler/symbols.go b/pkg/compiler/symbols.go index 3b793acf..4a5e370e 100644 --- a/pkg/compiler/symbols.go +++ b/pkg/compiler/symbols.go @@ -9,7 +9,7 @@ import ( type SymbolTable struct { constants []core.Value constantsIndex map[uint64]int - globals map[string]int + globals map[string]runtime.Operand scope int locals []Variable registers *RegisterAllocator @@ -19,7 +19,7 @@ func NewSymbolTable(registers *RegisterAllocator) *SymbolTable { return &SymbolTable{ constants: make([]core.Value, 0), constantsIndex: make(map[uint64]int), - globals: make(map[string]int), + globals: make(map[string]runtime.Operand), locals: make([]Variable, 0), registers: registers, } @@ -62,8 +62,6 @@ func (st *SymbolTable) AddConstant(constant core.Value) runtime.Operand { } func (st *SymbolTable) DefineVariable(name string) runtime.Operand { - var index int - if st.scope == 0 { // Check for duplicate global variable names. _, ok := st.globals[name] @@ -72,12 +70,11 @@ func (st *SymbolTable) DefineVariable(name string) runtime.Operand { panic(core.Error(ErrVariableNotUnique, name)) } - index = len(st.globals) + op := st.AddConstant(values.NewString(name)) // Define global variable. - st.globals[name] = index + st.globals[name] = op - // Return a constant operand to indicate that this is a global variable and use its index. - return st.AddConstant(values.NewString(name)) + return op } register := st.registers.AllocateLocalVar(name) @@ -88,7 +85,7 @@ func (st *SymbolTable) DefineVariable(name string) runtime.Operand { Register: register, }) - return runtime.NewRegisterOperand(index) + return register } func (st *SymbolTable) LookupVariable(name string) runtime.Operand { @@ -99,13 +96,13 @@ func (st *SymbolTable) LookupVariable(name string) runtime.Operand { } } - index, ok := st.globals[name] + op, ok := st.globals[name] if !ok { panic(core.Error(ErrVariableNotFound, name)) } - return runtime.NewConstantOperand(index) + return op } func (st *SymbolTable) ExitScope() { diff --git a/pkg/compiler/visitor.go b/pkg/compiler/visitor.go index bc6bea1b..b51d427c 100644 --- a/pkg/compiler/visitor.go +++ b/pkg/compiler/visitor.go @@ -491,16 +491,21 @@ func (v *visitor) VisitVariable(ctx *fql.VariableContext) interface{} { } func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { - //var size int - // - //if args := ctx.ArgumentList(); args != nil { - // out := v.VisitArgumentList(args.(*fql.ArgumentListContext)) - // size = out.(int) - //} - // - //v.emitter.EmitABC(runtime.OpArray, size) + dest := v.registers.Allocate(VarTemporary) + var size int - return nil + if args := ctx.ArgumentList(); args != nil { + exps := args.AllExpression() + size = len(exps) + + //for _, arg := range exps { + // arg.Accept(v) + //} + } + + v.emitter.EmitABx(runtime.OpArray, dest, size) + + return dest } func (v *visitor) VisitArgumentList(ctx *fql.ArgumentListContext) interface{} { @@ -664,7 +669,12 @@ func (v *visitor) VisitLiteral(ctx *fql.LiteralContext) interface{} { func (v *visitor) VisitReturnExpression(ctx *fql.ReturnExpressionContext) interface{} { valReg := ctx.Expression().Accept(v).(runtime.Operand) - v.emitter.EmitAB(runtime.OpMove, runtime.ResultOperand, valReg) + if valReg.IsConstant() { + v.emitter.EmitAB(runtime.OpLoadGlobal, runtime.ResultOperand, valReg) + } else { + v.emitter.EmitAB(runtime.OpMove, runtime.ResultOperand, valReg) + } + v.emitter.Emit(runtime.OpReturn) //if len(v.loops) == 0 { diff --git a/pkg/runtime/opcode.go b/pkg/runtime/opcode.go index 189e4da0..177499ea 100644 --- a/pkg/runtime/opcode.go +++ b/pkg/runtime/opcode.go @@ -17,6 +17,8 @@ const ( OpDecr OpArray + OpStoreIndex + OpObject OpLoadProperty OpLoadPropertyOptional diff --git a/pkg/runtime/vm.go b/pkg/runtime/vm.go index c87a59ed..cc94b4ad 100644 --- a/pkg/runtime/vm.go +++ b/pkg/runtime/vm.go @@ -4,6 +4,7 @@ import ( "context" "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/operators" + "github.com/MontFerret/ferret/pkg/runtime/values" ) type VM struct { @@ -55,7 +56,7 @@ loop: case OpStoreGlobal: vm.globals[program.Constants[dst.Constant()].String()] = vm.load(src1) case OpLoadGlobal: - reg[dst] = vm.globals[program.Constants[src1].String()] + reg[dst] = vm.globals[program.Constants[src1.Constant()].String()] case OpAdd: reg[dst] = operators.Add(vm.load(src1), vm.load(src2)) case OpSub: @@ -75,8 +76,7 @@ loop: //stack.Push(values.ToBoolean(stack.Pop())) case OpArray: - //size := arg - //arr := values.NewSizedArray(size) + arr := values.NewSizedArray(int(src1)) // //// iterate from the end to the beginning //// because stack is LIFO @@ -85,6 +85,7 @@ loop: //} // //stack.Push(arr) + reg[dst] = arr case OpObject: //obj := values.NewObject()