{"id":19492319,"url":"https://github.com/glouw/rr","last_synced_at":"2025-04-25T19:32:25.728Z","repository":{"id":128287567,"uuid":"436166702","full_name":"glouw/rr","owner":"glouw","description":"The Roman II Programming Language","archived":false,"fork":false,"pushed_at":"2022-04-28T00:48:27.000Z","size":1677,"stargazers_count":68,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-04T02:51:04.031Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/glouw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-12-08T08:12:01.000Z","updated_at":"2025-03-21T12:20:51.000Z","dependencies_parsed_at":"2024-01-29T08:09:46.649Z","dependency_job_id":"c4ab3c8f-da66-493d-9f58-01f0d91bfd1c","html_url":"https://github.com/glouw/rr","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glouw%2Frr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glouw%2Frr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glouw%2Frr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glouw%2Frr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/glouw","download_url":"https://codeload.github.com/glouw/rr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250882637,"owners_count":21502341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T21:20:24.870Z","updated_at":"2025-04-25T19:32:25.300Z","avatar_url":"https://github.com/glouw.png","language":"C","readme":"# The ROMAN II Programming Language\n\nRoman II is a dynamic programming language with a naive mark and sweep garbage\ncollector, all written from the ground up in about 5000 lines of the GNU11 dialect of C.\n\n\n```\nFib(n)\n{\n    if(n == 0)\n    {\n        ret 0;\n    }\n    elif((n == 1) || (n == 2))\n    {\n        ret 1;\n    }\n    else\n    {\n        ret Fib(n - 1) + Fib(n - 2);\n    }\n}\n\nMain()\n{\n    Print(Fib(25));\n    ret 0;\n}\n```\n\n## Installation\n\n```\nmake install\n```\n\n## Usage\n\n```\nroman2 Main.rr\n```\nFlags passed to `roman2`:\n\n```\n-d: Output bytecode and .data segment then terminate. Do not run the program.\n```\n\n## Program Entry\n\nRoman II enters and starts execution from function `Main`. The `Main` function\nmust return a number.\n\n```\nMain()\n{\n    Print(\"hello, world\");\n    ret 0;\n}\n```\n\n## Value Types\n\nAside from numbers which are of double precision, Roman II supports maps,\nqueues, files, strings, booleans, nulls, and pointers, the latter which may\npoint to functions or variables.\n\n## Numbers\n\nVariable assignment with Roman II is done with the `:=` operator.\nVariables are mutable and can be reassigned:\n\n```\nMain()\n{\n    a := 1;\n    a = 2;\n    Assert(a == 2);\n    ret 0;\n}\n```\n\nOperators compatible with numbers include:\n\n```\n+  : Addition\n-  : Subtraction\n/  : Division\n*  : Multiplication\n%  : Floating Point Modulus\n** : Power Of\n%% : Integer Modulus\n// : Integer Division\n```\n\nBoolean operators include:\n\n```\n== : Equal To\n!= : Not Equal To\n\u003c= : Less Than or Equal To\n\u003e= : Greater Than or Equal To\n\u003e  : Greater Than\n\u003c  : Less Than\n```\n\nEach operator supports its relational variant: `+=`, `-=`, `/=`, `*=`, `%=`, `**=`, `%%=`, `//=`.\n\nOperators and relational variants have optional support for `queue`, `map`, and `string` types.\n\n## Queues\n\nQueues (also known as lists with O(1) front and back operations), can store value types:\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3, 4];\n    ret 0;\n}\n```\n\nQueues can be appended to, and prepended to, with the `+=` and `-=` operators:\n\n\n```\nMain()\n{\n    queue := [0, 1, 2];\n    queue += 3;\n    queue += 4;\n    queue -= -2;\n    queue -= -1;\n    Assert(queue == [-1, -2, 0, 1, 2, 3, 4]);\n    ret 0;\n}\n```\n\nQueues can access their elements with the `[]` operator:\n\n```\nMain()\n{\n    queue := [-1, 2, 3];\n    Assert(queue[1] == 2);\n    ret 0;\n}\n```\n\nQueues can be sliced:\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3];\n    Assert(queue[1:3] == [1, 2]);\n    ret 0;\n}\n```\n\nQueue slices are copies, so attempting to set a queue slice will have no effect:\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3]\n    queue[1:3] = [9, 9];\n    Assert(queue == [0, 1, 2, 3]);\n    ret 0;\n}\n```\n\nThe back of the queue can be accessed with the [-1] operator:\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3];\n    Assert(queue[-1] == 3);\n    ret 0;\n}\n```\n\nElements can be removed from a queue with the `Del` keyword:\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3];\n    Del(queue,  0); # Pop front.\n    Del(queue, -1); # Pop back.\n    Assert(queue == [1, 2]);\n    ret 0;\n}\n```\n\nQueues can be iterated over with the `foreach` loop.\nIndexing via `foreach` is done by reference.\n\n```\nMain()\n{\n    queue := [0, 1, 2, 3];\n    foreach(x : queue)\n    {\n        x += 1;\n    }\n    Assert(queue == [1, 2, 3, 4]);\n    ret 0;\n}\n```\n\nAny value type may be inserted into a queue.\n\n## Strings\n\nStrings contain an array of characters and can be indexed and modified like a queue:\n\n```\nMain()\n{\n    string := \"the roman II Programming LanguagE\";\n    string[0] = \"T\";\n    string[4] = \"R\";\n    string[-1] = \"e\";\n    ret 0;\n}\n```\n\nTo the programmer there is no notion of a `char` type in Roman II. Setting a\nstring element to a string longer than 1 will overwrite that character and the\ncharacters following:\n\n```\nMain()\n{\n    ip := \"192.168.10.0\";\n    ip[0] = \"111\";\n    Assert(ip == \"111.168.10.0\");\n    ret 0;\n}\n```\n\nStrings can be appended to with the `+` operator.\nStrings can be numerically compared with the `-` operator (eg. C's `strcmp`).\nString elements can be deleted with the `Del` keyword. String element deletion\nis O(1) from the back and O(N) from the middle and front.\n\n```\nMain()\n{\n    string := \"Roman II\";\n    Del(string, -1);\n    Del(string, -1);\n    Del(string, -1);\n    Assert(string == \"Roman\");\n    ret 0;\n}\n```\n\nStrings can be formatted with the `%` operator:\n\n```\nMain()\n{\n    formatted := \"number : {.2}, string : {}\" % [1, \"Roman II\"];\n    Assert(formatted == \"number : 1.00, string : Roman II\");\n    ret 0;\n}\n```\n\n## Maps\n\nMaps strictly associate strings with a value type:\n\n```\nMain()\n{\n    map := {\n        \"string\" : 1,\n        \"roman\"  : 2\n    };\n    Assert(map[\"string\"] == 1);\n    ret 0;\n}\n```\n\nShort hand, C struct style map notation is supported: \n\n```\nMain()\n{\n    map := {\n        .string : 1,\n        .roman : 2,\n    };\n    Assert(map.string == 1);\n    ret 0;\n}\n```\n\nValue types are interchangeable. A map can associate strings to queues\njust as a queue can hold maps and queues:\n\n```\nMain()\n{\n    map := {\n        .roman : {\n            .revision : 2,\n            .queue : [0, 1, 2, 3, {}, []]\n        },\n    };\n    Assert(map.roman.revision == 2);\n    ret 0;\n}\n```\nNew keys can be inserted into a map with the `:=` operator.\nPre-existing keys can be modified with the `=` operator. Should a key\nnot exist, the `=` operator is a no-op.\n\n```\nMain()\n{\n    map := {};\n    map[\"key\"] := 1;\n    map[\"key\"] = 2;\n    map.door := 3;\n    map.door = 4; # No-op.\n    map.ceiling = 99;\n    Assert(map.key == 2);\n    Assert(map.door == 4);\n    Assert(map.ceiling == null);\n    ret 0;\n}\n```\n\nLike queues, any map element can be deleted with the `Del` keyword.\n\n```\nMain()\n{\n    map := { .key : 1 };\n    Del(map, \"key\");\n    Assert(map.key == null);\n    ret 0;\n}\n```\n\nValues of type `null` can be inserted into maps. The `Exists` keyword\ncan be used to check for such `nulls` by checking for the existence of a key:\n\n```\nMain()\n{\n    map := { .key : null };\n    Assert(Exists(map, \"key\"));\n    ret 0;\n}\n```\n\nTwo maps can be merged with the `+` operator.\n\nMaps can be sliced like queues. Like a queue slice, the last element of a\nmap slice is not included.\n\n```\nMain()\n{\n    map := {\n        \"a\" : 1,\n        \"b\" : 2,\n        \"c\" : 3,\n        \"d\" : 4,\n        \"e\" : 5,\n    };  \n    Assert(map[\"b\" : \"d\"] == {\n        \"b\": 2,\n        \"c\": 3\n    });\n    ret 0;\n}\n```\n\nMaps can be iterated with the `Keys` keyword: \n\n\n```\nMain()\n{\n    map := {\n        \"a\" : 1,\n        \"b\" : 2,\n        \"c\" : 3,\n        \"d\" : 4,\n        \"e\" : 5,\n    };  \n    keys := Keys(map);\n    foreach(key : keys)\n    {\n        map[key] += 1;\n    }\n    Assert(map == {\n        .a : 2,\n        .b : 3,\n        .c : 4,\n        .d : 5,\n        .e : 6,\n    });\n    ret 0;\n}\n```\n\n## Boolean Expressions\n\nBoolean expression are pascal-like requiring enclosed parenthesis\nper boolean expression. Operators `\u0026\u0026` and `||` evaluate expressions to\nthen left and then the right. Boolean expressions do not short circuit.\n\n```\nMain()\n{\n    boolean := (1 \u003c 2) \u0026\u0026 (1 \u003c 2 + 3) || (0 \u003c 1); # Beware, no short circuiting occurs.\n    ret 0;\n}\n```\n\n## Loops and Control Flow\n\nStandard `for` and `while` loops can loop expression blocks.\nContinuing within a `for` loop will run it's advancement expression.\n\nThe following two loops are equivalent:\n\n```\nMain()\n{\n    for(i := 0; i \u003c 10; i += 1)\n    {\n        Print(i);\n    }\n    ret 0;\n}\n```\n\n```\nMain()\n{\n    i := 0;\n    while(i \u003c 10)\n    {\n        Print(i);\n        i += 1;\n    }\n    ret 0;\n}\n```\n\n## Functions\n\nFunctions pass values by reference. Functions return `null` if\nno specific return value is specified.\n\n```\nIncrement(number)\n{\n    number += 1;\n}\n\nAdd(a, b)\n{\n    ret a + b;\n}\n\nMain()\n{\n    a := Add(1, 3);\n    b := Add({ .x : 1 }, { .y : 2 });\n    c := Add([0, 1, 2, 3], [4, 5, 6, 7]);\n    d := 1;\n    Increment(d);\n    Assert(a == 4);\n    Assert(b == { .x : 1, .y : 2 });\n    Assert(c == [0, 1, 2, 3, 4, 5, 6, 7]);\n    Assert(d == 2);\n    ret 0;\n}\n```\n\nReferences passed to functions can be checked for aliasing with\nthe `?` operator to avoid a superfluous copies:\n\n```\nSet(value, with)\n{\n    if(value ? with)\n    {\n        ret;\n    }\n    else\n    {\n        value = with;\n    }\n}\n\nMain()\n{\n    a := 1;\n    Set(a, a);\n    Assert(a == 1);\n    ret 0;\n}\n```\n\n## Pointers\n\nVariables and functions can be pointed to with pointer syntaxing.\nA variable pointer requires use of the address-of `\u0026` operator, followed by\na dereference `*` operator:\n\n```\nMain()\n{\n    f := 1;\n    g := \u0026f;\n    Assert(*g == 1);\n    ret 0;\n}\n```\n\nAll variable types can be pointed to, but to ease pointer syntaxing\nwith maps using C struct notation the `@` operator can be employed:\n\n```\nMain()\n{\n    map := { .key : 1 };\n    pointer := \u0026map;\n    Assert(map.key == 1);\n    Assert((*pointer).key == 1);\n    Assert(pointer@key == 1); # Same as above but a little easier on the programmer.\n    ret 0;\n}\n```\n\nFunctions can also be pointed to with pointer syntaxing.\n\n```\nFun(arg)\n{\n    Print(arg);\n}\n\nMain()\n{\n    a := \u0026Fun;\n    b := Fun;\n    a(42); # `a` is automatically dereferenced.\n    b(42); # Same as above.\n    (*a)(42); # Manual dereferencing for `a` works as well.\n    ret 0;\n}\n```\n\nFunction pointers can be used as callbacks.\n\n```\nPrinter(text)\n{\n    Print(text);\n}\n\nMessage(printer, text)\n{\n    printer(text);\n}\n\nMain()\n{\n    Message(Printer, \"Hello, world!\");\n    ret 0;\n}\n```\n\n## Sorting\n\nFunction pointers (in this case, a comparison function pointer)\nare often used while quick sorting:\n\n```\nCompare(a, b)\n{\n    ret a \u003c b;\n}\n\nMain()\n{\n    a := [0, 5, 3, 2];\n    b := [\"b\", \"c\", \"a\", \"f\", \"z\"];\n    Qsort(a, Compare);\n    Qsort(b, Compare);\n    ret 0;\n}\n```\n\n## File Input and Output\n\nFiles can be opened, read, and written to, with built in calls `Open`, `Read`, and `Write`.\n\n```\nMain()\n{\n    path := \"file.txt\";\n    file := Open(path, \"w\");\n    in := \"testing!\";\n    Write(file, in);\n    file = Open(path, \"r\");\n    out := Read(file, Len(file));\n    Assert(out == in);\n    Assert(Len(file) == Len(in));\n    Assert(Len(file) == Len(out));\n    ret 0;\n}\n```\n\nA file is automatically closed once it's reference count reaches 0. The length of\nthe file returned by `Len` is the number of bytes in the file.\n\n## Value Length\n\nStrings, numbers, and booleans can be compared with all boolean operators.\nOther value types, such as queues, maps, files, use their length (size) for comparison.\nThe `Len` keyword returns the length (or size) of a queue, map, string, or file:\n\n```\nMain()\n{\n    Assert(Len(\"abc\") == 3);\n    Assert(Len([1,2,3]) == 3);\n    Assert(Len({.a : 1}) == 1);\n    file := Open(\"file.txt\", \"r\");\n    Assert(Len(file) == 42); # Assuming `file.txt` consists of 42 characters.\n    ret 0;\n}\n```\n## Binary Searching\n\nAs with sorting, a queue of values can be binary searched\nfor a key if the queue is already sorted. Bsearch requires\na difference function that returns 0 with a match.\n\n```\nComp(a, b) { ret a \u003c b; }\nDiff(a, b) { ret a - b; }\n\nMain()\n{\n    a := [\"b\", \"c\", \"a\", \"f\", \"z\"];\n    Qsort(a, Comp);\n    found := Bsearch(a, \"b\", Diff);\n    if(found != null)\n    {\n        Assert(*found == \"b\");\n    }\n    ret 0;\n}\n```\n\nA queue of maps can be sorted and searched with a minor\nadjustment to the Comparison and Difference functions:\n\n```\nComp(a, b) { ret a.key \u003c b.key; }\nDiff(a, b) { ret a.key - b.key; }\n\nMain()\n{\n    want := 99; \n    a := [\n        { .key : \"b\", .value : 99 },\n        { .key : \"a\", .value :  2 },\n        { .key : \"d\", .value :  3 },\n        { .key : \"c\", .value :  4 },\n        { .key : \"z\", .value :  5 },\n        { .key : \"f\", .value :  6 },\n    ];  \n    Qsort(a, Comp);\n    b := Bsearch(a, { .key : \"b\" }, Diff);\n    Assert(b@value == 99);\n    ret 0;\n}\n```\n\nIn the above example, string subtraction within `Diff` is analogous to C's `strcmp`,\nreturning 0 with a string match.\n\n## Modules\n\nModules can be packaged and imported. Modules do not namespace and are recommended\nto include a suffix denoting the module name:\n\nMath.rr:\n```\nMath_Add(a, b)\n{\n    ret a + b;\n}\n```\n\nMain.rr:\n```\ninc Math;\n\nMain()\n{\n    ret Math_Add(-1, 1);\n}\n```\nModule inclusions are akin to C's `#include` preprocessor directive, which performs\na source copy and paste. Modules are processed only once, even with multiple\ninclusions of the same module.\n\nModules within directories can be included with the dot `.` operator. Dot operators\nprefixing a module reference modules one up from the current directory:\n\n```\ninc ..Lib.Basic.Math; # \"../../Lib/Basic/Math.rr\"\n```\n\n## Shared Object Libraries\n\nRoman II can call functions from native C shared objects libraries. Value types\nsupported are `number`, `string`, and `bool`, mapping to types `double*`,\n`char*`, and `bool*`, respectively:\n\nMath.c:\n```\n// gcc Math.c -o Math.so --shared -fpic\n\nvoid Math_Add(double* self, double* other)\n{\n    *self += *other;\n}\n```\nMain.rr:\n\n```\nlib Math\n{\n    Math_Add(self, other);\n}\n\nMain()\n{\n    a := 1;\n    Math_Add(a, 2);\n    Assert(a == 3); \n    ret 0;\n}\n```\n\n## Built-In Keywords\n\nBuilt in keywords are exposed by the compiler to present an include-free standard library.\nThese keywords cannot be pointed to with pointer syntaxing:\n\n```\nKEYWORD  ARGUMENTS\n\nAbs      (number)\nAcos     (number)\nAll      (queue)\nAny      (queue)\nAsin     (number)\nAssert   (bool)\nAtan     (number)\nBsearch  (queue, value, function)\nCeil     (number)\nCopy     (value)\nCos      (number)\nDel      (value, value);\nExists   (map, string);\nExit     (number)\nFloor    (number)\nKeys     (map)\nLen      (value)\nLog      (number)\nMax      (value, value)\nMin      (value, value)\nOpen     (file, string)\nPow      (number)\nPrint    (value)\nQsort    (queue, function)\nRand     ()\nRead     (file, number)\nRefs     (value)\nSin      (number)\nSqrt     (number)\nSrand    (number)\nTan      (number)\nTime     () # Microseconds.\nType     (value)\nWrite    (file, string)\n```\n\n## Garbage Collection\n\nWhile most values free when their internal reference counts reaches 0,\nvalues with cyclical references require the intervention of the garbage collector.\nAn example of a circular reference is that of two maps pointing to each other:\n\n```\nMain()\n{\n    a := {};\n    b := {};\n    # Ref count of `a` is now 1.\n    # Ref count of `b` is now 1.\n\n    a.pointer := \u0026b;\n    b.pointer := \u0026a;\n    # Ref count of `a` is now 2.\n    # Ref count of `b` is now 2.\n\n    ret 0;\n\n    # Return decrements `a` and `b` ref counts by 1.\n    # Ref count of `a` is now 1 and needs to be garbage collected.\n    # Ref count of `b` is now 1 and needs to be garbage collected.\n}\n```\n\nRoman II's garbage collector is tracked by an internal tracking value named `alloc_cap`.\nThis value is set to an arbitrary buffer value (eg. 1024 values) at startup.\nShould the number of values allocated exceed `alloc_cap` all values are marked\nas either reachable or unreachable and the unreachable values are sweeped (freed).\nThe new `alloc_cap` size is set to the current number of reachable values plus\nthe arbitrary buffer value (eg. 1024).\n\nThe garbage collector checks `alloc_cap` with every new local variable assignation.\n\n## Constant Values\n\nObjects marked with the `const` keyword escape garbage collection (eg. cyclical reference checks).\n`const` objects may not hold pointers.\n\n\n```\nMain()\n{\n    path := \"path/to/a/very/large/file.json\"; # This is a 300MB JSON.\n    file := Open(path, \"r\");\n    const json := Value(Read(file, Len(file)));\n    ret 0;\n}\n```\n\nAn ideal program will have all values initialized as `const` to escape the garbage collector.\n","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglouw%2Frr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglouw%2Frr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglouw%2Frr/lists"}