r3-v2/update_templates.php

787 lines
16 KiB
PHP
Executable File

#!/usr/bin/php
<?php
include "utils.php";
include "graph.php";
function from_camel_case($input) {
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
function getTokens($type)
{
$fn = fopen('src/templates/token.db', "r");
$tokens = [];
while (!feof($fn)) {
$line = fgets($fn);
$matches = [];
if (preg_match('/^(' . $type . '.*$)/', $line, $matches)) {
$tokens[$matches[1]] = [];
}
}
fclose($fn);
return $tokens;
}
/**
* @throws ErrorException
*/
function loadSaved($file, $tokens) {
$saveFile = $file . '.saved';
echo "loading file " . $saveFile . "\n";
$loadedTokens = [];
$fn = fopen($saveFile, "r");
if (!$fn) {
throw new ErrorException('Could not open save file: ' . $saveFile . "\n");
}
$currentTokenKey = null;
while (!feof($fn)) {
$line = fgets($fn);
$tokenFound = false;
foreach ($tokens as $key => $store) {
if (preg_match('/\b' . $key . '\b/', $line))
{
if (array_key_exists($key, $loadedTokens)) {
throw new ErrorException("TOKEN ALREADY EXISTS! : $key\n");
}
$tokenFound = true;
$loadedTokens[$key] = [];
$currentTokenKey = $key;
break;
}
}
if (!$tokenFound && $line != false) {
array_push($loadedTokens[$currentTokenKey], $line);
}
}
fclose($fn);
return $loadedTokens;
}
function save($file, $tokens) {
echo "saving file " . $file . "\n";
$currentTokens = [];
$fn = fopen($file, "r");
while (!feof($fn)) {
$line = fgets($fn);
foreach ($tokens as $key => $store) {
if (preg_match('/\b' . $key . '_END\b/', $line)) {
array_pop($currentTokens);
break;
}
}
$size = sizeof($currentTokens);
if ($size > 0) {
array_push($tokens[$currentTokens[$size - 1]], $line);
}
foreach ($tokens as $key => $store) {
if (preg_match('/\b' . $key . '_START\b/', $line)) {
array_push($currentTokens, $key);
break;
}
}
}
fclose($fn);
$saveFile =$file . '.saved';
$saved = [];
$stores = [];
foreach ($tokens as $key => $store) {
if (sizeof($store) > 0) {
$stores[$key] = $store;
array_push($saved, $key . "\n");
foreach ($store as $line) {
array_push($saved, $line);
}
}
}
file_put_contents($saveFile, $saved);
echo "saved file $saveFile\n";
return [
$saveFile,
$stores
];
}
function deleteSavedFile($saveFile)
{
if ($saveFile) {
unlink($saveFile);
} else {
echo "saved file was null";
}
}
function getWhitespace($contents, $token)
{
$matches = [];
if (preg_match('/^\n*(\s*)\b' . $token . '\b/m', $contents, $matches)) {
return [
'white-space' => $matches[1],
'inline-comment' => false
];
} else {
if (preg_match('/^\n*(\s*)\/\/\b' . $token . '\b/m', $contents, $matches)) {
return [
'white-space' => $matches[1],
'inline-comment' => true
];
}
return null;
}
}
function updateSection($file, $token, $updates, $separator = "") {
if (getType($updates) === 'array') {
$updates = implode($separator, $updates);
}
if (strlen($updates) <= 0) {
echo "No contents to be updated for token $token\n";
return false;
}
if (substr($updates, -1) !== "\n") {
$updates .= "\n";
}
$contents = file_get_contents($file);
$whiteSpaceObject = getWhitespace($contents, $token . '_END');
if ($whiteSpaceObject === null) {
/**
* This file does not contain the token which requires an update - we can return here
*/
echo "Skipping token $token because it was not found but it could be generated soon...\n";
return $token;
}
$endToken = $token . "_END";
if ($whiteSpaceObject['inline-comment']) {
$endToken = '//' . $endToken;
}
$whiteSpace = $whiteSpaceObject['white-space'];
$contents = preg_replace(
'/\b' . $token . '_START\b.*?\b' . $token . '_END\b/s',
$token . "_START\n" . $updates . $whiteSpace . $endToken,
$contents
);
file_put_contents($file, $contents);
return true;
}
function doGetInheritedTemplateUpdates($node, $restoreTokens)
{
try {
$tokens = loadSaved($node->file, $restoreTokens);
} catch (ErrorException $e) {
echo $e->getMessage();
exit(1);
}
$updates = '';
if ($node->parent !== null && $node->parent->name !== "R3") {
$updates = doGetInheritedTemplateUpdates($node->parent, $restoreTokens);
}
$template = file_get_contents('src/templates/generate_inherited.template');
$CLASS_NAME = $node->name;
$PROPERTY_LIST = 'test';
$METHOD_LIST = 'test';
$STATIC_METHOD_LIST = 'test';
$updated = str_replace('CLASS_NAME', $CLASS_NAME, $template);
$updated = str_replace('PROPERTY_LIST', $PROPERTY_LIST, $updated);
$updated = str_replace('STATIC_METHOD_LIST', $STATIC_METHOD_LIST, $updated);
$updated = str_replace('METHOD_LIST', $METHOD_LIST, $updated);
$updates .= $updated;
return $updates;
}
function generateInherited($file, $restoreTokens)
{
echo "Generating inherited fields for $file\n";
global $graph;
$node = $graph->search('file', $file);
if ($node === null) {
/**
* This node is not part of R3
*/
return;
}
if ($node->parent === null) {
/**
* This node has no inherited properties
*/
return;
}
if ($node->parent->name === 'R3') {
/**
* This is a base class
*/
return;
}
$updates = doGetInheritedTemplateUpdates($node->parent, $restoreTokens);
updateSection($file, 'GENERATE_INHERITED' , $updates);
}
function generateInitOptions($file, $tokens)
{
$token = 'CUSTOM_OPTIONS';
$store = getTokenStore($token, $tokens);
if (sizeof($store) <= 0) {
return;
}
echo "Will be building options for $token\n";
$header = file_get_contents('src/templates/generate_custom_options_init_header.template');
$template = file_get_contents('src/templates/generate_custom_options_init.template');
$updates = $header;
foreach ($store as $item) {
$item = trim($item);
$key_value = preg_split('/=/', $item);
if ($key_value === false) {
continue;
}
$key = $key_value[0];
$value = $key_value[1];
$updated = str_replace('KEY', $key, $template);
$updated = str_replace('VALUE', $value, $updated);
$updates .= $updated;
}
updateSection($file, 'GENERATE_OPTIONS_INIT' , $updates);
}
function getTokenStore($tokenName, $tokens)
{
if (array_key_exists($tokenName, $tokens)) {
return $tokens[$tokenName];
}
return [];
}
function getInstanceMappings($tokens)
{
$instanceMappings = [];
foreach (getTokenStore('CUSTOM_INSTANCE_OPTIONS_MAPPING', $tokens) as $mapping) {
$mapping = trim($mapping);
$key_value = preg_split('/=/', $mapping);
if ($key_value === false) {
continue;
}
$key = $key_value[0];
$value = $key_value[1];
$instanceMappings[$key] = $value;
}
return $instanceMappings;
}
function isExcluded($key, $tokens)
{
if (array_key_exists('CUSTOM_EXCLUDED_FROM_INSTANCE_OPTIONS', $tokens)) {
$excluded = [];
foreach ($tokens['CUSTOM_EXCLUDED_FROM_INSTANCE_OPTIONS'] as $exclude) {
array_push($excluded, trim($exclude));
}
if (in_array($key, $excluded)) {
return true;
}
}
return false;
}
function doInstanceUpdate($template, $tokens, $asArray = false)
{
$token = 'CUSTOM_OPTIONS';
$store = getTokenStore($token, $tokens);
if (sizeof($store) <= 0) {
return null;
}
$instanceMappings = getInstanceMappings($tokens);
$updates = '';
if ($asArray) {
$updates = [];
}
foreach ($store as $item) {
$item = trim($item);
$key_value = preg_split('/=/', $item);
if ($key_value === false) {
continue;
}
$key = $key_value[0];
$value = $key_value[0];
if (array_key_exists($key, $instanceMappings)) {
$value = $instanceMappings[$key];
}
if (isExcluded($key, $tokens)){
continue;
}
$updated = str_replace('INSTANCE_KEY', $value, $template);
$updated = str_replace('KEY', $key, $updated);
if ($asArray) {
array_push($updates, $updated);
} else {
$updates .= $updated;
}
}
return $updates;
}
function generateCreateInstanceOptions($file, $tokens)
{
$template = file_get_contents('src/templates/generate_create_instance_options.template');
$updates = doInstanceUpdate($template, $tokens, true);
if ($updates) {
echo "Updating create instance options\n";
updateSection($file, 'GENERATE_CREATE_INSTANCE_OPTIONS', $updates, ",\n");
} else {
echo "No create instance options found to generate\n";
}
}
/**
* Process updateInstance options
*/
function generateUpdateInstanceOptions($file, $tokens)
{
$template = file_get_contents('src/templates/generate_update_instance_options.template');
$updates = doInstanceUpdate($template, $tokens);
if ($updates) {
echo "Updating update instance options\n";
updateSection($file, 'GENERATE_UPDATE_INSTANCE_OPTIONS', $updates);
} else {
echo "No update instance options found to generate\n";
}
}
/**
* Process updateFromInstance options
*/
function generateUpdateFromInstanceOptions($file, $tokens)
{
$template = file_get_contents('src/templates/generate_update_from_instance_options.template');
$updates = doInstanceUpdate($template, $tokens);
if ($updates) {
echo "Updating update from instance options\n";
updateSection($file, 'GENERATE_UPDATE_FROM_INSTANCE_OPTIONS', $updates);
} else {
echo "No update from instance options found to generate\n";
}
}
function doMethodUpdate($template, $tokens, $token)
{
$store = getTokenStore($token, $tokens);
if (sizeof($store) <= 0) {
return null;
}
$updates = '';
foreach ($store as $item) {
$item = trim($item);
$methodName = $item;
$matches = [];
$result = preg_match('/^(.*?)\(/', $item, $matches);
if ($result !== false && sizeof($matches) >= 2) {
$methodName = $matches[1];
}
$args = '';
$result = preg_match('/\((.*?)\)/', $item, $matches);
if ($result !== false && sizeof($matches) >= 2) {
$args = $matches[1];
}
$params = $args;
$argsArray = preg_split('/,(\s*)/', $args);
if (sizeof($argsArray) > 1) {
$args = implode(",\n\t\t", $argsArray);
$args = "\n\t\t" . $args . "\n\t";
$params = implode("\n\t * @param ", $argsArray);
$params = "@param " . $params;
} else {
$params = "\n\t * @param " . $params;
}
$result = preg_match('/.*?\).*?(\w.*$)/', $item, $matches);
if ($result !== false && sizeof($matches) >= 2) {
$comment = $matches[1];
}
if ($result === 0) {
$comment = 'No comment';
}
$returns = preg_split('/@return[s]*\s*/', $comment);
if (sizeof($returns) > 1) {
$comment = $returns[0];
$returns = '@returns ' . $returns[1];
} else {
$returns = '';
}
$methodTokenName = strtoupper(from_camel_case($methodName));
$spaces = '';
$length = strlen($methodName . '() - ');
for ($i = 0; $i < $length; $i++) {
$spaces = ' ' . $spaces;
}
$comment = wordwrap($comment, 100, "\n * " . $spaces);
$updated = str_replace('METHOD_ARGS', $args, $template);
$updated = str_replace('METHOD_NAME_UPPERCASE', $methodTokenName, $updated);
$updated = str_replace('METHOD_NAME', $methodName, $updated);
$updated = str_replace('COMMENT', $comment, $updated);
$updated = str_replace('PARAMS', $params, $updated);
$updated = str_replace('RETURNS', $returns, $updated);
$updates .= $updated;
}
return $updates;
}
function generateStaticMethods($file, $tokens)
{
$token = 'CUSTOM_STATIC_METHODS';
$template = file_get_contents('src/templates/generate_static_methods.template');
$updates = doMethodUpdate($template, $tokens, $token);
if ($updates) {
echo "Updating static methods\n";
updateSection($file, 'GENERATE_STATIC_METHODS', $updates);
} else {
echo "No static methods found to generate\n";
}
}
function generateMethods($file, $tokens)
{
$token = 'CUSTOM_METHODS';
$template = file_get_contents('src/templates/generate_methods.template');
$updates = doMethodUpdate($template, $tokens, $token);
if ($updates) {
echo "Updating methods\n";
updateSection($file, 'GENERATE_METHODS', $updates);
} else {
echo "No methods found to generate\n";
}
}
$nodeList = [];
/**
* @throws ErrorException
*/
function buildNodeList($file)
{
echo "loading file $file\n";
global $nodeList;
$fn = fopen($file, "r");
$line = '';
$found = false;
while (!feof($fn) && !$found) {
$line = fgets($fn);
if (preg_match('/^class\s+/', $line)) {
$found = true;
break;
}
}
if ($found) {
// echo $line . "\n";
$matches = [];
$result = preg_match('/^class\s+(.*?)(\s+extends\s+(.*?)\s+|\s+?{)/', $line, $matches);
if ($result === false) {
throw new ErrorException('Could not match');
}
$extends = null;
if (sizeof($matches) === 4) {
$class = $matches[1];
$extends = $matches[3];
} else if (sizeof($matches) === 3) {
$class = $matches[1];
} else {
throw new ErrorException('Unhandled case');
}
$node = new Node(
$file,
$class,
$extends
);
array_push($nodeList, $node);
} else {
echo "not found\n";
}
fclose($fn);
}
global $files;
foreach ($files as $file) {
$saveFile = null;
if ($argv[2] == 'save') {
$saveFile = $file . '.saved';
if (file_exists($saveFile)) {
echo "A previous restore operation did not complete - please remove the saved file before trying this again\n";
echo "The save file is located at $saveFile\n";
echo "Remove easily with:\n";
echo "rm $saveFile\n";
exit(1);
}
$tokens = getTokens('CUSTOM');
$result = save($file, $tokens);
exit(0);
} else if ($argv[2] == 'restore') {
$saveFile = $file . '.saved';
$restoreTokens = getTokens('CUSTOM');
$restoreTokenKeys = array_keys($restoreTokens);
try {
$tokens = loadSaved($file, $restoreTokens);
} catch (ErrorException $e) {
echo $e->getMessage();
exit(1);
}
$skipped = [];
foreach ($tokens as $token => $store) {
if (in_array($token, $restoreTokenKeys)) {
updateSection($file, $token, $store);
}
}
generateInitOptions($file, $tokens);
generateCreateInstanceOptions($file, $tokens);
generateUpdateInstanceOptions($file, $tokens);
generateUpdateFromInstanceOptions($file, $tokens);
generateMethods($file, $tokens);
generateStaticMethods($file, $tokens);
echo `r3 update-token-db`;
/**
* Try to restore the rest of the old data because now methods were generated.
* If not all data restores now - a method name / token has changed and we need
* to notify the user
*/
foreach ($tokens as $token => $store) {
if (in_array($token, $restoreTokenKeys)) {
$result = updateSection($file, $token, $store);
if ($result !== true && $result !== false) {
array_push($skipped, $result);
}
}
}
if (sizeof($skipped) !== 0) {
echo "Some tokens could not be restored because they could not be found in the new version\n";
print_r($skipped);
echo "Please restore them manually from the saved file :$saveFile\n";
echo "If you do not do it now - on the next template update code will be overwritten and you could lose code!!!\n";
exit(1);
} else {
// deleteSavedFile($saveFile);
exit(0);
}
} else if ($argv[2] == 'build-graph') {
buildNodeList($file);
}
}
if ($argv[2] == 'build-graph') {
global $nodeList;
/**
* Now generate the graph based on the new classes
*/
$graph = new Graph(
$nodeList
);
$restoreTokens = getTokens('CUSTOM');
$restoreTokenKeys = array_keys($restoreTokens);
/**
* Now start processing the files again - this time generating linked objects / inherited properties / methods
*/
foreach ($files as $file) {
$saveFile = $file . '.saved';
generateInherited($file, $restoreTokens);
}
foreach ($files as $file) {
$saveFile = $file . '.saved';
deleteSavedFile($saveFile);
}
}
exit(0);
?>