{"id":16576305,"url":"https://github.com/mkhan45/rustscript2","last_synced_at":"2025-03-21T12:31:42.587Z","repository":{"id":37541363,"uuid":"418033396","full_name":"mkhan45/RustScript2","owner":"mkhan45","description":"RustScript is a functional scripting language with as much relation to Rust as Javascript has to Java. ","archived":false,"fork":false,"pushed_at":"2023-03-01T04:30:59.000Z","size":1900,"stargazers_count":38,"open_issues_count":15,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-18T01:12:14.503Z","etag":null,"topics":["programming-language"],"latest_commit_sha":null,"homepage":"https://mkhan45.github.io/RustScript2/","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkhan45.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-10-17T05:46:53.000Z","updated_at":"2025-02-05T12:12:08.000Z","dependencies_parsed_at":"2024-01-29T08:09:48.126Z","dependency_job_id":"ac797b47-afed-42c6-8ad0-5cf36631b690","html_url":"https://github.com/mkhan45/RustScript2","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/mkhan45%2FRustScript2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkhan45%2FRustScript2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkhan45%2FRustScript2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkhan45%2FRustScript2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkhan45","download_url":"https://codeload.github.com/mkhan45/RustScript2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244799428,"owners_count":20512243,"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":["programming-language"],"created_at":"2024-10-11T22:07:52.292Z","updated_at":"2025-03-21T12:31:42.047Z","avatar_url":"https://github.com/mkhan45.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=center\u003e\n    \u003cimg src=\"assets/logo.png\" width=\"600px\"\u003e\n    \u003ch1\u003e\n        RustScript V2\n    \u003c/h1\u003e\n\u003c/div\u003e\n\n\nV2 of \u003chttps://github.com/mkhan45/RustScript\u003e\n\nI originally wrote RustScript in Java because it was part of a school project,\nignoring performance/code quality because I only had one night to do it.\n\nThis is an improved version of RustScript with improved performance and more features\nwritten to learn OCaml. It also served as a testbed for features, and a demonstration\nof the 80/20 rule; the language's design was largely based on ease of implementation.\n\n### Examples:\n\nThe most impressive examples are:\n- The tic-tac-toe AI: \u003chttps://github.com/mkhan45/RustScript2/blob/main/examples/tictactoe_minimax.rsc\u003e\n- The TOML parser library: \u003chttps://github.com/mkhan45/rustscript_toml\u003e\n- A simple static site generator/HTML template engine: \u003chttps://github.com/mkhan45/ssg_rustscript\u003e\n- My personal website served using the previous two libraries: \u003chttps://github.com/mkhan45/rustscript_site\u003e, hosted at \u003chttps://rustscript.mikail-khan.com\u003e\n- More examples below\n\n### Language Tour\n\n#### Basic types:\n\nRustScript has 5 basic types\n```ex\nlet x = 5 # integer\nlet f = 5.0 # float\nlet s = \"Hello\" # string\nlet b = T # boolean\nlet a = :atom # atom\n```\n\n#### Compound types:\n\nThere are also a few compound types\n```ex\nlet t = (1, \"hello\", :aaa) # tuples\nlet ls = [1, 2, 3, 4, 5] # lists\nlet m = %{one: 2, \"three\" =\u003e 3} # maps\nlet f1 = fn(x) =\u003e x * 2 # closures\nlet f2(x) = x * 2 # functions\n```\n\n#### Patterns:\n\nAll bindings in RustScript are done through pattern matching. Aside from the primitives, there\nare:\n\n```ex\nlet (a, (b, c), d) = (1, (2, 3), 4) # tuple patterns\ninspect((a, b, c, d)) # (1, 2, 3, 4)\n\nlet [a, b, c] = [1, 2, 3] # list patterns\ninspect((a, b, c)) # (1, 2, 3)\n\nlet [a, b | tl] = [1, 2, 3, 4] # list head/tail patterns\ninspect((a, b, tl)) # (1, 2, [3, 4])\n\nlet %{one, \"two\" =\u003e two} = %{one: 1, \"two\" =\u003e 2, unused: 0} # map patterns\ninspect((one, two)) # (1, 2)\n\nlet _ = :something # wildcard pattern\n# no bindings are created\n\nlet [x | xs] as ls = [1, 2, 3] # as patterns\ninspect((x, xs, ls)) # (1, [2, 3], [1, 2, 3])\n```\n\nWhile pattern matching is most frequently used in let bindings, it is also used in `if let` expressions, `match` expressions,\nand function arguments.\n\n`if let` expressions are used for refutable patterns:\n\n```ex\nlet result = (:ok, 5)\nif let (:ok, n) = result then\n    inspect(n)\nelse\n    println(\"Error\")\n```\n\n`match` expressions:\n\n```ex\nlet ls = [1, 2, 3, 4]\nmatch ls\n    | [1 | xs] -\u003e println(\"Starts with 1\")\n    | [_ | xs] -\u003e println(\"Starts with something other than 1\")\n```\n\n#### Closures:\n\n```ex\nlet a = 5\nlet f = fn(x) =\u003e x * a # f captures a\n\ninspect(f(2)) # 10\n\nlet g = fn(a, [x | xs]) = (a * x, xs) # pattern matching works in function arguments\ninspect(g(1, [2, 3, 4])) # (2, [3, 4])\n```\n\n#### Named functions:\n\nNamed functions do not capture their environment. As a result, they run\nslightly faster and can be made mutually recursive\n\n```\nlet f(x) = x * 2\ninspect(f(2)) # 4\n```\n\n#### Maps:\n\n```ex\n# pairs with non-atom keys use \"=\u003e\" arrows\nlet x = %{\"one\" =\u003e 1, \"two\" =\u003e 2, \"three\" =\u003e 3}\n\n# pairs with atom keys use colons\nlet y = %{one: 1, two: 2, three: 3}\n\n# the following are equivalent:\n%{one: 1, two: 2}\n%{:one =\u003e 1, :two, 2}\n\n# Maps are accessed via function call syntax\ninspect(x(\"one\")) # 1\ninspect(y(:one)) # 1\n\n# However it's often more convenient to pattern match over them,\n# especially with atoms as keys\n\nlet %{\"one\" =\u003e one, \"two\" =\u003e two} = x\ninspect((one, two)) # the three does not get bound\n\nlet %{one, two} = y # key punning, equivalent to the next line\nlet %{:one =\u003e one, :two =\u003e two} = y\n\n# Maps can be updated using update syntax\nlet m = %{one: 1, two: 2}\nlet g = %{three: 3 | m}\ninspect(g) # %{:one: 1, :three: 3, :two: 2}\n```\n\n#### Lists\n\n```ex\n# Lists are heterogenous linkedlists.\nlet ls = [1, 2, 5, 7]\n\n# Generally, lists are accessed via pattern matching\nlet [a, b | tl] = ls\ninspect((a, b, tl)) # (1, 2, [5, 7])\n\n# They can also be accessed by index in O(n) time via the nth function\ninspect(nth(ls, 2)) # 5\n\n# Range expressions\ninspect([1..10]) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\ninspect([1,5..25]) # [1, 5, 9, 13, 17, 21]\n\n# List comprehensions\ninspect([n * n for n in [1..100] if n mod 12 == 0]) # [144, 576, 1296, 2304, 3600, 5184, 7056, 9216]\n```\n\n#### Captures and Pipes\n\n```ex\n# Currying is emulated via function captures\n\nlet polynomial = fn(a, b, c, x) =\u003e a * x * x + b * x + c\nlet f = polynomial(2, 3, 4, _)\nlet g = polynomial(_, _, _, 10)\n\ninspect(f(10)) # 234\ninspect(g(2, 3, 4)) # 234\n\n# Captures are especially useful in combination with the pipe operator.\n# The following code takes advantage of the standard add, sub, mul, and div functions\n# as well as the fact that inspect returns its arguments unchanged after printing them\n\nlet f = polynomial(2, 3, 4)\n\n10\n|\u003e f\n|\u003e inspect # 234\n|\u003e add(_, 10)\n|\u003e inspect # 244\n|\u003e div(_, 100)\n|\u003e inspect # 2.44\n|\u003e sub(1000, _)\n|\u003e inspect # 997.56\n|\u003e mul(_, 10)\n|\u003e inspect # -9975.599\n```\n\n### Build\n\n```bash\ndune build\n```\n\nRun a file using:\n\n```bash\ndune exec ./bin/rustscript_cli.exe \u003cfile\u003e\n```\n\nStart a REPL using:\n\n```bash\ndune exec ./bin/rustscript_cli.exe\n```\n\n# Further examples\n\n#### FizzBuzz\n```ex\n# ideally, for ... in will become a macro over foreach\nlet fizzbuzz(n) = foreach([1..101], fn(n) =\u003e match (n % 3, n % 5)\n    | (0, 0) -\u003e println(\"FizzBuzz\")\n    | (0, _) -\u003e println(\"Fizz\")\n    | (_, 0) -\u003e println(\"Buzz\")\n    | _ -\u003e println(to_string(n))\n)\n\nfizzbuzz(100)\n```\n\n#### Quicksort\n\n```ex\nlet sort = fn(ls) =\u003e match ls\n    | [] -\u003e []\n    | [pivot | tail] -\u003e {\n\tlet (higher, lower) = partition(tail, fn(x) =\u003e x \u003e= pivot)\n        sort(lower) + [pivot] + sort(higher)\n    }\n\ninspect(sort([5, 3, 7, 9, 10, 4, 6])) # [3, 4, 5, 6, 7, 9, 10]\n```\n\n#### Run Length Encode\n```ex\nlet run_len_encode = fn(ls) =\u003e match ls\n    | [] -\u003e []\n    | [x | xs] -\u003e {\n        let next = run_len_encode(xs)\n        match next\n            | [(next_x, cnt) | tl] when x == next_x -\u003e [(x, cnt + 1) | tl]\n            | _ -\u003e [(x, 1) | next]\n    }\n\nlet test_ls = [1, 1, 2, 3, 4, 4, 4, 5, 6, 1, 2, 2]\n\n# [(1., 2.), (2., 1.), (3., 1.), (4., 3.), (5., 1.), (6., 1.), (1., 1.), (2., 2.)]\ninspect(run_len_encode(test_ls))\n```\n\n#### Binary Search Tree\n```ex\nlet insert = fn(root, key) =\u003e match root\n    | () -\u003e %{val: key}\n    | %{right, val} when val \u003c key -\u003e %{right: insert(right, key) | root}\n    | %{left} -\u003e %{left: insert(left, key) | root}\n\nlet tree_to_ls_inorder = {\n    let loop = fn(root, acc) =\u003e match root\n\t| () -\u003e acc\n\t| %{val, left, right} -\u003e {\n\t    let acc = loop(left, acc)\n\t    let acc = [val | acc]\n\t    loop(right, acc)\n\t}\n\n    fn(bst) =\u003e reverse(loop(bst, []))\n}\n\nlet construct_from_list = fn(ls) =\u003e\n    fold((), fn(t, v) =\u003e insert(t, v), ls)\n\nlet ls = [50, 30, 20, 65, 42, 20, 40, 70, 60, 80]\nlet bst = construct_from_list(ls)\ninspect(tree_to_ls_inorder(bst)) # [20, 20, 30, 40, 42, 50, 60, 65, 70, 80]\n```\n\n#### Two Sum\n```ex\nlet two_sum = fn(nums, target) =\u003e {\n    let helper = fn(m, ls, target) =\u003e match ls\n        | [] -\u003e ()\n        | [(i, x) | xs] -\u003e {\n            let complement = target - x\n            match m\n                | %{complement =\u003e ()} -\u003e helper(%{x: i | m}, xs, target)\n                | %{complement =\u003e y} -\u003e (y, i)\n        }\n\n    helper(%{}, enumerate(nums), target)\n}\n\ninspect(two_sum([1,9,13,20,47], 10)) # (0, 1)\ninspect(two_sum([3,2,4,1,9], 10)) # (0, 4)\ninspect(two_sum([], 10)) # ()\n```\n\n##### Caesar Cipher\n```ex\nlet (to_number, to_letter) = {\n    let enumerated = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" |\u003e to_charlist |\u003e enumerate\n    let to_number = fold(%{}, fn(m, (i, l)) =\u003e %{l =\u003e i | m}, enumerated)\n    let to_letter = fold(%{}, fn(m, (i, l)) =\u003e %{i =\u003e l | m}, enumerated)\n    (to_number, to_letter)\n}\n\nlet encode = fn(text, n) =\u003e {\n    let shift = shift % 26\n\n    let loop = fn(char_ls, acc) =\u003e match char_ls\n\t| [] -\u003e concat(reverse(acc))\n\t| [c | xs] when to_number(c) == () -\u003e loop(xs, [c | acc])\n\t| [c | xs] -\u003e {\n\t    let new_letter = c\n\t\t|\u003e to_number\n\t\t|\u003e add(shift, _)\n\t\t|\u003e fn(c) =\u003e if c \u003c 0 then 26 + c else c\n\t\t|\u003e fn(c) =\u003e to_letter(c % 26)\n\t    loop(xs, [new_letter | acc])\n\t}\n\n    loop(to_charlist(text), [])\n}\n\nlet decode = fn(text, n) =\u003e encode(text, -n)\n\ninspect(encode(\"HELLO WORLD\", 5)) # \"MJQQT BTWQI\"\ninspect(decode(encode(\"HELLO WORLD\", 5), 5)) # \"HELLO WORLD\"\n```\n\n##### Project Euler #1\n```ex\neuler1 = sum([x for x in [1..1000] if x % 3 == 0 || x % 5 == 0])\ninspect(euler1) # 233168\n```\n\n##### Project Euler #2\n```ex\nlet euler2 = {\n    let aux = fn((a, b), acc) =\u003e\n        if b \u003c 4000000 then \n            aux((b, a + 4 * b), acc + b)\n       else \n            acc\n\n    aux((0, 2), 0)\n}\n\ninspect(euler2) # 4613732\n```\n\n#### Euler 3\n```ex\nlet gcd = fn(a, b) =\u003e match (a, b)\n    | (0, x)|(x, 0) -\u003e x\n    | (a, b) when a \u003e b -\u003e gcd(b, a)\n    | (a, b) -\u003e {\n        let remainder = b % a\n        if remainder != 0 then (gcd(a, remainder)) else a\n    }\n\nlet abs = fn(x) =\u003e if x \u003c 0 then -x else x\n\nlet pollard = fn(n) =\u003e match n\n    | 1 -\u003e ()\n    | n when n % 2 == 0 -\u003e 2\n    | n -\u003e {\n        let g = fn(x, n) =\u003e (x * x + 1) % n\n        let iter = fn(x, y, d) =\u003e match (x, y, d)\n            | (x, y, 1) -\u003e {\n                let x = g(x, n)\n                let y = g(g(y, n), n)\n                let d = gcd(abs(x - y), n)\n                iter(x, y, d)\n            }\n            | (_, _, d) -\u003e if d == n then () else d\n\n        iter(2, 2, 1)\n    }\n\nlet factor = fn(n) =\u003e {\n    let d = pollard(n)\n    if d == () then () else n / d\n}\n\nlet euler3 = {\n    # repeatedly factors until largest is found\n    let aux = fn(n) =\u003e match factor(n)\n        | () -\u003e n\n        | f when n == f -\u003e f\n        | f -\u003e aux(f)\n\n    let n = 600851475143\n    aux(n)\n}\n\ninspect(euler3) # 6857\n```\n\nMore project euler problems can be found in the [examples folder](https://github.com/mkhan45/RustScript2/tree/main/examples).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkhan45%2Frustscript2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkhan45%2Frustscript2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkhan45%2Frustscript2/lists"}