• ​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/

  • 99 bottles of beer on the wall

    Andrey Kalmatskiy05/28/2023 at 01:21 0 comments

    There is a famous song:

    /*
    99 bottles of beer on the wall, 99 bottles of beer.
    Take one down and pass it around, 98 bottles of beer on the wall.
    ...
    1 bottle of beer on the wall, 1 bottle of beer.
    Take one down and pass it around, no more bottles of beer on the wall.
    
    No more bottles of beer on the wall, no more bottles of beer.
    Go to the store and buy some more, 99 bottles of beer on the wall.
    */

     This song is sometimes used in computer science to compare languages wikipedia.

    And even more, there is a dedicated website, that collected 1500+ variations of this program in different languages.

    Let's look how this program can be written in Argentum:

    using sys {String, log}
    using utils { forRange }
    using string;
    
    forRange(-99, 1, (count) {
        bottles = (c, n) {
            c == 0 ? "{}
                {n}o more bottles
            " : "{}
                {c} bottle{c != 1 ? "s"}
            "
        };
        log("{}/
            {bottles(-count, "N")} of beer on the wall, {bottles(-count, "n")} of beer.
            {count == 0
                ? "Go to the store and buy some more"
                : "Take one down and pass it around"
            }, {bottles((99 - count) % 100, "n")} of beer on the wall.
        ");
    })

     Unlike code in Wikipedia, this solution does the thing:

    • it correctly adds the ending (s) to the second line,
    • it adds the "Go to the store" statement,
    • it adds "No more" to the beginning of the verse where it's needed,
    • it uses the correct case for "N" letter,
    • it follows DRY principle.

    All in all, despite its young age Argentum succeeded in solving this question in a straightforward and effective way (it compiled to 20k standalone Windows executable).

  • Argentum got Generics

    Andrey Kalmatskiy05/26/2023 at 22:04 0 comments

    Argentum classes and interfaces can be parameterized:

    class UnorderedMap(Key, Value) { ... }
    interface InStream(T) { ... }

    These parameters can be used in inheritance, field and method declarations as well as inside methods.

    class Map(K, V) {
       +Array(Pair(K, V));            // Base class parameterized with our class parameters
       getAt(key K) ?V { ... }        // Class parameters affect types of method prototypes
       setAt(key K, newVal V) { ... }
       forEach(visitor (K, V)void) { ... } // visitor lambda is typed with class parameters.
       ...
    }
    class Pair(A, B) {
      a = ?A;  // field `a` of type `Optional(A)` initialized with `none`
      b = ?B;  // Field types also depend on class parameters.
    }

    Right now the internal implementation of generics is does not allow instantiation-of and casts-to the parameters, but I can easily add them to the language in the future.

    So bottom line is:

    • Now it become very easy to implement a rich container and standard algorithms library right in the Argentum language without using C/C++.
    • Switching to generics allowed to remove all type-casts from all examples, tests and tutorials. It dramatically simplified source code.

    These generics do not introduce additional code, do not consume memory and do not cost CPU time.

  • Argentum got string interpolation

    Andrey Kalmatskiy05/18/2023 at 14:05 0 comments

    result = db.query("{}
         SELECT name, id
         FROM users
         WHERE name LIKE '{ encodeSql(request.userMask) }%'
         LIMIT {request.page * xPageSize}, {xPageSize};
    ");
    request.mode == Json
       ? response.send(result.toJson())
       : response.send("{}
            <ul>{
                result.forRecords((record) { "{}
                      <li data-id="{  record["id"]  }">
                          {  record["name"]  }
                      </li>
                "})
            }</ul>
        ")
    

     In this example we use string interpolation twice:

    • to build an SQL query
    • to convert query results to HTML

    This SQL query is a simple multiline text string with { placeholders } . Placeholders are Argentum expressions that return everything convertible to strings.

    HTML example demonstrates two nested interpolated strings - one for <ul> and one for nested <li>s.

    More on string interpolation: here.

  • Argentum got raw multiline string literals

    Andrey Kalmatskiy05/17/2023 at 20:20 0 comments

    // Program
    log("
         Hello,
         I'm a multiline "raw" string
    ");
    
    // Prints:
    Hello,
    I'm a multiline "raw" string
    

     These strings have no problems of their siblings in other languages:

    • No need for any special termination symbol/sequence - it's done by indentation.
    • Source code indentation is not included in the string.
    • Additional indentation can be added in the string to help formatting the output.
    • Line end encoding is controlled by developer. It doesn't depend on source code formatting. Make it CR, LF or any combinations.
    • Extra indentation, can go in form of tabs or spaces, this is also under developer's control.
    • Argentum also takes in account such peculiar details as last line endings and indentations.

    More examples and details are here: multiline-string-constants

  • Interop with C/C++

    Andrey Kalmatskiy05/08/2023 at 02:56 0 comments

    Argentum can natively call C/C++ functions, pass primitives, objects and raw structures (with sys_Blob object). Example:

    In C/C++

    #include <stdio.h>
    #include "../argentum/src/runtime/runtime.h"
    
    bool ag_fn_io_writeFile(AgString* name, AgString* content) {
      FILE* f = fopen(name->ptr, "wb");
      if (!f) return false;
      bool r = fputs(content->ptr, f) >= 0;
      fclose(f);
      return r;
    }

    In Argentum (file io.ag):

    using sys { String; log; }
    fn writeFile(name String, content String) bool;
    
    writeFile("1.txt", "File data") ? log("done")

    More details are here: ffi-tutorial

  • Argentum got const objects and shared pointers

    Andrey Kalmatskiy04/27/2023 at 17:18 0 comments

    Anrgentum objects can be turned immutable with prefix freeze operator "*". 

    class Point {
       x = 0;
       y = 0;
       set(x int, y int) this { this.x := x; this.y := y; }
    }
    p = *Point.set(10, 42);
    // now point p is immutable
    p.x += 1; // compilation error, `p` is *Point, and cannot be modified.

    Immutable objects are shared across object hierarchies and threads.

    There can be declared a methods, specifically callable to immutable objects and methods that cam be called on any objects - regardless their mutable status:

    class Node {
       name = "";
       chilren = Array;
       -isLeaf() bool { children.size() == 0 } 
    }
    // method `isLeaf` can be called on any Node - frozen or not

    Frozen pointers can reference one object from different other objects. They represent the "aggregation" relationships in terms of UML. With adding of this mechanism Argentum now supports all UML object relations:

    • Composition with unique "@pointers" to mutable objects
    • Aggregation with shared "*pointers" to immutable objects
    • Association with non owning "&pointers".

    Details are here: http://aglang.org/.....shared-pointers-to-immutable-objects

  • Argentum objects got parent pointers and safe-n-fast object reattachments

    Andrey Kalmatskiy04/27/2023 at 01:55 0 comments

    Each object can tell who is its parent.
    These parent pointer are maintained automatically at a very low cost (single move instruction).
    This ability simplifies the handling of document data models and can facilitate integrity checks if needed.

    sys_getParent(existingObject)

    Function sys_GetParent returns

    • object in which field this object is located,
    • or array object if it's in an array,
    • or null if this object is not nested in another object.

    Function result is ?sys_Object.

    This parent pointers also assist a new "splice" operation, that allows to extract an existing object from one part of the object hierarchy and insert it in other place:

    temp = object.field;           // save the link to object stored somewhere
    object.field := newValue;  // make it not stored there anymore
    // or
    object := newValue;         // or destroy the hierarchy it belonged
    
    anotherParentObject.field @= temp  // "splice" this object in another place.
    

     This allows to reorganize object hierarchies in safe and effortless manner (previously it required a copy operation).

    Details and more examples are in this post.

  • Improved modules and name resolution

    Andrey Kalmatskiy04/18/2023 at 20:23 0 comments

    Today Argentum got small but fundamental improvements:

    • Names are isolated in modules.
    • Explicit module dependencies.
    • lightweight imports.

    How it was previously (file `demo.ag`):

    using sdl;
    using utils;
    using gui;
    
    class Scene{
        +gui_Group;
        sdl = sdl_Sdl;
        w = sdl_Window;
        rnd = utils_Random;
        flare = sdl_Texture;
        star = sdl_Texture;
        background = sdl_Texture;
        foreground = sdl_Texture;
    ...

    How it is now:

    using sdl { Window, Sdl, Texture }
    using gui { Actor, Group }
    
    class Scene{
        +Group;
    
        sdl = Sdl;
        w = Window;
        rnd = Random;
        flare = Texture;
        star = Texture;
        background = Texture;
        foreground = Texture;

    Previously any compound names like `sdl_image_mySurface` were able to be created and used in any package. And they always had to be written in full.

    Since now names should follow rule: `packageName_functionOrClassName` (for example: `sdlImage_mySurface` or from example above `demo_Scene`. And since names are bound to a module, no need to write them in that long form.

    All names defined in a module `demo` are automatically `demo_*`.

    When a module declares some dependency with the `using` directive, it may list all names to import and use in short form: `using gui { Actor; Group; }`

    If names from different imports collide, give them aliases: `using gui { Control=Actor; }`
    Now `Control` can be used instead of `gui_Actor`.

    TL;DR: Argentum names just got better.

    More on names: link
    New compiler and stdlib version: windows demo