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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | % Natural Language Parser for Scribbler II Robot % by Justin Mangue, 2013 % % Uses DCG grammar parsing to parse natural language input into Python code, to be executed on a Scribbler II robot. % % References used: % * "The Art of Prolog", Sterling & Shapiro, 1986. % ParseToCode is the main routine. Takes a sentence and converts it to a list of executable python code. % Call with parseToCode([sentence,as,a,list],Status,Code). parse ToCode ( Sentence , Status , CodeListOut ) :- compound_sentence( ParseList , Sentence , []), Status = valid, generate_code_list( ParseList , [], CodeListOut ), !. parse ToCode ( Sentence , Status , CodeOut ) :- + compound_sentence(_, Sentence , []), Status = invalid_sentence, CodeOut = 'null' , !. %%% GRAMMAR DEFINITIONS %%% % Compound Sentence is the top level. Consists of an optional address statement followed by one or more simple sentences joined by a connective. compound_sentence( Compound ) --> optionally_address(robot), simple_sentence( Command ), connective( and ), compound_sentence( Sentence ), { append([ Command ], [ Sentence ], Compound_NotFlat ) }, { flatten2( Compound_NotFlat , Compound ) }. compound_sentence( Command ) --> optionally_address(robot), simple_sentence( Command ). % A simple sentence is a command phrase, possibly followed by an optional repetition clause. simple_sentence([loop(C,T)]) --> command_phrase(C), (to_number(T), [times] ; to_number_eng(T)). simple_sentence([ Simple ]) --> command_phrase( Simple ). % Command phrases are the basic command-level structure. Most consist of an action and some sort of argument. command_phrase(move(D,S)) --> action(go), direction(D), optional(for), unit_to_seconds(S). command_phrase(move(D,S)) --> action(go), unit_to_seconds(S), direction(D). command_phrase(move(forward,S)) --> action(go), unit_to_seconds(S). command_phrase(move(D,3)) --> action(go), direction(D). command_phrase(turn(D,S)) --> action(turn), optional(to), (optional(the) ; optional(your)), direction(D), unit_to_seconds(S). command_phrase(turn(D,S)) --> action(turn), optional(to), (optional(the) ; optional(your)), direction(D), { S is 90*(3.25 / 360) }. % 90 degrees by default command_phrase(turn(right,D)) --> action(turn), direction(around), { D is 180 * (3.25 / 360) }. % 180 degree turn command_phrase(turn( Dir ,D)) --> action(spin), direction(around), optional(to), (optional(the) ; optional(your)), direction( Dir ), { D is 3.25 }. % 360 degree turn command_phrase(turn(right,D)) --> action(spin), direction(around), { D is 3.25 }. % 360 degree turn command_phrase(pic( Mode )) --> action(take), adposition(a), photomode( Mode ), object(picture). command_phrase(pic(0)) --> % b&w pic by default, for speed reasons action(take), adposition(a), object(picture). command_phrase(wait(S)) --> action(wait), optional(for), unit_to_seconds(S). command_phrase(wait(3.0)) --> action(wait). command_phrase(beep) --> action(beep). command_phrase(moonwalk(S)) --> action(moonwalk), optional(for), unit_to_seconds(S). command_phrase(moonwalk(4.0)) --> action(moonwalk). command_phrase(move_until_wall(D)) --> command_phrase(move(D,_)), condition(until), [you], condition(encounter), object(wall). % Vocabulary/synonym definitions action(go) --> [go] ; [move] ; [drive] ; [roll] ; [scoot]. action(turn) --> [turn] ; [rotate] ; [swivel]. action(spin) --> [spin]. action(take) --> [take] ; [obtain] ; [get] ; [snap]. action(wait) --> [wait] ; [pause] ; [stop]. action(beep) --> [beep]. action(moonwalk) --> [moonwalk]. direction(forward) --> [forward] ; [forwards] ; [ahead] ; [up]. direction(backward) --> [backward] ; [backwards] ; [back]. direction(left) --> [left] ; [counter-clockwise] ; [counter],[clockwise]. direction(right) --> [right] ; [clockwise]. direction(around) --> [around] ; [in],adposition(a),[circle]. adposition(a) --> [a] ; [one]. condition(until) --> [until]. condition(encounter) --> [encounter] ; [reach] ; [sense] ; [hit]. photomode(0) --> [grayscale] ; [greyscale] ; [gray] ; [grey] ; [black],[ and ],[white] ; [black],[&],[white]. photomode(1) --> [color]. object(picture) --> [photo] ; [picture] ; [pic] ; [snapshot]. object(wall) --> [a],[wall] ; [the],[wall] ; [an],[obstacle] ; [something]. pronoun(robot) --> [robot] ; [scribbler]. connective( and ) --> [ and ] ; [then] ; [comma] ; [ and ], [then] ; [comma], [then] ; [comma], [ and ], [then]. % Optional form of address optionally_address(robot) --> (optional(robot) ; optional(scribbler)), optional(comma), optional(please). % Allow a DCG word to be optional optional(X) --> [X] ; []. % Number parsing predicates % 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). to_number_eng(1) --> [once]. to_number_eng(2) --> [twice]. to_number_eng(3) --> [thrice]. 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]. % Scribbler Unit conversions % All of the Myro commands for Scribbler are expressed in seconds, so conversions of other unit types to seconds are required. % Seconds -> Seconds unit_to_seconds(S) --> to_number(S), { + S = 1 }, [seconds] ; to_number(S), { S = 1 }, [second]. % Milliseconds -> Seconds unit_to_seconds( MS ) --> to_number(S), { + S = 1 }, { MS is (S / 1000) }, [milliseconds] ; to_number(S), { S = 1 }, { MS is (S / 1000) },[millisecond]. % Feet -> Seconds 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]. % Inches -> Seconds 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]. % Degrees -> Seconds unit_to_seconds(S) --> to_number( Degrees ), { + Degrees = 1 }, { S is Degrees * (3.25 / 360) }, ([degrees] ; [º]) ; to_number( Degrees ), { Degrees = 1 }, { S is (3.25 / 360) }, ([degree] ; [º]). %%% CODE GENERATION %%% % The top level structure is a code list, which is a list of one or more code fragments to be executed. % This part is pretty messy, but is needed to handle passing instructions back to Python from Prolog. 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(move(left,S), CodeOut ) :- string_to_atom( Keyword1 , turn Left ), string_to_atom( Keyword2 , forward), Ninety is 90*(3.25 / 360), CodeOut = [[ Keyword1 ,1.0, Ninety ],[ Keyword2 ,1.0,S]], !. generate_code(move(right,S), CodeOut ) :- string_to_atom( Keyword1 , turn Right ), string_to_atom( Keyword2 , forward), Ninety is 90*(3.25 / 360), CodeOut = [[ Keyword1 ,1.0, Ninety ],[ Keyword2 ,1.0,S]], !. generate_code(turn(left,S), CodeOut ) :- string_to_atom( Keyword , turn Left ), CodeOut = [[ Keyword ,1.0,S]], !. generate_code(turn(right,S), CodeOut ) :- string_to_atom( Keyword , turn Right ), CodeOut = [[ Keyword ,1.0,S]], !. generate_code(wait(S), CodeOut ) :- string_to_atom( Keyword , wait), CodeOut = [[ Keyword ,S]], !. generate_code(pic( Mode ), CodeOut ) :- string_to_atom( Keyword , take Photo ), CodeOut = [[ Keyword , Mode ]], !. generate_code(beep, CodeOut ) :- string_to_atom( Keyword , beep), CodeOut = [[ Keyword ,0.4, 640]], !. generate_code(loop(C,T), CodeOut ) :- generate_code(C, Code ), append_n_times( Code ,[],T, CodeOut ), !. generate_code(move_until_wall(forward), CodeOut ) :- string_to_atom( Keyword , move_until_wall), CodeOut = [[ Keyword ]], !. generate_code(move_until_wall(left), CodeOut ) :- string_to_atom( Keyword1 , turn Left ), string_to_atom( Keyword2 , move_until_wall), Ninety is 90*(3.25 / 360), CodeOut = [[ Keyword1 ,1.0, Ninety ],[ Keyword2 ]], !. generate_code(move_until_wall(right), CodeOut ) :- string_to_atom( Keyword1 , turn Right ), string_to_atom( Keyword2 , move_until_wall), Ninety is 90*(3.25 / 360), CodeOut = [[ Keyword1 ,1.0, Ninety ],[ Keyword2 ]], !. generate_code(move_until_wall(backward), CodeOut ) :- string_to_atom( Keyword1 , turn Right ), string_to_atom( Keyword2 , move_until_wall), string_to_atom( Keyword3 , turn Left ), OneEighty is 180*(3.25 / 360), CodeOut = [[ Keyword1 ,1.0, OneEighty ],[ Keyword2 ],[ Keyword3 ,1.0, OneEighty ]], !. generate_code(moonwalk(S), CodeOut ) :- string_to_atom( Keyword , moonwalk), CodeOut = [[ Keyword ,S]], !. %%% MISC SUPPORT PREDICATES %%% % Check if S is a sublist of L sublist(S, L) :- append(_, L2 , L), append(S, _, L2 ). % Builds a list of N instances of item C, then returns the result in L append_n_times(_,L,0,L):- !. append_n_times(C,L,N, Result ):- append(L,C, NewL ), NewN is N-1, append_n_times(C, NewL , NewN , Result ). % Flatten a list of lists flatten2([], []) :- !. flatten2([L| Ls ], FlatL ) :- !, flatten2(L, NewL ), flatten2( Ls , NewLs ), append( NewL , NewLs , FlatL ). flatten2(L, [L]). |
Tag Archive for code
nlp.pl
scribpro.py
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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | # coding=latin-1 # # File: scribpro.py # by Justin Mangue # # Description: # This is a script that enables use of a Prolog natural language parsing component to control a Scribbler II robot # over bluetooth. Use in sync with nlp.pl. # # Dependencies: # Python 2.7.4 http://www.python.org/getit/ # PySwip 0.2.3 https://code.google.com/p/pyswip/ # PySpeech https://code.google.com/p/pyspeech/ # Myro 2.9.5 http://myro.roboteducation.org/download/ # Swi-Prolog 6.2.6 http://www.swi-prolog.org/Download.html # IMPORTS from sys import exit from myro import * from pyswip import Prolog import speech as pyspeech # GLOBAL SETTINGS verNum = '0.6' # ScribPro Version Number scribblerPort = 'COM5' # Scribbler Bluetooth Port prologFile = 'nlp.pl' # Prolog file to consult debug = 'on' # Debug mode, if enabled, shows detailed prolog parse information inputMode = "text" # Input in text mode by default outputMode = "text" # Output to text by default # CLASSES class PrologResult( object ): def __init__( self , prologcall, status, pythoncode): self .prologcall = prologcall self .status = status self .pythoncode = pythoncode # FUNCTIONS def strClean(string): """ strClean(str) -- converts "A string like This" into a list of lowercase atoms i.e. [a,string,like,this] for use in Prolog. also converts commas to "and"s """ finalstring = "[" loweredstring = string.lower() exclude = '!"#$%&' () * + - . / :;< = >?@[\]^_`{|}~' for c in exclude: loweredstring = loweredstring.replace(c,"") for ch in loweredstring: if ch = = ' ' : finalstring = finalstring + "," elif ch = = ',' : finalstring = finalstring + ",comma" #elif ch == u'xb0' or ch == '°': # finalstring = finalstring + ",degrees" else : finalstring = finalstring + ch return finalstring + "]" def parse(inStr): """parse(str) -- cleans the user input and then throws it at Prolog for analysis""" codeOut = "null" sentence = strClean(inStr) prologCall = 'parseToCode(' + sentence + ', Status, CodeOut).' for soln in prolog.query(prologCall, maxresult = 1 ): statusOut = ''.join(soln[ "Status" ]) if statusOut = = 'valid' : codeOut = buildMultiCode(soln[ "CodeOut" ]) return PrologResult(prologCall,statusOut,codeOut) def buildCode(prologterms): """converts a [instruction, param1, param2, ...] list into Python-exec-friendly "instruction(param1,param2,...)" functor format""" parameters = [ str (item) for item in prologterms] if len (parameters) > 2 : codeOut = parameters[ 0 ] + '(' for x in range ( len (parameters) - 2 ): codeOut = codeOut + parameters[x + 1 ] + ', ' codeOut = codeOut + parameters[ len (parameters) - 1 ] + ')' elif len (parameters) = = 2 : codeOut = parameters[ 0 ] + '(' + parameters[ 1 ] + ')' else : codeOut = parameters[ 0 ] + '()' return codeOut def buildMultiCode(instructions): """converts a list of [instruction, param1, param2] codes into a list of Python-exec-friendly "instruction(param1,param2,...)" strings i.e. buildMultiCode([('forward',['1.0','3.0']),('stop',[])]) -> ['forward(1.0, 3.0)', 'stop()']""" multiCodeOut = [] for x in instructions: line = buildCode(x) multiCodeOut.append(line) return multiCodeOut def execute(code): # potential security concerns here! """executes multiple lines of python script in sequence""" for line in code: if type (line) is list : execute(line) else : exec line def toggle_inputMode(mode): """toggles between text and voice mode""" if mode = = "text" : print "Now activating voice input." return "voice" else : print "Switching back to text-input mode." return "text" def toggle_outputMode(mode): """toggles between text and voice mode""" if mode = = "text" : print "Now activating voice output." return "voice" else : print "Switching back to text-only output mode." return "text" def toggle_debugMode(): """toggles between debug mode being on/off""" if debug = = "on" : print "Disabling debug mode." return "off" else : print "Enabling debug mode." return "on" # addon robot commands def takePhoto(mode): """Take and display a photo in the specified color mode. 2=color(fast), 1=color, 0=gray""" if mode = = 2 : pic = takePicture( "jpeg-fast" ) elif mode = = 1 : pic = takePicture( "color" ) else : pic = takePicture( "gray" ) show(pic, "Scribby Cam" ) return def move_until_wall(): """Repeatedly moves forward in small increments until wall is sensed""" while not wall(): forward( 1.0 , 0.7 ) return def moonwalk(time): """Moonwalk backwards for time""" while timeRemaining(time): forward(. 25 ,. 1 ) backward( 1 ,. 3 ) # MAIN def main(): # global variable references global debug global prolog global inputMode global outputMode # initialize the robot print "***************** ScribPro v" + verNum + " *****************" init(scribblerPort) print "Scribbler II found on bluetooth (" + scribblerPort + ")." # instantiation of Prolog interface prolog = Prolog() prolog.consult(prologFile) print "Prolog has been initialized." print "Loaded " + prologFile + " into SWI-Prolog!" print "Starting in " + inputMode + " input mode. ['voice' to toggle]" print "Starting in " + outputMode + " output mode. ['sound' to toggle]" print "*************************************************" # main routine while ( 1 ): # grab a line of input, either text or voice if inputMode = = "voice" : try : inStr = pyspeech. input ( "nAwaiting voice command. (Ctrl-C to enter text)" ) print "Voice command: " + inStr + "n" except KeyboardInterrupt: inStr = raw_input ( "Type in a command: " ) else : inStr = raw_input ( "nEnter a command: " ) # parse the input and behave appropriately if (inStr = = 'quit' or inStr = = 'exit' ): print "Exiting..." exit( 0 ) elif inStr = = 'reload' : newprolog = Prolog() newprolog.consult(prologFile) prolog = newprolog print "Reloaded " + prologFile + " into Prolog!" elif inStr = = 'voice' : inputMode = toggle_inputMode(inputMode) elif inStr = = 'sound' : outputMode = toggle_outputMode(outputMode) elif inStr = = 'debug' : debug = toggle_debugMode() else : Result = parse(inStr) if Result.status = = 'valid' : if outputMode = = "voice" : pyspeech.say( "Okay" ) print "Okay." execute(Result.pythoncode) else : if outputMode = = "voice" : errorMsg = "I don't understand " + inStr pyspeech.say(errorMsg) print "I don't understand " " + inStr + " "." # Print detailed debug information if debug = = "on" : print "nProlog query executed:" , Result.prologcall print "Status:" , Result.status print "CodeOut:" , Result.pythoncode if __name__ = = "__main__" : main() |
Prolog implementation of a C-lite parser
Wrote a Clite parser– in Prolog!
Read more