Monday, April 10, 2017

Porting the text pager from Python to D (DLang)

By Vasudev Ram

$ tp file.txt
$ dir /s | tp


A few weeks ago, I had written this post:

tp, a simple text pager in Python

Some days ago, I thought of porting the pager from Python to D, a.k.a. the D language, and did so. The Python version was called tp.py (tp for Text Pager). So the D version is called tp.d.

The Wikipedia article about D says:

[ Though it originated as a re-engineering of C++, D is a distinct language, having redesigned some core C++ features while also taking inspiration from other languages, notably Java, Python, Ruby, C#, and Eiffel. ]

Here is the code for tp.d.
/*
File: tp.d
Purpose: A simple text pager.
Version: 0.1
Platform: Windows-only.
May be adaptable for Unix using tty / termios calls.
Only the use of getch() may need to be changed.
Author: Vasudev Ram
Copyright 2017 Vasudev Ram
Web site: https://vasudevram.github.io
Blog: https://jugad2.blogspot.com
Product store: https://gumroad.com/vasudevram
Twitter: https://mobile.twitter.com/vasudevram
*/

import std.stdio;
import std.string;
import std.file;

extern (C) int getch();

void usage(string[] args) {
    stderr.writeln("Usage: ", args[0], " text_file");
    stderr.writeln("or");
    stderr.writeln("command_generating_text | ", args[0]);
}

void pager(File in_fil, int lines_per_page=12, char quit_key='q')
{
    assert (lines_per_page > 1);
    int line_count = 0;
    int c;
    foreach (line; in_fil.byLine()) {
        writeln(line);
        line_count++;
        if ( (line_count % lines_per_page) == 0) {
            stdout.flush();
            c = getch();
            if (c == 'q')
                break;
            else
                line_count = 0;
        }
    }
}

int main(string[] args) {

    int lines_per_page = 20;
    File in_fil;

    try {
        if (args.length > 2) {
            usage(args);
            return 1;
        }
        else {
            if (args.length == 2) {
                in_fil = File(args[1]);
            }
            else {
                in_fil = stdin;
            }
            pager(in_fil, lines_per_page, 'q');
        }
    } catch (FileException fe) {
        stderr.writeln("Caught FileException: msg = ", fe.msg);
    } catch (Exception e) {
        stderr.writeln("Caught Exception: msg = ", e.msg);
    }
    finally {
        in_fil.close();
    }
    return 0;
}
Compile it the usual way with:
dmd tp.d
which will create tp.exe.
It can be run in either of two ways (paging a file or stdin), similar to the Python version. Here are two runs, dogfooding it:
tp tp.d
or
type tp.d | tp
As in the case of the Python pager (when run on itself), the output is the same as the source program itself, so I will not show it.

So, now I can say that tp is less than less :) [1] [2]

[1] Like Less is more
[2] Less in two senses of the word: it has less features, and a shorter name.

After writing this D pager, I did a brief visual comparison of it with the Python version, and was interested to see that the overall structure of the code was roughly the same, in the Python and D versions. Opening the file (or using stdin), reading lines from it, closing the file, getting characters from the keyboard, all were roughly similar in behavior. My guess that this is at least partly because both Python and D share a C heritage.

In fact, even the D line:

foreach (line; in_fil.byLine()) {

can, if you squint a bit, be read as the Python line:

for lin in fil.byLine()

which, while not valid Python, could be so, if you had a byLine method on file objects :)

For those interested, there was a good discussion about D on HN recently, here:

The reference D compiler is now open source (dlang.org)

Despite the thread title, it is more about the D language features than the licensing aspects.

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Are you a blogger with some traffic? Get Convertkit:

Email marketing for professional bloggers



5 comments:

Vasudev Ram said...


A comment on this:

>for lin in fil.byLine()

>which, while not valid Python, could be so, if you had a byLine method on file objects :)

Of course, the actual Python version is simpler:

for lin in in_fil:

which does a lazy (i.e. on-demand) iteration over the lines of the file (since Python file objects implement the iterator protocol), like the D one does. IOW, it does not load all the lines into memory at one go; it only loads each one on request. So it is fast and still uses little memory - and the same is true for the D version.

Kotet said...

I am translating this article to read it.
Can I also publish the translation on my website?

Vasudev Ram said...


Yes, you may do that, but please attribute the original post to me, by putting a sentence about it with a link to my post.

Kotet said...

Thanks, I published the Japanese translation!
https://kotet.github.io/2017/04/23/porting-text-pager-from-python-to-d.html

Vasudev Ram said...

Welcome, and thanks for your interest in the post :)