Skip to content
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
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ test_install:

test:
build/pocketpy.exe tests/test_numpy.py

build_wasm:
bash build_wasm.sh
.PHONY: build_wasm

serve_wasm:
cd web && python3 -m http.server 8080 --bind 0.0.0.0
.PHONY: serve_wasm
58 changes: 58 additions & 0 deletions build_wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
set -e

# Run prebuild if _generated.c doesn't exist
if [ ! -f pocketpy/src/common/_generated.c ]; then
python3 pocketpy/prebuild.py
fi

# Create output directory
rm -rf web/lib
mkdir -p web/lib

# Generate test_numpy.js from test source
python3 -c "
import json
with open('tests/test_numpy.py') as f:
src = f.read()
print('var TEST_SOURCE = ' + json.dumps(src) + ';')
" > web/test_numpy.js

# Common flags
DEFINES="-DPK_ENABLE_OS=0 -DPK_ENABLE_THREADS=0 -DPK_ENABLE_DETERMINISM=0 \
-DPK_ENABLE_WATCHDOG=0 -DPK_ENABLE_CUSTOM_SNAME=0 -DPK_ENABLE_MIMALLOC=0 \
-DPK_BUILD_MODULE_LZ4 -DPK_BUILD_MODULE_MSGPACK -DNDEBUG"
INCLUDES="-Ipocketpy/include -Iinclude -Ipocketpy/3rd/lz4 -Ipocketpy/3rd/lz4/lz4/lib -Ipocketpy/3rd/msgpack/include"
WARNINGS="-Wno-sign-compare -Wno-conversion -Wno-unused-variable -Wno-unused-parameter"

# Generate a unity C file that includes all pocketpy sources
TMPDIR=$(mktemp -d)
UNITY_C="$TMPDIR/unity_pocketpy.c"

for f in $(find pocketpy/src/ -name "*.c" | sort); do
echo "#include \"$(pwd)/$f\"" >> "$UNITY_C"
done
echo "#include \"$(pwd)/pocketpy/3rd/lz4/lz4/lib/lz4.c\"" >> "$UNITY_C"
echo "#include \"$(pwd)/pocketpy/3rd/msgpack/src/mpack.c\"" >> "$UNITY_C"
echo "#include \"$(pwd)/pocketpy/3rd/msgpack/src/bindings.c\"" >> "$UNITY_C"

# Compile unity C source with emcc
emcc "$UNITY_C" -c -Os $DEFINES $INCLUDES $WARNINGS -o "$TMPDIR/pocketpy.o"

# Compile C++ source and link with em++
em++ "$TMPDIR/pocketpy.o" \
src/numpy.cpp \
$INCLUDES \
-std=c++17 -Os \
$DEFINES \
-DSUPPRESS_XTENSOR_WARNINGS \
-DPY_DYNAMIC_MODULE \
-sEXPORTED_FUNCTIONS=_py_initialize,_py_exec,_py_finalize,_py_printexc,_py_clearexc,_py_module_initialize \
-sEXPORTED_RUNTIME_METHODS=ccall \
-sALLOW_MEMORY_GROWTH=1 \
-sSTACK_SIZE=1048576 \
$WARNINGS \
-o web/lib/pocketpy.js

rm -rf "$TMPDIR"

echo "Build complete: web/lib/pocketpy.js, web/lib/pocketpy.wasm"
225 changes: 225 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pocket-numpy</title>
<style>
body {
font-family: monospace;
background-color: #1e1e1e;
color: #d4d4d4;
margin: 20px;
}
h1 { color: #569cd6; margin-bottom: 12px; }
#status { color: #dcdcaa; margin-bottom: 10px; padding-top: 30px; }
.editor-container {
display: flex;
width: 100%;
min-height: 400px;
background-color: #282a36;
border: 1px solid #444;
border-radius: 6px;
position: relative;
}
.line-numbers {
width: 50px;
padding: 10px 8px 10px 5px;
background-color: #1e1e1e;
color: #858585;
text-align: right;
font-family: monospace;
font-size: 14px;
line-height: 1.4;
border-right: 1px solid #444;
user-select: none;
overflow-y: hidden;
overflow-x: hidden;
height: 100%;
box-sizing: border-box;
white-space: pre;
}
#editor {
flex: 1;
min-height: 400px;
background-color: #282a36;
color: #f8f8f2;
border: none;
padding: 10px;
font-family: monospace;
font-size: 14px;
line-height: 1.4;
tab-size: 4;
resize: none;
outline: none;
}
#run-btn {
margin-top: 8px;
padding: 6px 24px;
font-family: monospace;
font-size: 14px;
font-weight: bold;
cursor: pointer;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
}
#run-btn:hover { background-color: #0056b3; }
#run-btn:active { background-color: #004085; transform: translateY(1px); }
#run-btn:disabled { background-color: #555; cursor: not-allowed; }
#output {
margin-top: 10px;
background-color: #282a36;
padding: 15px;
border-radius: 6px;
white-space: pre-wrap;
min-height: 50px;
line-height: 1.4;
}
.pass { color: #4ec9b0; font-weight: bold; }
.fail { color: #f44747; font-weight: bold; }
</style>
</head>
<body>
<h1>pocket-numpy</h1>
GitHub: <a href="https://github.com/cubao/xtensor-numpy" style="color: #ffff00; text-decoration: none;" target="_blank">cubao/xtensor-numpy</a>
<div class="editor-container">
<div class="line-numbers" id="line-numbers">1</div>
<textarea id="editor" spellcheck="false"></textarea>
</div>
<br>
<button id="run-btn" disabled>Run</button>
<div id="status">Loading WASM module...</div>
<pre id="output"></pre>

<script src="test_numpy.js"></script>
<script>
var editorEl = document.getElementById('editor');
var outputEl = document.getElementById('output');
var statusEl = document.getElementById('status');
var runBtn = document.getElementById('run-btn');
var ready = false;

// Fill editor with bundled test source
editorEl.value = TEST_SOURCE;

// Tab key inserts a tab instead of moving focus
editorEl.addEventListener('keydown', function (e) {
if (e.key === 'Tab') {
e.preventDefault();
var s = this.selectionStart, end = this.selectionEnd;
this.value = this.value.substring(0, s) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = s + 4;
}
});

// Update line numbers
function updateLineNumbers() {
var lines = editorEl.value.split('\n');
var lineNumbers = '';
for (var i = 1; i <= lines.length; i++) {
lineNumbers += i + '\n';
}
// Remove trailing newline
lineNumbers = lineNumbers.replace(/\n$/, '');
document.getElementById('line-numbers').textContent = lineNumbers;
}

// Update line numbers on input and scroll
editorEl.addEventListener('input', function() {
updateLineNumbers();
syncLineNumbersHeight();
});

editorEl.addEventListener('scroll', function() {
document.getElementById('line-numbers').scrollTop = this.scrollTop;
});

// Ensure line numbers height matches editor height
function syncLineNumbersHeight() {
var editorHeight = editorEl.scrollHeight;
document.getElementById('line-numbers').style.height = editorHeight + 'px';
}

// Initial line numbers and height sync
updateLineNumbers();
syncLineNumbersHeight();

// Sync height on window resize
window.addEventListener('resize', function() {
setTimeout(syncLineNumbersHeight, 100);
});

// Monitor editor changes for better synchronization
var observer = new MutationObserver(function() {
setTimeout(function() {
updateLineNumbers();
syncLineNumbersHeight();
}, 10);
});

// Observe changes to editor
if (window.MutationObserver) {
observer.observe(editorEl, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
}

function runCode() {
if (!ready) return;
outputEl.textContent = '';
var allOutput = '';
Module._print_buf = function (text) {
allOutput += text + '\n';
outputEl.textContent += text + '\n';
};

var t0 = performance.now();

var ok = Module.ccall(
'py_exec', 'boolean', ['string', 'string', 'number', 'number'],
[editorEl.value, 'main.py', 0, 0]
);

var elapsed = (performance.now() - t0).toFixed(3);

if (!ok) {
Module.ccall('py_printexc', null, [], []);
Module.ccall('py_clearexc', null, ['number'], [0]);
}

if (allOutput.indexOf('ALL TESTS PASSED') !== -1) {
statusEl.innerHTML = '<span class="pass">ALL TESTS PASSED</span> (' + elapsed + ' ms)';
} else if (!ok) {
statusEl.innerHTML = '<span class="fail">ERROR</span> (' + elapsed + ' ms)';
} else {
statusEl.textContent = 'Done (' + elapsed + ' ms)';
}
}

runBtn.onclick = runCode;

var Module = {
onRuntimeInitialized: function () {
Module.ccall('py_initialize', null, [], []);
Module.ccall('py_module_initialize', 'boolean', [], []);
ready = true;
runBtn.disabled = false;
statusEl.textContent = 'Ready';
runCode();
},
print: function (text) {
if (Module._print_buf) Module._print_buf(text);
},
printErr: function (text) {
if (Module._print_buf) Module._print_buf(text);
}
};
</script>
<script src="lib/pocketpy.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions web/lib/pocketpy.js

Large diffs are not rendered by default.

Binary file added web/lib/pocketpy.wasm
Binary file not shown.
1 change: 1 addition & 0 deletions web/test_numpy.js

Large diffs are not rendered by default.

Loading