summaryrefslogtreecommitdiff
path: root/graphics/jflap2tikz
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /graphics/jflap2tikz
Initial commit
Diffstat (limited to 'graphics/jflap2tikz')
-rw-r--r--graphics/jflap2tikz/CHANGES5
-rw-r--r--graphics/jflap2tikz/JFLAP2TikZ.pdfbin0 -> 195791 bytes
-rw-r--r--graphics/jflap2tikz/JFLAP2TikZ.tex190
-rw-r--r--graphics/jflap2tikz/README74
-rw-r--r--graphics/jflap2tikz/examples/ex0.1a.jff50
-rw-r--r--graphics/jflap2tikz/examples/ex0_1a.pdfbin0 -> 26267 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex0_1a.pngbin0 -> 16349 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex0_1a.tex19
-rw-r--r--graphics/jflap2tikz/examples/ex0_1aGrid100.pdfbin0 -> 26049 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex0_1aGrid100.tex19
-rw-r--r--graphics/jflap2tikz/examples/ex0_1aScale2.pdfbin0 -> 26293 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex0_1aScale2.tex19
-rw-r--r--graphics/jflap2tikz/examples/ex9-anbncn.jff249
-rw-r--r--graphics/jflap2tikz/examples/ex9-anbncn.pdfbin0 -> 38595 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex9-anbncn.pngbin0 -> 55575 bytes
-rw-r--r--graphics/jflap2tikz/examples/ex9-anbncn.tex44
-rw-r--r--graphics/jflap2tikz/jflap2tikz.groovy458
17 files changed, 1127 insertions, 0 deletions
diff --git a/graphics/jflap2tikz/CHANGES b/graphics/jflap2tikz/CHANGES
new file mode 100644
index 0000000000..40b14b20b5
--- /dev/null
+++ b/graphics/jflap2tikz/CHANGES
@@ -0,0 +1,5 @@
+Changes in version 1.2
+Added the command line option -k, for keeping the state names used in the
+JFLAP file. The default behavior is to replace the state names with names of
+the form '$q_{id}$', where id is the unique state number. When using this
+option state names will not be sanitized and thus may contain invalid TeX.
diff --git a/graphics/jflap2tikz/JFLAP2TikZ.pdf b/graphics/jflap2tikz/JFLAP2TikZ.pdf
new file mode 100644
index 0000000000..9395d434c8
--- /dev/null
+++ b/graphics/jflap2tikz/JFLAP2TikZ.pdf
Binary files differ
diff --git a/graphics/jflap2tikz/JFLAP2TikZ.tex b/graphics/jflap2tikz/JFLAP2TikZ.tex
new file mode 100644
index 0000000000..281a39f9f9
--- /dev/null
+++ b/graphics/jflap2tikz/JFLAP2TikZ.tex
@@ -0,0 +1,190 @@
+\documentclass[11pt]{article}
+
+%packages
+\usepackage[colorlinks,urlcolor=blue,linkcolor=blue]{hyperref}
+\usepackage{tikz}
+\usepackage{mathabx}
+\usetikzlibrary{automata,positioning}
+\usepackage{fancyhdr}
+\usepackage[top=0.4in,bottom=0.4in,left=0.75in,right=0.75in,
+ nofoot,head=14pt,headsep=8pt,includeheadfoot] {geometry}
+
+%commands
+\newcommand{\tkz}{Ti{\em k}Z}
+\newcommand{\JFLAP}{JFLAP}
+\newcommand{\jtot}{{\JFLAP}2\tkz}
+\newcommand{\httpURL}[1]{\href{http://#1}{#1}}
+
+%title
+\title{\jtot}
+\author{Andrew Mertz and William Slough}
+\date{}
+
+%header
+\pagestyle{fancy}
+\fancyhead{}
+\fancyfoot{}
+\fancyhead[RO,LE]{\thepage}
+\fancyhead[LO,RE]{\jtot\ v1.1}
+
+\begin{document}
+\maketitle
+\thispagestyle{empty}
+\section*{Introduction}
+
+\href{http://www.jflap.org}{\JFLAP}\footnote{\httpURL{www.jflap.org}} is a
+popular Java program for experimenting with finite state machines, Turing
+machines and other concepts from Formal Languages and Automata Theory. Version
+7 of \JFLAP\ can export JPG, PNG, GIF, BMP, or SVG images. \jtot\ is a
+\href{http://groovy.codehaus.org}{Groovy}\footnote{\httpURL{groovy.codehaus.org}} script
+that converts a \JFLAP\ jff file representing a finite automaton, pushdown
+automaton, or Turing machine into a \LaTeX\ file depicting the automaton
+graphically using \tkz.
+
+\section*{Requirements}
+
+To use \jtot\ you will need \href{http://www.java.com}{Java}
+installed. Additionally, you may find it useful to have
+\href{http://groovy.codehaus.org}{Groovy} installed as well. You will
+need to download either {\tt jflap2tikz.jar} (if you only have Java
+installed) or {\tt jflap2tikz.groovy} (if you also have groovy
+installed).
+
+\section*{Usage}
+
+\jtot\ is invoked from the command line with:
+\begin{quote}
+\begin{verbatim}
+java -jar jflap2tikz.jar -i example.jff
+\end{verbatim}
+\end{quote}
+or:
+\begin{quote}
+\begin{verbatim}
+groovy jflap2tikz.groovy -i example.jff
+\end{verbatim}
+\end{quote}
+In the above cases the output will be written to the console. Use the -h option
+to see the full usage information, which is also given in
+Figure~\ref{fig:usage}.
+
+\begin{figure}
+\begin{center}
+\begin{verbatim}
+usage: jflap2tikz [options]
+Version 1.2
+ -d,--accepting-distance <distance> Distance, in pt, between the circles of an
+ accepting state (default is 2)
+ -g,--grid <size> Round positions so that they are on a
+ grid. If a size is given it sets the
+ spacing of the grid (default is 20.0)
+ -h,--help Show usage information and quit
+ -i,--input-file <filename> Name of a JFLAP jff file representing a
+ finite automaton, pushdown automaton, or
+ Turing machine. If a file is not given
+ standard input will be used.
+ -k,--keep-names Use the state names from the JFLAP file.
+ The default is to replace the state names
+ with names of the form '$q_{id}$', where
+ id is the unique state number. Note state
+ names will not be sanitized and thus may
+ contain invalid TeX.
+ -l,--arrow-length <length> Length of arrows in points (default is 9)
+ -o,--output-file <filename> Name of a file for writing output. If this
+ file already exists it will be
+ overwritten.
+ -r,--rotate Rotate labels along edges
+ -s,--scale <x> 1 pixel in JFLAP = x points in LaTeX
+ (default is 1.0)
+ -w,--arrow-width <width> Width of arrows in points (default is 6)
+ \end{verbatim}
+ \end{center}
+ \caption{Usage information}
+ \label{fig:usage}
+\end{figure}
+
+\section*{Examples}
+
+The following figures show machines from the book
+\href{http://www.jflap.org/jflapbook/}{JFLAP: An Interactive Formal
+ Languages and Automata Package} being converted to \tkz\ using
+\jtot. Note that \jtot\ currently ignores \JFLAP\ state
+annotations. Furthermore, not all \JFLAP\ files will be converted
+perfectly. You may have to adjust the scale and/or gridsize, or edit
+the resulting \tkz\ code to achieve the effect you want. However,
+\jtot\ should provide a good starting point.
+
+\begin{figure}
+ \begin{center}
+ \includegraphics{examples/ex0_1a.png}
+ \end{center}
+ \caption{{\tt ex0.1a.jff}}
+\end{figure}
+
+\begin{figure}
+ \begin{center}
+ \includegraphics{examples/ex0_1a.pdf}
+ \end{center}
+ \caption{{\tt ex0.1a.jff} converted to \tkz\ using default values}
+\end{figure}
+
+\begin{figure}
+ \begin{center}
+ \includegraphics{examples/ex0_1aScale2.pdf}
+ \end{center} \caption{{\tt ex0.1a.jff} converted to \tkz\ using a scale of 2}
+\end{figure}
+
+\begin{figure}
+ \begin{center}
+ \includegraphics{examples/ex0_1aGrid100.pdf}
+ \end{center} \caption{{\tt ex0.1a.jff} converted to \tkz\ using a gridsize of 100}
+\end{figure}
+
+\begin{figure}
+ \begin{center}
+ \includegraphics[width=\textwidth]{examples/ex9-anbncn.png}
+ \end{center}
+ \caption{{\tt ex9-anbncn.jff}}
+\end{figure}
+
+\begin{figure}
+ \begin{center}
+ \includegraphics{examples/ex9-anbncn.pdf}
+ \end{center}
+ \caption{{\tt ex9-anbncn.jff} converted to \tkz\ using a gridsize of
+ 50 and label rotations on}
+\end{figure}
+
+\newpage
+
+\section*{License}
+
+\jtot\ and its documentation are licensed under an MIT style license
+
+\begin{verbatim}
+ Copyright (c) 2009, 2014, 2015 Andrew Mertz and William Slough
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+\end{verbatim}
+
+\end{document}
+
+% LocalWords: JFlap TikZ jff documentclass LocalWords filename gridsize nofoot
+% LocalWords: includeheadfoot anbncn codehaus www jflap jflapbook
+% LocalWords: NONINFRINGEMENT
diff --git a/graphics/jflap2tikz/README b/graphics/jflap2tikz/README
new file mode 100644
index 0000000000..509fa06bfc
--- /dev/null
+++ b/graphics/jflap2tikz/README
@@ -0,0 +1,74 @@
+Introduction
+
+JFLAP is a popular Java program for experimenting with finite state machines,
+Turing machines and other concepts from Formal Languages and Automata Theory.
+Version 7 of JFLAP can export JPG, PNG, GIF, BMP, or SVG images. JFLAP2TikZ is
+a groovy script that converts a JFLAP jff file representing a finite automaton,
+pushdown automaton, or Turing machine into a LaTeX file depicting the automaton
+graphically using TikZ.
+
+Requirements
+
+To use JFLAP2TikZ you will need Java installed. Additionally, you may find it
+useful to have Groovy installed as well. You will need to download either
+jflap2tikz.jar (if you only have Java installed) or jflap2tikz.groovy (if you
+also have groovy installed).
+
+Usage
+
+JFLAP2TikZ is invoked from the command line with:
+
+java -jar jflap2tikz.jar -i example.jff
+
+or:
+
+groovy jflap2tikz.groovy -i example.jff
+
+In the above cases the output will be written to the console. Use the -h option
+to see the full usage information, which is also given below.
+
+usage: jflap2tikz [options]
+Version 1.1
+ -d,--accepting-distance <distance> Distance, in pt, between the circles of an
+ accepting state (default is 2)
+ -g,--grid <size> Round positions so that they are on a
+ grid. If a size is given it sets the
+ spacing of the grid (default is 20.0)
+ -h,--help Show usage information and quit
+ -i,--input-file <filename> Name of a JFLAP jff file representing a
+ finite automaton, pushdown automaton, or
+ Turing machine. If a file is not given
+ standard input will be used.
+ -l,--arrow-length <length> Length of arrows in points (default is 9)
+ -o,--output-file <filename> Name of a file for writing output. If this
+ file already exists it will be
+ overwritten.
+ -r,--rotate Rotate labels along edges
+ -s,--scale <x> 1 pixel in JFLAP = x points in LaTeX
+ (default is 1.0)
+ -w,--arrow-width <width> Width of arrows in points (default is 6)
+
+License
+
+JFLAP2TikZ and its documentation are licensed under an MIT style license
+
+Copyright (c) 2009,2014 Andrew Mertz and William Slough
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/graphics/jflap2tikz/examples/ex0.1a.jff b/graphics/jflap2tikz/examples/ex0.1a.jff
new file mode 100644
index 0000000000..e4068c24db
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0.1a.jff
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><!--Created with JFLAP 6.4.--><structure>
+ <type>fa</type>
+ <automaton>
+ <!--The list of states.-->
+ <state id="0" name="q0">
+ <x>62.0</x>
+ <y>114.0</y>
+ <initial/>
+ </state>
+ <state id="1" name="q1">
+ <x>163.0</x>
+ <y>51.0</y>
+ </state>
+ <state id="2" name="q2">
+ <x>184.0</x>
+ <y>162.0</y>
+ </state>
+ <state id="3" name="q3">
+ <x>275.0</x>
+ <y>92.0</y>
+ <final/>
+ </state>
+ <!--The list of transitions.-->
+ <transition>
+ <from>0</from>
+ <to>2</to>
+ <read>b</read>
+ </transition>
+ <transition>
+ <from>2</from>
+ <to>0</to>
+ <read>a</read>
+ </transition>
+ <transition>
+ <from>1</from>
+ <to>2</to>
+ <read>a</read>
+ </transition>
+ <transition>
+ <from>0</from>
+ <to>1</to>
+ <read/>
+ </transition>
+ <transition>
+ <from>1</from>
+ <to>3</to>
+ <read>a</read>
+ </transition>
+ </automaton>
+</structure> \ No newline at end of file
diff --git a/graphics/jflap2tikz/examples/ex0_1a.pdf b/graphics/jflap2tikz/examples/ex0_1a.pdf
new file mode 100644
index 0000000000..09e99dbb16
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1a.pdf
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex0_1a.png b/graphics/jflap2tikz/examples/ex0_1a.png
new file mode 100644
index 0000000000..a9d221d787
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1a.png
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex0_1a.tex b/graphics/jflap2tikz/examples/ex0_1a.tex
new file mode 100644
index 0000000000..d0032caf99
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1a.tex
@@ -0,0 +1,19 @@
+\documentclass{article}
+\usepackage{tikz}
+\usetikzlibrary{automata,positioning,arrows.meta}
+\usepackage[graphics,tightpage,active,pdftex]{preview}
+\setlength{\PreviewBorder}{5pt}
+\PreviewEnvironment{tikzpicture}
+\begin{document}
+\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto]
+ \draw (62.0pt, -114.0pt)node[state, initial, initial text =](0){$q_{0}$};
+ \draw (163.0pt, -51.0pt)node[state](1){$q_{1}$};
+ \draw (184.0pt, -162.0pt)node[state](2){$q_{2}$};
+ \draw (275.0pt, -92.0pt)node[state, accepting](3){$q_{3}$};
+ \path[->] (0) edge[bend left] node{b}(2);
+ \path[->] (2) edge[bend left] node{a}(0);
+ \path[->] (1) edge node{a}(2);
+ \path[->] (0) edge node{$\lambda$}(1);
+ \path[->] (1) edge node{a}(3);
+\end{tikzpicture}
+\end{document}
diff --git a/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf b/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf
new file mode 100644
index 0000000000..a1ee90038e
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1aGrid100.pdf
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex0_1aGrid100.tex b/graphics/jflap2tikz/examples/ex0_1aGrid100.tex
new file mode 100644
index 0000000000..a0cfc47152
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1aGrid100.tex
@@ -0,0 +1,19 @@
+\documentclass{article}
+\usepackage{tikz}
+\usetikzlibrary{automata,positioning,arrows.meta}
+\usepackage[graphics,tightpage,active,pdftex]{preview}
+\setlength{\PreviewBorder}{5pt}
+\PreviewEnvironment{tikzpicture}
+\begin{document}
+\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto]
+ \draw (100.0pt, -100.0pt)node[state, initial, initial text =](0){$q_{0}$};
+ \draw (200.0pt, -100.0pt)node[state](1){$q_{1}$};
+ \draw (200.0pt, -200.0pt)node[state](2){$q_{2}$};
+ \draw (300.0pt, -100.0pt)node[state, accepting](3){$q_{3}$};
+ \path[->] (0) edge[bend left] node{b}(2);
+ \path[->] (2) edge[bend left] node{a}(0);
+ \path[->] (1) edge node{a}(2);
+ \path[->] (0) edge node{$\lambda$}(1);
+ \path[->] (1) edge node{a}(3);
+\end{tikzpicture}
+\end{document}
diff --git a/graphics/jflap2tikz/examples/ex0_1aScale2.pdf b/graphics/jflap2tikz/examples/ex0_1aScale2.pdf
new file mode 100644
index 0000000000..860b0c8906
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1aScale2.pdf
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex0_1aScale2.tex b/graphics/jflap2tikz/examples/ex0_1aScale2.tex
new file mode 100644
index 0000000000..2c0e8264c2
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex0_1aScale2.tex
@@ -0,0 +1,19 @@
+\documentclass{article}
+\usepackage{tikz}
+\usetikzlibrary{automata,positioning,arrows.meta}
+\usepackage[graphics,tightpage,active,pdftex]{preview}
+\setlength{\PreviewBorder}{5pt}
+\PreviewEnvironment{tikzpicture}
+\begin{document}
+\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt, auto]
+ \draw (124.0pt, -228.0pt)node[state, initial, initial text =](0){$q_{0}$};
+ \draw (326.0pt, -102.0pt)node[state](1){$q_{1}$};
+ \draw (368.0pt, -324.0pt)node[state](2){$q_{2}$};
+ \draw (550.0pt, -184.0pt)node[state, accepting](3){$q_{3}$};
+ \path[->] (0) edge[bend left] node{b}(2);
+ \path[->] (2) edge[bend left] node{a}(0);
+ \path[->] (1) edge node{a}(2);
+ \path[->] (0) edge node{$\lambda$}(1);
+ \path[->] (1) edge node{a}(3);
+\end{tikzpicture}
+\end{document}
diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.jff b/graphics/jflap2tikz/examples/ex9-anbncn.jff
new file mode 100644
index 0000000000..024b7d80ef
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex9-anbncn.jff
@@ -0,0 +1,249 @@
+<?xml version="1.0"?>
+<!-- Created with JFLAP 4.0b10. -->
+<structure>
+ <type>turing</type>
+ <!--The list of states.-->
+ <state id="2">
+ <x>367.0</x>
+ <y>79.0</y>
+ </state>
+ <state id="7">
+ <x>101.0</x>
+ <y>411.0</y>
+ </state>
+ <state id="5">
+ <x>524.0</x>
+ <y>304.0</y>
+ </state>
+ <state id="0">
+ <x>253.0</x>
+ <y>266.0</y>
+ </state>
+ <state id="3">
+ <x>498.0</x>
+ <y>132.0</y>
+ </state>
+ <state id="9">
+ <x>60.0</x>
+ <y>319.0</y>
+ <initial />
+ </state>
+ <state id="10">
+ <x>149.0</x>
+ <y>235.0</y>
+ </state>
+ <state id="4">
+ <x>388.0</x>
+ <y>236.0</y>
+ </state>
+ <state id="1">
+ <x>257.0</x>
+ <y>143.0</y>
+ </state>
+ <state id="8">
+ <x>256.0</x>
+ <y>411.0</y>
+ <final />
+ </state>
+ <state id="6">
+ <x>384.0</x>
+ <y>381.0</y>
+ </state>
+ <!--The list of transitions.-->
+ <transition>
+ <from>5</from>
+ <to>6</to>
+ <read>c</read>
+ <write></write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>10</from>
+ <to>10</to>
+ <read>a</read>
+ <write>a</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>10</from>
+ <to>10</to>
+ <read>b</read>
+ <write>b</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>10</from>
+ <to>10</to>
+ <read>c</read>
+ <write>c</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>6</from>
+ <to>6</to>
+ <read>a</read>
+ <write>a</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>6</from>
+ <to>6</to>
+ <read>b</read>
+ <write>b</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>6</from>
+ <to>6</to>
+ <read>c</read>
+ <write>c</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>6</from>
+ <to>6</to>
+ <read>#</read>
+ <write>#</write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>10</from>
+ <to>0</to>
+ <read></read>
+ <write></write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>4</from>
+ <to>4</to>
+ <read>c</read>
+ <write>c</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>3</from>
+ <to>3</to>
+ <read>b</read>
+ <write>b</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>2</from>
+ <to>2</to>
+ <read>#</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>1</from>
+ <to>1</to>
+ <read>a</read>
+ <write>a</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>7</from>
+ <to>7</to>
+ <read>#</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>9</from>
+ <to>9</to>
+ <read>c</read>
+ <write>c</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>9</from>
+ <to>9</to>
+ <read>b</read>
+ <write>b</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>9</from>
+ <to>9</to>
+ <read>a</read>
+ <write>a</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>1</from>
+ <to>2</to>
+ <read>#</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>6</from>
+ <to>0</to>
+ <read></read>
+ <write></write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>9</from>
+ <to>10</to>
+ <read></read>
+ <write></write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>0</from>
+ <to>1</to>
+ <read>a</read>
+ <write></write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>1</from>
+ <to>3</to>
+ <read>b</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>0</from>
+ <to>7</to>
+ <read>#</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>7</from>
+ <to>8</to>
+ <read></read>
+ <write></write>
+ <move>L</move>
+ </transition>
+ <transition>
+ <from>3</from>
+ <to>4</to>
+ <read>c</read>
+ <write>c</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>2</from>
+ <to>3</to>
+ <read>b</read>
+ <write>#</write>
+ <move>R</move>
+ </transition>
+ <transition>
+ <from>0</from>
+ <to>8</to>
+ <read></read>
+ <write></write>
+ <move>S</move>
+ </transition>
+ <transition>
+ <from>4</from>
+ <to>5</to>
+ <read></read>
+ <write></write>
+ <move>L</move>
+ </transition>
+</structure>
diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.pdf b/graphics/jflap2tikz/examples/ex9-anbncn.pdf
new file mode 100644
index 0000000000..401999f25e
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex9-anbncn.pdf
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.png b/graphics/jflap2tikz/examples/ex9-anbncn.png
new file mode 100644
index 0000000000..26d1999c87
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex9-anbncn.png
Binary files differ
diff --git a/graphics/jflap2tikz/examples/ex9-anbncn.tex b/graphics/jflap2tikz/examples/ex9-anbncn.tex
new file mode 100644
index 0000000000..ad4ed30f52
--- /dev/null
+++ b/graphics/jflap2tikz/examples/ex9-anbncn.tex
@@ -0,0 +1,44 @@
+\documentclass{article}
+\usepackage{tikz}
+\usetikzlibrary{automata,positioning,arrows.meta}
+\usepackage{amssymb}
+
+\usepackage[graphics,tightpage,active,pdftex]{preview}
+\setlength{\PreviewBorder}{5pt}
+\PreviewEnvironment{tikzpicture}
+\begin{document}
+\begin{tikzpicture}[>={Stealth[width=6pt,length=9pt]}, accepting/.style={double distance = 2pt, outer sep = 1pt + \pgflinewidth}, shorten >=1pt]
+ \draw (350.0pt, -100.0pt)node[state](2){$q_{2}$};
+ \draw (100.0pt, -400.0pt)node[state](7){$q_{7}$};
+ \draw (500.0pt, -300.0pt)node[state](5){$q_{5}$};
+ \draw (250.0pt, -250.0pt)node[state](0){$q_{0}$};
+ \draw (500.0pt, -150.0pt)node[state](3){$q_{3}$};
+ \draw (50.0pt, -300.0pt)node[state, initial, initial text =](9){$q_{9}$};
+ \draw (150.0pt, -250.0pt)node[state](10){$q_{10}$};
+ \draw (400.0pt, -250.0pt)node[state](4){$q_{4}$};
+ \draw (250.0pt, -150.0pt)node[state](1){$q_{1}$};
+ \draw (250.0pt, -400.0pt)node[state, accepting](8){$q_{8}$};
+ \draw (400.0pt, -400.0pt)node[state](6){$q_{6}$};
+ \path[->] (5) edge node[sloped,anchor=north]{c : $\Box$, L}(6);
+ \path[->] (10) edge[loop above] node[align=center]{a : a, L\\ b : b, L\\ c : c, L}(10);
+ \path[->] (6) edge[loop above] node[align=center]{a : a, L\\ b : b, L\\ c : c, L\\ \# : \#, L}(6);
+ \path[->] (10) edge node[sloped,anchor=south]{$\Box$ : $\Box$, R}(0);
+ \path[->] (4) edge[loop above] node{c : c, R}(4);
+ \path[->] (3) edge[loop above] node{b : b, R}(3);
+ \path[->] (2) edge[loop above] node{\# : \#, R}(2);
+ \path[->] (1) edge[loop above] node{a : a, R}(1);
+ \path[->] (7) edge[loop above] node{\# : \#, R}(7);
+ \path[->] (9) edge[loop above] node[align=center]{c : c, R\\ b : b, R\\ a : a, R}(9);
+ \path[->] (1) edge node[sloped,anchor=south]{\# : \#, R}(2);
+ \path[->] (6) edge node[sloped,anchor=north]{$\Box$ : $\Box$, R}(0);
+ \path[->] (9) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(10);
+ \path[->] (0) edge node[sloped,anchor=north]{a : $\Box$, R}(1);
+ \path[->] (1) edge node[sloped,anchor=south]{b : \#, R}(3);
+ \path[->] (0) edge node[sloped,anchor=north]{\# : \#, R}(7);
+ \path[->] (7) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(8);
+ \path[->] (3) edge node[sloped,anchor=north]{c : c, R}(4);
+ \path[->] (2) edge node[sloped,anchor=south]{b : \#, R}(3);
+ \path[->] (0) edge node[sloped,anchor=north]{$\Box$ : $\Box$, S}(8);
+ \path[->] (4) edge node[sloped,anchor=south]{$\Box$ : $\Box$, L}(5);
+\end{tikzpicture}
+\end{document}
diff --git a/graphics/jflap2tikz/jflap2tikz.groovy b/graphics/jflap2tikz/jflap2tikz.groovy
new file mode 100644
index 0000000000..c049e5f0f4
--- /dev/null
+++ b/graphics/jflap2tikz/jflap2tikz.groovy
@@ -0,0 +1,458 @@
+/*
+ * This groovy script converts a JFLAP jff file representing a finite automaton
+ * to LaTeX file depicting the automaton graphically using TikZ.
+ *
+ * Copyright (c) 2009, 2014, 2015 Andrew Mertz and William Slough
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Define a few constants
+scale = 1.0 // 1 pixel in JFLAP = scale points in LaTeX
+gridsize = 20.0 // gridsize is measured in pixels
+turingBlank = '$\\Box$' // the symbol to be used for a blank in a TM
+blank = '$\\lambda$' // the symbol to be used for blank otherwise
+arrowWidth = 6 // width of an arrow in points
+arrowLength = 9 // length of an arrow in points
+arrowType = 'Stealth' // arrow type from arrows.meta TikZ library
+acceptingDistance = 2 // distance, in pt, between circles in accepting
+ // state
+version = '1.2'
+infilename = null
+outputStream = null
+
+// Define a function that cleans up text that could be "bad" for LaTeX to
+// ingest
+String fixText(String text)
+{
+ if(text)
+ {
+ text = text.replace('\\', '$\\backslash$')
+ text = text.replace('~', '$\\sim$')
+ text = text.replaceAll('[$%^&_#{}]', {'\\'+ it[0]})
+ }
+ return text
+}
+
+// Setup the command line options
+// TODO: maybe add a switch for arrow type
+// TODO: maybe allow different scales and grid sizes for each axis
+// TODO: maybe allow user to set preview boarder
+cmdLine = new CliBuilder(usage: 'jflap2tikz [options]',
+ header: "Version ${version}",
+ width: 80)
+
+cmdLine.d(longOpt:'accepting-distance',
+ args: 1,
+ argName: 'distance',
+ required: false,
+ "Distance, in pt, between the circles of an accepting state (default is ${acceptingDistance})")
+cmdLine.i(longOpt: 'input-file',
+ args: 1,
+ argName: 'filename',
+ 'Name of a JFLAP jff file representing a finite automaton, pushdown automaton, or Turing machine. If a ' +
+ 'file is not given standard input will be used.')
+cmdLine.g(longOpt:'grid',
+ args:1,
+ optionalArg: true,
+ argName: 'size',
+ required: false,
+ "Round positions so that they are on a grid. If a size is given it sets the spacing of the grid (default " +
+ "is ${gridsize})")
+cmdLine.h(longOpt:'help',
+ required: false,
+ 'Show usage information and quit')
+cmdLine.l(longOpt:'arrow-length',
+ args:1,
+ argName: 'length',
+ required: false,
+ "Length of arrows in points (default is ${arrowLength})")
+cmdLine.o(longOpt: 'output-file',
+ args: 1,
+ argName: 'filename',
+ 'Name of a file for writing output. If this file already exists it will be overwritten.')
+cmdLine.r(longOpt:'rotate',
+ required: false,
+ 'Rotate labels along edges')
+cmdLine.s(longOpt:'scale',
+ args:1,
+ argName:'x',
+ required: false,
+ "1 pixel in JFLAP = x points in LaTeX (default is ${scale})")
+cmdLine.w(longOpt:'arrow-width',
+ args:1,
+ argName:'width',
+ required: false,
+ "Width of arrows in points (default is ${arrowWidth})")
+cmdLine.k(longOpt:'keep-names',
+ required: false,
+ "Use the state names from the JFLAP file. The default is to replace the state names with names of the " +
+ "form '\$q_{id}\$', where id is the unique state number. Note state names will not be sanitized and thus " +
+ "may contain invalid TeX.")
+
+// Parse the command line options
+options = cmdLine.parse(args)
+
+// If there is a parse error, quit. Note usage information will already have
+// been displayed.
+if(!options)
+{
+ System.exit(1)
+}
+
+// If the user has asked for help print the usage information and quit
+if(options.h)
+{
+ cmdLine.usage()
+ System.exit(0)
+}
+
+keapNames = options.k
+
+grid = false
+if(options.g)
+{
+ grid = true
+
+ // Verify that gridsize is valid (a positive double)
+ if(!(options.g instanceof Boolean))
+ {
+ try
+ {
+ gridsize = Double.valueOf(options.g)
+ if (gridsize <= 0) throw new NumberFormatException()
+ }
+ catch(NumberFormatException ex)
+ {
+ System.err.println "error: gridsize must be a positive number"
+ cmdLine.usage()
+ System.exit(2)
+ }
+ }
+}
+
+// Verify that the scale is valid (a positive double)
+try
+{
+ if(options.s)
+ {
+ scale = Double.valueOf(options.s)
+ if (scale <= 0)
+ {
+ throw new NumberFormatException()
+ }
+ }
+}
+catch(NumberFormatException ex)
+{
+ System.err.println "error: scale must be a positive number"
+ cmdLine.usage()
+ System.exit(3)
+}
+
+// Verify that the accepting distance is valid (a positive double)
+try
+{
+ if(options.d)
+ {
+ acceptingDistance = Double.valueOf(options.d)
+ if (acceptingDistance <= 0)
+ {
+ throw new NumberFormatException()
+ }
+ }
+}
+catch(NumberFormatException ex)
+{
+ System.err.println "error: accepting distance must be a positive number"
+ cmdLine.usage()
+ System.exit(4)
+}
+
+if(options.i)
+{
+ infilename = options.i
+}
+
+if(options.o)
+{
+ try
+ {
+ outputStream = new FileWriter(options.o)
+ }
+ catch(Exception e)
+ {
+ System.err.println "error: unable to write to output file ${options.o}"
+ System.exit(5)
+ }
+}
+else
+{
+ outputStream = System.out
+}
+
+// Try to get the text from the file
+if(infilename)
+{
+ try
+ {
+ body = new File(infilename).text
+ }
+ catch(IOException ex)
+ {
+ System.err.println "error: unable to read ${infilename}"
+ System.exit(4)
+ }
+}
+else
+{
+ body = System.in.text
+}
+
+// Parse the XML
+try
+{
+ structure = new XmlParser().parseText(body)
+}
+catch(Exception ex)
+{
+ System.err.println "error: parsing XML"
+ System.exit(5)
+}
+
+// Check the type to be sure it is a machine that can be converted
+type = structure.type.text()
+if(type != "fa" && type != "pda" && type != "turing")
+{
+ System.err.println "error: unsupported machine type --- only FAs, PDAs and Turing machines can be converted"
+ System.exit(6);
+}
+
+// If the automaton is a Turing machine, get the number of tapes
+try
+{
+ tapes = Integer.valueOf(structure.tapes.text())
+}
+catch (Exception ex)
+{
+ tapes = 1
+}
+
+// Find the automaton in the XML tree
+automaton = structure.automaton
+if(!automaton)
+{
+ automaton = structure
+}
+
+// Print the LaTeX header
+outputStream.println """\\documentclass{article}
+\\usepackage{tikz}
+\\usetikzlibrary{automata,positioning,arrows.meta}${type == "turing" ? "\n\\usepackage{amssymb}\n" : ""}
+\\usepackage[graphics,tightpage,active,pdftex]{preview}
+\\setlength{\\PreviewBorder}{5pt}
+\\PreviewEnvironment{tikzpicture}
+\\begin{document}
+\\begin{tikzpicture}[>={Stealth[width=${arrowWidth}pt,length=${arrowLength}pt]}, accepting/.style={double distance = ${acceptingDistance}pt, outer sep = ${acceptingDistance/2.0}pt + \\pgflinewidth}, shorten >=1pt${options.r ? "" : ", auto"}]"""
+
+// For each state in the XML define a TikZ node and record their position
+statePosition = [:]
+automaton.state.each
+{
+ // Get the x and y value of the position, correcting for the change in
+ // coordinate systems
+ x = Double.valueOf(it.x.text()) * scale
+ y = -Double.valueOf(it.y.text()) * scale
+ if(grid)
+ {
+ x = Math.round(x / gridsize) * gridsize
+ y = Math.round(y / gridsize) * gridsize
+ }
+
+ // record position
+ statePosition[it.@id] = [x,y]
+
+ // get state name
+ def stateName
+ if(keapNames)
+ {
+ stateName = it.@name
+ }
+ else
+ {
+ stateName = "\$q_{${fixText(it.@id)}}\$"
+ }
+
+ // Output the TikZ version of this state
+ outputStream.println " \\draw (${x}pt, ${y}pt)" +
+ "node[state${it.initial ? ", initial, initial text =" : ""}" +
+ "${it.get("final") ? ", accepting" : ""}]" +
+ "(${fixText(it.@id)}){$stateName};"
+}
+
+// Define a function that makes a key based on the end points of a transition
+// without taking into account the order of the keys. In other words, edge
+// (a,b) results in the same key as the edge (b,a).
+String makeKey(String a, String b)
+{
+ return a < b ? "${a},${b}" : "${b},${a}"
+}
+
+// Scan transitions and create their labels. Also, check to see if there are
+// edges going in both directions.
+doubleEdges = [:]
+edgeLabels = [:]
+automaton.transition.each
+{
+ orderedKey = [it.from.text(), it.to.text()]
+
+ // Have we seen the reverse of this edge before?
+ if(edgeLabels.get([it.to.text(), it.from.text()]) &&
+ it.to.text() != it.from.text())
+ {
+ doubleEdges.put(makeKey(it.from.text(), it.to.text()), true)
+ }
+
+ // Get the read symbol. If it is blank use lambda for the symbol.
+ if(!(readSymbol = fixText(it.read.text())))
+ {
+ readSymbol = blank
+ }
+
+ // Get the pop and push symbols if the machine is a PDA
+ if(type == "pda")
+ {
+ if(!(popSymbol = fixText(it.pop.text())))
+ {
+ popSymbol = blank
+ }
+ if(!(pushSymbol = fixText(it.push.text())))
+ {
+ pushSymbol = blank
+ }
+ }
+
+ // If the machine is a Turing machine, get all of the read, write, and move
+ // information
+ if(type == "turing")
+ {
+ readSymbols = []
+ it.read.each{r->
+ readSymbols << (r.text() ? fixText(r.text()) : turingBlank)
+ }
+
+ writeSymbols = []
+ it.write.each{w->
+ writeSymbols << (w.text() ? fixText(w.text()) : turingBlank)
+ }
+
+ moves = []
+ it.move.each{m->
+ moves << fixText(m.text())
+ }
+ }
+
+ // Make the label for this transition
+ if(type == "fa")
+ {
+ label = readSymbol;
+ }
+ else if(type == "pda")
+ {
+ label = "${readSymbol}, ${popSymbol}; ${pushSymbol}"
+ }
+ else if(type == "turing")
+ {
+ // Merge the read, write and move lists into one label
+ label = []
+ readSymbols.eachWithIndex{read, i->
+ label << "${read} : ${writeSymbols[i]}, ${moves[i]}"
+ }
+ label = label.join('$|$')
+ }
+
+ // Add this edge's label to the edge label list
+ if((oldLabel = edgeLabels.get(orderedKey)))
+ {
+ oldLabel << label
+ }
+ else
+ {
+ edgeLabels.put(orderedKey, [label])
+ }
+}
+
+// For each transition draw an edge
+edgeLabels.each
+{
+ // Turn the label into a string
+ if(type == "fa")
+ {
+ label = it.value.join(", ")
+ }
+ else
+ {
+ label = it.value.join("\\\\ ")
+ }
+
+ // Construct the options for the node that will hold the label
+ nodeOptions = []
+ if(type != "fa" && it.value.size() > 1) // Are line breaks needed?
+ {
+ nodeOptions << "align=center"
+ }
+ if(options.r && it.key[0] != it.key[1]) // Does the text need to be rotated?
+ {
+ nodeOptions << "sloped"
+
+ // In the case of rotations auto positioning is not used. Thus, an anchor
+ // point for the node must be given.
+ if(statePosition[it.key[0]][0] < statePosition[it.key[1]][0])
+ {
+ nodeOptions << "anchor=south"
+ }
+ else
+ {
+ nodeOptions << "anchor=north"
+ }
+ }
+
+ // Turn the node options into a string. If there are no options then use the
+ // empty string. Otherwise join the option with commas and surround them with
+ // square brackets.
+ if(nodeOptions.size() > 0)
+ {
+ nodeOptions = "[${nodeOptions.join(",")}]"
+ }
+ else
+ {
+ nodeOptions = ""
+ }
+
+ outputStream.println " \\path[->] (${it.key[0]}) edge" +
+ (it.key[0] == it.key[1] ? "[loop above]" : "") + // Do we have a self loop?
+ (doubleEdges.get(makeKey(it.key[0], it.key[1])) ? "[bend left]" : "") + // Do we have more than one edge? If so add a bend.
+ " node${nodeOptions}" +
+ "{${label}}(${it.key[1]});"
+}
+
+// Print the LaTeX footer
+outputStream.println """\\end{tikzpicture}
+\\end{document}"""
+outputStream.close()