Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object & array pattern initialization #9

Merged
merged 5 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ jobs:
# Deploy job (only for the repo and **not** for forks)

deploy:
# only run on main branch
if: github.ref == 'refs/heads/main' && github.repository == 'xeus-javascript/xeus-javascript'

# Add a dependency to the build job
needs: emscripten_wasm_build

Expand All @@ -122,6 +125,5 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
if: github.ref == 'refs/heads/main' && github.repository == 'DerThorsten/xeus-javascript'
id: deployment
uses: actions/deploy-pages@v3 # or specific "vX.X.X" version tag for this action
62 changes: 43 additions & 19 deletions notebooks/xeus-javascript.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@
"var fubar = 42;\n",
"let fubar_local=43;\n",
"function call_me(arg){\n",
" pp(arg)\n",
"}"
" return arg;\n",
"}\n",
"let [fu,bar] = ['fu','bar'];\n",
"const {ping,pong} = {ping:1, pingpong:2, pong:3}"
]
},
{
Expand All @@ -174,24 +176,11 @@
},
"outputs": [],
"source": [
"// this works\n",
"console.log(fubar);"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "plaintext"
}
},
"outputs": [],
"source": [
"// this also works\n",
"\n",
"console.log(fubar);\n",
"console.log(fubar_local);\n",
"call_me(111)"
"console.log(call_me(42));\n",
"console.log(fu,bar);\n",
"console.log(ping,pong);"
]
},
{
Expand Down Expand Up @@ -268,6 +257,41 @@
"// same as above but with import scripts\n",
"importScripts(\"https://cdn.jsdelivr.net/npm/mathjs@12.3.0/lib/browser/math.min.js\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Dynamic imports"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "plaintext"
}
},
"outputs": [],
"source": [
"const {default: openai} = await import('https://cdn.jsdelivr.net/npm/openai@4.26.0/+esm');\n",
"console.log(openai.OpenAI);"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "plaintext"
}
},
"outputs": [],
"source": [
"const {OpenAI} = await import('https://cdn.jsdelivr.net/npm/openai@4.26.0/+esm');\n",
"console.log(OpenAI);"
]
}
],
"metadata": {
Expand Down
14 changes: 14 additions & 0 deletions src/main_emscripten_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@
#include "xeus/xembind.hpp"


void _stdout(const std::string& msg)
{
std::cout << msg << std::endl;
}

EMSCRIPTEN_BINDINGS(my_module) {
xeus::export_core();
using interpreter_type = xeus_javascript::interpreter;
xeus::export_kernel<interpreter_type>("xkernel");



emscripten::function("_publish_stdout_stream", &xeus_javascript::publish_stdout_stream);
emscripten::function("_publish_stderr_stream", &xeus_javascript::publish_stderr_stream);
emscripten::function("_display_data", &xeus_javascript::display_data);

// we overwrite the console.log function, so that we can redirect the output
// to the kernel. But sometime we need to call the original console.log without
// redirection.
// Since all std::cout calls are redirected to console.log, we just expose
// some api to stdout
emscripten::function("_stdout", &_stdout);


}
162 changes: 128 additions & 34 deletions src/pre.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,91 @@
Module["_make_async_from_code"] = function (code) {

if(code.length == 0){
return {
async_function: async function(){},
with_return: false
};
}

let code_to_use = code;
try{
var code_ast = Module["_ast_parse"](code);
}
catch(err){
throw err;
}
// add toplevel variables to global scope
Module["_add_to_global_scope"] = function (ast) {
let extra_code = [];
for(const node of code_ast.body){
for(const node of ast.body){
if(node.type == "FunctionDeclaration")
{
const name = node.id.name;
extra_code.push(`globalThis[\"${name}\"] = ${name};`);
}
else if(node.type == "VariableDeclaration")
{
const name = node.declarations[0].id.name;
extra_code.push(`globalThis[\"${name}\"] = ${name};`);
const declarations = node.declarations;
if(declarations.length != 1){
throw "VariableDeclaration with more than 1 declaration not yet implemented";
}
const declaration = declarations[0];
const declaration_type = declaration.id.type;

if(declaration_type == "ObjectPattern"){


// manual loop
for(const prop of declaration.id.properties){
const key = prop.key.name;

// in cases like const { default: defaultExport } = await import(url);
if(key == "default"){
// get the value
if(!prop.value.type == "Identifier"){
throw Error("default value is not an identifier");
}
const value = prop.value.name;
extra_code.push(`globalThis[\"${value}\"] = ${value};`);
}
// cases like const { a,b } = { a: 1, b: 2 , c: 3}
else{
extra_code.push(`globalThis[\"${key}\"] = ${key};`);
}
}

}
else if(declaration_type == "ArrayPattern"){
// get all the keys
const keys = declaration.id.elements.map((element)=>element.name);
Module["clog"]("ArrayPatternKeys",keys);
for(const key of keys){
extra_code.push(`globalThis[\"${key}\"] = ${key};`);
}
}
else if(declaration_type == "Identifier"){

const name = declaration.id.name;
extra_code.push(`globalThis[\"${name}\"] = ${name};`);
}
else{
throw `unknown VariableDeclaration type ${node.id.type}`;
}
}
}
return extra_code.join('\n');

// is the very last character a semicolon?
const last_char_is_semicolon = code_to_use[code_to_use.length-1] == ";";
}

Module["clog"] = function (...msg) {

let msg_str = "";
for (let i = 0; i < msg.length; i++) {
msg_str += `${msg[i]}`;
if (i < msg.length - 1) {
msg_str += " ";
}
}
Module["_stdout"](`${msg_str}\n`);
}


Module["_handle_last_statement"] = function (
code_user,
ast
) {


// is the very last character a semicolon?
const last_char_is_semicolon = code_user[code_user.length-1] == ";";

// get the last node
const last_node = code_ast.body[code_ast.body.length-1];
const last_node = ast.body[ast.body.length-1];

// if the last node is an expression statement
// then we need to add a return statement
Expand All @@ -44,26 +96,58 @@ Module["_make_async_from_code"] = function (code) {
const last_node_start = last_node.start;
const last_node_end = last_node.end;

const code_of_last_node = code_to_use.substring(last_node_start, last_node_end);

const new_code_of_last_node = `return ${code_of_last_node};`;
// remove the last node from the code
const modified_user_code = code_user.substring(0, last_node_start) + code_user.substring(last_node_end);
const code_of_last_node = code_user.substring(last_node_start, last_node_end);
const extra_return_code = `return ${code_of_last_node};`;

code_to_use = code_to_use.substring(0, last_node_start) + ";" +
extra_code.join('\n') +
new_code_of_last_node + code_to_use.substring(last_node_end);
with_return = true;
return {
with_return: true,
modified_user_code: modified_user_code,
extra_return_code: extra_return_code
}
}
else
{
const ec = extra_code.join('\n');
code_to_use = code_to_use.concat('\n', ec);
return {
with_return: false,
modified_user_code: code_user,
extra_return_code: ""
}
}

}




Module["_make_async_from_code"] = function (code) {

if(code.length == 0){
return {
async_function: async function(){},
with_return: false
};
}

const ast = Module["_ast_parse"](code);

console.dir(ast.body, {depth: null});

// code to add top level variables to global scope
const code_add_to_global_scope = Module["_add_to_global_scope"](ast);

// handle last statement / add return if needed
let {with_return, modified_user_code, extra_return_code} = Module["_handle_last_statement"](
code,
ast
);


// handle import statements (in reverse order)
// so that the line numbers stay correct
for(let i=code_ast.body.length-1; i>=0; i--){
const node = code_ast.body[i];
for(let i=ast.body.length-1; i>=0; i--){
const node = ast.body[i];
if(node.type == "ImportDeclaration"){
// most simple case, no specifiers
if(node.specifiers.length == 0){
Expand All @@ -74,13 +158,23 @@ Module["_make_async_from_code"] = function (code) {
}
const module_name = node.source.value;
const new_code_of_node = `importScripts("${module_name}");`;
code_to_use = code_to_use.substring(0, start) + new_code_of_node + code_to_use.substring(end);
modified_user_code = modified_user_code.substring(0, start) + new_code_of_node + modified_user_code.substring(end);
}
}
}

const combined_code = `
${modified_user_code}
${code_add_to_global_scope}
${extra_return_code}
`;

Module["clog"]("combined_code", combined_code);

let async_function = Function(`const afunc = async function(){
${code_to_use}

${combined_code}

};
return afunc;
`)();
Expand Down
4 changes: 1 addition & 3 deletions src/xinterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,17 @@ namespace xeus_javascript

auto data = nl::json::parse(json_str);


// publish stream
interpreter.display_data(
data["data"],
data["metadata"],
data["transient"]
);
std::cout<<"display_data_done"<<std::endl;
}

interpreter::interpreter()
{
std::cout<<"94th iteration of this file kernel (due to the service worker caching I need to print this to keep sanity)"<<std::endl;
std::cout<<"115th iteration of this file kernel (due to the service worker caching I need to print this to keep sanity)"<<std::endl;
xeus::register_interpreter(this);
}

Expand Down
Loading