# Logos Language Reference (v0.4.1) Logos is a dynamically-typed, interpreted scripting language written in Go. It uses a Pratt parser and a tree-walking interpreter. File extension: `.lgs` CLI: `lgs file.lgs` | `lgs build file.lgs` | `lgs fmt file.lgs` GitHub: https://github.com/codetesla51/logos Docs: https://logos-lang.vercel.app/docs Version: v0.4.1 --- ## SYNTAX OVERVIEW ### Variables ``` let x = 10 let name = "Uthman" let pi = 3.14 let flag = true let nothing = null ``` Variables are mutable. No `const`. Reassign with `=`. ### Compound Assignment ``` x += 5 x -= 2 x *= 3 x /= 2 x %= 4 ``` ### Postfix Increment/Decrement ``` let i = 0 i++ // 1 i-- // 0 print(i) ``` ### Strings ``` let s = "hello world" // String interpolation with ${} (v0.4+) let name = "world" let greeting = "hello ${name}" let age = 25 let bio = "${name} is ${age} years old" ``` ### Arrays ``` let arr = [1, 2, 3, "four", true] let first = arr[0] let last = arr[len(arr) - 1] ``` ### Tables (Maps/Objects) ``` let user = table{ name: "Uthman", age: 20, active: true } ``` **Dot access** — works with string-keyed tables: ``` user.name // "Uthman" user.age // 20 ``` **Dot assignment** — mutate table fields: ``` user.name = "New Name" user.age = 21 ``` **Bracket access** — works on tables and is type-aware. The key type matters: ``` user["name"] // works — string key lookup table{1: "one"}[1] // works — integer key lookup table{1: "one"}["1"] // returns null — different type, different key ``` Nested tables render with proper indentation via `print()`: ``` let config = table{ db: table{ host: "localhost", port: 5432 } } print(config) // Output: // { // db: { // host: "localhost", // port: 5432 // } // } ``` --- ## CONTROL FLOW ### If / Else ``` if x > 10 { print("big") } else { print("small") } ``` You can chain `else if`: ``` if x > 10 { print("big") } else if x > 5 { print("medium") } else { print("small") } ``` ### Switch ``` switch x { case 1 { print("one") } case 2 { print("two") } default { print("other") } } ``` ### For Loop (while-style) ``` for i < 10 { i += 1 } ``` ### For-In Loop ``` for item in arr { print(item) } ``` Use index variable with `for i, v in` (v0.3.1+): ``` for i, v in arr { print("${i}: ${v}") } ``` When iterating a table, each `item` is a 2-element array `[key, value]`: ``` for pair in user { print(pair[0]) // key string print(pair[1]) // value } ``` ### Break / Continue ``` for item in arr { if item == 3 { break } if item == 1 { continue } print(item) } ``` --- ## FUNCTIONS ### Anonymous Function ``` let add = fn(x, y) { return x + y } add(2, 3) // 5 ``` ### Named Function ``` fn greet(name) { return "Hello " + name } greet("Uthman") ``` ### Arrow Function (single expression) ``` let double = fn(x) -> x * 2 let square = fn(x) -> x * x ``` ### Closures ``` fn makeCounter() { let count = 0 return fn() { count += 1 return count } } let counter = makeCounter() counter() // 1 counter() // 2 ``` --- ## OPERATORS Arithmetic: `+`, `-`, `*`, `/`, `%` Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=` Logical: `&&`, `||`, `!` Ternary: `? :` (v0.3.2+) e.g., `condition ? valueIfTrue : valueIfFalse` Pipe: `|>` (v0.4+) e.g., `arr |> filter(fn) |> map(fn)` ### Pipe Operator (v0.4+) Chain function calls left to right: ``` let nums = [1, 2, 3, 4, 5] let result = nums |> filter(fn(x) -> x % 2 == 0) |> map(fn(x) -> x * 2) print(result) // [4, 8] ``` --- ## MODULES ``` use "filename" // loads filename.lgs from same directory use "std/math" // loads from stdlib use "std/array" use "std/string" use "std/path" use "std/time" use "std/type" use "std/log" use "std/testing" ``` Exports are implicit — everything defined at top level is accessible after `use`. --- ## ERROR HANDLING ### Try Expression (v0.4+) Unwraps result tables and propagates errors up the call stack: ``` fn fetchData() { let res = try httpGet("https://api.example.com/data") return res.value.body } ``` Without `try`, you would need: ``` fn fetchData() { let res = httpGet("https://api.example.com/data") if !res.ok { return res } return res.value.body } ``` --- ## CONCURRENCY ### Spawn Block Runs each statement concurrently as a goroutine. Waits for all to finish. ``` spawn { print("task 1") print("task 2") } ``` ### Spawn For-In Runs each iteration concurrently. Arrays only. ``` spawn for item in jobs { process(item) } ``` --- ## RESULT PATTERN All HTTP, file, and shell builtins return a result table: ``` table{ ok: true/false, value: , error: "" } ``` Always check `ok` before using `value`: ``` let res = httpGet("https://api.example.com/data") if res.ok { print(res.value.body) print(res.value.status) } else { print(res.error) } ``` --- ## BUILTINS REFERENCE ### I/O | Function | Signature | Description | |---|---|---| | `print` | `print(args...)` | Prints all arguments with proper formatting. Handles all types including nested tables with indentation. | | `input` | `input(prompt?)` | Reads line from stdin, optional prompt | | `prompt` | `prompt(msg)` | Prints msg then reads a line | | `confirm` | `confirm(msg)` | Prints "msg (y/n):", returns bool | | `select` | `select(msg, options[])` | Numbered menu, returns chosen element | | `clear` | `clear()` | Clears terminal screen | ### Color Output All take 1 string argument and return string wrapped in ANSI codes. | Function | Color | |---|---| | `colorRed(str)` | Red | | `colorGreen(str)` | Green | | `colorYellow(str)` | Yellow | | `colorBlue(str)` | Blue | | `colorMagenta(str)` | Magenta | | `colorCyan(str)` | Cyan | | `colorWhite(str)` | White | | `colorBold(str)` | Bold | ### Type / Inspection | Function | Signature | Returns | |---|---|---| | `type(val)` | `type(val)` | Type name string: `"INTEGER"`, `"FLOAT"`, `"STRING"`, `"BOOLEAN"`, `"NULL"`, `"ARRAY"`, `"TABLE"`, `"FUNCTION"` | | `len(val)` | `len(str\|arr)` | Length of string or array | | `keys(t)` | `keys(table)` | Array of table keys as strings (without type prefix) | | `values(t)` | `values(table)` | Array of table values | | `has(t, key)` | `has(table, key)` | Returns bool — true if string key exists | ### Type Conversion (v0.4.1+) | Function | Description | |---|---| | `str(val)` | Convert any value to string (short alias) | | `int(val)` | Convert to integer (short alias) | | `float(val)` | Convert to float (short alias) | | `toStr(val)` | Convert any value to its string representation | | `toInt(val)` | Converts string/float/bool to integer | | `toFloat(val)` | Converts string/int/bool to float | | `toBool(val)` | Converts value to boolean | Note: `str()`, `int()`, `float()` are short aliases added in v0.4.1. The `to*` forms still work. ### String | Function | Signature | Description | |---|---|---| | `upper(s)` | `upper(str)` | Uppercase | | `lower(s)` | `lower(str)` | Lowercase | | `trim(s)` | `trim(str)` | Trim leading/trailing whitespace | | `replace(s, old, new)` | `replace(str, old, new)` | Replace all occurrences | | `split(s, sep)` | `split(str, sep)` | Split string into array | | `join(arr, sep)` | `join(arr, sep)` | Join array of strings into one string | | `contains(val, sub)` | `contains(str\|arr, val)` | True if string contains substring OR array contains value | | `startsWith(s, prefix)` | `startsWith(str, prefix)` | Returns bool | | `endsWith(s, suffix)` | `endsWith(str, suffix)` | Returns bool | | `indexOf(s, sub)` | `indexOf(str, sub)` | Index of first occurrence, -1 if not found | | `repeat(s, n)` | `repeat(str, n)` | Repeat string n times | | `slice(val, s, e)` | `slice(str\|arr, start, end)` | Substring or sub-array (end is exclusive) | | `format(tmpl, args...)` | `format(template, args...)` | Printf-style formatting with `{}` placeholders | ### Array | Function | Signature | Description | |---|---|---| | `push(arr, val)` | `push(arr, val)` | Returns new array with val appended | | `pop(arr)` | `pop(arr)` | Returns last element, null if empty | | `first(arr)` | `first(arr)` | Returns first element, null if empty | | `last(arr)` | `last(arr)` | Returns last element, null if empty | | `tail(arr)` | `tail(arr)` | Returns all but first, null if empty | | `prepend(arr, val)` | `prepend(arr, val)` | Returns new array with val at front | | `reverse(arr)` | `reverse(arr)` | Returns reversed array | | `sort(arr)` | `sort(arr)` | Returns sorted array. Handles both string and numeric arrays (v0.4.1+) | | `slice(arr, s, e)` | `slice(arr, start, end)` | Sub-array from start to end (exclusive) | | `contains(arr, val)` | `contains(arr, val)` | Returns bool | ### Table | Function | Signature | Description | |---|---|---| | `keys(t)` | `keys(table)` | Returns array of key strings | | `values(t)` | `values(table)` | Returns array of values | | `has(t, key)` | `has(table, key)` | Returns bool | | `tableDelete(t, key)` | `tableDelete(table, key)` | Returns new table without that key | | `merge(t1, t2)` | `merge(table, table)` | Merges two tables; t2 overwrites on conflict | ### Math | Function | Signature | Description | |---|---|---| | `mathAbs(n)` | `mathAbs(n)` | Absolute value | | `mathSqrt(n)` | `mathSqrt(n)` | Square root | | `mathPow(base, exp)` | `mathPow(base, exp)` | Power | | `mathFloor(n)` | `mathFloor(n)` | Floor (returns integer) | | `mathCeil(n)` | `mathCeil(n)` | Ceiling (returns integer) | | `mathRound(n)` | `mathRound(n)` | Round (returns integer) | | `mathMin(a, b)` | `mathMin(a, b)` | Minimum of two numbers (returns float) | | `mathMax(a, b)` | `mathMax(a, b)` | Maximum of two numbers (returns float) | | `mathRandom()` | `mathRandom()` | Random float between 0.0 and 1.0 | | `mathRandomInt(min, max)` | `mathRandomInt(min, max)` | Random integer between min and max inclusive | | `mathPi()` | `mathPi()` | Returns pi (3.14159...) | ### JSON | Function | Signature | Description | |---|---|---| | `parseJson(str)` | `parseJson(str)` | Parses JSON string. Returns `{ok, value, error}`. Arrays -> Array, objects -> Table. | | `toJson(val)` | `toJson(val)` | Returns `{ok, value, error}`. Converts Logos value to compact JSON string. | | `prettyJson(val)` | `prettyJson(val)` | Returns `{ok, value, error}`. Pretty-printed JSON string with color. | ### File I/O (disabled in sandbox) All file builtins except `fileExists` and `fileExt` return a result table `{ok, value, error}`. | Function | Signature | Description | |---|---|---| | `fileRead(path)` | `fileRead(path)` | Returns result with file content as string in `value` | | `fileWrite(path, content)` | `fileWrite(path, content)` | Overwrites file | | `fileAppend(path, content)` | `fileAppend(path, content)` | Appends to file | | `fileDelete(path)` | `fileDelete(path)` | Deletes file or empty directory | | `fileMkdir(path)` | `fileMkdir(path)` | Creates directory and all parents | | `fileReadDir(path)` | `fileReadDir(path)` | Returns result with array of filenames in `value` | | `fileGlob(pattern)` | `fileGlob(pattern)` | Returns result with matching path strings in `value` | | `fileCopy(src, dst)` | `fileCopy(src, dst)` | Copies file | | `fileMove(src, dst)` | `fileMove(src, dst)` | Moves/renames file or directory | | `fileExists(path)` | `fileExists(path)` | Returns bool directly (not a result table) | ### HTTP (disabled in sandbox) All HTTP builtins return a result table: `{ok, value: {body, status}, error}` `body` is a string. `status` is an integer HTTP status code. | Function | Signature | |---|---| | `httpGet` | `httpGet(url, headers?)` | | `httpPost` | `httpPost(url, body, headers?)` | | `httpPut` | `httpPut(url, body, headers?)` | | `httpPatch` | `httpPatch(url, body, headers?)` | | `httpDelete` | `httpDelete(url, headers?)` | `headers` is an optional table of string key-value pairs. `body` must be a string (typically JSON). ### Time | Function | Signature | Description | |---|---|---| | `timeNow()` | `timeNow()` | Current Unix timestamp in seconds (integer) | | `timeMs()` | `timeMs()` | Current Unix timestamp in milliseconds (integer) | | `timeStr()` | `timeStr()` | Current time string e.g. `"15:04:05"` | | `dateStr()` | `dateStr()` | Current date string e.g. `"2026-03-16"` | | `dateTimeStr()` | `dateTimeStr()` | Current date+time string e.g. `"2026-03-16 15:04:05"` | | `timeFormat(ts, fmt)` | `timeFormat(ts, fmt)` | Formats a unix timestamp using Go time layout string | | `sleep(ms)` | `sleep(ms)` | Pauses execution for given milliseconds | ### System / OS (disabled in sandbox) | Function | Signature | Description | |---|---|---| | `osname()` | `osname()` | Returns OS string: `"linux"`, `"darwin"`, `"windows"` | | `pwd()` | `pwd()` | Returns current working directory string | | `cd(path)` | `cd(path)` | Changes current working directory | | `env(key)` | `env(key)` | Returns env variable value string or null | | `setenv(key, val)` | `setenv(key, val)` | Sets environment variable | | `args()` | `args()` | Returns CLI arguments as array (index 0 onward). Has bounds checking — returns empty array if no arguments. | | `exit(code?)` | `exit(code?)` | Exits process, optional integer code (default 0) | | `shell(cmd)` | `shell(cmd)` | Runs shell command string, returns result table with output string in `value` | | `run(cmd, args...)` | `run(cmd, args...)` | Runs command with separate args, returns result table | --- ## STDLIB REFERENCE Stdlib modules must be imported with `use` before their functions are available. ### std/array ``` use "std/array" ``` | Function | Signature | Description | |---|---|---| | `map(arr, fn)` | `map(arr, func)` | Transform each element | | `filter(arr, fn)` | `filter(arr, func)` | Keep passing elements | | `reduce(arr, fn, init)` | `reduce(arr, func, initial)` | Combine to single value | | `unique(arr)` | `unique(arr)` | Remove duplicates | | `flat(arr)` | `flat(arr)` | Flatten nested arrays | | `sum(arr)` | `sum(arr)` | Sum all numbers | | `min(arr)` | `min(arr)` | Smallest number | | `max(arr)` | `max(arr)` | Largest number | | `indexOf(arr, val)` | `indexOf(arr, val)` | Find position | | `containsAll(arr, items)` | `containsAll(arr, items)` | Check all present | ### std/string ``` use "std/string" ``` | Function | Signature | Description | |---|---|---| | `strCapitalize(s)` | `strCapitalize(str)` | Capitalize first letter | | `strReverse(s)` | `strReverse(str)` | Reverse characters | | `strIsPalindrome(s)` | `strIsPalindrome(str)` | Returns true if palindrome | | `strIsEmpty(s)` | `strIsEmpty(str)` | Returns true if empty or whitespace | | `strCount(s, sub)` | `strCount(str, sub)` | Count occurrences | | `strRepeat(s, n)` | `strRepeat(str, n)` | Repeat string n times | | `strPadStart(s, len, c)` | `strPadStart(str, length, char)` | Pad start | | `strPadEnd(s, len, c)` | `strPadEnd(str, length, char)` | Pad end | ### std/math ``` use "std/math" ``` | Function | Signature | Description | |---|---|---| | `mathFib(n)` | `mathFib(n)` | nth Fibonacci number | | `mathFactorial(n)` | `mathFactorial(n)` | n! | | `mathMean(arr)` | `mathMean(arr)` | Average | | `mathMedian(arr)` | `mathMedian(arr)` | Middle value | | `mathMode(arr)` | `mathMode(arr)` | Most common value | | `mathClamp(n, min, max)` | `mathClamp(n, min, max)` | Constrain to range | | `mathLerp(a, b, t)` | `mathLerp(a, b, t)` | Linear interpolation | | `mathIsPrime(n)` | `mathIsPrime(n)` | Returns true if prime | | `mathGcd(a, b)` | `mathGcd(a, b)` | Greatest common divisor | | `mathLcm(a, b)` | `mathLcm(a, b)` | Least common multiple | ### std/path ``` use "std/path" ``` | Function | Signature | Description | |---|---|---| | `pathJoin(parts)` | `pathJoin(arr)` | Joins array of path parts with `/` | | `pathBase(path)` | `pathBase(path)` | Filename portion of path | | `pathDir(path)` | `pathDir(path)` | Directory portion of path | | `pathExt(path)` | `pathExt(path)` | File extension e.g. `".lgs"` | | `pathIsAbs(path)` | `pathIsAbs(path)` | Returns true if absolute | | `pathClean(path)` | `pathClean(path)` | Simplify path | | `pathExists(path)` | `pathExists(path)` | Returns true if exists | | `pathWithoutExt(path)` | `pathWithoutExt(path)` | Path without extension | ### std/time ``` use "std/time" ``` All datetime functions work with datetime objects: `table{timestamp, str, date, time}`. | Function | Signature | Description | |---|---|---| | `datetimeNow()` | `datetimeNow()` | Current datetime | | `datetimeFromTimestamp(ts)` | `datetimeFromTimestamp(ts)` | From Unix timestamp | | `datetimeFormat(dt, fmt)` | `datetimeFormat(dt, fmt)` | Custom format | | `datetimeIsAfter(dt1, dt2)` | `datetimeIsAfter(dt1, dt2)` | Returns bool | | `datetimeIsBefore(dt1, dt2)` | `datetimeIsBefore(dt1, dt2)` | Returns bool | | `datetimeIsEqual(dt1, dt2)` | `datetimeIsEqual(dt1, dt2)` | Returns bool | | `datetimeDiff(dt1, dt2, unit)` | `datetimeDiff(dt1, dt2, unit)` | Difference | | `datetimeAdd(dt, amount, unit)` | `datetimeAdd(dt, amount, unit)` | Add time | | `datetimeSubtract(dt, n, u)` | `datetimeSubtract(dt, n, u)` | Subtract time | | `datetimeToStr(dt)` | `datetimeToStr(dt)` | To string | ### std/type ``` use "std/type" ``` | Function | Signature | Description | |---|---|---| | `isNull(val)` | `isNull(val)` | Check null | | `isString(val)` | `isString(val)` | Check string | | `isInt(val)` | `isInt(val)` | Check integer | | `isFloat(val)` | `isFloat(val)` | Check float | | `isBool(val)` | `isBool(val)` | Check boolean | | `isArray(val)` | `isArray(val)` | Check array | | `isTable(val)` | `isTable(val)` | Check table | | `isNumber(val)` | `isNumber(val)` | Check int or float | | `isCallable(val)` | `isCallable(val)` | Check function | ### std/log ``` use "std/log" ``` | Function | Description | |---|---| | `logDebug(msg)` | Debug level (white) | | `logInfo(msg)` | Info level (green) | | `logWarn(msg)` | Warn level (yellow) | | `logError(msg)` | Error level (red) | ### std/testing ``` use "std/testing" ``` | Function | Signature | Description | |---|---|---| | `assert(name, got, expected)` | `assert(name, got, expected)` | Check equality | | `suite(name, fn)` | `suite(name, func)` | Group tests | | `summary()` | `summary()` | Print results | --- ## EMBEDDING LOGOS IN GO ```go import "github.com/codetesla51/logos/logos" // Create VM (all capabilities enabled) vm := logos.New() // Create sandboxed VM vm := logos.NewWithConfig(logos.SandboxConfig{ AllowFileIO: false, AllowNetwork: false, AllowShell: false, AllowExit: false, }) // Register a Go function callable from Logos vm.Register("greet", func(args ...logos.Object) logos.Object { name := args[0].(*logos.String).Value return &logos.String{Value: "Hello, " + name} }) // Set a variable in the Logos environment vm.SetVar("count", 42) vm.SetVar("config", map[string]interface{}{"port": 8080}) // Run a script string err := vm.Run(`print(greet("World"))`) // Get a variable back as a Go value result := vm.GetVar("count") // returns interface{} // Call a Logos function from Go result, err := vm.Call("myFunction", arg1, arg2) ``` Supported Go <-> Logos type conversions: - `int`, `int64` <-> `Integer` - `float64` <-> `Float` - `string` <-> `String` - `bool` <-> `Bool` - `[]interface{}` <-> `Array` - `map[string]interface{}` <-> `Table` - `nil` <-> `Null` --- ## SANDBOX MODE When running in the Logos playground (web), sandbox mode is active: - File I/O builtins disabled (`fileRead`, `fileWrite`, etc.) - HTTP builtins disabled (`httpGet`, `httpPost`, etc.) - Shell/exec builtins disabled (`shell`, `run`) - `exit()` disabled - Calling a sandboxed function returns an error object: `"function 'X' is not available in sandbox mode"` --- ## COMMON PATTERNS ### HTTP + JSON + Pipe ``` let users = try httpGet("https://api.example.com/users") |> try parseJson |> filter(fn(u) -> u.active) |> map(fn(u) -> u.name) print(users) ``` ### String Interpolation ``` let name = "Alice" let age = 28 print("${name} is ${age} years old") ``` ### Error Handling with try ``` fn safeFetch(url) { let res = try httpGet(url) let data = try parseJson(res.value.body) return data } ``` ### File Operations ``` let content = try fileRead("config.json") let config = try parseJson(content) print(config.port) ``` --- ## KNOWN LIMITATIONS - Dot access only works with string-keyed table fields. Integer or boolean keys must use bracket access. - Bracket access on tables is type-aware: `t["1"]` and `t[1]` are different keys. - No try/catch — use the result pattern or try expression. - No classes or structs — use tables as data containers. - No variadic user-defined functions — only builtins support `args...`. - `spawn` waits for all goroutines (no fire-and-forget). - `spawn for item in` only supports arrays, not tables or strings.