Added Elisp code, updated README
This commit is contained in:
parent
9eccaba263
commit
a1a7799523
381
README.org
381
README.org
@ -1,11 +1,27 @@
|
|||||||
[[file:https://cdn.rawgit.com/syl20bnr/spacemacs/442d025779da2f62fc86c2082703697714db6514/assets/spacemacs-badge.svg]]
|
[[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
|
** Presentation
|
||||||
|
|
||||||
*Features Tree* is a small utility for linguists and especially conlangers
|
*Tree to dot* is a small utility for linguists and especially conlangers that
|
||||||
that allows them to declare trees with any number of children per node. I
|
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
|
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
|
as presentented by Joseph Windsor in his talk during the /Language Creation
|
||||||
Conference 8/, with examples below.
|
Conference 8/, with examples below.
|
||||||
@ -18,163 +34,213 @@
|
|||||||
|
|
||||||
*** TL;DR
|
*** TL;DR
|
||||||
|
|
||||||
In your *NIX terminal, clone the project, edit the example trees or create a
|
**** Elisp
|
||||||
new one, and execute this:
|
|
||||||
#+BEGIN_SRC sh
|
Load this source code within Emacs, either in a ~emacs-lisp-mode~ buffer or
|
||||||
cd features-tree
|
in a source block in your ~org-mode~ buffer. Create trees, pass them as
|
||||||
chicken-csc features-tree.scm # Compile the .scm file
|
arguments to ~tree-to-dot~, execute, and voilà.
|
||||||
./features-tree | dot -Tpng -o output.png # for PNG output
|
|
||||||
./features-tree | dot -Tsvg -o output.svg # for SVG output
|
**** Scheme
|
||||||
#+END_SRC
|
|
||||||
|
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
|
*** More details
|
||||||
|
|
||||||
For now, the workflow is not the best, as you have to edit yourself the
|
**** Elisp
|
||||||
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
|
The Elisp code was written with the intent of being used from org-mode in
|
||||||
typical node should be declared like so:
|
order to create inline images out of code. Put that source code in a code
|
||||||
#+BEGIN_SRC scheme
|
block in org-mode, and declare it as a noweb block.
|
||||||
("text" (child1) (child2) ...)
|
#+BEGIN_SRC org
|
||||||
#+END_SRC
|
,#+NAME: process-tree
|
||||||
Each child is itself a tree that should follow the same type of declaration,
|
,#+BEGIN_SRC emacs-lisp :exports none :noweb yes
|
||||||
with as many child as you like per node –I discourage you to have more than
|
Code here!
|
||||||
nine children though, otherwise it might break the output. If a node does
|
,#+END_SRC
|
||||||
not have any child, it should be declared like so:
|
#+END_SRC
|
||||||
#+BEGIN_SRC scheme
|
Don’t forget to name your source blocks, it will be important for the noweb
|
||||||
("text")
|
part later.
|
||||||
#+END_SRC
|
|
||||||
As an example, here is the tree that was used to declare the first example
|
Then, you can declare later a beautiful tree in another code block, like
|
||||||
image:
|
so, and call your ~tree-to-dot~ function on it:
|
||||||
#+BEGIN_SRC scheme
|
#+BEGIN_SRC org
|
||||||
(define vowels
|
,#+NAME: my-tree
|
||||||
'("[vowel]"
|
,#+BEGIN_SRC emacs-lisp :noweb yes :exports none
|
||||||
("[back]"
|
(defvar mytree
|
||||||
("[tense]"
|
'("Tree"
|
||||||
("[high]" ("ü"))
|
("First child"
|
||||||
("{high}" ("ö")))
|
("First child’s child"))
|
||||||
("{tense}"
|
("Second child"
|
||||||
("[high]" ("u"))
|
("Second child’s first child")
|
||||||
("{high}" ("o"))))
|
("Second child’s second child"))))
|
||||||
("{back}"
|
,#+END_SRC
|
||||||
("[tense]"
|
#+END_SRC
|
||||||
("[high]" ("y"))
|
|
||||||
("{high}" ("ë")))
|
You can now finally create one last code block in order to get your image:
|
||||||
("{tense}"
|
#+BEGIN_SRC org
|
||||||
("[high]" ("i"))
|
,#+BEGIN_SRC dot :file whatever.png :var input=my-tree :exports results
|
||||||
("{high}" ("e"))))))
|
$input
|
||||||
#+END_SRC
|
,#+END_SRC
|
||||||
And here is the source code of the second example image:
|
#+END_SRC
|
||||||
#+BEGIN_SRC scheme
|
You can now export this last code block only to get your image. It will be
|
||||||
(define syntax-tree '("S"
|
automatically evaluated when you export your org buffer, but you can also
|
||||||
("Obl")
|
manually trigger the evaluation by typing ~C-c C-c~ with your cursor on
|
||||||
("S'"
|
this last code block. Be sure to have enabled the dot language in babel and
|
||||||
("NPerg"
|
to have configured it properly ([[https://www.orgmode.org/worg/org-contrib/babel/languages/ob-doc-dot.html][this]] might help).
|
||||||
("NP"))
|
|
||||||
("VP"
|
**** Scheme
|
||||||
("NPdat"
|
|
||||||
|
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"))
|
("NP"))
|
||||||
("VP'"
|
("VP"
|
||||||
("NPabs"
|
("NPdat"
|
||||||
("NP"
|
("NP"))
|
||||||
("S")
|
("VP'"
|
||||||
("NP'"
|
("NPabs"
|
||||||
("Adj")
|
("NP"
|
||||||
("N"))))
|
("S")
|
||||||
("V'"
|
("NP'"
|
||||||
("Mood")
|
("Adj")
|
||||||
("Tense")
|
("N"))))
|
||||||
("V")
|
("V'"
|
||||||
("Neg")))))))
|
("Mood")
|
||||||
#+END_SRC
|
("Tense")
|
||||||
|
("V")
|
||||||
|
("Neg")))))))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
Once you’ve declared the tree you want to get, modify the last line of the
|
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
|
source code =(tree-to-dot ...)= by replacing the default argument with the
|
||||||
name of your tree. For the first example, we would call
|
name of your tree. For the first example, we would call =(tree-to-dot
|
||||||
=(tree-to-dot vowels)=, while for the second we would call
|
vowels)=, while for the second we would call =(tree-to-dot syntax-tree)=.
|
||||||
=(tree-to-dot syntax-tree)=.
|
|
||||||
|
|
||||||
*Only one =(tree-to-dot)= call can be done at once, else what follows might
|
*Only one =(tree-to-dot)= call can be done at once, else what follows might
|
||||||
break!*
|
break!*
|
||||||
|
|
||||||
Once you’ve done that, compile your file! I personally use [[https://call-cc.org/][Chicken]] as my
|
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
|
Scheme compiler, but if you already have another, you can use your own.
|
||||||
replace my calls to =chicken-csc= by your own compiler’s command. Also, be
|
Just replace my calls to =chicken-csc= by your own compiler’s command.
|
||||||
aware that I use =chicken-csc= as the command for che Chicken compiler, but
|
Also, be aware that I use =chicken-csc= as the command for che Chicken
|
||||||
if you also use Chicken, you might have to call =csc= instead (this might
|
compiler, but if you also use Chicken, you might have to call =csc= instead
|
||||||
mean you have an older version than the one I use).
|
(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
|
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,
|
edits were alright, you should have some text output that looks like this,
|
||||||
except that it will be way more compact.
|
except that it will be way more compact.
|
||||||
#+BEGIN_SRC dot
|
#+BEGIN_SRC dot
|
||||||
graph{
|
graph{
|
||||||
node[shape=plaintext];
|
node[shape=plaintext];
|
||||||
graph[bgcolor="transparent"];
|
graph[bgcolor="transparent"];
|
||||||
0[label="[vowel]"];
|
0[label="[vowel]"];
|
||||||
1[label="[back]"];
|
1[label="[back]"];
|
||||||
0 -- 1;
|
0 -- 1;
|
||||||
11[label="[tense]"];
|
11[label="[tense]"];
|
||||||
1 -- 11;
|
1 -- 11;
|
||||||
111[label="[high]"];
|
111[label="[high]"];
|
||||||
11 -- 111;
|
11 -- 111;
|
||||||
1111[label="ü"];
|
1111[label="ü"];
|
||||||
111 -- 1111;
|
111 -- 1111;
|
||||||
112[label="{high}"];
|
112[label="{high}"];
|
||||||
11 -- 112;
|
11 -- 112;
|
||||||
1121[label="ö"];
|
1121[label="ö"];
|
||||||
112 -- 1121;
|
112 -- 1121;
|
||||||
12[label="{tense}"];
|
12[label="{tense}"];
|
||||||
1 -- 12;
|
1 -- 12;
|
||||||
121[label="[high]"];
|
121[label="[high]"];
|
||||||
12 -- 121;
|
12 -- 121;
|
||||||
1211[label="u"];
|
1211[label="u"];
|
||||||
121 -- 1211;
|
121 -- 1211;
|
||||||
122[label="{high}"];
|
122[label="{high}"];
|
||||||
12 -- 122;
|
12 -- 122;
|
||||||
1221[label="o"];
|
1221[label="o"];
|
||||||
122 -- 1221;
|
122 -- 1221;
|
||||||
2[label="{back}"];
|
2[label="{back}"];
|
||||||
0 -- 2;
|
0 -- 2;
|
||||||
21[label="[tense]"];
|
21[label="[tense]"];
|
||||||
2 -- 21;
|
2 -- 21;
|
||||||
211[label="[high]"];
|
211[label="[high]"];
|
||||||
21 -- 211;
|
21 -- 211;
|
||||||
2111[label="y"];
|
2111[label="y"];
|
||||||
211 -- 2111;
|
211 -- 2111;
|
||||||
212[label="{high}"];
|
212[label="{high}"];
|
||||||
21 -- 212;
|
21 -- 212;
|
||||||
2121[label="ë"];
|
2121[label="ë"];
|
||||||
212 -- 2121;
|
212 -- 2121;
|
||||||
22[label="{tense}"];
|
22[label="{tense}"];
|
||||||
2 -- 22;
|
2 -- 22;
|
||||||
221[label="[high]"];
|
221[label="[high]"];
|
||||||
22 -- 221;
|
22 -- 221;
|
||||||
2211[label="i"];
|
2211[label="i"];
|
||||||
221 -- 2211;
|
221 -- 2211;
|
||||||
222[label="{high}"];
|
222[label="{high}"];
|
||||||
22 -- 222;
|
22 -- 222;
|
||||||
2221[label="e"];
|
2221[label="e"];
|
||||||
222 -- 2221;
|
222 -- 2221;
|
||||||
}
|
}
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
If you get some errors, then you fucked up somewhere in your tree, probably
|
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
|
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
|
parenthesis after the name of your tree. Go back to your source file and
|
||||||
that. Also, it might be easier to edit the file if you have a decent text
|
fix that. Also, it might be easier to edit the file if you have a decent
|
||||||
editor, I’d recommend using something along the lines of VS Code, Atom or
|
text editor, I’d recommend using something along the lines of VS Code, Atom
|
||||||
Brackets, or even Emacs if you are not afraid by steep –but extremely
|
or Brackets, or even Emacs if you are not afraid by steep –but extremely
|
||||||
rewarding– learning curves.
|
rewarding– learning curves.
|
||||||
|
|
||||||
Now, you need to have [[https://graphviz.org/][Graphviz]]’s dot tool installed to generate images. In
|
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
|
your terminal, either redirect the output of your newly compiled program
|
||||||
like so:
|
like so:
|
||||||
#+BEGIN_SRC sh
|
#+BEGIN_SRC sh
|
||||||
./features-tree | dot -Tpng -o output.png
|
./features-tree | dot -Tpng -o output.png
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
Or simply copy and paste the output in a separate file, then only run the
|
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
|
dot part of the above command. You’ve got an ~output.png~ file containing
|
||||||
your tree now!
|
your tree now!
|
||||||
|
|
||||||
*** My elements are not aligned/centered, what do?
|
*** My elements are not aligned/centered, what do?
|
||||||
|
|
||||||
@ -184,10 +250,17 @@
|
|||||||
|
|
||||||
*** How can I do that on Windows?
|
*** How can I do that on Windows?
|
||||||
|
|
||||||
IDK. Get a UNIX terminal (like the Linux subsystem, Putty(?) or Cygwin) and
|
**** Scheme
|
||||||
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
|
IDK. Get a UNIX terminal (like the Linux subsystem, Putty(?) or Cygwin) and
|
||||||
request.
|
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
|
* License
|
||||||
|
|
||||||
|
50
tree-to-dot.el
Normal file
50
tree-to-dot.el
Normal file
@ -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)
|
||||||
|
"}")))
|
Reference in New Issue
Block a user