From 66b14dc83b63d179c6d5bb9d1a95fa830d63dd83 Mon Sep 17 00:00:00 2001 From: Tim Voronov Date: Tue, 29 Oct 2024 16:01:05 -0400 Subject: [PATCH] Implemented member expression --- pkg/compiler/compiler.go | 1 + pkg/compiler/compiler_setup_test.go | 62 ++++++++-------- pkg/compiler/compiler_test.go | 44 +++++------ pkg/compiler/visitor.go | 109 ++++++++++++---------------- pkg/runtime/program.go | 1 + pkg/runtime/vm.go | 104 +++++++++++++------------- 6 files changed, 157 insertions(+), 164 deletions(-) diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 96562aa7..8e3672e9 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -67,6 +67,7 @@ func (c *Compiler) Compile(query string) (program *runtime.Program, err error) { program.Bytecode = l.emitter.instructions program.Constants = l.symbols.constants program.CatchTable = l.catchTable + program.Registers = int(l.registers.nextRegister) return program, err } diff --git a/pkg/compiler/compiler_setup_test.go b/pkg/compiler/compiler_setup_test.go index 582041d2..0ab479aa 100644 --- a/pkg/compiler/compiler_setup_test.go +++ b/pkg/compiler/compiler_setup_test.go @@ -77,44 +77,46 @@ func ShouldHaveSameItems(actual any, expected ...any) string { func RunUseCasesWith(t *testing.T, c *compiler.Compiler, useCases []UseCase, opts ...runtime.EnvironmentOption) { for _, useCase := range useCases { - Convey(useCase.Expression, t, func() { - // catch panic - //defer func() { - // if r := recover(); r != nil { - // panic(fmt.Sprintf("%v,\nUse Case %d: - %s", r, idx+1, useCase.Expression)) - // } - //}() + t.Run(useCase.Expression, func(t *testing.T) { + Convey(useCase.Expression, t, func() { + // catch panic + //defer func() { + // if r := recover(); r != nil { + // panic(fmt.Sprintf("%v,\nUse Case %d: - %s", r, idx+1, useCase.Expression)) + // } + //}() - prog, err := c.Compile(useCase.Expression) + prog, err := c.Compile(useCase.Expression) - So(err, ShouldBeNil) + So(err, ShouldBeNil) - options := []runtime.EnvironmentOption{ - runtime.WithFunctions(c.Functions().Unwrap()), - } - options = append(options, opts...) + options := []runtime.EnvironmentOption{ + runtime.WithFunctions(c.Functions().Unwrap()), + } + options = append(options, opts...) - out, err := Exec(prog, ArePtrsEqual(useCase.Assertion, ShouldEqualJSON), options...) + out, err := Exec(prog, ArePtrsEqual(useCase.Assertion, ShouldEqualJSON), options...) - if !ArePtrsEqual(useCase.Assertion, ShouldBeError) { - So(err, ShouldBeNil) - } + if !ArePtrsEqual(useCase.Assertion, ShouldBeError) { + So(err, ShouldBeNil) + } - if ArePtrsEqual(useCase.Assertion, ShouldEqualJSON) { - expected, err := j.Marshal(useCase.Expected) - So(err, ShouldBeNil) - So(out, ShouldEqualJSON, string(expected)) - } else if ArePtrsEqual(useCase.Assertion, ShouldBeError) { - if useCase.Expected != nil { - So(err, ShouldBeError, useCase.Expected) + if ArePtrsEqual(useCase.Assertion, ShouldEqualJSON) { + expected, err := j.Marshal(useCase.Expected) + So(err, ShouldBeNil) + So(out, ShouldEqualJSON, string(expected)) + } else if ArePtrsEqual(useCase.Assertion, ShouldBeError) { + if useCase.Expected != nil { + So(err, ShouldBeError, useCase.Expected) + } else { + So(err, ShouldBeError) + } + } else if ArePtrsEqual(useCase.Assertion, ShouldHaveSameItems) { + So(out, ShouldHaveSameItems, useCase.Expected) } else { - So(err, ShouldBeError) + So(out, ShouldEqual, useCase.Expected) } - } else if ArePtrsEqual(useCase.Assertion, ShouldHaveSameItems) { - So(out, ShouldHaveSameItems, useCase.Expected) - } else { - So(out, ShouldEqual, useCase.Expected) - } + }) }) } } diff --git a/pkg/compiler/compiler_test.go b/pkg/compiler/compiler_test.go index ac9a0649..8fb8fd6d 100644 --- a/pkg/compiler/compiler_test.go +++ b/pkg/compiler/compiler_test.go @@ -758,17 +758,17 @@ func TestMember(t *testing.T) { }, { `LET o1 = { - first: { - second: { - ["third"]: { - fourth: { - fifth: { - bottom: true - } - } - } - } - } + first: { + second: { + ["third"]: { + fourth: { + fifth: { + bottom: true + } + } + } + } + } } LET o2 = { prop: "third" } @@ -780,17 +780,17 @@ func TestMember(t *testing.T) { }, { `LET o1 = { - first: { - second: { - third: { - fourth: { - fifth: { - bottom: true - } - } - } - } - } + first: { + second: { + third: { + fourth: { + fifth: { + bottom: true + } + } + } + } + } } LET o2 = { prop: "third" } diff --git a/pkg/compiler/visitor.go b/pkg/compiler/visitor.go index 707bbbfd..bde29d60 100644 --- a/pkg/compiler/visitor.go +++ b/pkg/compiler/visitor.go @@ -400,39 +400,49 @@ func (v *visitor) VisitFunctionCallExpression(ctx *fql.FunctionCallExpressionCon } func (v *visitor) VisitMemberExpression(ctx *fql.MemberExpressionContext) interface{} { - //src := ctx.MemberExpressionSource().(*fql.MemberExpressionSourceContext) - // - //if c := src.Variable(); c != nil { - // c.Accept(v) - //} else if c := src.Param(); c != nil { - // c.Accept(v) - //} else if c := src.ObjectLiteral(); c != nil { - // c.Accept(v) - //} else if c := src.ArrayLiteral(); c != nil { - // c.Accept(v) - //} else if c := src.FunctionCall(); c != nil { - // c.Accept(v) - //} - // - //segments := ctx.AllMemberExpressionPath() - // - //for _, segment := range segments { - // p := segment.(*fql.MemberExpressionPathContext) - // - // if c := p.PropertyName(); c != nil { - // c.Accept(v) - // } else if c := p.ComputedPropertyName(); c != nil { - // c.Accept(v) - // } - // - // if p.ErrorOperator() != nil { - // v.emitter.EmitABC(runtime.OpLoadPropertyOptional) - // } else { - // v.emitter.EmitABC(runtime.OpLoadProperty) - // } - //} + mes := ctx.MemberExpressionSource().(*fql.MemberExpressionSourceContext) + + var mesOut interface{} + + if c := mes.Variable(); c != nil { + mesOut = c.Accept(v) + } else if c := mes.Param(); c != nil { + mesOut = c.Accept(v) + } else if c := mes.ObjectLiteral(); c != nil { + mesOut = c.Accept(v) + } else if c := mes.ArrayLiteral(); c != nil { + mesOut = c.Accept(v) + } else if c := mes.FunctionCall(); c != nil { + mesOut = c.Accept(v) + } + + var dst runtime.Operand + src1 := v.toRegister(mesOut.(runtime.Operand)) + segments := ctx.AllMemberExpressionPath() + + for _, segment := range segments { + var out2 interface{} + p := segment.(*fql.MemberExpressionPathContext) + + if c := p.PropertyName(); c != nil { + out2 = c.Accept(v) + } else if c := p.ComputedPropertyName(); c != nil { + out2 = c.Accept(v) + } - return nil + src2 := v.toRegister(out2.(runtime.Operand)) + dst = v.registers.Allocate(VarTemporary) + + if p.ErrorOperator() != nil { + v.emitter.EmitABC(runtime.OpLoadPropertyOptional, dst, src1, src2) + } else { + v.emitter.EmitABC(runtime.OpLoadProperty, dst, src1, src2) + } + + src1 = dst + } + + return dst } func (v *visitor) VisitRangeOperator(ctx *fql.RangeOperatorContext) interface{} { @@ -529,7 +539,7 @@ func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { // Free source register if temporary if srcReg.IsRegister() { - v.registers.Free(srcReg) + //v.registers.Free(srcReg) } } @@ -537,7 +547,7 @@ func (v *visitor) VisitArrayLiteral(ctx *fql.ArrayLiteralContext) interface{} { v.emitter.EmitAs(runtime.OpArray, destReg, seq) // Free seq registers - v.registers.FreeSequence(seq) + //v.registers.FreeSequence(seq) return destReg } @@ -594,7 +604,7 @@ func (v *visitor) VisitObjectLiteral(ctx *fql.ObjectLiteralContext) interface{} // Free source register if temporary if propOp.IsRegister() { - v.registers.Free(propOp) + //v.registers.Free(propOp) } } @@ -817,7 +827,7 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { // If condition was temporary, free it if condReg.IsRegister() { - v.registers.Free(condReg) + //v.registers.Free(condReg) } // Jump to 'false' branch if condition is false @@ -830,7 +840,7 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { // Free temporary register if needed if trueReg.IsRegister() { - v.registers.Free(trueReg) + //v.registers.Free(trueReg) } } @@ -845,7 +855,7 @@ func (v *visitor) VisitExpression(ctx *fql.ExpressionContext) interface{} { // Free temporary register if needed if falseReg.IsRegister() { - v.registers.Free(falseReg) + //v.registers.Free(falseReg) } } @@ -999,26 +1009,3 @@ func (v *visitor) toRegister(op runtime.Operand) runtime.Operand { return reg } - -func (v *visitor) endLoopScope() { - //v.loops = v.loops[:len(v.loops)-1] - // - //var unwrap bool - // - //if len(v.loops) == 0 { - // unwrap = true - //} else if !v.loops[len(v.loops)-1].passThrough { - // unwrap = true - //} - // - //if unwrap { - // v.emitter.EmitABC(runtime.OpLoopUnwrapOutput) - //} -} - -// emitLoop emits a loop instruction. -func (v *visitor) emitLoop(loopStart int) { - //pos := v.emitJump(runtime.OpJumpBackward) - //jump := pos - loopStart - //v.arguments[pos-1] = jump -} diff --git a/pkg/runtime/program.go b/pkg/runtime/program.go index f1d98a67..9c6c6152 100644 --- a/pkg/runtime/program.go +++ b/pkg/runtime/program.go @@ -12,6 +12,7 @@ type Program struct { Bytecode []Instruction Constants []core.Value CatchTable [][2]int + Registers int } func (program *Program) Disassemble() string { diff --git a/pkg/runtime/vm.go b/pkg/runtime/vm.go index 27dda73c..ec95c9fe 100644 --- a/pkg/runtime/vm.go +++ b/pkg/runtime/vm.go @@ -5,6 +5,7 @@ import ( "github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/operators" "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/runtime/values/types" ) type VM struct { @@ -35,7 +36,7 @@ func (vm *VM) Run(ctx context.Context, opts ...EnvironmentOption) (core.Value, e } vm.env = newEnvironment(opts) - vm.currentFrame = newFrame(32, 0, nil) + vm.currentFrame = newFrame(vm.program.Registers, 0, nil) vm.frames = make([]*Frame, 4) vm.globals = make(map[string]core.Value) vm.pc = 0 @@ -44,11 +45,12 @@ func (vm *VM) Run(ctx context.Context, opts ...EnvironmentOption) (core.Value, e loop: for vm.pc < len(program.Bytecode) { inst := program.Bytecode[vm.pc] + op := inst.Opcode dst, src1, src2 := inst.Operands[0], inst.Operands[1], inst.Operands[2] reg := vm.currentFrame.registers vm.pc++ - switch inst.Opcode { + switch op { case OpMove: reg[dst] = reg[src1] case OpLoadConst: @@ -126,55 +128,55 @@ loop: reg[dst] = obj case OpLoadProperty, OpLoadPropertyOptional: - //prop := stack.Pop() - //val := stack.Pop() - // - //switch getter := prop.(type) { - //case values.String: - // switch src := val.(type) { - // case *values.Object: - // stack.Push(src.MustGetOr(getter, values.None)) - // case core.Keyed: - // out, err := src.GetByKey(ctx, getter.String()) - // - // if err == nil { - // stack.Push(out) - // } else if op == OpLoadPropertyOptional { - // stack.Push(values.None) - // } else { - // return nil, err - // } - // default: - // if op != OpLoadPropertyOptional { - // return nil, core.TypeError(src, types.Object, types.Keyed) - // } - // - // stack.Push(values.None) - // } - //case values.Float, values.Int: - // switch src := val.(type) { - // case *values.Array: - // idx := values.ToInt(getter) - // - // stack.Push(src.Get(idx)) - // case core.Indexed: - // out, err := src.GetByIndex(ctx, int(values.ToInt(getter))) - // - // if err == nil { - // stack.Push(out) - // } else if op == OpLoadPropertyOptional { - // stack.Push(values.None) - // } else { - // return nil, err - // } - // default: - // if op != OpLoadPropertyOptional { - // return nil, core.TypeError(src, types.Array, types.Indexed) - // } - // - // stack.Push(values.None) - // } - //} + val := reg[src1] + prop := reg[src2] + + switch getter := prop.(type) { + case values.String: + switch src := val.(type) { + case *values.Object: + reg[dst] = src.MustGetOr(getter, values.None) + case core.Keyed: + out, err := src.GetByKey(ctx, getter.String()) + + if err == nil { + reg[dst] = out + } else if op == OpLoadPropertyOptional { + reg[dst] = values.None + } else { + return nil, err + } + default: + if op != OpLoadPropertyOptional { + return nil, core.TypeError(src, types.Object, types.Keyed) + } + + reg[dst] = values.None + } + case values.Float, values.Int: + switch src := val.(type) { + case *values.Array: + idx := values.ToInt(getter) + + reg[dst] = src.Get(idx) + case core.Indexed: + out, err := src.GetByIndex(ctx, int(values.ToInt(getter))) + + if err == nil { + reg[dst] = out + } else if op == OpLoadPropertyOptional { + reg[dst] = values.None + } else { + return nil, err + } + default: + if op != OpLoadPropertyOptional { + return nil, core.TypeError(src, types.Array, types.Indexed) + } + + reg[dst] = values.None + } + } case OpNegate: reg[dst] = values.Negate(reg[src1]) case OpFlipPositive: