/** * @param In $inputStream * @param bool $keepCrlf If true, the line ending CRLF will be returned in the string * @param int $offset An offset tracker for the stream * @param int $maxLineLength The max line length. If it is reached, an exception will be thrown * @return string * * @throws \Avalonia\Component\Message\Exception\MapperInvalidDataException If the line doesn't end with a CRLF, or if $maxLineLength is reached (if EOF or unix EOL is found) * @throws \Avalonia\Component\Message\Exception\MapperMaxLineLengthException If the line isn't ended before $maxLineLength * * Read an HTTP line */ protected function readHttpLine(In $inputStream, bool $keepCrlf = false, int &$offset = null, int $maxLineLength = self::DEFAULT_MAX_LINE_LENGTH) : string { $buffer = ''; $lastChar = ''; $bufferLength = 0; $offset = $offset ?: 0; do { if ($inputStream->eof()) { throw (new MapperInvalidDataEofFoundException("The given line has no end. Found EOF before CRLF."))->setRemainingBufferData($buffer); } if ($maxLineLength <= $bufferLength) { throw new MapperMaxLineLengthException("Http line max length reached."); } $char = $inputStream->readCharacter(); $buffer .= $char; $bufferLength++; $offset++; if (static::LF === $char) { if (static::CR !== $lastChar) { throw new MapperInvalidDataException("The given line ends with a LF, not with a CRLF"); } if (!$keepCrlf) { $buffer = substr($buffer, 0, -2); } return $buffer; } $lastChar = $char; } while (true); }
/** * 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); }