From a1a77995232bafccc34eaa730629e1093db5ebc2 Mon Sep 17 00:00:00 2001 From: Phuntsok Drak-pa Date: Wed, 14 Aug 2019 02:16:40 +0200 Subject: [PATCH] Added Elisp code, updated README --- README.org | 381 ++++++++++++++++----------- tree-to-dot.el | 50 ++++ features-tree.scm => tree-to-dot.scm | 0 3 files changed, 277 insertions(+), 154 deletions(-) create mode 100644 tree-to-dot.el rename features-tree.scm => tree-to-dot.scm (100%) diff --git a/README.org b/README.org index 65b736a..d2b8d15 100644 --- a/README.org +++ b/README.org @@ -1,11 +1,27 @@ [[file:https://cdn.rawgit.com/syl20bnr/spacemacs/442d025779da2f62fc86c2082703697714db6514/assets/spacemacs-badge.svg]] -* Features Tree +* Table of content :TOC_5_gh:noexport: +- [[#tree-to-dot][Tree to dot]] + - [[#presentation][Presentation]] + - [[#usage][Usage]] + - [[#tldr][TL;DR]] + - [[#elisp][Elisp]] + - [[#scheme][Scheme]] + - [[#more-details][More details]] + - [[#elisp-1][Elisp]] + - [[#scheme-1][Scheme]] + - [[#my-elements-are-not-alignedcentered-what-do][My elements are not aligned/centered, what do?]] + - [[#how-can-i-do-that-on-windows][How can I do that on Windows?]] + - [[#scheme-2][Scheme]] + - [[#elisp-2][Elisp]] +- [[#license][License]] + +* Tree to dot ** Presentation - *Features Tree* is a small utility for linguists and especially conlangers - that allows them to declare trees with any number of children per node. I + *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. @@ -18,163 +34,213 @@ *** TL;DR - In your *NIX terminal, clone the project, edit the example trees or create a - new one, and execute this: - #+BEGIN_SRC sh - 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 - #+END_SRC +**** 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: + #+BEGIN_SRC sh + 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 + #+END_SRC *** More details - 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. +**** Elisp - You will have to declare a Scheme list containing your tree, and your - typical node should be declared like so: - #+BEGIN_SRC scheme - ("text" (child1) (child2) ...) - #+END_SRC - 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: - #+BEGIN_SRC scheme - ("text") - #+END_SRC - As an example, here is the tree that was used to declare the first example - image: - #+BEGIN_SRC scheme - (define vowels - '("[vowel]" - ("[back]" - ("[tense]" - ("[high]" ("ü")) - ("{high}" ("ö"))) - ("{tense}" - ("[high]" ("u")) - ("{high}" ("o")))) - ("{back}" - ("[tense]" - ("[high]" ("y")) - ("{high}" ("ë"))) - ("{tense}" - ("[high]" ("i")) - ("{high}" ("e")))))) - #+END_SRC - And here is the source code of the second example image: - #+BEGIN_SRC scheme - (define syntax-tree '("S" - ("Obl") - ("S'" - ("NPerg" - ("NP")) - ("VP" - ("NPdat" + 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. + #+BEGIN_SRC org + ,#+NAME: process-tree + ,#+BEGIN_SRC emacs-lisp :exports none :noweb yes + Code here! + ,#+END_SRC + #+END_SRC + Don’t 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: + #+BEGIN_SRC org + ,#+NAME: my-tree + ,#+BEGIN_SRC emacs-lisp :noweb yes :exports none + (defvar mytree + '("Tree" + ("First child" + ("First child’s child")) + ("Second child" + ("Second child’s first child") + ("Second child’s second child")))) + ,#+END_SRC + #+END_SRC + + You can now finally create one last code block in order to get your image: + #+BEGIN_SRC org + ,#+BEGIN_SRC dot :file whatever.png :var input=my-tree :exports results + $input + ,#+END_SRC + #+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 ([[https://www.orgmode.org/worg/org-contrib/babel/languages/ob-doc-dot.html][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: + #+BEGIN_SRC scheme + ("text" (child1) (child2) ...) + #+END_SRC + 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: + #+BEGIN_SRC scheme + ("text") + #+END_SRC + As an example, here is the tree that was used to declare the first example + image: + #+BEGIN_SRC scheme + (define vowels + '("[vowel]" + ("[back]" + ("[tense]" + ("[high]" ("ü")) + ("{high}" ("ö"))) + ("{tense}" + ("[high]" ("u")) + ("{high}" ("o")))) + ("{back}" + ("[tense]" + ("[high]" ("y")) + ("{high}" ("ë"))) + ("{tense}" + ("[high]" ("i")) + ("{high}" ("e")))))) + #+END_SRC + And here is the source code of the second example image: + #+BEGIN_SRC scheme + (define syntax-tree '("S" + ("Obl") + ("S'" + ("NPerg" ("NP")) - ("VP'" - ("NPabs" - ("NP" - ("S") - ("NP'" - ("Adj") - ("N")))) - ("V'" - ("Mood") - ("Tense") - ("V") - ("Neg"))))))) - #+END_SRC + ("VP" + ("NPdat" + ("NP")) + ("VP'" + ("NPabs" + ("NP" + ("S") + ("NP'" + ("Adj") + ("N")))) + ("V'" + ("Mood") + ("Tense") + ("V") + ("Neg"))))))) + #+END_SRC - Once you’ve 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)=. + Once you’ve 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!* + *Only one =(tree-to-dot)= call can be done at once, else what follows might + break!* - Once you’ve done that, compile your file! I personally use [[https://call-cc.org/][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 compiler’s 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). + Once you’ve done that, compile your file! I personally use [[https://call-cc.org/][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 compiler’s 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 you’ve 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. - #+BEGIN_SRC dot - 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; - } - #+END_SRC - 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, I’d 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 that you’ve 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. + #+BEGIN_SRC dot + 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; + } + #+END_SRC + 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, I’d 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 [[https://graphviz.org/][Graphviz]]’s dot tool installed to generate images. In - your terminal, either redirect the output of your newly compiled program - like so: - #+BEGIN_SRC sh - ./features-tree | dot -Tpng -o output.png - #+END_SRC - Or simply copy and paste the output in a separate file, then only run the - dot part of the above command. You’ve got an ~output.png~ file containing - your tree now! + Now, you need to have [[https://graphviz.org/][Graphviz]]’s dot tool installed to generate images. In + your terminal, either redirect the output of your newly compiled program + like so: + #+BEGIN_SRC sh + ./features-tree | dot -Tpng -o output.png + #+END_SRC + Or simply copy and paste the output in a separate file, then only run the + dot part of the above command. You’ve got an ~output.png~ file containing + your tree now! *** My elements are not aligned/centered, what do? @@ -184,10 +250,17 @@ *** How can I do that on Windows? - 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. +**** 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 you’ve configured + properly org-babel. I haven’t tested it though. * License diff --git a/tree-to-dot.el b/tree-to-dot.el new file mode 100644 index 0000000..e8a06b4 --- /dev/null +++ b/tree-to-dot.el @@ -0,0 +1,50 @@ +(defun declare-node (node-text node-generation) + "Declares a node in the graphviz source code. The node’s identifier will be +~node-generation~, and it will bear the label ~node-text~." + (concat (number-to-string node-generation) + "[label=\"" + node-text + "\"];")) + +(defun make-link (previous-node current-node) + "This creates a link in the graphviz source code between the two nodes +bearing ~previous-node~ and ~current-node~ respectively as their node +identifier." + (concat (number-to-string previous-node) " -- " + (number-to-string current-node) ";")) + +(defun tree-to-dot-helper (tree current-generation previous-generation) + "Helper to ~tree-to-dot~ that translates an Elisp tree with any number of +children per node to a corresponding graphviz file that can be executed from +dot. +Arguments: +- tree :: tree-to-convert +- current-generation :: Generation number, incremented when changing from a node + to another node from the same generation, multiplied by 10 when going from + a node to one of its children. +- previous-generation :: generation number from previous named node" + (cond + ((null tree) "") + ((atom (car tree)) ;; '("text" () () ()) + (concat (declare-node (car tree) current-generation) + (make-link previous-generation current-generation) + (tree-to-dot-helper (cdr tree) + (+ 1 (* 10 current-generation)) + current-generation))) + ((listp (car tree)) ;; '(() () ()) + (concat (tree-to-dot-helper (car tree) ;; child of current node + current-generation + previous-generation) + (tree-to-dot-helper (cdr tree) + (+ 1 current-generation) + previous-generation))))) + +(defun tree-to-dot (tree) + "Returns a graphviz’s dot compatible string representing an Elisp tree" + (interactive) + (if (null tree) "" + (concat + "graph{node[shape=plaintext];graph[bgcolor=\"transparent\"];" + (declare-node (car tree) 0) + (tree-to-dot-helper (cdr tree) 1 0) + "}"))) diff --git a/features-tree.scm b/tree-to-dot.scm similarity index 100% rename from features-tree.scm rename to tree-to-dot.scm