SequenceParser - Query Validator jozefchutka forked:0favorite:1lines:310license : MIT License modified : 2011-10-14 17:15:26 Embed Tweet 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; } } Code Fullscreen Preview Fullscreen xor Error source match String Boolean RegExp message charAt push Date.parse Keyboard.ENTER replace type substr concat KeyboardEvent.KEY_DOWN test keyCode multiline KeyboardEvent