Week 7: Scribby’s First Steps

This week, I worked solely on the Prolog natural language parsing component of the project.  This is basically a set of grammar rules along with very simple code-generation based on the results of the parse. I made quite a bit of progress this week… Basic movement commands are now parsed, along with quantities and conversions of a handful of different units. For example, you can now tell the Scribbler such things as:

* go forward for 3 seconds and then turn left
* drive forward three feet and then turn right 180 degrees
* move backwards 6.5 inches

And so on and so forth. Here is a sample run:

Enter a command: go forward two feet, turn right 90 degrees, drive backwards for three seconds
Prolog query executed: parseToCode([go,forward,two,feet,and,turn,right,90,degrees,and,drive,backwards,for,three,seconds], Status, CodeOut).
Status: valid
CodeOut: [‘forward(1.0, 4.1)’, ‘turnRight(1.0, 0.7875)’, ‘backward(1.0, 3)’]

A big component that I had to work on this week was english -> integer translation. It actually requires quite a bit of code to convert an English phrase like “nine hundred forty two point threee” to “942.3”!

My next task is to add some “fun” commands to the program; beeping, taking pictures, and maybe a wander/doodle capability.  I would also like to add some flavorful error messages to return upon a failed parse.

Another potential goal is to add conditional statements to the parser, i.e. parsing of a statement like “go forward until you hit a wall and then turn left and take a picture.”

Here is the source code for the Prolog parser as it stands today…

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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
% Natural Language Parser for Scribbler II Robot
% by Justin Mangue, 2013
%
% References:
% * "The Art of Prolog", Sterling & Shapiro, 1986.
 
parseToCode(Sentence, Status, CodeListOut) :-
    compound_sentence(ParseList, Sentence, []),
    Status = valid,
    generate_code_list(ParseList, [], CodeListOut),
    !.
 
parseToCode(Sentence, Status, CodeOut) :-
    command_phrase(Parse, Sentence, []),
    Status = valid,
    generate_code_list([Parse], [], CodeOut),
    !.
 
parseToCode(Sentence, Status, CodeOut) :-
    + command_phrase(_, Sentence, []),
    Status = invalid_sentence,
    CodeOut = 'null',
    !.
 
generate_code_list([], CodeList, CodeList).
generate_code_list([H|T], CodeList, CodeListOut) :-
    generate_code(H, CodeLineOut),
    append(CodeList, [CodeLineOut], NewCodeList),
    generate_code_list(T, NewCodeList, CodeListOut).
 
generate_code(move(forward,S), CodeOut) :-
    string_to_atom(Keyword, forward),
    CodeOut = [Keyword,1.0,S], !.
 
generate_code(move(backward,S), CodeOut) :-
    string_to_atom(Keyword, backward),
    CodeOut = [Keyword,1.0,S], !.
 
generate_code(turn(left,S), CodeOut) :-
    string_to_atom(Keyword, turnLeft),
    CodeOut = [Keyword,1.0,S], !.
 
generate_code(turn(right,S), CodeOut) :-
    string_to_atom(Keyword, turnRight),
    CodeOut = [Keyword,1.0,S], !.
 
/*
generate_code(pic(Mode), CodeOut) :-
    string_to_atom(Keyword, takePicture),
    CodeOut = [[Keyword, Mode],, */
 
compound_sentence(Compound) -->
    command_phrase(Command),
    connective(and),
    compound_sentence(Sentence),
    { append([Command], [Sentence], Compound_NotFlat) },
    { flatten2(Compound_NotFlat, Compound) }.
 
compound_sentence(Simple) --> command_phrase(Simple).
 
command_phrase(pic("color")) -->
    [take],
    [a],
    [picture].
 
command_phrase(move(D,S)) -->
  action(go),
  direction(D),
  [for],
  unit_to_seconds(S).
 
command_phrase(move(D,S)) -->
  action(go),
  direction(D),
  unit_to_seconds(S).
 
command_phrase(move(D,1.5)) -->
  action(go),
  direction(D).
 
command_phrase(turn(D,S)) -->
  action(turn),
  direction(D),
  unit_to_seconds(S).
 
command_phrase(turn(D,S)) -->
  action(turn),
  direction(D),
  { S is 90*(3.15 / 360) }.  % 90 degrees by default
 
unit_to_seconds(S) -->
    to_number(S), { + S = 1 }, [seconds] ;
    to_number(S), { S = 1 }, [second].
 
unit_to_seconds(S) -->
    to_number(Feet), { + Feet = 1 }, { S is Feet * 2.05 }, [feet] ;
    to_number(Feet), { Feet = 1 }, { S is 2.05 }, [foot].
 
unit_to_seconds(S) -->
    to_number(Inches), { + Inches = 1 }, { S is Inches * (2.05 / 12) }, [inches] ;
    to_number(Inches), { Inches = 1 }, { S is (2.05 / 12) }, [inch].
 
unit_to_seconds(S) -->
    to_number(Degrees), { + Degrees = 1 }, { S is Degrees * (3.15 / 360) }, [degrees] ;
    to_number(Degrees), { Degrees = 1 }, { S is (3.15 / 360) }, [degree].
 
action(go) --> [go].
action(go) --> [move].
action(go) --> [drive].
action(go) --> [roll].
 
action(turn) --> [turn].
action(turn) --> [rotate].
 
action(spin) --> [spin].
 
connective(and) --> [and], [then].
connective(and) --> [and].
connective(and) --> [then].
 
direction(forward) --> [forward].
direction(forward) --> [forwards].
direction(forward) --> [ahead].
direction(backward) --> [backward].
direction(backward) --> [backwards].
direction(left) --> [left].
direction(left) --> [counter-clockwise].
direction(right) --> [right].
direction(right) --> [clockwise].
 
% English number recognition from 0.0 - 9999.9
 
to_number(N) --> num(N1), [point], digit(N2), { N is N1 + (0.1 * N2) }.
to_number(N) --> num(N).
 
num(0) --> [zero].
num(N) --> xxxx(N).
num(N) --> xxx(N).
num(N) --> xx(N).
num(N) --> digit(N).
 
num(N) --> [N], { number(N) }.
 
xxxx(N) --> digit(D), [thousand], xxx(N1), { N is D*1000+N1 }.
xxx(N) --> digit(D), [hundred], rest_xxx(N1), { N is D*100+N1 }.
 
rest_xxx(0) --> [].
rest_xxx(N) --> [and], xx(N).
rest_xxx(N) --> xx(N).
 
xx(N) --> digit(N).
xx(N) --> teen(N).
xx(N) --> tens(T), rest_xx(N1), { N is T+N1 }.
 
rest_xx(0) --> [].
rest_xx(N) --> digit(N).
 
digit(1) --> [one].
digit(2) --> [two].
digit(3) --> [three].
digit(4) --> [four].
digit(5) --> [five].
digit(6) --> [six].
digit(7) --> [seven].
digit(8) --> [eight].
digit(9) --> [nine].
 
teen(10) --> [ten].
teen(11) --> [eleven].
teen(12) --> [twelve].
teen(13) --> [thirteen].
teen(14) --> [fourteen].
teen(15) --> [fifteen].
teen(16) --> [sixteen].
teen(17) --> [seventeen].
teen(18) --> [eighteen].
teen(19) --> [nineteen].
 
tens(20) --> [twenty].
tens(30) --> [thirty].
tens(40) --> [forty].
tens(50) --> [fifty].
tens(60) --> [sixty].
tens(70) --> [seventy].
tens(80) --> [eighty].
tens(90) --> [ninety].
 
sublist(S, L) :-
  append(_, L2, L),
  append(S, _, L2).
 
flatten2([], []) :- !.
flatten2([L|Ls], FlatL) :-
    !,
    flatten2(L, NewL),
    flatten2(Ls, NewLs),
    append(NewL, NewLs, FlatL).
flatten2(L, [L]).
About the Author: Justin
A 34 year old Software Engineer in Seattle, WA with a love for coding, music, video games, and the great outdoors.
Author Website: http://www.justinmangue.com

Leave a Reply

Your email address will not be published. Required fields are marked *