Skip to content

Commit 7921474

Browse files
committed
added notebook
1 parent 76ab6db commit 7921474

9 files changed

Lines changed: 2893 additions & 0 deletions

File tree

include/pythonic/REPL/ScriptIt.cpp

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,12 +1211,263 @@ void executeScript(const std::string &content)
12111211
}
12121212
}
12131213

1214+
// ═══════════════════════════════════════════════════════════
1215+
// ──── Minimal JSON Helpers (for kernel mode) ───────────────
1216+
// ═══════════════════════════════════════════════════════════
1217+
1218+
// Minimal JSON value: string→string map parser (for kernel protocol)
1219+
inline std::unordered_map<std::string, std::string> parse_json_object(const std::string &json)
1220+
{
1221+
std::unordered_map<std::string, std::string> result;
1222+
size_t i = json.find('{');
1223+
if (i == std::string::npos)
1224+
return result;
1225+
i++;
1226+
1227+
auto skipWS = [&]()
1228+
{ while (i < json.size() && std::isspace(json[i])) i++; };
1229+
auto readString = [&]() -> std::string
1230+
{
1231+
skipWS();
1232+
if (i >= json.size() || json[i] != '"')
1233+
return "";
1234+
i++; // skip opening "
1235+
std::string s;
1236+
while (i < json.size() && json[i] != '"')
1237+
{
1238+
if (json[i] == '\\' && i + 1 < json.size())
1239+
{
1240+
i++;
1241+
if (json[i] == 'n')
1242+
s += '\n';
1243+
else if (json[i] == 't')
1244+
s += '\t';
1245+
else if (json[i] == '\\')
1246+
s += '\\';
1247+
else if (json[i] == '"')
1248+
s += '"';
1249+
else if (json[i] == '/')
1250+
s += '/';
1251+
else
1252+
s += json[i];
1253+
}
1254+
else
1255+
s += json[i];
1256+
i++;
1257+
}
1258+
if (i < json.size())
1259+
i++; // skip closing "
1260+
return s;
1261+
};
1262+
1263+
while (i < json.size())
1264+
{
1265+
skipWS();
1266+
if (json[i] == '}')
1267+
break;
1268+
if (json[i] == ',')
1269+
{
1270+
i++;
1271+
continue;
1272+
}
1273+
std::string key = readString();
1274+
skipWS();
1275+
if (i < json.size() && json[i] == ':')
1276+
i++;
1277+
skipWS();
1278+
// Value can be string, number, or null
1279+
if (i < json.size() && json[i] == '"')
1280+
{
1281+
result[key] = readString();
1282+
}
1283+
else
1284+
{
1285+
// Read non-string value as raw text until , or }
1286+
std::string val;
1287+
while (i < json.size() && json[i] != ',' && json[i] != '}')
1288+
val += json[i++];
1289+
// trim
1290+
while (!val.empty() && std::isspace(val.back()))
1291+
val.pop_back();
1292+
result[key] = val;
1293+
}
1294+
}
1295+
return result;
1296+
}
1297+
1298+
inline std::string json_escape(const std::string &s)
1299+
{
1300+
std::string out;
1301+
out.reserve(s.size() + 10);
1302+
for (char c : s)
1303+
{
1304+
switch (c)
1305+
{
1306+
case '"':
1307+
out += "\\\"";
1308+
break;
1309+
case '\\':
1310+
out += "\\\\";
1311+
break;
1312+
case '\n':
1313+
out += "\\n";
1314+
break;
1315+
case '\r':
1316+
out += "\\r";
1317+
break;
1318+
case '\t':
1319+
out += "\\t";
1320+
break;
1321+
default:
1322+
out += c;
1323+
}
1324+
}
1325+
return out;
1326+
}
1327+
1328+
inline std::string make_json_response(const std::string &cellId, const std::string &status,
1329+
const std::string &stdoutStr, const std::string &stderrStr,
1330+
const std::string &result, int execCount)
1331+
{
1332+
return "{\"cell_id\":\"" + json_escape(cellId) + "\","
1333+
"\"status\":\"" +
1334+
json_escape(status) + "\","
1335+
"\"stdout\":\"" +
1336+
json_escape(stdoutStr) + "\","
1337+
"\"stderr\":\"" +
1338+
json_escape(stderrStr) + "\","
1339+
"\"result\":\"" +
1340+
json_escape(result) + "\","
1341+
"\"execution_count\":" +
1342+
std::to_string(execCount) + "}";
1343+
}
1344+
1345+
// ═══════════════════════════════════════════════════════════
1346+
// ──── Kernel Mode ──────────────────────────────────────────
1347+
// ═══════════════════════════════════════════════════════════
1348+
1349+
void runKernel()
1350+
{
1351+
Scope globalScope;
1352+
globalScope.define("PI", var(3.14159265));
1353+
globalScope.define("e", var(2.7182818));
1354+
int executionCount = 0;
1355+
1356+
// Signal ready
1357+
std::cout << "{\"status\":\"kernel_ready\",\"version\":\"2.0\"}" << std::endl;
1358+
std::cout.flush();
1359+
1360+
std::string line;
1361+
while (std::getline(std::cin, line))
1362+
{
1363+
if (line.empty())
1364+
continue;
1365+
1366+
auto cmd = parse_json_object(line);
1367+
std::string action = cmd["action"];
1368+
1369+
if (action == "shutdown")
1370+
{
1371+
std::cout << "{\"status\":\"shutdown_ok\"}" << std::endl;
1372+
std::cout.flush();
1373+
break;
1374+
}
1375+
1376+
if (action == "reset")
1377+
{
1378+
globalScope.clear();
1379+
globalScope.define("PI", var(3.14159265));
1380+
globalScope.define("e", var(2.7182818));
1381+
executionCount = 0;
1382+
std::cout << "{\"status\":\"reset_ok\"}" << std::endl;
1383+
std::cout.flush();
1384+
continue;
1385+
}
1386+
1387+
if (action == "execute")
1388+
{
1389+
std::string cellId = cmd["cell_id"];
1390+
std::string code = cmd["code"];
1391+
executionCount++;
1392+
1393+
// Capture stdout
1394+
std::ostringstream capturedOut;
1395+
std::streambuf *oldBuf = std::cout.rdbuf(capturedOut.rdbuf());
1396+
1397+
std::string errorStr;
1398+
std::string resultStr;
1399+
1400+
try
1401+
{
1402+
Tokenizer tokenizer;
1403+
auto tokens = tokenizer.tokenize(code);
1404+
Parser parser(tokens);
1405+
auto program = parser.parseProgram();
1406+
1407+
for (auto &stmt : program->statements)
1408+
stmt->execute(globalScope);
1409+
}
1410+
catch (ReturnException &e)
1411+
{
1412+
resultStr = format_output(e.value);
1413+
}
1414+
catch (std::exception &e)
1415+
{
1416+
errorStr = e.what();
1417+
}
1418+
1419+
// Restore stdout
1420+
std::cout.rdbuf(oldBuf);
1421+
std::string stdoutStr = capturedOut.str();
1422+
1423+
std::string status = errorStr.empty() ? "ok" : "error";
1424+
std::cout << make_json_response(cellId, status, stdoutStr, errorStr, resultStr, executionCount) << std::endl;
1425+
std::cout.flush();
1426+
continue;
1427+
}
1428+
1429+
if (action == "complete")
1430+
{
1431+
std::string code = cmd["code"];
1432+
// Simple completion: list scope variables and builtins that match prefix
1433+
std::vector<std::string> matches;
1434+
for (auto &[name, _] : globalScope.getAll())
1435+
{
1436+
if (name.find(code) == 0)
1437+
matches.push_back(name);
1438+
}
1439+
// Return as JSON array
1440+
std::string out = "{\"status\":\"ok\",\"completions\":[";
1441+
for (size_t i = 0; i < matches.size(); i++)
1442+
{
1443+
if (i > 0)
1444+
out += ",";
1445+
out += "\"" + json_escape(matches[i]) + "\"";
1446+
}
1447+
out += "]}";
1448+
std::cout << out << std::endl;
1449+
std::cout.flush();
1450+
continue;
1451+
}
1452+
1453+
// Unknown action
1454+
std::cout << "{\"status\":\"error\",\"stderr\":\"Unknown action: " + json_escape(action) + "\"}" << std::endl;
1455+
std::cout.flush();
1456+
}
1457+
}
1458+
12141459
// ═══════════════════════════════════════════════════════════
12151460
// ──── Main ─────────────────────────────────────────────────
12161461
// ═══════════════════════════════════════════════════════════
12171462

12181463
int main(int argc, char *argv[])
12191464
{
1465+
if (argc > 1 && std::string(argv[1]) == "--kernel")
1466+
{
1467+
runKernel();
1468+
return 0;
1469+
}
1470+
12201471
if (argc > 1 && std::string(argv[1]) == "--test")
12211472
{
12221473
std::string source =

include/pythonic/REPL/notebook.sh

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env bash
2+
# ╔══════════════════════════════════════════════════╗
3+
# ║ ScriptIt Notebook Launcher ║
4+
# ╚══════════════════════════════════════════════════╝
5+
#
6+
# Usage:
7+
# ./notebook.sh # New empty notebook
8+
# ./notebook.sh my_notebook.nsit # Open existing notebook
9+
# ./notebook.sh --port 9999 # Custom port
10+
# ./notebook.sh my.nsit --port 9999 # Both
11+
12+
set -e
13+
14+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
15+
NOTEBOOK_DIR="$SCRIPT_DIR/notebook"
16+
SERVER="$NOTEBOOK_DIR/notebook_server.py"
17+
BINARY="$SCRIPT_DIR/scriptit"
18+
CPP_SOURCE="$SCRIPT_DIR/ScriptIt.cpp"
19+
INCLUDE_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)/include"
20+
STUBS_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)/src/pythonicDispatchStubs.cpp"
21+
22+
# Colors
23+
RED='\033[0;31m'
24+
GREEN='\033[0;32m'
25+
BLUE='\033[0;34m'
26+
CYAN='\033[0;36m'
27+
BOLD='\033[1m'
28+
NC='\033[0m'
29+
30+
# ─── Check / Build binary ────────────────────────────────
31+
32+
if [ ! -f "$BINARY" ]; then
33+
echo -e "${CYAN}Building ScriptIt...${NC}"
34+
if [ -f "$CPP_SOURCE" ] && [ -f "$STUBS_DIR" ]; then
35+
g++ -std=c++20 -I"$INCLUDE_DIR" -O2 -o "$BINARY" "$CPP_SOURCE" "$STUBS_DIR"
36+
echo -e "${GREEN}✓ Built successfully${NC}"
37+
else
38+
echo -e "${RED}Error: Cannot find source files to build ScriptIt.${NC}"
39+
echo " Expected: $CPP_SOURCE"
40+
echo " Expected: $STUBS_DIR"
41+
exit 1
42+
fi
43+
fi
44+
45+
# ─── Check dependencies ──────────────────────────────────
46+
47+
if ! command -v python3 &>/dev/null; then
48+
echo -e "${RED}Error: python3 not found${NC}"
49+
exit 1
50+
fi
51+
52+
if [ ! -f "$SERVER" ]; then
53+
echo -e "${RED}Error: Server not found at $SERVER${NC}"
54+
exit 1
55+
fi
56+
57+
# ─── Parse args ──────────────────────────────────────────
58+
59+
PORT=8888
60+
NOTEBOOK_FILE=""
61+
62+
while [[ $# -gt 0 ]]; do
63+
case "$1" in
64+
--port|-p)
65+
PORT="$2"
66+
shift 2
67+
;;
68+
--help|-h)
69+
echo "Usage: $0 [notebook.nsit] [--port PORT]"
70+
echo ""
71+
echo "Options:"
72+
echo " --port, -p PORT Server port (default: 8888)"
73+
echo " --help, -h Show this help"
74+
exit 0
75+
;;
76+
*)
77+
if [[ "$1" == *.nsit ]]; then
78+
NOTEBOOK_FILE="$1"
79+
fi
80+
shift
81+
;;
82+
esac
83+
done
84+
85+
# ─── Launch ──────────────────────────────────────────────
86+
87+
echo -e ""
88+
echo -e "${BOLD}${BLUE}╔══════════════════════════════════════════════════╗${NC}"
89+
echo -e "${BOLD}${BLUE}${NC} ${BOLD}ScriptIt Notebook${NC} ${BOLD}${BLUE}${NC}"
90+
echo -e "${BOLD}${BLUE}╚══════════════════════════════════════════════════╝${NC}"
91+
echo -e ""
92+
echo -e " ${CYAN}URL:${NC} http://localhost:$PORT"
93+
if [ -n "$NOTEBOOK_FILE" ]; then
94+
echo -e " ${CYAN}File:${NC} $NOTEBOOK_FILE"
95+
fi
96+
echo -e " ${CYAN}Binary:${NC} $BINARY"
97+
echo -e " ${CYAN}Press${NC} Ctrl+C to stop"
98+
echo -e ""
99+
100+
# Build command
101+
CMD=(python3 "$SERVER" --port "$PORT")
102+
if [ -n "$NOTEBOOK_FILE" ]; then
103+
CMD+=("$NOTEBOOK_FILE")
104+
fi
105+
106+
# Open browser after short delay
107+
(sleep 1.5 && {
108+
if command -v xdg-open &>/dev/null; then
109+
xdg-open "http://localhost:$PORT" 2>/dev/null
110+
elif command -v open &>/dev/null; then
111+
open "http://localhost:$PORT"
112+
fi
113+
}) &
114+
115+
# Run server (foreground, Ctrl+C will stop it)
116+
exec "${CMD[@]}"

0 commit comments

Comments
 (0)