JTWAYNE CODE / 0.1.0
ENET IDE
ENET AST target · native executables not yet supported
Opening editor…
—
Ready
ENET BY JTWAYNE / LEARNING LIBRARY
SKIENET & ENET: Learn from the source.
150 pages · 6 × 9 inches. Six classrooms, 24 lessons, worked solutions, eight console projects and a tour of the actual interpreter. ENET 0.1 is a working prototype; native compilation and universal compatibility remain future work.
Read the ENET interpreter source
import jsep from "jsep";
export const ENET_VERSION = "0.1.0";
export const ENET_PRICE_USD = 0;
export const MAX_SOURCE_LENGTH = 50000;
// ENET interprets an allowlisted AST. No eval, host imports, file or network access.
export function compileEnet(source) {
if (typeof source !== "string" || !source.trim()) throw new Error("Program is empty.");
if (source.length > MAX_SOURCE_LENGTH) throw new Error("Program exceeds 50,000 characters.");
const lines = source.replace(/\r/g, "").split("\n");
let cursor = 0;
const expression = (text) => {
if (text.length > 4000) throw new Error("Expression is too long.");
return jsep(text);
};
function block(nested = false, depth = 0, openingLine = 0) {
if (depth > 32) throw new Error("Too many nested blocks.");
const statements = [];
while (cursor < lines.length) {
const line = ++cursor;
const text = lines[line - 1].trim();
if (!text || text.startsWith("//")) continue;
if (text === "}") {
if (!nested) throw new Error(`Line ${line}: unexpected }.`);
return statements;
}
try {
let m;
if ((m = text.match(/^fn\s+([A-Za-z_]\w*)\(([^)]*)\)\s*\{$/))) {
const params = m[2].trim() ? m[2].split(",").map(x => x.trim()) : [];
if (params.some(x => !/^[A-Za-z_]\w*$/.test(x)) || new Set(params).size !== params.length) throw new Error("Invalid function parameters.");
statements.push({ kind: "fn", name: m[1], params, body: block(true, depth + 1, line), line });
} else if ((m = text.match(/^(if|repeat)\s+(.+)\s*\{$/))) {
statements.push({ kind: m[1], expr: expression(m[2]), body: block(true, depth + 1, line), line });
} else if ((m = text.match(/^(let|set)\s+([A-Za-z_]\w*)\s*=\s*(.+)$/))) {
statements.push({ kind: m[1], name: m[2], expr: expression(m[3]), line });
} else if ((m = text.match(/^(say|return)\s+(.+)$/))) {
statements.push({ kind: m[1], expr: expression(m[2]), line });
} else if (text === "use jtwayne.code") {
statements.push({ kind: "use", line });
} else {
throw new Error("Expected use, let, set, say, if, repeat, fn or return. Put one statement on each line.");
}
} catch (error) {
if (/^Line \d+:/.test(error.message)) throw error;
throw new Error(`Line ${line}: ${error.message}`);
}
}
if (nested) throw new Error(`Line ${openingLine}: Missing closing }.`);
return statements;
}
const program = block();
function check(node, depth = 0) {
if (depth > 64) throw new Error("Expression nesting limit reached.");
const visit = child => check(child, depth + 1);
switch (node.type) {
case "Literal": case "Identifier": return;
case "ArrayExpression": node.elements.forEach(visit); return;
case "MemberExpression":
if (!node.computed) throw new Error("Object properties are not available in ENET 0.1.");
visit(node.object); visit(node.property); return;
case "UnaryExpression":
if (!["!", "-", "+"].includes(node.operator)) throw new Error("Unsupported unary operator.");
visit(node.argument); return;
case "BinaryExpression":
if (!["+", "-", "*", "/", "%", "==", "===", "!=", "!==", "<", "<=", ">", ">=", "&&", "||"].includes(node.operator)) throw new Error("Unsupported binary operator.");
visit(node.left); visit(node.right); return;
case "CallExpression":
if (node.callee.type !== "Identifier") throw new Error("Only named ENET functions can be called.");
node.arguments.forEach(visit); return;
default: throw new Error(`Unsupported expression: ${node.type}.`);
}
}
let statements = 0;
function validate(items, inFunction = false) {
for (const s of items) {
statements++;
try {
if (s.kind === "return" && !inFunction) throw new Error("return is only valid inside a function.");
if (s.expr) check(s.expr);
} catch (error) { throw new Error(`Line ${s.line}: ${error.message}`); }
if (s.body) validate(s.body, inFunction || s.kind === "fn");
}
}
validate(program);
return { program, statements, version: ENET_VERSION, target: "enet-ast" };
}
export function runEnet(source, options = {}) {
const { program } = compileEnet(source);
let steps = 0;
let outputSize = 0;
const output = [];
const stepLimit = Math.min(10000, Math.max(1, options.maxSteps ?? 10000));
const builtins = new Map(Object.entries({
len: x => typeof x === "string" || Array.isArray(x) ? x.length : fail("len needs text or an array."),
upper: x => String(x).toUpperCase(), lower: x => String(x).toLowerCase(),
str: x => String(x), number: x => Number(x), abs: x => Math.abs(numeric(x)),
round: x => Math.round(numeric(x)), sqrt: x => Math.sqrt(numeric(x)),
min: (a, b) => Math.min(numeric(a), numeric(b)), max: (a, b) => Math.max(numeric(a), numeric(b)),
}));
function fail(message) { throw new Error(message); }
function numeric(x) { return typeof x === "number" ? x : fail("Expected a number."); }
function tick() { if (++steps > stepLimit) fail("Execution limit reached (10,000 steps)."); }
function bounded(x) {
if (typeof x === "number" && !Number.isFinite(x)) fail("Number must be finite (check division by zero).");
if (typeof x === "string" && x.length > 20000) fail("Text exceeds 20,000 characters.");
if (Array.isArray(x) && x.length > 1000) fail("Array exceeds 1,000 items.");
return x;
}
function resolve(scope, name) {
for (let s = scope; s; s = s.parent) if (s.values.has(name)) return s;
fail(`Unknown name: ${name}.`);
}
function evaluate(node, scope, depth = 0) {
tick();
if (depth > 64) fail("Expression nesting limit reached.");
const ev = n => evaluate(n, scope, depth + 1);
let value;
switch (node.type) {
case "Literal": value = node.value; break;
case "Identifier": value = resolve(scope, node.name).values.get(node.name); break;
case "ArrayExpression": value = node.elements.map(ev); break;
case "MemberExpression": {
if (!node.computed) fail("Object properties are not available in ENET 0.1.");
const object = ev(node.object), index = ev(node.property);
if (!(Array.isArray(object) || typeof object === "string") || !Number.isInteger(index) || index < 0 || index >= object.length) fail("Invalid array/text index.");
value = object[index]; break;
}
case "UnaryExpression": {
const x = ev(node.argument);
if (node.operator === "!") value = !x;
else if (node.operator === "-") value = -numeric(x);
else if (node.operator === "+") value = numeric(x);
else fail("Unsupported unary operator.");
break;
}
case "BinaryExpression": {
const a = ev(node.left);
if (node.operator === "&&") { value = a && ev(node.right); break; }
if (node.operator === "||") { value = a || ev(node.right); break; }
const b = ev(node.right);
switch (node.operator) {
case "+": value = typeof a === "string" || typeof b === "string" ? String(a) + String(b) : numeric(a) + numeric(b); break;
case "-": value = numeric(a) - numeric(b); break;
case "*": value = numeric(a) * numeric(b); break;
case "/": value = numeric(a) / numeric(b); break;
case "%": value = numeric(a) % numeric(b); break;
case "==": case "===": value = a === b; break;
case "!=": case "!==": value = a !== b; break;
case "<": value = a < b; break;
case "<=": value = a <= b; break;
case ">": value = a > b; break;
case ">=": value = a >= b; break;
default: fail("Unsupported binary operator.");
}
break;
}
case "CallExpression": {
if (node.callee.type !== "Identifier") fail("Only named ENET functions can be called.");
const args = node.arguments.map(ev);
const name = node.callee.name;
if (builtins.has(name)) {
const arity = name === "min" || name === "max" ? 2 : 1;
if (args.length !== arity) fail(`${name} needs ${arity} argument(s).`);
value = builtins.get(name)(...args);
} else {
const fn = resolve(scope, name).values.get(name);
if (!fn || fn.kind !== "function") fail(`${name} is not a function.`);
if (args.length !== fn.params.length) fail(`Wrong number of arguments for ${name}.`);
if (scope.calls >= 32) fail("Function call depth limit reached.");
const local = { values: new Map(fn.params.map((p, i) => [p, args[i]])), parent: fn.scope, calls: scope.calls + 1 };
value = execute(fn.body, local, true)?.value ?? null;
}
break;
}
default: fail(`Unsupported expression: ${node.type}.`);
}
if (value && value.kind === "function") fail("Functions cannot be used as values.");
return bounded(value);
}
function execute(statements, scope, inFunction = false) {
for (const s of statements) {
try {
tick();
if (s.kind === "use") continue;
if (s.kind === "fn") {
if (scope.values.has(s.name) || builtins.has(s.name)) fail(`Name already defined: ${s.name}.`);
scope.values.set(s.name, { kind: "function", ...s, scope });
// kind must not be overwritten by the parsed statement's kind.
scope.values.get(s.name).kind = "function";
continue;
}
const value = evaluate(s.expr, scope);
if (s.kind === "let") {
if (scope.values.has(s.name) || builtins.has(s.name)) fail(`Name already defined: ${s.name}.`);
scope.values.set(s.name, value);
} else if (s.kind === "set") {
const target = resolve(scope, s.name);
if (target.values.get(s.name)?.kind === "function") fail("Cannot overwrite a function.");
target.values.set(s.name, value);
} else if (s.kind === "say") {
const text = Array.isArray(value) ? JSON.stringify(value) : String(value);
outputSize += text.length;
if (output.length >= 1000 || outputSize > 50000) fail("Output limit reached.");
output.push(text);
} else if (s.kind === "return") {
if (!inFunction) fail("return is only valid inside a function.");
return { value };
} else if (s.kind === "if" && value) {
const result = execute(s.body, scope, inFunction);
if (result) return result;
} else if (s.kind === "repeat") {
if (!Number.isInteger(value) || value < 0 || value > 10000) fail("repeat needs an integer from 0 to 10,000.");
for (let i = 0; i < value; i++) {
const result = execute(s.body, { values: new Map(), parent: scope, calls: scope.calls }, inFunction);
if (result) return result;
}
}
} catch (error) {
if (/^Line \d+:/.test(error.message)) throw error;
throw new Error(`Line ${s.line}: ${error.message}`);
}
}
}
execute(program, { values: new Map(), parent: null, calls: 0 });
return { output, steps, version: ENET_VERSION };
}Universal
One language meant to reach every platform SKIENET runs on, from apps to the OS itself.
Universal ambition
Python, C, JavaScript and Lua bridges are future work. ENET 0.1 does not yet call other languages.
The JTWAYNE CODE
Every ENET program follows the JTWAYNE CODE: the shared rules and standard library that tie ENET to SKIENET.
A growing foundation
Version 0.1 runs variables, arithmetic, conditions, repeats, arrays and functions. Native apps and AI bindings are not implemented yet.
Simple to learn
Clean, readable syntax so new programmers can start fast.
Free forever
ENET will be free to download and use. Programs can be shared and uploaded to SKIENET.
Beyond version 0.1: the roadmap
PART 01
Compiler
Turns ENET code into programs that run on SKIENET and other systems.
PART 02
Standard library (JTWAYNE CODE)
Built-in tools for files, networking, screens and SKI agents.
PART 03
Bridges
Connectors that let ENET use code written in other languages.
PART 04
Package hub on SKIENET
Download ENET, and upload or share your ENET programs.
Your language. Your programs.
The free starter kit includes the interpreter, a Node.js command-line runner, examples, tests and a continuation brief for Claude. Saved member programs are private. This is not a native compiler or a production OS release.