Skip to content
This repository has been archived by the owner on Mar 10, 2022. It is now read-only.

Syntax Rewrite #7

Open
bristermitten opened this issue Oct 6, 2020 · 0 comments
Open

Syntax Rewrite #7

bristermitten opened this issue Oct 6, 2020 · 0 comments

Comments

@bristermitten
Copy link
Member

bristermitten commented Oct 6, 2020

This issue details the updated Elara language specification. Some features such as extension functions have been reworked with practical, JVM-based use in mind.

Syntax Descriptions

Syntax is described in a predictable format, where [name] represents an optional value, <name> represents a required value.

+ means that the "expression" can be repeated 1 or more times.
Any other text should be interpreted literally.

The "types" are explained under the syntax, in terms of their AST elements.

Basic Syntax

Identifiers:

Almost every Unicode character is a valid identifer, similar to Julia. However there are some exceptions:

., ,, :, {, }, [, ], (, ), , //, /*, */, and any other reserved symbol / keyword.

Comments

Single Line comments are declared with // at the start of the comment - any following text until a new line will be ignored.
Multiline comments begin with /* and end with */ - all characters in between these symbols are ignored.

Statements

Variables

All variable types are declared with the same syntax:

let [mut] <name> = <value>
name: Identifier
value: Expression

Variable reassignment is only supported for mutable (mut) variables, and is done with

<name> = <value>
name: Identifier
value: Expression

Expressions

Literals

Integer literals are supported in base 10. Base 2 / 16 may be added in the future.

Floating point literals are also supported in base 10.
If a literal does not have a decimal point, it is considered to be an integer.

Function Literals

There are 2 types of function literals, Explicit and Implicit

Explicit literals are declared as follows:

([param-type] <name>,)+ => [return-type] {
    [body]
}
param-type: Type
name: Identifier
return-type: Type
body: Block

This represents an "explicit" function, with a clear transformation between an input and output.
For example:

(Int a, Int b) => Int {
    2 * (a + b)
}

Implicit literals are more concise, and are only supported in an anonymous context:

{
[body]
}
body: Block

Parameters of the function are implicitly declared based on context. For example, if the function was passed to a function taking a (Int) => Int, 1 parameter, named a would be declared with the type Int in the anonymous function's scope.
Multiple parameters follow this naming scheme: a, b, c, etc...

Collection Literals

These may be removed, just an idea

Literals for the 2 basic data structures are also supported:

List Literals:

[<value>+,]
value: Expression

These will probably be implemented as linked lists or arrays, and should be immutable.

Map Literals:

{
    <key>: <value>,+
}
key: Expression
value: Expression

Simple hash map / table implementation, again immutable.

TODO:
Function calling, binary operators, etc

Structs, Types and Extensions

Structs

Structs are data-only structures that simply represent a named composite of types.
Why data-only? In implementations, they could be implemented similar to inline / value types, and it also suits the functional paradigm.

However, since Elara does not distinguish functions and other variables, a struct can accept a function as one of its values.
The important distinction is that this is not a method, more of an abstract method.

Struct syntax:

struct <name> {
    ([mut|restricted] [type] <property-name> [= value])+
}
name: Identifer
type: Type
property-name: Identifier
value: Expression

For example:

struct Person {
    String name
    mut Int age
}

Types

Alias types are simple remappings of an alias to an actual type.

type <name> = <type>
name: Identifier
type: Type

For example:

type IntList = List<Int>

Sum types are types that combine the contract of 2 types to create a new type that fulfills both.

type <name> = (<type> & <type>)+
name: Identifier
type: Type

For example:

type KillableEntity = Entity & Killable

This will accept any type that is assignable to both Entity and Killable

Composite types (name may change) are types that represent 2 or more possible values of different types

type <name> = (<type> | <type>)+
name: Identifier
type: Type

For example:

type Result = Some | None

If there are only 2 possibilities, the first type is truthy and the second is falsy.

Interfaces / Contract Types

These types define a specific contract of the receiving type.

type <name> = {
    <contract>+
}
name: Identifier
type: Type
contract: Contract

Contract can be 1 of 2 possibilities:

Struct property contract:

<type> <identifier>
type: Type
identifier: Identifer

Function contract:

<identifier>([type]+) => [type]
type: Type
identifier: Identifer

A generic type T can also be used that represents any type.

Struct property contracts assert that a property with a matching name and type is present on the type.
For example:

type Named = {
    String name
}

asserts that a property of type String and named name is present on the type.

Extension function contracts assert that a function with the given contract is present in the type's module.

For example:

type Addable = {
    add(T) => T
}

will accept any type whose module defines an extension function with the name add and signature (T) => T

Extensions

Structs can be "extended" to add functionality and create "substructs"

extend <struct> {
    [body]+
}
struct: Identifier
body: VariableDeclaration | StructDeclaration

For example:

struct Person {
    ...
}

extend Person { 
    let first-name = name substring-until " "

    let say-hello = () => { 
        print(name + " says Hello!")
    }

    struct Student {
        Map<String, String> grades
    }
}  

Declaring a variable in an extension block effectively makes the variable part of the struct, with the small difference that it will not be included in the constructor.
The true semantics of this feature will depend on backend

In the above example, both first-name and say-hello can be used as if they are part of the struct.

The struct defined in the extend block is not a "subclass". Instead, a new struct is defined, with every property of Person copied. Because Elara's type system is contract based, this makes it effectively a subclass.
Again, how these are actually handled depends on the backend. They may compile to subclasses for JVM interop.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant