From bee75360420ec1b62210c444ac39f5cd1cda65ed Mon Sep 17 00:00:00 2001 From: Aotokitsuruya Date: Wed, 22 May 2024 22:35:18 +0800 Subject: [PATCH] Implement minimal rest parameter feature --- array.go | 38 ++++++++++++++++++++++++++++++++++++++ class.go | 2 ++ features/method.feature | 1 - init.go | 4 ++++ mruby.go | 1 + presym.go | 2 ++ proc.go | 12 ++++++++++++ vm.go | 19 ++++++++++++++++++- 8 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 array.go diff --git a/array.go b/array.go new file mode 100644 index 0000000..ec152c4 --- /dev/null +++ b/array.go @@ -0,0 +1,38 @@ +package mruby + +func initArray(mrb *State) (err error) { + mrb.ArrayClass, err = mrb.DefineClassId(_ArrayClass(mrb), mrb.ObjectClass) + if err != nil { + return err + } + + mrb.DefineMethodId(mrb.ArrayClass, _join(mrb), arrayJoin) + + return nil +} + +func arrayJoin(mrb *State, self Value) Value { + argv := mrb.GetArgv() + + elem, ok := self.([]Value) + if !ok { + return nil + } + + ret := "" + elemLen := len(elem) + for i, v := range elem { + v, ok := v.(string) + if !ok { + continue + } + + ret += v + hasNext := elemLen > i+1 + if hasNext { + ret += argv[0].(string) + } + } + + return ret +} diff --git a/class.go b/class.go index c16c647..e157b46 100644 --- a/class.go +++ b/class.go @@ -41,6 +41,8 @@ func (mrb *State) Class(v Value) RClass { switch v := v.(type) { case RObject: return v.Class() + case []Value: + return mrb.ArrayClass case bool: if v { return mrb.TrueClass diff --git a/features/method.feature b/features/method.feature index afda085..102e3ae 100644 --- a/features/method.feature +++ b/features/method.feature @@ -48,7 +48,6 @@ Feature: Method """ Then there should return string "Hello, Ruby! I love Ruby" - @wip Scenario: I can call the method with rest arguments When I execute ruby code: """ diff --git a/init.go b/init.go index d5840ae..6457b93 100644 --- a/init.go +++ b/init.go @@ -13,5 +13,9 @@ func initCore(mrb *State) (err error) { return } + if err = initArray(mrb); err != nil { + return + } + return } diff --git a/mruby.go b/mruby.go index dcba331..114e102 100644 --- a/mruby.go +++ b/mruby.go @@ -21,6 +21,7 @@ type State struct { ClassClass RClass FalseClass RClass TrueClass RClass + ArrayClass RClass KernelModule RClass topSelf RObject diff --git a/presym.go b/presym.go index ebc0bcf..38a0681 100644 --- a/presym.go +++ b/presym.go @@ -19,5 +19,7 @@ var ( _Kernel = predefineSymbol("Kernel") _TrueClass = predefineSymbol("TrueClass") _FalseClass = predefineSymbol("FalseClass") + _ArrayClass = predefineSymbol("Array") + _join = predefineSymbol("join") _raise = predefineSymbol("raise") ) diff --git a/proc.go b/proc.go index aa024ce..62dec4d 100644 --- a/proc.go +++ b/proc.go @@ -2,6 +2,18 @@ package mruby import "github.com/elct9620/mruby-go/insn" +func AspecReq(n int) int { + return (((n) >> 18) & 0x1f) +} + +func AspecOpt(n int) int { + return (((n) >> 13) & 0x1f) +} + +func AspecRest(n int) int { + return (((n) >> 12) & 0x1) +} + type RProc interface { IsGoFunction() bool Body() any diff --git a/vm.go b/vm.go index 61f125f..ee30a34 100644 --- a/vm.go +++ b/vm.go @@ -198,7 +198,24 @@ func (mrb *State) VmExec(proc RProc, code *insn.Sequence) (ret Value, err error) ctx.Set(0, method.Call(mrb, recv)) mrb.callinfoPop() case op.Enter: - _ = code.ReadW() + a := code.ReadW() + a = append([]byte{0x00}, a...) + aspec := int(binary.BigEndian.Uint32(a)) + + req := AspecReq(aspec) + opt := AspecOpt(aspec) + rest := AspecRest(aspec) + + if rest > 0 { + values := make([]Value, 0) + argv := mrb.GetArgv() + for i := 0; i < len(argv); i++ { + values = append(values, argv[i]) + } + + ctx.Set(req+opt+1, values) + } + ctx.SetSequenceCursor(code.Cursor()) case op.EQ: a := code.ReadB()