Loading src/Calc/Formule.php 0 → 100644 +364 −0 Original line number Diff line number Diff line <?php namespace Unicaen\OpenDocument\Calc; class Formule { private string $formule; private array $terms = []; private array $expr = []; public function __construct(string $formule) { $this->formule = $formule; } private function t(int $index): array { if (array_key_exists($index, $this->terms)) { return $this->terms[$index]; } else { return ['type' => 'end', 'v' => null]; } } private function tw(array $term, int $index, int $length = 1) { $this->terms[$index] = $term; if ($length > 1) { for ($i = 1; $i < $length; $i++) { unset($this->terms[$index + $i]); } $this->terms = array_values($this->terms); } } public function analyse() { $this->terms = []; for ($i = 4; $i < strlen($this->formule); $i++) { $this->terms[] = [ 'type' => null, 'v' => $this->formule[$i], ]; } $this->lexing(); $this->grammar(); } public function displayTerms() { echo '<div style="clear: both"><code>' . htmlentities($this->formule) . '</code></div>'; echo '<div>'; foreach ($this->terms as $index => $term) { $this->displayTerm($index, $term); } echo '</div>'; } private function displayTerm(int $index, array $term) { $type = $term['type']; unset($term['type']); ?> <div style="width:120px;float:left;margin:2px"> <div class="panel panel-default"> <div class="panel-heading"><b><?= $index ?></b> <?= $type ?: '<span style="color:red">IND</span>' ?></div> <table class="table table-bordered table-condensed table-extra-condensed"> <?php foreach ($term as $t => $v): ?> <tr> <th><?= $t ?></th> <td><?= htmlentities($v) ?></td> </tr> <?php endforeach; ?> </table> </div> </div> <?php } private function lexing() { $lexers = [ 'lexString', 'lexVarRange', 'lexOpsSeps', 'lexOthers', ]; foreach ($lexers as $lexer) { $i = 0; while (($t = $this->t($i)) && $t['type'] !== 'end') { if ($t['type'] === null) { $this->{$lexer}($i); } $i++; } } } private function lexString(int $i) { $t = $this->t($i); if ($t['v'] !== '"') { return; } $string = ''; $length = 2; $c = $i; do { $c++; $t = $this->t($c); if ($t['type'] === null) { if ($t['v'] !== '"') { $string .= $t['v']; $length++; } else { $next = $this->t($c + 1); if ($next['type'] === null && $next['v'] === '"') { $string .= '"'; $length += 2; $c++; $t['v'] = ''; } } } } while (!($t['type'] != null || $t['v'] === '"')); $this->tw([ 'type' => 'string', 'content' => $string, ], $i, $length); } private function lexVarRange(int $i) { $t = $this->t($i); if ($t['v'] !== '[') { return; } $val = ''; $length = 1; $c = $i; do { $c++; $t = $this->t($c); if ($t['v'] != ']') { $val .= $t['v']; } $length++; } while ($t['type'] === null && $t['v'] !== ']'); if ($val[0] !== '.') { return; } $val = substr($val, 1); if (false !== strpos($val, ':')) { $range = explode(':', $val); $term = [ 'type' => 'range', 'begin' => $range[0], 'end' => substr($range[1], 1), ]; } else { $term = [ 'type' => 'cell', 'name' => $val, ]; } $this->tw($term, $i, $length); } private function lexOpsSeps(int $i) { $t = $this->t($i); if ($t['v'] === '<') { $next = $this->t($i + 1); if ($next['type'] === null && $next['v'] === '>') { $this->tw(['type' => 'op', 'name' => '<>'], $i, 2); } } $ops = ['<', '>', '-', '+', '*', '/', '=']; if (in_array($t['v'], $ops)) { $this->tw(['type' => 'op', 'name' => $t['v']], $i); } $seps = ['(', ')', ';']; if (in_array($t['v'], $seps)) { $this->tw(['type' => 'sep', 'name' => $t['v']], $i); } } private function lexOthers(int $i) { $c = $i; $t = $this->t($c); $v = ''; $length = 0; $type = null; while ($t['type'] === null && $t['type'] !== 'end') { $v .= $t['v']; $length++; $c++; $t = $this->t($c); } /* Détection de nombres */ if ($v === '0') { $this->tw(['type' => 'number', 'value' => 0], $i); return; } else { $vt = str_replace([',', ' '], ['.', ''], $v); $vf = (float)$vt; if ((string)$vf === $vt) { $this->tw(['type' => 'number', 'value' => $vf], $i); return; } } $this->tw(['type' => 'variable', 'name' => $v], $i, $length); /* Détection de fonctions */ $next = $this->t($i + 1); if ($next['type'] === 'sep' && $next['name'] === '(') { $this->tw(['type' => 'function', 'name' => $v], $i); } } private function grammar() { $expr = &$this->terms; // on met la structure en relief $level = 1; foreach ($expr as $i => $term) { if ($term['type'] === 'sep' && $term['name'] === ')') { $level--; } if ($term['type'] === 'sep' && $term['name'] === ';') { $expr[$i]['level'] = $level - 1; } else { $expr[$i]['level'] = $level; } if ($term['type'] === 'sep' && $term['name'] === '(') { $level++; } } $this->expr = $this->terms; $this->analyseExpr($this->expr, 0); } private function searchExpr(array &$expr, int $i, array $filters): ?int { foreach( $expr as $c => $term ){ if ($c >= $i) { $match = true; foreach ($filters as $name => $value) { if ($term[$name] !== $value) { $match = false; } if ($match) return $c; } } } return null; } private function analyseExpr(array &$expr, int $i) { while( ($i = $this->searchExpr($expr, $i, ['type' => 'function'])) !== null ){ $this->buildFunction($expr, $i); $i++; } } private function buildFunction(array &$expr, int $i) { $level = $expr[$i]['level']; $exprs = []; $currentExpr = 0; $end = count($expr); unset($expr[$i+1]); // on supprime la parenthèse ouvrante for ($c = $i + 2; $c < $end; $c++) { $t = $expr[$c]; unset($expr[$c]); if ($t['type'] === 'sep' && $t['name'] === ')' && $t['level'] === $level) { // fin de la fonction break; }elseif ($t['type'] === 'sep' && $t['name'] === ';' && $t['level'] === $level){ $currentExpr++; }else{ $exprs[$currentExpr][] = $t; } // if () } for($e=0;$e<=$currentExpr;$e++){ $this->analyseExpr($exprs[$e], 0); } $expr[$i]['exprs'] = $exprs; $expr = array_values($expr); } public function displayExprs() { echo '<div style="clear:both">'; phpDump($this->expr); echo '</div>'; } } No newline at end of file Loading
src/Calc/Formule.php 0 → 100644 +364 −0 Original line number Diff line number Diff line <?php namespace Unicaen\OpenDocument\Calc; class Formule { private string $formule; private array $terms = []; private array $expr = []; public function __construct(string $formule) { $this->formule = $formule; } private function t(int $index): array { if (array_key_exists($index, $this->terms)) { return $this->terms[$index]; } else { return ['type' => 'end', 'v' => null]; } } private function tw(array $term, int $index, int $length = 1) { $this->terms[$index] = $term; if ($length > 1) { for ($i = 1; $i < $length; $i++) { unset($this->terms[$index + $i]); } $this->terms = array_values($this->terms); } } public function analyse() { $this->terms = []; for ($i = 4; $i < strlen($this->formule); $i++) { $this->terms[] = [ 'type' => null, 'v' => $this->formule[$i], ]; } $this->lexing(); $this->grammar(); } public function displayTerms() { echo '<div style="clear: both"><code>' . htmlentities($this->formule) . '</code></div>'; echo '<div>'; foreach ($this->terms as $index => $term) { $this->displayTerm($index, $term); } echo '</div>'; } private function displayTerm(int $index, array $term) { $type = $term['type']; unset($term['type']); ?> <div style="width:120px;float:left;margin:2px"> <div class="panel panel-default"> <div class="panel-heading"><b><?= $index ?></b> <?= $type ?: '<span style="color:red">IND</span>' ?></div> <table class="table table-bordered table-condensed table-extra-condensed"> <?php foreach ($term as $t => $v): ?> <tr> <th><?= $t ?></th> <td><?= htmlentities($v) ?></td> </tr> <?php endforeach; ?> </table> </div> </div> <?php } private function lexing() { $lexers = [ 'lexString', 'lexVarRange', 'lexOpsSeps', 'lexOthers', ]; foreach ($lexers as $lexer) { $i = 0; while (($t = $this->t($i)) && $t['type'] !== 'end') { if ($t['type'] === null) { $this->{$lexer}($i); } $i++; } } } private function lexString(int $i) { $t = $this->t($i); if ($t['v'] !== '"') { return; } $string = ''; $length = 2; $c = $i; do { $c++; $t = $this->t($c); if ($t['type'] === null) { if ($t['v'] !== '"') { $string .= $t['v']; $length++; } else { $next = $this->t($c + 1); if ($next['type'] === null && $next['v'] === '"') { $string .= '"'; $length += 2; $c++; $t['v'] = ''; } } } } while (!($t['type'] != null || $t['v'] === '"')); $this->tw([ 'type' => 'string', 'content' => $string, ], $i, $length); } private function lexVarRange(int $i) { $t = $this->t($i); if ($t['v'] !== '[') { return; } $val = ''; $length = 1; $c = $i; do { $c++; $t = $this->t($c); if ($t['v'] != ']') { $val .= $t['v']; } $length++; } while ($t['type'] === null && $t['v'] !== ']'); if ($val[0] !== '.') { return; } $val = substr($val, 1); if (false !== strpos($val, ':')) { $range = explode(':', $val); $term = [ 'type' => 'range', 'begin' => $range[0], 'end' => substr($range[1], 1), ]; } else { $term = [ 'type' => 'cell', 'name' => $val, ]; } $this->tw($term, $i, $length); } private function lexOpsSeps(int $i) { $t = $this->t($i); if ($t['v'] === '<') { $next = $this->t($i + 1); if ($next['type'] === null && $next['v'] === '>') { $this->tw(['type' => 'op', 'name' => '<>'], $i, 2); } } $ops = ['<', '>', '-', '+', '*', '/', '=']; if (in_array($t['v'], $ops)) { $this->tw(['type' => 'op', 'name' => $t['v']], $i); } $seps = ['(', ')', ';']; if (in_array($t['v'], $seps)) { $this->tw(['type' => 'sep', 'name' => $t['v']], $i); } } private function lexOthers(int $i) { $c = $i; $t = $this->t($c); $v = ''; $length = 0; $type = null; while ($t['type'] === null && $t['type'] !== 'end') { $v .= $t['v']; $length++; $c++; $t = $this->t($c); } /* Détection de nombres */ if ($v === '0') { $this->tw(['type' => 'number', 'value' => 0], $i); return; } else { $vt = str_replace([',', ' '], ['.', ''], $v); $vf = (float)$vt; if ((string)$vf === $vt) { $this->tw(['type' => 'number', 'value' => $vf], $i); return; } } $this->tw(['type' => 'variable', 'name' => $v], $i, $length); /* Détection de fonctions */ $next = $this->t($i + 1); if ($next['type'] === 'sep' && $next['name'] === '(') { $this->tw(['type' => 'function', 'name' => $v], $i); } } private function grammar() { $expr = &$this->terms; // on met la structure en relief $level = 1; foreach ($expr as $i => $term) { if ($term['type'] === 'sep' && $term['name'] === ')') { $level--; } if ($term['type'] === 'sep' && $term['name'] === ';') { $expr[$i]['level'] = $level - 1; } else { $expr[$i]['level'] = $level; } if ($term['type'] === 'sep' && $term['name'] === '(') { $level++; } } $this->expr = $this->terms; $this->analyseExpr($this->expr, 0); } private function searchExpr(array &$expr, int $i, array $filters): ?int { foreach( $expr as $c => $term ){ if ($c >= $i) { $match = true; foreach ($filters as $name => $value) { if ($term[$name] !== $value) { $match = false; } if ($match) return $c; } } } return null; } private function analyseExpr(array &$expr, int $i) { while( ($i = $this->searchExpr($expr, $i, ['type' => 'function'])) !== null ){ $this->buildFunction($expr, $i); $i++; } } private function buildFunction(array &$expr, int $i) { $level = $expr[$i]['level']; $exprs = []; $currentExpr = 0; $end = count($expr); unset($expr[$i+1]); // on supprime la parenthèse ouvrante for ($c = $i + 2; $c < $end; $c++) { $t = $expr[$c]; unset($expr[$c]); if ($t['type'] === 'sep' && $t['name'] === ')' && $t['level'] === $level) { // fin de la fonction break; }elseif ($t['type'] === 'sep' && $t['name'] === ';' && $t['level'] === $level){ $currentExpr++; }else{ $exprs[$currentExpr][] = $t; } // if () } for($e=0;$e<=$currentExpr;$e++){ $this->analyseExpr($exprs[$e], 0); } $expr[$i]['exprs'] = $exprs; $expr = array_values($expr); } public function displayExprs() { echo '<div style="clear:both">'; phpDump($this->expr); echo '</div>'; } } No newline at end of file