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*Ortwins. Tooling after that: seeCHANGELOG.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.
| From | asInt | asFloat | asBool | asString |
|---|---|---|---|---|
int | itself | n.0 | truthiness | decimal text |
float | truncate toward zero | itself | truthiness | Echo stringify |
str | parse trimmed integer text; reject "" and "3.9" | parse trimmed float text | truthiness | itself |
bool | type error | type error | itself | true / false |
null | type error | type error | false | null |
list / hash | type error | type error | truthiness | Echo 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
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)—sepmust be a non-emptystr. Empty separator is an error.replace(old, new)— replace all non-overlapping occurrences. Emptyoldis an error.contains(part)—partmust bestr. Emptypartistrue.slice(start, end)— exclusive end. Same range rule as lists.startsWith(prefix)/endsWith(suffix)— argument must bestr. Empty prefix or suffix istrue.indexOf(part)/lastIndexOf(part)—partmust bestr. Missing substring is-1. Emptypartis0/ the string length.repeat(n)—nmust be a non-negativeint(notbool).padStart(width, fill)/padEnd(width, fill)—widthis a non-negativeint(notbool).fillis a non-emptystrand is repeated as needed. If the string is already at leastwidth, the original is returned.replaceFirst(old, new)— replace the first non-overlapping occurrence. Emptyoldis 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
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)—keymust bestr. slice(start, end)on list or string returns a new value. End is exclusive.startin[0, length],endin[start, length].- Out of range is an index error. No negative indexes. No clamp.
map / filter are not in v0.4.
Host
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 newlistofstr. 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)returnsfallbackif unset.fallbackmay be any Echo value.readFile/writeFileare UTF-8 text. Missing file, permission, and invalid UTF-8 abort.readFileOr(path, fallback)returns file text on success andfallbackwhen the file is missing, is a directory, is not valid UTF-8, or cannot be read. Host deny and a non-strpath still abort.readFilestill aborts.fileExists(path)returnsbool. Missing files arefalse. Directories arefalse. Path must bestr.isDir(path)returnsbool. Missing paths and files arefalse. Path must bestr.listFiles(path)returns a new sortedlistof 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 bestr.removeFile(path)deletes a file. Missing paths and directories abort. Path must bestr.copyFile(src, dest)copies a file as bytes (not UTF-8 text).srcmust be a file. Missingsrcand missing dest parent abort. Existing dest files are overwritten. Dest directories abort. Path arguments must bestr.pathJoin(...)joins 2+ string parts with pathlib. Not a file operation. Available when files are denied.run(command, args)runscommandwith a list of stringargs(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 asintseconds. Takes no arguments.assert(cond, message)aborts withmessagewhencondis falsy.messagemust bestr.fail(message)always aborts withmessage.messagemust bestr. Error codeE2825.expect(cond, message)requirescondto beboolandmessageto bestr. Underecho testa false condition records E2826 and the unit continues; outsideecho testit aborts likeassert.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 astr. Takes no arguments.exit(code)stops the program.codemust be anint(notbool). The process returns that code. No Echo error is printed.eprint(...)issayfor 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, andcopyFilethen abort. That is an Echo runtime error, not a silent no-op orfalse.cwd,exit, andpathJoinstay available. - A host may deny process launch (
Host.allow_run=False; playground).runthen 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 orfallbackfor invalid JSON. Non-strtext is still a type error.parseJsonstill aborts.writeJsonwrites compact JSON. Non-finite floats and non-Echo values are errors.
Numbers
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 (intorfloat).boolis a type error.abskeeps the input kind:abs(-3)is3,abs(-3.5)is3.5.floorandceilreturnint.min(a, b)/max(a, b)take two numbers. Mixedint/floatuses Echo numeric comparison and returns the winning argument.random()takes no arguments and returns afloatin[0, 1).randomInt(min, max)returns an inclusiveint. Both bounds areint(notbool).minmust 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.
