summaryrefslogtreecommitdiff
path: root/spec/1 - language.md
blob: 345eb1a38f2bd7c58d3371ee5849ced55225bf80 (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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# The Language

## Section 1 - Files

### Folder Structure

TNSL project structure has a root source folder with TNSL files contained within the folder and sub-folders.  There is no strictly enforced system, but the standard organization is to place code sub-modules in sub-folders, and name the module entry point file the same name as its enclosing folder.

The main file to compile is known as the root file, which generally resides in the root source folder.  This file will contain either a main method, or the pre-processor statement `rootfile` to denote the root of a library.  A main file may contain both.

### TNSL Files

TNSL files contain the .tnsl extension and contain one or more of the following:
- Comments
- Pre-processor statements
- Modules
- Named function blocks
- Method and interface blocks
- Constant, variable, and type definitions

TNSL files may only contain the following enclosed in function blocks:
- Re-assignment of variables
- Control flow blocks
- Use of variables in definition of variables
- Function calls

## Section 2 - Blocks

### TNSL Block Notation

Blocks in tnsl consist of a slash and a character denoting the type of block as opening, and the inverse of these symbols as closing.  The three types of blocks are comment, pre-processor, and code.  Code blocks can be further broken down into modules, functions, methods, and control blocks.

	Examples of standard block opening and closing characters:

	/# - open comment
	#/ - close comment

	/: - open pre-processor
	:/ - close pre-processor

	/; - open code
	;/ - close code

In addition to the standard opening and closing characters, there exist "swivel" character sets to quickly close and re-open a block type

	;; - close code, then open code
	#; - close comment, open code
	;# - close code, open comment
	:: - close pre-processor, open preprocessor
	#: - close comment, open pre-processor
	:# - close pre-processor, open comment

Code blocks may have inputs and/or outputs.  Inputs are enclosed by `()` and outputs are enclosed by `[]`

Usage examples:

	# This is a line comment

	/#
		This is a block comment, the next block is a module named "my_module",
		it contains one function named "my_function" with no inputs or
		outputs.
	#/

	/; module my_module
		
		/##
			This is a documentation comment, notice the two #s at the
			beginning of the block instead of just one.

		#; my_function
		
		;/
	;/

### Modules

Modules are to TNSL what namespaces are to c++, a way to contain a group of related functions, types, methods, and other namespaces such that they won't interfere with outside code.  Modules may only be accessed (by other programs) if they are exported using the `export` keyword when defining the module.  Modules contained within the module (Sub-modules) are not automatically exported, and must also use the keyword if they wish to be accessible by other programs.  Unexported modules may still be used within the project from which they originate.

### Module definition example:

*File a.tnsl (project a)*

	/; export module my_module
		/; module my_hidden_module
			# Can access all from my_module, and my_hidden_module
		;/
		# Can access all from my_module, and my_hidden_module
	;/
	# Can access all from my_module, and my_hidden_module

*File aa.tnsl (project a)*

	/; my_function_a
		# Can access all from my_module, and my_hidden_module
	;/
	# Can access all from my_module, and my_hidden_module

*File b.tnsl (project b)*

	/; my_function
		# Can access all from my_module, but not from my_hidden_module
	;/
	# Can access all from my_module, but not from my_hidden_module

### Functions

TNSL functions are code blocks whose definition contains none of the following: control flow keywords, the module keyword, the method keyword.  TNSL functions are called methods if they are contained within a method block.  TNSL methods may only be called with relation to the user defined type they are linked to.  If a TNSL function has no user defined name, it is anonymous.  Anonymous functions can be stored as void type variables or called immediately.  If an anonymous function is not stored, it is interpreted as inline and called immediately (this is known as a scope block).

TNSL functions may have inputs (enclosed with `()`) and/or outputs (enclosed with `[]`).  Inputs must be named; naming outputs is optional.

TNSL functions may have their call stack modified by the `raw` and/or `inline` keywords.  If the `inline` keyword is placed around the function declaration, the function will still be exported (if it is being exported), but any time it is used in the project's code, it will be optimized as if in-line.

The use of the `raw` keyword has several effects:  the function will have no generated assembly preamble, the function will allow `raw return` statements, the function will not be optimized, and the function will allow `asm` statements.  Any function may be labeled `raw`, even `main` and anonymous functions.

Examples:

	# simple function with no inputs or outputs named "my_function"
	/; my_function
		<statements>
	;/

	# function with inputs and outputs
	/; my_second_function ( <type> input1, <type (optional)> input2 ) [ <type 1>, <type 2>, ... , <type n> ]
		<statements>
	;/

	# funtion calling an anonymous function
	/; my_third_function
		# an anonymous function (scope block)
		/;
			<statements>
		;/
	;/

### Control Flow Blocks

Control flow blocks are code blocks whose definitions contain the keywords if, else, loop, match, case, or default.

For if, else, loop, and match any inputs and/or outputs are a semicolon-separated list of statements.  For case or default, only inputs are accepted in the form of a single value.  Any variables defined in these inputs or outputs are scoped to the block only.  Control flow blocks may not actually output any values; instead, any statements in the output are evaluated when the block ends, weather it loops or not.

Examples:

	# simple if block
	/; if ( <statement resolving in boolean value> )
		<statements>
	;/

	# if block with else and else if
	/; if ( <statement (optional)> ; <statement (optional)> ; ... ; <statement resolving in boolean value> )
		<statements>
	;; else if ( <statement resolving in boolean value> )
		<statements>
	;; else
		<statements>
	;/

	# loop block
	/; loop ( <statement (optional)> ; ... ; <statement resolving in boolean value (optional)> )
		[ <statements to be evaluated on loop (optional)> ]

		<statements>
	;/

	# match block
	/; match ( <statement (optional)> ; ... ; <input value> )

		/; case <match value>
			<statements>
		;; case <match value>
			<statements>
			# Continue here would fall through to default
		;; default
			<statements>
		;/
	;/

## Section 3 - Statements

### TNSL Statement Notation

There are three types of tnsl statements: code, pre-processor, and comment.  Code statements begin with `;` and end at the next statement.  Pre-processor statements begin with `:` and end at the next statement.  Comment statements (line comments) begin with `#` and end at the next new line.  After a line comment ends, the previous statement resumes.

### Variable Declaration

Declaring a variable is done by referencing a type and then giving a list of names for the new variables.  Optionally, a list of values may be given to initialize the new variables.

Variables may be augmented by the following keywords: `const`, `volatile`, and/or `static`.

Declaring a variable as `const` means that it is a constant and must be immediately initialized.  A constant may not be re-assigned a value.

Declaring a variable as `volatile` means that the compiler will not attempt to optimize operations performed on it.

Declaring a variable `static` means that the value will be kept between function calls.  Static variables may create race conditions when paired with threads.

Examples:

	# list with initialization
	;int a, b = 0, 1
	
	# single without initialization
	;int c
	
	# list with partial initialization
	;int d, e = 0 # d is defined, but e is not

## Section 4 - Types

An exhaustive list of built-in types can be found in Appendix B.

### The `void` Type

The `void` type represents one of two things: an unknown data type, or a function.  When the void type is paired with input and output parameters `void( <types> )[ <types> ]` it represents a function.  When it is not paired with these things it represents an unknown data type.

Pure `void` types can not be created.  Function types are *always* references to the function, more like pointers than data holders.  Otherwise, void types must be created as pure pointers.  In this case, they are in a sense "un-typed" pointers which do not know the length of the object they are pointing at, only the address.

Examples:

	# simple function
	/; func_1
	;/

	# void example func
	/; func_2

		# create a void type and assign it func_1's value
		;void()[] func_ref = func_1
		
		# call func_1 using func_ref
		;func_ref()
	;/

More examples of pointer voids are available in the pointers section of this document.

### Arrays

In memory, arrays store their length as a uint, then immediately follow with the contents of the array.  This way, all arrays can be checked for length with the `len` operator.

Arrays are created by prefixing a type with `{ <# of elements> }` or simply `{}` so long as the array is immediately initialized.  One can similarly access an element of an array by suffixing the variable name with `{ <value of element to return> }`.

When initializing or assigning a new value to an entire array, use `{}` to encase a list of values.

The length of the array can be gotten by `len <variable name>`

Examples:

	# create an array of five integers
	;{5}int i

	# assign values to the array
	;i{0} = 0
	;i{1} = 2
	;i{2} = 0
	;i{3} = 2
	;i{4} = 1

	# store the length of the array
	;uint array_length = len i

	# create an initialized array with length five
	;{}int j = {1, 2, 3, 4, 5}

	# loop through the array and add them.
	/; loop (int k = 0; k < array_length) [k++]

		;i{k} += j{k}
	;/


### Pointers

Pointer types are created using the `~` (pointer to) operator.  This operator serves as both part of the type, and as a way to get a pointer from a type.  To de-reference a pointer into it's base type, use the `` ` `` (de-reference) operator.

Passing by reference can be done by prefixing the type with the de-reference operator.  This type is only available in function parameter declaration.  To call a function with this type, a pointer to the desired variable must be given.

Examples:

	# define int
	;int i

	# pointer to i
	;~int p = ~i

	# set i using p
	;`p = 1

	# a function taking a pass by reference
	/; add_two (`int i)
		i += 2
	;/

	# calling add_two in two different ways
	;add_two(p)
	;add_two(~i)

	# i is now 5

### Casting Types

Casting between types uses the standard input and output enclosing `()` and `[]` in conjunction.  Place a value in the input enclosing characters and a type to output in the output enclosing characters to cast one type to another (`( <value> )[ <type> ]`).

Examples:

	# define an int and a float
	;int i = 10
	;float f = 11.5

	# define a void pointer and set it to reference i
	;~void v = ~i
	
	# define an int pointer and cast the void pointer to initialize it
	;~int p = (v)[~int]

	# cast the float to an int and set the value of i
	;`p = (f)[int]

### Defining Types



### Interfaces

### Type Levels

## Section 5 - Operators

An exhaustive list of operators can be found in Appendix A

### Operator Precedence

Operator precedence is as follows (from greatest to least):

	Pointer operators (p0):
	
	~ - address of
	
	` - de-reference


	Access operator (p1):

	. - get/access


	Increment/de-increment (p2):

	++ - increment

	-- - de-increment


	Multiplication/division (p3):

	* - multiply

	/ - divide


	Addition and subtraction (p4):

	+ - addition

	- - subtraction


	Modulus (p5):

	% - modulus


	Bitwise operators (p6):

	& - and

	| - nor

	^ - xor

	<< - shift left
	
	>> - shift right

	!& - nand

	!| - nor

	!^ - xand

	! - not (bitwise or boolean)


	Boolean operators (p7):

	&& - boolean and

	|| - boolean or

	== - boolean eq

	> - greater than

	< - less than
	
	!&& - boolean nand

	!|| - boolean nor

	!== - boolean neq

	!> - boolean not greater than

	!< - boolean not less than

	>== - boolean greater than or equal to

	<== - boolean less than or equal to

## Section 6 - `raw` and `asm`

### The `raw` Keyword

The `raw` keyword can be used in three different scenarios, and each has a different meaning.

1. The `raw` keyword can be used in function definitions.  These effects were discussed in section 2.2.

2. The `raw` keyword may be used in conjunction with the `return` keyword, but only inside of raw functions.  This causes an instant return regardless of stack or register state.  It is recommended to clean up function and provide return types before using this.

3. The `raw` keyword may be used with the `struct` keyword to create a raw struct.  Raw structs can not contain user defined types or generics.  Raw types encode no type information and may not be extended.  Raw structs, unlike static or dynamic structs, are only as wide their members.
	- Static and dynamic structs contain a small amount of information pertaining to their actual type and generics so may be larger than only their members.
		- In addition, since static and dynamic structs may be extended, they may not be the initially defined type and may be larger, further complicating matters.

### The `asm` Keyword

The `asm` keyword may be used in `raw` functions or blocks to produce raw asm code for the assembler.  Any valid assembly code may be used, and certain extensions are available such as variable pointer references.

Syntax:

	;asm "<valid line of assembly code>"

## Credits

	Copyright 2021 Kyle Gunger

	This file is licensed under the CDDL 1.0 (the License)
	and may only be used in accordance with the License.
	You should have received a copy of the License with this
	software/source code. If you did not, a copy can be found
	at the following URL:

	https://opensource.org/licenses/CDDL-1.0

	THIS SOFTWARE/SOURCE CODE IS PROVIDED "AS IS" WITH NO
	WARRANTY, GUARANTEE, OR CLAIM OF FITNESS FOR ANY PURPOSE
	EXPRESS OR IMPLIED