Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The ISLisp Interpreter @ shibuya.lisp

The ISLisp Interpreter @ shibuya.lisp

ISLisp in JS/Go: Standards-compliant Lisp smaller than Common-Lisp. I introduced ISLisp in the lispmeetup of shibuya.lisp

TANIGUCHI Masaya

August 31, 2017
Tweet

Other Decks in Programming

Transcript

  1. INTRODUCE TANIGUCHI MASAYA ▸ ਺ֶΛઐ߈͍ͯ͠Δେֶੜ ▸ ܭࢉػ୅਺ॲཧܥʹ͍ͭͯษڧத ▸ CommonLisp, Go,

    JavaScript ͕޷͖ ▸ @ta2gch (Twitter / Github) ▸ ৄ͘͠͸https://ta2gch.github.ioΛ͝ཡ͍ͩ͘͞
  2. AGENDA AGENDA ▸ Introduction to ISLisp and Iris ▸ History

    of the ISLisp ▸ The Features of the ISLisp ▸ The Features of the Iris ▸ The internals of Iris ▸ ILOS - ISLisp Object System ▸ Condition Systems of ISLisp ▸ Iris in the feature
  3. INTRODUCTION TO ISLISP AND IRIS HISTORY OF ISLISP ▸ CommonLispʹ͍ͭͯޮ཰ͷྑ͞ͱֶशٴͼར༻ͷ༰қ͞

    Λ໨ඪʹ֤ํݴΛࢀߟʹͯ͠։ൃ͞Εͨɻ ▸ ೔ຊͷKernel Languageͱ͍͏Ҋ͕౔୆ʹͳ͍ͬͯΔɻ ▸ ISO/IEC 13816:1997 - Programming Language ISLISP ▸ ISO/IEC 13816:2007 - Programming Language ISLISP ▸ OKI ISLisp Version 0.80 (1999/02/25)
  4. INTRODUCTION TO ISLISP AND IRIS THE FEATURES OF ISLISP ▸

    ILOS - ISLisp Object System͕͋Δ ▸ શͯͷܕ͕ILOSͷࢧ഑Լʹ͋Δ ▸ ੩తൣғΛجຊͱͭͭ͠΋ಈతൣғ͕ѻ͑Δ ▸ ίϯύΫτͳݴޠ࢓༷ (134ϖʔδఔ౓ʣ ▸ ύοέʔδ؅ཧγεςϜ͕ະࡦఆ ▸ PUBLIC DOMAINͷن֨ॻͷ૲Ҋ͕ެ։͞Ε͍ͯΔ ▸ ެࣜαΠτʹن֨४ڌ֬ೝ༻ͷݕূγεςϜ͕͋Δ
  5. INTRODUCTION TO ISLISP AND IRIS DYNAMIC (defun http.request ()
 (format

    
 (standard-output)
 “curl —X ~A ~A”
 (dynamic url)
 (dynamic method))) (dynamic-let
 ((url “islisp.js.org”)
 (method “GET”))
 (http.request)) ▸ΩʔϫʔυҾ਺͕ͳͯ͘΋ɺ
 ہॴಈతม਺Λ࢖ͬͯΩʔϫʔυҾ਺
 ͷΑ͏ͳ͜ͱ͕Ͱ͖Δ ▸CommonLispͱҧͬͯɺେҬม਺ͷΈ
 ಈతม਺ͱ͍͏͜ͱͰ͸ͳ͘ɺͲͪΒ΋
 ྆ํͷൣғΛαϙʔτ͍ͯ͠Δ
  6. INTRODUCTION TO ISLISP AND IRIS IRIS ▸ 2017೥݄̓຤ΑΓ։ൃΛ։࢝ ▸ ISLispΠϯλϓϦλͷGoݴޠ࣮૷(8000ߦҐ)

    ▸ ެࣜαΠτ͸ islisp.js.org ▸ GopherjsʹΑΔJavaScript൛͕͋Δ ▸ ֦ுੑͷߴ͕͞ར఺ͱͳΔͷ༧ఆ ▸ ΫϩεϓϥοτϑΥʔϜ
  7. INTRODUCTION TO ISLISP AND IRIS IRIS PLATFORMS ▸ Darwin ▸

    Linux ▸ FreeBSD ▸ OpenBSD ▸ Windows ▸ NetBSD ▸ Plan9 ▸ Solaris ▸ Android ▸ Dragonfly Goͷαϙʔτ͢ΔϓϥοτϑΥʔϜ Gopherjsͷαϙʔτ͢ΔϓϥοτϑΥʔϜ ▸ Web Browsers ▸ Node.js
  8. THE INTERNALS OF IRIS OVERVIEW IRIS RUNTIME GOPHERJS GO CGO

    GO PLUGIN WEB BROWSERS NATIVE REPL NODE.JS WEB REPL JVM RHINO OS
  9. THE INTERNALS OF IRIS ▸ GoݴޠͷϓϦϛςΟϒܕ(func, int, float, rune) ▸

    ILOSࣗ਎͕༻ҙ͢ΔσʔλΛද͢ߏ଄ମͷܕ ▸ ILOSࣗ਎͕༻ҙ͢ΔΫϥεΛද͢ߏ଄ମͷܕ ▸ interfaceʹΑͬͯந৅Խͯ͠ಉ༷ʹѻ͏ɻ ILOS - ISLISP OBJECT SYSTEM
  10. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM type

    Class interface { Supers() []Class Slots() []Instance Initform(Instance) (Instance, bool) Initarg(Instance) (Instance, bool) Class() Class String() string } type Instance interface { Class() Class String() string } METACLASS 1 METACLASS 2 CLASS 1 CLASS 2 CLASS 3 INSTANCE 1 INSTANCE 3 INSTANCE 4 INSTANCE 2
  11. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Meta

    Class type Instance struct { class ilos.Class supers []ilos.Instance slots map[ilos.Instance]ilos.Instance }
  12. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Meta

    Class type Instance struct { class ilos.Class supers []ilos.Instance slots map[ilos.Instance]ilos.Instance }
  13. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Array

    type GeneralArrayStar struct { Vector []GeneralArrayStar Scalar ilos.Instance }
  14. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Array

    type GeneralVector []ilos.Instance
  15. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Function

    type Function struct { name ilos.Instance function interface{} }
  16. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Function

    type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method }
  17. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM List

    and Symbol type Cons struct { Car ilos.Instance Cdr ilos.Instance }
  18. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Condition

    type Instance struct { class ilos.Class supers []ilos.Instance slots map[ilos.Instance]ilos.Instance }
  19. THE INTERNALS OF IRIS ILOS - ISLISP OBJECT SYSTEM Stream

    type Stream struct { Reader io.Reader Writer io.Writer }
  20. THE INTERNALS OF IRIS FUNCTION INSTANCE OF FUNCTION NATIVE FUNCTION

    .APPLY ENVIRONMENT INSTANCE INSTANCE INSTANCE INSTANCE Arguments - Reflection Return Escaping (defun foo (a) a)
  21. THE INTERNALS OF IRIS FUNCTION func Car(e env.Environment, cons ilos.Instance)

    (ilos.Instance, ilos.Instance) { if err := ensure(class.Cons, cons); err != nil { return nil, err } return cons.(*instance.Cons).Car, nil }
  22. THE INTERNALS OF IRIS FUNCTION func Car(e env.Environment, cons ilos.Instance)

    (ilos.Instance, ilos.Instance) { if err := ensure(class.Cons, cons); err != nil { return nil, err } return cons.(*instance.Cons).Car, nil } type Environment struct { // Lexical BlockTag stack TagbodyTag stack Function stack Variable stack // Global Class stack Macro stack Special stack Property map2 GensymID int Constant stack // Dynamic CatchTag stack DynamicVariable stack StandardInput ilos.Instance StandardOutput ilos.Instance ErrorOutput ilos.Instance Handler ilos.Instance }
  23. THE INTERNALS OF IRIS FUNCTION func Car(e env.Environment, cons ilos.Instance)

    (ilos.Instance, ilos.Instance) { if err := ensure(class.Cons, cons); err != nil { return nil, err } return cons.(*instance.Cons).Car, nil }
  24. THE INTERNALS OF IRIS FUNCTION func Car(e env.Environment, cons ilos.Instance)

    (ilos.Instance, ilos.Instance) { if err := ensure(class.Cons, cons); err != nil { return nil, err } return cons.(*instance.Cons).Car, nil } type Instance interface { Class() Class String() string }
  25. THE INTERNALS OF IRIS FUNCTION func Car(e env.Environment, cons ilos.Instance)

    (ilos.Instance, ilos.Instance) { if err := ensure(class.Cons, cons); err != nil { return nil, err } return cons.(*instance.Cons).Car, nil }
  26. THE INTERNALS OF IRIS GENERIC FUNCTION INSTANCE OF GENERIC FUNCTION

    NATIVE FUNCTION .APPLY ENVIRONMENT INSTANCE INSTANCE INSTANCE INSTANCE Arguments Return Escaping NATIVE FUNCTION NATIVE FUNCTION - Selection
 - Ordering
 - Reflection
 - Combination (defgeneric foo (foo bar))
 (defmethod foo :around ((foo A) (bar B)) nil)
  27. THE INTERNALS OF IRIS GENERIC FUNCTION INSTANCE OF GENERIC FUNCTION

    NATIVE FUNCTION .APPLY ENVIRONMENT INSTANCE INSTANCE INSTANCE INSTANCE Arguments Return Escaping NATIVE FUNCTION NATIVE FUNCTION - Selection
 - Ordering
 - Reflection
 - Combination (defgeneric foo (foo bar))
 (defmethod foo :around ((foo A) (bar B)) nil)
  28. THE INTERNALS OF IRIS GENERIC FUNCTION 1. Simple Combination (nil)

    ▸ ༏ઌॱҐΛܾఆ ▸ ࠷༏ઌؔ਺ͷ໭Γ஋͕แׅؔ਺ͷ໭Γ஋ͱͳΔ ▸ CALL-NEXT-METHODͰ࣍఺༏ઌؔ਺Λݺͼग़ͤΔ 2. Standard Combination (نఆ) ▸ ༏ઌॱҐΛܾఆ ▸ ࠷༏ઌؔ਺ͷ໭Γ஋͕แׅؔ਺ͷ໭Γ஋ͱͳΔ ▸ :around → (:before) → ࢦఆແ͠ →(:after)ɹͷॱͰ࣮ߦ ▸ :aroundͱࢦఆແ͠ͷؔ਺͸CALL-NEXT-METHODͰ࣍఺༏ઌؔ਺Λݺͼग़ͤΔ
  29. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  30. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  31. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) ௨ৗͷؔ਺ͷؔ਺ݺͼग़͠ͷ࣮૷ ϦϑϨΫγϣϯΛ࢖ͬͯωΠςΟϒؔ਺Λ ݺͼग़͢ɻ
  32. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) ௨ৗͷؔ਺ͷؔ਺ݺͼग़͠ͷ࣮૷ ϦϑϨΫγϣϯΛ࢖ͬͯωΠςΟϒؔ਺Λ ݺͼग़͢ɻ
  33. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  34. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  35. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  36. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) แׅؔ਺ͷؔ਺ݺͼग़͠ͷ࣮૷ ຖճCALL-NEXT-METHODΛہॴؔ਺ͱͯ͠
 ༏ઌॱҐΛݩʹ௨ৗؔ਺ͱͯؔ͠਺Λଋറ͠ ͍ͯΔɻ࣮૷ʹΑΔ͕ɺҰൠʹಈతʹݺͼग़ ؔ͢਺Λܾఆ͍ͯ͠ΔͷͰίϯύΠϧʹΑΔ Ըܙ΋ड͚ͮΒ͍ɻ
  37. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) แׅؔ਺ͷؔ਺ݺͼग़͠ͷ࣮૷ ຖճCALL-NEXT-METHODΛہॴؔ਺ͱͯ͠
 ༏ઌॱҐΛݩʹ௨ৗؔ਺ͱͯؔ͠਺Λଋറ͠ ͍ͯΔɻ࣮૷ʹΑΔ͕ɺҰൠʹಈతʹݺͼग़ ؔ͢਺Λܾఆ͍ͯ͠ΔͷͰίϯύΠϧʹΑΔ Ըܙ΋ड͚ͮΒ͍ɻ
  38. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) แׅؔ਺ͷؔ਺ݺͼग़͠ͷ࣮૷ ຖճCALL-NEXT-METHODΛہॴؔ਺ͱͯ͠
 ༏ઌॱҐΛݩʹ௨ৗؔ਺ͱͯؔ͠਺Λଋറ͠ ͍ͯΔɻ࣮૷ʹΑΔ͕ɺҰൠʹಈతʹݺͼग़ ؔ͢਺Λܾఆ͍ͯ͠ΔͷͰίϯύΠϧʹΑΔ Ըܙ΋ड͚ͮΒ͍ɻ
  39. THE INTERNALS OF IRIS GENERIC FUNCTION e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) }

    return methods[0].function.Apply(e, arguments ...) //Call first of method } // if f.methodCombination == NewSymbol("STANDARD") { test := func(i int) bool { return methods[i].qualifier == around } width := len(methods) if index := sort.Search(width, test); index < width { // if has :around methods // This callNextMethod is called in :around methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth for index, method := range methods[:int(depth.(Integer))+1] { if method.qualifier == around { // If have :around method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil || methods[index+i+1].qualifier == around } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) // Call next method } } // If has no :around method then, // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } // Do the first of primary methods // this callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) // Get index of next mehotd e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) // Call next method } // callNextMethod ends here index := 0 // index of the first primary method { // index != 0 is always true because this function has :around methods width := len(methods) - index - 1 test := func(i int) bool { return methods[index+i+1].qualifier == nil } index = sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) } // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } // Do primary methods ret, err := methods[index].function.Apply(e, arguments ...) if err != nil { return nil, err } // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[index].function.Apply(e, arguments ...) } } { // Function has no :around methods // This callNextMethod is called in primary methods var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) // Convert depth to integer { test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 index = sort.Search(width, test) } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set Current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functionss test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } return methods[int(depth.(Integer))].function.Apply(e, arguments ...) } // callNextMethod ends here // Do All :before mehtods for _, method := range methods { if method.qualifier == before { if _, err := method.function.Apply(e, arguments ...); err != nil { return nil, err } } } index := 0 // index of the first primary method { test := func(i int) bool { return methods[i].qualifier == nil } width := len(methods) index := sort.Search(width, test) e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) if index == len(methods) { return nil, NewUndefinedFunction(f.funcSpec) } } e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil) { // If Generic Function has next method, set these functions test := func(i int) bool { return methods[index+i+1].qualifier == nil } width := len(methods) - index - 1 if sort.Search(width, test) < width { e.Function.Define(NewSymbol("NEXT-METHOD-P"), nextMethodPisT) e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) } } ret, err := methods[index].function.Apply(e, arguments ...) // Do all :after methods for i := len(methods) - 1; i >= 0; i -- { if methods[i].qualifier == after { if _, err := methods[i].function.Apply(e, arguments ...); err != nil { return nil, err } } } return ret, err // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http: //mozilla.org/MPL/2.0/. package instance import ( "fmt" "reflect" "sort" "github.com/ta2gch/iris/runtime/env" "github.com/ta2gch/iris/runtime/ilos" ) type Applicable interface { Apply(env.Environment, ...ilos.Instance) (ilos.Instance, ilos.Instance) } type Function struct { name ilos.Instance function interface{} } func NewFunction(name ilos.Instance, function interface{}) ilos.Instance { return Function{name, function} } func (Function) Class() ilos.Class { return FunctionClass } func (f Function) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f Function) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { fv := reflect.ValueOf(f.function) ft := reflect.TypeOf(f.function) argv := []reflect.Value{reflect.ValueOf(e)} for _, cadr := range arguments { argv = append(argv, reflect.ValueOf(cadr)) } if ft.NumIn() != len(argv) && (!ft.IsVariadic() || ft.NumIn()-2 >= len(argv)) { return nil, NewArityError() } rets := fv.Call(argv) a, _ := rets[0].Interface().(ilos.Instance) b, _ := rets[1].Interface().(ilos.Instance) return a, b } type method struct { qualifier ilos.Instance classList []ilos.Class function Function } type GenericFunction struct { funcSpec ilos.Instance lambdaList ilos.Instance methodCombination ilos.Instance genericFunctionClass ilos.Class methods []method } func NewGenericFunction(funcSpec, lambdaList, methodCombination ilos.Instance, genericFunctionClass ilos.Class) ilos.Instance { return &GenericFunction{funcSpec, lambdaList, methodCombination, genericFunctionClass, []method{}} } func (f *GenericFunction) AddMethod(qualifier, lambdaList ilos.Instance, classList []ilos.Class, function ilos.Instance) bool { if f.lambdaList.(List).Length() != lambdaList.(List).Length() { return false } for i, param := range f.lambdaList.(List).Slice() { if param == NewSymbol(":REST") || param == NewSymbol("&REST") { if lambdaList.(List).Nth(i) != NewSymbol(":REST") && lambdaList.(List).Nth(i) != NewSymbol("&REST") { return false } } } for i := range f.methods { if f.methods[i].qualifier == qualifier && reflect.DeepEqual(f.methods[i].classList, classList) { f.methods[i].function = function.(Function) return true } } f.methods = append(f.methods, method{qualifier, classList, function.(Function)}) return true } func (f *GenericFunction) Class() ilos.Class { return f.genericFunctionClass } func (f *GenericFunction) String() string { return fmt.Sprintf("#%v", f.Class()) } func (f *GenericFunction) Apply(e env.Environment, arguments ...ilos.Instance) (ilos.Instance, ilos.Instance) { parameters := f.lambdaList.(List).Slice() variadic := false { test := func(i int) bool { return parameters[i] == NewSymbol(":REST") || parameters[i] == NewSymbol("&REST") } if sort.Search(len(parameters), test) < len(parameters) { variadic = true } } if (variadic && len(parameters)-2 > len(arguments)) || (!variadic && len(parameters) != len(arguments)) { return nil, NewArityError() } methods := []method{} for _, method := range f.methods { matched := true for i, c := range method.classList { if !ilos.InstanceOf(c, arguments[i]) { matched = false break } } if matched { methods = append(methods, method) } } before := NewSymbol(":BEFORE") around := NewSymbol(":AROUND") after := NewSymbol(":AFTER") sort.Slice(methods, func(a, b int) bool { for i := range methods[a].classList { if ilos.SubclassOf(methods[a].classList[i], methods[b].classList[i]) { return false } if ilos.SubclassOf(methods[b].classList[i], methods[a].classList[i]) { return true } } t := map[ilos.Instance]int{around: 4, before: 3, nil: 2, after: 1} return t[methods[a].qualifier] > t[methods[b].qualifier] }) nextMethodPisNil := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return Nil, nil }) nextMethodPisT := NewFunction(NewSymbol("NEXT-METHOD-P"), func(e env.Environment) (ilos.Instance, ilos.Instance) { return T, nil }) if f.methodCombination == NewSymbol("NIL") { var callNextMethod func(e env.Environment) (ilos.Instance, ilos.Instance) // To Recursive callNextMethod = func(e env.Environment) (ilos.Instance, ilos.Instance) { // CALL-NEXT-METHOD depth, _ := e.DynamicVariable.Get(NewSymbol("IRIS/DEPTH")) // Get previous depth index := int(depth.(Integer)) + 1 // Get index of next method e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(index)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if int(depth.(Integer))+1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("CALL-NEXT-METHOD"), NewFunction(NewSymbol("CALL-NEXT-METHOD"), callNextMethod)) e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT)) } return methods[index].function.Apply(e, arguments ...) // Call next method } e.DynamicVariable.Define(NewSymbol("IRIS/DEPTH"), NewInteger(0)) // Set current depth // If Generic Function has no next-mehtods, NEXT-METHOD-P e function returns nil e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisNil)) if 1 < len(methods) { // If Generic Function has next method, set these functionss e.Function.Define(NewSymbol("NEXT-METHOD-P"), NewFunction(NewSymbol("NEXT-METHOD-P"), nextMethodPisT))
  40. THE INTERNALS OF IRIS NON-LOCAL EXITS 1. block / return-from

    2. tagbody / go 3. catch / throw 4. with-handler / singnal-condition LEXICAL LEXICAL DYNAMIC DYNAMIC
  41. THE INTERNALS OF IRIS BLOCK / RETURN-FROM (block sum-block (for

    ((x '(1 a 2 3) (cdr x)) (sum 0 (+ sum (car x)))) ((null x) sum) (cond ((not (numberp (car x))) (return-from sum-block 0)))))
  42. THE INTERNALS OF IRIS TAGBODY / GO (defmacro with-retry (:rest

    forms) (let ((tag (gensym))) `(block ,tag (tagbody ,tag (return-from ,tag (flet ((retry () (go ,tag))) ,@forms)))))) (let ((i -5)) (with-retry ;; if-error is a hypothetical error correction function ;; not supplied by ISLISP. (if-error (sqrt (setq i (+ i 4))) (retry))))
  43. THE INTERNALS OF IRIS CATCH / THROW (defun foo (x)

    (catch 'block-sum (bar x))) (defun bar (x) (for ((l x (cdr l)) (sum 0 (+ sum (car l)))) ((null l) sum) (cond ((not (numberp (car l))) (throw 'block-sum 0))))) (foo '(1 2 3 4)) (foo '(1 2 a 4))
  44. THE INTERNALS OF IRIS WITH-HANDLER / SIGNAL-CONDITION (defun handler (c)

    ɹ(continue-condition c true)) (with-handler #'handler ɹ(cerror “continue-message“ “error-message”))
  45. THE INTERNALS OF IRIS NON-LOCAL EXITS 1. Goݴޠͷ৔߹setjmpͱlongjmp͕࢖͑ͳ͍ 2. ؀ڥʹcatch͍ͨ͠৔ॴΛελοΫʹϓογϡ͢Δɻ

    3. ୤ग़༻໭Γ஋ΛEvalͳͲ͕ॲཧΛৼΓ෼͚Δɻ PROCEDURE A PROCEDURE B1 PROCEDURE B1 PROCEDURE B1 PROCEDURE C FAIL SUCCESS MATCH ENVIRONMENT A ENVIRONMENT B SUCCESS
  46. IRIS IN THE FEATURE PLUGIN & FFI SUPPORT ▸ Goݴޠʹ͸ಈతʹϥΠϒϥϦΛಡΈࠐΉγεςϜ΋͋Δ

    ▸ PluginΛ࢖ͬͯศརͳϥΠϒϥϦͷϥούʔΛॻ͘ ▸ Goͷඪ४ϥΠϒϥϦʹ͸ศརͳ΋ͷ͕ͨ͘͞Μ͋Δɻ ▸ ը૾ॲཧ ▸ ҉߸Խ ▸ ଟഒ௕੔਺ ▸ HTTP & SMTPͳͲͷωοτϫʔΫϓϩτίϧͷ࣮૷ ▸ ɹCGOΛ࢖͑͹CݴޠͷϥΠϒϥϦ΋ಡΈࠐΊΔɻ ▸ جຊతʹωΠςΟϒؔ਺ΛILOSͷؔ਺ΠϯελϯεͰแΉ͚ͩͳͷͰ؆୯ʹϥοϐϯάͰ͖Δ
  47. IRIS IN THE FEATURE PACKAGE SYSTEM ▸ ͔ͭͯJavaScriptʹ͸ύοέʔδ؅ཧγεςϜ͕ͳ͔ͬͨ ▸ Webpack΍browserifyɺRequireJSͱ͍ͬͨJavaScript͚ͩ

    Ͱ࣮૷͞ΕͨASTϨϕϧͷม׵ͷΈͰύοέʔδ؅ཧΛ࣮ݱ ͍ͯͨ͠ɻ ▸ ISLisp΋ISLispͷΈΛ࢖ͬͯASTϨϕϧͷม׵ͷΈͰύο έʔδ؅ཧΛ࣮ݱ͢Δ