{"id":16210244,"url":"https://github.com/bastianblokland/novus","last_synced_at":"2025-03-19T09:30:30.450Z","repository":{"id":43946342,"uuid":"211661903","full_name":"BastianBlokland/novus","owner":"BastianBlokland","description":"General purpose, statically typed, functional programming language","archived":false,"fork":false,"pushed_at":"2025-02-14T20:39:59.000Z","size":3294,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T17:12:37.678Z","etag":null,"topics":["compiler","cxx","language","novus","programming-language","toy-compiler","toy-language"],"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/BastianBlokland.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-09-29T12:46:35.000Z","updated_at":"2025-02-14T20:40:02.000Z","dependencies_parsed_at":"2024-10-27T20:27:37.352Z","dependency_job_id":"96f58d11-04e2-4486-a3e3-89fa8ea4ffe9","html_url":"https://github.com/BastianBlokland/novus","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BastianBlokland%2Fnovus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BastianBlokland%2Fnovus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BastianBlokland%2Fnovus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BastianBlokland%2Fnovus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BastianBlokland","download_url":"https://codeload.github.com/BastianBlokland/novus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243976375,"owners_count":20377690,"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","cxx","language","novus","programming-language","toy-compiler","toy-language"],"created_at":"2024-10-10T10:36:25.687Z","updated_at":"2025-03-19T09:30:29.826Z","avatar_url":"https://github.com/BastianBlokland.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Novus\n\n![build](https://img.shields.io/github/actions/workflow/status/BastianBlokland/novus/.github/workflows/build.yml?branch=master)\n[![codecov](https://codecov.io/gh/BastianBlokland/novus/branch/master/graph/badge.svg?token=JK2KNANZ73)](https://codecov.io/gh/BastianBlokland/novus)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nNovus is a general purpose, statically typed, functional programming language.\nNovus source compiles into instructions for the Novus virtual machine which provides the runtime\n(eg Garbage collection) and platform abstraction (eg io).\nThe runtime supports Linux, Mac and Windows at this time.\n\nNote: This is intended as an academic exercise and not meant for production projects.\n\n# Intro\n\nNovus is an expression based pure functional language with eager evaluation. In practice this means\nthat all types are immutable, normal functions cannot have side-effects and all functions have to\nproduce a value.\n\nSimplest function:\n```n\nfun square(int x) x * x\n```\nInput parameters have to be typed manually but the return type is inferred from the expression.\n\nAdvantage of providing type information for the input parameters is that functions can be overloaded:\n```n\nfun getTxt(string s)  s\nfun getTxt(bool b)    b ? \"true\" : \"false\"\nfun getTxt(User u)    n.name\n```\n\nExpressions can be combined using the group operator ';' (similar to the comma operator in some\nlanguages). In an expression group the result is the value of the last expression.\n\nCan be used to define a constant and reuse it in the next expression:\n```n\nfun cube(int x)\n  sqr = x * x; sqr * x\n```\nNote: All 'variables' are single assignment only, so in the above example `sqr` cannot be redefined\nto mean something else later.\n\n## Control flow\n\n### Conditional operator\nFor simple control-flow the conditional-operator (aka ternary-operator) can be used:\n```n\nfun max(int a, int b)\n  a \u003e b ? a : b\n```\n\n### Switch expression\nFor more elaborate control-flow there is the switch expression:\n```n\nfun fizzbuzz(int i)\n  fizz = i % 3 == 0;\n  buzz = i % 5 == 0;\n  if fizz \u0026\u0026 buzz -\u003e \"FizzBuzz\"\n  if fizz         -\u003e \"Fizz\"\n  if buzz         -\u003e \"Buzz\"\n  else            -\u003e string(i)\n```\nThe switch expression is very similar to 'if' statements in imperative languages, however in\nNovus its an expression so the entire switch has to produce a value of a single type (meaning\nit has to be exhaustive, so an 'else' is required if the compiler cannot guarantee the if conditions\nbeing exhaustive).\n\nNote: there are no loops in Novus, instead iterating is done using recursion. When performing tail\nrecursion the runtime guarantees to execute it in constant stack space.\n\n## Type system\n\nThe Novus type system contains some basic types build in to the language:\n* `int` (32 bit signed integer)\n* `long` (64 bit signed integer)\n* `float` (32 bit floating point number)\n* `bool`\n* `string`\n* `char` (8 bit unsigned integer)\n\n(Plus a few more niche types: `sys_stream`, `sys_process`, `function`, `action`, `future`, `lazy`\nand `lazy_action`).\n\nNote: These are the types the language itself supports, there are however many more types\nimplemented in the [standard library](https://github.com/BastianBlokland/novus/tree/master/std).\n\nAnd can be extended with three kinds of user defined types:\n\n### Struct (aka record)\n```n\nstruct User =\n  string  name,\n  int     age\n\nfun getDefaultUser()\n  User(\"John doe\", 32)\n\nfun getName(User u)\n  u.name\n```\n\n### Union (aka discriminated union or tagged union)\n```n\nunion IntOrBool = int, bool\n\nfun getVal(int i)\n  i == 0 ? false : i\n\nfun getNum(IntOrBool ib)\n  if ib as int  i -\u003e i\n  if ib as bool b -\u003e b ? 1 : 0\n```\nNote that there is no 'else' in the last switch expression, this is allowed because the compiler\ncan guarantee that the if conditions are exhaustive.\n\n### Enum (aka enumeration)\n```n\nenum WeekDay =\n  Monday    : 1,\n  Tuesday   : 2,\n  Wednesday : 3,\n  Thursday,\n  Friday,\n  Saturday,\n  Sunday\n\nfun sunday()\n  WeekDay.Sunday\n\nfun isSunday(WeekDay wd)\n  wd == WeekDay.Sunday\n```\nEnum's follow the conventions that most c-like languages have, they are named values. If no value\nis provided the last value is automatically incremented by one (starting from 0).\n\n## Generic programming (type and function templates)\n\nTo aid in generic programming you can create type and function templates\n(similar in spirit to c++ templates). Instead of angle brackets '\u003c\u003e' found in many other\nlanguages to define a type set, Novus uses curly braces '{}'.\n\n### Type template\n```n\nstruct Pair{T1, T2} =\n  T1 first,\n  T2 second\n```\n\nInstantiation of a type template:\n```n\nfun sum(float a, float b)\n  sum(Pair(a, b))\n\nfun sum(Pair{float, float} p)\n  p.first + p.second\n```\nNote: When constructing a type the type parameters can be inferred from usage.\n\n### Function template\n```n\nfun sum{T}(T a, T b)\n  a + b\n\nfun onePlusTwo()\n  sum(1, 2)\n```\nNote: When calling a templated function most of the time the type parameters can be inferred.\n\n## Operator overloading\n\nOperators can defined like any other function.\n```n\nstruct Pair = int first, int second\n\nfun +(Pair a, Pair b)\n  Pair(a.first + b.first, a.second + b.second)\n\nfun [](Pair p, int i)\n  i == 0 ? p.first : p.second\n\nfun sum(Pair a, Pair b)\n  a + b\n\nfun getFirst(Pair p)\n  p[0]\n```\n\nThe following list of operators can be overloaded:\n`+`, `++`, `-`, `--`, `*`, `/`, `%`, `\u0026`, `|`, `!`, `!!`, `\u003c\u003c`, `\u003e\u003e`, `^`, `~`, `==`, `!=`, `\u003c`, `\u003e`,\n`\u003c=`, `\u003e=`, `::`, `[]`, `()`, `?`, `??`.\n\nNote: All operators are left associative except for the `::` operator, which makes the `::` operator\nideal for creating linked lists.\n\n## First class functions\n\nFunctions can be passed as values using the build-in `function{}` type template.\nThe last type in the type-set is the return-type, the types before that are the input types.\n```n\nfun performOp(int val, function{int, int} op)\n  op(val)\n\nfun square(int v) v * v\nfun cube(int v)   v * v * v\n\nprint(performOp(43, cube))\nprint(performOp(43, square))\n```\n\n### Lambda's (aka anonymous functions)\n\nAnonymous functions can be defined using the lambda syntax.\n```n\nfun performOp(int val, function{int, int} op)\n  op(val)\n\nprint(performOp(43, lambda (int x) x * x))\n```\n\n### Closures\n\nLamba's can capture variables from the scope they are defined in.\n```n\nfun performOp(int val, function{int, int} op)\n  op(val)\n\nfun makeAdder(int y)\n  lambda (int x) x + y\n\nprint(performOp(42, makeAdder(1337))\n```\n\n## Instance calls\n\nThe first argument to a function can optionally be placed in front of the function call.\nThis is syntactic sugar only, but can aid in making function 'chains' easier to read.\n```n\nfun isEven(int x) (x % 2) == 0\nfun square(int x) x * x\n\nprint(rangeList(0, 100).filter(isEven).map(square))\n```\n\n## Parallel computing\n\nPutting the keyword `fork` in front of any function call runs it on its own executor (thread) and\nreturns a `future{T}` handle to the executor.\n\n```n\nfun fib(int n)\n  n \u003c= 1 ? n : fib(n - 1) + fib(n - 2)\n\nfun calc()\n  a = fork fib(25);\n  b = fork fib(26);\n  a.get() + b.get()\n```\nNote this is where a pure functional language shines, its completely safe to run any pure function\nin parallel without any need for synchronization.\n\n## Actions\nAs mentioned before functions are pure and cannot have any side-effects, but a program without\nside-effects is not really useful (something about a tree falling in a forest..).\n\nThat's why there are special kind of impure functions which are allowed to perform side-effects.\nThose functions are called 'actions'. An action is allowed to call into a function but not vise\nversa.\n\n```n\nimport \"std.ns\"\n\nact printFile(Path file)\n  in  = fileOpen(file, FileMode.Open);\n  out = consoleOpen(ConsoleKind.StdOut);\n  copy(in, out)\n\nact main()\n  print(\"Which file do you want to print?\");\n  p = Path(readLine());\n  print(\"Ok, printing: \" + p);\n  printFile(p)\n\nmain()\n```\n\nNote: To pass an action as a value you use the `action{}` type template instead of the\n`function{}` one.\n\nNote: To create an 'action' lambda you can use the `impure` keyword in front of the lambda:\n`impure lambda (int x) x * x`\n\n# Try it out\n\n## Examples\n\nSeveral examples can be found in the [examples](https://github.com/BastianBlokland/novus/tree/master/examples)\ndirectory.\n\n## Docker\n\nYou can quickly try it out using docker.\nOpen a interactive container with the compiler, runtime and examples installed:\n`docker run --rm -it bastianblokland/novus sh`\n\nRun an example:\n`nove.nx examples/fizzbuzz.ns`\n\n## Installing the compiler and runtime\n\nAt this time there are no releases of binary files, however you can try out the binaries produced\nby the [ci](https://github.com/BastianBlokland/novus/actions).\n\nThe best way is to build the compiler and runtime yourself, the process is documented below.\n\n# Building\n\n## Requirements\n\n### Linux\n\n* [CMake](https://cmake.org/) (At least version `3.15`).\n\n* Modern C++ compiler (at least c++ 17) (Tested with `gcc 7.5.0` and `clang 9.0.0`).\n\n### MacOs\n\n* [CMake](https://cmake.org/) (At least version `3.15`).\n\n* Modern C++ compiler (at least c++ 17) (Tested with `apple clang 11` and `clang 9.0.0`).\n\n### Windows\n\n* [CMake](https://cmake.org/) (At least version `3.15`).\n\n  For example with `chocolatey`: `choco install cmake`.\n\nBuilding can either be done using MinGW (windows port of Gcc and related tooling) or\nVisual Studio (msvc).\n\n#### MinGW\n\n* [MinGW-w64](http://mingw-w64.org/). Note: Either version `7.x` or `9.x`, version `8.x` has a\n[broken `std::filesystem` library impl](https://sourceforge.net/p/mingw-w64/bugs/737/).\n\n  For example with `chocolatey`: `choco install mingw --version=7.3.0`. Unfortunately `9.x` is at\n  the time of writing not yet on `chocolatey`, but the [nuwen](https://nuwen.net/mingw.html)\n  distribution ships it.\n\n### Visual Studio\n\n* Tested with Visual Studio 2019 (make sure the c++ tooling is installed).\n\n* For command-line building install `vswhere` (https://github.com/microsoft/vswhere).\n\n  For example with `chocolatey`: `choco install vswhere`.\n\n## Configure\n\nBefore building you have to configure the project, run `scripts/configure.sh` on unix or\n`scripts/configure.ps1` on windows.\n\nFor Visual Studio run `scripts/configure.ps1 -Gen VS2019`, after which the Visual Studio project\ncan be found in the `build` directory.\n\nOn unix run `scripts/configure.sh --help` for a listing of options, on windows:\n`Help scripts/configure.ps1`\n\n## Building\n\nAfter configuring the project can be build by running `scripts/build.sh` on unix or\n`scripts/build.ps1` on windows.\n\nBuild output can be found in the `bin` directory.\nFor more convenience you can add the `bin` directory to your `PATH`.\n\n## Building novus source code\n\nNovus source (`.ns`) can be compiled into an novus executable (`.nx`) using the `novc` compiler.\n\nExample: `./bin/novc examples/fizzbuzz.ns`. The output can be found at `examples/fizzbuzz.nx`.\n\n## Running novus an executable\n\nAn Novus executable (`.nx`) can be run in the novus runtime (`novrt`).\n\nExample: `./bin/novrt examples/fizzbuzz.nx`.\n\nFor more convenience you can also run `.nx` files without specifying the runtime:\n\n### Unix\n\nOn unix this can be achieved by adding the runtime to your `PATH`.\n\n### Windows\n\nOn Windows you will have to install file associations using `./bin/novrt --install` or run\nthe `./bin/novus-install.bat` batch script.\n\nThat way windows knows to open `.nx` files with the `novrt.exe`\nexecutable. To uninstall the associations run `./bin/novrt --uninstall` or the\n`./bin/novus-uninstall.bat` batch script.\n\nIn either case the result is that you can directly run novus executables:\n```\n./fizzbuzz.nx\n```\n\n## Evaluator\n\nAlternatively you can use the `nove.nx` (novus evaluator) to combine the compilation and running.\n\nYou can either pass the source straight to the evaluator:\n`./bin/nove.nx 'print(pow(42, 1.337))'`.\n\nor give a `.ns` source file to the evaluator:\n`./bin/nove.nx examples/fizzbuzz.ns`.\n\n## Debugging\n\nWhile there is no debugger (yet?) for `novus` programs, there are a few diagnostic programs:\n* `novdiag-prog` (Show the generated ast, optionally after optimizing)\n* `novdiag-asm` (Show the generated nov assembly code)\n* `novdiag-parse` (Show the parse-tree output)\n* `novdiag-lex` (Show the output tokens of the lexer)\n\n## Testing\n\nAfter building the project you can run the tests by running `scripts/test.sh` on unix or\n`scripts/test.ps1` on windows.\n\nNote: To run the compiler and vm tests they have to be enabled and build in the configure step.\n\n## Ide\n\nFor basic ide support when editing `novus` source code check the `ide` directory if there is a\nplugin for your ide.\n\nFor ide support when editing the compiler and vm:\n* Configure the project: `make configure.debug`.\n* Copy / symlink the file `build/compile_commands.json` to the root of the project.\n* Use a ide extension for `cpp` support, for example: `clangd` (vscode ext: [vscode-clangd](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd))\n\n## Name\n\nNaming things is hard 😅 From the Stargate tv-show: [Novus = 'new' in ancient](https://stargate.fandom.com/wiki/Novus).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbastianblokland%2Fnovus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbastianblokland%2Fnovus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbastianblokland%2Fnovus/lists"}