A CSP-safe workflow programming language for browser automation, designed to run in Chrome service workers and other restricted JavaScript environments.
π Try it in the browser playground!
- β
100% CSP-Safe - No
eval()
ornew Function()
, runs safely in Chrome service workers - π― Zero Ambiguity Grammar - Deterministic parsing with exactly one parse tree for valid syntax
- π¦ Modern JavaScript-like Syntax - Full support for classes, modules, async/await, destructuring
- π Pluggable Module System - Implement your own module resolution (memory, IndexedDB, HTTP, etc.)
- π Enhanced Error Reporting (v0.16.1+) - Full context with line numbers, stack traces, module names, and intelligent suggestions
- π Browser Automation Focus - Built for DOM manipulation and web workflows
- π JavaScript Method Chaining - Full support for method chaining, functional programming patterns, and multiline expressions
- β¨ Full Class Support - Classes with constructors, methods, inheritance with super(), and proper
this
binding - π Robust Variable Scoping - Const immutability, var hoisting, block scoping with proper shadowing
- β»οΈ Circular Dependency Support - Handles circular module imports without memory leaks
- π Execution Metadata API - Comprehensive compilation and runtime metadata for debugging and analysis
- π Implicit Return Values - Last expression in code becomes the return value, perfect for REPL and workflows
- β Ternary Conditional Operator - Full support for
condition ? true : false
expressions - π§ͺ Fully Tested - Comprehensive test suite using Vitest (638/640 tests passing - 99.7% coverage)
- π Rich Standard Library - 70+ built-in functions for arrays, objects, strings, math, and utilities
- β Compound Assignment - Modern operators (
+=
,-=
,*=
,/=
) with zero-ambiguity grammar - π Regular Expression Support - Full regex literals with all JavaScript flags (
/pattern/gimsuy
) - βΈοΈ Pausable Execution - Pause and resume interpreter execution at any point
- πΎ State Serialization - Save and restore complete interpreter state to/from JSON
- π€ Reserved Keywords as Properties - Use reserved words like
from
,import
,class
as property names (JavaScript-compatible) - π Console Capture - Capture all console.log, warn, and error outputs with metadata (v0.12.0+)
- π Multi-line Function Calls - Support for multi-line function calls and parameters for better readability (v0.15.14+)
- π Template Literals - Full support for template strings with expression interpolation
`Hello, ${name}!`
- π¨ Syntax Highlighting - Language support for Monaco Editor, VS Code, and CodeMirror
npm install wang-lang
Wang provides command-line tools for validating and executing .wang
files:
# Execute a .wang file
npx wang-run script.wang
# Execute with verbose output
npx wang-run script.wang --verbose
# Execute from stdin
echo 'console.log("Hello Wang!")' | npx wang-run -
# Quiet mode (only show output)
npx wang-run script.wang --quiet
# Validate syntax
npx wang-validate script.wang
# Show AST
npx wang-validate script.wang --ast
# Show syntax suggestions
npx wang-validate script.wang --suggestions
# Validate from stdin
echo 'let x = 1' | npx wang-validate -
Create a file called hello.wang
:
// Simple Hello World example
console.log("Hello, Wang!")
// Test basic operations
let x = 42
let y = x * 2
console.log(`x = ${x}, y = ${y}`)
// Test array operations
let numbers = [1, 2, 3, 4, 5]
let evenNumbers = filter(numbers, n => n % 2 === 0)
let doubledNumbers = map(evenNumbers, n => n * 2)
console.log("Even numbers doubled:", doubledNumbers)
// Test functions
function greet(name) {
return `Hello, ${name}!`
}
let message = greet("Wang Developer")
console.log(message)
Then run it:
npx wang-run hello.wang
import { WangInterpreter, InMemoryModuleResolver } from 'wang-lang'
// Create a module resolver
const resolver = new InMemoryModuleResolver()
// Add a module
resolver.addModule('utils', `
export function processData(data) {
return data
.filter(item => item.active)
.map(item => item.name)
.sort()
`)
// Create interpreter - 70+ stdlib functions are automatically available!
const interpreter = new WangInterpreter({
moduleResolver: resolver,
// Add custom functions if needed (stdlib already includes filter, map, sort_by, etc.)
functions: {
myCustomFunction: (x) => x * 2
}
})
// Set JavaScript objects as variables (v0.11.1+)
interpreter.setVariable('Math', Math)
interpreter.setVariable('JSON', JSON)
interpreter.setVariable('customObject', { value: 42 })
// Execute Wang code - returns the last expression value
const result = await interpreter.execute(`
import { processData } from "utils"
let data = [
{ name: "Alice", active: true },
{ name: "Bob", active: false },
{ name: "Charlie", active: true }
]
let processed = processData(data)
log(processed) // ["Alice", "Charlie"]
// Last expression becomes the return value
{ processed, count: processed.length }
`)
console.log(result); // { processed: ["Alice", "Charlie"], count: 2 }
Wang includes a comprehensive standard library with 70+ built-in functions that work seamlessly with method chaining and require no imports:
// Array operations (immutable)
let arr = [3, 1, 4, 1, 5, 9, 2, 6]
let uniqueArr = unique(arr) // [3, 1, 4, 5, 9, 2, 6] - Remove duplicates
let sortedArr = sort(uniqueArr) // [1, 2, 3, 4, 5, 6, 9] - Sort ascending
let chunkedArr = chunk(sortedArr, 2) // [[1,2], [3,4], [5,6], [9]] - Group into pairs
let result = map(chunkedArr, pair => sum(pair)) // [3, 7, 11, 9] - Sum each pair
// Advanced array operations
let users = [
{ name: "Alice", age: 30, active: true },
{ name: "Bob", age: 25, active: false },
{ name: "Charlie", age: 35, active: true }
]
let activeUsers = filter(users, u => u.active) // Only active users
let sortedUsers = sort_by(activeUsers, "age") // Sort by age property
let names = map(sortedUsers, u => u.name) // Extract names
let result = join(names, ", ") // "Alice, Charlie"
// Object operations
let user = { name: "Alice", age: 30, email: "alice@example.com", password: "secret" }
let publicData = pick(user, ["name", "age"]) // { name: "Alice", age: 30 }
let withoutSecret = omit(user, ["password"]) // Remove sensitive data
let merged = merge(user, { location: "NYC" }) // Add new properties
// String operations
let str = " hello world "
let trimmed = trim(str) // "hello world" - Remove whitespace
let uppercased = upper(trimmed) // "HELLO WORLD" - Uppercase
let replaced = replace_all(uppercased, "O", "0") // "HELL0 W0RLD" - Replace all O's
let result = split(replaced, " ") // ["HELL0", "W0RLD"] - Split to array
// Math and utilities
let numbers = [1, 5, 3, 9, 2]
let stats = {
total: sum(numbers), // 20
average: avg(numbers), // 4
median: median(numbers), // 3
min: min(...numbers), // 1
max: max(...numbers) // 9
}
// Type checking and validation
is_array([1, 2, 3]) // true
is_object({a: 1}) // true
is_string("hello") // true
is_empty([]) // true
is_empty("") // true
is_empty({}) // true
// Functional utilities
range(5) // [0, 1, 2, 3, 4] - Generate sequence
uuid() // "123e4567-e89b-12d3-a456-426614174000"
sleep(1000) // Promise that resolves after 1 second
See Standard Library Reference for the complete list.
Wang supports most modern JavaScript features including multiline expressions (v0.21.0+):
// Variables with proper scoping semantics
let mutable = 10; // Block-scoped
const immutable = 20; // Block-scoped and immutable
var hoisted = 30; // Function-scoped with hoisting
// Compound assignment operators (v0.6.3 - fully working!)
mutable += 5; // Addition assignment: 15
mutable -= 2; // Subtraction assignment: 13
mutable *= 3; // Multiplication assignment: 39
mutable /= 2; // Division assignment: 19.5
// Increment and decrement operators
let counter = 0
counter++ // Post-increment: returns 0, then increments to 1
++counter; // Pre-increment: increments to 2, then returns 2
counter--; // Post-decrement: returns 2, then decrements to 1
--counter; // Pre-decrement: decrements to 0, then returns 0
// Const immutability is enforced
const PI = 3.14159
// PI = 3.14 // β Error: Cannot reassign const variable
// Var hoisting works correctly
log(typeof x) // "undefined" (not an error!)
var x = 42
log(x) // 42
// Multiline conditionals (v0.21.0+)
if (text && href && (
text.toLowerCase().includes('result') ||
text.toLowerCase().includes('fixture') ||
text.toLowerCase().includes('match') ||
text.toLowerCase().includes('schedule')
)) {
console.log("Sports link found!")
}
// Multiline logical expressions
let isValid = (
value !== null
&& value !== undefined
&& (value > 0 || value === -1)
)
// Multiline ternary operators
let message = value > 100
? "Large value"
: value > 50
? "Medium value"
: "Small value"
// Multiline for loops
for (
let i = 0;
i < items.length;
i++
) {
process(items[i])
}
// Block scoping with proper shadowing
let outer = 1
{
let outer = 2 // Shadows outer variable
const inner = 3
log(outer) // 2
}
log(outer) // 1
// log(inner); // β Error: inner is not defined
// Destructuring
const { name, age } = person
const [first, second, ...rest] = numbers
// Template literals with full expression support
const message = `Hello, ${name}!`
const math = `Result: ${2 + 3} and ${10 * 5}`
const complex = `User ${user.name} is ${user.age >= 18 ? "adult" : "minor"}`
const formatted = `Price: $${price.toFixed(2)}`
// Note: Nested template literals are not supported (architectural limitation)
// Optional chaining (dot notation and computed member access)
const value = obj?.nested?.property ?? defaultValue
const title = titles.data?.[0]?.textContent
const item = matrix?.[row]?.[col]?.value
// Reserved keywords as property names (JavaScript-compatible)
const result = Array.from([1, 2, 3]) // 'from' is a reserved keyword
const config = {
import: "module",
class: "MyClass",
from: "source"
}
const source = config.from // Access reserved keyword properties
// Spread operator
const combined = [...array1, ...array2]
const merged = { ...obj1, ...obj2 }
// Arrow functions
const double = x => x * 2
const add = (a, b) => a + b
// Regular expression literals with all JavaScript flags
const emailPattern = /^[^@]+@[^@]+\.[^@]+$/
const phoneRegex = /\(\d{3}\)\s\d{3}-\d{4}/g
const unicodePattern = /[\u{1F600}-\u{1F64F}]/gu // Emoji with unicode flag
const multilineText = /^start.*end$/ms; // Multiline and dotAll flags
// Regex methods work seamlessly
const text = "Contact: user@domain.com or call (555) 123-4567"
const emails = text.match(/\w+@\w+\.\w+/g) // ["user@domain.com"]
const hasPhone = /\(\d{3}\)/.test(text); // true
const cleaned = text.replace(/\d+/g, "XXX"); // Replace all digits
// Ternary conditional operator
const status = age >= 18 ? "adult" : "minor"
const value = condition ? (nested ? 1 : 2) : 3
// Async/await
async function fetchData() {
const response = await fetch(url)
return await response.json()
}
Full object-oriented programming support with inheritance and proper this
binding:
class Animal {
constructor(name) {
this.name = name
}
speak() {
return this.name + " makes a sound"
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name) // Call parent constructor
this.breed = breed
}
speak() {
return this.name + " barks"
}
getBreed() {
return this.breed
}
}
const dog = new Dog("Max", "Golden Retriever")
log(dog.speak()) // "Max barks"
log(dog.getBreed()); // "Golden Retriever"
Wang returns the last evaluated expression, making it perfect for REPL usage and functional workflows:
// Simple expression return
const sum = await interpreter.execute(`
let x = 5
let y = 10
x + y // Returns 15
`)
// Object construction return
const config = await interpreter.execute(`
const env = "production"
const port = 3000
// This object is returned
{ env, port, debug: false }
`)
// Functional programming with method chaining
const result = await interpreter.execute(`
[1, 2, 3, 4, 5]
.filter(n => n > 2)
.map(n => n * 2)
.reduce((sum, n) => sum + n, 0)
`)
// result = 24
Wang provides full support for JavaScript's method chaining and functional programming patterns:
// Array method chaining
const result = data
.filter(x => x > 0)
.map(x => x * 2)
.reduce((sum, x) => sum + x, 0)
// Complex data transformations
const users = [
{name: "Alice", scores: [80, 90, 85]},
{name: "Bob", scores: [75, 85, 95]}
]
const averages = users.map(user => ({
name: user.name,
avg: user.scores.reduce((a, b) => a + b, 0) / user.scores.length
}))
// Multiline method chains with proper indentation
const processed = rawData
.filter(item => item.active)
.map(item => ({ ...item, processed: true }))
.sort((a, b) => a.priority - b.priority)
.slice(0, 10)
// Regular expressions with method chaining
const logData = "ERROR: Failed login\nINFO: Success\nERROR: Database timeout"
const errorCount = logData
.split(/\n/)
.filter(line => line.match(/ERROR:/))
.length; // 2
// Extract and process data with regex
const userEmails = "Contact alice@company.com or bob@startup.org for info"
const domains = userEmails
.match(/(\w+)@(\w+\.\w+)/g)
.map(email => email.split('@')[1])
.filter((v, i, a) => a.indexOf(v) === i) // unique
.sort(); // ["company.com", "startup.org"]
// Method chaining across lines
const builder = new StringBuilder()
.append("Hello")
.append(" ")
.append("World")
.toString()
Full ES6 module support:
// math.wang
export function square(x) {
return x * x
}
export const PI = 3.14159
// main.wang
import { square, PI } from "math"
const area = square(5) * PI
Wang provides a pluggable module resolution system. Implement your own resolver:
import { ModuleResolver } from '@wang-lang/core'
class MyCustomResolver extends ModuleResolver {
async resolve(modulePath, fromPath) {
// Your logic to find and return module code
const code = await fetchModuleFromSomewhere(modulePath)
return { code, path: modulePath }
}
async exists(modulePath) {
// Check if module exists
return await checkIfModuleExists(modulePath)
}
async list(prefix) {
// Return available modules for autocomplete
return await getAvailableModules(prefix)
}
}
const interpreter = new WangInterpreter({
moduleResolver: new MyCustomResolver()
})
- InMemoryModuleResolver - Store modules in memory (great for testing)
- IndexedDBModuleResolver - Persist modules in browser storage
- HTTPModuleResolver - Load modules from URLs
- CompositeModuleResolver - Chain multiple resolvers with fallback
// Define a workflow module
resolver.addModule('linkedin-workflow', `
export async function extractProfiles() {
let profiles = querySelectorAll(".profile-card")
let results = []
for (let profile of profiles) {
let nameEl = querySelector(profile, ".name")
let nameText = getText(nameEl)
let titleEl = querySelector(profile, ".title")
let titleText = getText(titleEl)
let companyEl = querySelector(profile, ".company")
let companyText = getText(companyEl)
let data = {
name: trim(replace(nameText, /[^\w\s]/g, "")), // Clean name
title: match(titleText, /^([^@]+)/)?.[1] || titleText, // Extract title before @
company: trim(replace(companyText, /\s+/g, " ")), // Normalize whitespace
isVerified: querySelector(profile, ".verified-badge") !== null
}
// Skip profiles without email patterns in title/company
if (titleText.match(/@/) || companyText.match(/\.(com|org|net)/i)) {
results.push(data)
}
await wait(1000) // Rate limiting
}
return results
}
`)
// Bind DOM functions
const interpreter = new WangInterpreter({
moduleResolver: resolver,
functions: {
querySelectorAll: (sel) => [...document.querySelectorAll(sel)],
querySelector: (el, sel) => el.querySelector(sel),
getText: (el) => el?.innerText || "",
replace: (str, pattern, replacement) => str.replace(pattern, replacement),
match: (str, pattern) => str.match(pattern),
trim: (str) => str.trim(),
wait: (ms) => new Promise(r => setTimeout(r, ms))
}
})
// Execute the workflow
await interpreter.execute(`
import { extractProfiles } from "linkedin-workflow"
let profiles = await extractProfiles()
log(\`Found \${profiles.length} profiles\`)
`)
Wang has syntax highlighting support for popular code editors:
import { registerWangLanguage } from 'wang-lang/editor/monaco'
// After Monaco loads
registerWangLanguage(monaco)
const editor = monaco.editor.create(container, {
value: wangCode,
language: 'wang',
theme: 'vs-dark'
})
Install the Wang language extension from the VS Code marketplace or build from source:
# Build the extension
cd syntaxes
vsce package
# Install the .vsix file in VS Code
import { wangLanguage } from 'wang-lang/editor/codemirror'
// Use with CodeMirror 6
See SYNTAX_HIGHLIGHTING.md for detailed setup instructions.
Wang includes a PausableWangInterpreter
that extends the base interpreter with pause/resume capabilities and state serialization:
import { PausableWangInterpreter } from 'wang-lang'
const interpreter = new PausableWangInterpreter({
functions: {
fetchData: async (id) => {
// Simulate async work
await new Promise(r => setTimeout(r, 100))
return { id, data: `Data ${id}` }
}
}
})
// Start long-running execution
const promise = interpreter.execute(`
let results = []
for (let i = 1; i <= 100; i = i + 1) {
let data = await fetchData(i)
results.push(data)
}
results
`)
// Pause execution after some time
setTimeout(() => {
if (interpreter.isRunning()) {
interpreter.pause()
console.log('Paused at:', interpreter.getCurrentVariables())
}
}, 500)
// Resume later
if (interpreter.isPaused()) {
const result = await interpreter.resume()
console.log('Completed:', result)
}
Save and restore the complete interpreter state:
// Execute some code to build state
await interpreter.execute(`
let gameState = {
player: { name: "Alice", score: 100 },
level: 5,
inventory: ["sword", "shield"]
}
function updateScore(points) {
gameState.player.score += points
}
`)
// Start a process
const promise = interpreter.execute(`
for (let round = 1; round <= 20; round = round + 1) {
updateScore(round * 10)
if (round === 10) {
log("Checkpoint!")
}
}
gameState
`)
// Pause at checkpoint
setTimeout(() => interpreter.pause(), 200)
// Save state to JSON
if (interpreter.isPaused()) {
const serialized = interpreter.serialize()
// Save to file, database, etc.
await saveToFile('game-state.json', serialized)
// Later, restore from saved state
const savedState = await loadFromFile('game-state.json')
const restored = await PausableWangInterpreter.deserialize(savedState, {
functions: { /* re-bind custom functions */ }
})
// Continue execution from saved point
const result = await restored.resume()
}
Monitor and control execution:
// Check execution state
interpreter.isRunning() // true during execution
interpreter.isPaused() // true when paused
interpreter.isCompleted() // true after completion
interpreter.hasError() // true if error occurred
// Get execution information
interpreter.getExecutionState() // Full state object
interpreter.getCallStackTrace() // Current call stack
interpreter.getCurrentVariables() // All accessible variables
// Control execution
interpreter.pause() // Request pause at next checkpoint
interpreter.resume() // Continue from pause point
Wang provides a comprehensive metadata API that captures and exposes compilation, interpretation, and execution data:
import { WangInterpreter } from 'wang-lang'
import { MetadataCollector } from 'wang-lang/metadata'
// Create interpreter with metadata collection
const collector = new MetadataCollector()
const interpreter = new WangInterpreter({
onNodeVisit: (node, depth) => collector.onNodeVisit(node, depth),
onFunctionCall: (name, args, node) => collector.onFunctionCall(name, args, node),
onVariableAccess: (name, type, value) => collector.onVariableAccess(name, type, value)
})
// Execute code with metadata collection
collector.onExecutionStart()
await interpreter.execute(code)
collector.onExecutionEnd()
// Get comprehensive metadata
const metadata = collector.getMetadata()
// Query execution insights
console.log('Hot functions:', metadata.getHotFunctions(5))
console.log('Variable access patterns:', metadata.getHotVariables(5))
console.log('Execution path:', metadata.getExecutionPath())
console.log('Performance summary:', metadata.getExecutionSummary())
// Export for external tools
const json = collector.export()
- Compilation Phase: Tokens, AST nodes, parse timing, source mapping
- Interpretation Phase: Module resolution, symbol tables, dependency graphs
- Execution Phase: Call tracking, variable access, control flow, method chaining
- Runtime Data: Live variables, execution path, current position, event stream
Wang supports all core JavaScript features for workflow automation:
- Variables & Scoping:
let
,const
,var
with proper hoisting and block scoping - Functions: Regular functions, arrow functions, async/await, closures, recursion
- Classes: Constructors, methods, inheritance with
super()
, static methods, getters/setters - Control Flow:
if/else
, loops (for
,for-in
,for-of
,while
,do-while
),try/catch/finally
, ternary operator (? :
) - Operators: All standard JavaScript operators - arithmetic, comparison, logical, increment/decrement (
++
,--
), compound assignment (+=
,-=
,*=
,/=
), ternary (? :
) - Regular Expressions: Full regex literal syntax (
/pattern/flags
) with all JavaScript flags (g
,i
,m
,s
,u
,y
) - Data Types: Objects, arrays, destructuring, template literals, spread/rest parameters, JSON-like multiline objects
- Modules: Named imports/exports (
import { name } from "module"
) - Async: Promises, async/await, error handling
- Built-ins: Error constructor, type conversion functions, array methods, native constructor support (KeyboardEvent, MouseEvent, etc.)
Some advanced JavaScript features are intentionally unsupported to maintain implementation simplicity. See UNSUPPORTED_FEATURES.md
for full details:
- Private fields (
#field
) - Use_private
naming conventions - Default imports (
import name from
) - Use named imports instead - Async generators (
async function*
) - Use regular async functions - Tagged templates (
tag`template`
) - Use function calls instead - Destructuring defaults in parameters - Handle defaults manually
All unsupported features have clear workarounds using supported syntax.
Wang achieves 99.6% test coverage with comprehensive testing:
# Run all tests (569/571 passing)
npm test
# Watch mode for development
npm test:watch
# Generate coverage report
npm test:coverage
# Run tests with UI
npm test:ui
Test Results: 569/571 tests passing (99.6% coverage), including:
- Comprehensive language features (classes, async/await, modules)
- Advanced method chaining and functional programming
- Full regular expression support with all JavaScript features
- Full standard library coverage (70+ functions)
- Compound assignment and increment/decrement operators
- Edge cases and error handling
# Install dependencies
npm install
# Build the grammar (Nearley parser)
npm run build:grammar
# Build the package (ESM, CJS, and browser bundles)
npm run build
# Run tests
npm test
# Run linter
npm run lint
# Format code
npm run format
# Type checking
npm run typecheck
# Generate documentation
npm run docs
wang/
βββ src/
β βββ grammar/ # Nearley grammar definition
β β βββ wang.ne # Grammar rules (zero ambiguity!)
β βββ interpreter/ # Core interpreter
β β βββ index.ts # Main interpreter with proper this binding
β βββ resolvers/ # Module resolvers
β β βββ memory.ts # In-memory resolver
β β βββ indexeddb.ts # Browser storage resolver
β β βββ http.ts # HTTP resolver
β βββ utils/ # Utilities
β βββ errors.ts # Comprehensive error handling
βββ tests/
β βββ unit/ # Unit tests
β β βββ parser.test.js # Parser tests (13 tests)
β β βββ interpreter.test.js # Interpreter tests (25 tests)
β βββ e2e/ # End-to-end tests
β β βββ language-features.test.js # Comprehensive language tests (48 tests)
β βββ test-utils.js # Test utilities
βββ dist/ # Built files
β βββ esm/ # ES modules
β βββ cjs/ # CommonJS
β βββ browser/ # Browser bundle
βββ vitest.config.js # Vitest configuration
The Wang grammar is completely deterministic with zero ambiguity:
- Requires semicolons for statement separation (no ASI ambiguity)
- Separate expression rules for different contexts
- Proper precedence and associativity
- Clean separation between statements and expressions
- Proper typeof operator handling for hoisted variables
- Memory-efficient circular module dependency resolution
Wang is designed to run in Content Security Policy restricted environments where eval()
and new Function()
are blocked:
<!-- Strict CSP that blocks eval -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
<script type="module">
import { WangInterpreter } from '@wang-lang/core'
// This works even with strict CSP!
const interpreter = new WangInterpreter()
await interpreter.execute('log("Hello from Wang!")')
</script>
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Works in:
- β Browser main thread
- β Web Workers
- β Service Workers
- β Chrome Extensions (with CSP)
- β Electron apps
- Zero-ambiguity grammar ensures fast, predictable parsing
- No code generation means no JIT compilation overhead
- Direct AST interpretation for consistent performance
- Module caching for efficient imports
The main interpreter class.
class WangInterpreter {
constructor(options?: {
moduleResolver?: ModuleResolver
functions?: Record<string, Function>
globalContext?: ExecutionContext
})
// Default: returns execution result
execute(code: string, context?: ExecutionContext): Promise<any>
// With metadata: returns result and captured console logs (v0.12.0+)
execute(code: string, context?: ExecutionContext, options?: { withMetadata: true }):
Promise<{ result: any; metadata: { logs: ConsoleLog[] } }>
bindFunction(name: string, fn: Function): void
setVariable(name: string, value: any): void // v0.11.1+
}
The execute()
method can capture all console output from Wang code when using the withMetadata
option:
const interpreter = new WangInterpreter()
// Capture console logs with metadata
const { result, metadata } = await interpreter.execute(`
log("Processing started")
warn("Low memory")
error("Failed to connect")
let data = [1, 2, 3]
log("Data:", data)
data.reduce((sum, n) => sum + n, 0)
`, undefined, { withMetadata: true })
console.log(result) // 6
console.log(metadata.logs)
// [
// { type: 'log', args: ['Processing started'], timestamp: 1234567890 },
// { type: 'warn', args: ['Low memory'], timestamp: 1234567891 },
// { type: 'error', args: ['Failed to connect'], timestamp: 1234567892 },
// { type: 'log', args: ['Data:', [1, 2, 3]], timestamp: 1234567893 }
// ]
// Process captured logs
metadata.logs.forEach(log => {
switch(log.type) {
case 'error':
// Handle errors
break
case 'warn':
// Handle warnings
break
case 'log':
// Handle info logs
break
}
})
// Default behavior (backward compatible) - no metadata
const result2 = await interpreter.execute(`log("Hello"); 42`)
console.log(result2) // 42
The setVariable()
method (v0.11.1+) allows you to inject JavaScript objects and values directly into Wang's global scope:
const interpreter = new WangInterpreter()
// Set JavaScript built-in objects
interpreter.setVariable('Math', Math)
interpreter.setVariable('JSON', JSON)
interpreter.setVariable('Object', Object)
interpreter.setVariable('Array', Array)
interpreter.setVariable('console', console)
// Set custom objects
interpreter.setVariable('myAPI', {
baseURL: 'https://api.example.com',
getUser: (id) => fetch(`/users/${id}`),
data: [1, 2, 3]
})
// Now accessible in Wang code
await interpreter.execute(`
let x = Math.abs(-10)
let data = JSON.stringify({ value: 42 })
let keys = Object.keys(myAPI)
console.log("API URL:", myAPI.baseURL)
`)
A lightweight parser and syntax validator that validates Wang code without executing it. Perfect for IDE integrations, linting, and syntax checking.
class WangValidator {
validate(code: string, options?: ParserOptions): ValidationResult
checkSyntaxPatterns(code: string): SyntaxPatterns
suggestFixes(code: string): string[]
}
interface ValidationResult {
valid: boolean
error?: {
message: string
line: number
column: number
suggestion?: string
}
ast?: any // Optional AST when includeAST: true
}
interface ParserOptions {
includeAST?: boolean // Include the parsed AST in result
}
import { WangValidator } from 'wang-lang'
const validator = new WangValidator()
// Simple validation
const result = validator.validate(`
let x = 10
double(log(x))
`)
if (result.valid) {
console.log("Code is valid!")
} else {
console.error(`Error at line ${result.error.line}, col ${result.error.column}:`)
console.error(result.error.message)
if (result.error.suggestion) {
console.log("Suggestion:", result.error.suggestion)
}
}
// Get AST for further analysis
const resultWithAST = validator.validate(code, { includeAST: true })
if (resultWithAST.valid) {
console.log("AST:", resultWithAST.ast)
}
// Check for specific syntax patterns
const patterns = validator.checkSyntaxPatterns(code)
console.log("Has method chains:", patterns.hasMethodChains)
console.log("Has async/await:", patterns.hasAsyncAwait)
console.log("Has classes:", patterns.hasClasses)
// Get suggestions for common issues
const suggestions = validator.suggestFixes(code)
suggestions.forEach(suggestion => console.log("Tip:", suggestion))
The validator provides detailed error messages with visual context:
const result = validator.validate(`
let x = 10
x.
`)
// Output:
// Parse error: Syntax error at line 3 col 6:
//
// 1
// 2 let x = 10
// 3 x.
// ^
// Unexpected NL token. Instead, I was expecting to see one of the following:
//
// A function name token based on:
// MethodCall β Object "." β MethodName
// ... and more
The validator automatically detects and suggests fixes for common issues:
- Regex in HTML contexts: Suggests escaping forward slashes in patterns like
</a>
- Multiline arrow functions: Detects missing braces in multiline arrow function bodies
- Missing operators: Identifies missing commas, semicolons, or operators between statements
- Method chaining issues: Ensures method calls are properly formatted
Base class for implementing module resolution.
abstract class ModuleResolver {
abstract resolve(modulePath: string, fromPath?: string): Promise<ModuleResolution>
abstract exists(modulePath: string, fromPath?: string): Promise<boolean>
abstract list(prefix?: string): Promise<string[]>
}
Wang's standard library provides 70+ functions organized into logical categories. All functions are immutable and designed for functional programming:
// Core operations
filter(arr, predicate) // Filter elements
map(arr, mapper) // Transform elements
reduce(arr, reducer, initial) // Reduce to single value
forEach(arr, fn) // Iterate (side effects)
find(arr, predicate) // Find first match
some(arr, predicate) // Test if any match
every(arr, predicate) // Test if all match
// Sorting and ordering
sort(arr, compareFn?) // Sort array
sort_by(arr, property) // Sort by property
reverse(arr) // Reverse order
// Array manipulation
slice(arr, start, end) // Extract portion
concat(...arrays) // Combine arrays
flatten(arr, depth?) // Flatten nested arrays
chunk(arr, size) // Split into chunks
zip(...arrays) // Combine arrays element-wise
partition(arr, predicate) // Split based on condition
// Unique and grouping
unique(arr) // Remove duplicates
unique_by(arr, property) // Remove duplicates by property
group_by(arr, property) // Group by property
// Utilities
compact(arr) // Remove falsy values
at(arr, index) // Safe array access
join(arr, separator) // Join to string
includes(arr, item) // Check inclusion
indexOf(arr, item) // Find index
// Property manipulation
pick(obj, keys) // Select specific properties
omit(obj, keys) // Remove specific properties
merge(...objects) // Deep merge objects
clone(obj) // Deep clone object
// Property access
get(obj, path) // Safe nested access: get(obj, "a.b.c")
set(obj, path, value) // Set nested property
has(obj, path) // Check if property exists
// Object inspection
keys(obj) // Get object keys
values(obj) // Get object values
entries(obj) // Get [key, value] pairs
// Case transformation
upper(str) // Convert to uppercase
lower(str) // Convert to lowercase
capitalize(str) // Capitalize first letter
// String manipulation
trim(str) // Remove whitespace
split(str, separator) // Split to array
join(arr, separator) // Join array to string
replace_all(str, search, replace) // Replace all occurrences
// String testing
starts_with(str, prefix) // Check prefix
ends_with(str, suffix) // Check suffix
truncate(str, length) // Truncate with ellipsis
// Basic math
sum(numbers) // Sum all numbers
avg(numbers) // Average
median(numbers) // Median value
min(...nums) // Minimum
max(...nums) // Maximum
// Advanced math
clamp(value, min, max) // Constrain value to range
random_int(min, max) // Random integer in range
abs(n), ceil(n), floor(n), round(n) // Standard math functions
pow(base, exp), sqrt(n) // Power and square root
is_array(val) // Check if array
is_object(val) // Check if object (not array/null)
is_string(val) // Check if string
is_number(val) // Check if number
is_boolean(val) // Check if boolean
is_null(val) // Check if null
is_empty(val) // Check if empty (array/object/string)
count(arr, predicate?) // Count elements (optionally matching predicate)
find_index(arr, predicate) // Find index of first match
range(n) // Generate array [0, 1, ..., n-1]
uuid() // Generate UUID v4
sleep(ms) // Async sleep (returns Promise)
to_json(obj) // Convert to JSON string
from_json(str) // Parse JSON string
encode_base64(str) // Base64 encode
decode_base64(str) // Base64 decode
log(...args) // Console log
error(...args) // Console error
warn(...args) // Console warn
All functions follow snake_case naming and are immutable - they return new values without modifying inputs.
- Classes: Constructors, methods, inheritance with
extends
andsuper()
, static methods, getters/setters - Method Chaining: Cross-line method chaining with proper continuation
- Variable Declarations:
let
,const
,var
with proper hoisting and block scoping - Functions: Regular functions, arrow functions, async/await, closures, recursion, default parameters
- Control Flow:
if/else
, loops (for
,while
,do-while
,for...of
,for...in
),switch
statements,try/catch/finally
- Operators:
- Arithmetic (
+
,-
,*
,/
,%
,**
) - Comparison (
==
,!=
,===
,!==
,<
,>
,<=
,>=
) - Logical (
&&
,||
,!
) - Increment/Decrement (
++
,--
) with prefix/postfix support - Compound Assignment (
+=
,-=
,*=
,/=
) - Ternary Conditional (
? :
) with nested support - Optional Chaining (
?.
) with computed member access (?.[expression]
) and Nullish Coalescing (??
)
- Arithmetic (
- Data Types: Arrays, objects, destructuring, template literals, spread/rest operators
- Module System: Named imports/exports with ES6 syntax
- Error Handling: Comprehensive try/catch/finally with proper error propagation
- Regular Expressions: Full regex literal support (
/pattern/flags
) with all JavaScript flags and methods - Advanced Features: Method chaining, labeled statements,
new
operator, properthis
binding - Standard Library: 70+ built-in functions for arrays, objects, strings, math, and utilities
- Destructuring (works in most contexts except function parameters)
- Arrow function
this
preservation in closures (works in methods but not fully in nested closures)
- Private fields (#field syntax)
- Async generators
- Default exports/imports
- Namespace imports (import * as)
- Re-exports
- Tagged template literals
- Destructuring in function parameters
- Spread syntax in expressions (e.g.,
...array
in function calls)
MIT Β© 2024
Contributions are welcome! Please ensure:
- All tests pass (
npm test
) - Grammar remains deterministic (zero ambiguity)
- Code is CSP-safe (no eval/new Function)
- TypeScript types are updated