A Nova implementation in Python.
Find a file
2025-07-01 15:27:37 -07:00
examples Correcting a typo. 2025-07-01 15:27:37 -07:00
nova-compiler.py Add argument parsing to compiler and interpreter 2025-05-30 22:04:26 -05:00
nova.py Adding escapes for delimiters. 2025-06-13 17:47:43 -07:00
pyproject.toml Added pyproject.toml. 2025-05-21 18:10:19 -07:00
README.md Cleaning up. 2025-05-11 16:41:24 -07:00
trie.py Added a small visualizer for Nova rulesets. 2025-06-04 15:11:51 -07:00

Nova


Nova is a multi-paradigm rule-oriented programming language with a flexible syntax. It is heavily influenced by literate programming concepts, can be learned and implemented in a short amount of time, and can be embedded into existing projects.

This repository holds the first sketch of a Nova interpreter in Python. It is slow and most likely buggy and will be gradually improved over time.

This repository also holds a fairly hacky Nova-to-C compiler, which is maintained in parallel (to the best of my ability). It should generate a fragment of compliant C and C++ from Nova source code.

Tutorials, introductory guides, and explainers are soon to come.

Getting Started


Running Code

To run a piece of Nova source code, you'd just need to run the following snippet.

[$] ./nova.py my-code.nv

By default, the state of your program is hidden from view. If your program did not produce any output or accept any input, it'll look as if nothing happened. To get a better view of what's going on underneath, we can use the -d flag to raise the debug level of the interpreter.

[$] ./nova.py my-code.nv -d

This will print out state of the Nova interpreter when your program terminates, allowing you to see the results of your program without having to explicitly output anything. If you add multiple -d flags, you will continue to raise the debug level, giving you a real-time stream of what your program is doing internally as it executes.

The interpreter also accepts multiple files, and concatenates them internally into one large file before running.

[$] ./nova.py file-1.nv file-2.nv file-3.nv

This will combine all of the files specified into one large file. It's equivalent to..

[$] cat file-1.nv file-2.nv file-3.nv > file.nv; ./nova.py file.nv

...on Unix systems.

Using the REPL

The interpreter also comes with a REPL, which can be invoked by passing -r as a flag, like so.

[$] ./nova.py my-code.nv -r

This may be combined with the -d flag to view details of your program's execution. When the REPL is initiated, you'll be dropped to a prompt that looks like the following snippet.

[$] ./nova.py -r
::>

The Active Stack

When using the REPL, there is always one stack that is "active", or selected. Anything you type will be parsed as a fact and pushed to the active stack. For example..

::> hello nova
:: hello nova
::> this is an example 
:: this is an example
   hello nova
::> of some input
:: of some input
   this is an example
   hello nova
::>

Notice how newer items appear above older items. Stacks are displayed from the top down, so as you give the REPL input, it'll push that input to the active stack. To change the active stack, you can use the @ command.

::> @todo
:todo:> sweep floors
todo: sweep floors
:todo:> do laundry
todo: do laundry
      sweep floors
:todo:> make lunch
todo: make lunch
      do laundry
      sweep floors
:todo:> get groceries
todo: get groceries
      make lunch
      do laundry
      sweep floors
:todo:>

When switching between stacks, the things you've already pushed stay pushed. This allows you to switch back and forth between stacks, interactively debugging your program.

Interactive Development

The interpreter also allows you to invoke the REPL after running one or multiple files, like so.

[$] cat my-code.nv 
|::|:greeting: welcome to the REPL!
[$] ./nova.py my-code.nv -r
greeting: welcome to the REPL!
::>

In the REPL, you have the ability to define new rules that are either temporary or permanent. To run a temporary rule, we can use the . command.

::> @echo
:echo:> hello nova
echo: hello nova
:echo:> . |:echo: $some $thing| :tell the user: $some $thing
tell the user: hello nova
:echo:> 

This will temporarily add any Nova code you type in to the interpreter's internal state and then trigger a full program run. To make the change permanent, you can use the : command instead.

:> @echo
:echo:> hello nova
echo: hello nova
:echo:> . |:echo: $some $thing| :tell the user: $some $thing
tell the user: hello nova
:echo:> ^C[linear@wyvern nova]$ ./nova.py -r
::> @echo
:echo:> hello nova
echo: hello nova
:echo:> : |:echo: $some $thing| :tell the user: $some $thing
tell the user: hello nova
:echo:> hello nova
tell the user: hello nova
               hello nova
:echo:> hello nova
tell the user: hello nova
               hello nova
               hello nova
:echo:> hello nova
tell the user: hello nova
               hello nova
               hello nova
               hello nova
:echo:>

By using ., : and @, you can incrementally build a Nova program entirely in the REPL! New rules defined with . or : are added above old rules, and thus take priority, allowing you to make small patches as you work.