SequenceParser - Query Validator
/**
* Copyright jozefchutka ( http://wonderfl.net/user/jozefchutka )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/b3gd
*/
package
{
import flash.ui.Keyboard;
import flash.events.KeyboardEvent;
import flash.text.TextFieldType;
import flash.text.TextField;
import flash.display.Sprite;
import flash.text.TextFormat;
[SWF(width="465", height="465", frameRate="30", backgroundColor="#ffffff")]
public class WonderflApp extends Sprite
{
private var textField:TextField = new TextField;
private var textInput:TextField = new TextField;
public function WonderflApp():void
{
textInput.width = stage.stageWidth - 1;
textInput.height = 23;
textInput.type = TextFieldType.INPUT;
textInput.text = "a b c anD b or (c ed Or ((a) AND de*f) and andor)";
textInput.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
textInput.border = true;
addChild(textInput)
textField.y = 25;
textField.width = stage.stageWidth;
textField.height = stage.stageHeight - 25;
textField.wordWrap = true;
textField.multiline = true;
textField.defaultTextFormat = new TextFormat(null, 13);
addChild(textField);
parse();
}
private function onKeyDown(event:KeyboardEvent):void
{
if(event.keyCode == Keyboard.ENTER)
parse();
}
private function parse():void
{
var input:String = textInput.text;
var output:String;
try
{
output = SimpleQueryParser.parse(input, expressionCallback);
}
catch(error:Error)
{
output = error.message;
}
textField.htmlText = output +
"\n\nupdate text input and hit enter.";
}
private function expressionCallback(value:String):String
{
return "<b><u>" + value + "</u></b>";
}
}
}
class SimpleQueryParser
{
private var expression:String = "";
private var expressionCallback:Function;
private var pendingExpression:String = "";
private var openedBrackets:uint = 0;
private var isBracketStart:Boolean;
private var isBracketEnd:Boolean;
public static function parse(input:String, callback:Function=null):String
{
var parser:SimpleQueryParser = new SimpleQueryParser;
parser.expressionCallback = callback;
var sequences:Vector.<ISequence> = new Vector.<ISequence>;
sequences.push(new StartStringEndStringSequence("(", ")", sequences, false, parser.bracketStart, parser.bracketEnd));
sequences.push(new MatchRegexpSequence(/^and\b/i, false, parser.andCallback));
sequences.push(new MatchRegexpSequence(/^or\b/i, false, parser.orCallback));
sequences.push(new MatchRegexpSequence(/^[^\s\(\)]+/i, false, parser.anythingCallback));
sequences.push(new MatchStringSequence(")", false, parser.unexpectedBracketEndCallback));
sequences.push(new MatchAnythingSequence(false, parser.anythingCallback));
SequenceParser.parse(input, sequences);
parser.evaluatePendingExpression();
if(parser.openedBrackets)
throw new Error("Invalid expression. Missing " + parser.openedBrackets + " closing bracket(s).");
return parser.expression;
}
private function evaluatePendingExpression():void
{
pendingExpression = pendingExpression.replace(/[\s]+/g, " ");
pendingExpression = pendingExpression.replace(/^[\s]+/, "");
pendingExpression = pendingExpression.replace(/[\s]+$/, "");
if(pendingExpression && expressionCallback != null)
append(expressionCallback(pendingExpression));
else if(pendingExpression)
append(pendingExpression);
pendingExpression = "";
}
private function append(value:String):void
{
if(!expression
|| expression.charAt(expression.length - 1) == "("
|| expression.charAt(expression.length - 1) == ")"
|| value == ")")
expression += value;
else
expression += " " + value;
}
private function anythingCallback(value:String):void
{
if(!pendingExpression)
value = value.replace(/^[\s]+/, "");
pendingExpression += value;
if(value)
isBracketStart = isBracketEnd = false;
}
private function bracketStart(value:String):void
{
if(pendingExpression && !isBracketStart)
throw new Error("Unexpected bracket start. Missing operator before opening bracket.");
openedBrackets++;
evaluatePendingExpression();
append(value);
isBracketStart = true;
isBracketEnd = false;
}
private function bracketEnd(value:String):void
{
if(!pendingExpression && !isBracketEnd)
throw new Error("Unexpected bracket end. Missing expression before closing bracket.");
openedBrackets--;
evaluatePendingExpression();
append(value);
isBracketStart = false;
isBracketEnd = true;
}
private function andCallback(value:String):void
{
if(!pendingExpression && !isBracketEnd)
throw new Error("Unexpected operator. Missing expression before operator.");
evaluatePendingExpression();
append(isBracketEnd ? " AND" : "AND");
isBracketStart = isBracketEnd = false;
}
private function orCallback(value:String):void
{
if(!pendingExpression && !isBracketEnd)
throw new Error("Unexpected operator. Missing expression before operator.");
evaluatePendingExpression();
append(isBracketEnd ? " OR" : "OR");
isBracketStart = isBracketEnd = false;
}
private function unexpectedBracketEndCallback(value:String):void
{
throw new Error("Unexpected closing bracket. No bracket was opened.");
}
}
class SequenceParser
{
public static function parse(input:String, sequences:Vector.<ISequence>):String
{
if(input == null || !input.length
|| !sequences || !sequences.length)
return input;
var source:String = input;
var i:uint = 0;
while(true)
{
var sequence:ISequence = sequences[i++];
var match:String = sequence.test(source);
if(match != null)
{
source = source.substr(match.length);
source = parse(source, sequence.sequences);
if(sequence.stopSequence)
break;
i = 0;
}
else if(i == sequences.length)
break;
}
if(source == input)
throw new Error("Unmatching sequences");
return source;
}
}
interface ISequence
{
function test(input:String):String
function get sequences():Vector.<ISequence>
function get stopSequence():Boolean
}
class StartStringEndStringSequence implements ISequence
{
private var startSequence:MatchStringSequence;
private var endSequence:MatchStringSequence;
private var _sequences:Vector.<ISequence>;
private var _stopSequence:Boolean;
public function StartStringEndStringSequence(start:String, end:String,
sequences:Vector.<ISequence> = null, stopSequence:Boolean = false,
startCallback:Function = null, endCallback:Function = null)
{
startSequence = new MatchStringSequence(start, false, startCallback);
endSequence = new MatchStringSequence(end, true, endCallback);
_sequences = sequences;
_stopSequence = stopSequence;
}
public function get sequences():Vector.<ISequence>
{
var result:Vector.<ISequence> = _sequences
? _sequences.concat() : new Vector.<ISequence>;
result.splice(0, 0, endSequence);
return result;
}
public function test(input:String):String
{
return startSequence.test(input);
}
public function get stopSequence():Boolean
{
return _stopSequence;
}
}
class MatchStringSequence implements ISequence
{
private var match:String;
private var _stopSequence:Boolean;
private var matchCallback:Function;
public function MatchStringSequence(match:String,
stopSequence:Boolean = false, matchCallback:Function = null)
{
this.match = match;
_stopSequence = stopSequence;
this.matchCallback = matchCallback;
}
public function test(input:String):String
{
if(input.substr(0, match.length) == match)
{
if(matchCallback != null)
matchCallback(match);
return match;
}
return null;
}
public function get sequences():Vector.<ISequence>
{
return null;
}
public function get stopSequence():Boolean
{
return _stopSequence;
}
}
class MatchRegexpSequence implements ISequence
{
private var match:RegExp;
private var _stopSequence:Boolean;
private var matchCallback:Function;
public function MatchRegexpSequence(match:RegExp,
stopSequence:Boolean = false, matchCallback:Function = null)
{
this.match = match;
_stopSequence = stopSequence;
this.matchCallback = matchCallback;
}
public function test(input:String):String
{
var matches:Array = input.match(this.match);
if(matches)
{
var match:String = matches[0];
if(matchCallback != null)
matchCallback(match);
return match;
}
return null;
}
public function get sequences():Vector.<ISequence>
{
return null;
}
public function get stopSequence():Boolean
{
return _stopSequence;
}
}
class MatchAnythingSequence implements ISequence
{
private var _stopSequence:Boolean;
private var matchCallback:Function;
public function MatchAnythingSequence(stopSequence:Boolean = false,
matchCallback:Function = null)
{
_stopSequence = stopSequence;
this.matchCallback = matchCallback;
}
public function test(input:String):String
{
if(input == null || !input.length)
return null;
var match:String = input.substr(0, 1);
if(matchCallback != null)
matchCallback(match);
return match;
}
public function get sequences():Vector.<ISequence>
{
return null;
}
public function get stopSequence():Boolean
{
return _stopSequence;
}
}