I want to make a library for using (webui)

65 views
Skip to first unread message

Hunar Omar

unread,
Aug 26, 2024, 8:01:11 AM8/26/24
to 4tH-compiler
Hello :)

There is a great project called webui (http://webui.me) that is a single c header file. where you make web based gui applications very easily.
I want to use it in 4tH. what are my options?

If I modified the source code then other people cant easily benefit from the time I spend. and one question: in the manual when extending the compiler, you say "Note that constants with the same length HAVE to be grouped together" is that a must? what would happen if I didn't?

If I made another C code to act as a layer where 4tH talks to it through pipes (like the gtk server) and it talk to the library I want.

what other options are there? since i know FFI isn't an option.

and can I instead make a webserver in 4th myself? I cant find any information about sockets the the pdf manual

thanks :)

The Beez

unread,
Aug 26, 2024, 8:27:14 AM8/26/24
to 4tH-compiler
Hi!

4tH is a complete sandbox - that means it only sees (and can access) things in its VM. E.g. if file access wasn't exposed by OPEN, CLOSE, REFILL, ACCEPT, etc. - there would be no files. The same goes for data. E.g. if WEBUI has structures to be manipulated and you don't write 4tH words to access them, well, you can't access them.

The advantage is you can make 4tH quite bullet proof that way (given you do your homework when error checking). The disadvantage is that you have to map the entire API to 4tH commands (or make a reasonable subset).

The reason 4tH words are grouped together in the compiler (because there is where it counts) is to speed up compilation. You could do that by hashing - which may get complex and error prone. I did that by quickly skipping words based on their length, instead of elaborately testing each single word. So let's say a word has length 6, now we can skip an "x" number of words that are too short, test *ONLY* the words with length 6 - and bombing out when we get to seven-character words.

I added this length a few years ago to the (unsorted) user symbol table and it sped up the *raw* compilation time (that is: including predefined words) by 25%.

Yes, the gtkserver approach is always a possibility - with the added advantage you can use any vanilla 4tH to control it.

There is already a working webserver - but it needs INETD: https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/web4th.4th

So far, I haven't been able to find a *PORTABLE* socket solution - so it's not included. The only non-ANSI-C thing is pipes, and those cost me enough headaches. There is a lib that interfaces with CURL or WGET and allows you to read stuff from the net: https://sourceforge.net/p/forth-4th/code/HEAD/tree/trunk/4th.src/lib/wwwopen.4th

Note 4tH is intended to be used the way you intend to use it - as a scripting language tied to an external API or application. Therefore I keep the number of tokens down, so you can use 150 tokens for yourself. Note you can extend that number by piggybacking commands that do not require a parameter (these will show up as a table or switch() in exec_4th.c).

Hans Bezemer

Hunar Omar

unread,
Aug 26, 2024, 9:52:56 AM8/26/24
to 4tH-compiler

Thanks for the quick reply :) afew followup questions:

so 150 is the limit? I might choose the gtk-server route then (which I have no experience)

In the manual you clearly explained which files to edit (I'm currently trying to add a single word) but is there a specific location to add the external library headers (#include stuff.h) ? or I can put it anywhere I like e.x. comp_4th.c

Do you know any programs that work like the gtk-server but make a web-based gui instead? that would be the best thing for 4th

You do use 4tH to make programs for other people, do they ever request a graphical gui (buttons sliders etc)? if yes, do you always use gtk-server?

I didn't quite get the API part. when I edit/extend the compiler, I get 4th executable with my modifications. but when using the API I get an executable, right? I cant get a 4th editor (i think).

thanks :)

The Beez

unread,
Aug 28, 2024, 11:45:46 AM8/28/24
to 4tH-compiler
Hi!

150 is not the limit, but you'll have to do piggybacking. The only words in 4tH that need an argument are constants, branches, addresses, etc. If you need multiple non-argument words you could write something like

           CODE (MYCMD)     
                           switch (OPERAND) {
                              case (0): /* Do your thing */ break;
                              case (1): /* Do your thing */ break;
                              case (2): /* Do your thing */ break;
                           }
                           NEXT;

BTW, I have *no* specific place to put header files. Use something that is either prescribed by the product you want to interface with or you feel comfortable with. The 4tH header files are always in the working directory.

I know of no programs that work like gtkserver - but web based.

Yes, I use 4tH both professionally (as a matter of fact: today) and for my own pleasure. I'm not much into GUIs. I know there are a few portable ones, but I don't like to do frontend code. Even gtkserver couldn't be brought to run on Windows. I'd like to run my programs where ever I feel like, so the prompt it is for me.

Again, 4tH is basically a library - and 4tH, PP4tH and 4tsH are essentially example programs. You can make any kind of compiler you like - with only a few lines of C. Sure, if you like the vanilla 4tH shell (the Turbo Pascal one), you will have to recompile the editor programs (the Linux build does that automatically), but after that it will still be part of your 4tH compiler - whatever extensions you added. Sure, you'll have to recompile the whole shebang, but it *will* include the standard 4tH program, the editor and all your extensions.

I know of people who added a whole graphical interface, so they can generate pictures on a canvas, read mouse movements, etc. So it can be done IMHO.

Hans Bezemer

The Beez

unread,
Aug 31, 2024, 10:02:49 AM8/31/24
to 4tH-compiler
Now I thought this was an interesting question: how do you encode a structure in 4tH - especially when you want a "builtin" structure.

In fact, it's quite easy: all STRUCT, FIELD: and END-STRUCT do is create a bunch of +CONSTANTs and a CONSTANT for the size of the structure. So if we evaluate this:

    webui_event_t {
        size_t window;        // The window object number
        size_t event_type;    // Event type
        char* element;        // HTML element ID
        size_t event_number;  // Internal
        size_t bind_id;       // Bind ID
        size_t client_id;     // Client's unique ID
        size_t connection_id; // Client's connection ID
        char* cookies;        // Client's full cookies
    };

We can easily translate that to:

0 +constant window
1 +constant event_type
2 +constant element^
3 +constant event_number
4 +constant bind_id
5 +constant connection_id
6 +constant cookies^
7  constant /Event

.. and those can be easily be included in comp_4th() as constants. Now this is a trivial one - only 4tH doesn't take such long labels (although you can easily fix that by modifying a constant):

C:
    enum {
        WEBUI_EVENT_DISCONNECTED = 0, // 0. Window disconnection event
        WEBUI_EVENT_CONNECTED,        // 1. Window connection event
        WEBUI_EVENT_MOUSE_CLICK,      // 2. Mouse click event
        WEBUI_EVENT_NAVIGATION,       // 3. Window navigation event
        WEBUI_EVENT_CALLBACK,         // 4. Function call event
    };

4tH:
0 enum   EVENT_DISCONNECTED
  enum   EVENT_CONNECTED
  enum   EVENT_MOUSE_CLICK
  enum   EVENT_NAVIGATION
constant EVENT_CALLBACK

And now for the payload itself:

void events(webui_event_t* e) {
    if(e->event_type == WEBUI_EVENT_CONNECTED)
        printf("Connected. \n");
    else if(e->event_type == WEBUI_EVENT_DISCONNECTED)
        printf("Disconnected. \n");
    else if(e->event_type == WEBUI_EVENT_MOUSE_CLICK)
        printf("Click. \n");
    else if(e->event_type == WEBUI_EVENT_NAVIGATION)
        printf("Starting navigation to: %s \n", e->data);
}

Now it may be that someone had second thoughts, because there is no "data" member (this won't compile IMHO), so we take "element" instead.

void events(webui_event_t* e) {
    if(e->event_type == WEBUI_EVENT_CONNECTED)
        printf("Connected. \n");
    else if(e->event_type == WEBUI_EVENT_DISCONNECTED)
        printf("Disconnected. \n");
    else if(e->event_type == WEBUI_EVENT_MOUSE_CLICK)
        printf("Click. \n");
    else if(e->event_type == WEBUI_EVENT_NAVIGATION)
        printf("Starting navigation to: %s \n", e->data);
}

And this is it:

: events
  dup -> event_type @
  case
    EVENT_CONNECTED    of ." Connected. "    endof
    EVENT_DISCONNECTED of ." Disconnected. " endof
    EVENT_MOUSE_CLICK  of ." Click. "        endof
    EVENT_NAVIGATION   of
      ." Starting navigation to: " dup -> element^ @ count type space
    endof
  endcase drop cr
;

Yes, we have to initialize the pointers by hand. I don't think C will be different, though. You can use the library and make this dynamic strings (easier and with added security):

/Event array MyEvent

64 string element
64 string cookies

   element MyEvent -> element^ !
   cookies MyEvent -> cookies^ !
s" button" MyEvent -> element^ @ place

Note we added a few extra labels to display (just to be sure). This is the program:

cr ." Connected.." cr
EVENT_CONNECTED MyEvent -> event_type !
MyEvent events

cr ." Disconnected.." cr
EVENT_DISCONNECTED MyEvent -> event_type !
MyEvent events

cr ." Clicking.." cr
EVENT_MOUSE_CLICK MyEvent -> event_type !
MyEvent events

cr ." Navagating.." cr
EVENT_NAVIGATION MyEvent -> event_type !
MyEvent events depth .      \ yes, just to be sure.. ;-)

And this is the output:

$ pp4th -x webui.4th

Connected..
Connected.

Disconnected..
Disconnected.

Clicking..
Click.

Navagating..
Starting navigation to: button
0


Now sure, mapping the 4tH structure to the C structure is another issue. Then you copy the webui_event structure, but make all the members type cell and carefully transfer each member to a webui_event_t struct using the proper casts and conversions before issuing it to the webui API.

And that's how I would approach the issue. The callbacks are another thing - but look closely at 4tsH and see how you can use PAUSE and OUT to communicate between a 4tH programs and a C host. There is also a Wiki page on the subject at Sourceforge.

Hans Bezemer

Timur Aksenov

unread,
Sep 2, 2024, 9:19:27 AM9/2/24
to 4th-co...@googlegroups.com
Hello! 

You can use this approach: return via OUT  abstracted command , for example just string “call func dowork” . Or just operational code (for example 101 mean “do task 101”. On C side you must have associated abstracted commands to real function pointers.  I don’t know well C, but I know in C++ you can use for this std::map<Key, Value>. Where key is abstracted command (operational code or phrase , and value can be callable object like lambda or pointer to function. 

Timur 



Сб, 31 авг. 2024 г. в 17:02, The Beez <the.bee...@gmail.com>:
--
You received this message because you are subscribed to the Google Groups "4tH-compiler" group.
To unsubscribe from this group and stop receiving emails from it, send an email to 4th-compiler...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/4th-compiler/87b8680a-4fa2-41dd-b651-aa3658d6bd05n%40googlegroups.com.

The Beez

unread,
Sep 4, 2024, 1:21:53 PM9/4/24
to 4tH-compiler
Correct! That's exactly the trick 4tsH uses. It plants a code on the shared stack, followed by its parameters. A shared stack was discussed in detail on the following Wiki page:


Hans Bezemer

Timur Aksenov

unread,
Sep 5, 2024, 4:57:50 AM9/5/24
to 4th-co...@googlegroups.com
In Cocos Creator engine used the same abstraction  - just sending 2 strings arg0 and arg1 -




ср, 4 сент. 2024 г. в 20:21, The Beez <the.bee...@gmail.com>:
Reply all
Reply to author
Forward
0 new messages