@mehran-ul-islam/ez-lang
v0.5.0-development.1
Published
Ez - a JS/Python-flavored scripting language that runs on Node
Maintainers
Readme
Ez
A small scripting language that blends JS-style braces with Python-style def-ish
function syntax, built to run on Node. Files use the .ez extension.
Running a script
node index.js path/to/script.ezOr install it as a global CLI:
npm install -g .
ez path/to/script.ezLanguage guide
Variables
Only let — no var.
let name = "Mehran"
let count = 3Printing
print.console("logs like console.log")
print.alert("goes to stderr, like a browser alert")Functions
Define with function "Name" = { ... }. A zero-arg function can be invoked
just by writing its bare name as a statement. Functions with parameters are
called normally with parens.
function "Hello" = {
print.console("Hello! world")
}
Hello // calls it, zero-arg style
function "square"(x) = {
return x * x
}
print.console(square(6)) // 36Functions can recurse and return values normally with return.
Control flow
if (x < 10) {
print.console("small")
} else {
print.console("big")
}
while (i < 3) {
print.console(i)
i = i + 1
}
for (let j = 0; j < 3; j = j + 1) {
print.console(j)
}Arrays
let nums = [1, 2, 3]
print.console(nums[0])
nums[0] = 99Input (reads a line from the terminal)
let n = input.from("your-name")
print.console("Hi " + n)input.from("id") is a terminal stand-in for the web idea of
document.getElementById — it can't reach into a real DOM from a CLI script
(there isn't one), so it prompts on stdin instead, labeled by the id you pass.
Objects
let person = { name: "Mehran", age: 20 }
print.console(person.name)
person.age = 21Array methods
let nums = [3, 1, 2]
nums.push(4) // -> 4 (new length)
nums.sort()
print.console(nums) // [1, 2, 3, 4]
print.console(nums.includes(2)) // true
print.console(nums.indexOf(2)) // 1
print.console(nums.join("-")) // "1-2-3-4"
print.console(nums.length)Also available: .pop(), .slice(start, end), .reverse().
String methods
let s = " Hello World "
print.console(s.trim().toUpperCase())
print.console(s.trim().split(" "))
print.console(s.includes("World"))
print.console(s.length)Also available: .toLowerCase(), .replace(a, b), .indexOf(sub), .slice(start, end), .charAt(i).
Objects, arrays, and strings share .length as a plain property (no parens) — everything else above is a method call.
try/catch
try {
let x = somethingThatDoesntExist
} catch (err) {
print.console("caught: " + err)
}Importing other .ez files
import "mathlib.ez"
print.console(square(5))Paths resolve relative to the importing file. Each file is only imported
once even if multiple files import the same thing. (This is a CLI-only
feature — in the browser, use multiple <script type="text/ez"> tags
instead, which already share one global scope.)
axios-style HTTP requests
let res = axios.get("https://api.example.com/users")
print.console(res.status)
print.console(res.data)
let created = axios.post("https://api.example.com/users", { name: "Mehran" })
axios.put("https://api.example.com/users/1", { name: "Updated" })
axios.delete("https://api.example.com/users/1")Every axios.* call and get() also accepts an optional callback as the
last argument, which makes the request non-blocking:
function "onDone"(res) = {
print.console("got it: " + res.data)
}
get("https://api.example.com/thing", onDone)
print.console("this line runs immediately, before the response arrives")Without a callback, both get() and axios.* still block until the
response comes back, same as before.
REPL
Run ez with no file argument to drop into an interactive prompt:
$ ez
Ez REPL -- type Ez code, .exit to quit
ez> let x = 5
ez> x + 10
15
ez> .exitRunning in a browser (replacing inline JS)
There's also a browser build at browser/ez.js that lets .ez code run directly
inside an HTML page — no Node needed on the visitor's end.
<script src="ez.js"></script>
<script type="text/ez">
function "Hello" = {
print.console("Ez loaded and running in the browser!")
}
Hello
</script>Drop <script type="text/ez"> tags anywhere you'd normally put a JS <script>
tag — inline, or with src="somefile.ez". They run in order, top to bottom,
same as regular scripts, and share one global scope across all of them on
the page.
In the browser, the built-ins behave differently than the CLI version, in ways that make more sense for a webpage:
input.from("id")→ now genuinely doesdocument.getElementById("id").value, reading a real form field off the pageprint.alert(x)→ a realwindow.alert(x)popupprint.console(x)→console.log(x), shows up in devtools like normalget(url)→ a real HTTP GET, still made to look blocking/synchronous (Ez has noasync/await). Under the hood this uses a deprecated synchronousXMLHttpRequest, which freezes page interaction while the request is in flight — fine for demos and small tools, not something to build a production site's data-fetching around.
Wiring up HTML events
Ez doesn't have its own onclick-style syntax yet, so call an Ez function
straight from HTML using the EzRuntime bridge that ez.js exposes:
<input id="nameBox" type="text">
<button onclick="EzRuntime.call('greet')">Greet me</button>
<script type="text/ez">
function "greet" = {
let name = input.from("nameBox")
print.console("Hi " + name)
}
</script>See browser/example.html for a full working page with this pattern.
lexer.js— turns source text into tokensparser.js— recursive-descent parser, tokens → ASTinterpreter.js— tree-walking evaluator, AST → running programhttp-get-helper.js— child-process helper soget()can look synchronousindex.js— CLI entry pointexamples/— sample.ezscripts
What's next (not built yet)
- Real browser/DOM support (
input.fromactually reaching a webpage) would need a separate transpile-to-JS mode, since a Node CLI script has no DOM. - Objects/dictionaries currently only exist as a runtime type produced by
parsed JSON — there's no
{ key: value }literal syntax yet. - No module/import system yet for splitting a project across multiple
.ezfiles.
