cortoproject / corto Goto Github PK
View Code? Open in Web Editor NEWA hierarchical object store for connecting realtime machine data with web applications, historians & more
Home Page: https://www.corto.io
License: MIT License
A hierarchical object store for connecting realtime machine data with web applications, historians & more
Home Page: https://www.corto.io
License: MIT License
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.
The code that c_type generated was ugly - very inconsistent with regards to newline characters. It makes the file very unreadable.
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.
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}
.
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:
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 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.
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.
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.
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.
A temporary representation of real-life or abstract concepts that resides in an object store.
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 |
In the traditional sense of the word, any "name" that can be used in code. Identifiers include both objects and variables.
Because every object has a scope, every object works like a namespace. Pure namespaces are void
objects and solely serve the purpose of namespacing.
A package is a *.so file that may be loaded along a script.
May refer to:
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.
The class from which another class can inherit members and behavior. May be referred to as superclass.
Parent may refer to either of a parent class (superclass) or the (hierarchical) parent of an object. In the latter case
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.
Integrate with the Travis continuous integration system. Enabled by #11
The following code:
struct Point:: x, y: uint32;
Point returnPoint() = {10, 20};
returnPoint()[x, y];
Will call returnPoint twice. A fix should store the result of returnPoint in a temporary storage 'T' which is then to be expanded into T.x, T.y.
Turns out that this wasn't such a good idea:
uint32 o;
o.nameof; // calls the nameof method
These are the drawbacks:
Since it's just syntactic sugar, it shouldn't complicate the language. Because it does, it must be removed.
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.
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
Errors referenced in the issue #29 were not collected by the test suite apparently.
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);
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
Add serialization version for JSON interface so that we can introduce (as a last resort, of course) backwards compatibility breaks later in time.
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.
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.
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:
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();
}
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.
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.
When comparing two string literals in a hyve script, the parser will crash because the COND_EQ and COND_NEQ operators are not yet implemented.
The following sections of the code need to be ported to 64 bit:
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.
Two examples here:
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}
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
...
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 new design should satisfy the following requirements:
The following questions are not yet answered:
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:
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.
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.
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.
For example:
error: unable to open output file 'obj/db.o': 'Error opening output file 'obj/db.o': No such file or directory'
Expected to create file and print to it.
Be careful with files such as makefiles.
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 for the following topics (initially):
Semicolons are for parsers, not people!!!!! (https://golang.org/doc/faq#semicolons)
An empty hyve script throws a syntax error. A hyve script with only a comment crashes the parser.
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
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
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;
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.
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:
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.
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 support for C-style multiline comments.
The current tests only test valid code. Additionally, a set of testcases is required that validate whether the parser throws the correct warnings / errors when invalid code is provided.
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.
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.
Information such as platform and Hyve version should be in some scope. I would suggest ::Hyve
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.