Standard Library

The AlexScript standard library is a collection of focused, single-purpose modules covering the things every program eventually needs: math, dates and time, file I/O, JSON and CSV, networking, hashing, secure randomness. Every module is implemented natively in Ruby and exposed as an ordinary AlexScript class — that means full integration with the language (inheritance, reflection, method dispatch) and excellent performance, since the actual work happens in compiled Ruby code.

Each library is loaded with import("name") (no path prefix, no .as extension):

import("czas")
import("plik")
import("json")

niech t = Czas.teraz()
niech tresc = Plik.czytaj("dane.json")
niech dane = Json.parsuj(tresc)

Most standard library classes are static-only — they’re collections of related functions, not classes you instantiate. Calling Csv.nowy(), Mat.nowy(), or Json.nowy() raises a runtime error like Csv jest klasą statyczną i nie może być instancjonowana. Two important exceptions are Czas and the networking primitives (SocketTcp, SerwerTcp), which represent stateful resources and are constructed normally.

Every method in this reference is verified against the source — the names, argument lists, return types, and behavior described here match the implementation. Where the documentation describes both common and advanced use, the common case is shown first.

Csv — comma-separated values

The Csv library handles parsing and generating CSV (comma-separated values) data. It’s built on Ruby’s standard CSV library, so it handles all the edge cases of the format you’d expect: quoted fields, embedded commas, multi-line values, and custom separators.

CSV data in AlexScript is represented in two ways depending on which entry point you use. The plain functions return arrays of arrays — each row is an array of strings, with the header row (if present) just being the first array. The *_z_naglowkami (with-headers) variants return arrays of objects, treating the first row as field names.

import("csv")

# Plain parsing — array of arrays
niech wiersze = Csv.parsuj("imie,wiek\nJan,30\nAna,25")
pokazl wiersze[0]    # ["imie", "wiek"]
pokazl wiersze[1]    # ["Jan", "30"]

# With headers — array of objects
niech ludzie = Csv.parsuj_z_naglowkami("imie,wiek\nJan,30\nAna,25")
pokazl ludzie[0]["imie"]    # "Jan"
pokazl ludzie[1]["wiek"]    # "25"

Note that all CSV values are returned as strings, even when they look like numbers. If you need numeric data, convert it explicitly with .liczba().

The library is Csv only — it does not pull in any classes you’d construct. Csv.nowy() raises an error.

Csv constants

Csv.SEPARATOR — the default separator, ",". You normally don’t reference this directly; it’s the implicit default for every method.

Parsing strings

Csv.parsuj(tekst) / Csv.parsuj(tekst, separator) — parse a CSV string into an array of arrays. Each element of the outer array is one row; each element of an inner array is one field as a string. The separator defaults to ",".

niech wiersze = Csv.parsuj("a,b,c\n1,2,3")
pokazl wiersze.dlg()    # 2
pokazl wiersze[0]       # ["a", "b", "c"]

# Custom separator
niech tab_sep = Csv.parsuj("a;b;c\n1;2;3", ";")
pokazl tab_sep[1][0]    # "1"

# Empty input returns an empty array
pokazl Csv.parsuj("").dlg()    # 0

Csv.parsuj_linie(tekst) / Csv.parsuj_linie(tekst, separator) — parse a single line into an array of fields. Unlike parsuj, this expects exactly one line and returns just that row, not an array containing one row.

niech pola = Csv.parsuj_linie("x,y,z")
pokazl pola         # ["x", "y", "z"]
pokazl pola.dlg()   # 3

Parsing with headers

Csv.parsuj_z_naglowkami(tekst) / Csv.parsuj_z_naglowkami(tekst, separator) — treat the first row as field names and return an array of objects (hashes). Each row becomes an object whose keys are the column names.

niech dane = Csv.parsuj_z_naglowkami("imie,wiek,miasto\nJan,30,Warszawa\nAna,25,Kraków")

pokazl dane.dlg()              # 2  (header row is consumed, not counted)
pokazl dane[0]["imie"]         # "Jan"
pokazl dane[1]["miasto"]       # "Kraków"

# Iterate naturally
dla osoba w dane {
    pokazl "#{osoba["imie"]} z #{osoba["miasto"]}"
}

This is usually the form you want when working with structured data.

Csv.naglowki(tekst) / Csv.naglowki(tekst, separator) — extract just the header row as an array of strings, without parsing the rest of the data.

niech naglowki = Csv.naglowki("kol1,kol2,kol3\na,b,c")
pokazl naglowki    # ["kol1", "kol2", "kol3"]

Useful for inspecting a file’s structure before deciding how to process it.

Reading from files

Csv.parsuj_plik(sciezka) / Csv.parsuj_plik(sciezka, separator) — read and parse a CSV file. Returns an array of arrays, just like parsuj.

niech wiersze = Csv.parsuj_plik("./dane.csv")
pokazl wiersze.dlg()

Csv.parsuj_plik_z_naglowkami(sciezka) / Csv.parsuj_plik_z_naglowkami(sciezka, separator) — file equivalent of parsuj_z_naglowkami. Returns an array of objects.

niech ludzie = Csv.parsuj_plik_z_naglowkami("./osoby.csv")
dla osoba w ludzie {
    pokazl osoba["imie"]
}

Csv.naglowki_pliku(sciezka) / Csv.naglowki_pliku(sciezka, separator) — extract just the header row from a file.

niech kolumny = Csv.naglowki_pliku("./dane.csv")
pokazl "Plik ma #{kolumny.dlg()} kolumn"

Generating CSV strings

Csv.generuj(wiersze) / Csv.generuj(wiersze, separator) — turn an array of arrays back into a CSV string. The trailing newline is included.

niech tekst = Csv.generuj([
    ["imie", "wiek"],
    ["Jan", "30"],
    ["Ana", "25"]
])
pokazl tekst
# imie,wiek
# Jan,30
# Ana,25

Csv.generuj_linie(tablica) / Csv.generuj_linie(tablica, separator) — generate a single CSV line from an array of fields.

pokazl Csv.generuj_linie(["a", "b", "c"])    # "a,b,c\n"

Csv.generuj_z_naglowkami(naglowki, wiersze) / Csv.generuj_z_naglowkami(naglowki, wiersze, separator) — generate a CSV string with an explicit header row, followed by the data rows. Convenient when you have headers and rows in separate variables.

niech tekst = Csv.generuj_z_naglowkami(
    ["produkt", "cena"],
    [["Chleb", "5"], ["Mleko", "4"]]
)

Writing to files

Csv.generuj_plik(sciezka, wiersze) / Csv.generuj_plik(sciezka, wiersze, separator) — write rows to a CSV file. Returns the number of rows written.

niech zapisane = Csv.generuj_plik("./out.csv", [
    ["imie", "wiek"],
    ["Jan", "30"]
])
pokazl "Zapisano #{zapisane} wierszy"

Csv.generuj_plik_z_naglowkami(sciezka, naglowki, wiersze) / Csv.generuj_plik_z_naglowkami(sciezka, naglowki, wiersze, separator) — write a header row followed by data rows.

Csv.generuj_plik_z_naglowkami(
    "./osoby.csv",
    ["imie", "wiek"],
    [["Jan", "30"], ["Ana", "25"]]
)

Inspection and column extraction

Csv.liczba_wierszy(tekst) / Csv.liczba_wierszy(tekst, separator) — count the rows in a CSV string, including the header if present.

pokazl Csv.liczba_wierszy("a,b\n1,2\n3,4")    # 3

Csv.liczba_kolumn(tekst) / Csv.liczba_kolumn(tekst, separator) — count the columns based on the first row.

pokazl Csv.liczba_kolumn("a,b,c\n1,2,3")    # 3

Csv.kolumna(tekst, nazwa) / Csv.kolumna(tekst, nazwa, separator) — extract a single column by header name. Returns an array of values from that column, in row order. The header row itself is consumed.

niech wieki = Csv.kolumna("imie,wiek\nJan,30\nAna,25", "wiek")
pokazl wieki    # ["30", "25"]

Csv.kolumna_pliku(sciezka, nazwa) / Csv.kolumna_pliku(sciezka, nazwa, separator) — same, but reads from a file.

niech ceny = Csv.kolumna_pliku("./produkty.csv", "cena")

These two are the workhorses for “give me one piece of data from this whole file” — particularly useful when you only care about one column and don’t want to load the entire dataset into memory just to filter it down.

Czas — dates and time

The Czas library represents instants in time. A Czas value combines a date (year, month, day) with a time-of-day (hour, minute, second, sub-second precision) and a timezone (UTC, local, or an explicit offset). The library is a thin wrapper over Ruby’s Time class — operations are fast, the API is comprehensive, and the timezone handling matches what you’d expect from Ruby.

Unlike most standard library classes, Czas can be instantiated — every time value is a Czas instance with rich method support.

import("czas")

niech teraz = Czas.teraz()
pokazl "Dziś jest #{teraz.nazwa_dnia_tygodnia()}"
pokazl "Rok: #{teraz.rok()}, miesiąc: #{teraz.miesiac()}"

Time arithmetic returns new Czas values — the original is never modified. Method chains work naturally:

niech jutro_o_18 = Czas.teraz().dodaj_dni(1).dodaj_godziny(18 - Czas.teraz().godzina())

Constructing time values

There are several ways to create a Czas value, depending on what you have on hand.

Czas.teraz() — current local time. The simplest and most common entry point.

niech t = Czas.teraz()
pokazl t.rok()       # e.g. 2026

Czas.nowy() — same as teraz() when called with no arguments. With arguments, builds a time from explicit components or parses a string.

The argument forms:

Czas.nowy()                              # current time
Czas.nowy(2024)                          # 2024-01-01 00:00:00
Czas.nowy(2024, 6, 15)                   # 2024-06-15 00:00:00
Czas.nowy(2024, 6, 15, 14, 30, 45)       # 2024-06-15 14:30:45
Czas.nowy("2024-06-15 14:30:45")          # parsed from string

Missing components default to the start of their range — month and day default to 1, hour/minute/second to 0.

Czas.utc(rok, ...) — construct a UTC time. Same argument shape as nowy, but the result is in the UTC timezone:

niech t = Czas.utc(2024, 1, 1, 12, 0, 0)
pokazl t.czy_utc()    # prawda
pokazl t.strefa()     # "UTC"

Czas.lokalny(rok, ...) — construct a time in the system’s local timezone. Same argument shape.

niech t = Czas.lokalny(2024, 7, 4, 18, 0, 0)

Czas.z_timestampu(sekundy) / Czas.z_timestampu(sekundy, mikrosekundy) — build a time from a Unix timestamp (seconds since the epoch, 1970-01-01 UTC). The argument can be an integer or float.

niech t1 = Czas.z_timestampu(0)
pokazl t1.rok()    # 1970

niech t2 = Czas.z_timestampu(1700000000)
pokazl t2.rok()    # 2023

# Negative timestamps are valid (pre-epoch)
niech t3 = Czas.z_timestampu(-86400)
pokazl t3.rok()    # 1969

Czas.parsuj(tekst) — parse a time from a string using heuristic detection. Recognizes most common formats — ISO-like dates, RFC formats, slash-separated dates, and so on.

niech t = Czas.parsuj("2024-08-20 09:15:30")
pokazl t.rok()       # 2024
pokazl t.miesiac()   # 8

Czas.parsuj_format(tekst, format) — parse with an explicit strftime-style format. Use this when the input format is non-standard or when you want to be strict about what’s accepted.

niech t = Czas.parsuj_format("20/06/2024", "%d/%m/%Y")
pokazl t.dzien()      # 20
pokazl t.miesiac()    # 6

Czas.z_iso8601(tekst) — parse a strict ISO 8601 timestamp.

niech t = Czas.z_iso8601("2024-06-15T14:30:00Z")

Czas.z_rfc2822(tekst) — parse an RFC 2822 timestamp (the format used in email headers).

Czas.z_httpdate(tekst) — parse an HTTP-date timestamp (the format used in HTTP headers like Last-Modified).

niech t = Czas.z_httpdate("Wed, 15 Jun 2024 14:30:00 GMT")

Reading components

These methods read individual parts of a time value. None of them modify the receiver.

Method Returns
t.rok() year, e.g. 2024
t.miesiac() month 1–12
t.dzien() day of month 1–31
t.godzina() hour 0–23
t.minuta() minute 0–59
t.sekunda() second 0–59 (or 60 on a leap second)
t.mikrosekunda() microseconds within the second, 0–999999
t.nanosekunda() nanoseconds within the second
t.ulamek_sekundy() sub-second part as a float (e.g. 0.123)
t.dzien_tygodnia() day of week 0–6 (Sunday = 0)
t.dzien_roku() day of year 1–366
t.strefa() timezone name as a string, e.g. "UTC" or "CET"
t.przesuniecie_utc() UTC offset in seconds
t.timestamp() Unix timestamp as integer (whole seconds)
t.timestamp_f() Unix timestamp as float (with sub-second precision)
niech t = Czas.nowy(2024, 3, 14, 15, 9, 26)

pokazl t.rok()             # 2024
pokazl t.miesiac()         # 3
pokazl t.dzien()           # 14
pokazl t.dzien_roku()      # 74  (March 14 in a leap year)
pokazl t.dzien_tygodnia()  # 4   (Thursday)
pokazl t.timestamp()       # e.g. 1710432566

Day-of-week predicates

Each of the seven days has a dedicated predicate that returns a boolean:

Method Returns prawda if the time is on…
t.czy_poniedzialek() Monday
t.czy_wtorek() Tuesday
t.czy_sroda() Wednesday
t.czy_czwartek() Thursday
t.czy_piatek() Friday
t.czy_sobota() Saturday
t.czy_niedziela() Sunday
niech t = Czas.nowy(2024, 3, 14)    # Thursday
pokazl t.czy_czwartek()    # prawda
pokazl t.czy_poniedzialek() # falsz

State predicates

t.czy_utc()prawda if the time is in UTC.

t.czy_czas_letni()prawda if daylight saving time is in effect at this time and timezone.

niech t = Czas.utc(2024, 1, 1)
pokazl t.czy_utc()    # prawda

Time arithmetic

All arithmetic methods return a new Czas value — the receiver is never modified.

t.dodaj(sekundy) — add seconds. Negative values go backward in time.

niech t = Czas.nowy(2024, 1, 1, 12, 0, 0)
niech za_minute = t.dodaj(60)
niech minute_temu = t.dodaj(-60)

pokazl za_minute.minuta()    # 1
pokazl minute_temu.minuta()  # 59  (and hour rolled back to 11)

t.odejmij(arg) — overloaded based on argument type:

niech t1 = Czas.nowy(2024, 1, 1, 12, 0, 0)
niech t2 = Czas.nowy(2024, 1, 1, 13, 0, 0)

pokazl t2.odejmij(t1)     # 3600.0  (one hour, in seconds)
pokazl t1.odejmij(60)     # Czas value, one minute earlier

Convenience addition methods — each takes a count and returns a new time advanced by that many of the named unit:

Method Effect
t.dodaj_sekundy(n) t + n seconds
t.dodaj_minuty(n) t + n * 60 seconds
t.dodaj_godziny(n) t + n * 3600 seconds
t.dodaj_dni(n) t + n * 86400 seconds
t.dodaj_tygodnie(n) t + n * 604800 seconds

Convenience subtraction methods — each takes a count and returns a new time moved backward:

Method Effect
t.odejmij_sekundy(n) t - n seconds
t.odejmij_minuty(n) t - n * 60 seconds
t.odejmij_godziny(n) t - n * 3600 seconds
t.odejmij_dni(n) t - n * 86400 seconds
t.odejmij_tygodnie(n) t - n * 604800 seconds

These compose well — chain them for natural-reading expressions:

niech termin = Czas.teraz()
    .dodaj_dni(7)
    .dodaj_godziny(3)
    .dodaj_minuty(30)

A subtle behavior to know: month and year arithmetic is intentionally not part of this set, because months have variable length. If you add 30 days to January 31st, you get March 2nd, not “February 31st”. For calendar-aware arithmetic at month/year granularity, build it yourself by adjusting rok() and miesiac() and constructing a new time.

t.roznica(inny) — explicit difference in seconds, returned as a float. Equivalent to t.odejmij(inny) when inny is another Czas, but reads more clearly when that’s your intent:

niech ile_sek = pozniej.roznica(wczesniej)

Comparison

t.porownaj(inny) — three-way comparison. Returns -1 if t < inny, 0 if equal, 1 if t > inny.

niech t1 = Czas.nowy(2024, 1, 1)
niech t2 = Czas.nowy(2024, 12, 31)

pokazl t1.porownaj(t2)    # -1
pokazl t2.porownaj(t1)    # 1
pokazl t1.porownaj(t1)    # 0

t.rowny(inny) — strict equality (must match down to nanoseconds and timezone).

t.przed(inny)prawda if t is earlier than inny.

t.po(inny)prawda if t is later than inny.

jesli teraz.po(termin) {
    pokazl "Termin minął"
}

t.miedzy(od, do_czasu)prawda if t falls between od and do_czasu, inclusive on both ends.

niech srodek = Czas.nowy(2024, 6, 15)
niech start = Czas.nowy(2024, 1, 1)
niech koniec = Czas.nowy(2024, 12, 31)

pokazl srodek.miedzy(start, koniec)    # prawda

Formatting and conversion

t.format(wzor) — strftime-style formatting. Without arguments, defaults to "%Y-%m-%d %H:%M:%S".

niech t = Czas.nowy(2024, 3, 15, 14, 30, 45)

pokazl t.format("%Y-%m-%d")          # "2024-03-15"
pokazl t.format("%H:%M:%S")          # "14:30:45"
pokazl t.format("%d.%m.%Y %H:%M")    # "15.03.2024 14:30"

The format directives match Ruby’s strftime%Y for 4-digit year, %m for zero-padded month, %d for zero-padded day, %H/%M/%S for hour/minute/second, and many more.

t.do_tekstu() — default human-readable format with timezone info, like "2024-03-15 14:30:45 +0200".

t.ascii() — ASCII-formatted time, like "Fri Mar 15 14:30:45 2024".

t.iso8601() / t.iso8601(precyzja) — ISO 8601 format. Pass an integer for sub-second decimal places.

niech t = Czas.utc(2024, 3, 15, 14, 30, 45)
pokazl t.iso8601()    # "2024-03-15T14:30:45Z"

t.rfc2822() — RFC 2822 format (used in email headers).

t.httpdate() — HTTP-date format (used in Last-Modified and other HTTP headers).

pokazl Czas.teraz().httpdate()
# e.g. "Sat, 15 Mar 2024 14:30:45 GMT"

t.do_utc() — convert to UTC. Returns a new time at the same instant, expressed in UTC.

t.do_lokalnego() / t.do_lokalnego(przesuniecie) — convert to local time, or to an explicit offset.

t.do_strefy(przesuniecie) — convert to a specific timezone offset, such as "+09:00" or "-05:00".

niech t = Czas.utc(2024, 1, 1, 12, 0, 0)
niech tokyo = t.do_strefy("+09:00")
pokazl tokyo.godzina()    # 21

Rounding

t.zaokraglij(precyzja) / t.zaokraglij() — round to the nearest given sub-second decimal precision. Default precision is 0 (whole seconds).

t.sufit(precyzja) / t.sufit() — round up (toward future).

t.podloga(precyzja) / t.podloga() — round down (toward past).

niech t = Czas.z_timestampu(1700000000.7)

pokazl t.podloga(0).sekunda()      # rounded down to whole second
pokazl t.sufit(0).sekunda()        # rounded up

Decomposition

t.do_tablicy() — return a 10-element array [sekunda, minuta, godzina, dzien, miesiac, rok, dzien_tygodnia, dzien_roku, czy_czas_letni, strefa]. Useful when you want all components at once for further processing.

niech t = Czas.utc(2024, 3, 14, 15, 9, 26)
niech tab = t.do_tablicy()
pokazl tab[5]    # 2024  (year)
pokazl tab[4]    # 3     (month)

Polish locale names

For natural-language Polish formatting, several methods return the day-of-week or month name in Polish.

t.nazwa_dnia_tygodnia() — full Polish day name, e.g. "czwartek".

t.nazwa_dnia_skrot() — three-letter Polish abbreviation, e.g. "czw".

t.nazwa_miesiaca() — full Polish month name in nominative case, e.g. "marzec".

t.nazwa_miesiaca_dopelniacz() — Polish month name in genitive case (used when the month is a date qualifier), e.g. "marca" (as in 14 marca).

t.nazwa_miesiaca_skrot() — three-letter Polish abbreviation, e.g. "mar".

t.do_tekstu_pl() — full natural-Polish formatted string combining day name, day of month, genitive month name, year, and time.

niech t = Czas.nowy(2024, 3, 14, 15, 9, 26)
pokazl t.do_tekstu_pl()
# "czwartek, 14 marca 2024, 15:09:26"

Static helpers

Czas.stempel() — current Unix timestamp as integer. Equivalent to Czas.teraz().timestamp() but slightly faster since no Czas object is allocated.

niech ts = Czas.stempel()    # e.g. 1742990400

Czas.stempel_f() — current Unix timestamp as float, with sub-second precision.

niech start = Czas.stempel_f()
# ... do some work ...
niech elapsed = Czas.stempel_f() - start
pokazl "Trwało #{elapsed}s"

This is the standard idiom for measuring elapsed wall-clock time.

Czas.uspij(sekundy) — sleep for the given number of seconds. Accepts integers and floats. Blocks the current thread; in async code, prefer uspij(ms) (the global async builtin), which is cooperative.

Czas.uspij(0.5)    # half a second
Czas.uspij(2)      # two seconds

Digest — cryptographic hashes

The Digest library computes cryptographic hash digests — fixed-size fingerprints of arbitrary input data. Hashes are used for verifying file integrity, comparing large objects without comparing their contents directly, building HMACs for message authentication, and many other purposes.

Digest is a static-only class — you don’t instantiate it; you call its static methods directly:

import("digest")

niech h = Digest.sha256("Hello, World!")
pokazl h    # "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"

Five algorithms are supported: MD5, SHA1, SHA256, SHA384, and SHA512. For each algorithm, four output formats are available: hex string, base64 string, raw byte array, and file digest. The library also provides HMAC functions, a constant-time comparison helper, and conversion utilities between hex strings and byte arrays.

A note on algorithm choice: MD5 and SHA1 are not secure for cryptographic purposes (verifying authenticity, password hashing, signing). Use them only for non-security uses like cache keys, deduplication, or compatibility with legacy systems. For security-critical work, use SHA256 or stronger.

Hex digests

The most common form. Returns a lowercase hex string of fixed length per algorithm:

Method Output length
Digest.md5(tekst) 32 chars
Digest.sha1(tekst) 40 chars
Digest.sha256(tekst) 64 chars
Digest.sha384(tekst) 96 chars
Digest.sha512(tekst) 128 chars
pokazl Digest.md5("hello")     # "5d41402abc4b2a76b9719d911017c592"
pokazl Digest.sha256("hello")  # "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"

Every digest function is deterministic — the same input always produces the same output. This is what makes them useful for fingerprinting:

pokazl Digest.sha256("abc") == Digest.sha256("abc")    # prawda
pokazl Digest.sha256("abc") == Digest.sha256("xyz")    # falsz

Base64 digests

Sometimes you want a more compact representation — base64 packs 8 bits per character instead of the 4 bits per character of hex, so the output is roughly half as long. Each algorithm has a _base64 variant:

Method Returns
Digest.md5_base64(tekst) base64-encoded MD5
Digest.sha1_base64(tekst) base64-encoded SHA1
Digest.sha256_base64(tekst) base64-encoded SHA256
Digest.sha384_base64(tekst) base64-encoded SHA384
Digest.sha512_base64(tekst) base64-encoded SHA512
pokazl Digest.sha256_base64("hello")
# "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="

Raw byte arrays

For applications that want the raw binary digest as integers — for serialization to a custom format, embedding in a binary protocol, or feeding into another cryptographic operation. Each algorithm has a _bajty variant returning an array of byte values (each 0–255):

Method Array length
Digest.md5_bajty(tekst) 16
Digest.sha1_bajty(tekst) 20
Digest.sha256_bajty(tekst) 32
Digest.sha384_bajty(tekst) 48
Digest.sha512_bajty(tekst) 64
niech bajty = Digest.sha256_bajty("hello")
pokazl bajty.dlg()    # 32
pokazl bajty[0]       # first byte as integer 0–255

File digests

For hashing the contents of a file directly, without first reading it into memory as a string. Useful for large files where loading the whole content would be wasteful:

Method Returns
Digest.md5_plik(sciezka) hex MD5 of file content
Digest.sha1_plik(sciezka) hex SHA1
Digest.sha256_plik(sciezka) hex SHA256
Digest.sha384_plik(sciezka) hex SHA384
Digest.sha512_plik(sciezka) hex SHA512
niech checksum = Digest.sha256_plik("./large_archive.zip")
pokazl "Suma kontrolna: #{checksum}"

The result is the same as if you’d read the file into a string and called Digest.sha256 on it, but the implementation streams the file in chunks and uses constant memory regardless of file size.

HMAC — keyed message authentication

HMAC (Hash-based Message Authentication Code) combines a secret key with a message to produce a digest that proves both authenticity and integrity. Anyone who knows the key can verify the HMAC; anyone without the key cannot forge one for a new message.

Method Returns
Digest.hmac_md5(klucz, wiadomosc) hex HMAC-MD5 (32 chars)
Digest.hmac_sha1(klucz, wiadomosc) hex HMAC-SHA1 (40 chars)
Digest.hmac_sha256(klucz, wiadomosc) hex HMAC-SHA256 (64 chars)
Digest.hmac_sha512(klucz, wiadomosc) hex HMAC-SHA512 (128 chars)
niech klucz = "tajny-klucz-aplikacji"
niech podpis = Digest.hmac_sha256(klucz, "ważna wiadomość")
pokazl podpis    # 64-char hex string

A typical use is signing API requests:

funkcja podpisz_zapytanie(metoda, sciezka, body) {
    niech kanoniczne = "#{metoda}\n#{sciezka}\n#{body}"
    zwroc Digest.hmac_sha256(API_KLUCZ, kanoniczne)
}

For security-sensitive HMACs, prefer hmac_sha256 or hmac_sha512hmac_md5 and hmac_sha1 are kept only for compatibility with older systems.

Comparing digests securely

Digest.porownaj(a, b) — compare two strings (typically two digests) in constant time, returning prawda if they match and falsz otherwise.

The reason for a dedicated function: a naive a == b comparison can leak information through timing — it returns false as soon as the first differing byte is found, so an attacker measuring response times could discover bytes one by one. Constant-time comparison always examines all bytes regardless of where the first difference is, removing the timing signal.

niech oczekiwany = Digest.hmac_sha256(klucz, wiadomosc)
niech otrzymany = naglowek_zapytania["X-Signature"]

jesli !Digest.porownaj(oczekiwany, otrzymany) {
    rzuc BladWykonania.nowy("Niepoprawny podpis")
}

Use Digest.porownaj whenever you compare HMAC signatures, password hashes, session tokens, or any other secret values.

Hex/byte conversion utilities

Two helpers for moving between hex strings and byte arrays — useful when interoperating with external systems that use one form when you have the other.

Digest.hex_na_bajty(hex) — parse a hex string into an array of byte values. Each pair of hex characters becomes one byte.

niech bajty = Digest.hex_na_bajty("48656c6c6f")
pokazl bajty.dlg()    # 5
pokazl bajty[0]       # 72  (0x48 = 'H')

Digest.bajty_na_hex(bajty) — serialize a byte array back into a lowercase hex string.

pokazl Digest.bajty_na_hex([72, 101, 108, 108, 111])    # "48656c6c6f"

Round-tripping through both gives back the original — Digest.bajty_na_hex(Digest.hex_na_bajty(s)) equals s for any valid hex string.

Mat — mathematics

The Mat library provides mathematical constants and functions — trigonometry, logarithms, roots, factorials, rounding, random numbers, and number theory helpers like GCD and LCM. It’s a thin wrapper over Ruby’s Math module plus a handful of additions specific to AlexScript.

Mat is a static-only class. Calling Mat.nowy() raises Mat jest klasą statyczną i nie może być instancjonowana.

import("mat")

pokazl Mat.PI                  # 3.141592653589793
pokazl Mat.sqrt(144)           # 12.0
pokazl Mat.sin(Mat.PI / 2)     # 1.0
pokazl Mat.silnia(10)          # 3628800
pokazl Mat.nwd(48, 18)         # 6

Trigonometric functions take and return values in radians — use Mat.na_radiany(stopnie) and Mat.na_stopnie(radiany) to convert. Most functions return floats; integer-only operations (like silnia, nwd, nww, integer division) preserve the integer type.

A note on naming: identifier-style names follow the convention used elsewhere in AlexScript — uppercase for constants (Mat.PI, not Mat.pi), lowercase Polish for methods. Common short English names like sin, cos, log, sqrt, exp, gamma, frexp, ldexp, erf, erfc are kept (they’re universal mathematical notation), but conceptually-named operations get Polish names: Mat.podloga/Mat.sufit for floor/ceil, Mat.zaokraglij for round, Mat.silnia for factorial, Mat.znak for sign.

Constants

Mat.PI — π, the ratio of a circle’s circumference to its diameter, ≈ 3.14159.

Mat.E — Euler’s number, the base of the natural logarithm, ≈ 2.71828.

Mat.NIESKONCZONOSC — positive infinity. Equal to its own arithmetic operations: 1.0 / 0 evaluates to this value.

Mat.MINUS_NIESKONCZONOSC — negative infinity, equivalent to -Mat.NIESKONCZONOSC.

Mat.NAN — “not a number”, the result of undefined operations like 0.0 / 0.0. Note that Mat.NAN == Mat.NAN is falsz by IEEE-754 rules — to test for NaN, use Mat.czy_nan(x).

pokazl Mat.PI                       # 3.141592653589793
pokazl Mat.NIESKONCZONOSC > 1e100   # prawda
pokazl Mat.czy_nan(Mat.NAN)         # prawda

Trigonometry

All trigonometric functions take and return radians.

Mat.sin(x) — sine of x.

Mat.cos(x) — cosine of x.

Mat.tan(x) — tangent of x.

pokazl Mat.sin(0)              # 0.0
pokazl Mat.sin(Mat.PI / 2)     # 1.0
pokazl Mat.cos(Mat.PI)         # -1.0
pokazl Mat.tan(Mat.PI / 4)     # 0.9999999999999999

Mat.asin(x) — arc sine. Domain: -1 ≤ x ≤ 1. Range: -π/2 .. π/2.

Mat.acos(x) — arc cosine. Domain: -1 ≤ x ≤ 1. Range: 0 .. π.

Mat.atan(x) — arc tangent. Range: -π/2 .. π/2.

Mat.atan2(y, x) — two-argument arc tangent. Returns the angle whose tangent is y/x, taking the signs of both arguments into account to determine the correct quadrant. Range: -π .. π.

pokazl Mat.asin(1)         # 1.5707963267948966   (π/2)
pokazl Mat.atan2(1, 1)     # 0.7853981633974483   (π/4)
pokazl Mat.atan2(0, -1)    # 3.141592653589793    (π)

Hyperbolic functions

Mat.sinh(x) — hyperbolic sine.

Mat.cosh(x) — hyperbolic cosine.

Mat.tanh(x) — hyperbolic tangent.

Mat.asinh(x) — inverse hyperbolic sine.

Mat.acosh(x) — inverse hyperbolic cosine. Domain: x ≥ 1.

Mat.atanh(x) — inverse hyperbolic tangent. Domain: -1 < x < 1.

pokazl Mat.sinh(0)    # 0.0
pokazl Mat.cosh(0)    # 1.0
pokazl Mat.tanh(0)    # 0.0

Exponential and logarithmic

Mat.exp(x)e raised to the power x.

Mat.expm1(x)e^x - 1, computed with extra precision for small x (where direct computation would lose precision to floating-point cancellation).

pokazl Mat.exp(0)    # 1.0
pokazl Mat.exp(1)    # 2.718281828459045

Mat.log(x) / Mat.log(x, podstawa) — natural logarithm (base e), or logarithm with explicit base.

pokazl Mat.log(Mat.E)     # 1.0
pokazl Mat.log(8, 2)      # 3.0   log base 2 of 8
pokazl Mat.log(1000, 10)  # 3.0   log base 10 of 1000

Mat.log2(x) — logarithm base 2.

Mat.log10(x) — logarithm base 10.

Mat.log1p(x)log(1 + x), computed with extra precision for small x.

pokazl Mat.log2(8)        # 3.0
pokazl Mat.log10(1000)    # 3.0

Power and root

Mat.sqrt(x) — square root. Domain: x ≥ 0.

Mat.cbrt(x) — cube root. Defined for all real numbers (including negative).

Mat.hipotenuza(x, y)sqrt(x² + y²) computed without intermediate overflow.

Mat.potega(x, y)x raised to the power y. Same as the ** operator, but available as a function for cases where you want to pass it as a value.

pokazl Mat.sqrt(2)              # 1.4142135623730951
pokazl Mat.cbrt(27)             # 3.0
pokazl Mat.hipotenuza(3, 4)     # 5.0
pokazl Mat.potega(2, 10)        # 1024

Error and gamma functions

Mat.erf(x) — error function, used in probability and statistics. Range: -1 .. 1.

Mat.erfc(x) — complementary error function, equal to 1 - erf(x) but more accurate when x is large.

Mat.gamma(x) — gamma function, the continuous extension of factorial: gamma(n+1) == n! for non-negative integers.

Mat.lgamma(x) — natural logarithm of the absolute value of gamma(x). Useful when gamma(x) would overflow.

pokazl Mat.erf(0)        # 0.0
pokazl Mat.erfc(0)       # 1.0
pokazl Mat.gamma(5)      # 24.0    (4!)
pokazl Mat.gamma(1)      # 1.0     (0!)
pokazl Mat.lgamma(1)     # 0.0     (log(1) = 0)

Float decomposition

Mat.frexp(x) — decompose a float into its fraction and exponent parts. Returns a 2-element array [fraction, exponent] such that x == fraction * 2^exponent. Useful for low-level float manipulation.

Mat.ldexp(fraction, exponent) — inverse of frexp. Returns fraction * 2^exponent.

niech rozklad = Mat.frexp(1024.0)
pokazl rozklad                  # [0.5, 11]   1024 = 0.5 * 2^11
pokazl Mat.ldexp(0.5, 11)       # 1024.0

Rounding

Mat.podloga(x) — floor: largest integer ≤ x.

Mat.sufit(x) — ceiling: smallest integer ≥ x.

Mat.zaokraglij(x) / Mat.zaokraglij(x, precyzja) — round to nearest integer, or to the given decimal precision.

Mat.obetnij(x) — truncate toward zero (drops the fractional part). Differs from podloga for negative numbers: podloga(-3.7) is -4, but obetnij(-3.7) is -3.

pokazl Mat.podloga(3.7)            # 3
pokazl Mat.podloga(-3.7)           # -4

pokazl Mat.sufit(3.2)              # 4
pokazl Mat.sufit(-3.7)             # -3

pokazl Mat.zaokraglij(3.5)         # 4
pokazl Mat.zaokraglij(3.14159, 2)  # 3.14

pokazl Mat.obetnij(3.9)            # 3
pokazl Mat.obetnij(-3.9)           # -3

Absolute value and sign

Mat.abs(x) — absolute value. Preserves the type — abs of an integer is an integer, abs of a float is a float.

Mat.znak(x) — sign function. Returns 1 if x > 0, -1 if x < 0, 0 if x == 0. Always returns an integer.

pokazl Mat.abs(-5)        # 5
pokazl Mat.abs(-3.14)     # 3.14

pokazl Mat.znak(42)       # 1
pokazl Mat.znak(-7)       # -1
pokazl Mat.znak(0)        # 0

Min, max, clamp

Mat.min(x, y) — smaller of two values.

Mat.max(x, y) — larger of two values.

Both are strictly two-argumentMat.min(1, 2, 3) is not valid. To find the min or max of an array, use the arr.min() / arr.max() methods on numeric arrays, or redukuj with Mat.min / Mat.max.

pokazl Mat.min(3, 7)      # 3
pokazl Mat.max(-1, 1)     # 1

# For arrays:
niech liczby = [5, 2, 9, 1, 7]
pokazl liczby.min()       # 1
pokazl liczby.max()       # 9

Mat.ogranicz(x, dolna, gorna) — clamp x to the range [dolna, gorna]. Returns dolna if x < dolna, gorna if x > gorna, otherwise x.

pokazl Mat.ogranicz(5, 0, 10)     # 5
pokazl Mat.ogranicz(-5, 0, 10)    # 0
pokazl Mat.ogranicz(15, 0, 10)    # 10

Combinatorics

Mat.silnia(n) — factorial of a non-negative integer n. For negative input, raises Silnia wymaga liczby nieujemnej.

pokazl Mat.silnia(0)     # 1
pokazl Mat.silnia(5)     # 120
pokazl Mat.silnia(10)    # 3628800
pokazl Mat.silnia(20)    # 2432902008176640000

The result is an arbitrary-precision integer, so you don’t have to worry about overflow for large factorials.

Degree-radian conversion

Mat.na_radiany(stopnie) — convert degrees to radians.

Mat.na_stopnie(radiany) — convert radians to degrees.

pokazl Mat.na_radiany(180)     # 3.141592653589793
pokazl Mat.na_stopnie(Mat.PI)  # 180.0

# Round-trip:
pokazl Mat.na_stopnie(Mat.na_radiany(45))   # 44.99999999999999

Random numbers

These methods produce non-cryptographic random numbers — fine for simulations, sampling, and games, but not suitable for security purposes. For cryptographic randomness (passwords, tokens, keys, session IDs), use the SecureRandom library.

Mat.losowa() — random float in [0, 1).

Mat.losowa_zakres(od, do) — random number in the inclusive range [od, do]. Returns an integer if both bounds are integers, otherwise a float.

Mat.losowa_calkowita(od, do) — same as losowa_zakres. Provided as an alias for code that wants to be explicit about wanting an integer.

pokazl Mat.losowa()              # e.g. 0.7234819203748
pokazl Mat.losowa_zakres(1, 6)   # e.g. 4   (dice roll)
pokazl Mat.losowa_zakres(0, 100) # e.g. 73

Predicates

Mat.czy_nan(x)prawda if x is NaN. The only correct way to test for NaN — direct equality (x == Mat.NAN) always returns falsz by IEEE-754 rules.

Mat.czy_nieskonczonosc(x)prawda if x is positive or negative infinity.

Mat.czy_parzysta(n)prawda if the integer n is even.

Mat.czy_nieparzysta(n)prawda if the integer n is odd.

pokazl Mat.czy_nan(Mat.NAN)                   # prawda
pokazl Mat.czy_nan(1.0)                       # falsz

pokazl Mat.czy_nieskonczonosc(Mat.NIESKONCZONOSC)  # prawda
pokazl Mat.czy_nieskonczonosc(1.0)            # falsz

pokazl Mat.czy_parzysta(4)                    # prawda
pokazl Mat.czy_nieparzysta(7)                 # prawda

Integer arithmetic

Mat.nwd(a, b) — greatest common divisor of two integers.

Mat.nww(a, b) — least common multiple of two integers.

Mat.reszta(a, b) — modulo, equivalent to a % b. Provided as a function for cases where you want to pass it as a value.

Mat.dzielenie_calkowite(a, b) — integer division. Raises Dzielenie przez zero if b == 0.

pokazl Mat.nwd(12, 8)               # 4
pokazl Mat.nwd(17, 5)               # 1   (coprime)
pokazl Mat.nww(4, 6)                # 12
pokazl Mat.nww(3, 7)                # 21

pokazl Mat.reszta(10, 3)            # 1
pokazl Mat.dzielenie_calkowite(10, 3)   # 3

Plik — file system I/O

The Plik library handles everything filesystem-related: reading and writing files, listing directories, querying file metadata, working with paths, and incremental I/O on open file handles. It’s built on Ruby’s File, FileUtils, and Dir classes, so it inherits their semantics — including UTF-8 handling, path normalization, and platform-specific separators.

Plik has two modes of use. The most common is the static convenience methods, which open a file, do their thing, and close it for you:

import("plik")

# Read entire file into a string
niech tresc = Plik.czytaj("./dane.txt")

# Write a whole string to a file (replacing existing content)
Plik.zapisz("./output.txt", "Witaj świecie")

# Append
Plik.dopisz("./log.txt", "kolejna linia\n")

The second mode is instance methods on a Plik value, useful when you want fine-grained control — incremental reading, seeking, mixing reads and writes:

niech p = Plik.nowy("./duzy_plik.txt", "r")
niech pierwsza_linia = p.czytaj_linie()
p.zamknij()

A few methods exist in both forms — czytaj, czytaj_linie, zapisz, zapisz_linie, rozmiar, czas_dostepu, czas_modyfikacji. The static version takes a path string and handles the open/close cycle; the instance version operates on an already-open Plik and gives you control over when to close it. Pick whichever fits your situation.

Constants

Plik.SEPARATOR — the directory separator used by the operating system. "/" on Unix-like systems, "\\" on Windows.

Plik.ALT_SEPARATOR — the alternative separator on systems that have one (Windows treats / as an alternative). Empty string on Unix.

Plik.PATH_SEPARATOR — the separator used between paths in environment variables like PATH. ":" on Unix, ";" on Windows.

pokazl Plik.SEPARATOR         # "/"   on Linux/macOS
pokazl Plik.PATH_SEPARATOR    # ":"   on Linux/macOS

For building paths portably, prefer Plik.polacz(...) — it handles separator selection automatically.

Static — reading

Plik.czytaj(sciezka) — read the entire file as a string. Suitable for files small enough to fit in memory.

niech tresc = Plik.czytaj("./README.md")
pokazl tresc.dlg()

Plik.czytaj_linie(sciezka) — read all lines as an array of strings. Trailing newlines are stripped from each line.

niech linie = Plik.czytaj_linie("./konfiguracja.txt")
dla linia w linie {
    pokazl linia
}

For very large files where you’d rather process line by line without loading everything into memory, use the instance form: Plik.nowy(sciezka, "r") and then iterate with czytaj_linie() calls in a loop, or use czytaj_linie_raw() to keep the trailing newlines if you need them.

Static — writing

Plik.zapisz(sciezka, tresc) — write tresc to the file, replacing any existing content. If the file doesn’t exist, it’s created. If the parent directory doesn’t exist, the call fails.

Plik.dopisz(sciezka, tresc) — append tresc to the end of the file, preserving existing content. Creates the file if it doesn’t exist.

Plik.zapisz_linie(sciezka, linie) — write an array of strings as separate lines, joined with newlines (and with a trailing newline). If linie is a single string, it’s written as one line.

Plik.zapisz("./out.txt", "linia 1\nlinia 2")
Plik.dopisz("./log.txt", "[INFO] start\n")

Plik.zapisz_linie("./lista.txt", ["alfa", "beta", "gamma"])
# Produces a file with three lines: "alfa\nbeta\ngamma\n"

Static — path queries

These check properties of a path without actually reading file content. They’re cheap to call and don’t fail on missing paths — they just return falsz.

Plik.istnieje(sciezka)prawda if anything (file, directory, symlink) exists at that path.

Plik.czy_plik(sciezka)prawda if the path is a regular file (not a directory or symlink).

Plik.czy_katalog(sciezka)prawda if the path is a directory.

Plik.czy_dowiazanie(sciezka)prawda if the path is a symbolic link.

Plik.czy_pusty(sciezka)prawda if the file has zero bytes, or if the directory is empty. Returns prawda for non-existent paths as well.

jesli Plik.istnieje("./dane.json") {
    niech d = Json.parsuj_plik("./dane.json")
}

jesli !Plik.czy_katalog("./output") {
    Plik.utworz_katalog("./output")
}

Plik.czy_odczytywalny(sciezka)prawda if the current process can read the path.

Plik.czy_zapisywalny(sciezka)prawda if the current process can write to the path.

Plik.czy_wykonywalny(sciezka)prawda if the path is marked executable.

These check effective permissions, accounting for the current user, group, and “other” bits.

Static — path components

Plik.nazwa(sciezka) / Plik.nazwa(sciezka, rozszerzenie) — file name (last component of the path). If a second argument is given, that suffix is stripped from the result.

pokazl Plik.nazwa("/home/anna/raport.pdf")          # "raport.pdf"
pokazl Plik.nazwa("/home/anna/raport.pdf", ".pdf")  # "raport"

Plik.katalog(sciezka) — directory part of the path (everything before the last /).

pokazl Plik.katalog("/home/anna/raport.pdf")    # "/home/anna"
pokazl Plik.katalog("plik.txt")                 # "."

Plik.rozszerzenie(sciezka) — file extension including the leading dot, or "" if there is none.

pokazl Plik.rozszerzenie("raport.pdf")        # ".pdf"
pokazl Plik.rozszerzenie("README")            # ""
pokazl Plik.rozszerzenie(".bashrc")           # ""

Plik.pelna_sciezka(sciezka) — expand ~, resolve .. and ., and return an absolute path. Does not require the file to actually exist.

pokazl Plik.pelna_sciezka("./test")     # "/home/user/projekt/test"
pokazl Plik.pelna_sciezka("~/dane")     # "/home/user/dane"

Plik.rzeczywista_sciezka(sciezka) — like pelna_sciezka but also resolves symlinks. The path must exist — fails otherwise.

Plik.polacz(...) — join path components using the platform’s separator. Variadic: takes any number of arguments.

pokazl Plik.polacz("home", "anna", "dane.txt")     # "home/anna/dane.txt" on Unix
pokazl Plik.polacz(".", "src", "main.as")          # "./src/main.as"

Plik.podziel(sciezka) — split a path into [directory, filename] as a 2-element array.

pokazl Plik.podziel("/var/log/app.log")    # ["/var/log", "app.log"]

Static — file metadata

Plik.rozmiar(sciezka) — file size in bytes, as an integer.

niech rozm = Plik.rozmiar("./video.mp4")
pokazl "Plik ma #{rozm} bajtów"

Plik.czas_dostepu(sciezka) — last access time as a Czas value.

Plik.czas_modyfikacji(sciezka) — last modification time as a Czas value.

Plik.czas_utworzenia(sciezka) — file creation time as a Czas value. On systems that don’t track creation time (most older filesystems), falls back to ctime (inode change time).

Plik.typ(sciezka) — file type as a string: "file", "directory", "link", "characterSpecial", "blockSpecial", "fifo", "socket", or "unknown".

import("czas")
niech kiedy = Plik.czas_modyfikacji("./README.md")
pokazl "Ostatnia zmiana: #{kiedy.do_tekstu_pl()}"

pokazl Plik.typ("./README.md")    # "file"
pokazl Plik.typ("./src")          # "directory"

Static — file manipulation

Plik.usun(sciezka) — delete a file. Fails if the path is a directory (use usun_katalog for that) or doesn’t exist. Returns prawda.

Plik.zmien_nazwe(stara, nowa) — rename a file or directory. Both arguments are paths. Atomic on the same filesystem.

Plik.kopiuj(zrodlo, cel) — copy a file. Overwrites the destination if it exists.

Plik.przesun(zrodlo, cel) — move a file. Equivalent to copy-then-delete, but more efficient on the same filesystem.

Plik.kopiuj("./template.html", "./output/page.html")
Plik.zmien_nazwe("./tmp.dat", "./final.dat")
Plik.usun("./old_log.txt")

Plik.utworz_katalog(sciezka) — create a directory, including any missing parent directories. No error if the directory already exists. Returns prawda.

Plik.usun_katalog(sciezka) — recursively delete a directory and everything in it. Use with care.

Plik.utworz_katalog("./output/raporty/2024")    # creates all three levels if needed
Plik.usun_katalog("./tmp")

Plik.zmien_uprawnienia(sciezka, tryb) — change file permissions to the given Unix mode (an integer like 0o644 or 420 in decimal).

Static — directory listing

Plik.lista(sciezka) / Plik.lista(sciezka, wzor) — list directory entries. With no pattern, returns everything (excluding . and ..). With a glob pattern ("*.txt", "raport_*", etc.), returns only matches.

niech wszystkie = Plik.lista("./src")
niech tylko_as = Plik.lista("./src", "*.as")

dla plik w tylko_as {
    pokazl plik
}

Plik.lista_rekurencyjna(sciezka) / Plik.lista_rekurencyjna(sciezka, wzor) — like lista but descends into subdirectories. The pattern, if given, is matched against full paths.

niech kazdy_as = Plik.lista_rekurencyjna(".", "*.as")
pokazl "Liczba plików .as: #{kazdy_as.dlg()}"

Static — temporary files and current directory

Plik.plik_tymczasowy() / Plik.plik_tymczasowy(prefiks) — return the path of a freshly created temporary file. The prefiks defaults to "alexscript" and helps identify the file’s origin.

Plik.katalog_tymczasowy() — return the path of the system’s temp directory (e.g. /tmp on Linux).

Plik.biezacy_katalog() — return the current working directory.

Plik.zmien_katalog(sciezka) — change the working directory.

pokazl Plik.biezacy_katalog()         # "/home/anna/projekt"
Plik.zmien_katalog("./tests")
pokazl Plik.biezacy_katalog()         # "/home/anna/projekt/tests"

Instance methods — opening a handle

Plik.nowy(sciezka) / Plik.nowy(sciezka, tryb) — open a file and return a Plik instance. The mode defaults to "r" (read).

Common mode strings:

niech p = Plik.nowy("./dane.txt", "r")
niech tresc = p.czytaj()
p.zamknij()

It’s important to call zamknij() when you’re done — open file handles are a limited OS resource. For cases where this is hard to guarantee (early returns, exceptions), prefer the static convenience methods, which handle close-on-exit automatically.

Instance — reading

p.czytaj() / p.czytaj(n) — read the rest of the file, or the next n bytes.

niech p = Plik.nowy("./binarny.dat", "rb")
niech naglowek = p.czytaj(16)     # first 16 bytes
niech reszta = p.czytaj()          # everything else
p.zamknij()

p.czytaj_linie() — read all remaining lines as an array of strings, with trailing newlines stripped.

p.czytaj_linie_raw() — same, but keeps trailing newlines on each line.

p.czytaj_bajty(n) — read the next n bytes and return them as an array of integer values (0–255), not as a string. Useful for binary data.

p.czytaj_znak() — read and return the next character as a string. Returns "" at end of file.

niech p = Plik.nowy("./tekst.txt", "r")
niech znak = p.czytaj_znak()
dopoki znak != "" {
    pokaz znak
    znak = p.czytaj_znak()
}
p.zamknij()

Instance — writing

p.zapisz(dane) — write a string to the file at the current position.

p.zapisz_linie(linie) — write an array of lines, or a single line. Each entry gets a trailing newline.

p.wyczysc() — truncate the file to zero length and rewind to the beginning. Useful for “open existing, reset, and write fresh” patterns.

p.flush() — flush any buffered output to disk. Without flushing, writes may stay in memory until the file is closed.

niech p = Plik.nowy("./log.txt", "a")
p.zapisz("nowy wpis\n")
p.flush()
p.zamknij()

Instance — cursor and position

p.przewin() — move the read/write cursor to the beginning of the file.

p.przesun_kursor(pozycja) / p.przesun_kursor(pozycja, skad) — move the cursor to a specific position. The skad argument controls the reference point: 0 (default) for absolute from the start, 1 for relative to current position, 2 for relative to the end.

p.pozycja() — return the current cursor position in bytes from the start.

niech p = Plik.nowy("./dane.txt", "r")
p.czytaj(100)
pokazl p.pozycja()       # 100
p.przewin()
pokazl p.pozycja()       # 0
p.zamknij()

Instance — state and metadata

p.zamknij() — close the file. Idempotent — calling it on an already-closed file is fine.

p.czy_zamkniety()prawda if the file has been closed.

p.czy_koniec()prawda if the cursor has reached the end of the file.

p.sciezka() — the path the file was opened with.

p.rozmiar() — current file size in bytes.

p.tryb() — file mode as an octal string (e.g. "100644").

p.czas_modyfikacji() — last modification time as a Czas value.

p.czas_dostepu() — last access time as a Czas value.

niech p = Plik.nowy("./dane.txt", "r")
dopoki !p.czy_koniec() {
    niech linia = p.czytaj_linie()
    # process line
    zakoncz       # we're using czytaj_linie which reads everything; just one iteration
}
p.zamknij()

Json — JSON serialization

The Json library handles parsing JSON text into AlexScript values and serializing AlexScript values back into JSON text. It’s built on Ruby’s standard JSON module — fast, well-tested, and standards-compliant.

The mapping between JSON and AlexScript is direct: JSON objects become AlexScript objects, JSON arrays become arrays, JSON strings/numbers/booleans become their AlexScript equivalents, and JSON null becomes nic.

import("json")

# Parse a string
niech dane = Json.parsuj("{\"imie\": \"Anna\", \"wiek\": 30}")
pokazl dane["imie"]      # "Anna"
pokazl dane["wiek"]      # 30

# Serialize a value
niech json = Json.generuj({"klucz": "wartosc", "lista": [1, 2, 3]})
pokazl json              # {"klucz":"wartosc","lista":[1,2,3]}

Json is a static-only class. Calling Json.nowy() raises Json jest klasą statyczną i nie może być instancjonowana.

A note on number types: JSON has only one numeric type, but AlexScript distinguishes integers from floats. Round-tripping through JSON preserves the difference — 42 parses back to a calkowita, while 42.0 parses back to a zmiennoprzecinkowa.

Parsing

Json.parsuj(tekst) — parse a JSON string. The result is an AlexScript value mirroring the JSON structure: objects → objects, arrays → arrays, strings → strings, numbers → integers or floats, booleans → prawda/falsz, nullnic.

# Object
niech obj = Json.parsuj("{\"a\": 1, \"b\": [2, 3]}")
pokazl obj["a"]         # 1
pokazl obj["b"][0]      # 2

# Array
niech arr = Json.parsuj("[1, 2, 3]")
pokazl arr.dlg()        # 3

# Null and booleans
niech d = Json.parsuj("{\"t\": true, \"f\": false, \"n\": null}")
pokazl d["t"]           # prawda
pokazl d["f"]           # falsz
pokazl d["n"]           # nic

If the input isn’t valid JSON, Json.parsuj raises a runtime error. To handle that gracefully, use parsuj_bezpiecznie (below) or wrap the call in a proba/zlap.

Json.parsuj_bezpiecznie(tekst) — like parsuj, but returns nic instead of raising on invalid input. Useful for “best effort” parsing where you don’t want to commit to error handling.

niech wynik = Json.parsuj_bezpiecznie(podejrzane_dane)
jesli wynik == nic {
    pokazl "To nie był poprawny JSON"
} albo {
    # use wynik
}

Json.parsuj_plik(sciezka) — read a file and parse it as JSON. Equivalent to Json.parsuj(Plik.czytaj(sciezka)) but avoids the intermediate string variable.

niech konfiguracja = Json.parsuj_plik("./config.json")
pokazl konfiguracja["wersja"]

Generating

Json.generuj(wartosc) — serialize an AlexScript value to compact JSON. The output has no extra whitespace — keys, values, and commas are packed tightly.

pokazl Json.generuj({"a": 1, "b": "tekst"})
# {"a":1,"b":"tekst"}

pokazl Json.generuj([1, 2, [3, 4]])
# [1,2,[3,4]]

Json.generuj_ladnie(wartosc) / Json.generuj_ladnie(wartosc, wciecie) — pretty-printed JSON with newlines and indentation. The default indent is 2 spaces; pass an integer to change it.

pokazl Json.generuj_ladnie({"imie": "Anna", "wiek": 30})
# {
#   "imie": "Anna",
#   "wiek": 30
# }

pokazl Json.generuj_ladnie({"a": 1}, 4)    # 4-space indent

Pretty-printed output is human-friendly but larger; compact output is what you want for transmission and storage where every byte counts.

Writing to files

Json.generuj_plik(sciezka, wartosc) / Json.generuj_plik(sciezka, wartosc, ladnie) — serialize a value and write it to a file. Pass prawda as the third argument for pretty-printed output. Returns the number of bytes written.

niech dane = {"użytkownicy": [{"imie": "Jan", "wiek": 30}]}

# Compact
Json.generuj_plik("./dane.json", dane)

# Pretty-printed
Json.generuj_plik("./dane_pretty.json", dane, prawda)

Validation

Json.czy_poprawny(tekst)prawda if the string parses as valid JSON, falsz otherwise. Doesn’t raise on invalid input.

pokazl Json.czy_poprawny("{\"a\": 1}")    # prawda
pokazl Json.czy_poprawny("{a: 1}")        # falsz   (unquoted key)
pokazl Json.czy_poprawny("nie json")      # falsz

Faster than parsuj_bezpiecznie if you only want to know whether the string is valid, since it doesn’t have to build the result value.

Object utilities

A few helpers for working with parsed JSON objects. These are conveniences — the same things can be done with the built-in object methods (.klucze(), .wartosci()).

Json.polacz(obj1, obj2) — merge two objects. Keys present in obj2 override the same keys in obj1. Both arguments must be objects (not arrays or scalars) — otherwise raises Oba argumenty muszą być obiektami.

niech a = {"x": 1, "y": 2}
niech b = {"y": 99, "z": 3}

pokazl Json.polacz(a, b)
# {"x": 1, "y": 99, "z": 3}

Json.klucze(obj) — return an array of the object’s keys. The argument must be an object.

Json.wartosci(obj) — return an array of the object’s values, in the same order as the keys.

niech obj = {"a": 1, "b": 2, "c": 3}
pokazl Json.klucze(obj)      # ["a", "b", "c"]
pokazl Json.wartosci(obj)    # [1, 2, 3]

These do the same thing as the built-in .klucze() / .wartosci() methods on objects — they exist on Json for stylistic consistency when you’re already working with JSON-derived data.

SecureRandom — cryptographic randomness

The SecureRandom library produces random values suitable for security-sensitive uses: passwords, session tokens, API keys, encryption nonces, unique IDs. It’s built on Ruby’s standard SecureRandom module, which draws entropy from the operating system’s secure random source (/dev/urandom on Unix, CryptGenRandom on Windows).

Use SecureRandom whenever the value needs to be unpredictable to an attacker. For non-security uses where you just need “some random number” — simulations, sampling, games — use the faster but non-cryptographic Mat.losowa() family instead.

import("securerandom")

niech token_sesji = SecureRandom.token()      # 32-char URL-safe token
niech id = SecureRandom.uuid()                 # standard UUID
niech klucz_api = SecureRandom.hex(32)         # 64-char hex string

SecureRandom is a static-only class. Calling SecureRandom.nowy() raises SecureRandom jest klasą statyczną — użyj SecureRandom.hex(), SecureRandom.uuid() itd..

Every method produces a fresh random value on every call — outputs are practically guaranteed to be unique across calls within a process and across processes:

pokazl SecureRandom.uuid() != SecureRandom.uuid()    # prawda
pokazl SecureRandom.token() != SecureRandom.token()  # prawda

Hex strings

SecureRandom.hex() / SecureRandom.hex(n) — hex-encoded random string. The argument is the number of bytes of randomness; the resulting string is twice as long because each byte is two hex characters. Default is 16 bytes → 32 hex characters.

pokazl SecureRandom.hex()        # 32-char hex (16 bytes of randomness)
pokazl SecureRandom.hex(10)      # 20-char hex (10 bytes)
pokazl SecureRandom.hex(32)      # 64-char hex (32 bytes — typical for keys)

Hex output is great when you need a string that’s safe everywhere — only digits and a-f, no special characters. It’s slightly less compact than base64, but the simplicity is often worth it.

Base64 strings

SecureRandom.base64() / SecureRandom.base64(n) — base64-encoded random string. The argument is the number of bytes; default is 16. Output may contain +, /, and = padding characters.

SecureRandom.urlsafe_base64() / SecureRandom.urlsafe_base64(n) — base64 with the URL-safe alphabet: + becomes -, / becomes _, no padding =. Safe to use directly in URL paths and query parameters.

niech zwykly = SecureRandom.base64(20)
niech bezpieczny = SecureRandom.urlsafe_base64(20)

pokazl bezpieczny.zawiera("+")    # falsz
pokazl bezpieczny.zawiera("/")    # falsz

For tokens that will be embedded in URLs or filenames, always prefer urlsafe_base64 over base64.

UUIDs

SecureRandom.uuid() — generate a UUID v4 (random). Returns a 36-character string in the standard 8-4-4-4-12 format, e.g. "550e8400-e29b-41d4-a716-446655440000".

pokazl SecureRandom.uuid()
# e.g. "f47ac10b-58cc-4372-a567-0e02b2c3d479"

UUIDs are great for identifiers that need to be unique without coordination — database primary keys, distributed system message IDs, file names that won’t collide across machines.

Alphanumeric strings

SecureRandom.alfanumeryczny() / SecureRandom.alfanumeryczny(n) — random string of letters and digits, exactly n characters long. Default n is 16. Uses both upper and lower case letters plus digits 0–9 (62 possible characters per position).

pokazl SecureRandom.alfanumeryczny()        # 16-char string
pokazl SecureRandom.alfanumeryczny(8)       # e.g. "Kp3Hx9Dq"
pokazl SecureRandom.alfanumeryczny(64)      # 64-char string

Useful when you need a random string of an exact length and want to avoid the variable-length output of base64.

Numeric values

SecureRandom.losowa_liczba() / SecureRandom.losowa_liczba(n) — a random number. With no argument, returns a float in [0, 1). With an integer argument n, returns an integer in [0, n).

niech ulamek = SecureRandom.losowa_liczba()       # e.g. 0.7234819203
niech kostka = SecureRandom.losowa_liczba(6)      # 0..5 (six-sided)
niech wybor = SecureRandom.losowa_liczba(100)     # 0..99

SecureRandom.losowa_z_zakresu(min, max) — random integer in the inclusive range [min, max].

pokazl SecureRandom.losowa_z_zakresu(1, 100)     # 1..100
pokazl SecureRandom.losowa_z_zakresu(5, 5)       # always 5

Note the difference from losowa_liczba(n): losowa_liczba(100) returns 0..99 (exclusive upper bound), while losowa_z_zakresu(0, 100) returns 0..100 (inclusive on both ends).

Random bytes

SecureRandom.losowe_bajty() / SecureRandom.losowe_bajty(n) — array of n random bytes (each value 0–255). Default n is 16.

niech bajty = SecureRandom.losowe_bajty(16)
pokazl bajty.dlg()        # 16
pokazl bajty[0]           # some integer 0..255

Useful when you want to feed random bits into a binary protocol, encryption key derivation, or file format — anywhere you need raw bytes rather than encoded strings.

Tokens

SecureRandom.token() / SecureRandom.token(n) — URL-safe token of exactly n characters. Default n is 32. Internally generates URL-safe base64 and slices it to the requested length.

niech ctoken = SecureRandom.token()       # 32-char URL-safe
niech longer = SecureRandom.token(64)      # 64-char URL-safe

The standard pick for session tokens, API keys, password reset tokens, and similar — long enough to be unguessable, short enough to fit comfortably in HTTP headers and URLs.

Custom alphabet

SecureRandom.wybierz(znaki, dlugosc) — generate a random string of the given length, using only characters from the znaki alphabet. Each position picks uniformly from the available characters.

# Numeric PIN
pokazl SecureRandom.wybierz("0123456789", 6)        # e.g. "847219"

# Lowercase only
pokazl SecureRandom.wybierz("abcdefghijklmnopqrstuvwxyz", 10)
# e.g. "kjqxnvbpzm"

# Custom set
pokazl SecureRandom.wybierz("ABCDEFGH123", 4)        # e.g. "C2FH"

Useful for human-friendly codes (no ambiguous characters like 0/O or l/1) and when you need to integrate with systems that have alphabet restrictions.

Socket — TCP and UDP networking

The socket library exposes four classes that together cover most network programming needs:

All of these integrate with AlexScript’s async scheduler — when you czytaj_linie() from a socket inside an async function, the call cooperatively yields instead of blocking the whole reactor. This means you can write straightforward request-handler code and have it scale to many concurrent connections without callbacks. See Async with native I/O in the main tutorial.

import("socket")

# Quick example: echo client
niech kl = SocketTcp.nowy("127.0.0.1", 8080)
kl.wyslij_linie("witaj")
pokazl kl.czytaj_linie()
kl.zamknij()

The four classes use the same closing pattern: s.zamknij() shuts down the socket, s.czy_zamkniety() tells you whether it’s been closed. It’s important to close every socket you open — they’re a limited OS resource.

SocketTcp — TCP client

A connected TCP socket, used to talk to a TCP server. Construct it with the host and port; the connection is established during construction.

SocketTcp.nowy(host, port) — connect to host:port. Raises a runtime error on connection failure (refused, timeout, DNS resolution failure).

niech kl = SocketTcp.nowy("example.com", 80)

You can also obtain a SocketTcp from a SerwerTcp.akceptuj() call — that’s how the server side gets a handle for talking to the connected client.

Sending data

s.wyslij(dane) — write a string to the socket. Returns the number of bytes written.

s.wyslij_linie(dane) — write a string with a trailing newline. Returns the total bytes written (including the newline).

kl.wyslij("GET / HTTP/1.0\r\n\r\n")
kl.wyslij_linie("PING")        # sends "PING\n"

s.flush() — flush any buffered output to the network immediately.

Receiving data

s.odbierz(rozmiar) / s.odbierz() — read up to rozmiar bytes (default 4096) from the socket. Returns whatever has arrived so far, which may be less than rozmiar.

s.czytaj(n) — read exactly n bytes (or all remaining bytes if no argument). Blocks until that much data is available or the connection closes.

s.czytaj_linie() — read one line from the socket, stripping the trailing newline. Returns "" if the connection has closed.

s.czytaj_wszystkie_linie() — read all remaining lines as an array. Blocks until the peer closes the write side.

niech naglowek = kl.odbierz(1024)
niech cale_cialo = kl.czytaj()
niech jedna_linia = kl.czytaj_linie()
niech wszystkie = kl.czytaj_wszystkie_linie()

The odbierz vs czytaj distinction matters: odbierz returns whatever’s available right now (up to the limit), while czytaj(n) keeps reading until exactly n bytes have been collected. For protocols where you parse messages of known length, use czytaj(n); for streaming where you want to react to whatever’s arrived, use odbierz.

Closing and half-closing

s.zamknij() — close both directions of the socket. Idempotent — calling on an already-closed socket is fine.

s.zamknij_zapis() — close the write half only. The peer will see end-of-file, but you can still receive their response. Useful for “I’m done sending; tell me when you’re done sending too” protocols.

s.zamknij_odczyt() — close the read half only.

s.zamknij_odlozone() — schedule the socket for cooperative closure via the async reactor. Useful in async contexts where immediate close could disrupt in-flight I/O on other fibers.

s.czy_zamkniety()prawda if either direction has been closed.

kl.wyslij_linie("ostatnia wiadomosc")
kl.zamknij_zapis()                  # tell peer we're done
niech odpowiedz = kl.czytaj()       # but still read their reply
kl.zamknij()

Address information

s.adres_lokalny() — return our end of the connection as {"adres": "...", "port": N}.

s.adres_zdalny() — return the peer’s end of the connection in the same format.

niech moj = kl.adres_lokalny()
niech peer = kl.adres_zdalny()
pokazl "Polaczono z #{peer["adres"]}:#{peer["port"]}"

Socket options

s.ustaw_timeout(sekundy) — set both send and receive timeouts to sekundy whole seconds. After the timeout, blocking operations fail with an error rather than hanging indefinitely.

s.ustaw_keepalive(wlacz) — enable or disable TCP keep-alive probes. Pass prawda to enable, falsz to disable. Useful for long-lived connections where you want to detect dead peers.

s.ustaw_nodelay(wlacz) — enable or disable Nagle’s algorithm (TCP_NODELAY). Pass prawda to disable Nagle (low latency, more packets), falsz to keep it enabled (higher throughput, more buffering).

kl.ustaw_timeout(30)
kl.ustaw_keepalive(prawda)
kl.ustaw_nodelay(prawda)        # for interactive protocols

SerwerTcp — TCP server

A listening socket that accepts incoming TCP connections. Each accepted connection becomes a separate SocketTcp that you can read from and write to independently.

SerwerTcp.nowy(port) / SerwerTcp.nowy(port, adres) — bind to a port and start listening. The default address is "0.0.0.0" (listen on all interfaces). Pass "127.0.0.1" to listen only on localhost. The constructor automatically sets SO_REUSEADDR, so a recently-killed server can immediately rebind to the same port.

niech srv = SerwerTcp.nowy(8080)              # all interfaces
niech lokalny = SerwerTcp.nowy(8080, "127.0.0.1")    # localhost only

Accepting connections

s.akceptuj() — block until a client connects, then return a SocketTcp for the new connection.

niech srv = SerwerTcp.nowy(8080)

petla {
    niech klient = srv.akceptuj()
    niech zapytanie = klient.czytaj_linie()
    klient.wyslij_linie("Otrzymano: #{zapytanie}")
    klient.zamknij()
}

In an async context, akceptuj() cooperatively yields while waiting, so other fibers can make progress.

Server lifecycle

s.zamknij() — close the listener. Connections already established stay open; only new ones are refused.

s.czy_zamkniety()prawda if the server has been closed.

s.adres_lokalny() — return {"adres": "...", "port": N} for the listening address.

s.port() — convenience for getting just the listening port. Useful when you opened on port 0 (kernel-assigned) and want to know what you got.

s.ustaw_nasluchiwanie(max) — set the listen backlog (maximum pending connections). The OS default is usually fine, but in high-throughput servers you may want to raise this.

niech srv = SerwerTcp.nowy(0)            # let kernel pick port
pokazl "Słucham na porcie #{srv.port()}"

Built-in connection loop

s.uruchom_petle(callback) — start an accept loop that handles each incoming connection in its own thread, invoking callback(klient) per connection. The callback receives the SocketTcp and is responsible for reading, writing, and closing it (closure happens automatically on callback exit, though).

This is a higher-level alternative to the manual petla { srv.akceptuj() ... } pattern — useful when you want concurrent handling without writing the threading code yourself.

funkcja obsluz(klient) {
    niech zapytanie = klient.czytaj_linie()
    klient.wyslij_linie("echo: #{zapytanie}")
}

niech srv = SerwerTcp.nowy(8080)
srv.uruchom_petle(obsluz)         # handles connections concurrently

uruchom_petle blocks the calling code — once started, it runs until the server socket is closed.

SocketUdp — UDP socket

UDP is connectionless — you send and receive datagrams to/from arbitrary peers without establishing a session first. A single SocketUdp can talk to many peers at once.

SocketUdp.nowy() — create an unbound UDP socket. To receive, you’ll need to zwiaz it to a port; to send only, no binding is necessary.

niech udp = SocketUdp.nowy()

Binding for receive

s.zwiaz(port) / s.zwiaz(port, adres) — bind the socket to a local port (and optionally a specific local address, default "0.0.0.0") so it can receive incoming datagrams.

niech srv = SocketUdp.nowy()
srv.zwiaz(5353)                  # listen on UDP port 5353, all interfaces

Sending datagrams

s.wyslij(dane, host, port) — send dane as a datagram to host:port. Each call sends exactly one datagram.

s.polacz(host, port) — bind a default destination so subsequent sends don’t need to specify it. Doesn’t actually establish a connection (UDP doesn’t have those) — it’s a convenience.

s.wyslij_polaczony(dane) — send a datagram to the previously-polacz-set destination.

niech kl = SocketUdp.nowy()
kl.wyslij("ping", "127.0.0.1", 5353)

# Or with implicit destination:
kl.polacz("127.0.0.1", 5353)
kl.wyslij_polaczony("ping1")
kl.wyslij_polaczony("ping2")

Receiving datagrams

s.odbierz(rozmiar) / s.odbierz() — wait for and return the next datagram. The default rozmiar is 4096; larger datagrams will be truncated. Returns an object with three keys:

niech wiadomosc = srv.odbierz()
pokazl "Z #{wiadomosc["adres"]}:#{wiadomosc["port"]}: #{wiadomosc["dane"]}"

Closing and inspection

s.zamknij() — close the socket.

s.czy_zamkniety()prawda if closed.

s.adres_lokalny() — return {"adres": "...", "port": N} for the bound address.

Socket — static helpers

The Socket class itself (different from SocketTcp etc.) provides utility functions for hostname resolution, IP lookup, and port management. It’s static — calling Socket.nowy() raises Socket jest klasą statyczną — użyj SocketTcp, SerwerTcp lub SocketUdp.

Hostname resolution

Socket.nazwa_hosta() — return the local machine’s hostname.

Socket.pobierz_adres_ip(nazwa) — resolve a hostname to a single IPv4 address (the first one returned by DNS).

Socket.pobierz_wszystkie_adresy(nazwa) — resolve a hostname to all of its IPv4 addresses, returned as an array of strings.

Socket.pobierz_nazwe_hosta(adres_ip) — reverse DNS — look up the hostname for an IP address. Returns the IP string itself if reverse resolution fails.

pokazl Socket.nazwa_hosta()                    # e.g. "moja-maszyna"
pokazl Socket.pobierz_adres_ip("localhost")    # "127.0.0.1"
pokazl Socket.pobierz_adres_ip("example.com")  # "93.184.215.14"

niech wszystkie = Socket.pobierz_wszystkie_adresy("dns.google")
pokazl wszystkie    # ["8.8.8.8", "8.8.4.4"]

Port utilities

Socket.czy_port_wolny(port) / Socket.czy_port_wolny(port, adres) — check whether a TCP port is free for binding. Default address is "127.0.0.1". Returns prawda if free, falsz if something is listening there.

Socket.wolny_port() — find and return an unused port number. Asks the kernel to assign one (binds port 0, reads back what was assigned, then immediately releases it). Useful for tests where you want a known-free port without hardcoding a number.

pokazl Socket.czy_port_wolny(80)        # likely falsz on a system with a web server
pokazl Socket.czy_port_wolny(54321)     # likely prawda

niech port = Socket.wolny_port()
niech srv = SerwerTcp.nowy(port)

The “find a free port” technique is racy in principle (something else could grab it between wolny_port() and SerwerTcp.nowy(port)), but it works reliably in practice for testing purposes.

Http — HTTP client

The Http library is a high-level HTTP client. You give it a URL and a verb, and it handles the connection, TLS for HTTPS, request encoding, redirects, and parsing the response into a friendly object format.

Http is a static-only class. Calling Http.nowy() raises Http jest klasą statyczną — użyj Http.get(), Http.post() itd..

import("http")

# Simplest request
niech odp = Http.get("https://example.com")
pokazl odp["status"]        # 200
pokazl odp["cialo"]          # the HTML

Every request method returns a response object — an AlexScript object with the same shape regardless of which verb you used. The fields are described in detail under The response object.

A few behaviors that apply to every method:

The response object

Every request returns an object with these keys:

Key Type Meaning
"status" integer HTTP status code, e.g. 200, 404, 500
"wiadomosc" string Status reason phrase, e.g. "OK", "Not Found"
"cialo" string Response body
"naglowki" object Response headers as an object (lowercase keys)
"czy_sukces" boolean prawda if 200 ≤ status < 300
"czy_przekierowanie" boolean prawda if 300 ≤ status < 400
"czy_blad_klienta" boolean prawda if 400 ≤ status < 500
"czy_blad_serwera" boolean prawda if 500 ≤ status < 600
niech odp = Http.get("https://api.example.com/users")

jesli odp["czy_sukces"] {
    niech dane = Json.parsuj(odp["cialo"])
    # ... process
} albojesli odp["czy_blad_klienta"] {
    pokazl "Błąd klienta: #{odp["status"]} #{odp["wiadomosc"]}"
} albojesli odp["czy_blad_serwera"] {
    pokazl "Błąd serwera: #{odp["status"]}"
}

After redirect-following, status reflects the final response — the one you actually want. Intermediate 301/302 hops are not exposed.

Verbs

Every HTTP verb has a corresponding method. The argument shape varies slightly: GET-like verbs take URL + headers + options; POST-like verbs take URL + body + headers + options.

Http.get(url) / Http.get(url, naglowki) / Http.get(url, naglowki, opcje) — perform a GET request.

niech odp = Http.get("https://api.example.com/users/42")
niech z_naglowkami = Http.get("https://api.example.com/protected",
    {"Authorization": "Bearer abc123"})

Http.post(url, cialo) / Http.post(url, cialo, naglowki) / Http.post(url, cialo, naglowki, opcje) — perform a POST request with the given body. The body is sent as-is — no automatic encoding.

Http.post("https://api.example.com/log", "ERROR: connection lost")

Http.put(url, cialo) / Http.put(url, cialo, naglowki) / Http.put(url, cialo, naglowki, opcje) — same shape as post, but PUT.

Http.patch(url, cialo) / Http.patch(url, cialo, naglowki) / Http.patch(url, cialo, naglowki, opcje) — same shape, but PATCH.

Http.delete(url) / Http.delete(url, naglowki) / Http.delete(url, naglowki, opcje) — perform a DELETE request. No body.

Http.head(url) / Http.head(url, naglowki) / Http.head(url, naglowki, opcje) — HEAD request. The response object’s cialo will always be empty (HEAD responses have no body by definition); the headers and status are still populated.

Http.options(url) / Http.options(url, naglowki) — OPTIONS request, used for CORS preflight and capability discovery.

niech ohead = Http.head("https://example.com/duzy_plik.zip")
pokazl ohead["naglowki"]["content-length"]    # check size before downloading

JSON convenience helpers

Most modern APIs send and receive JSON, so dedicated helpers handle the encoding/decoding automatically.

Http.get_json(url) / Http.get_json(url, naglowki) / Http.get_json(url, naglowki, opcje) — perform a GET, parse the response body as JSON, and return the parsed value directly (not the full response object). Sends Accept: application/json. Raises an error if the response isn’t valid JSON.

niech uzytkownik = Http.get_json("https://api.example.com/users/42")
pokazl uzytkownik["imie"]
pokazl uzytkownik["email"]

Http.post_json(url, dane) / Http.post_json(url, dane, naglowki) / Http.post_json(url, dane, naglowki, opcje) — serialize dane to JSON, POST it with Content-Type: application/json and Accept: application/json, then parse the response as JSON.

niech wynik = Http.post_json("https://api.example.com/users",
    {"imie": "Anna", "email": "[email protected]"})

pokazl wynik["id"]    # the server-assigned ID, parsed from JSON response

If dane is already a string, it’s sent as-is; otherwise it’s converted with Json.generuj. The argument can be any AlexScript value that’s JSON-serializable.

Http.put_json(url, dane) / Http.put_json(url, dane, naglowki) / Http.put_json(url, dane, naglowki, opcje) — same as post_json, but PUT. If the response isn’t JSON, returns the body as a plain string instead of raising.

Form-encoded POST

Http.post_formularz(url, dane) / Http.post_formularz(url, dane, naglowki) / Http.post_formularz(url, dane, naglowki, opcje) — POST with a form-encoded body (application/x-www-form-urlencoded). Useful for older APIs and traditional HTML form submissions.

niech odp = Http.post_formularz(
    "https://api.example.com/login",
    {"user": "anna", "pass": "tajne"}
)

The keys and values are URL-encoded automatically. Returns a full response object (not parsed JSON).

File download

Http.pobierz(url, sciezka) — download the body of a GET request directly to a file, streaming in chunks instead of loading everything into memory. Useful for large files.

Http.pobierz("https://example.com/duzy_plik.zip", "./pobrane.zip")

Returns prawda on success. Doesn’t follow redirects automatically — point it at a stable URL.

URL utilities

A handful of static methods for URL handling. These don’t make any network calls — they’re pure string operations.

Http.koduj_url(tekst) — percent-encode a string for safe inclusion in a URL. Spaces become %20 (not +), special characters get encoded, etc. The encoding follows the application/x-www-form-urlencoded rules.

Http.dekoduj_url(tekst) — reverse of koduj_url.

pokazl Http.koduj_url("witaj świecie!")          # "witaj%20%C5%9Bwiecie%21"
pokazl Http.dekoduj_url("witaj%20%C5%9Bwiecie")   # "witaj świecie"

Http.parsuj_url(url) — parse a URL into its components. Returns an object with eight keys:

Key Meaning
"schemat" scheme (e.g. "https")
"host" hostname
"port" port number as integer (0 if unspecified)
"sciezka" path
"zapytanie" query string (without the leading ?)
"fragment" fragment (without the leading #)
"uzytkownik" username from user:pass@host URL
"haslo" password from user:pass@host URL

Missing components come back as empty strings (or 0 for port).

niech p = Http.parsuj_url("https://example.com:8080/api/v1?k=v#sec")
pokazl p["schemat"]      # "https"
pokazl p["host"]         # "example.com"
pokazl p["port"]         # 8080
pokazl p["sciezka"]      # "/api/v1"
pokazl p["zapytanie"]    # "k=v"
pokazl p["fragment"]     # "sec"

Http.zbuduj_url(schemat, host) / Http.zbuduj_url(schemat, host, port) / Http.zbuduj_url(schemat, host, port, sciezka) / Http.zbuduj_url(schemat, host, port, sciezka, zapytanie) — assemble a URL from components. Trailing arguments default to sensible values (no port section, / path, no query).

pokazl Http.zbuduj_url("https", "example.com", 443, "/api", "q=1")
# "https://example.com:443/api?q=1"

Http.zbuduj_zapytanie(params) — build a URL-encoded query string from an object. Keys and values are URL-encoded automatically.

pokazl Http.zbuduj_zapytanie({"a": "1", "b": "spacja w wartosci"})
# "a=1&b=spacja+w+wartosci"

Http.parsuj_zapytanie(tekst) — reverse of zbuduj_zapytanie. Parse a query string into an object.

niech sp = Http.parsuj_zapytanie("x=10&y=20")
pokazl sp["x"]    # "10"
pokazl sp["y"]    # "20"

These utilities are useful both for handling incoming requests in a server and for building outgoing URLs in a client.

Options

The optional opcje argument on most methods is an object with these keys:

"timeout" — connect/read/write timeout in seconds. Default 30.

"przekierowania" — maximum number of redirects to follow. Default 5. Set to 0 to disable redirect following.

niech odp = Http.get("https://example.com",
    {},                                            # no extra headers
    {"timeout": 5, "przekierowania": 0})           # 5s timeout, no redirects