Skip to content
Logos / LLM Reference

For AI & LLM Code Generation

Copy this reference and paste it in your AI prompts. All code examples are validated against the v0.4.5 interpreter.

LLM Reference

This reference is the authoritative source for generating accurate Logos code. All examples are validated against the v0.4.5 interpreter.

Quick Facts

  • File extension: .lgs
  • CLI: lgs file.lgs | lgs build file.lgs | lgs fmt file.lgs
  • Interpreter: Pratt parser + tree-walking evaluator in Go
  • GitHub: github.com/codetesla51/logos

Variables

logos
// Basic variables
let x = 10
let name = "Alice"
let pi = 3.14
let active = true
let nothing = null

// Reassign freely
x += 5     // 15
x -= 2     // 13

// const - immutable binding (v0.4.2)
const PI = 3.14159
const API_KEY = "abc123"

PI = 3.14  // Error! Cannot reassign const

// Postfix operators (v0.4)
let i = 0
i++   // 1
i--   // 0

Strings

logos
let s = "hello world"

// String interpolation (v0.4+)
let name = "Alice"
let greeting = "Hello, ${name}!"  // "Hello, Alice!"

// No quoted strings inside ${}
let dollar = "$"
let price = "${dollar}10"  // CORRECT

// Functions
upper("hello")    // "HELLO"
lower("HELLO")    // "hello"
trim("  hi  ")    // "hi"
split("a,b,c", ",")  // ["a", "b", "c"]
join(["a", "b"], "-")  // "a-b"

Arrays

logos
let nums = [1, 2, 3, 4, 5]
let mixed = [1, "hello", true]

nums[0]           // 1 (zero-indexed)
len(nums)         // 5
contains(nums, 3) // true

// Mutations (modify in place)
push(nums, 6)       // append: [1,2,3,4,5,6]
prepend(nums, 0)    // prepend: [0,1,2,3,4,5,6]
pop(nums)           // remove last, return it
first(nums)         // first element
last(nums)          // last element
tail(nums)          // all except first

// Returns new array
reverse([1, 2, 3])  // [3, 2, 1]
sort([3, 1, 2])     // [1, 2, 3] (strings OR numbers)

Tables (Objects/Maps)

logos
let user = table{
    name: "Alice",
    age: 30,
    active: true,
}

// Dot access (string keys)
user.name         // "Alice"
user.name = "Bob" // mutate

// Bracket access (dynamic keys)
let key = "age"
user[key]         // 30

// Nested tables
let company = table{
    name: "Acme",
    address: table{ city: "Seattle" },
}
company.address.city  // "Seattle"

// Table utilities
keys(user)            // ["name", "age", "active"]
values(user)          // ["Bob", 30, true]
has(user, "email")    // false
tableDelete(user, "age")  // remove key
merge(t1, t2)          // merge two tables

// ⚠️ Bracket access is type-aware
let t = table{ "1": "one", 1: "one-again" }
t["1"]  // "one" (string key)
t[1]    // "one-again" (number key)

Control Flow

For Loops

logos
// While-style
let i = 0
for i < 5 {
    print(i)
    i += 1
}

// For-in arrays
for item in items {
    print(item)
}

// For-in with index
for i, v in items {
    print("${i}: ${v}")
}

// range() (v0.4.2) — end is EXCLUSIVE
for i in range(0, 5) {
    print(i)  // 0, 1, 2, 3, 4 (NOT 5!)
}

// With step
for i in range(0, 10, 2) {
    print(i)  // 0, 2, 4, 6, 8
}

// Countdown (step = -1)
for i in range(5, 0, -1) {
    print(i)  // 5, 4, 3, 2, 1
}

// For-in over table keys
for key in keys(user) {
    print(key + ": " + str(user[key]))
}

Switch

logos
switch role {
    case "admin" { print("full access") }
    case "editor" { print("can edit") }
    default { print("read only") }
}
// No fallthrough

Functions

logos
// Named function
fn greet(name) {
    return "Hello, " + name
}

// Arrow function (implicit return)
let double = fn(x) -> x * 2

// First-class values
let apply = fn(f, x) -> f(x)
apply(double, 5)  // 10

// Closures - capture surrounding scope
let makeAdder = fn(x) {
    return fn(y) -> x + y
}
let add5 = makeAdder(5)
add5(3)   // 8
add5(10)  // 15

Error Handling

logos
// Result pattern - manual checking
let res = httpGet("https://api.example.com")
if res.ok {
    print(res.value.body)
} else {
    print("Error: " + res.error)
}

// Try expression (v0.4+) - unwraps and propagates
fn readConfig(path) {
    let content = try fileRead(path)
    let config = try parseJson(content)
    return config
}

// Without try - verbose
fn readConfigVerbose(path) {
    let res = fileRead(path)
    if !res.ok { return res.error }
    let json = parseJson(res.value)
    if !json.ok { return json.error }
    return json.value
}

Try + Pipe (v0.4+)

Errors propagate automatically through the chain:

logos
let data = try httpGet("https://api.example.com")
    |> try parseJson
    |> try filter(fn(u) -> u.active)

// If httpGet fails, the whole chain returns the error

Concurrency

logos
// Spawn single task
spawn {
    print("runs in background")
}

// Spawn loop - each iteration concurrent
let urls = ["https://api.example.com/1", "https://api.example.com/2"]
spawn for url in urls {
    let data = try httpGet(url)
    cache(url, data)
}

// Program waits for ALL spawns to complete before exit

CLI Arguments

args() returns user arguments starting at index 0. The binary path and script path are already removed.

logos
// Running: lgs script.lgs --name "Alice" --verbose
let cliArgs = args()

print(cliArgs[0])    // "--name" (NOT the binary path!)
print(cliArgs[1])    // "Alice"
len(cliArgs)         // 2 (binary & script already removed)

// Common pattern: flag checking
if contains(cliArgs, "--verbose") {
    print("Verbose mode")
}
if contains(cliArgs, "--name") {
    let idx = indexOf(cliArgs, "--name")
    let name = cliArgs[idx + 1]
    print("Name: " + name)
}

Built-in Functions

I/O

print(x) outputs with trailing newline. printn(x) outputs without.

input() · prompt(msg) · confirm(msg) · select(msg, opts[]) · clear()

logos
print("Hello!")        // with newline
printn("No newline")  // without newline
print("Next line")

let name = input()           // read stdin
let age = prompt("Age: ")    // show prompt, read input

if confirm("Delete?") {
    print("Deleted!")
}

let color = select("Pick:", ["red", "green", "blue"])

Type

type(val) · len(str|arr) · keys(table) · values(table) · has(table, key)

Type Conversion

str(val) · int(val) · float(val) are short aliases (v0.4.1). The to* forms still work.

String

upper(s) · lower(s) · trim(s) · replace(s, old, new) · split(s, sep) · join(arr, sep) · contains(str|arr, val) · startsWith(s, prefix) · endsWith(s, suffix) · indexOf(s, sub) · repeat(s, n) · slice(str|arr, start, end) · format(tmpl, args...)

Array

push(arr, val) · prepend(arr, val) · pop(arr) · first(arr) · last(arr) · tail(arr) · reverse(arr) · sort(arr) · contains(arr, val) · range(start, end, step?)

Important: range() end is EXCLUSIVE. range(0, 10) gives 0-9, not 0-10.

JSON

parseJson(str) · toJson(val) · prettyJson(val)

logos
// Parse JSON
let data = try parseJson('{"name": "Alice", "age": 30}')
print(data.name)      // "Alice"
print(str(data.age)) // "30"

// Convert to JSON
let user = table{ name: "Bob", score: 95 }
let json = try toJson(user)

// Pretty format
let pretty = try prettyJson(data)

HTTP

httpGet(url, headers?) · httpPost(url, body, headers?) · httpPut(url, body) · httpPatch(url, body) · httpDelete(url)

logos
// GET request
let body = try httpGet("https://api.example.com/users")

// With headers
let headers = table{
    "Authorization": "Bearer " + env("API_TOKEN"),
    "Content-Type": "application/json"
}
let data = try httpGet("https://api.example.com/private", headers)

// POST with JSON
let payload = try toJson(table{ name: "Alice", email: "alice@example.com" })
let res = try httpPost("https://api.example.com/users", payload)

// Methods: httpGet, httpPost, httpPut, httpPatch, httpDelete

File I/O CLI Only

fileRead(path) · fileWrite(path, data) · fileAppend(path, data) · fileDelete(path) · fileExists(path) · fileMkdir(path) · fileReadDir(path) · fileGlob(pattern) · fileCopy(src, dst) · fileMove(src, dst)

logos
// ⚠️ File I/O only works in CLI, NOT playground

let content = try fileRead("config.json")
try fileWrite("output.txt", "Hello!")
try fileAppend("log.txt", "New entry\n")

if fileExists("data.json") {
    let data = try fileRead("data.json")
}

try fileMkdir("backup/2024")
let files = try fileReadDir(".")
let scripts = try fileGlob("*.lgs")

Math

mathAbs(n) · mathSqrt(n) · mathPow(base, exp) · mathFloor(n) · mathCeil(n) · mathRound(n) · mathMin(a, b) · mathMax(a, b) · mathRandom() · mathRandomInt(min, max) · mathPi()

Time

timeNow() · timeMs() · timeStr() · dateStr() · dateTimeStr() · timeFormat(ts, fmt) · sleep(ms)

System

osname() · pwd() · cd(path) · env(key) · setenv(key, val) · args() · exit(code) · run(cmd, args...) · shell(cmd)

Color Output

colorRed(s) · colorGreen(s) · colorYellow(s) · colorBlue(s) · colorBold(s)

logos
print(colorRed("Error!"))
print(colorGreen("Success"))
print(colorBold(colorYellow("Warning")))

Regex (v0.4.3)

reMatch(pattern, text) · reFind(pattern, text) · reFindAll(pattern, text) · reReplace(pattern, text, repl) · reSplit(pattern, text) · reGroups(pattern, text)

Uses Go regexp syntax. Use backticks for raw patterns.

logos
// reMatch - true/false if matches
reMatch(`\d+`, "abc123")   // true

// reFind - first match or null
reFind(`\d+`, "3 cats")   // "3"

// reFindAll - array of matches
reFindAll(`\d+`, "1 and 2")  // ["1", "2"]

// reReplace - replace all
reReplace(`\d+`, "3 cats", "X")  // "X cats"

// reSplit - split by pattern
reSplit(`\s+`, "a  b c")   // ["a", "b", "c"]

// reGroups - capture groups
let parts = reGroups(`(\w+)@(\w+)`, "alice@example.com")
parts[0]  // "alice"
parts[1]  // "example"

// Use backticks for raw patterns (Go regexp)

Standard Library

std/array

map(arr, fn) · filter(arr, fn) · reduce(arr, fn, init) · unique(arr) · sum(arr) · min(arr) · max(arr) · indexOf(arr, val)

logos
use "std/array"

// Map - transform each element
map([1, 2, 3], fn(x) -> x * 2)  // [2, 4, 6]

// Filter - keep matching elements
filter([1, 2, 3, 4], fn(x) -> x % 2 == 0)  // [2, 4]

// Reduce - combine to single value
reduce([1, 2, 3], fn(acc, x) -> acc + x, 0)  // 6

// Other utilities
unique([1, 2, 2, 3])        // [1, 2, 3]
sum([1, 2, 3])              // 6
min([3, 1, 2])              // 1
max([3, 1, 2])              // 3
indexOf(["a", "b", "c"], "b")  // 1

// ⚠️ Playground: Use loops instead

std/math

mathFib(n) · mathFactorial(n) · mathIsPrime(n) · mathGcd(a, b) · mathLcm(a, b) · mathMean(arr) · mathMedian(arr) · mathClamp(n, min, max) · mathLerp(a, b, t)

logos
use "std/math"

mathFib(10)          // 55 (10th Fibonacci)
mathFactorial(5)     // 120 (5!)
mathIsPrime(17)      // true
mathGcd(48, 18)      // 6
mathLcm(4, 6)        // 12
mathMean([1, 2, 3])  // 2.0
mathMedian([1, 2, 3]) // 2
mathClamp(15, 0, 10) // 10 (constrain to range)
mathLerp(0, 100, 0.5) // 50.0 (linear interpolation)

std/string

strCapitalize(s) · strReverse(s) · strIsPalindrome(s) · strIsEmpty(s) · strCount(s, sub) · strPadStart(s, len, c) · strPadEnd(s, len, c)

logos
use "std/string"

strCapitalize("hello world")  // "Hello World"
strReverse("hello")          // "olleh"
strIsPalindrome("racecar")   // true
strIsEmpty("")               // true
strCount("banana", "a")      // 3
strPadStart("42", 6, "0")    // "000042"
strPadEnd("hi", 6, ".")      // "hi...."

std/path

pathJoin(parts) · pathBase(p) · pathDir(p) · pathExt(p) · pathWithoutExt(p) · pathIsAbs(p) · pathExists(p)

logos
use "std/path"

pathJoin(["home", "user", "docs"])    // "home/user/docs"
pathBase("/home/user/file.txt")       // "file.txt"
pathDir("/home/user/file.txt")        // "/home/user"
pathExt("/home/user/file.txt")        // ".txt"
pathWithoutExt("/home/user/file.txt") // "/home/user/file"
pathIsAbs("/home/user")              // true
pathExists("/etc/passwd")            // true

std/time

datetimeNow() · datetimeFromTimestamp(ts) · datetimeFormat(dt, fmt) · datetimeAdd(dt, n, unit) · datetimeSubtract(dt, n, unit) · datetimeDiff(d1, d2, unit) · datetimeIsAfter(d1, d2) · datetimeIsBefore(d1, d2) · datetimeIsEqual(d1, d2) · datetimeToStr(dt)

logos
use "std/time"

let now = datetimeNow()
print(now["date"])  // "2026-03-20"
print(now["str"])   // "2026-03-20 15:04:05"

// Add/subtract
datetimeAdd(now, 1, "days")
datetimeSubtract(now, 7, "days")

// Format (Go time layout)
datetimeFormat(now, "January 2, 2006")  // "March 20, 2026"
datetimeFormat(now, "02/01/2006")        // "03/20/2026"

// Comparison
datetimeIsAfter(tomorrow, now)    // true
datetimeIsBefore(yesterday, now)   // true
datetimeIsEqual(now, now)         // true

std/type

isString(val) · isInt(val) · isFloat(val) · isNumber(val) · isBool(val) · isArray(val) · isTable(val) · isNull(val) · isCallable(val)

std/log

logDebug(msg) · logInfo(msg) · logWarn(msg) · logError(msg)

std/testing

assert(name, got, expected) · suite(name, fn) · summary()

Go Embedding

go
import "github.com/codetesla51/logos/logos"

vm := logos.New()

vm.Register("greet", func(args ...logos.Object) logos.Object {
    name := args[0].(*logos.String).Value
    return &logos.String{Value: "Hello, " + name}
})

vm.SetVar("count", 42)
err := vm.Run(`print(greet("World"))`)
result := vm.GetVar("count")

Sandbox Mode

Restrict capabilities for untrusted code:

go
vm := logos.NewWithConfig(logos.SandboxConfig{
    AllowFileIO:  false,  // Disable file operations
    AllowNetwork: false,  // Disable HTTP
    AllowShell:   false,  // Disable shell/exec
    AllowExit:    false,  // Disable exit()
})

Gotchas & Common Mistakes

logos
// WRONG
let bad = "${dollar}10"       // syntax error - no strings inside ${}
// Can't put quoted strings directly inside interpolation

// CORRECT
let hi = "hi"
let greeting = "${hi}"        // use variable, not quoted string
for i in range(10, 0, -1) {}   // countdown (end is EXCLUSIVE)
let item = len(arr) > 0 ? arr[0] : null  // check length first

// Type-aware bracket access
let t = table{ 1: "one", "1": "one-again" }
t[1]    // number key: "one"
t["1"]  // string key: "one-again"

// sort() works on strings AND numbers
sort(["c", "a", "b"])  // ["a", "b", "c"]
sort([3, 1, 2])       // [1, 2, 3]

Known Limitations

  • Dot access only works with string-keyed table fields
  • Bracket access is type-aware: t["1"]t[1]
  • No try/catch — use result pattern or try expression
  • No classes/structs — use tables
  • No variadic user functions — only builtins support args...
  • spawn waits for all goroutines to complete before exit
  • std/array not available on playground server
  • args() does not work in compiled binaries — use lgs script.lgs

Quick Cheat Sheet

Variables

let x = 10
const PI = 3.14
x += 5

Strings

"Hello ${name}"
split("a,b", ",")

Arrays

[1, 2, 3]
push(arr, x)
sort(arr)

Tables

let u = table{
name: "Alice"
}
u.name

Functions

fn add(a, b) {
return a + b
}
fn(x) -> x * 2

Loops

for i in range(0, 10) {
print(i)
}

Errors

try httpGet(url)
|> try parseJson

HTTP

try httpGet(url)
try httpPost(url, body)

Full reference: See llm.txt for the complete reference.