ALYX SHANG

THE NENE LANGUAGE SPECIFICATION

A detailed specification for the Nene Programming Language. - [back]

Reference implementation

The reference implementation in Zig v.0.14.0 can be viewed here: alyxshang/nene

Preamble

Nene is a semi-esoteric, statically-typed, functional, interpreted general-purpose programming language. Nene is short for "Nenetl", the Nahuatl word for "doll". Nene code is compiled to bytecode for the Wayob VM. The next sections shall go into detail about tokens, data types, behaviour, and syntax of Nene.

Tokens

The following is a complete list of all the token-types and their character patterns:

  • +: Adding two items.
  • ,: A list separator.
  • -: Subtracting two items.
  • ->: The case operator.
  • funky: Introduces a function.
  • yass: The boolean true keyword.
  • naww: The boolean false keyword.
  • :: The separator for a typed pair.
  • /: Dividing two items by each other.
  • inspo: The keyword to import an entity.
  • ?: Indicating that a value is optional.
  • load: Loads a symbol from an FFI-library.
  • vibe: Introducing a pattern-matching block.
  • %: Making the modulor for two items.
  • <: Carries out a lesser-than comparison.
  • >: Carries out a greater-than comparison.
  • fin: The keyword marking the end of a block.
  • close bracket: Closing a list or an expression.
  • =: Carries out an is-equal comparison.
  • asterisk: Multiplying two items with each other.
  • open bracket: Introducing a list of an expression.
  • !=: Carries out an is-not-equal comparison.
  • slay: The scope guard to mark something as public.
  • XX: An integer with a maximum length of 64 digits.
  • rehearsal: The keyword to introduce a testing block.
  • with: Supplies arguments to a symbol from an FFI-library.
  • bag: The keyword marking the introduction of a structure.
  • girlwait: The keyword to mark a function as asynchronous.
  • from: The keyword to specify where to import an entity from.
  • {: Opening the list of arguments for struct-initialization.
  • }: Closing the list of arguments for struct-initialization.
  • ˜˜: Introduces a comment. The two tildes must be followed by a space.
  • "...": A string with any type and number of characters between the quotation marks.
  • [A-Za-z_]+: An ident for a variable, function, structure or data type.
  • XX.XX: A floating-point number with a maximum of 32 digits before and after the decimal point, respectively.

Memory management and type-system rules

This section will talk about the following important topics: memory management, error-handling, and type-system rules. The list below explains each of these in depth.

  • Error-handling: Error-handling should be conducted via data structures in the standard library. Ideally, a structure should be defined that contains an ok and err field. Both of these fields should be pattern-matchable to extract the error as a value in case the err field is populated with data or the success value in case the ok field is populated with data.
  • Type-system rules: Nene is a statically-typed language, which means that the type of every piece of data has to be declared explictily and the user will get a syntax error if a piece of data's type is not declared. If type conversions are to be performed, this should be handled by a function or functions in the standard library. How this is done is left to implementers.
  • Memory management: Data used and declared inside a function is only valid for the scope of that function. Data cannot be used across function bounds, unless it is passed between functions as an argument. Data is owned by the current function and de-allocated once the function's scope ends. The same applies to asychronous functions.

Data types

Nene supports seven separate data types. Detecting the use of any of the type names of any of these data types should be done in the IR-generation phase of the interpreter. The data types are listed below:

  • Void: A "null" value that is only used when a function does not return anything The type name should be sleep.
  • Booleans: Booleans are stack-allocated values that can either be yass or naww. The type name should be mood.
  • Strings: Strings are heap-allocated character sequences surrounded by double-quotes. The type name should be tea.
  • Integers: Integers are stack-allocted base-10 integers with a maximum length of 64 digits. The type name should be cupcake.
  • Structures: Structures are a heap-allocated list of any of the other data types or of themselves.
  • Floats: Floating-point numbers are stack-allocated base-10 decimal numbers with a maximum length of 64 digits. The type name should be cash.
  • Optional types: An argument or structure field being optional should be marked with a ? operator in front of the type name of a structure field's type or function argument's type.

Syntax

Nene syntax is deliberately kept simple and borrows some its whitespace philosophy from Ruby and Python. In those two languages whitespace is important to separate tokens. Nene ignores whitespace completely.

Block statements

Nene syntax supports five different types of block statements: comments, functions, structures, import statements, and testing-block definitions. These and scope guards are covered in more detail in the following sections.

Comments

Comments in Nene syntax start with a two "˜" characters and end with a newline character.

Scope guards

The only block statements that scope guards can be added to are functions and structures. By default, names for both are only valid within the current scope. If the "slay" keyword is added to either before their introductory keywords, respectively, each of them become public and their names become valid entities outside the current module's scope.

Import statements

Import statements can only be used to import specific block entities such as functions or structures from a module into another module. Import statements are introduced by the "inspo" keyword, followed by a string, followed by the "from" keyword, followed by another string. The first string is the name of the function or structure being imported and the second string is the name of the file or namespace the function or structure is being imported from.

Structure definition statements

Structures definitions are a series of key-value pairings of a field name and the field's corresponding type. A field definition might look something like this: name: tea. Structure definitions start with the "bag" keyword, followed by the structure's name. After this initial naming of the structure, follow the field definitions. The structure definition is terminated with the "fin" keyword.

Function definition statements

In a functional programming language, there are no imperative statements that explicitly assign a variable or explicitly return a value from a function. Every function is an expression that defines a rule for how a piece of data is to be handled. In Nene, a function is also an expression and there exists no "return" keyword. The value the last statements inside a function evaluates to is returned. Functions are statically-typed and are introduced with the "funky" keyword, followed by the function's name, opening brackets, the function's arguments and their types, followed by a closing bracket, and finally, the return type of the function. Function definitions are closed by the "fin" keyword. If a function is to be asynchronous, the girlwait keyword must preceed the funky keyword and if the function is public, come after the slay keyword. If the asynchronous function is called the girlwait keyword should preceed the name of the function. Inside each function inline statements are written. These shall be elaborated on in the following section.

Testing-block definition statements

In Nene, the syntax for writing unit-tests should be included in the language. Testing blocks in Nene are special functions that must return a boolean true, or in Nene's case a yass. If the testing block returns anything else, the test is marked as having failed. Testing blocks are introduced with the rehearsal keyword, followed by a short string containing a description of the test being run. The test block is terminated with the fin keyword.

Inline Statements

Nene syntax only allows six different types of inline statements. These inline statements must be used within the body of a function. The types of statements are as follows: pattern-matching statements, boolean comparisons, arithmetic operations, comments, and extern calls.

Pattern-matching statements

In functional programming languages there exist no conditional operators like "if", "else", etc. Instead, a certain pattern is matched against patterns. In Nene, any pattern-matching used must return a value and must exhaustively match against all possible patterns. A pattern-matching statement is introduced via the "vibe" keyword, followed by an expression enclosed in brackets, that produces a value. After this, there follows a list of cases, not separated by a comma. On the left-hand side of a case statement, there is one of the possible values, followed by the "->" operator, followed by an expression that produces a value on the right-hand side that is to be returned from the pattern-matching block.

Arithmetic operations

There are five arithmetic operations in Nene: Addition, Subtraction, Multiplication, Division, and calculating the modulo. Addition is carried out with the "+" sign, subtraction is carried out with the "-", division is carried out with the "&slash;" sign, and a modulo operation is carried out with the "%" sign. On either side of these signs must be values. Arithmetic operations in Nene are evaluated from left to right with multiplication and division having a higher binding power than addition and subtraction.

Logical comparisons

In Nene syntax there are four logical operations: a less-than comparison, a greater-than comparison, a is-equal comparison, and a is-not-equal-comparison. A less-than comparison is carried out with the "<" sign, a greater-than comparison is carried out with the ">" sign, an is-equal operation is carried out with the "=" sign, and an is-not-equal comparison is carried out with the "!=" sign.

Struct-initialization statements

Struct initaliazation creates a new instance of a structure as a value. Because a structure is a heap-allocated list of the data inside it, some of which may be heap-allocated too, it is advised to only do this when neccessary. Structures are instantiated by writing the structure's name, an opening curly bracket, writing the values each corresponding to each of the structure's field as a comma-separated list with the last comma only being present only before the last value, followed by a closing curly bracket.

Function calls

Function calls in Nene are sometimes neccessary for creating loops. There are no keywords or patterns one can use to create loops explicitly. Since Nene is a functional language, some functions are written to be recursive, calling themselves. Function calls in Nene are written by writing the function's name, an open bracket, the list of values corresponding to the function's expected arguments and their types, and by a closing bracket. The last comma in the argument list should be before the last value corresponding to an argument.

Extern calls

When handling FFI functionality or working with third-party libraries, it is sometimes desirable to call functions from external non-Nene libraries. Nene has builtin FFI support that is passed to the Wayob VM for interpretation and execution. FFI calls have the following syntax. The call is introduced via the "load" keyword, followed by a string representing the name of the symbol to be loaded. This is then followed by the "from" keyword and a string representing the library's name the symbol should be loaded from. To give the external function some data to work with, this expression is then followed by the "with" keyword, an open bracket, a comma-separated list of values representing the arguments to the external function and corresponding to the argument's different types, and a closing bracket. The last comma in the argument list should be before the last value corresponding to an argument.