Smallest possible micro service - Combines compactness of portable machine code with the expressiveness of high level abstractions, and the power of mathematics.
- Abandon the delusion that there is a best language/method/paradigm to develop software with.
- Embrace computation.
- Embrace data.
- Embrace bottom up.
- Put the few proven, well established and most powerful abstractions to work.
- Restrain your appetite for syntactic sugar.
/* A Planckservice - smallest possible microservice. Implemented using state machines with actions written in Oblectamenta Assembler. */ kind Event; //You can now write Event MyEvent, and MyEvent is known to be a special 'thing' associated with Event kind Guard; // Guards for transitions, again you can now write Guard myguard; kind OblectamentaMsgDefDirective; // Is used in connection with the serialization of messages kind OblectamentaMsgReadDirective; // Is used in connection with the deserialization of messages kind OblectamentaMessageModifier; // Indicates flags modifying the standad behaviour of serialization/deserialization of messages OblectamentaMessageTag i32; // Used when writing/reading message fields which contain 32 bit integers OblectamentaMessageTag i64; // Used when writing/reading message fields which contain 64 bit integers OblectamentaMessageTag f64; // Used when writing/reading message fields which contain 64 bit floats (IEE 754) OblectamentaMessageTag sz; // ... zero terminated strings OblectamentaMessageModifier all; // This one let you iterate over all fields of a given name OblectamentaMsgDefDirective write; // Indicates the serialization of a message OblectamentaMsgReadDirective read; // ... deserialization ... OblectamentaDataLabel msg_buffer, accounts, count, client_count; // Data labels mark the location of data in the global data segment Event evAddAccount, evReplyOK, evReplyNOKTableFull, evPrintTable; //A couple of events used in transitions of state machines val max_accounts = 32; // val introduces a value which is computed beforehand, i.e. during compilation val entry_len = 8; val offs_a = 0; val offs_b = 4; val max_client_requests = 12; oblectamenta{ global{ data{ // here are the global data definitions count; 0; // count is the name of the location which contains a 32 bit integer with value 0 (all bits are 0) accounts; for (e: 1 .. max_accounts){0;0;} // reserves max_accounts of two consecutive 32 bit integers initialized with 0 // for (...) {...} is evaluated during AST expansion, i.e. is a program transformation (meta programming) msg_buffer; for(e : 1 .. 32) {e;} // Reserve 32 consecutively stored 32 bit integers initialized to the values 1,2,3,...,32 client_count;0; // 32 bit integer initialized with 0 }; }; }; sm{ // Our service is modeled as a state machine Service; // Name of the state machine states{Initial;Ready;ProcessRequest;}; // Atomic states Actions{ // Actions are triggered in transitions doAddAccount{ oblectamenta{text{asm{ // yeah, a lot of typing to indicate an assemble routine OblectamentaCodeLabel lbl_table_full; // We will do a goto and need a label (this is the declaration) lea(count); // The Oblectamenta VM implements a hybrid (virtual-)machine architecture, it has plenty of registers and also a compute stack. // lea(count) puts the address of the object labeled count on the top of the compute stack, hence // CS = |&count| (we use the C address of operator &) ldsi32; // Dereference the pointer on the top of CS and push the i32 value on CS // addr =TOP(CS); value = *(int32_t*)addr; CS = |value of count| ldi32(max_accounts); // max_accounts is a constant value, ldi32 pushes the max_accounts on CS // CS = |value of count|max_accounts| blteq(lbl_table_full); // a = pop 32 bit value from CS // b = pop 32 bit value from CS // (CS empty now) // if b <= a jump to lbl_table_full lea(count);ldsi32;ldi32(entry_len);muli32;sti32(R0); // R0 = count * entry_len; lea(count);ldsi32;ldi32(1);addi32;lea(count);stsi32; // count = count + 1 lea(accounts);ldi64(R0);addi64;sti64(R1); // R1 = &accounts + R0 = &accounts[count-1] msg{ // msg{...} is NOT a machine language instruction of any kind, msg is a directive which gets // translated into an Oblectamenta fragment which allows for a compact notation of message serialization/deserialization read; // Indicates that code for deserialization has to be generated msg_buffer; Account{ // The first element of the message is an Account, this innocuous looking struct is translated in a lengthy code fragment // which handles the correct deserialization of a node with the name Account and a variable number of sub nodes. a{ // Account.a i32; // Read a 32 bit signed integer, we denote its value with a ldi64(offs_a); // | ldi64(R1); // | addi64; // | stsi32; // ===> Accounts[count-1].a = a }; b{ // Account.b i32; // Read a 32 bit signed integer, we denote its value with b ldi64(offs_b); // | ldi64(R1); // | addi64; // | stsi32; // ===> Accounts[count-1].b = b }; }; }; assert_empty_cs; // CS should be empty, if not this will trigger an exception which prints a stack trace and terminates the process evReplyOK;halt; // Reply with the event evReplyOK and stop lbl_table_full;evReplyNOKTableFull; // We end up here if and only if the table is full, we reply by sending the event evReplyNOKTableFull };};}; }; }; t{Initial;Ready;}; // state machine transitions immediately to the state Ready t{Ready;Ready;evAddAccount;doAddAccount;}; // We loop on Ready, each time we receive an event evAddAccount we add an account (assuming the message's payload is in msg_buffer) }; sm{ Client; Actions{ doRequest{ oblectamenta{text{asm{ msg{ write; msg_buffer; Account{ a{ldi32(10);lea(client_count);ldsi32;addi32;i32;}; b{ldi32(100);lea(client_count);ldsi32;addi32;i32;}; }; }; lea(client_count);ldsi32;ldi32(1);addi32;lea(client_count);stsi32; // client_count = client_count + 1 evAddAccount; };};}; }; }; states{Initial;Request;WaitForReply;Final;}; t{Initial;Request;}; t{Request;WaitForReply;doRequest;}; t{WaitForReply;Request;evReplyOK;}; t{WaitForReply;Final;evReplyNOKTableFull;}; }; sm{ TablePrinter; states{Initial;Print;}; Actions{ doPrintTable{oblectamenta{text{asm{ OblectamentaCodeLabel lbl_done, lbl_loop; lea(accounts); sti64(R0); // R0 = address of accounts lea(count);ldsi32;sti32(R1); // R1 = number of stored entries ldi32(entry_len); sti32(R2); // R2 = size of enry in bytes ldi32(0); sti32(R3); // R3 = 0 lbl_loop; ldi32(R3);ldi32(R1); // CS = ...|R3(i32)|R1(i32)| blteq(lbl_done); // if R1 <= R2 (<=> #entries <= counter) then gotot lbl_done ldi64(R0);ldi64(R3);ldi64(R2);muli64;addi64; // CS = ...|Address of R3th entry| duptopi64; ldsi32; dbg_print_topi32; discardtopi32; ldi64(offs_b); addi64; ldsi32; dbg_print_topi32; discardtopi32; ldi32(R3);ldi32(1);addi32;sti32(R3); // R3 = R3 + 1 buc(lbl_loop); lbl_done; assert_empty_cs; };};};}; }; t{Initial;Print;evReplyOK;doPrintTable;}; t{Print;Initial;}; }; Simulation{ Start{Service;Client;TablePrinter;}; };