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!
eval()
or new Function()
, runs safely in Chrome service workersdata |> filter() |> map()
), nested pipelines, and multiline supportthis
bindingcondition ? true : false
expressions+=
, -=
, *=
, /=
) with zero-ambiguity grammar/pattern/gimsuy
)from
, import
, class
as property names (JavaScript-compatible)`Hello, ${name}!`
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 pipelines and require no imports:
// Array operations (immutable)
[3, 1, 4, 1, 5, 9, 2, 6]
|> unique(_) // [3, 1, 4, 5, 9, 2, 6] - Remove duplicates
|> sort(_) // [1, 2, 3, 4, 5, 6, 9] - Sort ascending
|> chunk(_, 2) // [[1,2], [3,4], [5,6], [9]] - Group into pairs
|> map(_, 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 }
]
users
|> filter(_, u => u.active) // Only active users
|> sort_by(_, "age") // Sort by age property
|> map(_, u => u.name) // Extract names
|> join(_, ", ") // "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
" hello world "
|> trim(_) // "hello world" - Remove whitespace
|> upper(_) // "HELLO WORLD" - Uppercase
|> replace_all(_, "O", "0") // "HELL0 W0RLD" - Replace all O's
|> split(_, " ") // ["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 }
`)
// Pipeline result return
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 powerful pipeline operators for elegant data transformation with full support for chaining, nesting, and multiline expressions:
// Pipe operator (|>) - passes result as first argument with _ placeholder
const result = data
|> filter(_, x => x > 0)
|> map(_, x => x * 2)
|> reduce(_, (sum, x) => sum + x, 0)
// Chained pipelines on same line (fully supported!)
const processed = data |> filter(_, active) |> map(_, transform) |> sort(_)
// Nested pipelines work perfectly on single lines
const groups = [[1, 2], [3, 4], [5, 6]]
const doubled = groups |> map(_, group => group |> map(_, x => x * 2))
// Result: [[2, 4], [6, 8], [10, 12]]
// Complex nested pipelines with calculations
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) |> (sum => sum / user.scores.length)
}))
// Multiline pipelines 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)
// Arrow operator (->) - passes result to function or stores
profiles
|> extractData(_)
-> saveToDatabase("profiles")
// Regular expressions work perfectly with pipelines
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])
|> unique(_)
|> sort(_); // ["company.com", "startup.org"]
// Method chaining across lines
const builder = new StringBuilder()
.append("Hello")
.append(" ")
.append("World")
.toString()
// Nested pipelines with explicit blocks (workaround for multiline)
const result = groups |> map(_, group => {
return group
|> filter(_, x => x > 2)
|> map(_, x => x * 10)
|> reduce(_, (a, b) => a + b, 0)
})
Pipeline Limitations:
return
)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()
})
// 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 nameText = profile |> querySelector(_, ".name") |> getText(_)
let titleText = profile |> querySelector(_, ".title") |> getText(_)
let companyText = profile |> querySelector(_, ".company") |> getText(_)
let data = {
name: nameText |> replace(_, /[^\w\s]/g, "") |> trim(_), // Clean name
title: titleText |> match(_, /^([^@]+)/)?.[1] || titleText, // Extract title before @
company: companyText |> replace(_, /\s+/g, " ") |> trim(_), // Normalize whitespace
isVerified: profile |> querySelector(_, ".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()
Wang supports all core JavaScript features for workflow automation:
let
, const
, var
with proper hoisting and block scopingsuper()
, static methods, getters/settersif/else
, loops (for
, for-in
, for-of
, while
, do-while
), try/catch/finally
, ternary operator (? :
)++
, --
), compound assignment (+=
, -=
, *=
, /=
), ternary (? :
), and pipeline operators (|>
, ->
)/pattern/flags
) with all JavaScript flags (g
, i
, m
, s
, u
, y
)import { name } from "module"
)Some advanced JavaScript features are intentionally unsupported to maintain implementation simplicity. See UNSUPPORTED_FEATURES.md
for full details:
#field
) - Use _private
naming conventionsimport name from
) - Use named imports insteadasync function*
) - Use regular async functionstag`template`
) - Use function calls insteadAll 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:
# 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:
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>
Works in:
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
x |> double |> log
`)
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 pipelines:", patterns.hasPipelines)
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:
// PipelineOperator โ _ "|>" โ FunctionName
// ... and more
The validator automatically detects and suggests fixes for common issues:
</a>
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 pipeline-friendly:
// 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.
extends
and super()
, static methods, getters/setters|>
(pipe) and ->
(arrow) with multiline supportlet
, const
, var
with proper hoisting and block scopingif/else
, loops (for
, while
, do-while
, for...of
, for...in
), switch
statements, try/catch/finally
+
, -
, *
, /
, %
, **
)==
, !=
, ===
, !==
, <
, >
, <=
, >=
)&&
, ||
, !
)++
, --
) with prefix/postfix support+=
, -=
, *=
, /=
)? :
) with nested support?.
) with computed member access (?.[expression]
) and Nullish Coalescing (??
)/pattern/flags
) with all JavaScript flags and methodsnew
operator, proper this
bindingthis
preservation in closures (works in methods but not fully in nested closures)...array
in function calls)MIT ยฉ 2024
Contributions are welcome! Please ensure:
npm test
)