Category: lysaac
Articles from 1 to 9
Lysaac: not talking about it but it goes forward
Lysaac is my reimplementation of the lisaac compiler. Until now, it wasn't very interesting to look at, but recently, I pushed a few interesting commits:
- you now have variables
- the default value is initialized correctly
- the read works
- the write works
- you also have BLOCKs (sorry, no upvalues for now)
This may looks like nothing, but under the hood, the infrastructure is almost completely there.
Next thing to come: inheritance and error reporting.
Then perhaps, syntax improvements like keyword messages and later: operators. For now, I want the basic functionnality working well.
If you want to play with it, you can. If you get an error, create a use-case and
propose it as a new feature. Please use as a model the .feature files.
Lysaac: on en parle pas mais ça avance
Lysaac c'est ma réimplémentation du compilateur lisaac. Jusqu'a présent, il n'y avait pas grand chose, mais dernièrement, il y a eu des commits intéressants:
- les variables fonctionnent
- avec des valeurs par défaut
- on peut les lire
- et y écrire
- on a aussi des BLOCKs, mais sans upvalues
Ça ne paye peut être pas de mine, mais en fait, l'infrastructure du compilo est presque complète.
Prochaines avancées: héritage et affichage des erreurs
Et peut être après: des améliorations de syntaxe (appels de slot à paramètres et bien plus tard: opérateurs). Pour le moment, je me concentre sur les choses basiques.
Si vous voulez jouer, vous pouvez. Si vous avez une erreur inattendue, créez un
scénario d'utilisation et donnez le moi (préférablement sous forme de
fichier .feature).
Lysaac now compiles Hello World!
This is great: Here is the source files:
c/cstring.li
Section Header
+ name := Reference CSTRING;
- role := String; // const char*
- type := Integer 8;
c/main.li
Section Header
+ name := MAIN;
Section Public
- puts str:CSTRING <- External `puts`;
- main <-
(
puts "Hello World";
);
You type lysaac compile c >c.bc and you get the following LLVM assembly code:
c.bc
@0 = private constant [12 x i8] c"Hello World\00"
declare void @puts (i8*)
define void @main () {
%1 = getelementptr [12 x i8]* @0, i32 0, i32 0
tail call void @puts(i8* %1)
ret void
}
And you can execute it using the standard LLVM tools:
$ llvm-as < c.bc | lli
Hello World
$
Isn't that great ?
Union types and null
Look at this:
object.li
Section Header
+ name := Singleton NULL;
Section Public
- is_null :BOOLEAN <- FALSE;
null.li
Section Header
+ name := Singleton NULL;
Section Inherit
- parent :OBJECT := OBJECT;
Section Public
- is_null :BOOLEAN <- TRUE;
union.li
Section Header
+ name := UNION;
Section Inherit
- parent :OBJECT := OBJECT;
union.1.li
Section Header
+ name := Expanded UNION(E);
- import := E;
Section Inherit
- parent :UNION := UNION;
Section Public
+ element:E;
- set_element e:E <- (element := e;);
- when o:T do blc:{o:T;} <-
(
(o = E).if {
blc.value element;
};
);
- from_e e:E :SELF <-
( + res :SELF;
res := clone;
res.set_element e;
res
);
union.2.li
Section Header
+ name := Expanded UNION(E, F...);
Section Inherit
+ parent_e :UNION(E);
+ parent_next :UNION(F...);
Section Public
- when o:T do blc:{o:T;} <-
(
(o = E).if {
parent_e.when o do blc;
} else {
parent_next.when o do blc;
};
);
use.li
Section Header
+ name := USE;
Section Public
- accept_object_or_null obj:UNION(USE,NULL) <-
(
obj
.when NULL do { o:NULL;
? { o.is_null };
}
.when USE do { o:USE;
? { o.is_null.not };
};
);
Stack environments
Stack environment would be an argument passed implicitely to every function in
the code. It would contain global policy. In particular the MEMORY object that
lets you allocate memory. If you want to change the allocation policy, you just
have to change the current environment, and all functions you call will use the
new policy.
We could allow user defined objects like that, not just system objects.
We could also manage errors that way. An error flag could be stored in the environment. Set by the calee and tested by the caller.
Lysaac annotations
Because I'm using an open world assumption, I need the compiler to generate annotations on units it compiles, so when it sees them again, it knows what it does (or does not) internally.
I was looking at a LLVM video this morning (VMKit precisely) and the person talked about an interesting optimization. What if we could allocate objects in stack instead of the heap. This would save time when creating the object. Then we wouldn't be tempted to avoid creating new objects for fear of memory leaks (there is not garbage collector in lisaac currently) and performance penalty.
This is the same thing as aliased variables in Ada.
An object can be allocated on the stack if:
- it is not returned by the function.
- it is not stored on the heap by the function.
- it is not used in a called function that would store a pointer to this object on the heap.
So, when the compiler compiles a cluster, it has to generate an annotation file containing for each argument in each code slot whether the argument is guaranteed to remain on the stack or if it might be stored on the heap. If an argument is guaranteed to stay on the stack, we can allocate it on the stack. When the function will return, the only instances would be located in the current stack frame.
Lysaac compilation model and inline prototypes
In Lysaac, I choose to follow the open world assumption, like the majority of programming languages out there, instead of the closed world assumption. There are two main reasons:
First, I don't strive at creating an optimizing compiler, not yet at least. Closed world is useful for that, but I don't need it.
Second, open world assumption increases the complexity a lot. The Lisaac compiler uses an exponential algorithm, and will always hit a limit with big projects. With an open world, you can partition the complexity.
Because I still believe in global compilation, I decided that my compilation unit would be the cluster instead of the prototype. That is, I'll compile a cluster completely in one object file. That makes it possible to optimize things like private prototypes.
This leaves a big performance problem for BOOLEANs in particular. BOOLEAN,
TRUE and FALSE are prototypes in the standard library, and having an open
world assumption would require pasing to the if then slot function pointers.
I can't realisticly do that.
So, These prototypes could be marked as Inline. They are separated from their
cluster and gets compiled in every cluster that uses them. The syntax could be
quite simple:
Section Header
+ name := Inline TRUE;
But, because each cluster is then free to compile it as it wants, there is a
problem of interoperability. How can you be sure that the TRUE in your cluster
is compiled the same way as in the neighbooring cluster you are using. As it is,
you can't pass TRUE object around clusters. Very annoying.
The solution would be to encode them and decode them manually. You could have:
Section Header
+ name := Inline TRUE;
Section Feature
- inline_size :INTEGER := 0;
Take a more interesting example:
Section Header
+ name := Inline Expanded BIT;
- size := 1;
Section Feature
- inline_size :INTEGER := 1;
- encode p:POINTER <-
(
p.to_native_array_of BIT.put bit to 0;
);
- decode p:POINTER <-
(
data := p.to_native_array_of BIT.item 0;
);
This needs to be refined.
Additionally, .cli files could also contain the Inline keyword. In that
case, the cluster it reference will be compiled with the current cluster. That
could be useful for private clusters.
Clusters in Lysaac works
◆ Root Cluster │ Cluster in: src ├─◆ LIB (src/lib.cli) │ │ Cluster in: src/../lib │ ├─◇ PATH_HELPER (src/../lib/path_helper.li) │ ├─◇ CSTRING (src/../lib/cstring.li) │ ╰─◇ LIBC (src/../lib/libc.li) ├─◇ PARSER (src/parser.li) ├─◆ STDLIB (src/stdlib.cli) │ │ Cluster in: src/../stdlib/standard │ ├─◆ INTERNAL (src/../stdlib/standard/internal.cli) │ │ │ Cluster in: src/../stdlib/standard/../internal │ │ ├─◆ PORTABLE (src/../stdlib/standard/../internal/portable.cli) │ │ │ │ Cluster in: src/../stdlib/standard/../internal/portable │ │ │ ├─◇ FLOAT_REAL (src/../stdlib/standard/../internal/portable/number/float_real.li) │ │ │ ├─◇ FIXED_REAL (src/../stdlib/standard/../internal/portable/number/fixed_real.li) │ │ │ ├─◇ FLOAT_MAP80 (src/../stdlib/standard/../internal/portable/number/float_map80.li) │ │ │ ├─◇ SIGNED_INTEGER (src/../stdlib/standard/../internal/portable/number/signed_integer.li) │ │ │ ├─◇ FLOAT_MAP32 (src/../stdlib/standard/../internal/portable/number/float_map32.li) │ │ │ ├─◇ UNSIGNED_INTEGER (src/../stdlib/standard/../internal/portable/number/unsigned_integer.li) │ │ │ ├─◇ FLOAT_MAP64 (src/../stdlib/standard/../internal/portable/number/float_map64.li) │ │ │ ├─◇ SIGNED_FIXED_REAL (src/../stdlib/standard/../internal/portable/number/signed_fixed_real.li) │ │ │ ├─◇ NUMERIC (src/../stdlib/standard/../internal/portable/number/numeric.li) │ │ │ ├─◇ FLOAT_MAP (src/../stdlib/standard/../internal/portable/number/float_map.li) │ │ │ ├─◇ UNSIGNED_FIXED_REAL (src/../stdlib/standard/../internal/portable/number/unsigned_fixed_real.li) │ │ │ ├─◇ FILE_INPUT_STREAM (src/../stdlib/standard/../internal/portable/io/file_input_stream.li) │ │ │ ├─◇ STD_INPUT_OUTPUT (src/../stdlib/standard/../internal/portable/io/std_input_output.li) │ │ │ ├─◇ FILE_OUTPUT_STREAM (src/../stdlib/standard/../internal/portable/io/file_output_stream.li) │ │ │ ├─◇ INPUT_STREAM (src/../stdlib/standard/../internal/portable/io/input_stream.li) │ │ │ ├─◇ OUTPUT_STREAM (src/../stdlib/standard/../internal/portable/io/output_stream.li) │ │ │ ├─◇ MEMORY (src/../stdlib/standard/../internal/portable/memory/memory.li) │ │ │ ├─◇ SYSTEM_DETECT (src/../stdlib/standard/../internal/portable/system/system_detect.li) │ │ │ ├─◇ HASHED_DICTIONARY_NODE (src/../stdlib/standard/../internal/portable/collection/hashed_dictionary_node.li) │ │ │ ├─◇ COLLECTION (src/../stdlib/standard/../internal/portable/collection/collection.li) │ │ │ ├─◇ HASH_TABLE_SIZE (src/../stdlib/standard/../internal/portable/collection/hash_table_size.li) │ │ │ ├─◇ ANY_HASHED_BIJECTIVE_DICTIONARY_NODE (src/../stdlib/standard/../internal/portable/collection/any_hashed_bijective_dictionary_node.li) │ │ │ ├─◇ ANY_LINKED_LIST_NODE (src/../stdlib/standard/../internal/portable/collection/any_linked_list_node.li) │ │ │ ├─◇ ANY_AVL_SET_NODE (src/../stdlib/standard/../internal/portable/collection/any_avl_set_node.li) │ │ │ ├─◇ LINKED2_LIST_NODE (src/../stdlib/standard/../internal/portable/collection/linked2_list_node.li) │ │ │ ├─◇ ANY_AVL_DICTIONARY_NODE (src/../stdlib/standard/../internal/portable/collection/any_avl_dictionary_node.li) │ │ │ ├─◇ COLLECTION3 (src/../stdlib/standard/../internal/portable/collection/collection3.li) │ │ │ ├─◇ SET (src/../stdlib/standard/../internal/portable/collection/set.li) │ │ │ ├─◇ ANY_TWO_WAY_LINKED_LIST_NODE (src/../stdlib/standard/../internal/portable/collection/any_two_way_linked_list_node.li) │ │ │ ├─◇ HASHED_SET_NODE (src/../stdlib/standard/../internal/portable/collection/hashed_set_node.li) │ │ │ ├─◇ ARRAYED_COLLECTION (src/../stdlib/standard/../internal/portable/collection/arrayed_collection.li) │ │ │ ├─◇ SIMPLE_DICTIONARY (src/../stdlib/standard/../internal/portable/collection/simple_dictionary.li) │ │ │ ├─◇ DICTIONARY (src/../stdlib/standard/../internal/portable/collection/dictionary.li) │ │ │ ├─◇ AVL_DICTIONARY_NODE (src/../stdlib/standard/../internal/portable/collection/avl_dictionary_node.li) │ │ │ ├─◇ AVL_CONSTANTS (src/../stdlib/standard/../internal/portable/collection/avl_constants.li) │ │ │ ├─◇ ANY_HASHED_SET_NODE (src/../stdlib/standard/../internal/portable/collection/any_hashed_set_node.li) │ │ │ ├─◇ NATIVE_ARRAY (src/../stdlib/standard/../internal/portable/collection/native_array.li) │ │ │ ├─◇ AVL_TREE (src/../stdlib/standard/../internal/portable/collection/avl_tree.li) │ │ │ ├─◇ NATIVE_ARRAY_VOLATILE (src/../stdlib/standard/../internal/portable/collection/native_array_volatile.li) │ │ │ ├─◇ COLLECTION2 (src/../stdlib/standard/../internal/portable/collection/collection2.li) │ │ │ ├─◇ ANY_HASHED_DICTIONARY_NODE (src/../stdlib/standard/../internal/portable/collection/any_hashed_dictionary_node.li) │ │ │ ├─◇ ARRAYED (src/../stdlib/standard/../internal/portable/collection/arrayed.li) │ │ │ ├─◇ AVL_SET_NODE (src/../stdlib/standard/../internal/portable/collection/avl_set_node.li) │ │ │ ├─◇ LINKED_XOR_NODE (src/../stdlib/standard/../internal/portable/collection/linked_xor_node.li) │ │ │ ├─◇ LINKED_LIST_NODE (src/../stdlib/standard/../internal/portable/collection/linked_list_node.li) │ │ │ ├─◇ AVL_TREE_NODE (src/../stdlib/standard/../internal/portable/collection/avl_tree_node.li) │ │ │ ├─◇ LINKED_COLLECTION (src/../stdlib/standard/../internal/portable/collection/linked_collection.li) │ │ │ ├─◇ FS_MIN (src/../stdlib/standard/../internal/portable/file_system/fs_min.li) │ │ │ ├─◇ STRING_BUFFER (src/../stdlib/standard/../internal/portable/string/string_buffer.li) │ │ │ ╰─◇ CHARACTER_REF (src/../stdlib/standard/../internal/portable/string/character_ref.li) │ │ ╰─◆ UNIX (src/../stdlib/standard/../internal/unix.cli) │ │ │ Cluster in: src/../stdlib/standard/../internal/os_support/unix │ │ ├─◇ FLOAT_PROCESSOR (src/../stdlib/standard/../internal/os_support/unix/system/float_processor.li) │ │ ├─◇ SYSTEM (src/../stdlib/standard/../internal/os_support/unix/system/system.li) │ │ ├─◇ CLOCK (src/../stdlib/standard/../internal/os_support/unix/system/clock.li) │ │ ├─◇ ENVIRONMENT (src/../stdlib/standard/../internal/os_support/unix/system/environment.li) │ │ ├─◇ SYSTEM_IO (src/../stdlib/standard/../internal/os_support/unix/system/system_io.li) │ │ ├─◇ PROCESSOR (src/../stdlib/standard/../internal/os_support/unix/system/processor.li) │ │ ├─◇ EVENT_SYSTEM (src/../stdlib/standard/../internal/os_support/unix/video/event_system.li) │ │ ├─◇ KEYBOARD (src/../stdlib/standard/../internal/os_support/unix/video/keyboard.li) │ │ ├─◇ TIMER (src/../stdlib/standard/../internal/os_support/unix/video/timer.li) │ │ ├─◇ VIDEO (src/../stdlib/standard/../internal/os_support/unix/video/video.li) │ │ ├─◇ MOUSE (src/../stdlib/standard/../internal/os_support/unix/video/mouse.li) │ │ ├─◇ FILE_UNIX (src/../stdlib/standard/../internal/os_support/unix/file_system/file_unix.li) │ │ ├─◇ FILE_SYSTEM (src/../stdlib/standard/../internal/os_support/unix/file_system/file_system.li) │ │ ├─◇ ENTRY_UNIX (src/../stdlib/standard/../internal/os_support/unix/file_system/entry_unix.li) │ │ ├─◇ DIRECTORY_UNIX (src/../stdlib/standard/../internal/os_support/unix/file_system/directory_unix.li) │ │ ├─◇ BMP_LINE_ASCII (src/../stdlib/standard/../internal/os_support/unix/video_ascii/bmp_line_ascii.li) │ │ ├─◇ BITMAP_ASCII (src/../stdlib/standard/../internal/os_support/unix/video_ascii/bitmap_ascii.li) │ │ ├─◇ VIDEO (src/../stdlib/standard/../internal/os_support/unix/video_ascii/video.li) │ │ ╰─◇ PIXEL_ASCII (src/../stdlib/standard/../internal/os_support/unix/video_ascii/pixel_ascii.li) │ ├─◇ STD_ERROR (src/../stdlib/standard/io/std_error.li) │ ├─◇ COMMAND_LINE (src/../stdlib/standard/io/command_line.li) │ ├─◇ IO (src/../stdlib/standard/io/io.li) │ ├─◇ STD_INPUT (src/../stdlib/standard/io/std_input.li) │ ├─◇ STD_OUTPUT (src/../stdlib/standard/io/std_output.li) │ ├─◇ TIME (src/../stdlib/standard/time/time.li) │ ├─◇ DATE (src/../stdlib/standard/time/date.li) │ ├─◇ HASHABLE (src/../stdlib/standard/property/hashable.li) │ ├─◇ COMPARABLE (src/../stdlib/standard/property/comparable.li) │ ├─◇ SAFE_EQUAL (src/../stdlib/standard/property/safe_equal.li) │ ├─◇ TRAVERSABLE (src/../stdlib/standard/property/traversable.li) │ ├─◇ OBJECT (src/../stdlib/standard/kernel/object.li) │ ├─◇ I_DONT_KNOW_PROTOTYPING (src/../stdlib/standard/kernel/i_dont_know_prototyping.li) │ ├─◇ POINTER (src/../stdlib/standard/kernel/pointer.li) │ ├─◇ CONVERT (src/../stdlib/standard/kernel/convert.li) │ ├─◇ REFERENCE (src/../stdlib/standard/kernel/reference.li) │ ├─◇ BLOCK (src/../stdlib/standard/kernel/block.li) │ ├─◇ HASHED_DICTIONARY (src/../stdlib/standard/collection/hashed_dictionary.li) │ ├─◇ ARRAY2 (src/../stdlib/standard/collection/array2.li) │ ├─◇ AVL_SET (src/../stdlib/standard/collection/avl_set.li) │ ├─◇ LINKED2_LIST (src/../stdlib/standard/collection/linked2_list.li) │ ├─◇ ARRAY3 (src/../stdlib/standard/collection/array3.li) │ ├─◇ ARRAY (src/../stdlib/standard/collection/array.li) │ ├─◇ ITERATOR (src/../stdlib/standard/collection/iterator.li) │ ├─◇ FAST_ARRAY3 (src/../stdlib/standard/collection/fast_array3.li) │ ├─◇ LINKED_XOR_LIST (src/../stdlib/standard/collection/linked_xor_list.li) │ ├─◇ LINKED_LIST (src/../stdlib/standard/collection/linked_list.li) │ ├─◇ HASHED_SET (src/../stdlib/standard/collection/hashed_set.li) │ ├─◇ FAST_ARRAY2 (src/../stdlib/standard/collection/fast_array2.li) │ ├─◇ FAST_ARRAY (src/../stdlib/standard/collection/fast_array.li) │ ├─◇ AVL_DICTIONARY (src/../stdlib/standard/collection/avl_dictionary.li) │ ├─◇ STD_FILE (src/../stdlib/standard/file_system/std_file.li) │ ├─◇ DIRECTORY (src/../stdlib/standard/file_system/directory.li) │ ├─◇ ENTRY (src/../stdlib/standard/file_system/entry.li) │ ├─◇ FILE (src/../stdlib/standard/file_system/file.li) │ ├─◇ HTTP_SERVER (src/../stdlib/standard/http/http_server.li) │ ├─◇ HTTP_HEADER (src/../stdlib/standard/http/http_header.li) │ ├─◇ FALSE (src/../stdlib/standard/boolean/false.li) │ ├─◇ BOOLEAN (src/../stdlib/standard/boolean/boolean.li) │ ├─◇ TRUE (src/../stdlib/standard/boolean/true.li) │ ├─◇ STRING_CONSTANT (src/../stdlib/standard/string/string_constant.li) │ ├─◇ STRING (src/../stdlib/standard/string/string.li) │ ├─◇ ABSTRACT_STRING (src/../stdlib/standard/string/abstract_string.li) │ ╰─◇ CHARACTER (src/../stdlib/standard/string/character.li) ├─◇ CLUSTER_ITEM (src/cluster_item.li) ├─◇ ITM_STYLE (src/itm_style.li) ├─◇ LYSAAC (src/lysaac.li) ├─◇ ITM_AFFECT (src/itm_affect.li) ├─◇ ANY (src/any.li) ├─◇ PARSER_CLI (src/parser_cli.li) ╰─◇ CLUSTER (src/cluster.li)
Lysaac and clusters
If you noticed, I started my own Lisaac compiler, called Lysaac and I want to make it a little bit different from Lisaac. I'll try to keep compatibility, but for few things, I might take a different direction.
One of these things is the way prototypes are found.
In Lisaac, you have a complete set of prototypes and when you look for a
prototype, it is looked everywhere. This is not desirable. Imagine you are
writing a library that requires the prototype FOO. Currently, if FOO is not
present in the library, instead of issuing an error, the compiler would take the
FOO prototype in the application that use the library. Meaning that the
library is effectvely using a pieve of the application code.
I want to take the SmartEiffel approach and separate the source code in few clusters. A cluster is a collection of prototypes. And the prototypes in a cluster can only use the prototype of the same cluster or the prototypes of imported clusters. This solve the above dependancy problem.
A cluster is a directory that contain prototypes in .li files and
subdirectories. If a subdirectory do not contain .li files, the
sub-subdirectories are not recursively searched. A cluster can import another
cluster using a cluster file ending with .cli.
An example of .cli file is as follows:
Section Header
- name := Cluster LIBFOO;
- path := ("libfoo-3.14", "../libfoo");
The search paths can be:
- relative to the
.clifile if it starts with. - relative to
LYSAAC_PATHdirectories otherwise
LISAAC_PATH defaults to $XDG_DATA_HOME/lysaac/lib:/usr/local/share/lysaac/lib:/usr/share/lysaac/lib.
The search paths would then be for this example:
$XDG_DATA_HOME/lysaac/lib/libfoo-3.14/usr/local/share/lysaac/lib/libfoo-3.14/usr/share/lysaac/lib/libfoo-3.14../libfoo
The parser for these files is being written. Then you can see the complete hierarchy of the project:
$ lysaac src
◆ Root Cluster
│ Cluster in: src
├─◆ LIB (src/lib.cli)
│ │ Cluster in: lib
│ ├─◆ STDLIB (lib/stdlib.cli)
│ │ │ Cluster in: /home/mildred/.local/share/lysaac/lib/stdlib
│ │ ├─◇ STRING (...)
│ │ ├─◇ ABSTRACT_STRING (...)
│ │ ╰─◇ ...
│ ├─◇ LIBC (lib/libc.li)
│ ╰─◇ CSTRING (lib/cstring.li)
├─◇ PARSER (src/parser.li)
├─◇ CLUSTER_ITEM (src/cluster_item.li)
├─◇ ITM_STYLE (src/itm_style.li)
├─◇ LYSAAC (src/lysaac.li)
├─◇ ITM_AFFECT (src/itm_affect.li)
├─◇ ANY (src/any.li)
├─◇ PARSER_CLI (src/parser_cli.li)
╰─◇ CLUSTER (src/cluster.li)
Now, each item in a cluster can be public or private. Public items are available to the users of the clusters whereas private items are restricted to members of the same cluster. To declare a private item, just say:
Section Header
+ name := Private PROTOTYPE;
or
Section Header
- name := Private Cluster LIBTOTO;
If you want to declare a whole bunch of prototypes private to your cluster, just include them in a private cluster. To do so, you'll need the following files:
cluster/my_private_prototypes.cli:
Section Header - name := Private Cluster MY_PRIVATE_PROTOTYPES; - path := "./deps/my_private_prototypes"; // makes the cluster relative to the .cli file // use a deps additional directory to avoid the current cluster to // look into deps/my_private_prototypes. deps should not contain any // files, just subdirectories.cluster/deps/my_private_prototypes/private_proto.li:
Section Header + name := Public Prototype PRIVATE_PROTO;
- 1
- latest