{"id":13668022,"url":"https://github.com/transistorfet/molten","last_synced_at":"2025-04-26T18:31:03.666Z","repository":{"id":93546014,"uuid":"120137677","full_name":"transistorfet/molten","owner":"transistorfet","description":"An LLVM compiler for an ML-like language (written in Rust)","archived":false,"fork":false,"pushed_at":"2022-02-28T01:12:21.000Z","size":2941,"stargazers_count":56,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-11-11T03:35:36.376Z","etag":null,"topics":["compiler","language","llvm","molten","programming-language","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/transistorfet.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":"2018-02-03T23:06:04.000Z","updated_at":"2024-08-22T17:45:34.000Z","dependencies_parsed_at":"2023-04-14T10:47:21.969Z","dependency_job_id":null,"html_url":"https://github.com/transistorfet/molten","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/transistorfet%2Fmolten","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transistorfet%2Fmolten/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transistorfet%2Fmolten/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/transistorfet%2Fmolten/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/transistorfet","download_url":"https://codeload.github.com/transistorfet/molten/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251035327,"owners_count":21526348,"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":["compiler","language","llvm","molten","programming-language","rust"],"created_at":"2024-08-02T07:01:00.327Z","updated_at":"2025-04-26T18:30:59.500Z","avatar_url":"https://github.com/transistorfet.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"\nMolten\n======\n\n###### *Started November 06, 2017*\n\nMolten is a programming language which borrows from the ML family of languages,\nas well as from Rust and Python.  The compiler is written in Rust and uses LLVM\nto generate IR which can be compiled to machine code.\n\nI originally started this project in order to learn Rust.  It is intended to be\na high level language with a full object system that facilitates both functional\nand object-oriented programming.  Some syntax elements have been changed from\ntypical ML languages to follow conventions found in more common languages, such\nas C++, Rust, and Python (eg. parenthesis-delimited blocks, conventional class\ndefinitions, generics/type parameters with angle brackets, etc).  For more info\non the internals, see [An Overview Of Molten\nInternals](https://jabberwocky.ca/posts/2021-08-molten_overview.html)\n\n\nInstalling\n----------\n\nYou will need `rustc` and `cargo` installed.  It's recommended that you use\n`rustup` to install these.  I've most recently tested it with rustc version\n1.52.  You will also need LLVM 11 installed, as well as libgc\n(Boehm-Demers-Weiser's Garbage Collector), and clang for linking, although clang\ncan be replace with gcc by editing the `molten` python script.\n\nOn Debian/Ubuntu, run:\n`sudo apt-get install llvm-11 llvm-11-runtime llvm-11-dev clang libgc-dev`\n\nOn macOS, run:\n`brew install llvm@11`\n\nYou may need to add /usr/local/opt/llvm@11/bin to your path, and you will probably\nneed to install libgc separately\n\nRunning\n-------\n\nThe `molten` script helps with compiling and linking IR files.  To run an example:\n\n```\n./molten run examples/fac.mol\n```\n\nThis will run cargo to build the compiler if needed, then compile the fac.mol\nfile, as well as all of its dependencies (in this case, the libcore.mol\nlibrary), link them together using clang, along with libgc, and then run the\nbinary.  It can also compile to LLVM IR, and run LLVM bitcode by using the `-S`\nflag.  The resulting .bc file can be run using `lli-11`.\n\n\nExample\n-------\n\n```\nfn fac(x) {\n    if x \u003c 1 then\n        1\n    else\n        x * fac(x - 1)\n}\n\nprintln(str(fac(10)))\n```\n\n### Types\n```\n()                  // unit type\nNil\nBool\nByte\nChar                // UCS-4 character\nInt\nReal\nString\n(Int, Int) -\u003e Int   // function type\n'a                  // universal type variable\nArray\u003cInt\u003e          // array of integers\n(Int, Real)         // tuple type\n{ a: Int, b: Real } // record type\n```\n\n### Declarations\n```\nlet foo = 0\nlet bar: String = \"Hey\"\n```\n\n### Functions\n```\nfn foo(x, y) =\u003e x + y\t\t    // named inline function\n\nfn foo(x, y) { x + y }\t\t    // named block function\n\nlet foo = fn x, y =\u003e x + y\t    // anonymous function\n\nfn foo(x: Int, y) -\u003e Int { x + y }  // with optional type annotations\n\n```\n\n### Invoking Functions\nUnlike in ML, the brackets of a function call are not elidable.  This is a\ndesign decision to improve readability of the code and to make the parser\nsimpler and more predictable.\n```\nfoo(1, 2)\n```\n\n### Blocks\nA block is a collection of expressions which return the result of the last\nexpression in the block.  They can be used in place of a single expression.\nThey do not create their own local scope, at least at the moment, so variables\ndefined inside blocks will appear in the parent scope (usually the function the\nblock is in).  Each expression in the block must end in a newline or semi-colon\ncharacter (or be the last expression in the block).  This applies to the top\nlevel.\n```\nlet is_zero = if self.x \u003c= 0 then {\n    self.x = 0\n    true\n} else {\n    false\n}\n```\n\n### Math\nInfix operators are evaluated using order of operations.  Both sides of an infix\noperation must be on the same line, or a `\\` character can be used to\ncontinue the line.\n```\n5 + 12 * 2      // equals 29\n\n42 * 4 \\\n   % 5          // equals 3\n```\n\n### And / Or\nThe keyword operators `and` and `or` have side-effects and will not execute the\nsecond expression if the result can be determined from the first expression.\nThe expressions must have the same type, and the returned value is the first\nexpression that returns a non-zero value.\n```\nlet is_cat = true\nlet result = is_cat and println(\"It's a cat\") == ()\n```\nSince is_cat is Bool, both sides of the `and` must be Bool.  Since the\n`println()` function returns Unit, we compare it with itself which will always\nbe true.  The `println()` will only execute if `is_cat` is true, and `result`\nwill be true if `is_cat` is true, or false if `is_cat` is false.\n\n### Tuples\n```\nlet tup = (1, \"String\", 4.5)\nprintln(tup.1)                  // prints \"String\"\n```\n\n### Records\nRecords are like tuples but with named fields.  Record literals use the equals\nsign (\"=\") to assign a value to a field.  Specifying a record type uses a colon\n(\":\") to separate the field name from the type.\n```\nlet rec = { i = 1, s = \"String\", r = 4.5 }\nprintln(rec.s)                  // prints \"String\"\n\nlet rec: { i: Int, s: String, r: Real }\n```\n\nRecords can be updated, which will copy all fields of the record into a new\nrecord, but with some of the fields modified.\n```\nlet rec = { i = 1, s = \"String\", r = 4.5 }\n\nlet newrec = { rec with s = \"Updated\" }\nprintln(\"New Value: \" + newrec.s)       // prints \"Updated\"\nprintln(\"Old Value: \" + rec.s)          // prints \"String\"\n```\n\n### Arrays\n```\nlet array1 = [ 1, 3, 6 ]\nfor x in array1\n    println(str(x))\n\nlet array2 = new Array\u003cString\u003e();\narray2.insert(0, \"Hello\")\nprintln(array2[0])\n```\nThe Array type is defined in libcore, which must be imported if arrays are used.\n\n### Flow Control\nThe return value of an if expression is the result of evaluating either the\n`then` clause or `else` clause.  The types of both clauses must match.  The\n`else` clause can be left out as long as the true clause evaluates to Nil.\n```\nif x == 5 then\n    \"It's five\"\nelse\n    \"It's not five\"\n```\n\nA match expression allows pattern matching, which can unpack refs, tuples,\nrecords, and enums.  It can bind values to named variables in the pattern and\ncreates a new scope for each arm of the match expression.\n```\nmatch x with\n| 1 =\u003e \"It's one\"\n| 5 =\u003e \"It's five\"\n| num =\u003e \"It's not one or five, it's \" + str(num)\n```\n\nValues can be unpacked with match as well, including records, tuples, refs, and\nenum variants.  A underscore `_` will match any value, and an identifier (eg.\n`value`) will match anything and bind that value to the name in the process, so\nthat it can be referenced inside the match arm.  Here's an example using a\nrecord:\n```\nmatch { num = 10, name = \"Ten\" } with\n| { num = 10, name = value } =\u003e println(value)\n| { num = _, name = value } =\u003e println(\"Something else named \" + value)\n```\n\n### Loops\n```\nwhile true\n    println(\"looping\")\n\nfor i in iter([ 1, 2, 3 ])\n    println(\"counting \" + i)\n```\nFor loops take an instance of `Iterator\u003c'item\u003e` and calls the `.next()` method\non it, running the body for each `Option::Some('item)` returned.  The `iter`\nfunction is defined for different types to convert them into an appropriate\niterator.  In the case of arrays, it will return the result of `new\nArrayInterator\u003c'item\u003e(input_array)`\n\n### Refs\nA ref is an indirect reference to some data.  It can be passed around as a\nvalue, and dereferenced to get or set the data inside of it.  The internal value\nof a reference is always mutable\n```\nlet r = ref 42\nprintln(str(*r))                // prints 42\n*r = 65\nprintln(str(*r))                // prints 65\n\nfn foo(x: ref Int) { }          // ref types look similar to ref constructors\n\nlet r = ref { a = 42, b = \"The Answer\" }\nprintln(*r.b)                   // prints \"The Answer\"\n```\n\n### Classes\n```\nclass Foo {\n    // A field with type String\n    val mut name: String\n\n    fn new(self, name) {\n        self.name = name\n    }\n\n    fn get(self) =\u003e self.name\n\n    fn static(x) =\u003e x * 2\n}\n\nclass Bar extends Foo {\n    fn get(self, title) =\u003e self.name + \" \" + title\n}\n\nlet bar = new Bar(\"Mischief\")\nbar.get(\"The Cat\")              // returns \"Mischief The Cat\"\nFoo::static(5)\n```\nAll methods are both closures, and virtual methods, so they can access variables\nin their parents' scopes and also be overridden by a child class's\nimplementation of the same method, accessible with a reference to the parent\nclass type.\n\nFields can have an optional type, but not an initializer.  If a class has at\nleast one field, it must have at least one \"new\" constructor, which must assign\nto each field an initial value.  The type can be inferred from this assignment\nif the optional type is not supplied.  If a field is declared as mutable, it can\nbe reassigned to, but if the `mut` keyword is absent, the field can only be\nassigned to within the constructor, and will be immutable after the constructor\nhas returned.  Every constructor must also call the `Super::new` method of its\nparent class, if it has a parent that has a constructor.\n\n### Enums (Tagged Unions)\nAn enum can either have no arguments, or a tuple of arguments.  Constructing an\nenum variant requires using the Resolve (::) notation.  Pattern matching is\ncurrently the only way to get values out of a variant.  Unlike with classes,\nwhich allocate memory for a new instance, enums are immediate data types like\ntuples and records.  In order to store it in a memory location, a `ref` must be\nused.  A ref is required in order to make a recursive enum.\n```\nenum Value =\n| None\n| Integer(Int)\n| String(String)\n| Pair(String, String)\n| Reference(ref Value)          // A recursive reference\n\nlet val = Value::String(\"Hey\")\n\nmatch val with\n| Value::String(s) =\u003e println(s)\n| _ =\u003e ()\n```\n\nMethods can be added to enums using a `methods` body.  Currently it can only be\nused with enums.\n\n```\nmethods Value {\n    fn is_some(val: Value) {\n        match val with\n        | Value::None =\u003e false\n        | _ =\u003e true\n    }\n}\n\nval.is_some()\n```\n\n### Traits and Trait Objects\nA trait can be defined with method declarations in the body, with the predefined\ntype alias \"Self\" used to refer to the current trait object\n```\ntrait Add {\n    decl add(Self, Self) -\u003e Self\n}\n```\n\nA trait can then be implemented for a given type.  An impl block can only have\nfunction definitions that match the trait declarations.  Inside the impl block\nthe \"Self\" type alias will refer to the implementation type.  Trait objects that\nare passed to arguments with type \"Self\" will automatically be unpacked into\ntheir impl type, and if the return type has type \"Self\", the result will\nautomatically be packed back into a trait object.\n```\nimpl Add for Int {\n    fn add(x: Self, y: Self) -\u003e Self {\n        x + y\n    }\n}\n\nimpl Add for Real {\n    fn add(x: Self, y: Self) -\u003e Self {\n        x + y\n    }\n}\n```\n\nTraits are not an object type, but instead are specified as a constraint on a\nuniversal variable using a `where` clause.  If the actual type given does not\nimplement the constrained trait, then an type error will be raised.  Currently\ntrait objects can only be created by passing them into a function that takes a\nuniversal variable with a constraint.  At the moment, only one trait can be\nspecified as a constraint but this will be changed in future.\n```\nfn do_some_adding(x: 'a) -\u003e 'a where a: Add {\n    x.add(x)\n}\n\nprintln(str(do_some_adding(400)))\nprintln(str(do_some_adding(1.5)))\n```\n\nThe above example will output:\n```\n800\n3.000000\n```\n\n### Exceptions\nAll exceptions must be an instance of the `Exception` class defined in libcore.\n```\ntry open(\"file.txt\")\nwith e =\u003e println(\"Exception Occurred: \" + e.msg)\n\ntry {\n    //...\n    raise new Exception(\"Problem\")\n} with\n    e =\u003e println(e.msg)\n```\n\n### Annotations\nA value can be type annotated using a colon followed by the type.\n```\n5 : Int\nstr(i : Int)\n(func() : String)\n```\n\n### Import\n```\nimport libcore\n```\n\n### External Functions\nA function can be declared without being implemented, and functions can also be\ndefined with an ABI specifier so that they are accessible to other languages.\nOnly C support is currently implemented. A C function cannot be a closure.\n```\ndecl foo(Int) -\u003e Int         // external molten function\ndecl bar(Int) -\u003e Int / C     // external C function\n\nfn baz(i: Int) / C {\n    // molten function that can be called from C\n}\n```\n\n### Linking to C\nAn example of writing a C file, and linking it to a molten program is shown in\n`lib/libccore.c`.  When imported into a molten file and compiled with the\n`molten` script, the library will be compiling using clang and the\n`libccore.cdec` (manually maintained) will be copied to `libccore.dec`.  The\nimporting molten program will be able to use declarations from libccore.cdec.\nSome declarations that can be used in C are in `include/molten.h`, such as\naccessing the garbage collected allocator.\n\n\nPreviously Uncompleted\n----------------------\n\n- Dynamic Dispatch/vtables works now!\n\n- Closures have been implemented! Most functions and methods are now\n  closures, although there is a new ABI type (MF) which is a\n  non-closure function that can still be overloaded and can still\n  throw exceptions (when they're implemented). It's mostly used by\n  builtin functions\n\n- Class field initializers are working!  It now adds a new closure to\n  each class called __init__ which is called during \"new\" to initialize\n  the class members\n\n- Exceptions have finally been added using the setjmp/longjmp functions\n\n- Enums have been added but with a lot of limitations.  It's not possible\n  to use an enum inside itself (eg. to make a tree data structure).  It\n  also doesn't properly calculate the enum size, because that will require\n  having the target architecture info.\n\n- Garbage collection has been added using the Boehm-Demers-Weiser\n  Conservative C Garbage Collector.  It is possible to compile without\n  the garbage collector by using the `--no-gc` command line argument.\n\n- Traits have finally been added.  They currently are limited to one\n  trait constraint per type variable, but I will hopefully add\n  trait depedencies/inheritence soon\n\n\nI'd be happy to hear of any additional features ideas or suggestions, if\nyou'd like to leave them under \"Issues\" on github.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftransistorfet%2Fmolten","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftransistorfet%2Fmolten","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftransistorfet%2Fmolten/lists"}