Language reference
Documentation
Getting started
Install the Humal compiler globally with npm:
npm install -g @qvelop444/humal
Create a file named main.hum:
print "Hello World!"
Run the file with:
humal main.hum
Humal compiles the source file to JavaScript and runs it with Node.js.
Text output
Use print to output a value to the console.
Parentheses are optional for simple output.
print "Hello World!"
print 2 + 2
Variables
Declare variables with let. Humal keeps the familiar
JavaScript semantics of let.
let name = "Bob"
let age = 25
let city = "Warsaw"
print name
print age
print city
Expressions
Humal keeps JavaScript expressions familiar, including arithmetic, function calls, indexing, property access and method calls.
let a = 10
let b = 5
let result = (a + b) * 2
print result
print Math.floor(10 / 3)
Operators
Humal supports familiar JavaScript arithmetic, comparison and logical operators.
let a = 10
let b = 5
print a + b
print a - b
print a * b
print a / b
print a % b
print a > b
print a < b
print a >= b
print a == b
print a != b
Logical operators
let age = 20
let active = true
let enabled = false
if age >= 18 && active
print "AND works"
if active || enabled
print "OR works"
if !enabled
print "NOT works"
Ternary operator
let age = 20
let status = age >= 18 ? "Adult" : "Minor"
print status
Nullish coalescing
let nickname = null
print nickname ?? "Unknown"
Optional chaining
let user = { name: "Bob" }
print user?.name
print user?.profile?.name
Strings
Strings can use single or double quotation marks.
let single = 'Hello'
let double = "World"
print single
print double
JavaScript string methods and properties remain available.
let text = "Hello Humal"
print text.length
print text.toUpperCase()
print text.split(" ")
Functions
Define functions with fn. The function body is determined by indentation.
fn greet(name)
print "Hello " + name
greet("Bob")
greet("Alex")
Function syntax without parentheses
Humal also supports a shorter function declaration syntax without parentheses.
fn add a, b
return a + b
print add(10, 20)
Default parameters
fn welcome(name = "World")
print "Welcome " + name
welcome()
welcome("Humal")
Rest parameters
fn sumAll(...numbers)
return numbers.reduce((a, b) => a + b, 0)
print sumAll(1, 2, 3, 4)
Arrow functions
let multiply = (a, b) => a * b
print multiply(5, 6)
Conditions
Humal uses indentation instead of curly braces for conditional blocks. Parentheses are not required around the condition.
let age = 20
if age >= 18
print "Adult"
else
print "Underage"
Multiple conditions
let score = 75
if score >= 90
print "Excellent"
elif score >= 70
print "Good"
elif score >= 50
print "Pass"
else
print "Fail"
Input
input reads a value from the terminal. The Humal implementation
uses the readline-sync package.
Install the dependency with:
npm install readline-sync
Then use:
let name = input "What is your name? "
print "Hello " + name
Loops
while
Use while to repeat an indented block while a condition is true.
let i = 0
while i < 5
print i
i++
for ... in
let numbers = [1, 2, 3, 4, 5]
for number in numbers
print number
break
let counter = 0
while counter < 10
if counter == 3
break
print counter
counter++
continue
let numbers = [1, 2, 3, 4, 5]
for number in numbers
if number == 3
continue
print number
Arrays
Arrays use familiar JavaScript syntax and methods.
let numbers = [10, 20, 30]
print numbers[0]
print numbers.length
numbers.push(40)
print numbers
Objects
Object literals keep JavaScript's curly braces because the braces describe the object itself, not a code block.
let user = { name: "Bob", age: 25 }
print user.name
print user.age
Object spread
let user = { name: "Bob", age: 25 }
let updatedUser = { ...user, age: 30 }
print updatedUser.age
Destructuring
Object destructuring
Curly braces are required because they identify the object properties being extracted.
let user = { name: "Bob", age: 25 }
let { name: userName, age: userAge } = user
print userName
print userAge
Array destructuring
let numbers = [10, 20, 30]
let [first, second, third] = numbers
print first
print second
print third
Modern JavaScript expressions
Humal keeps JavaScript expressions familiar, so modern JavaScript operators and expressions can be used directly when supported by the generated target.
Optional chaining
let user = { name: "Bob" }
print user?.name
print user?.profile?.name
The second expression safely produces undefined when profile does not exist.
Nullish coalescing
let name = null
print name ?? "Unknown"
Spread syntax
let numbers = [10, 20, 30]
let moreNumbers = [...numbers, 40, 50]
print moreNumbers.length
Async
Humal supports async fn, await and return
for asynchronous JavaScript code.
async fn getValue()
let value = await Promise.resolve(42)
return value
async fn runAsync()
let value = await getValue()
print value
runAsync()
Error handling
Humal supports try, catch and throw
using indentation for the blocks.
try
throw new Error("Boom")
catch
print "Error handled"
The current compiler and TypeScript validation should be considered when
working with caught error values. The documented example intentionally uses
a catch block without an error binding.
Modules
Humal is designed to work with both ESM and CommonJS modules and with existing Node.js packages.
CommonJS
let crypto = require("crypto")
let hash = crypto.createHash("sha256")
hash.update("Hello World!")
let result = hash.digest("hex")
print result
ESM import
import path from "path"
print path.basename("/tmp/hello.txt")
Named ESM import
import { readFileSync } from "fs"
let data = readFileSync("package.json", "utf8")
print data
Namespace import
import * as path from "path"
print path.basename("/tmp/hello.txt")
JavaScript interoperability
Humal compiles to JavaScript, so Node.js APIs and JavaScript libraries can be used from Humal projects.
Node.js crypto
let crypto = require("crypto")
let hash = crypto.createHash("sha256")
hash.update("Hello World!")
let result = hash.digest("hex")
print result
Node.js fs
let fs = require("fs")
fs.writeFileSync("hello.txt", "Hello from Humal!")
print "File written!"
Node.js os
let os = require("os")
print os.platform()
print os.arch()
print os.homedir()
Express
let express = require("express")
let app = express()
fn home(req, res)
res.send("Hello from Humal!")
app.get("/", home)
app.listen(3000)
print "Server running on port 3000"
Test the server from another terminal:
curl http://localhost:3000
CLI
Humal includes a project-oriented CLI for creating, building, checking and developing Humal projects.
Initialize a project
humal init my-project
Build
humal build
Development mode
humal dev
Watch mode
humal watch
Check
humal check
Format
humal fmt
Configuration
Humal projects can use humal.config.json to define the
entry file, source directory and module format.
{
"entry": "src/main.hum",
"sourceDir": "src",
"module": "esm"
}
The module can be configured as esm, cjs
or left unset for automatic behavior.
Formatter
Use humal fmt to normalize Humal source formatting.
humal fmt
The current formatter focuses on source normalization such as line endings, trailing whitespace and the final newline.
Tooling
Humal includes development tooling around the compiler and integrates with the JavaScript ecosystem.
VS Code
The Humal VS Code extension provides support for .hum files,
including syntax highlighting and editor assistance.
Validation
Humal can validate generated JavaScript with the project's TypeScript and
ESLint tooling through humal check and related compiler workflows.
Source maps
Humal provides source-map support so generated JavaScript can remain linked to the original Humal source. This is useful for development and debugging.
Source-map support is part of the compiler/tooling layer rather than a separate Humal language construct.
Humal 1.4.x · Browser development
Humal Web
Build browser-based applications and websites using Humal’s indentation-based syntax. Humal Web provides a browser runtime, DOM manipulation utilities, access to browser APIs, and integration with Parcel.
Overview
Humal Web is a convenience layer over browser functionality. It does not replace the browser or implement every browser API itself.
Quick start
The following instructions use the browser project template included
in the Humal CLI release that supports --env browser.
1. Create a browser project
humal init myapp --env browser
cd myapp
npm install
The initialization command creates a project configuration, a Humal entry file, an HTML entry point, a Parcel configuration and npm scripts.
2. Start development
npm run dev:web
Open the local URL printed by Parcel in your terminal. The default
development address is commonly http://localhost:1234/.
3. Create a production build
npm run build:web
Parcel writes the generated production assets to its output directory,
normally dist/.
Project configuration
The humal.config.json file controls the project's entry
file, source directory, module format and environment.
{
"entry": "src/main.hum",
"sourceDir": "src",
"module": "esm",
"env": "browser",
"webEntry": "index.html"
}
entry— the main Humal source file.sourceDir— the directory containing Humal source files.module— the JavaScript module format.env— selectsbrowserornodetooling behavior.webEntry— the HTML entry point used by Parcel.
Use "env": "browser" for a browser application. It enables
browser-specific tooling behavior, including the web API
type declarations when supported by the installed Humal release.
The env setting does not create a browser by itself.
The browser runtime must still be loaded by the compiled application.
HTML entry point
The HTML page is the entry point for the browser application. It defines the elements that your Humal code will interact with.
A typical generated index.html looks like this:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Humal Web</title>
</head>
<body>
<h1>Humal Web</h1>
<label for="name">Name</label>
<input id="name">
<button id="save">Save</button>
<p id="status">Ready</p>
<script type="module" src="./src/main.hum"></script>
</body>
</html>
The src path is relative to the HTML file. If your HTML
file is in another directory, adjust the path accordingly.
Your first Humal Web program
Create or edit src/main.hum:
web.document.title = "Humal Web"
web.text("#status", "Ready")
fn saveName()
let name = web.value("#name")
web.localstorage.setItem("name", name)
web.text("#status", "Saved: " + name)
web.value("#name", web.localstorage.getItem("name") ?? "")
web.button("#save").onClick(saveName)
This example updates the page title, reads the input value, stores the name in Local Storage and updates the status message when the button is clicked.
DOM helpers
The web namespace provides helpers for common DOM
operations. A target can be a CSS selector or, where supported,
an existing DOM element.
Set text
web.text("#status", "Hello from Humal")
web.text() assigns text content to the target element.
The value is converted to a string.
Read and set input values
let name = web.value("#name")
web.value("#name", "Alex")
Pass one argument to read an element's value, or two arguments to set it. Reading a value is most useful with form controls.
Access an element
let status = web.element("#status")
If a selector does not match an element, the runtime reports an error instead of silently returning a missing element.
Event handling
Use Humal functions as browser event callbacks.
Button events
fn handleClick()
web.text("#status", "Button clicked")
web.button("#save").onClick(handleClick)
The button helper also supports .text(value) and
.disable(flag).
General event listeners
fn handleChange(event)
web.text("#status", event.type)
web.on("#name", "input", handleChange)
Remove an event listener with web.off(). Pass the
same callback function used when the listener was registered.
web.off("#name", "input", handleChange)
Wait for the DOM
fn initialize()
web.text("#status", "Page is ready")
web.ready(initialize)
Browser APIs
The runtime exposes supported browser properties and functions through
the web namespace. The document and
window properties provide direct access to browser objects.
web.document.title = "My Application"
let currentURL = web.location.href
let language = web.navigator.language
let identifier = web.crypto.randomUUID()
web.alert("Hello from Humal")
Browser API methods and properties follow the behavior of the underlying browser API. Permissions, security restrictions and browser support continue to apply.
Selected API aliases
The runtime provides lowercase aliases for several browser constructors and objects:
web.localstorage
web.sessionstorage
web.indexeddb
web.urlsearchparams
web.abortcontroller
web.intersectionobserver
web.mutationobserver
web.resizeobserver
web.websocket
web.sharedworker
web.broadcastchannel
web.textencoder
web.textdecoder
web.formdata
web.filereader
For example, create URL search parameters or encode a string:
let params = new web.urlsearchparams("?name=Humal")
let name = params.get("name")
let encoder = new web.textencoder()
let bytes = encoder.encode("Hello Humal")
print name
print bytes.length
A constructor alias can be used with new. Check the
browser's support before relying on a particular API.
Browser storage
Humal Web exposes Local Storage, Session Storage and IndexedDB through lowercase aliases.
Local Storage
web.localstorage.setItem("theme", "dark")
let theme = web.localstorage.getItem("theme")
if theme == null
web.localstorage.setItem("theme", "light")
Local Storage keeps string values across browser sessions for the
same origin. Use JSON.stringify() and
JSON.parse() when storing structured data.
Session Storage
web.sessionstorage.setItem("step", "2")
let step = web.sessionstorage.getItem("step")
Session Storage is intended for data associated with the current browser tab's session.
IndexedDB
let database = web.indexeddb
IndexedDB is a browser database API. Opening a database, creating object stores and performing transactions follow the standard IndexedDB interface and its asynchronous event model.
Do not store passwords, secret keys or other sensitive credentials in client-side storage.
Async code and network requests
Humal supports async fn and await where
supported by the compiler. These constructs let browser programs
work with asynchronous JavaScript APIs.
async fn loadData()
let response = await web.fetch("/api/data")
if !response.ok
throw new Error("Request failed")
let data = await response.json()
web.text("#status", JSON.stringify(data))
web.button("#load").onClick(loadData)
A Fetch request generally rejects for network errors, but an HTTP
response with a status such as 404 or 500 does not normally reject
automatically. Check response.ok or
response.status.
Handle network failures with try and
catch:
async fn loadData()
try
let response = await web.fetch("/api/data")
if !response.ok
throw new Error("Request failed")
let data = await response.json()
web.text("#status", JSON.stringify(data))
catch
web.text("#status", "Unable to load data")
Browser input
Humal's terminal-oriented input feature normally reads
from the terminal using readline-sync. In a browser
build, the Web integration can replace this input mechanism with
the browser's prompt() dialog.
let name = input "What is your name? "
web.text("#status", "Hello " + name)
Browser input is interactive and blocks while the prompt is open.
For normal web forms, prefer HTML input elements and
web.value().
Parcel integration
Parcel bundles the HTML entry point and its dependencies. Humal's Parcel transformer compiles Humal source files into JavaScript and connects them to the Web runtime.
When using the browser project template generated by a compatible Humal CLI release, Parcel configuration and npm scripts are created for you.
Manual configuration
For an existing project, install Humal and Parcel as development dependencies:
npm install --save-dev parcel @qvelop444/humal
Create a .parcelrc file in the project root:
{
"extends": "@parcel/config-default",
"transformers": {
"*.hum": [
"./node_modules/@qvelop444/humal/src/parcel.js"
]
}
}
The relative path points to the transformer inside the installed Humal package. This is different from naming it as a scoped Parcel plugin package.
Your HTML entry point must reference a Humal source file, and the project must have the relevant Parcel commands configured.
Production builds
Use the browser build command to create production assets:
humal build:web
Or, if the project's npm scripts have been generated, run:
npm run build:web
Review the generated files before deployment. Do not put server credentials or private keys into browser code: assets served to users can be inspected.
Troubleshooting
“Parcel is not installed in this project”
Run npm install from the project directory. The browser
CLI uses the project's local Parcel installation.
“Browser entry file not found”
Check webEntry in humal.config.json and
ensure the HTML file exists at the configured path.
“Cannot find module” for the Parcel transformer
Check that Humal is installed in the project and that
.parcelrc uses the correct relative path to
node_modules/@qvelop444/humal/src/parcel.js.
Then run npm install again.
“Cannot find name 'web'” during checking
Confirm that the project's environment is set to
"env": "browser" and that the installed Humal package
contains types/humal-web.d.ts. The declaration file
provides type information; it does not create the runtime object.
Humal and JavaScript
Humal changes the parts of JavaScript syntax that add unnecessary visual noise, especially block braces and semicolons, while keeping JavaScript expressions, APIs and ecosystem compatibility familiar.
When a construct is meaningful JavaScript syntax, such as an object literal, its braces remain part of the expression.
View Humal on GitHub