Skip to content

Latest commit

 

History

History
610 lines (508 loc) · 17.1 KB

readme.org

File metadata and controls

610 lines (508 loc) · 17.1 KB

Syntree

https://melpa.org/packages/syntree-badge.svg

Content

Introduction

syntree is a package for GNU Emacs that draws plain text constituency trees. It is meant to be for plain text what packages like qtree or forest are for LaTeX, providing only those features that make sense for the plain text format.

While it is written with the linguist’s usage in mind, syntree can obviously be used for any sort of tree-like diagram. Here’s an example of what you can get from syntree:

("Sentence"
    "_NP:the man"
    ("VP" "Verb:hit"
    ("NP" "T:the" "N:ball")))

      Sentence
   ._____|_____.
   |           |
   NP          VP
.__|__.    .___|___.
|_____|    |       |
the man  Verb      NP
          hit   .__|__.
                |     |
                T     N
               the  ball

The drawing is done interactively, with the output displayed as you type:

./syntree-demo.gif

It does not provide functionalities such as drawing arrows between nodes. Once the tree is drawn, however, arrows are rather easily added manually with picture-mode (built-in in GNU Emacs).

You can install syntree through melpa.

Usage

Create a new tree

Upon calling the command syntree-new a new frame is created that is split in two windows: the upper one visits the input buffer, the lower one visits the output buffer. Both buffers are in syntree-mode.

At this point, syntree-mode-map is activated in the two buffers (more on this here). The output buffer will redisplay the tree every time the input buffer has changed and a valid syntree input is detected in it (or whenever the value of syntree--current-style is changed by calling some interactive command—more on that below).

Syntax of the input

The input is a substring of the buffer made up by parentheses and objects formatted as strings. This input is read as a list. Thus, these are valid inputs:

("abc" ("def" "ghi"))

("abc" "def" "ghi")

Every list must contain more than one string. These are not valid inputs:

"abc" "def"         ; this is not a list

("abc" ("def" ghi)) ; not everything here is a string

("abc" ("def"))     ; the embedded list contains only one string

The first string in a list is the label of the branching node. Here, the embedded constituent has an empty label:

("XP" "abc" ("" "def" "ghi" "X:xyz"))

      XP
  ____|____
 /         \
 |         |
abc    ____|____
      /    |    \
      |    |    X
     def  ghi  xyz

Terminal nodes can themselves be labeled: the label and the text are separated by a : (i.e., a colon). Here some examples of terminals:

 ("" "XP:abcd")  |   |
                 |  XP
                 | abcd
                 |
-----------------+-------
                 |
 ("" "abcd")     |   |
                 | abcd
                 |
-----------------+-------
                 |
 ("YP" ":abcd")  |  YP
                 |   |
                 | abcd
                 |
-----------------+-------
                 |
 ("YP" ":ab:cd") |  YP
                 |   |
                 | ab:cd

If the label on the terminal node starts with, or is _, the leaf will be under some sort of roof. For linguists, this usually indicates that the internal syntax of that node is ignored.

("DP" "D:a" "_NP:beautiful tree")
("DP" "D:a" "_:beautiful tree")

     DP                      DP
.____|____.             .____|____.
|         |             |         |
D        NP             D  .______|_____.
a  .______|_____.       a  |____________|
   |____________|          beautiful tree
   beautiful tree

Whether you are under a roof or not, if you want to force the text of a terminal node or of a label to be on multiple lines, use \n in the input.

("Modified \n sentence"
    "NP:the man"
    "_VP:vigorously\nhit the ball")


      Modified
      sentence
   ._____|_____.
   |           |
   NP         VP
the man  ._____|____.
         |__________|
          vigorously
         hit the ball

Whether the input string in the buffer is split in different lines or not, and whether it is indented in any way or not is irrelevant.

The interactive commands

These are all the commands that are available in syntree-mode buffers, each with the default keybinding. You can define your own keybindings in syntree-mode-map.

syntree-info

Read the documentation for syntree in the info system.

syntree-change-value (C-c C-v)

Change the value of any properties, with completion, and redisplay the tree according to the new value.

Note that changing values this way does not change the value of the property in the current style, neither permanently nor for the duration of the Emacs session. The value is simply temporarily overwritten. If, after changing a value you reset the style, the change will be discarded.

syntree-increase-padding (M-<right>) and syntree-reduce-padding (M-<left>)

Increase and reduce (if possible) the value of :hspace (see properties).

Note that changing values this way does not change the value of the property in the current style, neither permanently nor for the duration of the Emacs session. The value is simply temporarily overwritten. If, after changing a value you reset the style, the change will be discarded.

syntree-increase-height (M-<down>) and syntree-reduce-height (M-<up>)

Increase and reduce (if possible) the value of :height (see properties).

Note that changing values this way does not change the value of the property in the current style, neither permanently nor for the duration of the Emacs session. The value is simply temporarily overwritten. If, after changing a value you reset the style, the change will be discarded.

syntree-change-style (C-c C-v)

Select a different style defined in syntree-styles-list, with completion, as the current one, and redisplay the tree.

What the current style is right after syntree-new is called is determined by the value of syntree-default-style (more on this here).

syntree-done (C-c C-c)

Once you are satisfied with how the tree looks like, you can call syntree-done: the input and the output (the source for the tree and the tree itself) are added to the kill ring (in that order), the two syntree buffers and their dedicated frame are killed, and point is back in the position it was when syntree-new was called. You can now yank the tree.

Controlling the design of the tree

syntree produces a tree according to a specific style. A style is a plist that associates properties to values. Interactively, one can switch between predefined styles.

Properties

The properties defining how a tree looks like can be divided in two: the first group has booleans, integers or symbols as value, the second has strings or characters. We first examine the first group.

:growing

This property accepts one of these four symbols as value:
  • down
  • up
  • right
  • left

It determines the direction of growth of the tree: for example, if the value is right, the root of the tree will be to the left and the tree will horizontally grow to the right.

:oneline

This property has a boolean value.

If it is t, it means that all terminal nodes will be aligned (horizontally or vertically depending on the value of :growing).

:hspace

This property accepts an integer as value.

It determines the amount of space between two sister nodes. It can be increased or reduced interactively with syntree-increase-padding and syntree-reduce-padding.

Values smaller than 1 are interpreted as the smallest graphically viable value.

Note that if the tree grows horizontally, :hspace ends up determining vertical space between nodes.

:height

This property accepts an integer as value.

It determines the length of the stem connecting mother and daughter nodes. It can be increased or reduced interactively with syntree-increase-height and syntree-reduce-height.

Values smaller than 1 are interpreted as the smallest graphically viable value.

Note that if the tree grows horizontally, :height ends up determining a horizontal dimension despite its name.

:roofwidth

This property accepts an integer as value.

A positive value means that the roof will be larger than the text under it, a negative one, shorter.

:roofwidth 0

    |
.___|___.
|_______|
long leaf

:roofwidth 2

      |
._____|_____.
|___________|
  long leaf

:roofwidth -1

    |
 .__|__.
 |_____|
long leaf

:roofminwidth

This property accepts an integer as value.

It determines what the minimal width of a roof is. If its value is, say, 5, no roof will be drawn (despite what the input is) if the width of the text under it and the value of :roofwidth determine that it would have to be less than 5 characters wide.

Values smaller than 1 are interpreted as the smallest graphically viable value.

:word-wrap

This property accepts an integer as value.

A value smaller than 1 means that strings in the input are never broken in different lines except at new line characters. Any other value causes the string to be word wrapped at that given length: for instance, a value of 4 will cause the string “young boy” to be split in two.

:word-wrap 0

    |
young boy

:word-wrap 4

  |
young
 boy

Note that depending on the value of :growing, the wrapped text will have different apperance. If the trees grows vertically, the text is centered. If it grows horizontally, it is flushed left or right:

:word-wrap 4
:growing right

---young
   boy

:word-wrap 4
:growing left

young
  boy--

:compress

This property has a boolean value.

If it is t, it means that the width or height of the tree (depending on whether the value of :growing is down / up or right / left respectively) is reduced as much as possible. Nodes can end up being “stacked” on each other.

:compress t

   .______|_____.
   |            |
   NP     ._____|____.
the man   |          |
         ADV        VP
     vigorously     hit
                 the ball

:compress nil

   .________|_______.
   |                |
   NP         ._____|____.
the man       |          |
             ADV        VP
         vigorously     hit
                     the ball

String or character properties

These are all properties that determine which character is used to draw each part of the tree. The values can be strings (containing one character) or single characters.

Below is an example tree (vertical, growing down) that illustrates which component of the tree is associated with which property.

:hbranch "1"
:branchcenter "2"
:intersection "3"
:l-intersection "4"
:r-intersection "5"
:stem "6"
:l-stem "7"
:r-stem "8"
:roofstem "9"
:rooftop "A"
:rooftopangle "B"
:roofbottom "C"
:roofbottomangle "D"

("Sentence"
 "_NP:the young women"
 "Aux:may"
 ("VP" "Verb:hit"
  ("NP" "T:the" "N:ball")))

                    Sentence
                        6
                        6
         411111111111111211111111111115
         7              6             8
         6              6            VP
         NP            Aux            6
BAAAAAAAA9AAAAAAAAB    may            6
DCCCCCCCCCCCCCCCCCD             411111211115
  the young women               7          8
                                6          NP
                              Verb         6
                               hit         6
                                       411121115
                                       7       8
                                       6       6
                                       T       N
                                      the    ball

Built-in styles

syntree comes with four built-styles to provide you with a basis to define your own (see here). That is, the predefined value of syntree-styles-list is:

'((:name basic
         :der nil
         :growing down
         :hspace 0
         :one-line nil
         :height 0
         :hbranch "_"
         :branchcenter "|"
         :intersection "."
         :l-intersection "."
         :r-intersection "."
         :stem "|"
         :l-stem "|"
         :r-stem "|"
         :roofstem "|"
         :rooftop "_"
         :rooftopangle "."
         :roofbottom "_"
         :roofbottomangle "|"
         :roofwidth 0
         :roofminwidth 3
         :word-wrap 0
         :compress t)
  (:name horizontal
          :der nil
          :growing right
          :hspace 0
          :one-line nil
          :height 2
          :hbranch "|"
          :branchcenter "+"
          :intersection "+"
          :l-intersection "+"
          :r-intersection "+"
          :stem "-"
          :l-stem "-"
          :r-stem "-"
          :roofstem "|"
          :rooftop "|"
          :rooftopangle "|"
          :roofbottom ""
          :roofbottomangle ""
          :roofwidth 0
          :roofminwidth 1
          :word-wrap 5
          :compress t)
  (:name basic-upwards
          :der basic
          :growing up
          :hbranch "-"
          :branchcenter "+"
          :intersection "+"
          :l-intersection "+"
          :r-intersection "+"
          :rooftop "-"
          :roofbottom " "
          :roofstem "+"
          :roofbottomangle " "
          :rooftopangle "+")
  (:name basic-one-line
          :der basic
          :one-line t))

Define new styles

Defining a new style is a matter of adding a new properly constructed plist to the list syntree-styles-list.

There are two special properties that must be defined in every style:

  • :name
  • :der

Both accept a symbol as value. :name specifies the name of the style, :der specifies the name of the style from which the present one derives.

For example, say that I want to define a new style, new-horizontal, that is identical to the built in one named horizontal except for two things: I want trees in new-horizontal style to grow from right to left and to have all terminals of the tree be aligned.

The briefest way to define new-horizontal is thus:

(add-to-list
 'syntree-styles-list
 '(:name new-horizontal
   :der horizontal
   :one-line t
   :growing left))

All properties that are not defined in new-horizontal are looked up in horizontal.

If the value of :der is nil, then all of the properties described in properties must be defined.

Change default style

By default, basic is the style that trees are designed according to when syntree-new is called. If you want new-horizontal (the one defined here) to be the default, set the value of syntree-default-style accordingly:
(setq syntree-default-style 'new-horizontal)

Org-babel integration

Installing syntree provides you with a syntree src block that you can use like any other source block in you org-mode buffer. By writing an input string in such a block, you can evaluate it like any other source block and the output tree will be returned as the result of the evaluation, under the block.
.#+begin_src syntree
("Sentence"
 "_NP:the young women"
 "Aux:may"
 ("VP" "Verb:hit"
  ("NP" "T:the" "N:ball")))
.#+end_src

You can control the design of the tree, deviating from whatever syntree-default-style specifies, by passing the specifications for :style and all the other properties as parameters of the source block.

For example, the block below when evaluated will return a tree in my-style with value 8 and “l” for :height and :stem respectively.

.#+begin_src syntree :style 'my-style :height 8 :stem "l"
("Sentence"
 "_NP:the young women"
 "Aux:may"
 ("VP" "Verb:hit"
  ("NP" "T:the" "N:ball")))
.#+end_src