Skip to content

Commit 3f24df4

Browse files
committed
Add async/await API at require, do_str, init_python and add experimental asynchronous logic on python
1 parent 5c3ef3e commit 3f24df4

File tree

9 files changed

+414
-36
lines changed

9 files changed

+414
-36
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
package-lock.json

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ called before attempting to interact with MicroPython.
4848
do_str(code)
4949
```
5050

51-
Execute the input code. `code` must be a `string`.
51+
Execute the input code. `code` must be a `string`. Returns a promise resulting an stdout.
5252

5353
```
5454
init_repl()

index.js

+108-20
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,116 @@
1-
var mp = require('./lib/micropython.js');
2-
var browser = true;
1+
global.mpjscache = {};
2+
let pyjs;
3+
let browser = true;
4+
let stdout_text = '';
5+
let stdout_ready = false;
6+
let stdout_print = (stdout) => {
7+
if (browser) stdout = stdout.data;
8+
if (!browser) stdout = stdout.toString();
9+
stdout_text += stdout;
10+
if (stdout_text.indexOf('mpjsendline') > -1) {
11+
stdout_text = stdout_text.replace('mpjsendline', '');
12+
stdout_ready = true;
13+
console.log(stdout_text);
14+
}
15+
}
16+
global.mpjsPrintStdout = stdout_print;
17+
18+
const mp = require('./lib/micropython.js');
319
if (typeof window === 'undefined' && typeof importScripts === 'undefined') {
4-
mp.onRuntimeInitialized();
520
browser = false;
21+
pyjs = require('fs').readFileSync('./js.py');
22+
} else {
23+
pyjs = require('!raw-loader!./js.py');
24+
}
25+
26+
function wait_exist(fn) {
27+
return new Promise((resolve, reject) => {
28+
const clear = (id) => {
29+
clearInterval(id);
30+
resolve();
31+
}
32+
const interval = setInterval(() => {if (fn()) clear(interval)}, 0);
33+
});
634
}
7-
module.exports.init = global.mp_js_init;
8-
module.exports.do_str = global.mp_js_do_str;
9-
module.exports.init_repl = global.mp_js_init_repl;
10-
module.exports.process_char = global.mp_js_process_char;
35+
1136
if (browser) {
12-
var stdout = document.createElement('div');
37+
const stdout = document.createElement('div');
1338
stdout.id = 'mp_js_stdout';
1439
stdout.style.display = 'none';
15-
stdout.addEventListener('print', function (event) {
16-
console.log(event.data);
17-
}, false);
40+
stdout.addEventListener('print', stdout_print, false);
1841
document.body.append(stdout);
19-
var interval = setInterval(() => {
20-
if (global.mp_js_init) {
21-
module.exports.init = global.mp_js_init;
22-
module.exports.do_str = global.mp_js_do_str;
23-
module.exports.init_repl = global.mp_js_init_repl;
24-
module.exports.process_char = global.mp_js_process_char;
25-
clearInterval(interval);
26-
}
27-
}, 0);
2842
}
43+
44+
global.AsyncFunction = (async () => {}).constructor;
45+
46+
module.exports = (async () => {
47+
await wait_exist(() => global.mp_js_init);
48+
const methods = {}
49+
methods.init = global.mp_js_init;
50+
const do_str = global.mp_js_do_str;
51+
methods.do_str = async (code, concurrent) => {
52+
const codes = code.split('\n');
53+
let spaces = '';
54+
for (let line of codes) {
55+
if (!line) continue;
56+
let index = 0;
57+
for (let word of line) {
58+
if (index === 0 && word !== ' ') break;
59+
if (word === ' ') spaces += ' ';
60+
index += 1;
61+
}
62+
break;
63+
}
64+
if (spaces || concurrent) {
65+
if (concurrent) spaces = ' ';
66+
let index_split = 0;
67+
const new_code = [];
68+
for (let line of codes) {
69+
line = line.slice(spaces.length - 1);
70+
new_code.push(line);
71+
}
72+
if (concurrent) {
73+
const result = [];
74+
for (var each of new_code) {
75+
let await_code;
76+
if (each.indexOf(' = wait(') > -1) {
77+
const [variable, wait] = each.split(' = ')
78+
const promise = wait.slice(5, -1);
79+
await_code = variable + ' = ' + promise + '._value';
80+
}
81+
result.push(await global.mp_js_do_str(each, false));
82+
if (await_code) await global.mp_js_do_str(await_code, false);
83+
}
84+
return result.join('\n');
85+
}
86+
code = new_code.join('\n');
87+
}
88+
stdout_text = '';
89+
stdout_ready = false;
90+
code += "\nprint('mpjsendline')";
91+
if (global.promiseWaitInterval) await wait_exist(() => !global.promiseWaitInterval);
92+
do_str(code);
93+
await wait_exist(() => stdout_ready);
94+
return stdout_text;
95+
}
96+
methods.init_python = async (stack) => {
97+
methods.init(stack);
98+
return await methods.do_str(pyjs);
99+
}
100+
global.mp_js_do_str = methods.do_str;
101+
methods.init_repl = global.mp_js_init_repl;
102+
methods.process_char = global.mp_js_process_char;
103+
return methods;
104+
})().then((methods) => {
105+
module.exports.init = methods.init;
106+
module.exports.do_str = methods.do_str;
107+
module.exports.init_python = methods.init_python;
108+
module.exports.init_repl = methods.init_repl;
109+
module.exports.process_char = methods.process_char;
110+
return methods;
111+
});
112+
113+
module.exports.init = mp._mp_js_init;
114+
module.exports.do_str = mp._mp_js_do_str;
115+
module.exports.init_repl = mp._mp_js_init_repl;
116+
module.exports.process_char = mp._mp_js_process_char;

js.py

+233
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
import json
2+
import js
3+
dircache = {}
4+
objcache = {}
5+
funcache = {}
6+
procache = {}
7+
8+
def js_exec(code, *args):
9+
code = format(code, *args)
10+
return js.exec('(async () => {\n' + code + '\n})();')
11+
12+
def py_exec(code, *args):
13+
code = format(code, *args)
14+
return js.exec('(async () => {\nreturn await global.mp_js_do_str(`\n' + code + '\n`, true);\n})();')
15+
exec = py_exec
16+
17+
def format(string, *args):
18+
for index, arg in enumerate(args):
19+
string = string.replace('{%s}' % (index), str(arg))
20+
return string
21+
22+
#def print(value):
23+
# js.exec("""console.log(`%s`)""" % (str(value)))
24+
25+
def wait(promise):
26+
procache[promise._name] = promise
27+
#js.exec('console.log("function")')
28+
js.exec("""
29+
//console.log('exec');
30+
global.promiseWaitInterval = setInterval(() => {
31+
//console.log('interval')
32+
const object = global.mpjscache['{0}'];
33+
console.log(object)
34+
if (object && object.constructor !== Promise) {
35+
clearInterval(global.promiseWaitInterval);
36+
global.promiseWaitInterval = undefined;
37+
global.mp_js_do_str(`
38+
import js
39+
procache['{0}']._resolved = True
40+
procache['{0}'] = procache['{0}']._value
41+
`);
42+
}
43+
}, 500);
44+
""".replace('{0}', promise._name))
45+
exit
46+
47+
def resolve(cache, name, value):
48+
object = cache.get(name)
49+
if type(object) == JSPromise:
50+
cache[name] = object.resolve(value)
51+
else:
52+
cache[name] = value
53+
54+
class JSPromise():
55+
56+
def __init__(self, name):
57+
self._name = name
58+
self._resolved = False
59+
self._value = False
60+
61+
def resolve(self, value):
62+
self._resolved = True
63+
self._value = value
64+
return value
65+
66+
class JSObject():
67+
68+
def __init__(self, name):
69+
self._name = name
70+
71+
def __len__(self):
72+
return 1
73+
74+
def __dir__(self):
75+
js_exec("""
76+
let keys = JSON.stringify(Object.keys({0}));
77+
global.mp_js_do_str(`
78+
import js
79+
dircache['{0}'] = ${keys}
80+
`);
81+
""", self._name)
82+
return dircache.get(self._name, [])
83+
84+
def __getattr__(self, key):
85+
name = self._name + '.' + key
86+
js_exec("""
87+
let object = {0};
88+
if (object.constructor === Promise) {
89+
await global.mp_js_do_str(`
90+
import js
91+
objcache['{0}'] = JSPromise('{0}')
92+
`)
93+
object = await object;
94+
}
95+
try {
96+
if (object && [Array, Object, Number, String, Boolean, Function, AsyncFunction].indexOf(object.constructor) < 0) throw Error('Not JSON Serializable');
97+
if (object && object.constructor === Object) for (let key in Object.keys(object)) {
98+
if (object.indexOf(key) < 0) throw Error('Not a JSON');
99+
}
100+
else if (object && object.constructor === Function || (object && object.constructor && object.constructor.name === 'AsyncFunction')) {
101+
delete global.mpjscache['{0}'];
102+
return global.mp_js_do_str(`
103+
import js
104+
resolve(objcache, '{0}', JSFunction('{0}'))
105+
`);
106+
}
107+
object = object !== undefined ? JSON.stringify(object) : 'null';
108+
return global.mp_js_do_str(`
109+
import js
110+
import json
111+
resolve(objcache, '{0}', json.loads('''${object}'''))
112+
`);
113+
}
114+
catch(error) {
115+
return global.mp_js_do_str(`
116+
import js
117+
resolve(objcache, '{0}', JSObject('{0}'))
118+
`);
119+
}
120+
""", name)
121+
return objcache.get(name)
122+
123+
def __setattr__(self, key, value):
124+
value = json.dumps(value)
125+
object_name = self._name + '.' + key
126+
js_exec("""
127+
{0} = {1};
128+
""".format(object_name, value))
129+
130+
def JSFunction(name):
131+
short_name = name.split('.')[-1]
132+
def function(*args):
133+
args = json.dumps(list(args))
134+
js_exec("""
135+
let object;
136+
if (global.mpjscache['{0}']) {
137+
object = global.mpjscache['{0}'](...{1});
138+
}
139+
else if ({0}) {
140+
object = {0}(...{1});
141+
}
142+
//object = object(...{1});
143+
global.mpjscache['{0}'] = object;
144+
if (object.constructor === Promise) {
145+
await global.mp_js_do_str(`
146+
import js
147+
funcache['{0}'] = JSPromise('{0}')
148+
`)
149+
object = await object;
150+
}
151+
global.mpjscache['{0}'] = object;
152+
try {
153+
if (object && [Array, Object, Number, String, Boolean, Function, AsyncFunction].indexOf(object.constructor) < 0) throw Error('Not JSON Serializable');
154+
if (object && object.constructor === Object) for (let key in Object.keys(object)) {
155+
if (object.indexOf(key) < 0) throw Error('Not a JSON');
156+
}
157+
else if (object.constructor === Function || (object.constructor && object.constructor.name === 'AsyncFunction')) {
158+
return global.mp_js_do_str(`
159+
import js
160+
resolve(funcache, '{0}', JSFunction('{0}'))
161+
`);
162+
}
163+
object = object !== undefined ? JSON.stringify(object) : 'null';
164+
return global.mp_js_do_str(`
165+
import js
166+
import json
167+
resolve(funcache, '{0}', json.loads('''${object}'''))
168+
`);
169+
}
170+
catch(error) {
171+
return global.mp_js_do_str(`
172+
import js
173+
resolve(funcache, '{0}', JSObject('global.mpjscache{2}'))
174+
`);
175+
}
176+
""", name, args, '.' + name if '.' not in name else '["%s"]' % (name))
177+
return funcache.get(name)
178+
return function
179+
180+
def JS(variable):
181+
js_exec("""
182+
let object = {0};
183+
if (object.constructor === Promise) {
184+
await global.mp_js_do_str(`
185+
import js
186+
objcache['{0}'] = JSPromise('{0}')
187+
`)
188+
object = await object;
189+
}
190+
try {
191+
if (object && [Array, Object, Number, String, Boolean, Function, AsyncFunction].indexOf(object.constructor) < 0) throw Error('Not JSON Serializable');
192+
if (object && object.constructor === Object) for (let key in Object.keys({0})) {
193+
if (object.indexOf(value) < 0) throw Error('Not a JSON');
194+
}
195+
else if (object && (object.constructor === Function || object.constructor === AsyncFunction)) {
196+
delete global.mpjscache['{0}'];
197+
return global.mp_js_do_str(`
198+
import js
199+
resolve(objcache, '{0}', JSFunction('{0}'))
200+
`);
201+
}
202+
object = object !== undefined ? JSON.stringify(object) : 'null';
203+
return global.mp_js_do_str(`
204+
import js
205+
import json
206+
resolve(objcache, '{0}', json.loads('''${object}'''))
207+
`);
208+
}
209+
catch(error) {
210+
return global.mp_js_do_str(`
211+
import js
212+
resolve(objcache, '{0}', JSObject('{0}'))
213+
`);
214+
}
215+
""", variable)
216+
return objcache.get(variable)
217+
218+
#require = JS('require')
219+
#result = require('fs').readFileSync('./test.js').toString()
220+
#print(result)
221+
222+
#This code block is adaptable to Javascript's event loop
223+
#exec("""
224+
225+
#require = JS('require')
226+
#response = require('node-fetch')('https://github.com/')
227+
#response = wait(response)
228+
#print(response)
229+
#result = response.text()
230+
#result = wait(result)
231+
#print(result)
232+
233+
#""")

0 commit comments

Comments
 (0)