aboutsummaryrefslogtreecommitdiff
path: root/docs/language_manual/identifiers.md
blob: 2667c383942c0f02571581463c6295ce699ead8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

Identifiers
===========
Identifiers consist of one or more subidentifiers. The simplest case is where
there's only one subidentifier, like this:

    thing

An identifier may also consists of several subidentifiers, where first
subidentifier specifies the namespace, and the following specify nested
namespaces. The last subidentifier is the actual type, function or data
definition being referenced. For example:

    somenamespace:subnamespace:thing

Identifiers are case sensitive and may use the English letters A-Z (both
upper and lower case), the digits 0-9 and the _ and - symbols. However,
an identifier may not start with a digit. A future version of LRL might
also allow UTF-8 characters, but currently there's no standard way of encoding
these so it will cause errors in the backend.

Identifier lookup
-----------------
Identifiers are first searched for in the current scope, and then in
the scope which that scope is contained in, and so on. For example:

    int x = 1;
    int y = 2;
    
    int f() {
        int y = 3;
        {
            int y = 4;
            return x + y;  // returns 1+4 = 5
        }
    }

An identifier may reference a particular namespace. In this case, the first
subidentifier of the namespace will be looked up according to the rules above.
Then subidentifiers after it will be searched for in the referenced 
namespaces. For example:


    int a = 1;
    namespace b {
        int d = 2;
        namespace e {
            int f = 3;
            
            int x = d; // 2
        }
    }
    
    namespace c {
        int x = a;     // 1
        int y = b:d;   // 2
        int z = b:e:f; // 3
        
    }

Identifier definitions
----------------------
Usually a definition of an identifier consists of just a single subidentifier.
But it is also possible to define an identifier inside a namespace, or define
the current namespace's identifier. The following example illustrates this
with three type definitions:

    typedef thing = int;               // Usual identifier definition.
    typedef somenamespace:thing = int; // Define in "somenamespace".
    typedef here = int;                // Define this namespace's identifier
                                       // to be a type of int.

Note that the namespace "somenamespace" in the second line must be defined
somewhere also.

**TODO:** Actually require the namespace to be created explicitly, e.g. with "namespace numbers {}"

The last "here" definition is useful when defining a type and functions to
use it at the same time. For example:

    namespace Point {
        typedef here = (int x, int y);
        
        Point make_point(int x, int y);
        int distance(Point a, Point b);
    }

This defines a type called "Point" with two functions in it's namespace.
It's allowed for an identifier to be both a namespace and **either**
a type, a function or a data definition, like in the example. However, an
identifier can only have one definition which is not a namespace.