Git Product home page Git Product logo

corto's People

Contributors

gitter-badger avatar hendrenja avatar jeffplourde avatar jleeothon avatar sandermertens avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

corto's Issues

Add ternary built-in condition variable.

In ternary expressions often the condition expression is reused in the middle- and right expression. To avoid redundancy (especially with lengthy conditions) the following syntax is proposed:

var object drink = machine.coffee ? ?.cupOf : machine.tea.cupOf;

The ? here is the placeholder for machine.coffee. This provides an elegant method of checking for null references.

Improve code generated by c_type

The code that c_type generated was ugly - very inconsistent with regards to newline characters. It makes the file very unreadable.

Resolving multiple constants in an expression fails

The following code:

bitmask options:: SeatHeating, AutomaticWindows, SpareWheel;
var options v = SeatHeating | SpareWheel;

Has the parser complaining:

tc_bitmask01.hyve:8:41 error: unresolved identifier 'SpareWheel' near 'SpareWheel'

tc_bitmask01 captures this behavior, though could be extended further to test usage of bitmasks in more scenarios.

Specification for serialization of maps

Serialization of maps could be require relying on conventions for certain types because JSON object keys can only be strings (i.e. how to serialize an object of type map{int32, string}.

Replace arrays with sequences in language parser

Currently the Fast namespaces has a number of classes which use arrays in their structures with a worst-case size. Upon inspection instantiations of these classes take up an unnecessary large amount of space. This problem is magnified on 64bit where some structures can have a base-size of 70K.

It should be a relatively small amount of effort to translate these arrays into sequences - in some cases it may even reduce the amount of code. This approach would save potentially a lot of memory during parsing.

Concerned classes are:

  • Initializer
  • StaticInitializer
  • DynamicInitializer
  • Parser

Make JSON serialization options more granular

This can be achieved with binary masks. The serialization options, instead of having a member bool serializeMeta, could be broken down as db_bitmask serializeMeta, that could be set as in:

data.serializeMeta = JSON_SER_META_NAME | JSON_SER_META_TYPE;

Pushing this feature to later on would bring backwards compatibility issues.

Iterating over collections

Iterating over collections is at the heart of working with collections. Because it is anticipated that this feature will be used extensively, it should be expressive, performant, intuitive and concise. It also has to be as uniform as possible for all the supported collection types. Additionally, it als has to be type-safe and its usage should preferably be verifiable at compile time.

When walking over a collection, somewhere something has to keep track of the iteration 'state'. To put simply: when iterating over a collection, I need to keep track of the element that I last iterated over to know where I need to go next. This state is from hereon referred to as the 'iterator'.

Currently iterators are not exposed through the type system. Arguably they don't need to be when all the language would allow for is iterating over the whole of a collection. Note that in the following example it is not apparent that an iterator is used (pseudo-code):

foreach element in collection: "$element";

Clearly there is something in the background that is keeping track of the state of the iteration, but a developer wouldn't see it. Theoretically, iterators could be completely handled by the virtual machine, intermediate code and parser. However, iterators (and the things you can do with it) make sense to have in the language (and language bindings) as well. Therefore iterators need to be captured in the type system.

There are two basic approaches. One changes the type system while the other one doesn't. I'll describe the latter first.

Adding a builtin class

Consider creating a builtin class called 'iterator' in the lang scope. This class would be a generic iterator for all collection types. A (simplified) interface for this class could look like:

class Iterator::
   collection: any;
   bool hasNext();
   any next();

Using this could look like:

list{uint32} someList: 0, 1, 2, 3, 4, 5;
Iterator iter{someList};
while iter.hasNext:
    uint32 value = iter.next;

There are many downsides to this approach. For one, iterators would rely on the invocation of methods, which is not a necessity and therefore slower than what it could be. Because this is such a performance-critical part of the language this is a real issue and one that could not be fixed easily at a later stage.

The second downside is that it doesn't enforce compile time safety. Since the class is a generic iterator it has to rely on the 'any' type to store the collection- and return the element value. Aside from a small performance hit in the usage of any's, this is an issue as well.

The upside of this approach is that it doesn't require modifications to the core type system, which is nice on the short-term, but doesn't weigh up to the the previous considerations.

Adding a new type

The second approach is adding a new type kind "ITERATOR" to the type system. A new type-kind proliferates to the language design since it has to define behavior and syntax for this new type. This allows for a lot of freedom in how iterators are implemented. A consideration is whether it should be a top-level type kind (like PRIMITIVE, COMPOSITE, COLLECTION) or a 2nd level kind (like INTERFACE, STRUCT, CLASS). That way, iterators could leverage some properties from functionality that already exists.

The following code shows a potential iterator usage example:

list{uint32} myList: 0, 1, 2, 3, 4, 5;
iterator{uint32} iter = myList; // Compile-time safety
while iter.hasNext:
    uint32 value = iter.next; // Compiler can verify whether this is allowed

Further thought is required on how to map this in a way so that there is a good balance between reusing existing functionality and leveraging/extending the existing type system features in a way that is useful.

Create a glossary

Documentation is needed to increase adoption of the language+framework. In order to make the code more readable for the dev team and adopters, a glossary could be important to document the small or big differences between Hyve concepts and other languages'. Additionally there is need to disambiguate terms such as "class", "struct", "scope", "variable", etc.

I put the first draft here:
https://github.com/Seldomberry/Hyve/blob/jleeothon/glossary.md

But we may want to fast edit it in this issue before pouring it back into the glossary file.


Glossary

Object

A temporary representation of real-life or abstract concepts that resides in an object store.

Class

A class is an object that can be instanced to permit the creation of other objects. This concept is similar to other languages'.

A class is declared by instancing the class object. For example:

class Dog::
    name: string;

In this example, Dog is a class because it is an instance of class and therefore it can create Dog objects and Dog variables, for example:

Dog myDogObject;
var Dog myDogVar;

Careful! While int32 lets you create objects of type int32, int32 itself is not an instance of class and therefore it is technically not a class. Instead, it is an instance of the int class.

Object Type / Instance of Parent class / Inherits from Can be used to create objects
class class None Yes
Dog class None Yes
GoldenRetriever class Dog Yes
randomDog Dog N.A. No
gijs GoldenRetriever N.A. No
int class None Yes
int32 int None Yes
myNumber int32 N.A. No

Identifier

In the traditional sense of the word, any "name" that can be used in code. Identifiers include both objects and variables.

Namespace

Because every object has a scope, every object works like a namespace. Pure namespaces are void objects and solely serve the purpose of namespacing.

Package

A package is a *.so file that may be loaded along a script.

Scope

May refer to:

  • The scope of an object is the collection of elements that are declared "under" it.
  • The segment of code in which an identifier is usable. See code scope.

Code scope

Is the fragment of code during which a variable lives and its identifier can be used. Code scopes include values of procedures, scope of control flow structures, and pure scopes.

Parent class

The class from which another class can inherit members and behavior. May be referred to as superclass.

Parent

Parent may refer to either of a parent class (superclass) or the (hierarchical) parent of an object. In the latter case

C load routine doesn't work for primitive types

Due to a recent change in the object store load routines now no longer work for primitive types. The change involves setting the state of primitive objects to DEFINED immediately after they are declared - which is safe since they cannot have constructors / destructors.

However, the C load routine is build like this:

foo_o = db_declare( ...

if(!db_checkState(foo_o, DB_DEFINED)) {
    // set value
}

Because primitive values are already defined immediately after declaring them, their value is not set. Load routines have to be changed such that values of primitive objects are set after declaring them.

Remove support for method calls that omit argument lists

Turns out that this wasn't such a good idea:

uint32 o;
o.nameof; // calls the nameof method

These are the drawbacks:

  • It doesn't work nice with overloading (gets hard to parse)
  • It cannot be used for functions
  • It forces defining a new syntax for referring to an instance method (future delegate functionality)

Since it's just syntactic sugar, it shouldn't complicate the language. Because it does, it must be removed.

Enable passing of attributes to generators

To influence behavior of generators using a generic pattern, add the capability to add "attributes" to generators. An attribute will be a name-value pair that a generator tool can set and are made available to a generator.

The following API is suggested to provide this behavior:

db_string g_setAttribute(db_string key, db_string value); // Returns old value
db_string g_getAttribute(db_string key);

This behavior intends to support behavior described in issue #21, to prevent the c_interface generator from generating stubs, even when a function is defined.

Trailing space in "attribute" in dbsh

For clarification:

::hyve::lang $ hyve
name:         ::hyve
type:         ::hyve::lang::object
address:      <0xb772877c>
refcount:     5
state:        V|DCL|DEF
attributes:   S|O // <-- There is an extra space here (but you can't see it)!
parent:       ::
childs:       3

Allow scripts to exit with success or failure

Scripts currently do not have the capability to exit with a success or failure code. Add function "exit" to os namespace that takes a boolean argument, where true means success:

void exit(bool success);

Productize C++ binding

After running the command make in the console, I get:

g++ -O0  -g -Werror -Wall -Wextra -pedantic -std=c++0x -I"/home/johnny/Hyve/db/include" -I"/home/johnny/Hyve/public/c/include" -I"/home/johnny/Hyve/public/cpp/include" -I"/home/johnny/Hyve/infra/include" -I"/home/johnny/Hyve/interface/api/include" src/hyve/lang/Array.cpp -c -o obj/hyve/lang/Array.o
Assembler messages:
Fatal error: can't create obj/hyve/lang/Array.o: No such file or directory
make[2]: *** [obj/hyve/lang/Array.o] Error 1
make[2]: Leaving directory `/home/johnny/Hyve/public/cpp'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/johnny/Hyve/public'
make: *** [all] Error 2

Observer expressions

Currently the language supports observers that subscribe to updates of a single object. The following example illustrates this:

void bell;
on update bell: "somebody rang the bell!";

An observer expression is a sophisticated yet easy-to-understand mechanism that allows triggering on conditions, rather than on objects alone. Consider the following example:

int32 temperature;
on update temperature < 0: "It's getting cold!";

Rather than just providing an object, a condition is provided to the observer. Whenever the object changes, the condition is re-evaluated and the observer is only triggered when the condition evaluates to true. This mode of behavior will be activated when the observer expression is not an object.

Consider the following example:

int32 temperature;
int32 desiredTemperature: 72;
on update temperature < desiredTemperature: "It's too cold";
on update temperature > desiredTemperature: "It's too warm";
on update temperature == desiredTemperature: "It's just right";

This expression consists out of two objects. An update of either could trigger the condition to become true. Therefore this observer should subscribe to both objects and evaluate the expression whenever one updates.

The following example shows how observer expressions can be used to create confusing behavior:

int32 temperature;
int32 desiredTemperature: 72;
bool tooHot(int32 t) = t > desiredTemperature;
on update tooHot(temperature): "It's too hot";

In this example, even though a call to the function "tooHot" could return true, it doesn't necessarily means that the observer will have seen this. This is because "desiredTemperature" is not part of the observer expression. To make this example work, it should be rewritten like this:

int32 temperature;
int32 desiredTemperature: 72;
bool tooHot(int32 t, int32 desiredT) = t > desiredT;
on update tooHot(temperature, desiredTemperature): "It's too hot";

Observer expressions will have to be stored on the observer object. Expressions will be stored as a function. This way observer expressions can theoretically also be written in languages other than hyve. When an observer expression is written in the hyve language the function will contain the compiled VM code.

Make `dbsh_stateStr` and `dbsh_attrStr` publicly available

From dbsh.c, the functions dbsh_stateStr and dbsh_attrStr could be made available for reuse as the standard way of representing the state and attributes of an object, for example for serializers.

Also, what should be a recommended size for the buffer? Lines 129 and 130 in dbsh.c (https://github.com/Seldomberry/Hyve/blob/master/tools/dbsh/src/dbsh.c) seem to suggest that the representation was different than it actually is.

Solution 1. We could define a global size constant, something like #define STATE_STR_MAX (sizeof("S|W|O")). Cons: pollute the global namespace.

Solution 2. Let the functions internally handle the creation of a necessary buffers. Cons: buffer can be internally allocated and externally freed.

Solution 3. Postpone this issue to merge it with defining a buffer allocation strategy.

Create project generator for C language binding

When creating a new C component, the code generator generates interfaces, type definitions, load routines and type-specific object management routines. A user is then still left with creating:

  • A makefile
  • A file that provides the hyvemain entry procedure

Though small tasks, they are a bit tedious and could easily be automated. For a novice user they also steepen the learning curve. It would be desirable if the generator tool could automate these tasks as well.

Proposed a generator that will generate a makefile, and a new source file called Foo__main.c that will contain the following function:

int hyvemain(int argc, char* argv[]) {
    DB_UNUSED(argc);
    DB_UNUSED(argv);
    return Foo_load();
}

Create a test framework

In order to formalize testing and increase overall productivity when writing tests, a supportive framework would be nice. Of course there is a plethora of testing frameworks out there, so one could ask why build one from scratch?

For a number of reasons.

  1. A test framework based on Cortex would work for any programming language that has a binding with the platform. As such, it would ensure conformity across languages in how testcases are build, organized and log their output.
  2. The upcoming web bindings and documentation features would complement the test framework perfectly. Basically, the test framework would be extended with realtime visual feedback for free.
  3. In future releases bindings will be added that allow building truly distributed applications with Cortex. This in essence would enable the framework - again for free - to act as a distributed test engine.
  4. In future releases functionality will be added to the platform that allows for persistent storage of objects. This would, for free, add the capability of the framework to keep track of historic test results.
  5. Keeping the test results around in the Cortex object store will allow users to write scripts based on this data.

In summary, this framework will eventually be language agnostic, scriptable, provide real-time visual feedback on the execution of testcases, can manage running testcases on multiple machines simultaneously and store historical results.

That is combination of features that I have not yet seen with any OTS framework. The framework in itself would become a good reason to use the platform.

The framework would not be used just for testing the platform, but should also be used for testing applications build by end users.

Port source to x86-64.

The following sections of the code need to be ported to 64 bit:

  • The virtual machine
  • The assembly code used to dynamically invoke C-calls

The virtual machine shouldn't be a lot of work. It uses db_uint32 in some of its structs - this should be replaced with db_word.

The assembly code needs to be removed altogether from the project. It must be replaced with (generated) wrapper functions that forward to the actual call. Example:

When defining this function:

uint32 add(uint32 a, uint32 b);

The following wrapper function must be generated:

db_uint32 __add(void *buffer) {
    return add(*(db_uint32*)buffer, *(db_uint32*)((int)buffer + sizeof(db_uint32)));
}

These wrapper functions have to manually coded for the functions in the hyve scope.

The rest of the code of course needs to be tested.

Fix map collection

Two examples here:

  • Printing a map

Source:

map{int16, string} hakkaNumbers: 1: "yit", 2: "gni", 3:"sam";
"${hakkaNumbers}";

Expected output:

{1:yit, 2:gni, 3:sam}
yit

Actual output:

{0,0,0}

  • Accessing an element using bracket notation

Source:

map{int16, string} hakkaNumbers: 1: "yit", 2: "gni", 3:"sam";
"${hakkaNumbers[1]}"

Expected output:

yit

Actual output:

Access violation
[1] <main> (example.hyve:2)

  ...
  152762960: DEALLOC_LR 0 0 0 0
  152762980: PUSH_LR 4 0 0 0
  152763000: DB_VM_CALLVOID 152713796
  152763020: SET_LRV 0 0 152597724 0
> 152763040: ELEMMX_LRV 0 0 1 0
  152763060: STAGE2_LVV 0 0 3077296996 3077298116
  152763080: PCAST_SRQ 4 0 0 0
  152763100: STRCPY_LRR 0 4 0 0
  152763120: PUSH_LR 0 0 0 0
  ...

Delegates need to be revised in order to make them more generically applicable

The requirements for the current implementation of delegates are primarily driven by the type system. Currently, delegates are used in combination with callbacks as a mechanism to invoke functionality upon initialization, creation and destruction of a class instance.

There are a few issues and limitations with this approach:

  • The way delegates are managed by classes is a bit convoluted. Space is allocated for the callback pointers in memory outside of the objects' value - meaning that the object management has to be aware of delegates and callbacks. This is not ideal.
  • Delegates provide no support for wrapping invocations on class instances (methods).
  • Callback procedures are very similar to functions. They (callbacks) can probably be removed and replaced by functions, thus simplifying the overal design.

The new design should satisfy the following requirements:

  1. It should be possible to re-assign delegates after they have been given an initial value.
  2. It should be possible to wrap instance methods.
  3. When calling a delegate that has not been assigned a function, default to the base class delegate value. This will be challenging in combination with (1).

The following questions are not yet answered:

  • Presumably delegates should play nice with overloading, but I have to gather some thoughts on how this should exactly work (maybe it'll "just" work, depending on the design).
  • A delegate currently binds to a type (like a member), which reserves space for a "function pointer" in the type instance. There is currently no way of creating delegates by themselves which is a real limitation. These two behaviors could be solved separately but that introduces additional concepts and eventually seems less intuitive and elegant. One way (the only way?) to have one feature that solves both would be to have function types. This would completely remove the current concept of a delegate and replace it with a new kind of (function) type.
    • Function types are not "the type of a function". They would be the type of values that point to a function.

Make core filenames conformant to C language binding

Currently the core filenames loosely follow the convention of the C language binding, with a few exceptions. It would be preferable to have the core follow the exact convention of the C language binding for a number of reasons:

  • Consistency
  • Generation of wrapper-functions (see #18)
  • Generation of functions that implement forwarding to virtual calls

The generator will currently not generate empty procedure bodies for procedures that are already implemented - it will generate stubs instead. For this particular case, a feature would have to be aded to the generator that prevents generating stubs even when functions are already implemented.

dbsh spells "childs"

As far as I can see, this is the count of elements inside the object's scope. The json serializer (json.c) adopts this convention unless notified change thereof.

Modify build systems so that it automatically builds files in src folder

Makefiles include a full list of source files that have to be compiled for a particular artefact. This is tedious, every time a file is added or removed the makefile must be changed as well. Modify build system so that makefiles don't have to explicitly provide this list of files. Instead, just include all files in source folder.

This will greatly simplify issue #44.

Add support for resolving methods at runtime.

Certain language features such as using variables of an interface types and virtual methods require that the runtime is capable of resolving methods at runtime. Consider the following code:

interface Transport::
    void move(uint32 x, uint32 y);

class Plane: implements:{Transport}::
    void move(uint32 x, uint32 y);

var Transport t = Plane{};
t.move(10, 20); // This call must be resolved at runtime

The type system already provides all features required for validating interface implementations.

This feature must be designed with metaprogramming in mind. Ideally it should be possible that a method created at runtime can also be invoked in the same code.

Create tutorials

Create tutorials for the following topics (initially):

  • dbsh
  • language basics
  • code generation
  • C language binding basics

Class 'X' with member that has list of X crashes virtual machine.

The following code:

class Recursive::
    x : list{Recursive,1};

Recursive o3: {o3};
var Recursive v3 = Recursive{{o3}};

Crashes the virtual machine and dumps the following information:

Access violation
[1] <main> (test.hyve:5)

  ...
  161300980: DEFINE_LV 0 0 161268684 0
  161301000: NEW_LRP 4 0 161229916 0
  161301020: PUSHANY_LQ 4 0 0 161267296
  161301040: PUSHANY_LV 0 0 161268684 161229916
> 161301060: DB_VM_CALLVOID 3077923404 
  161301080: DEFINE_LR 4 0 0 0
  161301100: SETREF_LRR 0 4 0 0
  161301120: FREE_LR 0 0 0 0
  161301140: FREE_LR 4 0 0 0

gdb shows that the crash is caused in this stack:

Program received signal SIGSEGV, Segmentation fault.
0xb7f381b5 in db_llAppend (list=0x0, data=0x807f400) at src/db_ll.c:131
131     db_llNode node = list->first;
(gdb) where
#0  0xb7f381b5 in db_llAppend (list=0x0, data=0x807f400) at src/db_ll.c:131
#1  0xb7f1dbfa in db_list_appendAction (list=0x0, value=0x807f400, userData=0x0) at src/db_collection.c:440
#2  0xb7f1da3b in db_list_do (object=..., element=..., insert=0 '\000', action=0xb7f1dbd0 <db_list_appendAction>, userData=0x0)
    at src/db_collection.c:428
#3  0xb7f1db1c in db_list_append (object=..., element=...) at src/db_collection.c:448
#4  0xb7f99d21 in db_call_cdecl_word () at db_cdecl_x86.s:37
#5  0xb7f1ae00 in db_call_cdecl (f=0xb7fb224c <list_append.o+68>, result=0x0, args=0xbfffac70) at src/db_cdecl.c:94
#6  0xb7f1ac97 in db_call_intern (f=0xb7fb224c <list_append.o+68>, result=0x0, args=0xbfffac70) at src/db_call.c:93
#7  0xb7f1ad11 in db_callb (f=0xb7fb224c <list_append.o+68>, result=0x0, args=0xbfffac70) at src/db_call.c:115
#8  0xb7f71aa0 in db_vm_run_w_storage (program=0x80857c8, reg=0xbfffe900, result=0x0) at src/db_vm.c:1419
#9  0xb7f57938 in db_vm_run (program=0x80857c8, result=0x0) at src/db_vm.c:1475
#10 0xb7c5de09 in Fast_Parser_toIc (_this=0x8074cb8) at src/Fast_Parser.c:169
#11 0xb7c64557 in Fast_Parser_parse (_this=0x8074cb8) at src/Fast_Parser.c:2269
#12 0xb7c2e014 in fast_hyveRun (file=0xbfffece4 "test.hyve", udata=0x0) at src/fastmain.c:34
#13 0xb7f38dc8 in db_load (str=0xbfffece4 "test.hyve") at src/db_loader.c:142
#14 0x08048769 in main (argc=2, argv=0xbfffef94) at src/hyve.c:32

Invoking .typeof.nameof on objects throws exception

The following code:

@::.typeof.nameof;

throws the following error:

critical:  db_nameof: object 0xbfa17ec0 is not scoped.
  /home/sander/Hyve/bin/liblang.so(db_criticalv+0x61) [0xb76870b1]
  /home/sander/Hyve/bin/liblang.so(db_critical+0x21) [0xb7686f71]
  /home/sander/Hyve/bin/liblang.so(db_nameof+0x6c) [0xb768f75c]
  /home/sander/Hyve/bin/liblang.so(db_type_nameof+0x2e) [0xb76ab65e]
  /home/sander/Hyve/bin/liblang.so(db_call_cdecl_word+0x45) [0xb76eed21]
  /home/sander/Hyve/bin/liblang.so(db_call_cdecl+0xe0) [0xb766fe00]
  /home/sander/Hyve/bin/liblang.so(+0x40c97) [0xb766fc97]
  /home/sander/Hyve/bin/liblang.so(db_callb+0x31) [0xb766fd11]
  /home/sander/Hyve/bin/liblang.so(+0x9787d) [0xb76c687d]
  /home/sander/Hyve/bin/liblang.so(db_vm_run+0x48) [0xb76ac938]
  /home/sander/Hyve/bin/libfast.so(Fast_Parser_toIc+0x3b9) [0xb73b2e19]
  /home/sander/Hyve/bin/libfast.so(Fast_Parser_parse+0x157) [0xb73b9567]
  /home/sander/Hyve/bin/libfast.so(fast_hyveRun+0x64) [0xb7383024]
  /home/sander/Hyve/bin/liblang.so(db_load+0xf8) [0xb768ddc8]
  hyve() [0x8048769]
  /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb7477a83]

Abort
[1] <main> (test.hyve:1)

  156923416: PUSHANY_LV 0 0 3077552752 3077550956
  156923436: CALL_LR 0 0 0 3077579504
  156923456: PUSHANYX_LR 0 0 0 3077575056
> 156923476: CALL_LR 0 0 0 3077578964
  156923496: DEALLOC_LR 0 0 0 0
  156923516: STOP

Replace ':' with '=' in complex literals

Initializers are written like this:

class Point::
    x, y: uint32;

Point p: x:10 y:20;

The semantics of the ':' operator in this context are confusing. The first colon (after the 'p') means: initialize and define the object, while the other two colons are just assignments. To correct this inconsistency, colons in key-value pairs should be replaced with assignments, so that the above statement becomes:

Point p: x=10 y=20;

Replace assembly code responsible for doing generic C calls

The file db_cdecl_x86.s contains assembly code that is capable of generically invoking subroutines without knowing their signature at compile-time. This is something that is not possible in C, hence the need for assembly (you can't manipulate the stack directly in C). Note that this is a problem only in C and C++.

The assembly however makes it very hard to port the code other platforms / architectures / compilers. To encourage porting the code to other platforms the dependency on assembly code must be removed.

An alternative approach is to wrap the calls in an interface that is mandated by the language binding. Consider the following C-function:

db_uint32 multiply(db_uint32 a, db_uint32 b) {
    return a + b;
}

Suppose that now a script run by the virtual machine wants to call this function. Since the virtual machine doesn't know this function and doesn't know its signature it cannot call the function directly. However, when this function would be wrapped into a mandated interface:

db_void __multiply(void *args, void *result) {
    *(db_uint32*)result = 
        multiply(*(db_uint32*)args, *(db_uint32*)((intptr_t)args + sizeof(db_uint32)));
}

... the function could be generically called from anywhere even when the callee signature is not known by the caller. This approach is likely to be faster than the assembly approach since the assembly had to figure out based on metadata how to call the function, while this approach hard-codes the mapping to a generic interface.

The only downside of this approach is that wrapper functions have to be created and executable footprint is increased by a little bit. Wrapper functions can be generated though so this wouldn't be visible for a user.

The wrapper function pointer will be stored in the "impl" field of the function.

Review & slim down public API's

The public API's have evolved over a long period and I have a feeling that they could be reduced in both size and strategies employed to pass memory around.

The overall goal is to make the API as small and intuitive as possible as this will determine for a great part the usability and learning curve for the platform. The following things need to be addressed:

  • naming scheme
  • memory allocation strategy
  • string strategy
  • usage of variable argument lists
  • consistency with C language binding
  • error handling / reporting
  • ...

db_object.h contains the vast majority of API calls. There is a number of other files that have functions to do error reporting, time management, synchronization etc. but these arguably do not need to be exposed. The intend of this platform is not to provide a complete platform abstraction API for users though functionality build with the platform should be platform independent.

Impossible to ensure or check that 'this' of a metaprocedure is a reference.

Metaprocedures like nameof and parentof require an object as a 'this' pointer. These procedures are defined on the 'type' class, so currently they can be invoked on any value including variables - which are not necessarily references! Since there is no way to determine for the metaprocedures whether this points to an object, any attempt to invoke these methods on a non-object value will result in a crash.

Proposed solution is to add a member to a metaprocedure indicating whether it should be invoked on reference values. Another solution would be to annotate any-values with whether they are references or not.

Solution 1 is favorable since it enables checking at compile time. Solution 2 would make any-values more error prone (you'd now always have to indicate whether the value is a reference or not) and would only be detectable at runtime.

Add declare, destruct and new procedures.

To facilitate meta programming, create the following procedures (possibly more will be added later):

metaprocedure object declare (string name, typedef type);
metaprocedure object declare (string name, typedef type, lang::attr attributes);
metaprocedure void destruct();
metaprocedure object resolve (string name);
metaprocedure object lookup (string name);
function object hyve::new (typedef type);
function object hyve::new (typedef type, lang::attr attributes);

Note that the 'new (typedef type)' is already natively supported by the language with the Type{value} syntax.

Generator for reference documentation

Since the object store is fully reflective it serves as ideal purpose for generation of reference documentation. Doing so ensures conformity throughout all of the documentation artifacts. It is also a lot less labour intensive to create and (especially) maintain.

A generator for reference documentation would work like a regular code generator and would use the generator API. This has as advantage that the dbgen tool can be used to generate the documentation.

The output of the code generator will be a set of HTML, CSS and possibly JavaScript files that together will form the documentation. At a later point in time generators could be developed that output to different formats but HTML has the widest applicability thus is the preferred format for now.

A good reference for what this could look like is the Qt online reference documentation: http://qt-project.org/doc/qt-5/reference-overview.html

Note that the documentation generated from the object store alone is not sufficient and merely provides the "scaffolding". It should be possible to annotate objects in the store with text to provide more context and explanation. This possibly requires a small extension of the object management, depending on the design.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.