/index.html
HTML | 368 lines | 356 code | 12 blank | 0 comment | 0 complexity | 4d2b5af123eb071a6bbf7993edc33b94 MD5 | raw file
- <!DOCTYPE html>
- <html>
- <head>
- <title>YASH</title>
- <style>
- html, body {
- margin: 0;
- padding: 0;
- }
- </style>
- <link rel="stylesheet" href="jquery.yash.css">
- <script type="text/javascript" src="jquery.js"></script>
- <script type="text/javascript" src="jquery.yash.js"></script>
- <script type="text/javascript" src="yash.syntax.php.js"></script>
- <script type="text/javascript">
- window.onload = function() {
- $('#editor').yash();
- };
- </script>
- </head>
- <body>
- <div id="editor"><?php
- /**
- * parser.php
- */
- namespace tsml;
- use \Exception;
- /**
- * The Parser class constructs a parse tree from a TSML source document.
- */
- class Parser {
-
- /**
- * A flag indicating the next line is expected to have more indentation.
- */
- const EXPECT_MORE = 1;
-
- /**
- * A flag indicating the next line is expected to have the same indentation.
- */
- const EXPECT_SAME = 2;
-
- /**
- * A flag indicating the next line is expected to have less indentation.
- */
- const EXPECT_LESS = 4;
-
- /**
- * A convenience flag indicating the next line can have anu indentation.
- */
- const EXPECT_ANY = 7;
-
- /**
- * A regular expression for matching a valid tag name.
- */
- const RE_TAG = '/^\+([a-z][_:a-z0-9]*)/i';
-
- /**
- * A regular expression for matching a valid id.
- */
- const RE_ID = '/^#([_a-z][-_:a-z0-9]*)/i';
-
- /**
- * A regular expression for matching a valid class name.
- */
- const RE_CLASS = '/^\.(-?[_a-z][-_a-z0-9]*)/i';
-
- /**
- * A regular expression for matching a valid attribute/value.
- */
- const RE_ATTR = '/^([_:a-z][-_:.a-z0-9]*)(?:=(["\'])(\\\\.|(?!\\\\|\\2).*)\\2)?(?:\s|$)/i';
-
- /**
- * A regular expression for matching start-of-line escape sequences.
- */
- const RE_ESCAPES = '/^\\\\(\s|[+])/';
-
- /**
- * The options this instance is using.
- */
- protected $options = [];
-
- /**
- * The source document. An array of lines when using parse_string, a file
- * handle when using parse_file.
- */
- protected $source;
-
- /**
- * The root of the parse tree.
- */
- protected $parse_tree;
-
- /**
- * The current context in the parse tree.
- */
- protected $context;
-
- /**
- * The line number currently being parsed.
- */
- protected $line_number;
-
- /**
- * The line currently being parsed.
- */
- protected $line;
-
- /**
- * The current level of indentation.
- */
- protected $indent_level;
-
- /**
- * The parser's expected about the next line's indentation level.
- */
- protected $allow_child;
-
- /**
- * Instantiate a Parser with given options.
- */
- public function __construct(array $options = []) {
-
- $this->options = array_merge($this->options, $options);
-
- }
-
- /**
- * Parses a TSML string.
- */
- public function parse_string($tsml_string) {
-
- $tsml_string = str_replace(["\r\n", "\r"], "\n", $tsml_string);
- $this->source = explode("\n", $tsml_string);
- $this->parse();
- $this->source = [];
- return $this->parse_tree;
-
- }
-
- /**
- * Parses a TSML file.
- */
- public function parse_file($tsml_file) {
-
- $this->source = fopen($tsml_file, 'r');
- $this->parse();
- fclose($this->source);
- return $this->parse_tree;
-
- }
-
- /**
- * Parses stored TSML source.
- */
- protected function parse() {
-
- $this->reset();
- while($this->get_line()) {
- if(!trim($this->line)) {
- $this->allow_child = false;
- continue;
- }
-
- $this->handle_indent();
-
- if(strpos($this->line, '+#') === 0)
- $this->handle_loud_comment();
- elseif($this->line[0] == '+')
- $this->handle_tag();
- elseif(strpos($this->line, '-#') === 0)
- continue;
- else
- $this->handle_text();
- }
-
- }
-
- /**
- * Resets the parser.
- */
- protected function reset() {
-
- $this->parse_tree = new RootNode(-1);
- $this->context = $this->parse_tree;
- $this->line_number = 0;
- $this->indent_string = '';
- $this->indent_level = 0;
- $this->allow_child = false;
-
- if(is_resource($this->source))
- rewind($this->source);
- else
- reset($this->source);
-
- }
-
- /**
- * Gets the next line from the source.
- */
- protected function get_line() {
-
- $this->line_number ++;
-
- if(is_resource($this->source)) {
- if($this->line = rtrim(fgets($this->source), "\r\n"))
- return true;
- } else {
- if(is_array($this->line = each($this->source))) {
- $this->line = $this->line[1];
- return true;
- }
- }
-
- $this->line = '';
- return false;
-
- }
-
- /**
- * Adjusts the indentation level and context for the current line.
- */
- protected function handle_indent() {
-
- preg_match('/^\s*/', $this->line, $matches);
-
- if(str_replace(' ', '', $matches[0]))
- $this->exception('Invalid indentation');
- $new_indent_level = substr_count($matches[0], ' ');
-
- if($new_indent_level > $this->indent_level) {
- if($new_indent_level - $this->indent_level > 1)
- $this->exception('Indentation level increased by more than one');
-
- if(!$this->allow_child)
- $this->exception('Unexpected indentation level increase, previous line does not allow children');
-
- if(!($this->context = $this->context->last_child()))
- $this->exception('No child node for context update');
- } elseif($new_indent_level < $this->indent_level) {
- for($i = 0, $j = $this->indent_level - $new_indent_level; $i < $j; $i ++) {
- if(!($this->context = $this->context->parent()))
- $this->exception('No parent node for context update');
- }
- }
-
- $this->line = ltrim($this->line);
- $this->indent_level = $new_indent_level;
- $this->allow_child = false;
-
- }
-
- /**
- * Handles a loud comment.
- */
- protected function handle_loud_comment() {
-
- $node = new LoudCommentNode($this->line_number);
- $node->content = trim(substr($this->line, 2));
- $this->context->add_child($node);
-
- $this->indent_expected = static::EXPECT_SAME | static::EXPECT_LESS;
-
- }
-
- /**
- * Handles a tag line.
- */
- protected function handle_tag() {
-
- $line = $this->line;
- $node = new TagNode($this->line_number);
-
- if(!preg_match(static::RE_TAG, $line, $matches))
- $this->exception('Invalid tag name');
- $line = (string) substr($line, strlen($matches[0]));
- $node->tag_name = $matches[1];
-
- if($line and $line[0] == '#') {
- if(!preg_match(static::RE_ID, $line, $matches))
- $this->exception('Invalid id');
- $line = (string) substr($line, strlen($matches[0]));
- $node->id = $matches[1];
- }
-
- while($line and $line[0] == '.') {
- if(!preg_match(static::RE_CLASS, $line, $matches))
- $this->exception('Invalid class name');
- $line = (string) substr($line, strlen($matches[0]));
- $node->classes[] = $matches[1];
- }
-
- if($line and $line[0] == '[') {
- if($line[strlen($line) - 1] != ']')
- $this->exception('No closing `]` before end-of-line');
- $line = substr($line, 1, -1);
-
- while($line = trim($line)) {
- if(!preg_match(static::RE_ATTR, $line, $matches))
- $this->exception('Invalid attribute');
- $line = (string) substr($line, strlen($matches[0]));
- if(!isset($matches[2]) or ! $matches[2])
- $node->attributes[$matches[1]] = true;
- else
- $node->attributes[$matches[1]] = str_replace("\\$matches[2]", $matches[2], $matches[3]);
- }
- }
-
- if($line)
- $this->exception('Expected end-of-line after tag definition');
-
- $this->context->add_child($node);
- $this->allow_child = true;
-
- }
-
- /**
- * Handles a text line.
- */
- protected function handle_text() {
-
- $node = new TextNode($this->line_number);
- $node->content = preg_replace(static::RE_ESCAPES, '$1', $this->line);
- $this->context->add_child($node);
-
- $this->indent_expected = static::EXPECT_SAME | static::EXPECT_LESS;
-
- }
-
- /**
- * Throws an exception, appending the line number.
- */
- protected function exception($message) {
-
- throw new Exception('ParseError: ' . $message . ' - line ' . $this->line_number);
-
- }
-
- }
- require 'node.php';
- require 'root_node.php';
- require 'tag_node.php';
- require 'text_node.php';
- require 'loud_comment_node.php';
- require 'generator.php';
- $tsml = <<<TSML
- +# The main navigation
- +nav
- TSML;
- $parser = new Parser;
- $tree = $parser->parse_string($tsml);
- $tree->print_tree();
- $generator = new Generator;
- $html = $generator->generate($tree);
- echo htmlentities($html);
- ?></div>
- </body>
- </html>