summaryrefslogtreecommitdiff
path: root/doc/lang.txt
blob: 9b4ab86c3f572dd6e2b2ada12ba84e23285d629d (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
                    The Myrddin Programming Language
                              Jun 2012
                            Ori Bernstein

Overview:

        Myrddin is designed to be a simple, low level programming
        language.  It is designed to provide the programmer with
        predictable behavior and a transparent compilation model,
        while at the same time providing the benefits of strong
        type checking, generics, type inference, and similar.
        Myrddin is not a language designed to explore the forefront
        of type theory, or compiler technology. It is not a language
        that is focused on guaranteeing perfect safety. It's focus
        is on being a practical, small, fairly well defined, and
        easy to understand language for work that needs to be close
        to the hardware.

Introduction:

    We begin with the archetypical "Hello world" example, deconstructing
    it as we go:

        use std

        const main = {
            /* say hello */
            std.write(1, "Hello World\n")
        }

    The first line, `use std`, tells the compiler to import the standard
    library, which at the time of this writing only barely exists as a
    copy-paste group of files that works only on Linux, implementing almost
    no useful functions.  One of the functions that it does provide,
    however, is the 'write' system call.

    The next line, 'const main = ...' declares a constant value called
    'main'. These constant values must be initialized at their declaration
    to a literal value. In this case, it is intialized to a constant
    function '{;std.write(1, "Hello World\n");}'

    In Myrddin, all functions begin with a '{', followed by a list
    of arguments, which is terminated by a newline (or semicolon. The
    two are equivalent). This is followed by any number of statements,
    and closed by a '}'.

    The text '/* say hello */' is a comment. It is ignored by the compiler,
    and is used to add useful information for the programmer. In Myrddin,
    unlike many popular languages, comments nest. This makes code like
    /* outer /* inner coment */ comment */ valid.

    The text 'std.write' refers the 'write' function from the 'std' library.
    In Myrddin, a name can belong to an imported namespace. The language,
    for reasons of parsimony, only allows one level of namespace. I saw
    Java package names and ran screaming in horror, possibly too far to
    the other extreme. This function is statically typed, taking a single
    integer argument, and a byte slice to write.

    The text '(1, "Hello World)' is the function call itself. It takes
    the literal "1", and the byte slice "Hello World\n", and calls the
    function 'std.write' with them as arguments.

    It would be useful now to specify that the value '1' is an integer-like
    constant, but it is not an integer. It is polymorphic, and can be used
    at any point where a value of any integer type is needed.

Declarations:

    In Myrddin, declarations take the following form:

        var|const|generic name [: type] [= expr]

    To give a few examples:

        var x
        var foo : int
        const c = 123
        const pi : float32 = 3.1415
        generic id : (@a -> @a) = {a:@a -> @a; -> a}

    The first example, 'var x', declares a variable named x. The type is not
    set explicitly, but it will be determined by the compiler (or the code
    will fail to compile, saying that the type of the variable could not
    be determined).

    The second example, 'var foo : int' explicitly sets the type of a
    variable named 'foo' to an integer. It does not initialize it. However,
    it is [FIXME: make this not a lie] a compilation error to use a
    variable without prior intialization, so this is not dangerous.

    The third example, 'cosnt c = 123' declares a constant named c,
    and initializes it to the value 123. All constants require initializers,
    as they cannot be assigned to later in the code.

    The fourth example, 'const pi : float32 = 3.1415', shows the full form
    of declarations. It includes both the type and initializer components.

    The final "overdeclared" example declares a generic function called
    'id', which takes any type '@a' and returns the same type. It is
    initialized to a function which specifies these types again, and
    has a body that returns it's argument. This is not idiomatic code,
    and is only provided as an example of what is possible. The normal
    declaration would look something like this:

        generic id = {a:@a; -> a}

Control Structures:

Types:

    Myrddin comes with a large number of built in types. These are
    listed below:

        void
            The void type. This type represents an empty value.
            For reasons of consistency when specializing generics, void
            values can be created, assigned to, and manipulated like
            any other value.

        bool
            A Boolean type. The value of this is either 'true' (equivalent
            to any non-zero) or 'false', equivalent to a zero value. The
            size of this type is undefined.

        char
            A value representing a single code point in the default
            encoding. The encoding is undefined, and the value of the
            character is opaque.


        int8 int16 int32 int64 int
        uint8 uint16 uint32 uint64 uint
            Integer types. For the above types, the number at the end
            represents the size of the type. The ones without a number at
            the end are of undefined type. These values can be assumed to
            be in two's complement. The semantics of overflowing are yet to
            be specified.

        float32 float64
            Floating-point types. The exact semantics are yet to be
            defined.

        @<name>
            A generic type. This is only allowed in the scope of 'generic'
            constants.

    It also allows composite types to be defined. These are listed below:

        <type>*

            A pointer to a type This type does not support C-style pointer
            arithmetic, indexing, or any other such manipulation. However,
            slices of it can be taken, which subsumes the majority of uses
            for pointer arithmetic. The pointer is passed by value, but as
            expected, the pointed to value is not.

        <type>[,]

            A slice of a type. Slices point to a number of objects. They
            can be indexed, sliced, and assigned. They carry their range,
            and can in principle be bounds-checked (although the compiler
            currently does not do this, due to the lack of a runtime library
            that will allow a 'panic' function to be called).

        <type>[size]

            An array of <type>. Unlike most languages other than Pascal, the
            size of the array is a part of it's type, and arrays of
            different sizes may not be assigned between each other. Arrays
            are passed by value, and copied when assigned.

        <type0>,<type1>,...,<typeN>

            A tuple of type t0, t1, t2, ....

    Finally, there are aggregate types that can be defined:

        struct

        union

    Any of these types can be given a name. This naming defines a new
    type which inherits all the constraints of the previous type, but
    does not unify with it. Eg:

        type t = int
        var x : t
        var y : int
        x = y  // type error
        x = 42 // sure, why not?

Type Constraints


Literals:

    character
    bool
    int
    float
    func
    sequence

Symbols

Imports

Exports