{"id":16184352,"url":"https://github.com/mattmezza/mul","last_synced_at":"2025-10-28T23:10:23.220Z","repository":{"id":63579333,"uuid":"567776242","full_name":"mattmezza/mul","owner":"mattmezza","description":"⌨️ *mul* · My Useless Language, a simple interpreted programming language.","archived":false,"fork":false,"pushed_at":"2022-11-24T23:22:46.000Z","size":77,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-08T02:31:05.682Z","etag":null,"topics":["fun","interpreter","language","programming-language"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattmezza.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-18T14:52:08.000Z","updated_at":"2023-03-08T03:07:40.000Z","dependencies_parsed_at":"2023-01-22T19:16:18.373Z","dependency_job_id":null,"html_url":"https://github.com/mattmezza/mul","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmul","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmul/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmul/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmul/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattmezza","download_url":"https://codeload.github.com/mattmezza/mul/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246804508,"owners_count":20836750,"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":["fun","interpreter","language","programming-language"],"created_at":"2024-10-10T07:09:55.590Z","updated_at":"2025-10-28T23:10:18.199Z","avatar_url":"https://github.com/mattmezza.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"mul\n===\n[![PyPI version](https://badge.fury.io/py/mul.svg)](https://badge.fury.io/py/mul)\n\n`mul` (my useless language) is a useless, interpreted programming language. \nI created it to learn more about programming languages without any \nutility expectation whatsoever.\n\nIt features:\n- dynamic, strong typing\n- lambda functions\n- functional paradigm\n- easy to write interpreter (≈26Kb/742 LOC of python)\n- REPL\n\n# Hello, world!\n```\ngreeter = {:(who) print('Hello, ' + who + '!');};\ngreeter(\"world\");\n```\n`pip install mul \u0026\u0026 mul hello_world.mul` (*needs `python3.11.0rc2` or above*)\n\n# Main characteristics\n\nThe implementation is in Python, but in the future I'd like to port it to \na compiled language (maybe Rust).\n\nThe language is designed to be easy to write an interpreter for (therefore \nits uselessness).\n\nBy default, every variable is read-only. You can overwrite variables by \nconsciously using the `set` function.\n```\na = 5;\na = 6; # error Cannot re-assign symbol 'a'.\nset('a', 6);\nprint(a); # prints '6.0'\n```\nThere are no statements, and everything is an expression, with a return value.\nIf you play around with the REPL you will get what I mean:\n```\n$ mul\n\u003e nickname = 'mattmezza';\nType.STR\nmattmezza\n\u003e f = {};\nType.FN\n\u003cfunction\u003e\n```\n- an assignment returns the assigned value\n- a fn call returns the last expression in the fn definition\n\n# Syntax\n\n## Whitespaces\n\nWhitespace is not influencing parsing. All whitespaces are stripped out after \nlexical analysis.\n\n## Symbols\n\nIdentifiers can only use `[_a-zA-Z0-9!?]` and they have to start with `[_a-zA-Z]`.\n\n## Literals\n\nNumbers can only use `[.0-9]` (so no negative numbers – use `0 - num;` instead).\nStrings can be created with either double `\"...\"` or single `'...'` quotes. \nEscaping is not implemented but you can have multiline strings. \nYou can mix quotes within string to accomplish something like this:\n```\n\u003e '\"Hello\", she said.'\n\"Hello\", she said.\n```\n# Types\n\nThere are just a few types baked into the language: `None`, `Number`, `String` and `Function`.\n\n## `None`\n\n`None` is a type that you can instantiate from the literal `none`.\n\n## `Number`\n\nNo distinction between integer or float, everything is a number\n```\nnum = 4.3;\nanother_num = 5;\n```\nNegative numbers are not supported (sorry). You can use the following:\n```\nnegative_num = 0 - 4;\n```\n`0` is considered `false` and anything else is considered `true` even without \nhaving the explicit concept of a `Boolean` type.\n\n`true` and `false` themselves are implemented as functions like so:\n```\ntrue = {1;};\nfalse = {0;};\n```\n## `String`\n\nStrings can be created with single or double quotes.\n```\nname = 'Matteo';\nnickname = \"mattmezza\";\n```\nStrings can be concatenated with `+`.\n\n## `Function`\n\nFunctions can be defined like so:\n```\nfullname = {\n    fname = 'Matteo';\n    lname = 'M';\n    fname + lname;\n};\n```\nThe last expression is what the function will return when called. \nFunctions can be called like so:\n```\nfullname();\n```\nAnonymous function calls are supported:\n```\n{:(fname, lname) fname + ' ' + lname}('matt', 'mezza');\n```\nFunctions can have arguments defined as formal params like so:\n```\nfullname = {:(fname, lname)\n    fname + lname;\n};\n```\nAs you would expect, such function can be called like so:\n```\nfullname('Matt', 'M');\n```\n# Operators and precedence\n\n`mul` only has diadic operations implemented (so no monadic ops for now).\nThis means you can't do `-2;` but you'd rather do `0-2;`.\n\nThe implemented operators are:\n- `+` as in `2+3;` returning `5`\n- `-` as in `2-3;` returning `-1`\n- `*` as in `2*3;` returning `6`\n- `/` as in `2/3;` returning `0.6666666666666666`\n\nThe precedence is what you'd expect:\n- `2+3*4;` returning `14`\n- `2+3/3;` returning `3`\n- `3*4/12;` returning `1`\n\n# Control flow\n\nThere are no other constructs in the language (no `if`, no `for`, no `while`, \netc...). In order to make the language a little bit more useful, such \nconstructs are implemented in a native way in the form of a function.\n\nFor instance, there is an `if` function that you can use like so:\n```\nif(boolean, function, function_else);\n```\n# Comments\n\nSingle line comments are allowed by prepending the comment text with a `#`.\n```\n# this is a comment\na = 5;  # this is a comment too\n```\n# The standard library\n\nA very small `std` has been built so far. It includes things like:\n\n- `list` a pair based list implementation\n- `logic` things like `and`, `or`, `all`, etc...\n- `operator` functions like `sum`, `sub`, `mul`, `div`, etc...\n\nThe `std` lib is extremely sparse and unstructured, beware.\n\n## Native hooks implementation\n\n`mul` is implemented via native function call hooks that make it possible to \nexport host language feature to `mul` itself (as done for the `if` function).\n\nNative hooks implementation can be leveraged to quickly fill holes in the \n`std` lib.\n\nA number of functions are implemented via native hooks:\n- `char_at`\n- `concat`\n- `equals`\n- `lt`\n- `gt`\n- `if`\n- `len`\n- `print`\n- `set`\n\n# Tooling\n\n## Execution\n\n`mul` programs can be run via `$ mul program.mul`.\n\nGiven `hello_world.mul` containing:\n```\ngreeter = {:(who) print('Hello, ' + who + '!');};\ngreeter('world');\n```\n```\n$ mul hello_world.mul\nHello, world!\n```\nThere is also a `REPL` interface:\n```\n$ mul\n\u003e a = 5;\nType.NUM\n5.0\n```\n`ctrl+d` quits the `REPL`.\n\n## Development\n\n- `mul program.mul -t`: prints the tokens as parsed by the lexer\n- `mul program.mul -a`: prints the AST as parsed by the parser\n\n# Code Examples\n```\npow =\n{:(b, e)\n   if(equals(0, e), {1;}, {\n        pow(b, e - 1) * b;\n   });\n};\nprint(pow(2, 3));  # prints 8\n```\n```\ntrue = {1;};\nfalse = {0;};\nnot = {:(v) if(v, false, true);};\nand = {:(a,b) if(a, {if(b, true, false);}, false);};\nor = {:(a,b) if(a, true, {if(b, true, false);});};\nle = {:(v1, v2) if(or(lt(v1, v2), equals(v1, v2)), true, false);};\nge = {:(v1, v2) if(or(gt(v1, v2), equals(v1, v2)), true, false);};\n```\nFor more examples, browse through the [std](https://github.com/mattmezza/mul/tree/main/std) lib.\n\n# Installation\n\n`pip install mul` installs the latest version of `mul`. Make sure \nyou have a virtual env with `python3.11.0rc2` or newer.\n\n# Programmatic usage\n\n`mul` can be used within python projects as a library.\n\n```\nimport mul\n\n\ni = mul.Interpreter.with_std()\na = i.eval('a = 5;')\nb = i.eval('b = 10;')\nres = i.eval('res = a + b;')\nprint(res.val)  # prints 15\n```\n\n# Contributing\n\nI don't know if I will have time to review PRs and contributions but you can \nhave fun with the language and maybe port it to other host languages. If you \ndo, let me know, I'm a sucker for programming languages and I would enjoy \nchecking out what you did.\n\n# Acknowledgement\n\n`mul` is inspired by [cell](https://github.com/andybalaam/cell). Thanks to Andy \nexplaining rather difficult concepts in such a simple way.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattmezza%2Fmul","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattmezza%2Fmul","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattmezza%2Fmul/lists"}