DEV Community

Cover image for Sorting a file: A real program in Leaf
edA‑qa mort‑ora‑y
edA‑qa mort‑ora‑y

Posted on • Originally published at mortoray.com

Sorting a file: A real program in Leaf

Finally, a big step for Leaf: something that could be called an actual program. Over the weekend I finished a program that loads a file, sorts the lines, and writes it to the console. It may not sound like much, but it represents a significant milestone for the language.

var main = -> {
    var args = sys.get_args()
    var in_file = args#0

    var qi = shared_file.open_read(in_file)
    var s = qi.read_text()

    var all = split( s, '\n' )
    std.quicksort_comp( all, 0, all.size -1, string_less )

    for i in std.range(0,all.size) {
        std.print( [ all.data#i, "\n" ] )
    }
}
Enter fullscreen mode Exit fullscreen mode

All those functions are also written in Leaf, though deep in the code are a few embedded OS calls.

Things todo

The code is littered with a variety of things that still need to be done, or aren't done well. Let's look at a few of them.

The function quicksort_comp should really just be called quicksort. Parametric overloaded functions, exported from a library, aren't quite working right. This version takes a comparator, string_less. Note there is no standard string type at the moment, so we need a custom comparator.

defn string_less = ( a : arrayï½¢charï½£, b : arrayï½¢charï½£ ) -> ( :boolean) {
    var at = 0
    loop at < a.size and at < b.size ? {
        //TODO: shouldn't need {}'s on return
        code_val(a#at) < code_val(b#at) then { return true }
        code_val(a#at) > code_val(b#at) then { return false }
        at = at + 1
    }

    return at >= a.size
}
Enter fullscreen mode Exit fullscreen mode

shared_file is also part of a TODO at the top of the file:

//TODO: sys.file needs to be a "service" type that implies "shared"
alias shared_file : sys.file shared
Enter fullscreen mode Exit fullscreen mode

By default all types are assigned by reference in Leaf, which is certainly not desired for file -- it doesn't even make sense. I will introduce a service type for this, which will be a class that is automatically shared, and cannot be copied. This is how Leaf will expose the difference between types that are fundamentally values or fundamentally wrappers around resources.

That last bit with the loop could probably be cleaned up already. I'd make a join function to concatenate all the elements and then do one print statement.

Standard library

Leaf is still basically missing any kind of standard library. The bits that make up the above program will start being migrated to this effort. Keep in mind that virtually everything needs to be programmed, consider that innocent call to split above:

defn split = ( s : arrayï½¢charï½£, c : char ) -> {
    var o : std.vector「array「char」」 shared

    var at : integer = 0
    var last = at

    loop at < s.size ? {
        do s#at ==  c ? {
            o.push( arrsub(s, last, at - last) )
            last = at + 1
        }

        //TODO: incr/decr operators, or at least +=, -=
        at = at + 1
    }

    return o
}

defn arrsub = ( s : arrayï½¢anyï½£, offset : integer, length : integer ) -> {
    var o = arrayï½¢type_infer(s#0)ï½£(length)

    for i in std.range(0,length) {
        o#i = s#(i+offset)
    }

    return o
}
Enter fullscreen mode Exit fullscreen mode

The file.read_text required creating a buffer class to accumulate all the data in the file. The vector class also had to be written, but is extremely minimal now.

What isn't Leaf?

sys.file, std.print , and std.quicksort are written in Leaf. Ultimately these must talk to the operating system. I'm using LibC to do the actual OS level activities, but trying to keep the number of calls to a minimum, as it will all affect portability.

Calling a LibC function is done with an @import declaration of the function.

@import("open") multi os_open : ( pathname : raw_array<:abi_char:>, flags : abi_int, mode : abi_int ) -> ( : abi_int ) raw no_throw
Enter fullscreen mode Exit fullscreen mode

The LibC functions ultimately used in this example are:

  • File functions: open, close, read
  • Memory functions: malloc, free
  • Console functions: putchar
  • argv and argc use a special linking mechanism instead of being arguments to main
  • A minimal C++ exported function to get _errno since there is no support for this directly in Leaf yet

The biggest layer between the high-level example and these low-level functions is unicode handling. All UTF-8 decoding and encoding is being handled in Leaf code. Those functions are not pleasant: they are a collection of bitcode operations.

I'll start cleaning up the TODOs now.

Top comments (0)