|
| 1 | +import type { SyntaxNode } from "@lezer/common" |
| 2 | +import { getChildren } from "../../parser/index.js" |
| 3 | +import type { TransformContext, NodeTransformer } from "../types.js" |
| 4 | + |
| 5 | +/** |
| 6 | + * Handler configuration for builtin functions. |
| 7 | + * Simple handlers just need runtime and output pattern. |
| 8 | + */ |
| 9 | +interface SimpleHandler { |
| 10 | + runtime: string |
| 11 | + output: string // Use {args} as placeholder |
| 12 | + isAsync?: boolean |
| 13 | + needsNew?: boolean |
| 14 | +} |
| 15 | + |
| 16 | +/** |
| 17 | + * Simple builtin handlers that follow the pattern: |
| 18 | + * ctx.usesRuntime.add(runtime); return output.replace("{args}", args) |
| 19 | + */ |
| 20 | +const SIMPLE_BUILTINS: Record<string, SimpleHandler> = { |
| 21 | + // Core builtins |
| 22 | + len: { runtime: "len", output: "len({args})" }, |
| 23 | + range: { runtime: "range", output: "range({args})" }, |
| 24 | + int: { runtime: "int", output: "int({args})" }, |
| 25 | + float: { runtime: "float", output: "float({args})" }, |
| 26 | + str: { runtime: "str", output: "str({args})" }, |
| 27 | + bool: { runtime: "bool", output: "bool({args})" }, |
| 28 | + abs: { runtime: "abs", output: "abs({args})" }, |
| 29 | + min: { runtime: "min", output: "min({args})" }, |
| 30 | + max: { runtime: "max", output: "max({args})" }, |
| 31 | + sum: { runtime: "sum", output: "sum({args})" }, |
| 32 | + list: { runtime: "list", output: "list({args})" }, |
| 33 | + dict: { runtime: "dict", output: "dict({args})" }, |
| 34 | + set: { runtime: "set", output: "set({args})" }, |
| 35 | + tuple: { runtime: "tuple", output: "tuple({args})" }, |
| 36 | + enumerate: { runtime: "enumerate", output: "enumerate({args})" }, |
| 37 | + zip: { runtime: "zip", output: "zip({args})" }, |
| 38 | + sorted: { runtime: "sorted", output: "sorted({args})" }, |
| 39 | + reversed: { runtime: "reversed", output: "reversed({args})" }, |
| 40 | + type: { runtime: "type", output: "type({args})" }, |
| 41 | + input: { runtime: "input", output: "input({args})" }, |
| 42 | + ord: { runtime: "ord", output: "ord({args})" }, |
| 43 | + chr: { runtime: "chr", output: "chr({args})" }, |
| 44 | + all: { runtime: "all", output: "all({args})" }, |
| 45 | + any: { runtime: "any", output: "any({args})" }, |
| 46 | + map: { runtime: "map", output: "map({args})" }, |
| 47 | + filter: { runtime: "filter", output: "filter({args})" }, |
| 48 | + repr: { runtime: "repr", output: "repr({args})" }, |
| 49 | + round: { runtime: "round", output: "round({args})" }, |
| 50 | + divmod: { runtime: "divmod", output: "divmod({args})" }, |
| 51 | + hex: { runtime: "hex", output: "hex({args})" }, |
| 52 | + oct: { runtime: "oct", output: "oct({args})" }, |
| 53 | + bin: { runtime: "bin", output: "bin({args})" }, |
| 54 | + getattr: { runtime: "getattr", output: "getattr({args})" }, |
| 55 | + hasattr: { runtime: "hasattr", output: "hasattr({args})" }, |
| 56 | + setattr: { runtime: "setattr", output: "setattr({args})" }, |
| 57 | + |
| 58 | + // itertools |
| 59 | + chain: { runtime: "itertools/chain", output: "chain({args})" }, |
| 60 | + combinations: { runtime: "itertools/combinations", output: "combinations({args})" }, |
| 61 | + permutations: { runtime: "itertools/permutations", output: "permutations({args})" }, |
| 62 | + product: { runtime: "itertools/product", output: "product({args})" }, |
| 63 | + cycle: { runtime: "itertools/cycle", output: "cycle({args})" }, |
| 64 | + repeat: { runtime: "itertools/repeat", output: "repeat({args})" }, |
| 65 | + islice: { runtime: "itertools/islice", output: "islice({args})" }, |
| 66 | + takewhile: { runtime: "itertools/takeWhile", output: "takeWhile({args})" }, |
| 67 | + dropwhile: { runtime: "itertools/dropWhile", output: "dropWhile({args})" }, |
| 68 | + zip_longest: { runtime: "itertools/zipLongest", output: "zipLongest({args})" }, |
| 69 | + compress: { runtime: "itertools/compress", output: "compress({args})" }, |
| 70 | + filterfalse: { runtime: "itertools/filterFalse", output: "filterFalse({args})" }, |
| 71 | + accumulate: { runtime: "itertools/accumulate", output: "accumulate({args})" }, |
| 72 | + groupby: { runtime: "itertools/groupby", output: "groupby({args})" }, |
| 73 | + count: { runtime: "itertools/count", output: "count({args})" }, |
| 74 | + tee: { runtime: "itertools/tee", output: "tee({args})" }, |
| 75 | + pairwise: { runtime: "itertools/pairwise", output: "pairwise({args})" }, |
| 76 | + combinations_with_replacement: { |
| 77 | + runtime: "itertools/combinationsWithReplacement", |
| 78 | + output: "combinationsWithReplacement({args})" |
| 79 | + }, |
| 80 | + |
| 81 | + // collections |
| 82 | + Counter: { runtime: "collections/Counter", output: "new Counter({args})", needsNew: true }, |
| 83 | + defaultdict: { runtime: "collections/defaultdict", output: "defaultdict({args})" }, |
| 84 | + deque: { runtime: "collections/deque", output: "new deque({args})", needsNew: true }, |
| 85 | + |
| 86 | + // functools |
| 87 | + partial: { runtime: "functools/partial", output: "partial({args})" }, |
| 88 | + reduce: { runtime: "functools/reduce", output: "reduce({args})" }, |
| 89 | + lru_cache: { runtime: "functools/lruCache", output: "lruCache({args})" }, |
| 90 | + cache: { runtime: "functools/cache", output: "cache({args})" }, |
| 91 | + wraps: { runtime: "functools/wraps", output: "wraps({args})" }, |
| 92 | + cmp_to_key: { runtime: "functools/cmpToKey", output: "cmpToKey({args})" }, |
| 93 | + total_ordering: { runtime: "functools/totalOrdering", output: "totalOrdering({args})" }, |
| 94 | + |
| 95 | + // json |
| 96 | + dumps: { runtime: "json/dumps", output: "dumps({args})" }, |
| 97 | + loads: { runtime: "json/loads", output: "loads({args})" }, |
| 98 | + dump: { runtime: "json/dump", output: "dump({args})" }, |
| 99 | + load: { runtime: "json/load", output: "load({args})" }, |
| 100 | + |
| 101 | + // datetime |
| 102 | + datetime: { runtime: "datetime/datetime", output: "new datetime({args})", needsNew: true }, |
| 103 | + date: { runtime: "datetime/date", output: "new date({args})", needsNew: true }, |
| 104 | + time: { runtime: "datetime/time", output: "new time({args})", needsNew: true }, |
| 105 | + timedelta: { runtime: "datetime/timedelta", output: "new timedelta({args})", needsNew: true }, |
| 106 | + |
| 107 | + // string module |
| 108 | + Template: { runtime: "string/Template", output: "new Template({args})", needsNew: true }, |
| 109 | + capwords: { runtime: "string/capWords", output: "capWords({args})" }, |
| 110 | + |
| 111 | + // glob module - async |
| 112 | + glob: { runtime: "glob/glob", output: "await glob({args})", isAsync: true }, |
| 113 | + iglob: { runtime: "glob/iglob", output: "await iglob({args})", isAsync: true }, |
| 114 | + rglob: { runtime: "glob/rglob", output: "await rglob({args})", isAsync: true }, |
| 115 | + |
| 116 | + // shutil module - async |
| 117 | + copy: { runtime: "shutil/copy", output: "await copy({args})", isAsync: true }, |
| 118 | + copy2: { runtime: "shutil/copy2", output: "await copy2({args})", isAsync: true }, |
| 119 | + copytree: { runtime: "shutil/copytree", output: "await copytree({args})", isAsync: true }, |
| 120 | + move: { runtime: "shutil/move", output: "await move({args})", isAsync: true }, |
| 121 | + rmtree: { runtime: "shutil/rmtree", output: "await rmtree({args})", isAsync: true }, |
| 122 | + which: { runtime: "shutil/which", output: "await which({args})", isAsync: true }, |
| 123 | + |
| 124 | + // tempfile module - async |
| 125 | + mkstemp: { runtime: "tempfile/mkstemp", output: "await mkstemp({args})", isAsync: true }, |
| 126 | + mkdtemp: { runtime: "tempfile/mkdtemp", output: "await mkdtemp({args})", isAsync: true }, |
| 127 | + NamedTemporaryFile: { |
| 128 | + runtime: "tempfile/NamedTemporaryFile", |
| 129 | + output: "await NamedTemporaryFile.create({args})", |
| 130 | + isAsync: true |
| 131 | + }, |
| 132 | + TemporaryDirectory: { |
| 133 | + runtime: "tempfile/TemporaryDirectory", |
| 134 | + output: "await TemporaryDirectory.create({args})", |
| 135 | + isAsync: true |
| 136 | + }, |
| 137 | + |
| 138 | + // pathlib |
| 139 | + Path: { runtime: "pathlib/Path", output: "new Path({args})", needsNew: true } |
| 140 | +} |
| 141 | + |
| 142 | +/** |
| 143 | + * Handle isinstance() call with special tuple-to-array conversion |
| 144 | + */ |
| 145 | +function handleIsinstance( |
| 146 | + args: string, |
| 147 | + argList: SyntaxNode | undefined, |
| 148 | + ctx: TransformContext, |
| 149 | + transformNode: NodeTransformer |
| 150 | +): string { |
| 151 | + ctx.usesRuntime.add("isinstance") |
| 152 | + |
| 153 | + // Special handling: if second arg is a tuple, convert to array for multiple type check |
| 154 | + if (argList) { |
| 155 | + const argChildren = getChildren(argList).filter( |
| 156 | + (c) => c.name !== "(" && c.name !== ")" && c.name !== "," |
| 157 | + ) |
| 158 | + if (argChildren.length >= 2) { |
| 159 | + const firstArg = argChildren[0] |
| 160 | + const secondArg = argChildren[1] |
| 161 | + if (firstArg && secondArg?.name === "TupleExpression") { |
| 162 | + // Convert tuple to array literal |
| 163 | + const tupleChildren = getChildren(secondArg).filter( |
| 164 | + (c) => c.name !== "(" && c.name !== ")" && c.name !== "," |
| 165 | + ) |
| 166 | + const typesCodes = tupleChildren.map((el) => transformNode(el, ctx)) |
| 167 | + return `isinstance(${transformNode(firstArg, ctx)}, [${typesCodes.join(", ")}])` |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + return `isinstance(${args})` |
| 172 | +} |
| 173 | + |
| 174 | +/** |
| 175 | + * Handle builtin function call. |
| 176 | + * Returns the transformed code, or null if not a builtin. |
| 177 | + */ |
| 178 | +export function handleBuiltinCall( |
| 179 | + calleeName: string, |
| 180 | + args: string, |
| 181 | + argList: SyntaxNode | undefined, |
| 182 | + ctx: TransformContext, |
| 183 | + transformNode: NodeTransformer |
| 184 | +): string | null { |
| 185 | + // Special case: print -> console.log (no runtime needed) |
| 186 | + if (calleeName === "print") { |
| 187 | + return `console.log(${args})` |
| 188 | + } |
| 189 | + |
| 190 | + // Special case: isinstance needs complex handling |
| 191 | + if (calleeName === "isinstance") { |
| 192 | + return handleIsinstance(args, argList, ctx, transformNode) |
| 193 | + } |
| 194 | + |
| 195 | + // Check simple builtins registry |
| 196 | + const handler = SIMPLE_BUILTINS[calleeName] |
| 197 | + if (handler) { |
| 198 | + ctx.usesRuntime.add(handler.runtime) |
| 199 | + return handler.output.replace("{args}", args) |
| 200 | + } |
| 201 | + |
| 202 | + return null |
| 203 | +} |
0 commit comments