-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtm.nim
171 lines (134 loc) · 5.85 KB
/
tm.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import os, parseutils, strutils, tables
proc handleError(filename: string, lineNum: int, message = "", charNum = "" ) =
echo "Syntax error in " & filename & " at line: " & $lineNum & ":" & charNum
quit message
proc readDefinition(filename: string): (string, seq[string], seq[string], Table[(string, char), (string, char, char)]) =
var
file = readFile(filename).split("\n")
lookUp = initTable[(string, char), (string, char, char)]()
accepting, intermediate, rejecting: seq[string]
firstState: string
stateCount: int
direction: char
lineNum = 1
accepting = @[]
rejecting = @[]
intermediate = @[]
let stateLine = file[0].split(" ")
if stateLine.len != 2:
handleError(filename, lineNum, "First line should contain 2 fields")
if stateLine[0] != "states":
handleError(filename, lineNum, "Expected key word: \"states\" but got \"" & stateLine[0] & "\"")
if parseInt(stateLine[1], stateCount) == 0:
handleError(filename, lineNum, "Expected a number after \"states\"")
for i in countup(1, stateCount):
lineNum.inc
let splitted = file[i].split(" ")
if splitted.len == 2:
if splitted[1] == "+":
accepting.add(splitted[0])
elif splitted[1] == "-":
rejecting.add(splitted[0])
else:
handleError(filename, lineNum, "Expected either \"+\" or \"-\" but got \"" & splitted[1] & "\"")
elif splitted.len != 1:
handleError(filename, lineNum, "Number of symbols on line is incorrect")
else:
intermediate.add(splitted[0])
if i == 1:
firstState = splitted[0]
lineNum.inc
let alphabetLine = file[stateCount+1].split(" ")
if alphabetLine[0] != "alphabet":
handleError(filename, lineNum, "Expected key word: \"alphabet\" but got \"" & alphabetLine[0] & "\"")
var alphabetSize: int
if parseInt(alphabetLine[1], alphabetSize) == 0:
handleError(filename, lineNum, "Expected a number after \"alphabet\"")
let alphabet = alphabetLine[2..^1]
if alphabet.len != alphabetSize:
handleError(filename, lineNum, "Expected alphabet of length: \"" & $alphabetSize & "\" got: \"" & $alphabet.len & "\"")
if alphabet.join().len != alphabetSize:
handleError(filename, lineNum, "Expected character but got string")
for line in file[stateCount + 2..^1]:
if line == "":
continue
lineNum.inc
var params = line.split(" ")
if params.len != 5:
handleError(filename, lineNum, "Row of transition table should have 5 columns, got " & $params.len)
if params[4] == "L":
direction = 'L'
elif params[4] == "R":
direction = 'R'
elif params[4] == "S":
direction = 'S'
else:
handleError(filename, lineNum, "Expected either \"R\" or \"L\" or \"S\" but got \"" & params[4] & "\"")
if params[0] notin intermediate:
handleError(filename, lineNum, "\"" & params[0] & "\" is not an intermediate state")
if params[1] notin alphabet and params[1] != "_":
handleError(filename, lineNum, "\"" & params[1] & "\" not in alphabet")
if params[2] notin intermediate and params[2] notin accepting and params[2] notin rejecting:
handleError(filename, lineNum, "\"" & params[2] & "\" is not a state")
if params[3] notin alphabet and params[3] != "_":
handleError(filename, lineNum, "\"" & params[3] & "\" not in alphabet")
lookUp[(params[0], char(params[1][0]))] = (params[2], char(params[3][0]), direction)
return (firstState, accepting, rejecting, lookUp)
proc runMachine*(descriptionFile: string, inputFile: string, debug: bool, file: bool): (bool, string) =
var
tape = inputFile
oldTape = inputFile
(firstState, accepting, rejecting, lookUp) = readDefinition(descriptionFile)
index = 0
direction: char
moveCount = 0
if file:
tape = inputFile.readFile()
oldTape = inputFile.readFile()
while (firstState notin accepting and firstState notin rejecting):
if index > tape.len - 1:
tape.add("_")
if index < 0:
tape = "_" & tape
index = 0
if tape[index] in Whitespace:
tape[index] = '_'
if debug:
echo "state: \"" & firstState & "\" input: \"" & tape[index] & "\""
echo tape
if not lookUp.hasKey((firstState, tape[index])):
handleError(inputFile, 1, "State \"" & firstState & "\" does not take the input \"" & tape[index] & "\"")
(firstState, tape[index], direction) = lookUp[(firstState, tape[index])]
if direction == 'L':
index.dec
elif direction == 'R':
index.inc
if direction != 'S':
moveCount.inc
when isMainModule:
echo "tape: " & tape
echo "length: " & $tape.len
echo "head moves: " & $moveCount
if firstState in accepting:
when isMainModule:
echo "Input was accepted"
result = (true, tape)
else:
when isMainModule:
echo "Input was rejected"
result = (false, tape)
when isMainModule:
let paramCount = paramCount()
var debug = false
if paramCount <= 1:
quit("Please specify a description file as well as an input file.")
if paramCount == 3:
if paramStr(3) == "DEBUG":
debug = true
else:
quit("Unknown 3rd parameter \"" & paramStr(3) & "\"")
if paramCount > 3:
quit("A maximum of 3 command line arguments are allowed.")
let descriptionFile = paramStr(1)
let inputFile = paramStr(2)
discard runMachine(descriptionFile, inputFile, debug, true)