commit da4daf0ded43ba9492357b65545f424ecb2ae5ea
parent 9327772128bae1733a71254a8f66b123475cedd9
Author: finwo <finwo@pm.me>
Date: Fri, 17 Aug 2018 14:52:22 +0200
Working on the spec compiler
Diffstat:
5 files changed, 328 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,5 +1,13 @@
-# Created by https://www.gitignore.io/api/osx,linux,windows,intellij
+# Created by https://www.gitignore.io/api/osx,linux,windows,intellij,composer
+
+### Composer ###
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# composer.lock
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
@@ -143,6 +151,7 @@ $RECYCLE.BIN/
*.lnk
-# End of https://www.gitignore.io/api/osx,linux,windows,intellij
+# End of https://www.gitignore.io/api/osx,linux,windows,intellij,composer
/.gtm/
/.idea/
+composer.lock
diff --git a/compile.php b/compile.php
@@ -0,0 +1,163 @@
+<?php
+
+if(!defined('DS')) define('DS',DIRECTORY_SEPARATOR);
+require(__DIR__.DS.'vendor'.DS.'autoload.php');
+$params = array();
+@array_shift($argv);
+@parse_str(@implode('&',$argv), $params);
+
+function perror($msg=null) {
+ if(is_null($msg)) exit(1);
+ if(is_string($msg)) {
+ echo $msg, PHP_EOL;
+ } else {
+ try {
+ echo json_encode($msg, JSON_PRETTY_PRINT), PHP_EOL;
+ } catch(Exception $e) {
+ // Do nothing
+ }
+ }
+ exit(1);
+}
+
+function flatten( $data, $prefix = '', &$output = [] ) {
+ foreach($data as $key => $value) {
+ if(is_array($value)) { pack($value,$prefix.$key.'.',$output); }
+ else { $output[$prefix.$key] = $value; }
+ }
+ return $output;
+}
+function unflatten( $flatArray, &$output = [] ) {
+ static $accessor = null;
+ $accessor = $accessor ?: new \Finwo\PropertyAccessor\PropertyAccessor(true);
+ foreach ($flatArray as $key => $value ) $accessor->set($output,$key,$value,'.');
+ return $output;
+}
+
+function configFile( $file, &$output = [] ) {
+ $fd = null;
+ if(is_array($file)) $file = implode(DS,$file);
+ if(is_string($file) && file_exists($file)) { $fd = fopen($file,'r'); $file = null; }
+ if(is_string($file)) {
+ $fd = fopen('php://temp','r+');
+ fwrite($fd,$file);
+ fseek($fd,0);
+ $file = null;
+ }
+ if(!$fd) return null;
+ while(!feof($fd)) { // Loop until we're out of file
+ $line = fgets($fd); // Fetch the line we're about to process
+ $line = @array_shift(str_getcsv($line,'#')); // Strip comments
+ $line = trim($line); // Trim line (we don't care about whitespace)
+ if(strlen($line)==0) continue; // Blank line = skip
+ $line = array_map('trim',str_getcsv($line,':')); // Split key & value
+ if(count($line)!=2) continue; // Only 1 key & 1 value supported
+ $output[strtolower($line[0])]=$line[1]; // Flat keys
+ }
+ fclose($fd);
+ return $output;
+}
+
+function fcopy($src,$dst=null) {
+ if(is_null($dst)) $dst = fopen('php://temp','c+');
+ while(!feof($src)) fwrite($dst,fread($src,1024)?:'');
+ return $dst;
+}
+
+// Make sure we get the minimum params
+if(!isset($params['spec'])) perror('Spec missing from the parameters');
+$spec = $params['spec'];
+
+// Ensure the said spec has a src
+if(!file_exists('src'.DS.$spec.'.md')) perror('Given source does not exist');
+$filename = implode(DS,[__DIR__,'src',$spec.'.md']);
+
+// Fetch the file's headers
+$fd = fopen($filename,'r+');
+$headers = '';
+while(!feof($fd)) {
+ $line = trim(fgets($fd));
+ if(!$line) break;
+ $headers .= $line . "\n";
+}
+$headers = unflatten(configFile($headers));
+
+class Pipe {
+ protected $processor = null;
+ protected $next = null;
+ public function __construct( $processor, Pipe $next = null ) {
+ if(is_array($processor)) {
+ $this->processor = array_shift($processor);
+ if(count($processor)) $this->next = new Pipe($processor);
+ } else {
+ $this->processor = $processor;
+ $this->next = $next;
+ }
+ }
+ public function write( $chunk ) {
+ if(is_resource($this->processor)) return fwrite($this->processor,$chunk);
+ if(is_callable($this->processor)) return call_user_func($this->processor,$chunk,$this->next);
+ return false;
+ }
+}
+function normalize_newlines( $chunk, Pipe $next ) {
+ $next->write(str_replace(["\r\n","\r"],"\n",$chunk));
+}
+function inclusions( $chunk, Pipe $next ) {
+ if(substr($chunk,0,1)=='<') {
+ $fd = fopen('src'.DS.substr(trim($chunk),1).'.md','r');
+ while(!feof($fd)) $next->write(fgets($fd));
+ fclose($fd);
+ } else {
+ $next->write($chunk);
+ }
+}
+
+// Build the processing pipe
+$outfd = fopen('spec'.DS.'spec'.$spec.'.txt','c+');
+ftruncate($outfd,0);
+$pipe = new Pipe([
+ 'normalize_newlines',
+ 'inclusions',
+ $outfd,
+]);
+
+// Run data through the pipe
+var_dump($pipe);
+while(!feof($fd)) $pipe->write(fgets($fd));
+var_dump($pipe);
+
+//// Handle file inclusions
+//$temp_fd = fopen('php://temp','c+');
+//while(!feof($process_fd)) {
+// $line = fgets($process_fd);
+// if(substr($line,0,1)==='<') {
+// $fd = fopen('src'.DS.trim(substr($line, 1)) . '.md', 'r');
+// var_dump('src'.DS.trim(substr($line, 1)) . '.md');
+// fcopy($fd, $temp_fd);
+// fclose($fd);
+// } else {
+// fwrite($temp_fd,$line);
+// }
+//}
+//
+//// Rewind again
+//fseek($process_fd,0);
+//ftruncate($process_fd,0);
+//fseek($temp_fd,0);
+//fcopy($temp_fd,$process_fd);
+//fseek($process_fd,0);
+//fseek($temp_fd,0);
+//ftruncate($temp_fd,0);
+//
+//
+//
+//
+//
+//echo stream_get_contents($process_fd);
+//
+//
+////var_dump($config);
+////var_dump($contents);
+//var_dump($headers);
+//var_dump($params);
diff --git a/composer.json b/composer.json
@@ -0,0 +1,14 @@
+{
+ "name": "ratus/specifications",
+ "require": {
+ "finwo/property-accessor": "^0.1.4"
+ },
+ "license": "CC-By 4.0",
+ "authors": [
+ {
+ "name": "Robin Bron",
+ "email": "robin@finwo.nl"
+ }
+ ],
+ "minimum-stability": "stable"
+}
diff --git a/src/0000.md b/src/0000.md
@@ -0,0 +1,136 @@
+Date: 2018-08-15
+author: Robin Bron <robin@finwo.nl>
+organization: Ratus B.V.
+
+# Specification Format
+
+## Conventions
+
+<RFC2119
+
+## Character encoding
+
+Plain-text files for specifications MUST use the `CP437` standard with the
+exclusion of character code 0x0A which represents a line feed as specified in
+`RFC20`.
+
+## Line definition
+
+A line of text is a sequence of 0 or more characters followed by a line feed
+character. For the sake of and clarity, the ending line feed character is part
+of the line.
+
+Lines MUST NOT exceed 72 characters in length, including the ending line feed
+character. A line is called a blank line if it consists of only a line feed
+charachter.
+
+### Line numbering
+
+To ensure the following page dimension section is clear, we need to define how
+lines are numbered.
+
+Assuming a document is in digital format and has a length of greater than
+0 bytes, the first character in the document is part of line 0. Numbering lines
+from 0 instead of 1 gives us an advantage of clarity in the next section.
+
+## Pages
+
+A page is a sequence of 60 lines. That means for every line number n, the line
+is the start of a new page when $$ n mod 60 = 0 $$.
+
+### Page header
+
+The first line of a page SHOULD consist of a left-aligned spec number
+indicator, a centered (short) document title and a right-aligned publishing
+date (see [Document header][document header]). The second line of a page MUST
+always be blank, excluding the first page of the document.
+
+### Page footer
+
+The last line of a page MUST consist of a left-align last name of the author
+and a right-aligned page number between square brackets. The second-to-last
+line of a page must be blank, just like the second line of a page.
+
+## Paragraphs
+
+A paragraph is a sequence of consecutive lines containing characters other than
+only a line feed. Paragraphs are separated by either a blank line or a page
+break. Paragraphs MUST NOT span multiple pages, limiting their size to 56
+lines.
+
+## Document header
+
+The first lines of the first page of a specification document SHALL always
+contain left-aligned description headers (see
+[Descriptive header][descriptive header]) and right-aligned author
+identification and a right-aligned publishing date.
+
+After the initial lines (see [Descriptive header][descriptive header] through
+[Publish date][publish date]), the document title is REQUIRED to be written on
+the first page of the document. For it's specification, see section
+[document title][document title].
+
+Further information on the first page should give a quick description of the
+contents of the document.
+
+### Descriptive header
+
+Each descriptive header is made up of a key and a value. Whitespace is not
+allowed in both the key and the value. Whitespace can only be included in the
+value by wrapping the value in quote characters.
+
+The key of the header consists of all characters of the line up to the first
+semicolon, excluding the semicolon itself and omitting all white-space
+characters.
+
+The value of the header starts at the first non-whitespace character after the
+first semicolon of the line. If the first character is a quote, the value ends
+at the next quote in the line. If the first character is not a quote, the value
+ends at the next whitespace character.
+
+### Short author identification
+
+In order to allow authors to take some credit and to track who has written
+what, the author's name MUST be added right-aligned on the first line of the
+first page of the document. To prevent mixing notations between documents, the
+names SHOULD be written as only the first letters of all given names in
+capitals, separated by dots, a space and the Family name starting with a
+capital. When written by a group with a name, the short author identification
+string SHOULD state the group's name.
+
+### Publish date
+
+Because a document is unlikely to have been written within a day, a publish
+date is simply the month's name starting with a capital followed by the year,
+both following the Gregorian calendar.
+
+## Document footer
+
+The document SHOULD close, starting on a new page, with all informative
+resources which were used to write the document, noting their keyword and
+document title. When possible, a URL to the resource SHOULD be included.
+
+After the informative resources, the document SHOULD end with one or several
+pages dedicated to the information of the author(s) and if possible their
+contact information.
+
+## Section titles
+
+Section titles SHOULD be a short text about the subject the section describes.
+Whether it is simply the keyword of what it explains, a problem statement or
+other type of text is up to the author as long as it's relevant to the
+section's body.
+
+A section title MUST start with a capital character & MUST NOT contain any
+other capital letters, excluding where they are required in names or
+abbreviations.
+
+## Document title
+
+The title of the document should clearly state the main subject of the document
+and it's contents. Each word of the document title must start with a capital
+character when noted as the title of the document.
+
+On the first page of the document, the title should be centered horizontally
+and have at least 2 blank lines both above and below it. The document title
+SHOULD be as close to the document's descriptive headers.
diff --git a/src/RFC2119.md b/src/RFC2119.md
@@ -0,0 +1,4 @@
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
+document are to be interpreted as described in `RFC2119` when, and only when,
+they appear in all capitals, as shown here.