Git Product home page Git Product logo

mamba's Introduction

Mamba logo

GitHub Workflow Status Codecov coverage Crate
License Active milestones Built with Love

Mamba

This is the Mamba programming language. Mamba is like Python, but with a few key features:

  • Strict static typing rules, but with type inference so it doesn't get in the way too much
  • Type refinement features
  • Null safety
  • Explicit error handling
  • A distinction between mutability and immutability
  • Pure functions, or, functions without side effects

This is a transpiler, written in Rust, which converts Mamba source files to Python source files. Mamba code should therefore be interoperable with Python code. Functions written in Python can be called in Mamba and vice versa (from the generated Python files).

โŒจ๏ธ Code Examples

Below are some code examples to showcase the features of Mamba. We highlight how functions work, how de define classes, how types and type refinement features are applied, how Mamba can be used to ensure pureness, and how error handling works.

โž• Functions

We can write a simple script that computes the factorial of a value given by the user.

def factorial(x: Int) -> Int => match x
    0 => 1
    n => n * factorial(n - 1)

def num := input("Compute factorial: ")
if num.is_digit() then
    def result := factorial(Int(num))
    print("Factorial {num} is: {result}.")
else
    print("Input was not an integer.")

Notice how here we specify the type of argument x, in this case an Int, by writing x: Int. This means that the compiler will check for us that factorial is only used with integers as argument.

Note One could use dynamic programming in the above example so that we consume less memory:

def factorial(x: Int) -> Int => match x
    0 => 1
    n =>
        def ans := 1
        for i in 1 ..= n do ans := ans * i
        ans

๐Ÿ“‹ Types, Classes, and Mutability

Classes are similar to classes in Python, though we can for each function state whether we can write to self or not by stating whether it is mutable or not. If we write self, it is mutable, whereas if we write fin self, it is immutable and we cannot change its fields. We can do the same for any field. We showcase this using a simple dummy Server object.

from ipaddress import IPv4Address

class ServerError(def message: Str): Exception(message)

def fin always_the_same_message := "Connected!"

class MyServer(def ip_address: IPv4Address)
    def is_connected: Bool  := False
    def _last_message: Str  := "temp"

    def last_sent(fin self) -> Str raise [ServerError] =>
        self._last_message

    def connect(self) =>
        self.is_connected := True
        print(always_the_same_message)

    def send(self, message: Str) raise [ServerError] =>
        if self.is_connected then
            self._last_message := message
        else
            raise ServerError("Not connected!")

    def disconnect(self) => self.is_connected := False

Notice how self is not mutable in last_sent, meaning we can only read variables, whereas in connect self is mutable, so we can change properties of self. We can then use MyServer as follows:

import ipaddress
from server import MyServer

def fin some_ip := ipaddress.ip_address("151.101.193.140")
def my_server   := MyServer(some_ip)

http_server.connect()
if my_server.is_connected then http_server.send("Hello World!")

# This statement may raise an error, but for now de simply leave it as-is
# See the error handling section for more detail
print("last message sent before disconnect: \"{my_server.last_sent()}\".")
my_server.disconnect()

๐Ÿ—ƒ Type refinement (๐Ÿ‡ป 0.4.1+)

As shown above Mamba has a type system. Mamba however also has type refinement features to assign additional properties to types. Lets expand our server example from above, and rewrite it slightly:

from ipaddress import IPv4Address

type ConnMyServer: MyServer when self.is_connected
type DisConnMyServer: MyServer when not self.is_connected

class ServerErr(def message: Str): Exception(message)

class MyServer(self: DisConnMyServer, def ip_address: IPv4Address)
    def is_connected: Bool  := False
    def _last_message: Str? := None

    def last_sent(self) -> Str raise [ServerErr] => 
        if self.last_message != None then 
            self._last_message
        else
            raise ServerError("No last message!")

    def connect(self: DisConnMyServer) => self.is_connected := True

    def send(self: ConnMyServer, message: Str) => self._last_message := message

    def disconnect(self: ConnMyServer) => self.is_connected := False

Within the then branch of the if statement, we know that self._last_message is a Str. This is because we performed a check in the if condition.

Also Notice how above, we define the type of self. Each type effectively denotes another state that self can be in. For each type, we use when to show that it is a type refinement, which certain conditions.

import ipaddress
from server import MyServer

def fin some_ip := ipaddress.ip_address("151.101.193.140")
def my_server   := MyServer(some_ip)

# The default state of http_server is DisconnectedHTTPServer, so we don't need to check that here
http_server.connect()

# We check the state
if my_server isa ConnMyServer then
    # http_server is a Connected Server if the above is true
    my_server.send("Hello World!")

print("last message sent before disconnect: \"{my_server.last_sent}\".")
if my_server isa ConnectedMyServer then my_server.disconnect()

Type refinement also allows us to specify the domain and co-domain of a function, say, one that only takes and returns positive integers:

type PosInt: Int when 
    self >= 0 else "Must be greater than 0"

def factorial(x: PosInt) -> PosInt => match x
    0 => 1
    n => n * factorial(n - 1)

In short, types allow us to specify the domain and co-domain of functions with regards to the type of input, say, Int or Str. During execution, a check is done to verify that the variable does conform to the requirements of the refined type. If it does not, an exception is raised.

Type refinement allows us to do some additional things:

  • It allows us to further specify the domain or co-domain of a function
  • It allows us to explicitly name the possible states of an object. This means that we don't constantly have to check that certain conditions hold. We can simply ask whether a given object is a certain state by checking whether it is a certain type.

๐Ÿ”’ Pure functions (๐Ÿ‡ป 0.4.1+)

Mamba has features to ensure that functions are pure, meaning that if x = y, for any f, f(x) = f(y). (Except if the output of the function is say None or NaN.) By default, functions are not pure, and can read any variable they want, such as in Python. When we make a function pure, it cannot:

  • Read non-final properties of self.
  • Call impure functions.

Some rules hold for calling and assigning to passed arguments to uphold the pure property (meaning, no side-effects):

  • Anything defined within the function body is fair game, it may be used whatever way, as it will be destroyed upon exiting the function.
  • An argument may be assigned to, as this will not modify the original reference.
  • The field of an argument may not be assigned to, as this will modify the original reference.
  • One may only read fields of an argument which are final (fin).
  • One may only call methods of an argument which are pure (pure).

When a function is pure, its output is always the same for a given input. It also has no side-effects, meaning that it cannot write anything (assign to mutable variables) or read from them. Immutable variables and pure functions make it easier to write declarative programs with no hidden dependencies.

# taylor is immutable, its value does not change during execution
def fin taylor := 7

# the sin function is pure, its output depends solely on the input
def pure sin(x: Int) =>
    def ans := x
    for i in 1 ..= taylor .. 2 do
        ans := ans + (x ^ (i + 2)) / (factorial (i + 2))
    ans

โš  Error handling

Unlike Python, Mamba does not have try except and finally (or try catch as it is sometimes known). Instead, we aim to directly handle errors on-site so the origin of errors is more tracable. The following is only a brief example.

We can modify the above script such that we don't check whether the server is connected or not. In that case, we must handle the case where my_server throws a ServerErr:

import ipaddress
from server import MyServer

def fin some_ip := ipaddress.ip_address("151.101.193.140")
def my_server   := MyServer(some_ip)

def message := "Hello World!"
my_server.send(message) handle
    err: ServerErr => print("Error while sending message: \"{message}\": {err}")

if my_server isa ConnectedMyServer then my_server.disconnect()

In the above script, we will always print the error since we forgot to actually connect to the server. Here we showcase how we try to handle errors on-site instead of in a (large) try block. This means that we don't need a finally block: We aim to deal with the error where it happens and then continue executing the remaining code. This also prevents us from wrapping large code blocks in a try, where it might not be clear what statement or expression might throw what error.

handle can also be combined with an assign. In that case, we must either always return (halting execution or exiting the function), or evaluate to a value. This is shown below:

def g() =>
    def a := function_may_throw_err() handle
        err: MyErr =>
            print("We have a problem: {err.message}.")
            return  # we return, halting execution
        err: MyOtherErr =>
            print("We have another problem: {err.message}.")
            0  # ... or we assign default value 0 to a

    print("a has value {a}.")

If we don't want to use a handle, we can simply use raise after a statement or exception to show that its execution might result in an exception, but we don't want to handle that here. See the sections above for examples where we don't handle errors and simply pass them on using raise.

๐Ÿ’ป The Command Line Interface

USAGE:
    mamba.exe [FLAGS] [OPTIONS]

FLAGS:
    -a, --annotate          Enable type annotation of the output source.
                            Currently still buggy feature.
    -d, --debug             Add line numbers to log statements
    -h, --help              Prints help information
    -l, --level             Print log level
        --no-module-path    Disable the module path in the log statements
        --no-color          Disable colorized output
    -v                      Set level of verbosity
                            - v   : info, error, warning printed to sterr (Default)
                            - vv  : debug messages are printed
                            - vvv : trace messages are printed
    -V, --version           Prints version information

OPTIONS:
    -i, --input <INPUT>      Input file or directory.
                             If file, file taken as input.
                             If directory, recursively search all sub-directories for *.mamba files.
                             If no input given, current directory used as input directory.
    -o, --output <OUTPUT>    Output directory to store Python files.
                             Output directory structure reflects input directory structure.
                             If no output given, 'target' directory created in current directory.

You can type mamba -help for a message containing roughly the above information.

๐Ÿ‘ฅ Contributing

Before submitting your first issue or pull request, please take the time to read both our contribution guidelines and our code of conduct.

mamba's People

Contributors

jsabrahams avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

mamba's Issues

Desugar calls to either python functions or descriptors

Description of the feature

Calls in the Mamba programming language can either be to a function with no arguments or can be a call to an attribute of an object.

In Python, a.b and a.c() are two different things.
In Mamba, We can call function c using a.c, a.c() and a c.
During the desugar stage we should either desugar based to a function call or descriptor access based on the type of a in this example.

Description of potential solution

During the desugar stage there should be some sort of context which should allow us to get the type of an expression, and the accompanying functions.
With this list of functions, we can determine whether we wish to access a function or an attribute.

Alternatives considered

It is also possible to check the type of everything again in the desugar stage.
However, this seems inefficient and would add a lot of unnecessary code, since this is already done in the type checking stage.

Additional context

We should also think of a system of how to check python files directly.
Currently, the type checker will now only check the types, and the functions they contain, in Mamba files, and not Python files.

Desugar match rule always expects Id type

Description of Bug

Desugar rule expects id type as condition, so we cannot for instance have an id or underscore without accompanying type.

How to Reproduce

The following will trigger the error:

def a <- self.z_modified handle
            err: Err1 => print "hello"
            err => print "world" # this causes an error during desugar stage

Expected behavior

The desugar stage should be able to handle id and underscore as match arm conditions, in addition to the current id type.

Add a verification/build script

Current Issue

There is no verification script. Cool projects have a verification script.
To be more specific, there is no verify script which contains all the tooling used to verify the codebase such as rustfmt or clippy.

Description of the feature

A verify script should ideally clean the project, build it, run the static analysis tools, and then run the tests.

Description of potential solution

Add a script which can be by cargo.
Cargo does have some support for this: https://doc.rust-lang.org/cargo/reference/build-scripts.html

Alternatives considered

...

Additional context

...

Make `in` a separate construct

Current Issue

We can only check containment using contains.

High-level description of the feature

Allow checking of containment using in such as in Python.

Description of potential implementation

Make in a separate construct, or, operator.
Then remove in from the grammar of the for loop, instead, we check that the collection itself is a in construct.

Add doc strings

Current Issue

In Python, we can add documentation using """.
Mamba should have a similar construct.

High-level description of the feature

Parse documentation and preserve this so Python code retains the docs written in Mamba.

Description of potential implementation

Lex """ as doc token, and add this to the pipeline.

Preserve types as type hints

Current Issue

Types were thrown away for the sake of keeping the desugar stage simple.
However, Python has type hints, which is also ideal for making pseudo-interfaces such as #120 .
We should make use of such interfaces.

High-level description of the feature

Desugar types in Mamba to the relevant types in Python.
Primitive types, such as Int and String should be desugared, but also generics and containers such as Tuples.

Extra care should be taken when implementing generics.

Description of potential implementation

Need to check where types may be in Python.
Personally, I've only seen these as part of variable definitions and function arguments, so we should probably only desugar these in those specific cases and not top-level.

Add not implemented error messages to desugar stage

Current Issue

In the desugar stage, it is best to add errors for language features that are not implemented yet instead of panicking for the first deliverable version.

Description of the feature

Add errors to desugar stage and descriptive messages instead of using the unimplemented! macro.

Add OS X to CI build configuration

Issue, or potential issue

We have no CI builds for mac OS.
We only have for Linux and Windows.
Currently, the project does build on Mac OS, but it is a better idea to use CI tools to check that this remains the case.
For a long time, for instance, the fact that the PYTHON value was not included in MacOS builds.

Potential solutions

Add osx to Travis matrix.

Add mutability field to function arguments

Description of the feature

We want to be able to specify whether a function argument is mutable or not.
The grammar of the language has been updated, but we need to update the implementation as well.

Description of potential solution

id-mut-maybe-type construct in grammar must be implemented.

Alternatives considered

...

Additional context

...

Create ENum class

Description of the feature

There should be a Python ENum class in the standard library of the language, as this is one of the features of the language.

Description of potential solution

Always add an ENum class to the output when creating Python source files (see #17).
The standard library could be outputted in its entirety to the output.

Alternatives considered

Perhaps in future, it might be better to have the allow developers to add the standard library of Mamba as a dependency to a project.
Note Python has an excellent standard library, so ideally this standard library shouldn't have to add much as Python itself already contains much functionality.

Additional context

...

Add desugar rule for retry statement

Current Issue

We don't desugar the retry statement.

Description of the feature

A retry should allow us to retry the statement in a handle.
This is akin to the retry in Eiffel.

Description of potential solution

Wrap entire try except in a while block perhaps.

Alternatives considered

Can't really think of anything else at the moment, but more elegant solutions might be possible.

Add generics to the language

Current Issue

There are no generics

High-level description of the feature

Add generics

Description of potential implementation

Add pattern matching to match

Current Issue

Builds upon #98.
In Mamba, we wish to have pattern matching eventually.

Description of the feature

Allow pattern matching in Mamba.

Description of potential solution

...

Operations are not parsed fully

Description of Bug

Operations are only parsed in situations where it starts with literal or self.

How to Reproduce

Take for instance self.other + 10
This is parsed as a function call, and it does not parse the operation fully.

Expected behaviour

We would expect that any expression followed by a known operator should be parsed as an operation, with the correct order of operations.

Additional context

...

Build a context containing classes and their signatures

Current Issue

Builds upon #147

High-level description of the feature

Do a preliminary analysis of all classes to get the names of all classes in a project and their accompanying functions.

note We do not have to check imports, though that should also be done at some point.

Description of potential implementation

At a step to the type checker stage which precedes the current one, and which builds a context with mappings from classes to their accompanying functions and signatures.

Classes should store which interfaces they implement (Mamba as of now does not support inheritance, this is part of a discussion).
Function signatures should store function arguments, return type (if given), and errors the function may raise.

Change match syntax

Current Issue

Currently match is a bit verbose, as writing match <expression> with ... is a bit cumbersome.
In particular the with keyword, which is used nowhere else and serves no real purpose here.

High-level description of the feature

It might be better to do it like this: <expression> match ..., with ... being the match arms.
This is similar to how Scala does it and to my eye a more elegant way to write match expressions.

Description of potential implementation

Remove with keyword, and make match a postfix function.

Overhaul test suite

Issue, or potential issue

Currently, the test suite is still somewhat manageable, but as the codebase grows, this will become more of an issue.
There are still some slight inconsistencies, and some things are not as elegant as they should be, such as error reporting or test setup and cleanup.

A few identified issues:

  • The error messages generated by util are not descriptive because we don't do anything with the results, we immediately unwrap(). This Instead, we should actually do something when for instance a file is not found.
  • Function names in util are a bit long and unwieldy, we should consider changing these to make these more modular.
  • As we slowly move to the phase of testing outputted files based on input (see #17 and more importantly #32), we need to more systematically set up en clean up tests.
  • Tests should return a result (Result<(), Box<std::error::Error>>)

Potential solutions

The rust-lang-nursery provides some information about testing applications.

Additional Context

...

Lexer needs to be refactored

Current Issue

The lexer has become rather unwieldy and difficult to read.

Proposed Solution

Now that tests have been added for the lexer, it will be easier to refactor the lexer. Therefore, it would be a good idea to refactor the lexer soon to make it more readable. The tests will help to make it easier to check for any potential regression.

Add Documentation

Issue, or potential issue

There is little to no documentation at the moment.
As the project starts to grow we can ignore this issue no more.

Potential solutions

Start writing documentation.
As an added bonus, we can start making use of Rust's documentation testing feature.

Additional Context

...

Desugar default constructors to init functions

Current Issue

Default constructors are ignored.

Description of the feature

Default constructors are constructors stored alongside the class name, i.e.:

class MyServer(mut self: DisconnectedMyServer, def ip_address: IPv4Address) isa Server

The above should be desugared such that we have an init function where these are assigned to self. So the above is roughly desugared to (self should be ignored):

class MyServer(Server):
    ip_address = None

    __init__(self, ip_address):
        self.ip_address = ip_address

Description of potential solution

We desugar class nodes such that:

  • The class gets the constructor arguments as properties (save for self).
  • The class assign the constructor arguments to the properties of self.

The type checker should that that there is no other init function in the class.

How strict should we be about pure functions

Issue, or potential issue

Pure functions in Mamba are almost pure, but not quite.
Namely, while we disallow reading from global mutable variables, we do still allow writing to mutable variables outside the function, which is still a side-effect.

The question is, do we want to disallow this?
While this would make functions pure by definition, it would perhaps also decrease their usability.
My main concern is that while this feature would be nice, it is perhaps too cumbersome to use resulting in developers not using the feature at all.

I often see discussions about functional programming languages, and how they are safe, but that they are also often too restrictive, resulting in them not being used at all.
I think this discussion is somewhat similar to such discussions.
Do we wish to be pragmatic, and make functions "almost pure", or do we want to make them truly pure?

Transpile entire directories

Current Issue

Currently we only transpile one file at a time.

Description of the feature

When passed a directory, we transpile recursively every file in that directory.
This also requires that all this information be passed to the type checker before the desugaring stage so that the type checker can build a context with information regarding the entire directory.

Description of potential solution

Modify pipeline so:

  • We iterate over each file, tokenizing and parsing each
  • We pass the produced ast node array to the type checker. It will then:
    • Iterate over each, building a context
    • Iterate over each again, type checking them
  • We pass the array of ast nodes and the produced context to the desugar stage, which will produce an array of core nodes.
  • For each core node, produce a transpiled python file.

We likely want to store a transpiled python project alongside its mamba counterpart.
Furthermore, it is also desirable to preserve the directory structure of the original project.
Therefore, when outputting python files:

  • We store them in the same sub-directory relative to the new parent.

Convert imports to python imports

Description of Feature

Currently, imports in the language are not converted to python imports.
This should be implemented, as this is one of the key features needed to interface with python code.

Description of potential solution

Desugar imports similarly to other constructs.
Care should be taken when strings are used in imports.

Additional context

...

Functions with return type should return last expression

Current Issue

Unless we explicitly end a function with return, it does not return anything.

High-level description of the feature

In Mamba, a function that ends with an expression returns that type as long if the signature states as such.

Description of potential implementation

See #165

Allow empty interfaces

Description of Bug

At the moment we cannot have empty classes

How to Reproduce

Create an empty Interface:

type A

This raises a parse error, as we expect a body.

Note that in Mamba, the line between interfaces and type aliases is intentionally blurry.

Expected behaviour

An empty class is treated as a class with no body,

Add bitwise operators

Current Issue

There are no bitwise operators

Description of the feature

Add bitwise operators.

  • & Bitwise AND
  • | Bitwise OR
  • ^ Bitwise XOR
  • ~ Binary ones complement
  • << Binary left shift
  • >> Binary right shift

Description of potential solution

Add tokens for these operators, and modify the pipeline accordingly.
Since bitwise operators are a more advanced feature, I think that it's okay to let them be the same as in Python.
So, & in Python is & in Mamba, | in Python | in Mamba, etc.

None cannot be assigned to python tuples

Description of Bug

Currently, if we do not assign anything to an expression, we assign it None when it is converted to Python.
This is usually correct, except if said expression is a tuple.

How to Reproduce

Convert the following
``
You will get this:
(a, b) = None

Expected behavior

You should instead get this:
(a, b) = (None, None)

Additional context

...

Allow mutable identifiers within Tuples

Description of Bug

We cannot have mutable identifiers within tuples.

How to Reproduce

def (mut a, b) <- (10, 10)

Expected behaviour

The above is parsed, and the type checker correctly identifies a as mutable and b as immutable.

Implement Condition / Type refinement features

Current Issue

This is only parsed, we do not do anything with conditionals.

Description of the feature

Use conditionals in the type checker.
In the desugar stage, and if statements when these are encountered and errors where relevant.

Add generics to types

Current Issue

High-level description of the feature

Description of potential implementation

Test generated Python files

Current Issue

Currently, there is no framework for testing generated python files.

Description of the feature

Add functionality to the testing suite to test generated python files.
To begin with, it should test that these files compile, therefore ensuring that they are valid python source files.
In future, they should also test that the output coincides with what we would expect.

Description of potential solution

A utility functions which calls the python interpreter and asks it to interpret an outputted python file.
Perhaps it would be good to implement #17 first so the utility function could simply pass the path of the generated python file.
Ideally, we also want to interface with the python interpreter to output its errors and to also check the output from the generated scripts.

Alternatives considered

...

Additional context

...

Check that a statement correctly mentiones that it raises an exception

Current Issue

#148 and to a lesser extent #149

High-level description of the feature

In the language, any statement that may raise an exception must explicitly state so:
<statement or expression> raises [<Errors>]

Description of potential implementation

Check that said expression or statement indeed does raise a said exception by looking at the signature of said function.

We also may have to look at the signatures of built-in functions, i.e. division can also throw an arithmetic error: ZeroDivisionError.
It would however also need to apply type sensitive flow here, i.e. if we beyond a doubt know that the denominator cannot be 0 we know for sure that this error will not be raised.

Cannot have top-level tuples in scripts

Description of Bug

The Type checker throws an error when trying to build a Context.

How to Reproduce

Create file where we assign to a tuple.

Expected behavior

Tuple's should be ignored by the Context, as these cannot be accessed by an outsider.
These should only be visible in the Environment during the type inference stage.

Do we want total functions

Issue, or potential issue

We have pure functions, but we don't have a way of defining a total function, which is guaranteed to halt.
The question is, do we want total functions, or is their use-case too specific?

Potential solutions

Add total keyword. A total function is defined on all inputs and therefore halts on all inputs.
Therefore, a total function:

  • Never returns an error.
  • We can guarantee that it halts on all inputs by for instance:
    • checking that is is defined on every (discrete) possible input, for instance, an enum.
    • or somehow using induction to prove it always halts.

A function that is not total, is a "partial function".

consider the following examples:

def neg(x: Bool): Bool => not x

This returns the negation of its input, is both pure and total.

def f(x: Int): Int =>
    call_count <- call_count + 1 
    return 100 / x

Is neither pure nor total. It is not defined for the input 0, and it performs a side effect.
Though in the current implementation of Mamba, this is still seen as pure, as it adhere to the rules of the language for being pure (see #110 ).

def f(x: Int): Int => 
    call_count <- call_count + 1
    return x

Is total but not pure. It is defined on all inputs.

Code-wise I think having a total token would not hinder the readability of the language too much.
I.e. def pure total f (x: MyClass) => ... is still readable.

Always return value or exit function in handle arms

Current Issue

We sometimes return optionals in a handle statement, meaning we still have to match afterwards.
This makes them rather verbose, as we just move the problem (instead of dealing with errors we have to deal with an optional)

Description of the feature

Either always:

  • return a value in a match arm (it must be an expression)
  • exit the function

This means that if we assign to a value, it is no longer an optional, as we are guaranteed to either get a value or exit the function.

Description of potential solution

This must be checked in the type checker.

Automate conversion process from Mamba to Python

Description of the feature

Currently, there is no command line interface.
There should be a command line interface where:

  • We can specify what file or directory we want to transpile
  • Python source files should be generated
  • We should be able to specify where these should be stored (or have a default relative location)

Description of potential solution

Add a command line interface.
Wrap the different stages of the transpiler in a pipeline, and prepend this with a file reader, and
append it with a file writer.

Alternatives considered

...

Additional context

...

Use dot notation when accessing object fields or functions

Current Issue

Currently, we use postfix notation both for passing arguments to functions, and when accessing properties and functions of objects.
This, however, might make code less readable.

Description of the feature

Use dot notation for:

  • Accessing functions of objects
  • Accessing properties/fields of objects

We can still use postfix notation when passing arguments to functions, or when chaining function calls.

Description of potential solution

Just change the parsing rules.
As a bonus, this should make desugaring a lot easier.

Use unreachable macro instead of unwrap

Current issue

We use unwrap in multiple locations in the code which is discouraged in production code.

description of feature

Add extra assertion checks for debug, and use unimplemented! macro combined with 'unwrap_or_else'.

In situations where we are **absolutely ** sure we may use unreachable_checked.

Add functionality to TPIterator

Current issue

We often use macros in the parse stage , and pass the iterator, which is ugly.
TPIterator is not much more than an alias at the moment

possible solution

Instead, add two (or more perhaps) functions to TP:

  • eat to replace `check_next_is!'
  • 'parse' to replace get_or_err! and get_or_err_direct!. This should take a function name (if possible) and error message as argument.

Design wise this also makes more sense since the iterator is stateful.

Add desugaring of match statements (without pattern matching)

Current Issue

Match statements aren't desugared.

Description of the feature

Match statements should be desugared into something that looks like a switch.

Description of potential solution

match should be desugared into dictionary lookups, or something similar.

Additional context

We still don't have pattern matching.

Desugar interfaces

Current Issue

Interfaces, Denoted by type, are currently thrown away,

High-level description of the feature

Make these into stub classes.
Each function has pass as its body.

Description of potential implementation

In the desugar stage, don't throw away the type.
Construct a class, and use state so that we know that we're inside a state and that the body should contain a pass statement.

Add desugaring of handle statements

Current Issue

We cannot desugar handle statements.

Description of the feature

handle statements should be desugared to try except statements.

Description of potential solution

This should be done in the desugar stage.

Check types of built-in types

Current Issue

There is no type system

High-level description of the feature

A good place to start would be to build a type system which can check the types of built-in types of the language.
This can be built upon in further iterations.

Description of potential implementation

We can make a type system that checks the types of:

  • Bool
  • Int
  • Float
  • String
  • Any
  • Maybe
  • Set
  • List
  • Range
  • AnonymousFunction<List, Type>
  • Type
  • Maybe

A next step would be to do an analysis of classes so we can start analysing variables if they are user-defined classes (i.e. look at return types of functions).

Verify core output by verifying ast of Python output

Current Issue

We don't systematically test that outputted Python files, so we have no way of checking they are semantically similar.
In most cases, we simply test that Mamba files can be parsed at the moment.

High-level description of the feature

A simple way of doing this is by writing Python code and storing these in the test resources.
We can then compare these to the output of our pipeline.

Description of potential implementation

We can then use a python library, or something along those lines, to convert both the Python resource and the outputted Python files.
This makes the test cases less brittle and cumbersome to write than doing string comparisons.
We can then easily, visually and quickly verify that outputted Python code is what we expect it to be.

Cannot have statements followed by a class in a script

Description of Bug

The parser throws an error if we have a few statements followed by a class.

How to Reproduce

Write a script with a few statements followed by a class

Expected behaviour

We would expect the script to execute as usual
Namely, it should be possible to have a few statements followed by a class, i.e., write a script with some statements with for instance a helper class at the bottom.

Check if a function may raise a certain error

Current Issue

Use the context built using #148

High-level description of the feature

Do a simple check to check whether a function does indeed raise, and only raise, the errors mentioned in its signature.

Description of potential implementation

Check that the errors, and only the errors, are raised by statements:
<statement> raises [<Error>]

Match expressions can't have nested matches

Description of Bug

If we nest matches, the dedent after the nested is consumed, meaning that the expression of the next match arm comes immediately after (without a dedent in between), meaning that is is interpreted as a call (left is match, right is expression of next arm).

One fix to this was always following dedents by a newline.
However, this resulted in the issue where the parser had no way of detecting, for instance, an else in an if statement after a block, as this was hiding behind a newline, as opposed to immediately following a dedent.
Therefore, it might be time to reconsider the grammar and how if statements operate.

How to Reproduce

  1. Create match statement
  2. Create nested match
  3. Follow nested match by another arm

Expected behavior

The nested match should be its own expression.

Additional context

...

Add operator for calling unsafe Python code

Current Issue

We can circumvent much of the type system by simply calling unsafe Python code.

High-level description of the feature

Add an operator that signifies we're calling Python code.
Such code may, to left of the operator, be considered unsafe.
This means that we may get runtime errors caused by Python.

Everything right of the operator should be (type) safe "Mamba" code.
i.e.

# unsafe function presumably returns an integer
def a: Int! := unsafe_function(1, 4)!

# a now has type int!, meaning that we assume it has type int, but this cannot be guaranteed
print(a)

I would suggest using the ! token, similar to Kotlin, which also uses this token to signal calling unsafe Java code.

Description of potential implementation

Display source code in error messages

Current Issue

Error messages are verbose and not very helpful yet.

Description of the feature

When an error happens, we can use the location information to point to where in the source code the error occurred.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.