|
| 1 | +package runtime |
| 2 | + |
| 3 | +import ( |
| 4 | + "github.com/glojurelang/glojure/pkg/lang" |
| 5 | +) |
| 6 | + |
| 7 | +type directInt64ReducePipelinePlan struct { |
| 8 | + initial int64 |
| 9 | + rangeStart int64 |
| 10 | + rangeEnd int64 |
| 11 | + rangeStep int64 |
| 12 | + transforms []ReducePipelineTransformKind |
| 13 | + takeLimit int64 |
| 14 | +} |
| 15 | + |
| 16 | +func (env *environment) evalDirectInt64ReducePipeline( |
| 17 | + form interface{}, |
| 18 | + currentNS *lang.Namespace, |
| 19 | +) (result interface{}, ok bool, err error) { |
| 20 | + plan, ok := analyzeDirectInt64ReducePipeline(form, currentNS) |
| 21 | + if !ok { |
| 22 | + return nil, false, nil |
| 23 | + } |
| 24 | + |
| 25 | + defer env.recoverDirectInvoke(form, &result, &err) |
| 26 | + result = ReduceInt64Pipeline( |
| 27 | + plan.initial, |
| 28 | + lang.NewLongRange(plan.rangeStart, plan.rangeEnd, plan.rangeStep), |
| 29 | + plan.transforms, |
| 30 | + plan.takeLimit, |
| 31 | + ) |
| 32 | + return result, true, nil |
| 33 | +} |
| 34 | + |
| 35 | +func analyzeDirectInt64ReducePipeline( |
| 36 | + form interface{}, |
| 37 | + currentNS *lang.Namespace, |
| 38 | +) (*directInt64ReducePipelinePlan, bool) { |
| 39 | + seq, ok := form.(lang.ISeq) |
| 40 | + if !ok || seq == nil { |
| 41 | + return nil, false |
| 42 | + } |
| 43 | + operator, ok := seq.First().(*lang.Symbol) |
| 44 | + if !ok || !isDefaultCoreSymbol(currentNS, operator, "reduce") { |
| 45 | + return nil, false |
| 46 | + } |
| 47 | + args := directArgs(seq.Next()) |
| 48 | + if len(args) != 3 { |
| 49 | + return nil, false |
| 50 | + } |
| 51 | + reducer, ok := args[0].(*lang.Symbol) |
| 52 | + if !ok || !isDefaultCoreSymbol(currentNS, reducer, "+") { |
| 53 | + return nil, false |
| 54 | + } |
| 55 | + initial, ok := args[1].(int64) |
| 56 | + if !ok { |
| 57 | + return nil, false |
| 58 | + } |
| 59 | + |
| 60 | + start, end, step, transforms, takeLimit, ok := |
| 61 | + analyzeDirectPipelineSource(args[2], currentNS) |
| 62 | + if !ok || len(transforms) == 0 { |
| 63 | + return nil, false |
| 64 | + } |
| 65 | + return &directInt64ReducePipelinePlan{ |
| 66 | + initial: initial, |
| 67 | + rangeStart: start, |
| 68 | + rangeEnd: end, |
| 69 | + rangeStep: step, |
| 70 | + transforms: transforms, |
| 71 | + takeLimit: takeLimit, |
| 72 | + }, true |
| 73 | +} |
| 74 | + |
| 75 | +func analyzeDirectPipelineSource( |
| 76 | + form interface{}, |
| 77 | + currentNS *lang.Namespace, |
| 78 | +) ( |
| 79 | + start, end, step int64, |
| 80 | + transforms []ReducePipelineTransformKind, |
| 81 | + takeLimit int64, |
| 82 | + ok bool, |
| 83 | +) { |
| 84 | + operator, args, ok := directCall(form) |
| 85 | + if !ok { |
| 86 | + return 0, 0, 0, nil, -1, false |
| 87 | + } |
| 88 | + if isDefaultCoreSymbol(currentNS, operator, "range") { |
| 89 | + start, end, step, ok = directInt64RangeArgs(args) |
| 90 | + return start, end, step, nil, -1, ok |
| 91 | + } |
| 92 | + if len(args) != 2 { |
| 93 | + return 0, 0, 0, nil, -1, false |
| 94 | + } |
| 95 | + |
| 96 | + if isDefaultCoreSymbol(currentNS, operator, "take") { |
| 97 | + limit, constant := args[0].(int64) |
| 98 | + if !constant || limit < 0 { |
| 99 | + return 0, 0, 0, nil, -1, false |
| 100 | + } |
| 101 | + start, end, step, transforms, innerLimit, ok := |
| 102 | + analyzeDirectPipelineSource(args[1], currentNS) |
| 103 | + if !ok || innerLimit >= 0 { |
| 104 | + return 0, 0, 0, nil, -1, false |
| 105 | + } |
| 106 | + return start, end, step, transforms, limit, true |
| 107 | + } |
| 108 | + |
| 109 | + var operatorName string |
| 110 | + switch { |
| 111 | + case isDefaultCoreSymbol(currentNS, operator, "map"): |
| 112 | + operatorName = "map" |
| 113 | + case isDefaultCoreSymbol(currentNS, operator, "filter"): |
| 114 | + operatorName = "filter" |
| 115 | + default: |
| 116 | + return 0, 0, 0, nil, -1, false |
| 117 | + } |
| 118 | + kind, ok := directPipelineTransform(operatorName, args[0], currentNS) |
| 119 | + if !ok { |
| 120 | + return 0, 0, 0, nil, -1, false |
| 121 | + } |
| 122 | + |
| 123 | + start, end, step, transforms, takeLimit, ok = |
| 124 | + analyzeDirectPipelineSource(args[1], currentNS) |
| 125 | + // An inner take must count values before this outer transform, whereas |
| 126 | + // ReduceInt64Pipeline's limit counts the final transformed output. |
| 127 | + if !ok || takeLimit >= 0 { |
| 128 | + return 0, 0, 0, nil, -1, false |
| 129 | + } |
| 130 | + transforms = append(transforms, kind) |
| 131 | + return start, end, step, transforms, -1, true |
| 132 | +} |
| 133 | + |
| 134 | +func directInt64RangeArgs(args []interface{}) (start, end, step int64, ok bool) { |
| 135 | + switch len(args) { |
| 136 | + case 1: |
| 137 | + end, ok = args[0].(int64) |
| 138 | + return 0, end, 1, ok |
| 139 | + case 2: |
| 140 | + start, ok = args[0].(int64) |
| 141 | + if !ok { |
| 142 | + return 0, 0, 0, false |
| 143 | + } |
| 144 | + end, ok = args[1].(int64) |
| 145 | + return start, end, 1, ok |
| 146 | + case 3: |
| 147 | + start, ok = args[0].(int64) |
| 148 | + if !ok { |
| 149 | + return 0, 0, 0, false |
| 150 | + } |
| 151 | + end, ok = args[1].(int64) |
| 152 | + if !ok { |
| 153 | + return 0, 0, 0, false |
| 154 | + } |
| 155 | + step, ok = args[2].(int64) |
| 156 | + return start, end, step, ok |
| 157 | + default: |
| 158 | + return 0, 0, 0, false |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +func directPipelineTransform( |
| 163 | + operator string, |
| 164 | + callback interface{}, |
| 165 | + currentNS *lang.Namespace, |
| 166 | +) (ReducePipelineTransformKind, bool) { |
| 167 | + if symbol, ok := callback.(*lang.Symbol); ok { |
| 168 | + name := symbol.Name() |
| 169 | + if purePipelineCallbacks[operator][name] && |
| 170 | + isDefaultCoreSymbol(currentNS, symbol, name) { |
| 171 | + return pipelineTransformKind(operator, name), true |
| 172 | + } |
| 173 | + } |
| 174 | + if operator == "map" && isDirectInt64SquareFn(callback, currentNS) { |
| 175 | + return ReducePipelineMapSquare, true |
| 176 | + } |
| 177 | + return 0, false |
| 178 | +} |
| 179 | + |
| 180 | +func isDirectInt64SquareFn( |
| 181 | + form interface{}, |
| 182 | + currentNS *lang.Namespace, |
| 183 | +) bool { |
| 184 | + operator, args, ok := directCall(form) |
| 185 | + if !ok || operator.String() != "fn*" || len(args) != 2 { |
| 186 | + return false |
| 187 | + } |
| 188 | + params, ok := args[0].(lang.IPersistentVector) |
| 189 | + if !ok || params.Count() != 1 { |
| 190 | + return false |
| 191 | + } |
| 192 | + param, ok := params.Nth(0).(*lang.Symbol) |
| 193 | + if !ok { |
| 194 | + return false |
| 195 | + } |
| 196 | + multiply, bodyArgs, ok := directCall(args[1]) |
| 197 | + if !ok || len(bodyArgs) != 2 || |
| 198 | + !isDefaultCoreSymbol(currentNS, multiply, "*") { |
| 199 | + return false |
| 200 | + } |
| 201 | + left, leftOK := bodyArgs[0].(*lang.Symbol) |
| 202 | + right, rightOK := bodyArgs[1].(*lang.Symbol) |
| 203 | + return leftOK && rightOK && |
| 204 | + left.Equals(param) && right.Equals(param) |
| 205 | +} |
| 206 | + |
| 207 | +func directCall(form interface{}) (*lang.Symbol, []interface{}, bool) { |
| 208 | + seq, ok := form.(lang.ISeq) |
| 209 | + if !ok || seq == nil { |
| 210 | + return nil, nil, false |
| 211 | + } |
| 212 | + operator, ok := seq.First().(*lang.Symbol) |
| 213 | + if !ok { |
| 214 | + return nil, nil, false |
| 215 | + } |
| 216 | + return operator, directArgs(seq.Next()), true |
| 217 | +} |
| 218 | + |
| 219 | +func directArgs(seq lang.ISeq) []interface{} { |
| 220 | + args := make([]interface{}, 0, 3) |
| 221 | + for ; seq != nil; seq = seq.Next() { |
| 222 | + args = append(args, seq.First()) |
| 223 | + } |
| 224 | + return args |
| 225 | +} |
| 226 | + |
| 227 | +func isDefaultCoreSymbol( |
| 228 | + currentNS *lang.Namespace, |
| 229 | + symbol *lang.Symbol, |
| 230 | + name string, |
| 231 | +) bool { |
| 232 | + if symbol.Name() != name { |
| 233 | + return false |
| 234 | + } |
| 235 | + vr := directInvokeVar(currentNS, symbol) |
| 236 | + return vr != nil && |
| 237 | + vr.Namespace() == lang.NSCore && |
| 238 | + vr.Symbol().String() == name && |
| 239 | + IsDefaultCoreVar(vr) |
| 240 | +} |
0 commit comments