Skip to content

Commit 3bbfffa

Browse files
committed
More work on importing TS apis
1 parent 5d9b347 commit 3bbfffa

2 files changed

Lines changed: 120 additions & 16 deletions

File tree

pxtpy/ast.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace pxt.py {
55
export interface TypeOptions {
66
union?: Type;
77
classType?: py.ClassDef;
8+
moduleType?: py.Module | py.ClassDef;
89
primType?: string;
910
arrayType?: Type;
1011
}

pxtpy/converter.ts

Lines changed: 119 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
namespace pxt.py {
22
import B = pxt.blocks;
3+
import SK = pxtc.SymbolKind
34

45
interface Ctx {
56
currModule: py.Module;
@@ -15,6 +16,7 @@ namespace pxt.py {
1516
let typeId = 0
1617
let numUnifies = 0
1718
let currErrs = ""
19+
let autoImport = true
1820

1921
function stmtTODO(v: py.Stmt) {
2022
return B.mkStmt(B.mkText("TODO: " + v.kind))
@@ -32,22 +34,83 @@ namespace pxt.py {
3234
return B.mkStmt(B.mkText("/** " + cmt + " */"))
3335
}
3436

37+
function defName(n: string, tp: Type): Name {
38+
return {
39+
kind: "Name",
40+
id: n,
41+
isdef: true,
42+
ctx: "Store",
43+
tsType: tp
44+
} as any
45+
}
46+
47+
function mapTsType(tp: string) {
48+
let ai = apisInfo.byQName[tp]
49+
if (ai && ai.kind == SK.Enum)
50+
return tpNumber
51+
let sym = lookupSymbol(tp)
52+
if (sym && sym.kind == "ClassDef")
53+
return mkType({ classType: sym as ClassDef })
54+
return mkType({ primType: tp })
55+
}
56+
57+
function mapTsArg(a: pxtc.ParameterDesc): Arg {
58+
return {
59+
kind: "Arg",
60+
arg: a.name,
61+
type: mapTsType(a.type),
62+
} as any
63+
}
64+
3565
function lookupSymbol(name: string): py.Symbol {
3666
if (!name) return null
37-
if (moduleAst[name])
38-
return moduleAst[name]
67+
68+
const mod = moduleAst[name]
69+
if (mod) {
70+
if (!mod.body) {
71+
mod.body = []
72+
for (let sym of mod.tsBody) {
73+
if (isModule(sym)) {
74+
if (moduleAst[sym.qName])
75+
mod.body.push(lookupSymbol(sym.qName))
76+
} else if (sym.kind == SK.Variable || sym.kind == SK.EnumMember) {
77+
mod.body.push({
78+
kind: "Assign",
79+
targets: [defName(sym.name, mapTsType(sym.retType))],
80+
value: null
81+
} as any)
82+
} else if (sym.kind == SK.Function || sym.kind == SK.Method) {
83+
let fd: FunctionDef = {
84+
kind: "FunctionDef",
85+
name: sym.name,
86+
parent: mod,
87+
args: {
88+
kind: "Arguments",
89+
args: [],
90+
defaults: [],
91+
kwonlyargs: [],
92+
kw_defaults: []
93+
},
94+
body: [],
95+
decorator_list: [],
96+
retType: mapTsType(sym.retType)
97+
} as any
98+
mod.body.push(fd)
99+
fd.args.args = sym.parameters.map(mapTsArg)
100+
} else {
101+
// TODO
102+
}
103+
}
104+
}
105+
return mod
106+
}
107+
39108
let parts = name.split(".")
40109
if (parts.length >= 2) {
41110
let last = parts.length - 1
42111
let par = moduleAst[parts.slice(0, last).join(".")]
43112
let ename = parts[last]
44113
if (par) {
45-
if (!par.body) {
46-
par.body = []
47-
for (let sym of par.tsBody) {
48-
// do something
49-
}
50-
}
51114
for (let stmt of par.body) {
52115
if (stmt.kind == "ClassDef" || stmt.kind == "FunctionDef") {
53116
if ((stmt as py.FunctionDef).name == ename)
@@ -65,9 +128,19 @@ namespace pxt.py {
65128
return null
66129
}
67130

131+
function isModule(sym: pxtc.SymbolInfo) {
132+
switch (sym.kind) {
133+
case SK.Module:
134+
case SK.Enum:
135+
return true
136+
default:
137+
return false
138+
}
139+
}
140+
68141
function reflectModules() {
69142
for (let sym of U.values(apisInfo.byQName)) {
70-
if (sym.kind == pxtc.SymbolKind.Module) {
143+
if (isModule(sym)) {
71144
let name = sym.qName
72145
moduleAst[name] = {
73146
kind: "Module",
@@ -80,8 +153,6 @@ namespace pxt.py {
80153
}
81154
}
82155
for (let sym of U.values(apisInfo.byQName)) {
83-
if (sym.kind == pxtc.SymbolKind.Module)
84-
continue
85156
let m = /(.*)\.(.*)/.exec(sym.qName)
86157
if (!m)
87158
continue
@@ -106,8 +177,18 @@ namespace pxt.py {
106177
return ctx.currModule.name == "main" && !ctx.currFun && !ctx.currClass
107178
}
108179

109-
function defvar(n: string, opts: py.VarDescOptions) {
110-
let scopeDef = currentScope()
180+
function addImport(a: AST, name: string, scope?: ScopeDef) {
181+
const v = defvar(name, { isPlainImport: true }, scope)
182+
const sym = lookupSymbol(name)
183+
if (!sym)
184+
error(a, U.lf("No module named '{0}'", name))
185+
else
186+
unify(a, v.type, mkType({ moduleType: sym as Module }))
187+
return v
188+
}
189+
190+
function defvar(n: string, opts: py.VarDescOptions, scope?: ScopeDef) {
191+
let scopeDef = scope || currentScope()
111192
let v = scopeDef.vars[n]
112193
if (!v) {
113194
v = scopeDef.vars[n] = { type: mkType(), name: n }
@@ -165,6 +246,8 @@ namespace pxt.py {
165246
return t.primType
166247
else if (t.classType)
167248
return applyTypeMap(getFullName(t.classType))
249+
else if (t.moduleType)
250+
return applyTypeMap(t.moduleType.name)
168251
else if (t.arrayType)
169252
return t2s(t.arrayType) + "[]"
170253
else
@@ -184,6 +267,7 @@ namespace pxt.py {
184267
function typeCtor(t: Type): any {
185268
if (t.primType) return t.primType
186269
else if (t.classType) return t.classType
270+
else if (t.moduleType) return t.moduleType
187271
else if (t.arrayType) return "array"
188272
return null
189273
}
@@ -281,6 +365,9 @@ namespace pxt.py {
281365
s = s.parent
282366
} while (s && s.kind == "ClassDef")
283367
}
368+
if (autoImport && lookupSymbol(n)) {
369+
return addImport(currentScope(), n, ctx.currModule)
370+
}
284371
return null
285372
}
286373

@@ -321,6 +408,15 @@ namespace pxt.py {
321408
}
322409
}
323410

411+
function symbolType(s: Symbol): Type {
412+
if (s.kind == "Module" || s.kind == "ClassDef")
413+
return mkType({ moduleType: s as Module })
414+
else if (s.kind == "Assign")
415+
return typeOf((s as Assign).targets[0])
416+
else
417+
return mkType({})
418+
}
419+
324420
function scope(f: () => B.JsNode) {
325421
const prevCtx = U.flatClone(ctx)
326422
let r: B.JsNode;
@@ -830,9 +926,7 @@ namespace pxt.py {
830926
defvar(nm.asname, {
831927
expandsTo: nm.name
832928
})
833-
defvar(nm.name, {
834-
isPlainImport: true
835-
})
929+
addImport(n, nm.name)
836930
}
837931
return B.mkText("")
838932
},
@@ -1291,6 +1385,15 @@ namespace pxt.py {
12911385
let part = typeOf(n.value)
12921386
let fd = getTypeField(part, n.attr)
12931387
if (fd) unify(n, n.tsType, fd.type)
1388+
else if (part.moduleType) {
1389+
let sym = lookupSymbol(part.moduleType.name + "." + n.attr)
1390+
if (sym)
1391+
unifyTypeOf(n, symbolType(sym))
1392+
else
1393+
error(n, U.lf("module '{0}' has no attribute '{1}'", part.moduleType.name, n.attr))
1394+
} else {
1395+
error(n, U.lf("unknown object type; cannot lookup attribute '{0}'", n.attr))
1396+
}
12941397
return B.mkInfix(expr(n.value), ".", B.mkText(quoteStr(n.attr)))
12951398
},
12961399
Subscript: (n: py.Subscript) => {

0 commit comments

Comments
 (0)