-
Notifications
You must be signed in to change notification settings - Fork 24
/
ldpl.1
2867 lines (2069 loc) · 84.5 KB
/
ldpl.1
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
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.TH LDPL 1 "5 may 2019" "LDPL Man 1.0"
.SH NAME
ldpl - The LDPL programming language compiler
.SH SYNOPSIS
.PP
ldpl [-i=<extension file>]... <source file>|-c
[-o=<output name>|-r] [-f=<c++ flag>]... [-n]
.br
ldpl [-v|-h]
For more information on these options you can run "ldpl -h".
.SH DESCRIPTION
.PP
This program compiles LDPL source code into executable binaries.
LDPL is a programming language designed from the ground up to be excessively expressive, fast, readable and easy to learn.
In order to be able to properly compile LDPL executables you
.I
must
have a valid C++ compiler on your $PATH linked to the name 'c++'.
Documentation and reference for the LDPL Programming Language can be found in the sections below.
.SH OPTIONS
.PP
-v, --version
Print out LDPL version info and release details.
-h, --help
Print this list of options.
-r
By using -r you can just compile the project and print the C++ representation for that code.
-o=<name>
You can set the output file for the compiled binary with the -o flag. For example, if you want to name your program "dog", you could compile it with ldpl -o=dog main.ldpl.
-i=<file>
Extensions can be imported by using this flag; see the Extensions section.
-f=<flag>
The -f flag can be used to add flags to the C++ compilation line. See the Building C++ Extensions section for more information.
-n, --non-static
On Linux and Windows platforms, LDPL builds static binaries by default. If you want to build non-static ones use the -ns flag. To build on Android Termux you must use this flag.
-c
The -c flag tells LDPL to accept source code from the standard input.
.SH AUTHOR
This document is based on the LDPL Reference, written by Martín del Río in collaboration with Chris West.
.SH REPORTING BUGS
Report any bugs to <https://github.com/lartu/ldpl>.
.SH COPYRIGHT
Copyright © 2018 - 2019, Martín del Río. LDPL may be copied only under the terms of the GNU General Public License 3.0, which may be found in the LDPL repository.
.br
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
.ce 1
.SH -=-=-=-=-=-=-=-=-=- DOCS: LDPL DOCUMENTATION -=-=-=-=-=-=-=-=-=-
# LDPL Docs
## Introduction
The LDPL Community has compiled this document with the desire to teach
and standardize the LDPL programming language. This document contains the
specification for the LDPL programming language, as well as explanations,
instructions and examples for every feature included in it.
Feedback, corrections and suggestions are welcome, both on the main
LDPL repository
or by e-mail to [email protected].
You can join the LDPL community at r/ldpl
or via IRC on irc.freenode.net channel #ldpl.
The source code for this documentation is available in the LDPL repository.
## About LDPL
LDPL is a powerful compiled programming language designed from the
ground up to be expressive, readable, fast and easy to learn. It mimics plain
English, in the likeness of the good parts of older programming languages like
COBOL, with the desire that it can be understood by anybody. LDPL was designed
to run on Unix systems, including AMD-64 Linux, macOS, ARMv8 Linux, Android
Termux and both Intel and PowerPC OS X (tested from Tiger 10.4 onwards).
It even supports UTF-8 out of the box.
# LDPL 'Hello World' example
data:
name is text # Your name will go here.
procedure:
display "Hello World!" lf "What's your name? "
accept name
display "你好, " name ", welcome to LDPL!" lf
LDPL also aims to suppress unreadable code and redundancy by only having one
way of doing anything. What a command does should never overlap with the
purpose of another command and, as such, every LDPL command does one and only
one thing. Every line is a step further towards the completion of an algorithm,
no more, no less.
## The LDPL Compiler
To use LDPL, you should first download or compile the LDPL compiler.
For more information on how to do that, read the
*How to install LDPL* section
on the LDPL Readme.
To use the compiler, you must have a C++ compiler already installed on your
system and have mapped it to 'c++', found on your PATH. The LDPL Compiler
compiles LDPL code to C++ code and thus this is a requirement for it to work.
Once the compiler is set up, go write some LDPL source code, say 'source.ldpl'.
Then compile the source code using 'ldpl source.ldpl'. The compiled, executable
binary file will be saved as 'source-bin'. Done!
### Compiler Switches
To use the LDPL compiler, the syntax is as follows:
ldpl [-i='<included file>']... <source file>|-c
[-o='<output name>'|-r] [-f='<c++ flag>']... [-n]
ldpl [-v|-h]
The '-f' flag can be used to pass extra options to the 'c++' compiler when. For
example, '-f=-lSDL' could be used to link against SDL. The 'flag' statement can
also be used as well ('flag "-lSDL"') and its use is recommended. More on this
later.
By using '-r' you can just compile your project and print the C++ representation
for that code.
You can set the filename for the compiled binary with the '-o' flag. For
example, if you want to name your program "dog", you could compile it with
'ldpl -o=dog main.ldpl'.
On Linux platforms, LDPL builds static binaries by default. If you want to
build non-static ones use the '-ns' flag.
The '-c' flag tells LDPL to accept source code from the standard input.
You can import extra files and extensions to your LDPL compilation by using the
'-i=' flag. Extensions can be imported by passing '.o', '.a', or '.cpp' files
to this flag; see the Extensions section for more information. The use of the
'INCLUDE' statement is preferred.
'-v' and '--version' print out version info and release details.
'-h' and '--help' print this list of options.
## File Extensions
The preferred file extension for LDPL source files is '.ldpl'.
The extension '.lsc' (LDPL Source File) should also be accepted in case
the preferred extension couldn't be used for any reason.
~ Tip ~
File extensions are important: they help editors to recognize what language
your source code is written in and they tell the LDPL compiler how to treat
your files.
## License
This LDPL Compiler is distributed under the Apache 2.0 License.
All LDPL Dinosaur logos were created by Lartu and are
released under a Creative Commons Attribution 4.0 International (CC BY 4.0)
license. This documentation is released under the Apache 2.0 License.
# LDPL Source Code Structure
## Case Sensitivity
LDPL is a case-insensitive language. This means that variables and statements
that consist of the same characters are understood to be the same variable
or statement regardless of their case. For example, in other languages the
variables 'foo' and 'FOO' would represent two different variables, but in
LDPL they are the same one. This same thing happens with statements. For
LDPL it's the same if you write 'display' or 'dIsPlAy' (but please don't do so).
~ Warning ~
LDPL is case-insensitive only for latin A-z characters. Accented characters and
non-ASCII letters will be matched in a case-insensitive manner. Depending
on the LDPL implementation, 'ááá' and 'ÁÁÁ' may not represent the same
identifier. It is good practice to write your code in a single case and
always call your variables and functions by the exact name used when
declaring them.
## Comments
Comments in LDPL are denoted with a hash symbol ('#') and can be placed both
on their own line or at the end of a line that already contains a statement.
Everything after the hash is considered to be part of the comment and,
therefore, not executed.
data:
# This won't be executed
# This won't be either
procedure:
# This line won't be interpreted
## Data and Procedure Sections
LDPL was designed to be a rigidly structured programming
language and, as such, variable declarations and the remaining code procedures
are separated into two different, mutually exclusive sections within every
source file: the 'data:' section and the 'procedure:' section.
data:
# Variables go here
procedure:
# The rest of the code goes here
Variable declarations should be placed within the data section, while the
rest of the code should be placed inside the procedure section. Further
sub-procedures should be placed also within the procedure section, inside their
own sub-procedure subsections.
The data section may be obviated if no variables are used.
If your project consists of multiple LDPL source files, each file must have its
own data and procedure sections.
## Including More Source Files
You can import other LDPL source files to your LDPL source by using the
include statement. For example, say you have two sources:
# This is 'firstSource.ldpl'
procedure:
call someSubprocedure
and
# This is 'includedFile.ldpl'
procedure:
sub someSubprocedure
display "Hi there!"
end sub
You can import the second source into the first one in order to create one
big source file like this:
# This is 'firstSource.ldpl'
include "includedFile.ldpl"
procedure:
call someSubprocedure
When you run the code above, it will print
Hi there!
using the sub-procedure 'someSubprocedure' included from the second file.
The location where the included files are searched for is relative to the file
that includes them. You may include as many files as you like.
The 'include' statement can only be used before the data section.
## C++ Extensions
LDPL source code can be extended using C++ Extensions, C++ source files
that can be compiled along the C++ source code generated by the LDPL compiler.
While these are explained in greater detail in their respective section of
this document, two statements are relevant to this part of the documentation:
the 'flag' and 'extension' statements.
## C++ Compiler Flags
When writing C++ code, you may need to pass some flags to the C++ compiler.
Say, for example, you are writing something using the SDL Library. When
trying to compile your code, you will need to pass the flag '-lSDL' to your
C++ compiler. This same thing can be achieved under LDPL by using the 'flag'
statement.
Following the example above, if you want to pass the flag '-lSDL' to the C++
compiler that compiles the code generated by the LDPL compiler from your LDPL
source code, you may do this:
flag "-lSDL"
flag "-fpermissive" # you can pass as many flags as you want
data:
#...
procedure:
#...
Often, different operating systems require different flags. In those cases,
you can restrict the 'flag' statement to a particular OS:
flag "-O3"
flag linux "-lncursesw"
flag macos "-lncurses"
flag macos "-D_XOPEN_SOURCE_EXTENDED" # multiple flags per OS are permitted
data:
#...
procedure:
#...
~ Tip ~
LDPL officially supports the following operating systems:
• Linux
• MacOS
• Android
And includes experimental support for these fine systems:
• BSD
• Emscripten
The 'flag' statement can only be used before the data section.
## C++ Extension Including
Extensions are C++ source files that you can compile along your LDPL source
in order to extend the language, as stated above. If you want to include an
extension, you may use the 'extension' statement. For example:
extension "someDirectory/someFile.cpp"
flag "-O3"
data:
#...
procedure:
#...
The 'extension' statement can only be used before the data section.
## The Data Section
The data section is where global variables are declared (you can use them
anywhere in your program). If no variables are declared, the data section can
be skipped altogether.
The data section is defined and preceded by the 'data:' keyword.
An empty data section looks like this:
data:
On every line within the data section (that is, on every line after the 'data:'
keyword and before the 'procedure' keyword) one and only one variable can be
declared.
The syntax for declaring a variable in LDPL is:
variable-name is data-type
Variable naming schemes and data types will be explained later on this
document.
A data section cannot contain anything but variable declarations, comments and
empty lines. An example data section may end up looking like this:
data: # this is an example data section
foo is number
bar is text map
# foobar is my number list
foobar is number list
## Variable Data Types
LDPL natively supports the scalar number and text data types. It also
supports containers of said scalar types: maps and lists, combined in
any way.
Variables may be declared, as stated above, using the syntax:
variable-name is data-type
within the data section. The name of a data type is composed of either a scalar
data type name (number or text) or a container type plus one or more
scalar or container type names. For example:
foo is number
bar is text
foobar is map of lists of text
In the case above, foobar is a map of lists of text values.
### The Number Data Type
The number data type, as its name suggests, depicts numeric values.
It's recommended that it be represented internally as a binary64
double-precision floating-point format number as defined by the IEEE 754.
Both variables and numeric constants can be members of the number type.
Valid number literals must begin with a decimal value (for example 5 or 0.12,
.12 wouldn't be a valid number) and may be preceded by a minus sign for
negative numbers (-5, -567.912). Numbers may not be preceded by a plus sign
(+5 is not a valid number literal). The literal -0 is implicitly transformed
into 0.
-231897.123 # is an example number
### The Text Data Type
The text data type, as its name suggests, represents alphanumeric strings.
In the interest of supporting as many locales as possible, texts should be utf-8
encoded to be compatible with Unicode. A text maximum length for text values is
explicitly not defined and it should be limited only by the amount of available
memory on the system. Strings in LDPL are enclosed between two "quotes".
"This is an example string!"
LDPL strings may contain multiple escape sequences / control characters in
them. Each escape sequence counts as only one character. The available escape
sequences are:
• '\\a' = alert (bell)
• '\\b' = backspace
• '\\t' = horizontal tab
• '\\n' = newline / line feed
• '\\v' = vertical tab
• '\\f' = form feed
• '\\r' = carriage return
• '\\e' = non-standard GCC escape
• '\\0' = null byte
• '\\\\' = \\ character
• '\\"' = " character
For example, the string '"hello,\\nworld"' will be displayed as
hello,
world
when printed to the console.
### The List Data Type
The list data type is a collection of number or text values, or containers
of possibly more containers of said types. Values can be pushed to lists and
then be accessed and modified using the ':' operator. List indexes consist of
integer numbers. The first index of a list is index 0, and the rest count up to
the length of the list minus one.
Lists, as collections of number or text values (or collections of said types),
can only have one defined type at any given time: text or number. A single list
is not capable of storing both numeric and alphanumeric values.
data:
foo is number list # This is a list
procedure:
push 10 to foo
display foo:0 lf
~ Note ~
The 'display' statement prints values to the screen. While it will be
explained in more detail later, the line
'display foo:0 lf'
prints
the value of 'foo:0' (the index '0' of the list 'foo') followed by
a line break ('lf').
In most other languages, you may have to declare a list and specify how many
elements or components it contains. In such languages, the declaration causes a
contiguous block of memory to be allocated for that many elements. LDPL lists,
however, are different: they are dynamic. This means that you can store as many
values as you want in a single list without fear of running out of place (of
course, this is limited by the memory allocated by your operating system for
your LDPL program).
Suppose you store the values '"hi"', '"there"', '"I love"' and
'"LDPL, it's great!"' in a 'text list', in that particular order. Then the
contents of the list and the index associated with each element will be
.br
| Index | Element |
.br
| :---: | :---: |
.br
| 0 | '"hi"' |
.br
| 1 | '"there"' |
.br
| 2 | '"I love"' |
.br
| 3 | '"LDPL, it's great!"' |
We have shown the pairs in order because their order is relevant: if you added
a new element to the list, it would be inserted after the last element, thus
being associated with index '4'.
To add values to a list, you must first push them to the list. For example,
if you want to add the numbers '10', '20' and '30' to a 'number list', your
code should look like this:
data:
myList is number list
procedure:
push 10 to myList # 10 is stored in index 0 of myList
push 20 to myList # 20 is stored in index 1 of myList
push 30 to myList # 30 is stored in index 2 of myList
Values in lists can be stored and accessed just like any other variable
(see the store - in statement for further details) using the ':' operator.
This operator indicates what index of the list we are writing to or reading
from. Here we declare a 'number list' and store the values '5' and '-10.2' in
it, and then replace the number '5' by the number '890':
data:
myList is number list
procedure:
push 5 to myList
# 5 is stored in index 0 of myList
push -10.2 to myList
# -10.2 is stored in index 1 of myList
store 890 in myList:0
# We store 890 in index 0 of myList, thus replacing the 5
~ Note ~
The 'push' statement adds a value at the end of a list. Also, the 'store
• in' statement stores a value in a variable. These two statements will
be explained in more detail in their respective sections of this document.
Please note that as a list is variable that's a collection of values, a
single index of a list is a variable in itself. This means that any subindex
of a list that resolves to a scalar value be used in any position where you
could use a variable of the same type of that value. So, if you have something
like this:
store <number-variable or number> in <number-variable>
.br
You could use a 'number list' with a defined sub-index \(for example, in the
example above, 'myList:0'\) where it says number-var, just like in
the 'store - in' examples in the code extracts above.
In the list statements section, you'll find a collection of statements that
can be used to work with lists.
### The Map Data Type
The map data type is a collection of number or text values. Maps
superficially resemble lists but with fundamental differences. The biggest one
is that any number or string in LDPL may be used as an array index, not just
consecutive integers. Also, values in maps have no order.
data:
foo is number map # This is a map
procedure:
store 19 in foo:"hi there!"
display foo:"hi there!" lf
Maps, as collections of number or text values (or collections of said types),
can only have one defined type at any given time: text or number. A single map
is not capable of storing both numeric and alphanumeric values.
Unlike lists, maps are associative. This means that each map is a
collection of pairs: a key and its corresponding element. For example,
you could have a 'number map' with the following contents:
.br
| Key | Element |
.br
| :---: | :---: |
.br
| 4 | 30 |
.br
| 2 | 10 |
.br
| "Hi there!" | -56.3 |
.br
| "99ldplrocks89" | 0 |
We have shown the pairs in jumbled order because their order is irrelevant.
One advantage of maps is that new pairs can be added at any time. maps can be
sparse: they can have missing keys \(say for example you have keys 1 and 5, but
don't have keys 2, 3 and 4\). Another consequence of maps is that the keys
don't necessarily have to be positive integers. Any number, or even a string,
can be a key.
Values in maps can be stored and accessed just like any other variable
(see the store - in statement for further details) using the ':' operator.
This operator indicates what key of the map we are writing to or reading from.
Here we declare a 'number map' and store the values '5' and '-10.2' in the
keys '1' and '5', respectively.
data:
myMap is number map
procedure:
store 5 in myMap:1 #Stores 5 in key 1 of myMap
store -10.2 in myMap:5 #Stores -10.2 in key 5 of myMap
As stated before, map keys don't always have to be constant numbers.
They can also be number variables, text and text variables, or even sub-indexes
of lists or elements from other maps. For example:
data:
myMap is number map
myOtherMap is number map
myVar is number
procedure:
store 17 in myVar
store 1 in myMap:"hello"
#Stores 1 in key "hello" of myMap
store 7 in myMap:myVar
#Stores 7 in a key equal to the current value of myVar
store 3 in myMap:myOtherMap:4
#Stores 3 in a key of equal value to the key of myMap with value equal to
#key 4 of myOtherMap
In fact, when you use a number value as a subindex for a map, it is silently
casted into a text value. For example, 'myMap:1' will be interpreted \(an thus,
the same\) as 'myMap:"1"'.
Please note that as a map is variable that's a collection of values,
a single key of a map is a variable in itself. This means that any key of a
map that resolves to a scalar value can be used in any position where you could
use a variable of the same type of that value. So, if you have something like
this:
store <number-var or number> in <number-var>
.br
You could use a 'number map' with a particular key where it says number-var,
just like in the 'store - in' examples in the code extracts above
\(for example 'myMap:"hello"'\).
As you'll see in the Default Variable Values section, you can access
undeclared keys of a map, just like if they were declared.
See the following example:
data:
.br
myMap is number map
.br
procedure:
.br
display myMap:99
.br
In the example above, '0' will be printed and no errors displayed during
compilation, even though the key '99' of 'myMap' hasn't been explicitly
declared. This is because when you try to access an element that hasn't
been declared yet, LDPL declares it for you and initializes it to its type
default value.
It's important to note that this very feature is a double-edged weapon. While
you can use it to access uninitialized map keys, you cannot check if a value
exists in a map without initializing it if it wasn't there before. Statements
like store key count of and store keys of are provided as means to
overcome this situation.
In the map statements section, you'll find a collection of statements that
can be used to work with maps.
~ Warning ~
In older versions of LDPL, maps were called vectors.
Starting from LDPL 3.1.0 Diligent Dreadnoughtus, they have been renamed
to reflect the real data structure they represent. While it might still be
possible to call them vectors in code, and legacy code that declares maps
as vectors is and will continue to be supported, this nomenclature is
deprecated and shouldn't be used anymore.
## Multicontainers
As stated before, "The name of a data type is composed of either a scalar
data type name (number or text) or a container type plus one or more
scalar or container type names." This means that you can declare variables like:
myVariable is list of lists of text
myOtherVariable is map of lists of maps of maps of lists of maps of numbers
pleaseStop is list of maps of maps of lists of maps of maps of maps of lists of lists of maps of lists of lists of text
These variables are called multicontainers. Multicontainers hold other
containers, which can hold more containers or scalar values to an arbitrary
depth. For example, a 'map of lists of text' is a map that holds lists
of text values. You may access a value stored in a 'map of lists of text'
like so:
display foo:"hi":0
where 'hi' is the key used to access a list stored in the map, and '0' the
index used to access a text value stored in the list that was stored in the
map.
If you want to push a list to a 'list of lists' or a map to a 'list of maps' you
must use the 'push list to ' and 'push map to ' statements, respectively:
data:
listOfMaps is list of maps of texts
listOfLists is list of lists of texts
procedure:
push map to listOfMaps
store "Hello!" in listOfMaps:0:"hi!"
# Pushes a map to listOfMaps and then stores
# the value "Hello!" in the key "hi!" of the pushed map
push list to listOfLists
push "Hello!" listOfLists:0
# Pushes a map to listOfLists and then pushes
# the value "Hello!" at index 0 of the pushed list
These will be explained in more detail in their respective sections.
The LDPL compiler supports plural data-type names, as shown above. One can
use 'numbers' instead of 'number', 'texts' instead of 'text',
'lists' instead of 'lists' and 'maps'
instead of 'map'. A 'list of list of number' is the same as a 'list of lists of numbers'.
~ Note ~
In LDPL 4.3, multicontainers were introduced with a right-to-left syntax.
A 'map of list of text' was defined as 'text list map'. This syntax is
still supported.
## Default Type Values
In LDPL, each variable is initialized with a value by default. This means
that when you declare a variable, it will, by default, hold this value
until it's changed.
• Number variables are initialized with the value '0'. Each element of
a number map is a number variable, and thus also initialized to '0'.
• Text variables are initialized to the empty string '""'. The same goes
for text maps, where each element it contains is also initialized to '""'.
• Lists are initialized empty by default and trying to access a
non-existing index will result in an error.
• Keys of Maps of Lists ('map of list') are declared as empty lists of the
scalar type of the multicontainer by default.
## Predeclared Variables
Some variables in LDPL are already declared for you without you having to
declare them. These variables are the argv text list, and the errorcode
and errortext variables.
### The *argv* list variable
Every LDPL program comes with the 'argv' text list variable declared by
default.
If you pass command line arguments to your LDPL compiled program \(running, for
example, something like 'myBinary argument1 argument2)', the values stored
in the 'argv' list \(_argument vector_\) will be the values of each argument
passed \(in this case, '"argument1"' will be stored in 'argv:0' and
'"argument2"' in 'argv:1'\).
~ Hint ~
Given that 'argv' is a text list, the values passed as arguments are
always stored as text, even numbers.
Naturally, if no arguments are passed to the program, the 'argv' list will be
empty.
### The *errorcode* and *errortext* variables
Some LDPL operations may fail when executed. Maybe you tried loading a file
that wasn't there or getting the ASCII value of a multi-byte emoji. These
operations make use of the 'errorcode' and 'errortext' variables to
tell you if they ran successfully or not.
The 'errorcode' and 'errortext' variables come declared by default.
Some statements may modify their values to express their results.
The 'errorcode' variable is a number variable. It will hold the value 0
if the statement ran successfully, and any other number if it did not.
The 'errortext' variable is a text variable that will be empty if the
statement ran successfully. If it did not, it will store a human readable
description of what went wrong.
The 'errorcode' and 'errortext' variables can be read and written like any other
LDPL variable.
~ Warning ~
When handling error checks, please bear in mind that the content of the
'errortext' variable may change in future releases of LDPL. The value
stored in 'errorcode', however, will not change and so that's the value
that should be used to check whether an operation ran successfully or not.
## The Procedure Section
The procedure section is where all the code of a LDPL program that is not a
variable declaration is written. An LDPL program must contain a procedure
section, even if it's empty. Execution should and will fail otherwise.
Within the procedure section, every line can contain either a comment,
a statement, a statement and a comment or be empty. No two statements can be
written on the same line.
An example procedure section may end up looking like this:
procedure:
store 5 in foo
store "hi there" in bar:"hi"
# Note that these are the variables
# declared in the data section above.
Code within the procedure section is executed from top to bottom,
skipping sub-procedure declarations, unless they are explicitly called.
Available statements and sub-procedure declarations will be explained further
in the following sections of this document.
## Sub-procedures
A sub-procedure is a piece of code that can be called and executed from other
parts of the script. Sub-procedure subsections must be declared within the
procedure section of the code using a 'sub-procedure <name>' statement and
end with an 'end sub-procedure' statement. Alternatively, the shorter versions
'sub <name>' and 'end sub' may be used. Bear in mind that you can't define a
sub-procedure within a sub-procedure.
Sub-procedures may be invoked at any point in your code by calling them with
the 'call' statement. When you do that, the sub-procedure is executed and once
it finishes executing, the line after the 'call' that called the sub-procedure
is executed and execution continues normally. More on the call statement later.
Also bear in mind that you can 'call' a
sub-procedure before it has been declared, but the compilation process will
fail if the compiler doesn't find the sub-procedure once it has parsed all the
files in your program.
The full syntax for declaring sub-procedures is this one:
sub-procedure procedureName
parameters:
# parameters go here
local data:
# local variable declarations go here
procedure:
# code goes here
end sub-procedure
Or you may, as stated, use the shorter version:
sub procedureName
parameters:
# parameters go here
local data:
# local variable declarations go here
procedure:
# code goes here
end sub
In context, the full declaration of a sub-procedure looks like this:
data:
# ...
procedure:
# ...
sub mySubprocedure
parameters:
# ...
local data:
# ...
procedure:
# ...
end sub
The parameters and local data sub-sections are optional (more on these
later). If you decide to not include any of those sub-sections, you can skip
the procedure tag altogether and just go ahead writing your code like this:
sub someOtherSub
display "Hello there!" lf
end sub
You cannot have more than one sub-procedure with the same name. Also,
sub-procedure names must follow the guidelines stated in the Naming Schemes
section of this document.
### The Procedure Subsection:
In the procedure sub-section of the sub-procedure you may write the code
of your sub-procedure using statements like in the main procedure section.
In this procedure sub-section, however, you may also use the 'RETURN' statement
to halt execution of the sub-procedure and return to the point where it was
called from.
### The Parameters and Local Data Sub-Sections
Within the parameters and data sub-sections of a function you may only
declare variables just like in the global data section. The variables
defined here, however, can only be used inside the procedure sub-section of the
same sub-procedure they where declared in.
Variables between the parameters and local data sub-sections may not share
names.
If a variable declared within the parameters or data sub-sections of
a sub-procedure shares its name with a global variable, when using that name
within the procedure section of the sub-procedure, it will always refer to the
variable declared in said sub-procedure and not the global one.
At the start of the sub-procedure execution, all the variables declared in its
local data section will be initialized with their type default values, and
each invocation of the function will have its own copy of the local variables.
This is important if you want to implement recursive sub-procedures:
data:
execution is number
procedure:
sub myRecursiveSub
local data:
myLocalVar is number
procedure:
in executions solve executions + 1
if executions is equal to 3 then
return # We don't want this to run forever!
end if
display "[myLocalVar starts at " myLocalVar "!"
store executions in myLocalVar
call myRecursiveSub
display "I'm execution n°" myLocalVar "!]"
end sub
call myRecursiveSub
That weird recursive function displays the following output:
[myLocalVar starts at 0![myLocalVar starts at 0!I'm execution Nº2!]I'm execution Nº1!]
~ Hint ~
Most of the statements used in this example will be explained later.
Don't feel bad if you don't yet understand completely what that sub-procedure
does.
Variables declared within the parameters sub-section are quite different.
When calling sub-procedures using the 'call' statement, you may also use the
optional 'with' keyword to specify values to be passed to the sub-procedure.
The variables declared in the parameters sub-section will take these values,
following the order they were declared in.
For example, if you declare a sub-procedure like this:
sub addTwoNumbers
parameters:
a is number
b is number
c is number
procedure:
# ...
end sub
You may then call it like this:
call addTwoNumbers with 5 6 7
And 'a' will take the value 5, 'b' the value 6 and 'c' the value 7.
While this is fine, it gets more powerful when using variables instead of
fixed values. LDPL is a pass-by-reference language. This means that if you
pass a variable to a sub-procedure with the 'with' keyword and its value is
assigned to a parameter variable, if you modify that parameter variable the
original variable will be modified as well. Let's see an example:
data:
result is number
procedure:
sub addTwoNumbers
parameters:
a is number
b is number
c is number
procedure:
in c solve a + b
end sub
# the variable result is initialized to 0 by default
call addTwoNumbers with 4 5 result
display "The result is: " result "." lf
~ Hint ~
The 'in solve ' statement is used to solve mathematical expressions.
If you execute, for example, 'in foo solve 5 + 6 - 8', the result of solving
5 + 6 - 8 will be stored in the number variable 'foo'.
That code displays the following text:
The result is 9.
This is because, as we called the variable with a variable as a parameter
('call addTwoNumbers with 4 5 result', result is the variable here) it was