This repository has been archived on 2019-11-02. You can view files and clone it, but cannot push or open issues or pull requests.
Go to file
2019-09-16 13:22:31 +00:00
img added example images 2019-07-24 20:52:57 +02:00
.gitignore updated gitignore for new executable filename 2019-07-24 20:51:10 +02:00
LICENSE.org Added README and license 2019-07-24 20:51:26 +02:00
README.org Update README.org 2019-09-16 13:22:31 +00:00
tree-to-dot.el Added Elisp code, updated README 2019-08-14 02:16:40 +02:00
tree-to-dot.scm Added Elisp code, updated README 2019-08-14 02:16:40 +02:00

https://cdn.rawgit.com/syl20bnr/spacemacs/442d025779da2f62fc86c2082703697714db6514/assets/spacemacs-badge.svg

This project has been merged in [[https://labs.phundrak.fr/phundrak/conlang-layer][this one].

Tree to dot

Presentation

Tree to dot is a small utility for linguists and especially conlangers that allows them to declare trees with any number of children per node. I especially made it with the option in mind to make feature contrastive trees as presentented by Joseph Windsor in his talk during the Language Creation Conference 8, with examples below.

/phundrak/features-tree/media/commit/c64ad2928325316c9f4e9a9dcd5fbd68ab729492/img/nyqy-vowel-feature-tree.png

/phundrak/features-tree/media/commit/c64ad2928325316c9f4e9a9dcd5fbd68ab729492/img/nyqy-basic-syntax.png

Usage

TL;DR

Elisp

Load this source code within Emacs, either in a emacs-lisp-mode buffer or in a source block in your org-mode buffer. Create trees, pass them as arguments to tree-to-dot, execute, and voilà.

Scheme

In your *NIX terminal, clone the project, edit the example trees or create a new one, and execute this:

  cd features-tree
  chicken-csc features-tree.scm             # Compile the .scm file
  ./features-tree | dot -Tpng -o output.png # for PNG output
  ./features-tree | dot -Tsvg -o output.svg # for SVG output

More details

Elisp

The Elisp code was written with the intent of being used from org-mode in order to create inline images out of code. Put that source code in a code block in org-mode, and declare it as a noweb block.

  #+NAME: process-tree
  #+BEGIN_SRC emacs-lisp :exports none :noweb yes
    Code here!
  #+END_SRC

Dont forget to name your source blocks, it will be important for the noweb part later.

Then, you can declare later a beautiful tree in another code block, like so, and call your tree-to-dot function on it:

  #+NAME: my-tree
  #+BEGIN_SRC emacs-lisp :noweb yes :exports none
    <<process-tree>>
    (defvar mytree
      '("Tree"
        ("First child"
         ("First childs child"))
        ("Second child"
         ("Second childs first child")
         ("Second childs second child"))))
    (tree-to-dot mytree)
  #+END_SRC

You can now finally create one last code block in order to get your image:

  #+BEGIN_SRC dot :file whatever.png :var input=my-tree :exports results
    $input
  #+END_SRC

You can now export this last code block only to get your image. It will be automatically evaluated when you export your org buffer, but you can also manually trigger the evaluation by typing C-c C-c with your cursor on this last code block. Be sure to have enabled the dot language in babel and to have configured it properly (this might help).

Scheme

For now, the workflow is not the best, as you have to edit yourself the source code and re-compile it each time you edit your own tree.

You will have to declare a Scheme list containing your tree, and your typical node should be declared like so:

  ("text" (child1) (child2) ...)

Each child is itself a tree that should follow the same type of declaration, with as many child as you like per node I discourage you to have more than nine children though, otherwise it might break the output. If a node does not have any child, it should be declared like so:

  ("text")

As an example, here is the tree that was used to declare the first example image:

  (define vowels
    '("[vowel]"
      ("[back]"
       ("[tense]"
        ("[high]" ("ü"))
        ("{high}" ("ö")))
       ("{tense}"
        ("[high]" ("u"))
        ("{high}" ("o"))))
      ("{back}"
       ("[tense]"
        ("[high]" ("y"))
        ("{high}" ("ë")))
       ("{tense}"
        ("[high]" ("i"))
        ("{high}" ("e"))))))

And here is the source code of the second example image:

  (define syntax-tree '("S"
      ("Obl")
      ("S'"
       ("NPerg"
        ("NP"))
       ("VP"
        ("NPdat"
         ("NP"))
        ("VP'"
         ("NPabs"
          ("NP"
           ("S")
           ("NP'"
            ("Adj")
            ("N"))))
         ("V'"
          ("Mood")
          ("Tense")
          ("V")
          ("Neg")))))))

Once youve declared the tree you want to get, modify the last line of the source code (tree-to-dot ...) by replacing the default argument with the name of your tree. For the first example, we would call (tree-to-dot vowels), while for the second we would call (tree-to-dot syntax-tree).

Only one (tree-to-dot) call can be done at once, else what follows might break!

Once youve done that, compile your file! I personally use Chicken as my Scheme compiler, but if you already have another, you can use your own. Just replace my calls to chicken-csc by your own compilers command. Also, be aware that I use chicken-csc as the command for che Chicken compiler, but if you also use Chicken, you might have to call csc instead (this might mean you have an older version than the one I use).

Now that youve compiled your file, you will have to execute it. If your edits were alright, you should have some text output that looks like this, except that it will be way more compact.

  graph{
    node[shape=plaintext];
    graph[bgcolor="transparent"];
    0[label="[vowel]"];
    1[label="[back]"];
    0 -- 1;
    11[label="[tense]"];
    1 -- 11;
    111[label="[high]"];
    11 -- 111;
    1111[label="ü"];
    111 -- 1111;
    112[label="{high}"];
    11 -- 112;
    1121[label="ö"];
    112 -- 1121;
    12[label="{tense}"];
    1 -- 12;
    121[label="[high]"];
    12 -- 121;
    1211[label="u"];
    121 -- 1211;
    122[label="{high}"];
    12 -- 122;
    1221[label="o"];
    122 -- 1221;
    2[label="{back}"];
    0 -- 2;
    21[label="[tense]"];
    2 -- 21;
    211[label="[high]"];
    21 -- 211;
    2111[label="y"];
    211 -- 2111;
    212[label="{high}"];
    21 -- 212;
    2121[label="ë"];
    212 -- 2121;
    22[label="{tense}"];
    2 -- 22;
    221[label="[high]"];
    22 -- 221;
    2211[label="i"];
    221 -- 2211;
    222[label="{high}"];
    22 -- 222;
    2221[label="e"];
    222 -- 2221;
  }

If you get some errors, then you fucked up somewhere in your tree, probably missing some parenthesis or you forgot to add the ' before the first parenthesis after the name of your tree. Go back to your source file and fix that. Also, it might be easier to edit the file if you have a decent text editor, Id recommend using something along the lines of VS Code, Atom or Brackets, or even Emacs if you are not afraid by steep but extremely rewarding learning curves.

Now, you need to have Graphvizs dot tool installed to generate images. In your terminal, either redirect the output of your newly compiled program like so:

  ./features-tree | dot -Tpng -o output.png

Or simply copy and paste the output in a separate file, then only run the dot part of the above command. Youve got an output.png file containing your tree now!

My elements are not aligned/centered, what do?

IDK, Ive tried to look up the answer, but there was no trivial way to do it. If you found one, please tell me in a new issue, or even better, submit a PR!

How can I do that on Windows?

Scheme

IDK. Get a UNIX terminal (like the Linux subsystem, Putty(?) or Cygwin) and apply what has been said before, maybe. If you have a better explanation, you are more than welcome to either send it with a new issue or a pull request.

Elisp

This should work properly with Emacs on Windows, provided youve configured properly org-babel. I havent tested it though.

License

Check out the /phundrak/features-tree/src/commit/c64ad2928325316c9f4e9a9dcd5fbd68ab729492/LICENSE.org. TL;DR: a GPLv3 licence gives you the right to access, modify, and redistribute the source file at the condition it stays under the GPLv3 license, and if you somehow fuck up big time because of it (HOW?), you are responsible.