Your First GocciaScript Program
For developers who know JavaScript and want to learn GocciaScript — a guided walkthrough from hello world to a multi-file async program.
Executive Summary#
- Familiar syntax — GocciaScript is modern JavaScript minus the quirks;
.jsfiles, no transpilation needed - Key differences — No
var/function/==/loops; uselet/const, arrow functions,===,for...of/array methods - Full walkthrough — Variables, arrow functions, arrays, objects, classes, modules, async/await
- Next steps — Links to language restrictions, built-in API reference, and example programs
What is GocciaScript?#
GocciaScript is a subset of JavaScript implemented in FreePascal. It strips away the quirks of early ECMAScript — var, function, loose equality, eval, traditional loops — and keeps the modern parts: arrow functions, classes with private fields, async/await, modules, and implicit strict mode. If you've written modern JavaScript, you already know most of GocciaScript.
Prerequisites#
You need the FreePascal compiler (fpc):
# macOS
brew install fpc
# Ubuntu/Debian
sudo apt-get install fpc
# Windows
choco install freepascalClone the repository and build the script loader:
git clone https://github.com/frostney/GocciaScript.git
cd GocciaScript
./build.pas loaderThis produces build/GocciaScriptLoader, the command you'll use to run every script in this tutorial.
Hello, World#
Create a file called hello.js:
const message = "Hello from GocciaScript!";
console.log(message);Run it:
./build/GocciaScriptLoader hello.jsYou should see:
Hello from GocciaScript!That's it — GocciaScript files are plain .js files. No special extension, no transpilation step.
Variables#
GocciaScript has two variable declarations: let (mutable) and const (immutable). There is no var.
const name = "Alice";
let score = 0;
score = score + 10;
console.log(`${name} scored ${score} points`);
// Alice scored 10 pointsAttempting to reassign a const throws an error:
const x = 5;
x = 10; // TypeError: Assignment to constant variableArrow Functions#
GocciaScript uses arrow functions exclusively. The function keyword does not exist.
// Single expression — implicit return
const double = (n) => n * 2;
// Block body — explicit return
const greet = (name) => {
const greeting = `Hello, ${name}!`;
return greeting;
};
console.log(double(21)); // 42
console.log(greet("World")); // Hello, World!Arrow functions capture their surrounding scope's this — there's no this rebinding. For methods that need their own this, use shorthand method syntax inside classes or object literals.
Working with Arrays#
GocciaScript has no traditional loops (for, while, do...while). Instead, you use array methods and for...of:
const numbers = [1, 2, 3, 4, 5];
// Transform with map
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// Filter
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4]
// Reduce
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // 15
// Iterate with for...of
for (const n of numbers) {
console.log(n);
}All the modern array methods you'd expect are available: find, findLast, some, every, flat, flatMap, at, toSorted, toReversed, and more. See the Built-in Objects reference for the full list.
Objects#
Object literals work like JavaScript, including shorthand properties, computed keys, and methods:
const x = 10;
const y = 20;
const point = {
x,
y,
distanceTo(other) {
const dx = this.x - other.x;
const dy = this.y - other.y;
return Math.sqrt(dx ** 2 + dy ** 2);
},
};
const origin = { x: 0, y: 0 };
console.log(point.distanceTo(origin)); // 22.360679774997898Note that the distanceTo method uses shorthand method syntax (distanceTo() { ... }), not an arrow function. This is important: shorthand methods receive the call-site this (the object they're called on), while arrow functions inherit this from their enclosing scope.
Classes#
Classes support constructors, private fields, getters, setters, static methods, and inheritance:
Private names are validated at parse time: using an undeclared #name is a SyntaxError. Getter-only and setter-only private accessors also follow normal accessor semantics, so invalid writes or reads throw TypeError. Private static names are brand-checked against the actual class receiver, so inherited static calls like Derived.methodFromBase() cannot read or write Base's private static state through this.
class CoffeeShop {
#name = "Goccia Coffee";
#beans = ["Arabica", "Robusta", "Ethiopian"];
#prices = { espresso: 2.5, latte: 4.0, cappuccino: 3.75 };
getMenu() {
return this.#beans.map((bean) => `${bean} blend`);
}
calculateTotal(order) {
return order.reduce((total, item) => total + (this.#prices[item] ?? 0), 0);
}
get name() {
return this.#name;
}
}
const shop = new CoffeeShop();
console.log(`Welcome to ${shop.name}!`);
console.log("Menu:", shop.getMenu());
const order = ["espresso", "latte"];
console.log(`Total: $${shop.calculateTotal(order).toFixed(2)}`);Inheritance uses extends and super:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
speak() {
return `${this.name} barks`;
}
}
const dog = new Dog("Rex");
console.log(dog.speak()); // Rex barks
console.log(dog instanceof Animal); // trueModules#
GocciaScript supports ES-style named imports and exports. Default imports/exports are not supported — use named exports instead.
Create a file called math.js:
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;Import it from another file called app.js in the same directory:
import { add, multiply } from "./math.js";
console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20Run the entry point:
./build/GocciaScriptLoader app.jsYou can also rename imports with as:
import { add as sum } from "./math.js";
console.log(sum(1, 2)); // 3Or import the full namespace object when you want all exports under one binding:
import * as math from "./math.js";
import * as config from "./config.json";
console.log(math.add(2, 3)); // 5
console.log(config.version); // top-level JSON/TOML/YAML keys are namespace propertiesNamed imports and exports also support string-literal export names when a module exposes a name that is not a valid identifier:
import { "foo-bar" as fooBar } from "./config.json";
import { "0" as firstDoc } from "./multi.yaml";
const localValue = 42;
export { localValue as "0" };And re-export from one module to another:
export { add, multiply } from "./math.js";Async/Await#
Async functions and await work as you'd expect from modern JavaScript:
const fetchUser = async (id) => {
const result = await Promise.resolve({ id, name: "Alice" });
return result;
};
const main = async () => {
const user = await fetchUser(1);
console.log(`User: ${user.name}`);
};
main();Promises are fully supported — .then(), .catch(), .finally(), Promise.all(), Promise.race(), Promise.any(), Promise.allSettled(), and Promise.withResolvers().
GocciaScript uses a synchronous microtask queue: all pending .then() callbacks are drained after synchronous code completes.
Strict Equality Only#
GocciaScript enforces strict equality. The loose equality operators (== and !=) are not available — use === and !==:
console.log(1 === 1); // true
console.log(1 === "1"); // false
console.log(null === undefined); // falseWhat's Different from JavaScript#
Here's a quick reference of GocciaScript's key restrictions:
| JavaScript | GocciaScript | Alternative |
|---|---|---|
var x = 1 | Not supported | let x = 1 or const x = 1 |
function foo() {} | Not supported | const foo = () => {} |
== / != | Not supported | === / !== |
for (...) / while (...) | Not supported | for...of, .map(), .forEach(), .reduce() |
eval("code") | Not supported | No alternative (by design) |
arguments | Not supported | (...args) => {} |
parseInt("10") | Not available as global | Number.parseInt("10") |
isNaN(x) | Not available as global | Number.isNaN(x) |
import x from "mod" | Not supported | import { x } from "mod" |
These restrictions are intentional — they eliminate common sources of bugs and security issues. See Language for the full rationale.
Next Steps#
You now have a working understanding of GocciaScript. Here's where to go from here:
- [Language](language.md) — Full list of what's supported and what's excluded, with rationale
- [Built-in Objects](built-ins.md) — Complete API reference for all built-in objects (Array, String, Map, Set, Promise, Temporal, etc.)
- [`examples/`](../examples/) — More example programs: classes, promises, and unsupported feature demos
- [Architecture](architecture.md) — Pipelines and main layers
- [Interpreter](interpreter.md) and [Bytecode VM](bytecode-vm.md) — Tree-walk vs bytecode execution backends
- [Core patterns](core-patterns.md) — Recurring implementation patterns, internal terminology