The development of web applications in PicoLisp

Readers of the previous article a Radical approach to application development could rightly notice that the article is too theoretical. So I need to restore the balance good and evil theory and practice.

This article reveals only the tip of the iceberg called picoLisp. Behind interesting points about the innards of database, organization of a distributed database, debugging, functional I/O object model with multiple inheritance, PicoLisp Prolog...

I still hope that our programmers will look to this powerful tool.

Carefully, under the cut a lot of text and the brackets!


abu@software-lab.de
the

Developing web applications in PicoLisp


© Software Lab. Alexander Burger

This document provides an introduction to writing web applications in PicoLisp.

It concentrates on the XHTML/CSS framework (unlike the previous Java-AWT, Java-Swing and plain-HTML frameworks), which is easier to use, more flexible in design and does not depend on plug-ins, JavaScript, cookies and CSS.

Simple GUI, HTTP/HTML has several advantages: it works in any browser and can be completely controlled by the script ("@lib/scrape.l").

More precisely, the CSS can to be used to improve the layout. And the browsers JavaScript will react faster and more smoothly. But this framework works perfectly and in browsers that don't know anything about CSS or JavaScript. All examples were also tested in the text-browser w3m.

Basic information about the system PicoLisp: PicoLisp Reference and PicoLisp Tutorial. Assumes knowledge of HTML and a little CSS and HTTP.

The examples assume that PicoLisp was started from a global installation (see Installation).

the


the

Static page


You can use PicoLisp to generate static HTML pages. In itself, this does not make much sense, since with the same success it is possible to directly write HTML code, but it forms the base for interactive applications and allows us to get acquainted with the application server and other fundamental concepts.
the

Hello World


To begin with a minimal application, create a file "project.l" in the PicoLisp installation directory and enter the following two lines.

the
########################################################################
(html 0 "Hello" "@lib.css" NIL
"Hello World!" )
########################################################################

(We will use and modify this file in all following examples. Whenever you see this code fragment between lines ('#####'), just copy and paste it into the file "project.l" and click on the "Refresh" button of your browser to see the result)

application server


Open a terminal window and start a PicoLisp application server

the
$ pil @lib/http.l @lib/xhtml.l @lib/form.l --server 8080 project.l +

The prompt will not appear. The server is running and waiting for connections. You can stop it later by pressing Ctrl-C in this terminal or by running 'killall pil' in a terminal window.

(In subsequent examples, we assume that this HTTP server continues to work)

Now open URL 'http://localhost:8080' in the browser. You should see a blank page with a single line of text.

How it works?


The above line loads the debugger ( " + " ), code HTTP server ("@lib/http.l"), function XHTML ("@lib/xhtml.l") and a GUI-framework ("@lib/form.l" you will need it later to online forms).

It then calls the function server with the port number and URL by default. She will listen to this port for incoming HTTP requests in an endless loop. Every time it receives a GET request on port 8080, the file "project.l" is loaded and executed using the (load)

During the execution of this file all data written to the current output channel (STDOUT) is sent directly to the browser. The code in "project.l" is responsible for generating the HTML (or other format acceptable to the browser).



the

URL Syntax


The application server uses a slightly specialized syntax when exchanging URLS with the client. The part of the URL is the "path" that remains after cropping

the
    the
  • Protocol specification, host, and port
  • the
  • the final question mark and arguments

is interpreted according to some rules. The most important of them are:
the
    the
  • If the path starts with an exclamation mark ('!'), the remaining part (without '!') as the name Lisp functions. All the arguments after the question mark is passed to this function.
  • the
  • If the path ends ".l" (a dot and "L" in lowercase), it is taken as the name of a Lisp file that is uploaded using (load). This is the most common case, and we use it in our example "project.l".
  • the
  • If the file name extension matches an entry in the global table of mime types *Mimes, the file is sent to the client with mime-type and max-age value taken from this table.
  • the
  • otherwise, the file is sent to the client with mime type "application/octet-stream" and max-age of 1 second.

The application is no restriction to extend or modify the table *Mimes function mime. For example,

the
(mime "doc" "application/msword" 60)

defines a new mime type with a max-age of one minute.

Argument values in the URL, after the path and the question mark, are encoded in such a way as to preserve the data types Lisp:

the
    the
  • an Ordinary character (internal symbol) starts with a dollar sign ("$")
  • the
  • the Number starts with a plus sign (+)
  • the
  • External symbol (object DB) starts with a dash ('-')
  • the
  • List (single-level only) is encoded with underscores ('_')
  • the
  • otherwise it is a transit symbol (a string)

Therefore, the types high-level data can be directly passed to the function encoded in the URL, or assigned to global variables before a file is loaded.



the

Security


Of course this is a huge hole in security if you directly from the URL of any Lisp file may be downloaded, and any function can be called. For this reason, the application must take care to precisely specify which files and functions are allowed in the URL. The server checks the global variable *Allow, and when its value is not NIL, denies access to that does not match its content.
Typically, the *Allow do not change directly, use the functions allowed and allow

the
(allowed ("app/")
"!start" "@lib.css" "customer.l" "article.l" )

It is usually invoked at the beginning of the application and allows access to the directory "app/", the functions "start" and the files "@lib.css", "customer.l" and "article.l".

Later in the program, *Allow can be dynamically extended with allow

the
(allow "!foo")
(allow "newdir/" T)

This adds to the set of allowed elements the function "foo" and the directory "newdir/".

File ".pw"


For use in security control (primarily to use the function psh, as in some later examples) it is necessary to create a file with the name ".pw" in the PicoLisp installation directory. This file should contain one line of random data to use as the password.

The recommended way to create this file — the function call pw, defined in "@lib/http.l"

the
$ pil @lib/http.l -'pw 12' -bye

Please run this command.



the

the html


Now back to our example "Hello World". In principle, you could write "project.l" as a sequence of statements

the
########################################################################
(prinl "HTTP/1.0 200 OK^M")
(prinl "Content-Type: text/html; charset=utf-8")
(prinl "^M")
(prinl "<html>")
(prinl "Hello World!")
(prinl "</html>")
########################################################################

but using the function html is much more convenient.

In addition, html — no more than the printing function. You can easily see this if you connect a PicoLisp Shell (psh) to the server process (you had to generate ".pw"-file to do this) and introduce the function html

the
$ /usr/lib/picolisp/bin/psh 8080
: (html 0 "Hello" "@lib.css" NIL "Hello World!")
HTTP/1.0 200 OK
Server: PicoLisp
Date: Fri, 29 Dec 2006 07:28:58 GMT
Cache-Control: max-age=0
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Hello</title>
<base href="http://localhost:8080/"/>
<link rel="stylesheet" type="text/css" href="http://localhost:8080/@lib.css"/>
</head>
<body>Hello World!</body>
</html>
-> </html>
: # (type Ctrl-D here to terminate PicoLisp)

The arguments to html:

    the
  1. 0: the Value of max-age to control the browser cache (in seconds, zero means "no-cache"). You can set a higher value for pages that change seldom, or NIL to disable the cache-control.
  2. the
  3. "Hello": the name of the page.
  4. the
  5. "@lib.css": the name of the CSS file. Pass NIL if you don't want to use CSS, or a list of file names, if you want to use more than one CSS file.
  6. the
  7. NIL: the Specification of CSS attributes for the body (see CSS attributes below).

After these four arguments can be followed by an arbitrary number of expressions. They form the body of the page and are calculated according to special rules. This rules are a bit different from the usual rules of computing:

the
    the
  • If the argument is an atom (number or symbol/string), its value will be printed immediately.
  • the
  • otherwise (the list), it is evaluated as Lisp functions (usually some form of print statement).

Thus our source file also can be written as:

the
########################################################################
(html 0 "Hello" "@lib.css" NIL
(prinl "Hello World!") )
########################################################################

The most typical printing function is some HTML-tags:

the
########################################################################
(html 0 "Hello" "@lib.css" NIL
(<h1> NIL, "Hello World!")
(<br> "This is some text.")
(ht:Prin "And this is a number:" (+ 1 2 3)) )
########################################################################

<h1> and <br> are functions of tags. <h1> as its first argument takes a CSS attribute.

Note the use of ht:Prin instead of prin. The function ht:Prin should be used for direct printing in HTML pages, because it escapes special characters.


the

CSS Attributes


the html and many of functions, accept a CSS attribute. It can be an atom, a cons-pair or a list of cons-pairs. We show this by the example of the <h1>.

Atom (usually a symbol or string) as the name of the CSS class

the
: (<h1> 'foo "Title")
<h1 class="foo">Title</h1>

For a cons-pair CAR is perceived as the attribute name and the CDR as the attribute value

the
: (<h1> '(id . bar) "Title")
<h1 id="bar">Title</h1>

Hence the list of cons-pairs gives a set of pairs attribute-value.

the
: (<h1> '((id abc) (lang . "de")) "Title")
<h1 id="abc" lang="de">Title</h1>



the


All the predefined functions of the XHTML tags can be found in "@lib/xhtml.l". We recommend you to take a look at their definitions and experiment a little, performing them in the PicoLisp-console, or by clicking the browser "Refresh" after you edit the file "project.l".

To obtain suitable PicoLisp-console run PicoLisp Shell (psh) in a separate terminal window (only works if you are running the application server and you have created the file ".pw")

the
$ /usr/lib/picolisp/bin/psh 8080
:

or just run the interpreter with the module load "@lib/xhtml.l"

the
$ pil @lib/http.l @lib/xhtml.l +
:

Please note that for all these functions the tags are used rules of function evaluation.

Simple Tags


Most of the functions of the tags are simple and clear. Some of them just print their arguments.

the
: (<br> "Hello world")
Hello world<br/>

: (<em> "Hello world")
<em>Hello world</em>

However, the majority of them take the set the CSS attributes as first argument (like <h1> above)

the
: (<div> 'main "Hello world")
<div class="main">Hello world</div>

: (<p> NIL, "Hello world")
<p>Hello world</p>

: (<p> 'info "Hello world")
<p class="info">Hello world</p>

All these functions take an arbitrary number of arguments and can have arbitrarily deep nesting (as long as the resulting HTML markup is valid)

the
: (<div> 'main
(<h1> NIL "Head")
(<p> NIL
(<br> "Line 1")
"Line"
(<nbsp>)
(+ 1 1) ) )
<div class="main"><h1>Head</h1>
<p>Line 1<br/>
Line 2</p>
</div>

Lists


HTML-lists, implemented by the <ol> and <ul> let you define hierarchical structures. Paste the following code into your copy of "project.l":

the
########################################################################
(html 0 "Unordered List" "@lib.css" NIL
(<ul> NIL
(<li> NIL "Item 1")
(<li> NIL
"Sublist 1"
(<ul> NIL
(<li> NIL, "Item 1-1")
(<li> NIL, "Item 1-2") ) )
(<li> NIL, "Item 2")
(<li> NIL
"Sublist 2"
(<ul> NIL
(<li> NIL, "Item 2-1")
(<li> NIL, "Item 2-2") ) )
(<li> NIL, "Item 3") ) )
########################################################################

You also can put arbitrary code into each node of this tree, including the other functions of the tags.

Tables


Like hierarchical structures you can generate a two-dimensional table using the functions <table> and <row>.

The following example prints a table of numbers and their squares:

the
########################################################################
(html 0 "Table" "@lib.css" NIL
(<table> NIL NIL NIL
(for N 10 # A table with 10 rows
(<row> NIL N (prin (* N N))) ) ) ) # and 2 columns
########################################################################

The first argument to <table> is the usual CSS attribute, the second an optional title, and the third an optional list of column headers. In this list you can transmit the list for each column, with a CSS attribute in the CAR and the body of the tag in the CDR for the content of the column header.

The body of the <table> contains the function calls <row>. A characteristic feature is that each expression in its body will go in a separate column in the table. If the column header and the <row> attribute is specified CSS will be combined with space and passed to the <td>. This allows you to specify different CSS attributes for each row and column.
As an extension of the above example table, let's set some attributes for the table itself (although it is not recommended — better define such styles in a CSS file and then just pass the class name in the <table>), align the two columns right-aligned and output each line into variables (red and blue) colors

the
########################################################################
(html 0 "Table" "@lib.css" NIL
(<table>
'((width . "200px") (style . "border: dotted 1px;")) # table style
"Square Numbers" # caption
'((align "Number") (align "Square")) # 2 headers
(for N 10 # 10 rows
(<row > (xchg '(red) '(blue)) # red or blue
N # 2 columns
(prin (* N N) ) ) ) ) )
########################################################################

If you want to combine two or more cells in the table so that one cell was occupied by multiple columns, you can pass the symbol "-" as the additional parameter <row>. This leads to the fact that the data located to the left of the characters "-" will be expanded to the right.

You can also directly specify the table structure in the normal functions of tags, <th>, <tr> and <td>.

If you just need a two-dimensional arrangement of components, it is possible to apply more simple function <grid>:

the
########################################################################
(html 0 "Grid" "@lib.css" NIL
(<grid> 3
"A" "B" "C"
123 456 789 ) )
########################################################################

The first parameter is the number of columns (here: 3), and then one expression for each cell. Instead of a number, you can also pass a list of CSS attributes. The length of this list will determine the number of columns. You can change the second line in the above example to:

the
 (<grid> '(NIL NIL right)

The third column is aligned right.

Menu and tabs (tabs)


The two most powerful tag functions — <menu> and <tab>. Being used separately or in combination, they form a navigation framework

the
    the
  • items menu, in which you can open and close submenu
  • the
  • items submenu to navigate to different pages
  • the
  • tabs (tabs) to navigate to the various subpages

The following example is not very useful, because the URLS of all items are on the same page "project.l", but this is sufficient to demonstrate the functionality:

the
########################################################################
(html 0 "Menu+Tab" "@lib.css" NIL
(<div> '(id . menu)
(<menu>
(Item "project.l") # Top level item
(NIL (<hr>)) # Plain HTML
(T "Submenu 1" # Submenu
("Subitem's 1.1", "project.l")
(T "Submenu 1.2"
("Subitem's 1.2.1" "project.l")
("Subitem's 1.2.2" "project.l")
("Subitem's 1.2.3" "project.l") )
("Subitem's 1.3" "project.l") )
(T "Submenu 2"
("Subitem's 2.1" "project.l")
("Subitem's 2.2" "project.l") ) ) )
(<div> '(id . main)
(<h1> NIL "Menu+Tab")
(<tab>
("Tab1"
(<h3> NIL "This is Tab 1") )
("Tab2"
(<h3> NIL "This is Tab 2") )
("Tab3"
(<h3> NIL "This is Tab 3") ) ) ) )
########################################################################

<menu> accepts a sequence of menu items. Each menu item is a Lisp list whose CAR is

the
    the
  • NIL: the inactive menu item, the rest of the list may consist of arbitrary code (usually HTML tags).
  • the
  • T: the second element is the name of a submenu and clicking on the name will open or close the corresponding submenu. The tail of the list recursively specifies the submenu (of arbitrary depth).
  • the
  • Otherwise: the menu Item specifies a direct action (instead of opening a submenu), where the first element of the list is the name of the menu item and the second element the corresponding URL.

<tab> takes a list of sub-pages. Each page is simply the name of the tab, and then the arbitrary code (normally HTML tags).

Please note that only one menu and one tab can be active at one time.



In HTML, the only possibility for user input — using the <form> and <input>, using the HTTP POST method to communicate with the server.

"@lib/xhtml.l" defines a function with the name <post>and the collection of input tags of data, which allow direct programming of HTML forms. We will show you only one simple example:

the
########################################################################
(html 0 "Simple Form" "@lib.css" NIL
( NIL "project.l"
(<field> 10 '*Text)
(<submit> "Save") ) )
########################################################################

This example binds a text input field with the global variable *Text. The field displays the current value of *Text, and clicking the submit button causes a reload of "project.l" *Text has a string value entered by the user.

The application can then use this variable for any useful action, for example to store the value in the database.

The problem with such a straightforward use of forms is:

    the
  1. they require the programmer worries about maintaining a large number of global variables. Each input field on a page requires the variable to communicate between the server and the client.
  2. the
  3. they do not retain the internal state of the application. Every POST request creates a separate process on the server that sets the global variables to their new values, generates HTML pages and ends after that. Application state needs to be transferred explicitly, for example using the tag <hidden>.
  4. the
  5. they are not very interactive. Normally only one button "Send". The user fills in a (possibly) large number of input fields, but changes will only take effect when you click "Send".

Although we wrote several applications in this style, we recommend a GUI framework provided by the "@lib/form.l". He doesn't need any variables for client-server interactions and implements a class hierarchy of GUI components to abstract the application logic of the action buttons and connection data.



the

Session


First of all, we need to create a persistent environment on the server to handle each individual session (for each connected client).

Technically, it is just a child process of the server, which we launched above, which is not completed immediately after he sent an HTML page to the browser. This is achieved by calling the function app somewhere in the initialization code of the application.

the
########################################################################
(app) # Start a session

(html 0 "Simple Session" "@lib.css" NIL
( NIL "project.l"
(<field> 10 '*Text)
(<submit> "Save") ) )
########################################################################

No more differences from the previous example. However, when you connect your browser and then look at the terminal window where you launched your application server, you will notice the colon — command line PicoLisp

the
$ pil @lib/http.l @lib/xhtml.l @lib/form.l --server 8080 project.l +
:

A tool such as the Unix utility ps, will tell you that it is now running two processes picolisp, the first of which is the parent of the second.

If you enter some text, say "abcdef" in the text box in the browser window, and click the Save button and examine the variable *Text

the
: *Text
- >"abcdef"

you will see that we now have a dedicated PicoLisp process, "connected" to the client.

You can complete this process (like any interactive session of PicoLisp), pressing Ctrl-D an empty command line. Otherwise, he will end himself if the other browser requests not received within the timeout (default 5 minutes).

For a start, prodas version (without debugging), the server is typically run without a flag "+" with -wait

the
$ pil @lib/http.l @lib/xhtml.l @lib/form.l --server 8080 project.l -wait

Under this option, start the command prompt (:) appears when the client connects.


the

Forms (action forms)


Now that we have a session for each client, we can set up an active GUI framework.

To do this, we wrap the call to the function html in action. Inside the body of the html in addition to all other kinds of tag functions may be one or more calls to form

the
########################################################################
(app) # Start session

(action # Action handler
(html 0 "Form" "@lib.css" NIL # an HTTP/HTML protocol
(form NIL # Form
(gui 'a '(+TextField) 10) # Text Field
(gui '(+Button) "Print" # Button
'(msg (val > (: home a))) ) ) ) )
########################################################################

Please note that there is no longer a global variable, such as *Text, to store the contents of the input field. Instead, we gave a local, symbolic name 'a' component +TextField

the
 (gui 'a '(+TextField) 10) # Text Field

Other components can access it.

the
 '(msg (val > (: home a)))

(: home) always returns a reference to the form which contains this GUI component. So (: home a) — the referenced component 'a' in its current form. As msg prints its argument to STDERR, and the method val> retrieves the current content component, when we push the button we will see in the console the text typed into the text box.

Individually action and form has no particular meaning. However, inside html and form, you can freely mix HTML (and any other Lisp function).

A typical page may have the following structure:

the
(action # Action handler
(..html # HTTP/HTML protocol
(<h1> ..) # HTML tags
(form NIL # Form
(<h3> ..)
(gui ..) # GUI component(s)
(gui ..)
.. )
(<h2> ..)
(form NIL # Another form
(<h3> ..)
(gui ..) # GUI component(s)
.. )
(<br> ..)
.. ) )

the gui


The most important function in the body of the form — the gui. This is a workhorse of a visual interface.

Outside the function form gui is not defined. The first parameter is an optional alias, the list of classes and additional arguments needed by the constructors of these classes. We saw in the previous example,

the
 (gui 'a '(+TextField) 10) # Text Field

Here 'a' is an alias for a component of type (+TextField). A numeric argument to 10 is passed to the text field, specifying its width. See other examples in Chapter GUI Classes.
During GET-request to gui is essentially a frontend for new. He builds the component stores it in the internal structure of the current form and initialisere it by sending the message init>. Finally, it sends the message show> to generate HTML and pass it to the browser.

During POST-request gui does not build any new components. Instead, existing components are reused. Therefore, gui do not need to do anything more than sending the message bean show>.

control Flow


At HTTP only two methods to change a browser window: GET and POST. We use these two methods in a clearly defined way:

the
    the
  • GET means that this is new page. It is used when a page is visited for the first time, usually by typing the URL in the address field of your browser, or by clicking on the link (which is often submenu item or tab).
  • the
  • POST always leads to the page. It is initiated by pressing the button on the form, updates the data structure corresponding in shape and executes the code associated with this button.

Code associated with the button can do almost anything: read and modify the contents of input fields, communicate with the database to display messages and dialog boxes or even to forge a POST request because a GET request that will cause a completely different document (see Change URL).
GET builds all GUI components on the server. These components are objects that encapsulate the state and behavior of the HTML page in the browser. Whenever the button is pressed, the page reloads with a POST request. Then before sending any data to the browser — control is passed to the function action. It performs error checking of all components, processes user input in the HTML page and stores the values in each component in the desired format (text, number, date, object, etc.).

The form state is preserved over time. When the user returns to the previous page with Back button of browser, this state and resumes can be sent again (POST-request).

In the following simple example displays two text fields. If you enter some text in the Source field, you can copy it in upper or lowercase in the "Destination" by clicking one of the buttons

the
########################################################################
(app)

(action
(html 0 "Case Conversion" "@lib.css" NIL
(form NIL
(<grid> 2
"Source" (gui 'src '(+TextField) 30)
"Destination" (gui 'dst '(+Lock +TextField) 30) )
(gui '(+JS +Button) "Upper Case"
'(set > (: home dst)
(uppc (val > (: home src))) ) )
(gui '(+JS +Button) "Lower Case"
'(set > (: home dst)
(lowc (val > (: home src))) ) ) ) ) )
########################################################################

Prefix class +Lock in the "Destination" makes the field read-only. The only way to put some text in this field is to use one of the buttons.

Switching URLS


Because the code of actions (buttons) are performed before html can send HTTP header, it can abort the current page and present something different to the user. This can be another HTML page, but it's not a very interesting case, as it would be quite normal links. Instead, the code may cause the download of dynamically generated data.

The following example shows a text field and two buttons. Any text typed in the text area is exported to a text file via the first button or a PDF document via the second button

the
########################################################################
(load "@lib/ps.l")

(app)

(action
(html 0 "Export" "@lib.css" NIL
(form NIL
(gui '(+TextField) 30 8)
(gui '(+Button) "Text"
'(let Txt (tmp "export.txt")
(out Txt (prinl (val > (: home gui 1))))
(url Txt) ) )
(gui '(+Button) PDF
'(psOut NIL "foo"
(a4)
(indent 40 40)
(down 60)
(hline 3)
(font (14 . "Times-Roman")
(ps (val > (: home gui 1))) )
(hline 3)
(page) ) ) ) ) )
########################################################################

(when you pass two arguments in class +TextField (width and height), creating an element textarea)

Code for the first button creates a temporary file (i.e. a file with the name "export.txt" in the temporary directory of the current process), prints the value of the text area (this time we do not give the name of the gui element, but simply refer to it as the first element in form) in this file, and then calls the function url file name.

The second button uses the PostScript library "@lib/ps.l" to create a temporary file "foo.pdf". Here the creation of a temporary file and call the function url hidden inside psOut. As a result, the browser receives a PDF document and displays it.

Notifications and dialogs


In fact, alerts, and dialogs — not quite what we're used to seeing ;-)

They are not pop-up (popup). In this framework, they are the kind of simple to use forms predefined type. You can call them from the button code, and they always appear on the current page, just before the form that created them.

Let's look at an example that uses two alert and dialog box. In the beginning, it displays a simple form with a locked text field and two buttons

the
########################################################################
(app)

(action
(html 0 "Alerts and Dialogs" "@lib.css" NIL
(form NIL
(gui '(+Init +Lock +TextField) "Initial Text" 20 "My Text")

'(alert NIL "This is an alert" (okButton)) )
(gui '(+Button) "Dialog"
'(dialog NIL
(<br> "This is a dialog.")
(<br>
"You can change the text here "
(gui '(+Init +TextField) (val > (: top 1 gui 1)) 20) )
(<br> "and then re-submit it to the form.")
(gui '(+Button) "Re-Submit"
'(alert NIL "Are you sure? "
(yesButton
'(set > (: home top 2 gui 1)
(val > (: home top 1 gui 1)) ) )
(noButton) ) )
(cancelButton) ) ) ) ) )
########################################################################

Prefix class +Init initializefrom field "My Text" with the string "Initial Text". Because the field is locked, this value cannot be changed directly.

The first button will show the alert: "This is an alert". You can close it by clicking "OK".

The second button will show a dialog with a text box containing the copy of the value from the text box the main form. You can change this value and send it back to the form if you click "Re-Submit" and answer "Yes" to the warning "Are you sure?".

Sample calculator


Now let's put our test file "project.l" and move on to more substantial and Autonomous practical example. Using what we have learned, build a simple bignum calculator. ("bignum" because PicoLisp has only one type of numbers — bignums (unlimited integer))

It uses one form, a single numeric input field, and a lot of buttons. The original text of the calculator can be found in the PicoLisp distribution (for example, "/usr/share/picolisp/misc/calc.l") together with a directly an executable wrapper script "misc/calc".
calc.l
# 14may11abu
# (c) Software Lab. Alexander Burger
# *Init, *Accu *Stack
(allowed NIL "!calculator" "@lib.css")
(load "@lib/http.l" "@lib/xhtml.l" "@lib/form.l")
# Calculator logic
(de digit (N)
(when *Init (zero *Accu) (off *Init))
(setq *Accu (+ N (* 10 *Accu))) )
(de calc ()
(let (Fun (caar *Stack) Val (cddr (pop '*Stack)))
(setq *Accu
(if (and (== '/ Fun) (=0 *Accu))
(alert "Div / 0")
(Fun Val *Accu) ) ) ) )
(de operand (Fun Prio)
(when (>=(cadar *Stack) Prio) (calc))
(push '*Stack (cons Fun Prio *Accu))
(on *Init) )
(de finish ()
(while *Stack (calc))
(on *Init) )
# Calculator GUI
(de calculator ()
(app)
(action
(html 0 "Bignum Calculator" "@lib.css" NIL
(<h2> NIL "Bignum Calculator")
(form NIL
(<br> (gui '(+Var +NumField) '*Accu 60))
(<grid> 4
(gui '(+JS +Button) "±" '(setq *Accu (- *Accu)))
(gui '(+Able +JS +Button) '(ge0 *Accu) (char 8730)
'(setq *Accu (sqrt *Accu)) )
(gui '(+JS +Button) "\^" '(operand '** 3))
(gui '(+JS +Button) "/" '(operand '/ 2))
(gui '(+JS +Button) "7" '(7 digit))
(gui '(+JS +Button) "8" '(digit 8))
(gui '(+JS +Button) "9" '(digit 9))
(gui '(+JS +Button) "*" '(operand '* 2))
(gui '(+JS +Button) "4" '(4 digit))
(gui '(+JS +Button) "5" '(5 digit))
(gui '(+JS +Button) "6" '(digit 6))
(gui '(+JS +Button) "-" '(operand '- 1))
(gui '(+JS +Button) "1" '(digit 1))
(gui '(+JS +Button) "2" '(digit 2))
(gui '(+JS +Button) "3" '(digit 3))
(gui '(+JS +Button) "+" '(operand '+ 1))
(gui '(+JS +Button) "0" '(digit 0))
(gui '(+JS +Button) "C" '(zero *Accu))
(gui '(+JS +Button) "A" '(main))
(gui '(+JS +Button) "=" '(finish)) ) ) ) ) )
# Initialize
(de main ()
(on *Init)
(zero *Accu)
(off-Stack) )
# Start server
(de go ()
(server 8080 "!calculator") )


To use, go to the PicoLisp installation directory, and run it as

the
$ misc/calc

or call it with an absolute path, for example

the
$ /usr/share/picolisp/misc/calc

If you want to access the interactive session of PicoLisp, run it instead as

the
$ pil misc/calc.l -main-go +

Open as usual, the page 'http://localhost:8080'.

The code for the logic of the calculator and GUI is quite simple. The entry point is the calculator. It is called directly (as described in URL Syntax), first, as the default URL of the server, and second, implicitly through POST requests. After starting the calculator, the file access is not required.

Please note that for the production version, we insert the operator allowed at the beginning of "misc/calc.l" (as recommended in Chapter Security )

the
(allowed NIL "!calculator" "@lib.css")

This will limit external access only function calculator.

The calculator uses three global variables, *Init, *Accu and *Stack. *Init — Boolean flag set by the buttons of the operators to indicate that follow Article based on information from habrahabr.ru

Комментарии

Отправить комментарий

Популярные сообщения из этого блога

ODBC Firebird, Postgresql, executing queries in Powershell

garage48 for the first time in Kiev!

The Ministry of communications wants to ban phones without GLONASS