To start: I know this system will have flaws!
NOTE: Im adding a few other languages because I don\'t find this problem specific to php
Parsing natural language is non-trivial, if you want a true natural language parser I'd recommend that you try and use an existing project or library. Here's a web based parser, based on the Stanford Parser. Or wikipedia is a good jumping off point.
Having said that, if you're willing to restrict the syntax and the keywords involved you might be able to simplify it. First you need to know what's important -- you have 'things' (lights, fan) in 'places' (bedroom, kitchen) that need to go into a specific state ('on', 'off').
I would get the string into an array of words, either using str_tok, or just explode on ' '
.
Now you have an array of words start at the end and go backwards looking for a 'state' -- on or off. Then follow that backwards looking for a 'thing', and finally a 'place'. If you hit another state then you can start again.
Let me try and do that in pseudocode:
// array of words is inArray
currentPlace = null;
currentThing = null;
currentState = null;
for (i = (inArray.length - 1); i >= 0; i--) {
word = inArray[i];
if (isState(word)) {
currentState = word;
currentPlace = null;
currentThing = null;
} else if (currentState) {
if (isThing(word)) {
currentThing = word;
currentPlace = null;
} else if (currentThing) {
if (isPlace(word)) {
currentPlace = word
// Apply currentState to currentThing in currentPlace
}
// skip non-place, thing or state word.
}
// Skip when we don't have a thing to go with our state
}
// Skip when we don't have a current state and we haven't found a state
}
And, having written that, it's pretty clear that it should have used a state machine and switch statements -- which goes to show I should have designed it on paper first. If you get anymore complex you want to use a state machine to implement the logic -- states would be 'lookingForState', 'lookingForThing', etc
Also you don't really need currentPlace
as a variable, but I'll leave it as it makes the logic clearer.
EDIT
If you want to support 'turn the lights in the bedroom on' you'll need to be adjust the logic (you need to save the 'place' if when you don't have a thing). If you also want to support 'turn on the lights in the bedroom' you'll need to go even further.
Thinking about it, I wonder if you can just do:
have a currentState variable and arrays for currentPlace and currentThing
for each word
if it's a state:
store it in currentState
if it's a thing, or place:
add it to the approriate array
if currentState is set and there is content in currentPlaces and currentThings:
apply currentState to all currentThings in all currentPlaces
That's not quite there, but one of those implementations might give you a starting point.
EDIT 2
OK, I tested it out and there's a few issues due to the way English is structured. The problem is if you want to support 'Turn on ...' and 'Turn ... on' then you need to use my second pseudo-code but that doesn't work easily because of the 'and's in the sentence. For example:
Turn my kitchen lights on and my bedroom and living room lights off.
The first and joins two statements, the second and joins to places. The correct way to do this is to diagram the sentence to work out what applies to what.
There are two quick options, first you could insist on using a different word or phrase to join two commands:
Turn my kitchen lights on then my bedroom and living room lights off. Turn my kitchen lights on and also my bedroom and living room lights off.
Alternatively, and this is probably easier you can insist on only having commands of the form 'Turn ... off/on'. This works with my first psuedocode above.
JavaScript Example of first psuedocode.
Note, you'll probably need to heavily pre-process the string if there's any chance of punctuation, etc. You might also want to look at replacing 'living room' (and similar two word phrases) with 'livingroom' rather than just matching one word and hoping for the best like I'm doing. Also, the code could be simplified a bit, but I wanted to keep it close to the psuedocode example.
EDIT 3
New Javascript Example
This handles some extra sentences and is cleaned up a bit better, it still relies on the 'state' coming at the end of each clause as that's what it uses as a trigger to apply the actions (this version could probably read forwards instead of backwards). Also, it will not handle something like:
Turn my kitchen fan and my bedroom lights on and living room lights off.
You have to do something more complex to understand the relationship between 'kitchen' and 'fan' and 'bedroom' and 'lights'.
Some combination of those techniques is probably enough to do something fairly impressive, as long as whoever's entering / speaking the commands follows some basic rules.