summaryrefslogtreecommitdiffhomepage
path: root/neat.html.markdown
blob: f02461eeef066adecd57d6acb4d826f6d30b899e (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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
---
language: neat
contributors:
    - ["Feep", "https://github.com/FeepingCreature"]
filename: LearnNeat.nt
---

Neat is basically a smaller version of D1 with some experimental syntax and a focus on terseness without losing the basic C-like syntax.

[Read more here.](https://github.com/FeepingCreature/fcc/wiki)

```c
// single line comments start with //
/*
  multiline comments look like this
*/
/+
  or this
  /+ these can be nested too, same as D +/
+/

// Module name. This has to match the filename/directory.
module LearnNeat;

// Make names from another module visible in this one.
import std.file;
// You can import multiple things at once.
import std.math, std.util;
// You can even group up imports!
import std.(process, socket);

// Global functions!
void foo() { }

// Main function, same as in C.
// string[] == "array of strings".
// "string" is just an alias for char[],
void main(string[] args) {
  // Call functions with "function expression".
  writeln "Hello World";
  // You can do it like in C too... if you really want.
  writeln ("Hello World");
  // Declare a variable with "type identifier"
  string arg = ("Hello World");
  writeln arg;
  // (expression, expression) forms a tuple.
  // There are no one-value tuples though.
  // So you can always use () in the mathematical sense.
  // (string) arg; <- is an error

  /*
    byte: 8 bit signed integer
      char: 8 bit UTF-8 byte component.
    short: 16 bit signed integer
    int: 32 bit signed integer
    long: 64 bit signed integer

    float: 32 bit floating point
    double: 64 bit floating point
    real: biggest native size floating point (80 bit on x86).

    bool: true or false
  */
  int a = 5;
  bool b = true;
  // as in C, && and || are short-circuit evaluating.
  b = b && false;
  assert(b == false);
  // "" are "format strings". So $variable will be substituted at runtime
  // with a formatted version of the variable.
  writeln "$a";
  // This will just print $a.
  writeln `$a`;
  // you can format expressions with $()
  writeln "$(2+2)";
  // Note: there is no special syntax for characters.
  char c = "a";
  // Cast values by using type: expression.
  // There are three kinds of casts:
  // casts that just specify conversions that would be happening automatically
  // (implicit casts)
  float f = float:5;
  float f2 = 5; // would also work
  // casts that require throwing away information or complicated computation -
  // those must always be done explicitly
  // (conversion casts)
  int i = int:f;
  // int i = f; // would not work!
  // and, as a last attempt, casts that just reinterpret the raw data.
  // Those only work if the types have the same size.
  string s = "Hello World";
  // Arrays are (length, pointer) pairs.
  // This is a tuple type. Tuple types are (type, type, type).
  // The type of a tuple expression is a tuple type. (duh)
  (int, char*) array = (int, char*): s;
  // You can index arrays and tuples using the expression[index] syntax.
  writeln "pointer is $(array[1]) and length is $(array[0])";
  // You can slice them using the expression[from .. to] syntax.
  // Slicing an array makes another array.
  writeln "$(s[0..5]) World";
  // Alias name = expression gives the expression a name.
  // As opposed to a variable, aliases do not have an address
  // and can not be assigned to. (Unless the expression is assignable)
  alias range = 0 .. 5;
  writeln "$(s[range]) World";
  // You can iterate over ranges.
  for int i <- range {
    write "$(s[i])";
  }
  writeln " World";
  // Note that if "range" had been a variable, it would be 'empty' now!
  // Range variables can only be iterated once.
  // The syntax for iteration is "expression <- iterable".
  // Lots of things are iterable.
  for char c <- "Hello" { write "$c"; }
  writeln " World";
  // For loops are "for test statement";
  alias test = char d <- "Hello";
  for test write "$d";
  writeln " World\t\x05"; // note: escapes work
  // Pointers: function the same as in C, btw. The usual.
  // Do note: the pointer star sticks with the TYPE, not the VARIABLE!
  string* p;
  assert(p == null); // default initializer
  p = &s;
  writeln "$(*p)";
  // Math operators are (almost) standard.
  int x = 2 + 3 * 4 << 5;
  // Note: XOR is "xor". ^ is reserved for exponentiation (once I implement that).
  int y = 3 xor 5;
  int z = 5;
  assert(z++ == 5);
  assert(++z == 7);
  writeln "x $x y $y z $z";
  // As in D, ~ concatenates.
  string hewo = "Hello " ~ "World";
  // == tests for equality, "is" tests for identity.
  assert  (hewo == s);
  assert !(hewo is s);
  // same as
  assert  (hewo !is s);

  // Allocate arrays using "new array length"
  int[] integers = new int[] 10;
  assert(integers.length == 10);
  assert(integers[0] == 0); // zero is default initializer
  integers = integers ~ 5; // This allocates a new array!
  assert(integers.length == 11);

  // This is an appender array.
  // Instead of (length, pointer), it tracks (capacity, length, pointer).
  // When you append to it, it will use the free capacity if it can.
  // If it runs out of space, it reallocates - but it will free the old array automatically.
  // This makes it convenient for building arrays.
  int[auto~] appender;
  appender ~= 2;
  appender ~= 3;
  appender.free(); // same as {mem.free(appender.ptr); appender = null;}

  // Scope variables are automatically freed at the end of the current scope.
  scope int[auto~] someOtherAppender;
  // This is the same as:
  int[auto~] someOtherAppender2;
  onExit { someOtherAppender2.free; }

  // You can do a C for loop too
  // - but why would you want to?
  for (int i = 0; i < 5; ++i) { }
  // Otherwise, for and while are the same.
  while int i <- 0..4 {
    assert(i == 0);
    break; // continue works too
  } then assert(false); // if we hadn't break'd, this would run at the end
  // This is the height of loopdom - the produce-test-consume loop.
  do {
    int i = 5;
  } while (i == 5) {
    assert(i == 5);
    break; // otherwise we'd go back up to do {
  }

  // This is a nested function.
  // Nested functions can access the surrounding function.
  string returnS() { return s; }
  writeln returnS();

  // Take the address of a function using &
  // The type of a global function is ReturnType function(ParameterTypeTuple).
  void function() foop = &foo;

  // Similarly, the type of a nested function is ReturnType delegate(ParameterTypeTuple).
  string delegate() returnSp = &returnS;
  writeln returnSp();
  // Class member functions and struct member functions also fit into delegate variables.
  // In general, delegates are functions that carry an additional context pointer.
  // ("fat pointers" in C)

  // Allocate a "snapshot" with "new delegate".
  // Snapshots are not closures! I used to call them closures too,
  // but then my Haskell-using friends yelled at me so I had to stop.
  // The difference is that snapshots "capture" their surrounding context
  // when "new" is used.
  // This allows things like this
  int delegate(int) add(int a) {
    int add_a(int b) { return a + b; }
    // This does not work - the context of add_a becomes invalid
    // when add returns.
    // return &add_a;
    // Instead:
    return new &add_a;
  }
  int delegate(int) dg = add 2;
  assert (dg(3) == 5);
  // or
  assert (((add 2) 3) == 5);
  // or
  assert (add 2 3 == 5);
  // add can also be written as
  int delegate(int) add2(int a) {
    // this is an implicit, nameless nested function.
    return new λ(int b) { return a + b; }
  }
  // or even
  auto add3(int a) { return new λ(int b) -> a + b; }
  // hahahaaa
  auto add4 = λ(int a) -> new λ(int b) -> a + b;
  assert(add4 2 3 == 5);
  // If your keyboard doesn't have a λ (you poor sod)
  // you can use \ too.
  auto add5 = \(int a) -> new \(int b) -> a + b;
  // Note!
  auto nestfun = λ() { } // There is NO semicolon needed here!
  // "}" can always substitute for "};".
  // This provides syntactic consistency with built-in statements.


  // This is a class.
  // Note: almost all elements of Neat can be used on the module level
  //       or just as well inside a function.
  class C {
    int a;
    void writeA() { writeln "$a"; }
    // It's a nested class - it exists in the context of main().
    // so if you leave main(), any instances of C become invalid.
    void writeS() { writeln "$s"; }
  }
  C cc = new C;
  // cc is a *reference* to C. Classes are always references.
  cc.a = 5; // Always used for property access.
  auto ccp = &cc;
  (*ccp).a = 6;
  // or just
  ccp.a = 7;
  cc.writeA();
  cc.writeS(); // to prove I'm not making things up
  // Interfaces work same as in D, basically. Or Java.
  interface E { void doE(); }
  // Inheritance works same as in D, basically. Or Java.
  class D : C, E {
    override void writeA() { writeln "hahahahaha no"; }
    override void doE() { writeln "eeeee"; }
    // all classes inherit from Object. (toString is defined in Object)
    override string toString() { return "I am a D"; }
  }
  C cd = new D;
  // all methods are always virtual.
  cd.writeA();
  E e = E:cd; // dynamic class cast!
  e.doE();
  writeln "$e"; // all interfaces convert to Object implicitly.

  // Templates!
  // Templates are parameterized namespaces, taking a type as a parameter.
  template Templ(T) {
    alias hi = 5, hii = 8;
    // Templates always have to include something with the same name as the template
    // - this will become the template's _value_.
    // Static ifs are evaluated statically, at compile-time.
    // Because of this, the test has to be a constant expression,
    // or something that can be optimized to a constant.
    static if (types-equal (T, int)) {
      alias Templ = hi;
    } else {
      alias Templ = hii;
    }
  }
  assert(Templ!int == 5);
  assert(Templ!float == 8);
}
```

## Topics Not Covered

 * Extended iterator types and expressions
 * Standard library
 * Conditions (error handling)
 * Macros