Skip to content

Echo v0.4 standard library

Historical archive. Contract for the v0.4 builtins only. See Built-in Methods for the current catalog.

Status: Contract for the v0.4 builtins (plus later host/tooling notes below). Implemented. 0.4.1–0.4.3 add more host builtins and echo check. 0.5.0 adds language-basics builtins plus multiline strings and .5 / scientific literals. 0.5.3 adds *Or twins. Tooling after that: see CHANGELOG.md. Boundary: v0.4 language vs stdlib

Tests must match this builtin contract. v0.2 / v0.3 module/syntax rules are unchanged by the host cut itself (later releases add syntax separately).

Host builtins abort with Echo errors. There is no try/catch.


Conversion (semantic correction)

asInt / asFloat / asBool / asString keep their names.

FromasIntasFloatasBoolasString
intitselfn.0truthinessdecimal text
floattruncate toward zeroitselftruthinessEcho stringify
strparse trimmed integer text; reject "" and "3.9"parse trimmed float texttruthinessitself
booltype errortype erroritselftrue / false
nulltype errortype errorfalsenull
list / hashtype errortype errortruthinessEcho stringify

asInt(" 42 ") is 42. asInt(true) is a type error.

asIntOr(value, fallback) / asFloatOr(value, fallback) return the converted number on success. Unparseable strings, null, lists, and hashes return fallback. asIntOr(true, 0) / asFloatOr(true, 0.0) stay type errors — bool has no twin. asInt / asFloat still abort.


Strings

echo
parts: list = line.split(",");
clean: str = line.replace("foo", "bar");
ok: bool = line.contains("echo");
mid: str = line.slice(1, 4);
prefixed: bool = line.startsWith("echo");
suffixed: bool = line.endsWith(".txt");
joined: str = parts.join(",");
at: int = line.indexOf(",");
last: int = line.lastIndexOf(",");
padded: str = "5".padStart(3, "0");
once: str = line.replaceFirst("foo", "bar");
  • split(sep)sep must be a non-empty str. Empty separator is an error.
  • replace(old, new) — replace all non-overlapping occurrences. Empty old is an error.
  • contains(part)part must be str. Empty part is true.
  • slice(start, end) — exclusive end. Same range rule as lists.
  • startsWith(prefix) / endsWith(suffix) — argument must be str. Empty prefix or suffix is true.
  • indexOf(part) / lastIndexOf(part)part must be str. Missing substring is -1. Empty part is 0 / the string length.
  • repeat(n)n must be a non-negative int (not bool).
  • padStart(width, fill) / padEnd(width, fill)width is a non-negative int (not bool). fill is a non-empty str and is repeated as needed. If the string is already at least width, the original is returned.
  • replaceFirst(old, new) — replace the first non-overlapping occurrence. Empty old is an error.
  • join(separator) — list of strings plus a string separator, including "". Empty list is "". Non-string items or separator are a type error.

Collections

echo
ok: bool = nums.contains(3);
ok: bool = user.has("name");
mid: list = nums.slice(1, 4);
  • List contains(value) uses Echo ==.
  • List join(separator) requires a list of strings.
  • Hash has(key)key must be str.
  • slice(start, end) on list or string returns a new value. End is exclusive.
  • start in [0, length], end in [start, length].
  • Out of range is an index error. No negative indexes. No clamp.

map / filter are not in v0.4.


Host

echo
flags: list = args();
home: str = env("HOME");
city: str = envOr("CITY", "unknown");
text: str = readFile("notes.txt");
maybe: str = readFileOr("notes.txt", "");
writeFile("out.txt", text);
present: bool = fileExists("notes.txt");
here: str = cwd();
folder: bool = isDir("out");
names: list = listFiles(".");
mkdir("out");
removeFile("scratch.txt");
copyFile("src.bin", "dest.bin");
joined: str = pathJoin("a", "b", "c");
proc: hash = run("true", []);
stamp: int = now();
assert(fileExists("dest.bin"), "copy failed");
line: str = readLine();
data: dynamic = parseJson(text);
maybeJson: dynamic = parseJsonOr(text, null);
encoded: str = writeJson(data);
if names.length() == 0 {
    exit(1);
}
eprint("to stderr");
  • args() returns a new list of str. Program arguments only, not the source path and not --plain.
  • CLI: interpreter flags, then optional --, then program arguments. Extra positionals after the source file are program arguments.
  • env(name) aborts if unset. Empty string counts as set.
  • envOr(name, fallback) returns fallback if unset. fallback may be any Echo value.
  • readFile / writeFile are UTF-8 text. Missing file, permission, and invalid UTF-8 abort.
  • readFileOr(path, fallback) returns file text on success and fallback when the file is missing, is a directory, is not valid UTF-8, or cannot be read. Host deny and a non-str path still abort. readFile still aborts.
  • fileExists(path) returns bool. Missing files are false. Directories are false. Path must be str.
  • isDir(path) returns bool. Missing paths and files are false. Path must be str.
  • listFiles(path) returns a new sorted list of entry names (files and subdirectories). Missing paths and non-directories abort.
  • mkdir(path) creates the leaf directory only. Missing parent, existing directory, and a file in the way abort. Path must be str.
  • removeFile(path) deletes a file. Missing paths and directories abort. Path must be str.
  • copyFile(src, dest) copies a file as bytes (not UTF-8 text). src must be a file. Missing src and missing dest parent abort. Existing dest files are overwritten. Dest directories abort. Path arguments must be str.
  • pathJoin(...) joins 2+ string parts with pathlib. Not a file operation. Available when files are denied.
  • run(command, args) runs command with a list of string args (empty list allowed). No shell. Returns { "code": int, "stdout": str, "stderr": str }. Non-zero process exit is not an Echo error. Missing executable aborts.
  • now() returns unix time as int seconds. Takes no arguments.
  • assert(cond, message) aborts with message when cond is falsy. message must be str.
  • fail(message) always aborts with message. message must be str. Error code E2825.
  • expect(cond, message) requires cond to be bool and message to be str. Under echo test a false condition records E2826 and the unit continues; outside echo test it aborts like assert.
  • expectEq(left, right, message) / expectNeq(left, right, message) compare with Echo ==. Codes E2827 / E2828. Same continue-under-echo test / abort-otherwise rule.
  • readLine() reads one line from stdin with no required prompt. EOF aborts.
  • cwd() returns the host working directory as a str. Takes no arguments.
  • exit(code) stops the program. code must be an int (not bool). The process returns that code. No Echo error is printed.
  • eprint(...) is say for stderr: variadic values separated by spaces, then a newline. No keyword arguments.
  • Relative paths resolve against the process working directory, or a host-supplied cwd.
  • A host may deny files (playground). readFile, readFileOr, writeFile, fileExists, isDir, listFiles, mkdir, removeFile, and copyFile then abort. That is an Echo runtime error, not a silent no-op or false. cwd, exit, and pathJoin stay available.
  • A host may deny process launch (Host.allow_run=False; playground). run then aborts the same way. Default hosts allow it.
  • parseJson: objects → hash, arrays → list, JSON integers → int, other finite numbers → float. Invalid JSON aborts.
  • parseJsonOr(text, fallback) returns the parsed value or fallback for invalid JSON. Non-str text is still a type error. parseJson still aborts.
  • writeJson writes compact JSON. Non-finite floats and non-Echo values are errors.

Numbers

echo
say(abs(-3));
say(min(2, 5));
say(max(2, 5));
say(floor(3.2));
say(ceil(3.2));
  • abs(n), floor(n), ceil(n) take one number (int or float). bool is a type error.
  • abs keeps the input kind: abs(-3) is 3, abs(-3.5) is 3.5.
  • floor and ceil return int.
  • min(a, b) / max(a, b) take two numbers. Mixed int / float uses Echo numeric comparison and returns the winning argument.
  • random() takes no arguments and returns a float in [0, 1).
  • randomInt(min, max) returns an inclusive int. Both bounds are int (not bool). min must be <= max.

Tooling

echo check [paths...] (0.4.1; multi-path in 0.5.9) lexes, parses, and analyzes each entry file and its import graph without executing. A directory argument checks *.echo recursively; explicit file paths always check. Success is silent and exits 0. If any file fails, each diagnostic is printed and the process exits 1 after every path has been analyzed. Missing path exits 1. No path prints help and exits 2. The first argv token check is the subcommand; echo check.echo still runs a file named check.echo.

echo with no source file starts a REPL. A submission can span lines while { is unclosed or a triple-quoted string is unterminated. Bindings persist for the lifetime of the process. --plain is supported. exit(code) leaves the REPL with that process code. EOF (Ctrl-D) exits 0.

echo test [paths...] (0.5.6) is the native runner. Directories recursively run *_test.echo files; explicit file paths always run. Zero-argument top-level fn testXxx() functions are separate units. expect / expectEq / expectNeq record and continue under the runner. Exit 0 if every unit passed; 1 if any failed. echo test with no path prints help and exits 2. The first argv token test is the subcommand; echo test.echo still runs a file named test.echo. -run / --run (0.6.8) filters testXxx function names. --json (0.6.9) writes a machine-readable report instead of the human summary.

echo fmt [paths...] (0.5.4) rewrites Echo sources in place. --check prints dirty paths and exits 1. A directory argument formats *.echo recursively. echo fmt.echo still runs a file named fmt.echo.

echo lint [paths...] (0.5.5, rules expanded 0.5.8) reports style findings without running the program. Findings exit 1; clean files exit 0. A directory argument lints *.echo recursively. Parse errors match echo check. echo lint.echo still runs a file named lint.echo. Rules: unused-local, unused-function, unused-import, comparison-to-bool, redundant-by-one, empty-block, shadow-builtin, test-naming, self-assign, unreachable-after-fail.

Not in v0.4 / still held

Slice syntax, try/catch, function values, packages, file.read() namespaces, LSP, VM, generics, classes, async.

Echo is in active development. The docs reflect the current implementation.