• Argentum got a two-way JSON support

    Andrey Kalmatskiy12/23/2024 at 04:48 0 comments

    Argentum got a JSON module with both Parser and Writer.

    Let's try Writer in action:

    We'll use the same classes as in JSON Parser example:

    class Point{
        x = 0f;
        y = 0f;
    } 
    class Polygon {
        name = "";
        points = Array(Point);
        isActive = false;
    }

     This function converts an array of Polygons into a JSON string:

    fn polygonsToJson(data Array(Polygon)) str {
        Writer.useTabs().arr {
            data.each `poly _.obj {
                _("name").str(poly.name);
                _("active").bool(poly.isActive);
                _("points").arr\poly.points.each `pt _.obj {
                    _("x").num(double(pt.x));
                    _("y").num(double(pt.y))
                }
            }
        }.toStr()
    }
    • As with parser, the JSON writer does not create any intermediate DOM-like data structures and as such has no memory and CPU overheads.
    • It can produce both compact and pretty-printed JSONs, having configurable indentations.
    • As with parser, the JSON-writing code has absolutely no syntax overhead - all it contains is field-name-to data mapping, data formatters and handlers of nested objects and arrays. No DSL could have more concise syntax.
    • Combining parser and writer we can produce feature-rich JSON-handling applications running at a speed of native code and enjoying safety of managed one.

  • Argentum apps can parse JSONs

    Andrey Kalmatskiy12/02/2024 at 20:36 0 comments

    JSON module for Argentum programming language allows to parse JSONs directly in application data structures, skipping the creation of JSON DOM. This takes two times less memory and reduces the time overheads by three-folds. The usual alternative to DOM parsers is streaming SAX parser, that requires application to have cumbersome state machine to handle data. Argentum JSON module doesn't use it either.

    JSON Parsing in Argentum is simple and extremely effective.

    For example, if we have such data structures:

    class Polygon {
        name = "";
        points = Array(Point);
        isActive = false;
    }
    class Point{
        x = 0f;
        y = 0f;
    }

    ... and we want to read JSONs of this structure:

    [
        {
            "active": false,
            "name": "p1",
            "points": [
                {"x": 11, "y": 32},
                {"y": 23, "x": 12},
                {"x": -1, "y": 4}
            ]
        },
        {
            "points": [
                {"x": 10, "y": 0},
                {"x": 0, "y": 10},
                {"y": 0, "x": 0}
            ],
            "active": true,
            "name": "Corner"
        }
    ]

    ... we can do it with this function: 

    using json { Parser }
    fn readPolygonsFromJson(data str) Array(Polygon) {
        Array(Polygon).{
           json = Parser.init(data);
           json.getArr\_.append(Polygon)-> json.getObj `f
              f=="active" ? _.isActive := json.getBool(false) :
              f=="name"   ? _.name := json.getStr("") :
              f=="points" ? json.getArr\_.points.append(Point)-> json.getObj `f (
                  f=="x" ? _.x := float(json.getNum(0.0)) :
                  f=="y" ? _.y := float(json.getNum(0.0)));
           json.success() : log("parsing error {json.getErrorMessage()}{json.getErrorPos()?" at {_.offset}"}");
        }
     }

    As you can see this function contains just:

    • mapping JSON field names to Argentum class fields,
    • data conversions/validation,
    • creations objects of types we want
    • populating collections we want the way you want

    It also checks and handles all format errors. And it is all in just 12 lines of code. 

    Despite its size, this function handles all the edge cases:

    • If input JSON has an unexpected field, this field is skipped.
    • This code is tolerant to any order of fields in objects.
    • If some needed field is absent from JSON, the it uses default values or calls handlers.
    • If some field, root element or array item in the JSON is of an unexpected type, it handles this as well.
    • Since all parsing is performed in plain Argentum code, we can easily add validating/transforming/versioning logic without inventing template-driven or string-encoded DSLs.
    • This parser is extremely rigid and resilient, it validates its input against JSON standard, detects and reports all errors.

    Let's call this function for completeness:

    myPolygons = readPolygonsFromJson("
      [{
            "active": false,
            "name": "p1",
            "points": [ {"x": 11, "y": 32}, {"y": 23, "x": 12}, {"x": -1, "y": 4}]
        },{
            "points": [{"x": 10, "y": 0}, {"x": 0, "y": 10}, {"y": 0, "x": 0}],
            "active": true,
            "name": "Corner"
        }]
    ")

    Pretty high-level for a language that compiles to tiny standalone machine code executables and runs at the speed of C++?

    Details are here https://aglang.org/json-module/

  • Argentum got a Web Playground

    Andrey Kalmatskiy02/05/2024 at 04:21 0 comments

    Now it's possible to try the Argentum programming language online.

    Playground allows to play with a number of pre-existing examples as well as start from scratch. It opens to the old good modify-compile-run-repeat game. Also it allows to go by the number of existing tutorials and try-on all the examples from the argentum website posts.

    All details and a link to the playground is here: https://aglang.org/playground/

    Happy hacking! (but please don't break my Raspberry Pi on which it all runs)

  • Argentum on ARM-64 and Raspberry Pi

    Andrey Kalmatskiy01/20/2024 at 04:37 0 comments

    Argentum compiler and runtime library successfully ported to ARM-64.
    It builds code for ARM-64.
    All compiled demos work fine on ARM-64/Raspberry Pi with tiny memory footprint and little CPU consumption.

    There are many platforms ahead: ARM/Android, ARM/OSX, WASM, RISCV.

  • Argentum apps can talk to the Internet

    Andrey Kalmatskiy01/13/2024 at 16:08 0 comments

    Argentum cURL bindings gives full support for reach http(s), ftp... functionality.

    using httpClient{ get, Response }
    
    ...
    
    get("https://google.com", app.&onFetched(r Response){
        log("Result code={r.status}: response size={r.body.capacity()}");
    })
    • All operations are performed asynchronously on a separate thread.
    • Multiple simultaneous queries share the same resources/DNS caches etc.
    • For ssh/https requests the system key-stores are used.
    • All operations are cancellable. Then the listener object dies, the operation cancels automatically.
    • The httpClient_Request object allows to customize requests by adding different verbs, request headers etc.
    • The httpClient_Response object allows to get access to response headers, status code, response body and all request data.

  • Argentum got SQLite integration

    Andrey Kalmatskiy01/07/2024 at 01:00 0 comments

    Example:

    Sqlite.open("../demo/mydb.sqlite", xReadWrite) ?
    _.query("
       SELECT "id", "name", "avatar"
       FROM "table"
       LIMIT 0, 20
    ", xSimpleQuery)
    .execute() row {
       id = row.intAt(0);
       name = row.stringAt(1);
       ava = row.stringAt(2);
       log(`${id} ${name} ${ava}`);
    "))

     This prints, depending on the actual database content:

    1 Andy  0x4563
    2 Paula 0x2321 

    The SQLiteFfi argentum module has multiple feature not shown in the example:

    • Connection can be stored and reused for multiple queries.
    • Queries can have parameters of int, string, blob types.
    • Queries can be stored and executed multiple times, possibly with different parameters.
    • Queries and connections can be created on different threads and result->object mapping can be performed on one thread with passing the resulting objects to the business logic threads.
    • The sqliteFfi is a low level API, you can build any query execution strategies atop of it.

    Currently  sqliteFfi links the full database engine inside the application that uses it, but this can be easily switched to DLL/so if needed.

  • Argentum got key-value containers

    Andrey Kalmatskiy12/31/2023 at 17:25 0 comments

    using sys { Map } 
    
    class Pt{            // A user-def class.
       x = 0;            // Its fields
       y = 0;
       call(ax, int, ay int) this { x:= ax; y := ay }  // One of constructors.
    }
    
    m = Map(String, Pt)        // Declare map with string keys and Pt values.
       ("Alpha", Pt(1, 2))     // Fill it with data.
       ("Bravo", Pt(-1, 42));
    
    log(
       m["Aplha"]                 // Access element by key.
          ? `Pt(${_.x}, ${_.y})`  // Check it and convert to string.
          : `Not found`);         // Handle if no such element.

    This will output: Pt(1, 2).

    Maps are not built-in entities in Argentum, in fact even Arrays are not built-in. They are all mere classes of standard library. And since all Argentum classes are extendable, you can always add arbitrary methods to arrays and maps.

    m.each() k v {
        log(`key: ${k}  x: ${v.x}  y: ${v.y}`);
    }

    In this example `each` is the ordinary method implemented for map class in one of `utils` modules.

    Any user type can be a key.

    Argentum:

    • supports user-defined hash-functions
    • caches the once-computed hash in the object header
    • provides built-in hash for strings and address-based hash for generic objects.

    Map class requires keys to be frozen objects. This prevents from common error in other languages then by modifying map keys a map can be brought to incoherent state.

    There are owning maps, maps of weak pointers and maps of shared pointers to immutable objects. So maps follow all Argentum rules of object composition, sharing and thread-safe handling - so no races, no crashes, no overheads. And topology aware copy operation supports map as well:

    m1 = @m;  // Makes a full deep copy of the map

    This code not only copies the map along with all its values (keys are shared since they are immutable) but also it handles cross-references between map elements preserving object graph topology. In other languages this operation requires peculiar hand-written code, while in Argentum it's automated.

    Internally Maps are implemented as flat, open addressed, 2^n grow, linear probe, robin hood hash-tables. So they are fast and not that much memory consuming.

  • Argentum ported to Linux

    Andrey Kalmatskiy11/18/2023 at 04:10 0 comments

    Argentum ported to Linux and run (both compiler and the compiled demo) on SteamOS

    To run the Argentum Demo on Ubuntu/Debian:

    # Download package
    wget -O aglan_0.0-1_amd64.deb \
    https://github.com/karol11/argentum/releases/download/win-lin-demo-v0.0.12/aglan_0.0-1_amd64.deb
    # ...And install it
    sudo dpkg -i aglan_0.0-1_amd64.deb

    To run the Argentum Demo on Steam Deck (or Arch Linux), follow these steps: https://aglang.org/install-argentum-demo-on-the-steamdeck-arch-linux/

    Demo contains fully functional compiler, runtime, examples and build script.

  • ​Argentum got Threads

    Andrey Kalmatskiy08/03/2023 at 13:07 0 comments

    Argentum threads act as lightweight processes that share the same address space.

    Immutable objects are freely shared across all threads while mutable ones belong to exactly one thread. Though threads can pass mutable objects one another.

    Threads can acts as workers having no internal mutable state or as microservices with state.

    Threads communicate with task queues. So no synchronization primitives needed, no races, no deadlocks.

    Moving to multithreaded mode didn't turn reference counters into atomic ones.

    Example:

    using sys{ Object, Thread, setMainObject, log }
    
    class App {   // Holds mutable state of the main thread.
        // Worker thread, having Object as its internal state.
        worker = Thread(Object).start(Object);
    }
    app = App;
    setMainObject(app);
    log("Started on main thread\n");
    // Post a new "workerCode" task to the worker Object
    // Since it's passed to the object of other thread
    // this code will be executed on that thread.
    app.worker.root().&workerCode(callback &()) {
        log("Hello from the worker thread\n");
        // Post another task, that we received as a parameter
        callback~();
    }~(app.&endEverything() {
        // This "endEverything" task is connected to the
        // main thread object. So it will be activated
        // on the main thread. We pass in to the `callback`
        // parameter.
        log("Shutdown from the main thread\n");
    
        // `?Object` is a null-pointer of type `Object`.
        // `setMainObject` here deletes previous App instance
        // and as such stops worker thread.
        // And since there is no application state object anymore,
        // application ends.
        setMainObject(?Object);
    });

     This code uses one existing and one new operator

    Create delegate:

    receiver.&delegateName(parameters) { code }

    Post delegate to its `receiver` thread queue as an asynchronous task and transfer all parameters to that thread

    delegate~(parameters);

    More details: https://aglang.org/multithreading/

    New Windows demo: https://github.com/karol11/argentum/releases

    with `src\threadTest.ag` example.

  • Argentum got a debugger

    Andrey Kalmatskiy06/05/2023 at 19:04 0 comments

    Argentum compiler builds a DWARF-compatible debug info, And it's all integrated with VSCode:

    • Ctrl+Shift+B builds and runs release versions.
    • F5 - starts debugging sessions.

    So far it supports:

    1. Variable and class inspection (including lambda capture blocks)
    2. Call stack + intermediate frames' variables and code locations
    3. Breakpoints
    4. Stepping through code.

    It has special handling for pointers to functions, lambdas and delegates, strings and limited support for arrays (it shows only 20 first elements).

    So far it shows only static type of object by pointers. As a temporary work-around you can use watch expressions with type casts (they can also be used to access any array elements beyond 20 elements boundary).

    All details are here: https://aglang.org/argentum-debugger-v1/