/** * Load parser from a file that contains the grammar. * Example: * %skip space \s * * %token word [a-zA-Z]+ * %token number [0-9]+(\.[0-9]+)? * %token open_par \( * %token close_par \) * %token equal = * %token plus \+ * %token minus \- * %token divide \/ * %token times \* * * #equation: * formula() ::equal:: <number> * * formula: * factor() * ( * ::plus:: formula() #addition * | ::minus:: formula() #substraction * )? * * factor: * operand() * ( * ::times:: factor() #product * | ::divide:: factor() #division * )? * * operand: * <word> * | ::minus::? <number> #number * | ::open_par:: formula() ::close_par:: * * Use tabs or spaces, it does not matter. * Instructions follow the form: %<instruction>. Only %skip and %token are * supported. * Rules follow the form: <rule name>:<new line>[<space><rule><new line>]*. * Contexts are useful to set specific skips and tokens. We give a full * example with context + unification (for fun) to parse <a>b</a>: * %skip space \s * %token lt < -> in_tag * %token inner [^<]* * * %skip in_tag:space \s * %token in_tag:slash / * %token in_tag:tagname [^>]+ * %token in_tag:gt > -> default * * #foo: * ::lt:: <tagname[0]> ::gt:: * <inner> * ::lt:: ::slash:: ::tagname[0]:: ::gt:: * * @param \Hoa\Stream\IStream\In $stream Stream that contains the * grammar. * @return \Hoa\Compiler\Llk\Parser * @throws \Hoa\Compiler\Exception */ public static function load(Stream\IStream\In $stream) { $pp = $stream->readAll(); if (empty($pp)) { $message = 'The grammar is empty'; if ($stream instanceof Stream\IStream\Pointable) { if (0 < $stream->tell()) { $message .= ': the stream ' . $stream->getStreamName() . ' is pointable and not rewinded, maybe it ' . 'could be the reason'; } else { $message .= ': nothing to read on the stream ' . $stream->getStreamName(); } } throw new Compiler\Exception($message . '.', 0); } static::parsePP($pp, $tokens, $rawRules); $ruleAnalyzer = new Rule\Analyzer($tokens); $rules = $ruleAnalyzer->analyzeRules($rawRules); return new Parser($tokens, $rules); }