The HaPyLi Programming Language

Section 2: Expressions

HaPyLi is a typeless language. Every expression will evaluate to an integer, so it's up to the programmer to remember what data is passed to and returned by a function.

Numeric Literals

HaPyLi defines three kinds of numeric literals - decimal integers, hexadecimal integers, and characters. These literals may be used in place of any expression, including a function body.


    File: literal.hpl
    
        import "stdlib/base.hpl"

        def f () = 42       ; You can declare "constant functions" too! Literals are 
                            ; no different than any other expressions. 

        def main () = 
            let
                a = 65      ; a, b, and c all equal the same value. 
                b = 0x41
                c = 'A'
            in
                (do (print-number a) (print-char '\n')
                    (print-number b) (print-char '\n')
                    (print-number c) (print-char '\n'))

String Literals

Strings are slightly different than other literals. The HaPyLi compiler converts them into null-terminated arrays of characters and stores them in Whitespace's heap memory. A String literal or a variable containing a string will evaluate to its address in heap memory.

Both string and character literals support the following common character escape codes:

  • \s -> space
  • \t -> tab
  • \r -> carriage return
  • \n -> line feed
  • \0 -> null
  • \' -> '
  • \" -> "

In addition, any escaped character not listed above simply translates to itself.

Built-in Functions

So far, you've seen plenty of examples of seemingly "built-in" functions in HaPyLi, such as "print-string", "print-number", or the arithmetic operators. Actually, there is nothing special about these functions. They are defined within HaPyLi itself, in the file "stdlib/base.hpl". This is possible because HaPyLi allows you to embed Whitespace commands directly in your applications.

There are only two functions that are special to the compiler: "if" and "do".

If Expressions

Normally, when you call a function in HaPyLi all of its arguments are evaluated first, in order from left-to-right, and then the function itself is executed. If all functions behaved this way, it would be impossible to control the flow of the application. "if" is the only function which allows short circuiting.

The syntax of "if" expressions in HaPyLi is the same as those in Lisp.


    File: if.hpl
    
        import "stdlib/base.hpl"

        def main () = 
            (if (read-number)
                (print-string "TRUE")
                (print-string "FALSE"))

Since HaPyLi is typeless, the condition of the if-statement behaves much as it would in C. The value 0 represents "false" and any non-zero value represents "true". Some of the functions in the HaPyLi Standard Library, however, assume that "true" is denoted by the value 1 instead of just anything non-zero, so you must be careful.

Do Expressions

Sometimes you want to call several functions in order - a series of print functions, for example. Since all function calls evaluate their arguments in order from left-to-right, it's very easy to define a function to sequence operations together. Such a function doesn't actually have to do anything. Unfortunately, there's a small caveat to this approach as the example below illustrates:


    File: execute.hpl
    
        import "stdlib/base.hpl"

        def inline execute (x y) = 0        ; Perform two operations in order.
        def inline execute (x y z) = 0      ; Perform three operations in order
        def inline execute (w x y z) = 0    ; Perform four operations in order 
                                            ; ... and so on
        
        def main () = 
            (execute (print-string "ABCDEFG\n")
                     (print-string "1234567\n"))

We don't need to do this. HaPyLi provides the "do" function, which can accept any number of parameters. You don't need to pollute your application with 100 'execute' definitions as above just because you want to chain together 100 operations (although I can't imagine you ever wanting to chain together more than just a few - but at least you now can).


    File: do.hpl
    
        import "stdlib/base.hpl"
        
        def main () = 
            (do (print-string "Hello, ")
                (print-string "world!\n")
                (print-string "Testing\n")
                (print-string "... and so on... \n"))

Furthermore, "do" expressions return the value returned by their last argument. This can be very useful for computing and returning a result after a series of IO operations, like in prompts.


    File: prompt.hpl
    
        import "stdlib/base.hpl"

        def prompt () = 
            (do (print-string "Enter your favorite number: ")
                (read-number))

        def main () = 
            let
                x = (prompt)
            in
                (do (print-string "You entered: ")
                    (print-number x))