Friday, April 25, 2025

Cookies in Golf applications, plus HAProxy!

Cookies are used in web development to remember the state of your application on a client device. This way, your end-user on this client device doesn't have to provide the same (presumably fairly constant) information all the time. A "client device" can be just a plain web browser, or it can be anything else, such as any internet connected device.

In practicality, cookies are used to remember user name, session state, preferences and any other information that a web application wishes to keep on a client device.

So cookies are quite important for any web application or service. In this example, we'll set a single cookie (name of the user), and then we will retrieve it later. Very simple, but it's the foundation of what you'd need to do in pretty much any application.

First, in a separate directory, create an application "yum" (as in cookies are delicious):
gg -k yum

Next, create this source code file "biscuit.golf" (for everyone outside the US who feels it should have been "biscuit" instead of "cookie"):
 begin-handler /biscuit
     get-param action

     if-true action equal "enter-cookie"
         // Display a form to get cookie value
         @<h2>Enter your name</h2>
         @<form action="<<p-path "/biscuit">>" method="POST">
         @    <input type="hidden" name="action" value="save-cookie">
         @    <label for="cookie-value">Your name:</label><br/>
         @    <input type="text" name="cookie-value" value=""><br/>
         @    <br/>
         @    <input type="submit" value="Submit">
         @</form>

     else-if action equal "save-cookie"
         // Submittal of form: save the cookie through response to the browser
         get-param cookie_value
         get-time to cookie_expiration year 1 timezone "GMT"
         set-cookie "customer-name" = cookie_value  expires cookie_expiration  path "/"
         @Cookie sent to browser!
         @<hr/>

     else-if action equal "query-cookie"
         // Web request that delivers cookie value back here (to server); display it.
         get-cookie name="customer-name"
         @Customer name is <<p-web name>>
         @<hr/>

     else-if
         @Unrecognized action<hr/>
     end-if
 end-handler

The code is pretty easy:

Thursday, April 24, 2025

Golf 470 released

Minor maintenance version:
  • Reworked watch file per debian guidelines. 
  • Changed debhelper version for debian.

Wednesday, April 23, 2025

Golf 465 released

 This is a maintenance release:

  • Additional safety features when linking (bindnow, relro).
  • Fixed build issue with busy files in rare cases.
  • Removed LICENSE files from packaging per Debian guidelines since debian/copyright already has this information.
  • Move text files to /usr/share/doc per Debian guidelines.
  • Cleaned up debian control, copyright, rules and changelog files per Debian guidelines.
  • Added watch file per Debian guidelines.

Sunday, April 20, 2025

Golf 452 released

This is a minor release. 

  • Debian packaging changes needed for inclusion to Debian.
  • Minor documentation improvements.
  • RPM spec file changes needed to automatically build debuginfo package.

Saturday, April 19, 2025

Example of auto status checking with Golf

When talking about safety in programming, it is memory safety that usually takes the spotlight. However, here we'll touch on another common safety issue that typically causes application logic errors, meaning your program won't function correctly. It's about checking the status of any kind of statement that provides it. Not checking the status could mean that the result of a statement isn't correct, yet your program is proceeding as if it were. Of course, this is bound to cause problems, and this is true for any programming language.

Golf now has a built-in mechanism to help prevent issues like this. The approach taken (at this time at least) is not to force the developer to check status for every statement. Rather if status isn't checked, your program will stop in an orderly fashion and tell you exactly in which source file and which line number you didn't check the status that may cause an issue. It's a default fine line between code bloat and safety. Not every status needs to be checked. And this approach helps pinpoint the most pressing spots in your code where failure is the most likely to happen.

Another future enhancement is in the queue, which would allow (as an option) to force status checking on all statements.

Put the following code in file "file-name.golf" in a separate directory:
 %% /file-name public
     silent-header // don't output HTTP header
     get-param fname // get file name we'll read
     change-dir run-dir // change to directory we ran this program from
     read-file fname to file_content // read file
     // Transform file contents
     match-regex "File: ([0-9]+).imp" in file_content replace-with "\\1" result res cache
     // Print out the result
     p-out res
 %%

This will take file name ("fname") as an input parameter, read that file, and transform any text in it that starts with "File: " followed by a file name that's made up of digits (without an extension), and outputs those file names. So a bit contrived example, and arguably the point here has nothing to do with matching regex patterns. But it's an example from a real-world application, so I plugged it in just the same.

So, let's say the input file is "myfile" with contents:

Friday, April 18, 2025

Golf 423 released

  • New "run-dir" clause added to change-dir statement. It changes current working directory to the directory when command-line program ran from. This is useful for command-line programs. For service applications (i.e. running as servers), this clause has the same effect as the existing "home" clause.
  • Added source file name and line number to the error message that is emitted when status was not checked for a statement *and* statement failed. This clearly indicates where the issue is.

Thursday, April 17, 2025

Golf 419 released

 This release is mostly maintenance and a few bug fixes:

  • Fixed a number of typos and misspellings in the documentation.
  • Added Debian apt install, using OBS (Open Build Service) from OpenSUSE.
  • Fixed bugs with Fedora rpm spec file: debug directory explicitly created, and removed unneeded sudo for SELinux setup.
  • 'make clean' removes all artifacts now, including a few that have been missed before.
Overall, the biggest improvement is the addition of Debian build, which includes modernizing the debhelper version used, as well a clean lintian.

Monday, April 14, 2025

New status-safety feature

 Here's a link that describes in a bit more detail the new "status-safety" feature of Golf:

https://dev.to/golf-lang/major-new-golf-version-is-out-that-adds-status-safety-2409

This feature is available now with Golf 397. 

Golf 397 released

This is a major Golf release. The most notable features added are a new safety layer aimed at preventing logical errors in code, as well as directory support. The full list is here:

  • Added a number of statements for directory support (change-dir, delete-dir, new-dir)

  • Added change-mode, a statement that changes permissions mode for files and directories

  • Added back support for Arch/Manjaro builds (after building overhaul)

  • Added advanced application safety checks: it enforces status checking for statements that may cause serious application logic errors. This is done by checking for negative status outcome at run-time, but only if your code does not check for status, and by stopping the application if it happens. This provides for much safer application run-time because it prevents further execution of the program if such outcome happens, and it also forces the developer to add necessary status checks when needed. This feature is automatic and has an extremely low impact on performance.

  • Added octal and hexadecimal number support

  • delete-cookie now returns GG_OKAY or GG_ERR_EXISTS for more streamlined error checking

  • Files are now created with default mask of 600 and directories with 700 for added security

  • You can now use standard Linux constants, as defined in C "errno.h" include file to compare the error code obtained with get-req. For instance, you can use constants like EACCES or EEXIST. To see what's the meaning of errno number or name, use standard Linux utility "errno".

  • Added ability to get current working directory, by using "directory" clause in get-req

  • Added "end-of-file" clause in read-file statement, which makes it easier to see if a short read is due to end of file.

  • Fixed error in documentation: "count" clause was missing in split-string. Various documentation improvements.

  • Added ability to get permission mode for a file or directory (such as 0750) in stat-file statement via "mode" clause, which is reworked to be able to produce multiple queries about a file at once

Friday, April 11, 2025

New upcoming safety features

While Golf is already a memory-safe language, the work is in progress to add an additional safety layer.

This safety will internally check the status of all statements when you don't check it yourself, and your program will stop if such a statement returned a failure. This may protect your application from many forms of application logic errors.

The safety check will happen at run time, and will only kick in if there's an actual issue. Thus, the impact on your applications should be very small or non-existent, and would not require any source code changes.

The run-time cost of these checks should be close to zero, because Golf statements which encompass vast majority of run-time logic, are both written in C and produce native executables from generated C code (for performance), and are well-tested against memory and other failures (for safety). For that reason, just like with memory-safety, these checks will operate only on statements output (i.e. the result), which is a tiny bit of run time cost.

This improvement should be available in the near future, perhaps even in the next major release.  

Thursday, April 10, 2025

How to start, stop or restart Golf application server

To start an application server for your Golf Application, use mgrg:

mgrg <app name>

This will start your server in "dynamic" mode, meaning the number of processes will vary to accommodate the incoming request load.

You can control your server's resource utilization in dynamic mode by specifying the minimum and maximum number of server processes allowed:

mgrg --min-worker=10 --max-worker=100 <app name>

Saturday, April 5, 2025

How to build OpenSUSE zypper package for Golf

First, install the RPM tools:

sudo zypper --non-interactive in --replacefiles --force-resolution rpmlint sshpass rpm-build rpmdevtools wget

Then get the spec file:

wget 'https://github.com/golf-lang/golf/blob/main/golf.spec?raw=true' -O golf.spec

Create RPM build tree:

rpmdev-setuptree

Fetch the Golf source code:

rpmdev-spectool -g -R golf.spec

Install dependencies (zypper doesn't have the tool for this like apt or dnf, so we just parse golf.spec, which is the next best thing):

sudo zypper -n install --replacefiles --force-resolution $(rpmspec --parse golf.spec | grep BuildRequires|sed 's/^.*:\(.*\)$/\1/g')

Build the package:

rpmbuild -ba golf.spec 

In the home directory, this will produce file ~/rpmbuild/RPMS/x86_64/golf-<version>-1.x86_64.rpm, where <version> is the Golf's current version, and you can now copy that file to another system with the same Linux installed, and install Golf with (ignore the fact that we didn't sign the package):

sudo zypper install ./golf-<version>-1.x86_64.rpm  

Wednesday, April 2, 2025

Fragmentation of Linux and impact on packaging

Linux fragmentation (meaning many distros out there), while positive in some ways (spurring innovation, customization and freedom of choice), makes it more difficult to properly address diverging packaging methods between them. There are services online that make it possible to manage packaging for Linux, and some are quite easy, while some are (incredibly) difficult to use.

What's in packaging? For one, the list of third party packages that your package depends on.

Monday, March 31, 2025

Golf 373 released

  • Fixed issues with SELinux where security policy wouldn't be set correctly.
  • Removed Arch from the list of distros.
  • Rearranged methods of installation to allow direct installation using apt and dnf.
  • Removed lintian errors and warnings

Saturday, March 29, 2025

FIFO in Golf

Golf has built-in FIFO list type. You can create a FIFO variable and then store key/value string pairs in it, which you can then read back in the order you put them in. You can also rewind the FIFO list and obtain the same key/value pairs over and over again if needed.

This is an example of FIFO list usage. This small command-line application will store key/value pairs "key1"/"value1" and "key2"/"value2" and then obtain them twice.

First, let's create a directory for our application:
mkdir test-list
cd test-list/

And then create the application itself:
gg -k list-app

Copy and paste the Golf code below to file "list.golf":
 begin-handler /list
     silent-header

     // Create a list
     new-fifo mylist

     // Add data to the list
     write-fifo mylist key "key1" value "value1"
     write-fifo mylist key "key2" value "value2"

     start-loop
      // Get data from the list
      read-fifo mylist key k value v status st
      // Check if no more data
      if-true st not-equal GG_OKAY
          break-loop
      end-if
      @Obtained key <<p-out k>> with value <<p-out v>>
     end-loop

     // Go through the list again, use rewind-fifo for that
     rewind-fifo mylist
     start-loop
         read-fifo mylist key k value v status st
         if-true st not-equal GG_OKAY
             break-loop
         end-if
         @Again obtained key <<p-out k>> with value <<p-out v>>
     end-loop

     // Delete FIFO list
     purge-fifo mylist
 end-handler

Sunday, March 23, 2025

How to debug Golf programs with gdb

To include debugging information in your application executable (be it application server or a command-line), use "--debug" option, for instance when build the application:
gg -q --debug

This will include debugging information in your application code, so it can be debugged with gdb. You can debug your Golf program with gdb just as if it were a plain C program, which technically it is. This makes it much easier to get to the bottom of any issue directly without having to deal with virtual machines, p-code translation, assembler etc.

For this reason, the debugging ecosystem for Golf programs is already fully developed. For instance you can use Valgrind or Google ASAN with Golf programs just as you'd with a C program.

Note that in order to debug the Golf itself, it needs to have debug information, so either has to be compiled from source with debug information included, or you need to use the included debugging information (if installed from a package). This will be covered in another article here on Golf blog.
Example
Here's an example of debugging using Golf. We'll create a little parsing application to illustrate.

Create an application "split" (since you'll be splitting a URL query string into name/value pairs):
gg -k split

Create a source file "parse.golf" and copy this:
 begin-handler /parse
     silent-header
     set-string str = "a=1&b=2&c=3"
     // Split string using "&" as a delimiter
     split-string str with "&" to pair count pair_tot
     start-loop repeat pair_tot use pair_count
         read-split pair_count from pair to item
         // Now split each item using "=" as a delimiter
         split-string item with "=" to equal
         read-split 1 from equal to name
         read-split 2 from equal to value
         // Output each name/value
         pf-out "Name [%s] value [%s]\n", name, value
     end-loop
 end-handler

This program will parse the string "a=1&b=2&c=3" to produce name/value pairs - this is obviously a parsing of URL query string. It uses split-string statement which will split the string based on some delimiter string, and then you use read-split statement to get the split pieces one by one. Very simple.

Compile and link this:
gg -q --public --debug

What's what here? First of all "-q" will make your project (meaning compile and link it, both command-line executable and application server).

"--public" makes all handlers public, meaning they can be called directly from an outside caller.

"--debug" will add debugging information, so we can use gdb to debug this program. This is crucial to use gdb for debugging.
Debugging
Okay so now to execute this with gdb, do this:

Wednesday, March 19, 2025

Maximum file size uploaded to Golf server

By default, maximum file size you can upload to Golf application server is about 25MB. You can change that by using "--maxupload" option in gg command line utility when you build your application:

gg -q --maxupload=100000000

In this case, maximum size of an uploaded file is about 100MB.

In order to know in your application (at run-time) what is this limit, use get-app:

get-app upload-size to up_size
pf-out "Upload size is %ld\n", up_size

For an example of uploading files, see file manager example.

Sunday, March 16, 2025

Golf 324 released

These changes do not affect functionality, nor they fix any bugs. They are related to packaging of Golf, making it more compliant and smaller. They also allow debugging of production issues with the inclusion of debugging symbols, without compromising the performance.
  • Changes to debian packaging and build process to get a clean lintian check.
  • Removed HTML documentation from the installation packages, since man pages included cover it entirely, and the web pages are available on the web for viewing or download (see also single page documentation).
  • Debugging (.dbg) files are now included in all builds (in debian as a separate package, in others as debugging symbols files included in the package).
  • For manual install, stripping of all executables and libraries is done, along with creation of .dbg (debug symbol) files, to be used in debugging with gdb (as core or addr2line). Note this is also done for any other packaging (rpm, zypper, pacman).

Tuesday, March 11, 2025

Golf 297 released

  • Fixed bug where Golf build fails with libxml2 errors on some platforms. The issues comes from libxml2 changing const-ness of its error handling function at some point.

Golf 295 released

  • Fixed fedora.spec build file for Fedora/RedHat/OpenSuse/Mageia/Amazon Linux etc. Previous release removed some unnecessary files for regex feature, and that broke the COPR build for these platforms. This fix is to have a successful build.

Monday, March 10, 2025

Golf 288 released

This is a major Golf release. First and foremost, it adds XML support. Read below for any other changes that may affect you.
  • Added XML support. See xml-doc and read-xml statements for parsing and using XML documents.
  • Changed statements and clauses that use "utf8" to simply use "utf". The reason is that Golf supports both UTF8 and UTF16 already, and stating just "utf8" doesn't reflect that. Statements "text-utf8" and "utf8-text" are renamed to "text-utf" and "utf-text". These statements allow conversion from Unicode to UTF and back.  In addition, "utf8" clause of "match-regex" is now simply "utf".
  • When using MariaDB, the connection will

How to know Golf version

To get Golf version, use "-v" flag to gg utility:

gg -v

It might produce something like this, showing Golf version (in this case 285) and Operating System version it's running on (in this case Ubuntu 24):

Golf 285 on ubuntu (24)
...


Sunday, March 2, 2025

Golf 273 released

  • Fixed two bugs that prevented compilation of Golf program in some cases. The issues were introduced in version 265.
  • Set proper permissions on pcre2* files for packaging

Saturday, March 1, 2025

Web framework for C programming language

Golf has an "extended" mode, which allows for C code to be used directly. Normally, this is not allowed, but if you use extended-mode statement in your .golf file, then you can call C code from it.

In extended mode, Golf is effectively a web framework for C programming language; read this for more about implications on memory safety.

Here's the example of using Golf as a web framework for C. In this case it's a simple C function to calculate the factorial of a a number. Then we'll expose this functionality in an equally simple web application.
Create the Golf application
mkdir -p c-for-web
cd c-for-web

Create "fact" application ("-k"):
gg -k fact

Save the following into a file "calc-fact.golf". This is the web service which will interact with web clients (such as browsers); it will also call the C function we'll create a bit further below:
 extended-mode // extended mode in order to call C code

 %% /calc-fact public
     get-param par // get input parameter from URL
     string-number par to num // convert to number
     set-number result
     call-extended factorial(num, &result) // call C function
     // Output result
     @Factorial of <<p-out par>> is <<p-num result>>
 %%

Add C code to Golf application
Next, create C file "factorial.c". This is the C function we're calling from Golf code above:
 #include "golf.h"

 // Compute factorial of f, and store result into res
 void factorial(gg_num f, gg_num *res)
 {
     *res = 1;
     gg_num i;
     for (i = 2; i <= f; i++) {
         *res *= i;
     }
 }

Monday, February 24, 2025

Make RPM package on Fedora and RedHat

To create a Golf  installation package for Fedora or RedHat (or similar based on those, like Rocky), you need to get Golf RPM spec file. 

For Redhat/Rocky/etc. install EPEL:

sudo dnf install epel-release

Next, install rpm-build package:

sudo dnf install rpm-build

Sunday, February 23, 2025

DEB package on Ubuntu and Debian

If you'd like to create a Golf  installation package for Ubuntu or Debian (or similar based on those), first get Golf source code. To do that, install git first:

sudo apt update
sudo apt install git

Then get Golf source code (use debian upstream folder):

git clone https://github.com/golf-lang/golf.git
cd golf
mv debian-upstream debian

Before you can create a package, you must first install Debian tools:

sudo apt update
sudo apt -y install devscripts equivs build-essential lintian

Next, get the dependencies for Golf (i.e. packages needed to build it):

mk-build-deps -i -s sudo debian/control

Golf 261 released

  • Added Fedora spec file for building RPM packages, and for upcoming COPR builds on Fedora infrastructure cloud service.
  • Fixed a compilation issue with RedHat regarding _GNU_SOURCE flag.
  • Refactor Golf memory code for about 10% better performance in memory-access intensive applications.  

Friday, February 21, 2025

34000 requests per second on a modest laptop

Here's a video showing how to create and start an application server, and how to connect to it and make requests from a C client (or from any language that supports C API extension):


Create new directory for the Golf server and also for C API client:
mkdir -p srv-example
cd srv-example
mkdir -p client

Create file "srv.golf" and copy this:
 begin-handler /srv public
     silent-header
     @Hello world!
 end-handler

Create Golf application server:
gg -k hello

Build Golf application server (exclude client directory as it contains C API client):
gg -q --exclude-dir=client

Start the application server (a single-process server in this case):
mgrg -w 1 hello

Next, go to C API client directory:
cd client

Then create C file "cli.c" and copy this:
 #include "gcli.h"

 int golf_client (gg_cli *req, char *connection, char *method, char *app_path, char *request, char *url_params);

 int golf_client (gg_cli *req, char *connection, char *method, char *app_path, char *request, char *url_params)
 {
     memset ((char*)req, 0, sizeof(gg_cli));
     req->server = connection;
     req->req_method = method;
     req->app_path = app_path;
     req->req = request;
     req->url_params = url_params;
     return gg_cli_request (req);
 }

 void main ()
 {
     int i;
     for (i = 0; i < 1000000; i++)
     {
         gg_cli req;
         int res = golf_client (&req, "/var/lib/gg/hello/sock/sock", "GET", "/hello", "/srv", "/");
         if (res != GG_OKAY) printf("Request failed [%d] [%s]\n", res, req.errm);
         else printf("%s", gg_cli_data(&req));
         gg_cli_delete(&req);
     }
 }

Compile the client:
gcc -o cli cli.c $(gg -i) -O3

Run it:
./cli

The result is "Hello world!" 1,000,000 times from each request invocation.

Wednesday, February 19, 2025

Golf 253 released

  • Fixed a memory leak that happens in rare situations with internal memory reallocation.
  • Improved performance with request memory management.
  • Improved performance with queries (across all databases, PostgreSQL, MariaDB, SQLite) with reduced number of memory copies and memory allocations.

Sunday, February 16, 2025

Golf 247 released

  • Fixed bug with lists, where if process-scoped, data retrieved with read-list statement may in some rare cases be inaccurate.
  • About 2% speed-up in request execution, due to refactoring of memory cleanup at the end of each request.

Thursday, February 13, 2025

Golf 244 released

  • This release brings much faster memory for large-data servers, for instance servers that hold millions of rows organized in trees, hashes etc. The change is about separating ordinary memory used by a request (such as variables) from the process-scoped memory (which can hold lots of data). The performance improvement is mostly in a high-load environments, for instance when there are tens of thousands of requests per second inserting, deleting or querying data.  

Wednesday, February 12, 2025

Use C language API to talk to Golf Server

Golf application server can be accessed via C API. Most programming languages allow for C linkage, so this makes it easy to talk to Golf server from anywhere. The Client-API is very simple with just a few functions and a single data type. It's also MT-safe (i.e. safe for multi-threaded applications).

In this example, a Golf server will use a tree object to store key/value pairs, which can be added, queried and deleted for as long as the server is running (i.e. it's an in-memory database, or a cache server). Client will insert the key/value pairs, query and delete them.
Create a server and start it
To get started, create a directory for this example and position in it:
mkdir -p c-api
cd c-api

Save this into a file "srv.golf":
 begin-handler /srv public
     silent-header
     do-once
         new-tree ind process-scope
     end-do-once
     get-param op
     get-param key
     get-param data
     if-true op equal "add"
         write-tree ind key (key) value data status st
         if-true st equal GG_ERR_EXIST
             @Key exists [<<p-out key>>]
         else-if
             @Added [<<p-out key>>]
         end-if
     else-if op equal "delete"
         delete-tree ind key (key) value val status st
         if-true st equal GG_ERR_EXIST
             @Not found [<<p-out key>>]
         else-if
             @Deleted, old value was [<<p-out val>>]
         end-if
     else-if op equal "query"
         read-tree ind equal (key) value val status st
         if-true st equal GG_ERR_EXIST
             @Not found, queried [<<p-out key>>]
         else-if
             @Value [<<p-out val>>]
         end-if
     end-if
 end-handler

Create "index" application ("-k"):

Tuesday, February 11, 2025

Golf 241 released

  • This release improves memory handling, which is now faster. Also, several issues with process-scoped memory, such as leaking in some cases, has been resolved. 
  • delete-string will now always delete ordinary (non process-scoped) memory if not referenced prior to deletion, otherwise memory is always released at the end of request. This strikes a good balance between the ability to delete memory while request is executing (if needed), and at the same time, have an automatic memory de-allocator and safety mechanism that's high-performance because it's very lightweight. 

Saturday, February 8, 2025

Golf 231 released

  • Fixed memory issue with long running server processes when using process-scoped memory with a tree object. The problem would in some situations utilize more memory than needed. This fixes the issue and improves performance.  
  • Option "--optimize-memory" has been removed from gg utility due to adding an overhead for the benefit that's generally proven negligible.

Tuesday, February 4, 2025

Golf package page on AUR for Arch Linux

Golf's AUR page is https://aur.archlinux.org/packages/golf

You can build a pacman package from it, and install Golf from that package (on this or other machines):

git clone https://aur.archlinux.org/golf.git
cd golf
makepkg -sirc

Sunday, February 2, 2025

Ubuntu apt package available for Golf

You can install Golf from precompiled binaries provided by Launchpad which is Ubuntu service that builds Golf directly from its github source code repo.  

You would add Golf repo:

sudo add-apt-repository ppa:golf-lang/golf
sudo apt update

And then install Golf with:

sudo apt install golf

You can then manage the package using standard Ubuntu apt tools.

Golf 210 released

  • Added new "error-line" and "error-char" clauses in JSON parsing (json-doc statement) to produce the line number and the character within the line where error in parsing was detected.
  • Fixed a build bug with missing 'stub_xml.o' file. This file is a part of upcoming XML parsing support and plays no role currently, but it prevented the build from being completed.
  • Fixed issue with maximum length of source code line, which should be approx 8K.
  • Added debian apt package build support (debian/control etc.) 

Thursday, January 30, 2025

How to send email with Golf

This example shows how to send email with Golf. The web service you'll create here is very simple and it will display an HTML form to collect info needed to send email, such as "From" and "To" (the sender and the recipient emails), the Subject and of course the Message itself. Once user clicks Submit, email is sent.

Create directory for your application:
mkdir -p mail
cd mail

Create "mail-sender" application:
gg -k mail-sender

Copy the following code to file "mail.golf":
 begin-handler /mail public
     // Get URL parameter
     get-param action

     if-true action equal "show_form"
         // Display HTML form
         @<h2>Enter email and click Send to send it</h2>
         @Note: 'From' field must be the email address from the domain of your server.<br/><br/>
         @<form action="<<p-path "/mail">>" method="POST">
         @    <input type="hidden" name="action" value="submit_form">
         @    <label for="from_mail">From:</label><br>
         @    <input type="text" name="from_mail" value=""><br>
         @    <label for="to_mail">To:</label><br>
         @    <input type="text" name="to_mail" value=""><br><br>
         @    <label for="subject_mail">Subject:</label><br>
         @    <input type="text" name="subject_mail" value=""><br><br>
         @    <label for="message">Message:</label><br>
         @    <textarea name="message" rows="3" columns="50"></textarea>
         @    <br/><br/>
         @    <input type="submit" value="Send">
         @</form>
     else-if action equal "submit_form"
         // Get data from HTML form
         get-param from_mail
         get-param to_mail
         get-param message
         get-param subject_mail
         // Construct email message
         write-string msg
             @From: <<p-out from_mail>>
             @To: <<p-out to_mail>>
             @Subject: <<p-out subject_mail>>
             @
             <<p-out message>>
         end-write-string
         // Send email
         exec-program "/usr/sbin/sendmail" args "-i", "-t" input msg status st
         // Check status of email sending
         if-true st not-equal GG_OKAY
             @Could not send email!
         else-if
             @Email sent!
         end-if
         @<hr/>
     else-if
         @Unrecognized action!<hr/>
     end-if
 end-handler

The example uses

Tuesday, January 28, 2025

Fast JSON parser with little coding

Golf's JSON parser produces an array of name/value pairs. A name is a path to value, for instance "country"."state"."name", and the value is simply the data associated with it, for instance "Idaho". You can control if the name contains array indexes or not, for instance if there are multiple States in the document, you might have names like "country"."state"[0]."name" with [..] designating an array element.

You can iterate through this array and get names of JSON elements, examine if they are of interest to you, and if so, get the values. This typical scenario is how Golf's parser is built, since it uses a "lazy" approach, where values are not allocated until needed, speeding up parsing. That is the case in this example. The JSON document below is examined and only the names of the cities are extracted.

You can also store JSON elements into trees or hashes for future fast retrieval, or store them into a database, etc.

To get started, create a directory for this example and position in it:
mkdir -p json
cd json

Save this JSON into a file "countries.json" - we will get the names of the cities from it:
{ "country": [
    { 
        "name": "USA",
        "state": [
            { 
                "name": "Arizona",
                "city": [
                    {
                        "name" : "Phoenix",
                        "population": 5000000
            	    } ,
                    {
                        "name" : "Tuscon",
                        "population": 1000000
            	    } 

                ]
            } ,
            { 
                "name": "California",
                "city": [
                    {
                        "name" : "Los Angeles",
                        "population": 19000000
            	    },
                    {
                        "name" : "Irvine"
            	    }
                ]
            } 
        ] 
    } ,
    { 
        "name": "Mexico",
        "state": [
            { 
                "name": "Veracruz",
                "city": [
                    {
                        "name" : "Xalapa-Enríquez",
                        "population": 8000000
            	    },
                    {
                        "name" : "C\u00F3rdoba",
                        "population": 220000
            	    }
                ]
            } ,
            { 
                "name": "Sinaloa",
                "city": [
                    {
                        "name" : "Culiac\u00E1n Rosales",
                        "population": 3000000
            	    }
                ]
            } 
        ] 
    }
    ]
}

What follows is the code to parse JSON. We open a JSON file, process the document, check for errors, and then read elements one by one. We look for a key "country"."state"."city"."name" because those contains city names. Note use "no-enum" clause in json-doc (which is the Golf's JSON parser), so that element designations aren't showing (meaning we don't have [0], [1] etc. for arrays).

Save this code to "parse-json.golf":
 begin-handler /parse-json public
     // Read the JSON file
     read-file "countries.json" to countries status st
     if-true st lesser-equal 0
         @Cannot read file or file empty
         exit-handler -1
     end-if

     // Parse JSON
     json-doc countries no-enum status st error-text et error-position ep to json

     // Check for errors in JSON document
     if-true st not-equal GG_OKAY
         @Error [<<p-out et>>] at [<<p-num ep>>]
         exit-handler -2
     end-if

     // This is the JSON element we're looking for
     set-string city_name unquoted ="country"."state"."city"."name"

     // Read elements one by one - note you can then store them in a tree or hash for future fast searches
     start-loop
         // Read just a key
         read-json json key k type t
         // Exit if end of document
         if-true t equal GG_JSON_TYPE_NONE
             break-loop
         end-if
         // If matches key we're looking for, get the value, and output it
         if-true city_name equal k
             read-json json value v
             @Value [<<p-out v>>]
             @--------
         end-if
         // Move on to the next JSON element
         read-json json next
     end-loop

     // Optionally delete JSON object, or it will be automatically deleted
     json-doc delete json
 end-handler