1/*
2 * Copyright © 2016-2018 Sören Tempel
3 *
4 * This program is free software: you can redistribute it and/or
5 * modify it under the terms of the GNU Affero General Public
6 * License as published by the Free Software Foundation, either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22
23#include <sys/types.h>
24
25#include "turing.h"
26#include "parser.h"
27#include "util.h"
28
29/**
30 * Shape used for normal states. Normal states are all states that are
31 * neither an accepting nor an initial state.
32 */
33static char *nodeshape = "circle";
34
35/**
36 * Shape used for the initial node.
37 */
38static char *initialshape = "diamond";
39
40/**
41 * Shape used for the accepting nodes.
42 */
43static char *acceptingshape = "doublecircle";
44
45/**
46 * Writes the dot language reprasentation for a given transition
47 * from a given state to a given stream.
48 *
49 * @param trans Transition to create dot markup for.
50 * @param state State the transition belongs to.
51 * @param arg Void pointer to a stream the output should be written to.
52 */
53static void
54exporttrans(tmtrans *trans, tmstate *state, void *arg)
55{
56 fprintf(arg, "q%d -> q%d [label=\"%c/%c/%c\"];\n",
57 state->name, trans->nextstate,
58 trans->rsym, trans->wsym,
59 dirstr(trans->headdir));
60}
61
62/**
63 * Writes the dot language reprasentation for a given state
64 * to a given stream.
65 *
66 * @param state State to create dot markup for.
67 * @param arg Void pointer to a stream the output should be written to.
68 */
69static void
70exportstate(tmstate *state, void *arg)
71{
72 eachtrans(state, exporttrans, arg);
73}
74
75/**
76 * Writes the dot language reprasentation for a given turing machine
77 * to a given stream.
78 *
79 * @param tm Turing machine to create dot markup for.
80 * @param stream Stream to write dot markup to.
81 */
82static void
83export(dtm *tm, FILE *stream)
84{
85 fprintf(stream, "digraph G {\nrankdir = \"LR\";\n\n");
86
87 fprintf(stream, "node [shape = %s];\n", initialshape);
88 fprintf(stream, "q%d;\n", tm->start);
89
90 fprintf(stream, "\nnode [shape = %s];\n", acceptingshape);
91 for (size_t i = 0; i < tm->acceptsiz; i++)
92 fprintf(stream, "q%d;\n", tm->accept[i]);
93
94 fprintf(stream, "\nnode [shape = %s];\n", nodeshape);
95 eachstate(tm, exportstate, stream);
96 fprintf(stream, "}\n");
97}
98
99/**
100 * Writes the usage string for this program to stderr and terminates
101 * the programm with EXIT_FAILURE.
102 */
103static void
104usage(char *prog)
105{
106 fprintf(stderr, "USAGE: %s %s\n", prog,
107 "[-s nodeshape] [-i initialshape]\n"
108 "\t[-a acceptingshape] [-o path] [-h|-v] FILE");
109
110 exit(EXIT_FAILURE);
111}
112
113/**
114 * The main function invoked when the program is started.
115 *
116 * @param argc Amount of command line parameters.
117 * @param argv Command line parameters.
118 */
119int
120main(int argc, char **argv)
121{
122 int opt;
123 parerr ret;
124 dtm *tm;
125 parser *par;
126 char *fc, *fp;
127 FILE *ofd;
128 ssize_t len;
129
130 ofd = stdout;
131 while ((opt = getopt(argc, argv, "s:i:a:o:hv")) != -1) {
132 switch (opt) {
133 case 's':
134 nodeshape = optarg;
135 break;
136 case 'i':
137 initialshape = optarg;
138 break;
139 case 'a':
140 acceptingshape = optarg;
141 break;
142 case 'o':
143 if (!(ofd = fopen(optarg, "w")))
144 die("couldn't open output file");
145 break;
146 case 'v':
147 fprintf(stderr, "tmsim-"VERSION"\n");
148 return EXIT_FAILURE;
149 case 'h':
150 default:
151 usage(argv[0]);
152 }
153 }
154
155 if (argc <= 1 || optind >= argc)
156 usage(argv[0]);
157
158 fp = argv[optind];
159 if ((len = readfile(&fc, fp)) == -1)
160 die("couldn't read from input file");
161 par = newparser(fc, (size_t)len);
162
163 tm = newtm();
164 if ((ret = parsetm(par, tm)) != PAR_OK) {
165 strparerr(par, ret, fp, stdout);
166 return EXIT_FAILURE;
167 }
168 freeparser(par);
169
170 export(tm, ofd);
171 return EXIT_SUCCESS;
172}