{"id":13661927,"url":"https://github.com/stevehalliwell/ulox","last_synced_at":"2025-04-30T08:11:57.144Z","repository":{"id":41356227,"uuid":"356749570","full_name":"stevehalliwell/ulox","owner":"stevehalliwell","description":"A bytecode interpreted scripting language for games in Unity.","archived":false,"fork":false,"pushed_at":"2025-04-24T06:59:45.000Z","size":253248,"stargazers_count":25,"open_issues_count":32,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-30T08:11:49.558Z","etag":null,"topics":["lox","scripting-language","unity"],"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/stevehalliwell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-04-11T02:38:57.000Z","updated_at":"2025-04-25T16:21:46.000Z","dependencies_parsed_at":"2023-12-21T22:22:41.833Z","dependency_job_id":"6832526c-a142-4f66-9ede-ab43f819afa3","html_url":"https://github.com/stevehalliwell/ulox","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevehalliwell%2Fulox","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevehalliwell%2Fulox/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevehalliwell%2Fulox/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevehalliwell%2Fulox/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevehalliwell","download_url":"https://codeload.github.com/stevehalliwell/ulox/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251666336,"owners_count":21624295,"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":["lox","scripting-language","unity"],"created_at":"2024-08-02T05:01:44.545Z","updated_at":"2025-04-30T08:11:57.136Z","avatar_url":"https://github.com/stevehalliwell.png","language":"C#","funding_links":[],"categories":["Embed-Script/VM/","C\\#"],"sub_categories":["Lua"],"readme":"# ULox\n\n[![Coverage Status](https://coveralls.io/repos/github/stevehalliwell/ulox/badge.svg?branch=main)](https://coveralls.io/github/stevehalliwell/ulox?branch=main) [![Tests](https://github.com/stevehalliwell/ulox/actions/workflows/tests.yml/badge.svg)](https://github.com/stevehalliwell/ulox/actions/workflows/tests.yml)\n\nA scripting language and language playground, in C#, in Unity.\n\nOriginally grew out of the [lox](https://github.com/munificent/craftinginterpreters) implementation, I was playing around with, [here](https://github.com/stevehalliwell/ulox-work). It was so engaging, I wanted to keep using, and growing, the language.\n\nIf you want to know more about the language itself, the tests are the best way right now. They're in the ulox.core.tests project in the ulox folder.\n\n## What's it look like\n\n```js\n// single line comments\n\n/* multi line comments\nare as expected\n*/\n\n// the language is imperative, statements are executed as encountered at top level\n\n// variables are declared with 'var'\n// numbers are 64bit floats \nvar pi = 22/7; //ish\n\n//multi declare\nvar e = 2.71828,\n    phi = 1.618;\n// ulox is not whitespace sensitive so this could be all one line or different intends.\n\n// functions are declared with 'fun'\nfun Foo() \n{\n}\n//and invoked via ()\nFoo();\n\n// function args are named only\nfun DoubleIt(a)\n{\n    // return values are named, retval is the default for single returns\n    retval = a * 2;\n}\n\n// multiple return values are named in brackets after the arg list\nfun VecAdd(x1,y1,x2,y2) (x,y)\n{\n    x = x1+x2;\n    y = y1+y2;\n}\n\nfun VecAddIfNotZero(x1,y1,x2,y2) (x,y)\n{\n    if(x1 == 0)\n    {\n        x = x1;\n        y = y1;\n        //return statement exits immediately.\n        //  Since return values are named, what ever value\n        //  they hold at the return are the return values\n        return;\n    }\n    x = x1+x2;\n    y = y1+y2;\n}\n\n// multi var assignment wraps the declaring variables in '()'\nvar (x,y) = VecAdd(1,2,3,4);\n\n// vars are dynamically typed, if unassigned they are 'null'\nvar somethingThatWillBeNull;\n\n//strings are via \"\"\nvar someString = \"hi\";\n\n//functions are also values\nvar someFunc = Foo;\n// and invoked via ()\nsomeFunc();\n\n//strings can be combined with '+'\nvar someCombinedString = someString + \" \" + \"there\";\n\n//all strings are interpolated\n//  within a string code between {} must be an expression \n//  and is desugared to string fragments plus-ed with\n//  the expression.\nvar someName = \"Steve\";\nvar interpedCombined = \"Hi {someName}\";\n\n//if we want the {, we escape it with \\\nvar stringWithBrak = \"this has a \\{ in it.\"\n\n// native types for list, map, and object types\nvar aList = []; //an empty list\naList.Add(7);   //methods to add remove and the like.\n\nvar anotherList = [1,2,3,4,]; // inline declare the value of a list\n\nvar aMap = [:]; //an empty map\naMap.Create(1,\"hi\");    //CRUD methods on map \n\nvar dynObject = {=};    //an empty object\n// these objects are not 'frozen' so fields can be added\ndynObject.a = 7;\n\nvar anotherDynObject = //an inline dynamic object \n{\n    a = Foo,\n    b = 7,\n    c = \"hi\",\n};\n\n//object type variables are referenced\nvar referenceToAnother = anotherDynObject;\n//duplicate creates a deep copy\nvar dupOfOther = Duplicate(anotherDynObject);\n//Object.TraverseUpdate will call for all matching elements \n//  in left hand side with the matching values from right side\n//  here it changes the dupOfOther.a to the 7 in dynObject\ndupOfOther = Object.TraverseUpdate(dupOfOther, dynObject, fun (lhs, rhs) {retval = rhs;});\n\n//comparisons classics\n3 == 2; // false\n3 != 2; // true\n3 \u003e 2;  // true\n3 \u003c 2;  // false\n2 \u003c= 2; // true\n2 \u003e= 2; // true\ntrue and true; // true\ntrue and false; // false\ntrue or false; // true\n\n//if classics \nif(1 \u003c a and b != null)\n{\n} \nelse if (c != null or IsTuesday())\n{\n}\nelse\n{\n}\n\n//ulox has match syntax for chaining single if == style logic, \n//  having no match will result in a runtime error\nmatch a\n{\n    1: print(2);    //equiv to if (a == 1) {print(2);}\n    0+2: print(1);  //equiv to if (a == (0+2)) \n    3:\n    {\n        //match can also have blocks \n        //   when more than 1 statement is required\n        var d = Foo();\n        d += 7;\n        print(d);\n    }\n}\n\n//looping constructs\n//while continues the block (or single statement)\n//  until the condition is false\nwhile(true)\n{\n    break;  //go to end of containing loop\n}\n\n//for is declare ; condition; post step, each is optional\nfor(var i = 0; i \u003c 10 ; i += 1)\n{\n    if(i % 3 == 0)\n        continue;   //skip to next \n}\n\n//loop, without anything following is equiv to while(true)\nloop\n{\n    break;\n}\n\n//loop with following info is for iterating over collections\nvar myArr = [1,2,3,4,5,];\nloop myArr    \n// the above auto defines, i, item, icount. You can optionally\n//  provide a name for i, e.g.\"j\" gives, jtem, jcount.   \n{\n    print(\"Val \" + item + \" @ \" + i \" of \" + count);\n}\n\n//user created data types are enum, class\n\n//enums are named values\n//  similar in intent and usage to c style languages\nenum Foo\n{\n    //each enum is a key and value, \n    //  That value can be anything. \n    //  If no values are assigned they auto increment from 0\n    Bar = 7,\n    Baz = 8,\n}\n\n//a class with data only is the most straight forward, \n//  it is simply a named prototype for a collection of vars\nclass Addresss\n{\nvar\n//as with other vars they can be given a starting value\n    number = 0, \n    street,\n    state,\n    postcode,\n    country \n// class var syntax is very tolerant, \n//  you can leave dangling commas or end with a ;\n//  This makes moving from code made around a dynamic to\n//  a class very straightforward.\n}\n\nvar anAddress = Address();\nanAddress.number = 7;\n\n//classes are the feature rich.\n//  The order of elements declared within the class is enforced\nclass MyFoo\n{\n    // one more vars, must be declared first, \n    //  any assigned values are calculated before init \n    //  or returning the instance to the user\n    var a,b = 3,c;\n\n    //init is a special method called automatically \n    //  when you create an instance, arity must match.\n    // you do not have to define an init for your class, \n    //  not doing so is equiv to init(){}\n    // Sugar. Any arg matching a field name is automatically \n    //  assigned to the field, \n    //  so here the equiv of this.a = a; occurs automatically\n    init(a)\n    {\n    }\n\n    //methods are the same as functions \n    //  but they have access to the 'this', \n    //  the instance the method is being invoked upon\n    Bar(arg)\n    {\n        this.a = arg;\n    }\n\n    //you can omit the this. for fields of the instance\n    Baz(arg)\n    {\n        a = arg;\n    }\n}\n\n//to get an instance of the class, \n//   we invoke it's name and match it's init args\nvar myFoo = MyFoo(1);\n\n//Classes can be used without instances \n//  if they don't use this or if the method is marked static\nclass FooSystem\n{\n    Bar(someState, dt)\n    {\n        //does stuff with args passed to it, has no this\n    }\n}\n\nFooSystem.Bar(a, 0.1);\n\n//ulox does not have inheritance, it does have mixins. \n//  These combine the elements of the type into the containing one.\n\nclass ThenSome\n{\n    var d;\n\n    MoreBar(arg)\n    {\n        //...\n    }\n}\n\nclass MyFooAndThenSome\n{\n    //this class will have all the vars (including default values),\n    //  and methods of both named mixins.\n    mixin \n        MyFoo,\n        ThenSome;\n\n    var alsoMyOwnList = [];\n\n    // mixins are pretty smart, \n    //  this will auto assign to the a we got from MyFoo, \n    //  the d from ThenSome, and our declared alsoMyOwnList. \n    init(a, d, alsoMyOwnList){}\n\n    //...\n}\n\n//Since we don't have inheritance, we have operators\n//   to check that objects have partial matching shapes.\n//  meets returns a bool checking lhs has all the parts of rhs\nvar matchingShape1 = anAddress meets Address(); //will be true\nvar matchingShape2 = anAddress meets myFoo(); //will be false\n\n//ulox also supports the use of fields via subscript syntax.\n//  This allows for code that does not know the identifier ahead\n//  of time to be written.\nvar someFieldNameWeDidntKnow = \"a\";\nmyFoo[someFieldNameWeDidntKnow] = 7; \n\n//testing is built into the language, \n//  they are setup like test fixtures with test cases.\n//  They are auto run by the vm, in an isolated inner vm. \n//testset can have name, here FooTests, but the name is optional\ntestset FooTests\n{\n    test ForceFail\n    {\n        //throw keyword can be used anywhere, \n        //  it will stop the vm, \n        //  it can have an arg or message\n        //Tests are run in an isolated inner vm, \n        //  the vm running the tests continues, but knows \n        //  the test failed.\n        throw;\n    }\n\n    test AreEqual\n    {\n        //there are many Assert methods, \n        //  this means you don't need to if something throw\n        Assert.AreEqual(1,1);\n    }\n\n    test ExpectEqual\n    {\n        //expect allows for chaining multiple asserts\n        //  It will desugar to a throw with a message\n        //  You can specify the message as a string\n        //  after the expression with : \"literal did not match\"\n        expect \n            1 == 1,\n            2 == 2, //trailing commas are allowed\n            ;\n    }\n    \n    // test cases can also have data provided, \n    //  here we have 2 sets of data, this\n    //the test will run twice once for each \n    //  set of values \n    //The values are expanded out to the args of the test method\n    test ([ [1,2,3], [1,1,2] ]) Addition(a,b, expected)\n    {\n        var result = a+b;\n\n        expect result == expected : \"This custom message shown on failure, is optional\";\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevehalliwell%2Fulox","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevehalliwell%2Fulox","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevehalliwell%2Fulox/lists"}