{"id":21224457,"url":"https://github.com/eignnx/funlang","last_synced_at":"2025-03-15T01:41:22.742Z","repository":{"id":47463460,"uuid":"381545892","full_name":"eignnx/funlang","owner":"eignnx","description":"A programming language similar to OCaml and Rust with Ruby-inspired syntax.","archived":false,"fork":false,"pushed_at":"2023-06-22T03:46:58.000Z","size":303,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-21T17:35:41.446Z","etag":null,"topics":["bytecode-interpreter","programming-language"],"latest_commit_sha":null,"homepage":"","language":"Haskell","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/eignnx.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}},"created_at":"2021-06-30T01:54:48.000Z","updated_at":"2023-04-30T18:00:19.000Z","dependencies_parsed_at":"2024-11-20T22:58:35.866Z","dependency_job_id":"897e320d-5d1e-4ed8-8d9a-4a879b1d9a13","html_url":"https://github.com/eignnx/funlang","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/eignnx%2Ffunlang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eignnx%2Ffunlang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eignnx%2Ffunlang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eignnx%2Ffunlang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eignnx","download_url":"https://codeload.github.com/eignnx/funlang/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243672369,"owners_count":20328762,"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":["bytecode-interpreter","programming-language"],"created_at":"2024-11-20T22:58:29.127Z","updated_at":"2025-03-15T01:41:22.714Z","avatar_url":"https://github.com/eignnx.png","language":"Haskell","readme":"# Funlang\n\nIt's a fun language-side-project (not that I have a real project to work on tho).\n\n## What it Looks Like\n```ruby\ntype NamedColor\n  | :Red\n  | :Green\n  | :Blue\n  | :Cyan\n  | :Magenta\n  | :Yellow\n  | :Black\n  | :White\nend\n\ntype Color\n  | :Rgb Int, Int, Int\n  | :Hsb Int, Int, Int\n  | NamedColor\nend\n\ndef main[] do\n  let green = { :Green } as NamedColor;\n  let color = { :Rgb 50, 100, 200 } as Color;\n  color = green; # Note: allowed because NamedColor \u003c: Color.\n\n  match color\n    | { :Red } =\u003e intr.puts[\"I love red!\"]\n    | { :Rgb r, g, b } =\u003e\n        intr.puts[\"The red component is\"];   intr.dbg_int[r];\n        intr.puts[\"The green component is\"]; intr.dbg_int[g];\n        intr.puts[\"The blue component is\"];  intr.dbg_int[b];\n    | other =\u003e intr.puts[\"I don't know that color!\"];\n  end\nend\n```\n\n### Longer Example\n```ruby\ntype rec Expr\n  | :Num Int\n  | :Bool Bool\n  | :Add rec, rec\n  | :Mul rec, rec\n  | :If rec, rec, rec\n  | :Var Text\n  | :Let Text, rec, rec\nend\n\n# Note: `Val` and `Expr` are allowed to have overlapping constructor names!\ntype Val\n  | :Num Int\n  | :Bool Bool\nend\n\ntype rec Ctx\n  | :Empty\n  | :Bind Text, Val, rec\nend\n\ndef eval[ctx: Ctx, expr: Expr] -\u003e Tuple[Ctx, Val] do\n  match expr\n    | { :Num x } =\u003e { ctx, { :Num x } }\n    | { :Bool x } =\u003e { ctx, { :Bool x } }\n    | { :Add x, y } =\u003e\n        let { ctx, x } = eval[ctx, x];\n        let { ctx, y } = eval[ctx, y];\n        let { :Num x } = x else\n          type_error[\"The `+` operator requires numbers!\"];\n        end\n        let { :Num y } = y else\n          type_error[\"The `+` operator requires numbers!\"];\n        end\n        { ctx, { :Num x + y } }\n    | { :Mul x, y } =\u003e\n        let { ctx, x } = eval[ctx, x];\n        let { ctx, y } = eval[ctx, y];\n        let { :Num x } = x else\n          type_error[\"The `*` operator requires numbers!\"];\n        end\n        let { :Num y } = y else\n          type_error[\"The `*` operator requires numbers!\"];\n        end\n        { ctx, { :Num x * y } }\n    | { :If cond, yes, no } =\u003e\n        let { ctx, cond } = eval[ctx, cond];\n        let { :Bool cond } = cond else\n          type_error[\"An `if` expression requires a boolean in it's conditional!\"];\n        end\n        if cond then\n          eval[ctx, yes]\n        else\n          eval[ctx, no]\n        end\n    | { :Var x } =\u003e { ctx, ctx_lookup[ctx, x] }\n    | { :Let x, expr, body } =\u003e\n        let { ctx, val } = eval[ctx, expr];\n        let ctx = { :Bind x, val, ctx };\n        eval[ctx, body]\n  end\nend\n\ndef ctx_lookup[ctx: Ctx, x: Text] -\u003e Val do\n  match ctx\n    | { :Empty } =\u003e\n        intr.puts[\"Unbound variable \" ++ x];\n        intr.exit[];\n    | { :Bind y, val, ctx } =\u003e\n        if intr.eq_text[y, x] then\n          val\n        else \n          ctx_lookup[ctx, x]\n        end\n  end\nend\n\ndef type_error[msg: Text] -\u003e Never do\n  intr.puts[\"Type Error: \" ++ msg];\n  intr.exit[];\nend\n\ndef print_val[val: Val] do\n  match val\n    | { :Num x } =\u003e intr.dbg_int[x]\n    | { :Bool x } =\u003e intr.dbg_bool[x]\n    end\nend\n\ndef main[] do\n  let ctx = { :Empty };\n  let expr =\n    { :Let \"x\", { :Num 123 },\n      { :Add { :Mul { :Num 2 }, { :Num 3 } }, { :Var \"x\" } } };\n  let { ctx, result } = eval[ctx, expr];\n  print_val[result];\nend\n```\n\n## Build/Run Instructions\n### Dependencies\n- You need `stack` installed. I'm using version `2.9.3` just FYI.\n- `Stack` should be using `ghc-8.10.4` for the haskell compiler.\n- You need `cargo` to run the Rust bytecode interpreter.\n\n### Running a Source File\n```bash\n$ stack run -- path/to/my/source-file.funlang\n```\n\n## Features/TODO\n- [X] Compile a (somewhat) high-level language down to bytecode that can be run on a virtual machine\n    - [X] Impl basic operations, and control flow statements\n    - [X] Impl function calls\n    - [ ] Impl lexical scoping for blocks\n    - [ ] Impl closures\n- [ ] Add a bidirectional type-checking algorithm\n  - [X] Implement non-polymorphic type checking\n  - [ ] Implement polymorphic type checking\n- [ ] Add algebraic data types\n  - [X] Impl basic variant types\n  - [X] Impl tuple types\n  - [X] Impl record types (currently called Mod types)\n    - [ ] Call them records instead\n  - [X] Allow recursive types\n  - [ ] Impl `match` expressions\n    - [X] Support basic pattern matching\n    - [ ] Allow irrefutable patterns in `match` expressions\n    - [ ] Impl literal patterns\n    - [ ] Impl pattern guards\n  - [X] Impl `let-else` expressions\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feignnx%2Ffunlang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feignnx%2Ffunlang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feignnx%2Ffunlang/lists"}