Gradually implementing PHP codegen

This commit is contained in:
Pavel Shevaev 2022-12-06 13:36:09 +03:00
parent da3f1f78e7
commit 155d7af191
4 changed files with 343 additions and 36 deletions

View File

@ -54,30 +54,30 @@ function _add_twig_support(\Twig\Environment $twig)
$twig->addFilter(new \Twig\TwigFilter('default_value',
function($default)
{
return _default_value($default);
return default_value($default);
}
));
$twig->addFunction(new \Twig\TwigFunction('apply_value_filters',
function ($field_name, array $tokens, $val, $add_assoc_check = true)
{
return _apply_value_filters($field_name, $tokens, $val, $add_assoc_check);
return apply_value_filters($field_name, $tokens, $val, $add_assoc_check);
}
));
$twig->addFunction(new \Twig\TwigFunction('data2value',
function($name, \mtgType $type, $buf, $prefix = '', $tokens = array(), $as_is = false, $postfix = '')
{
return _data2value($name, $type, $buf, $prefix, $tokens, $as_is, $postfix);
return data2value($name, $type, $buf, $prefix, $tokens, $as_is, $postfix);
}
));
$twig->addFunction(new \Twig\TwigFunction('value2data',
function($name, \mtgType $type, $buf, $prefix = '', $tokens = array())
{
return _value2data($name, $type, $buf, $prefix, $tokens);
return value2data($name, $type, $buf, $prefix, $tokens);
}
));
}
function _default_value($default)
function default_value($default)
{
if($default == "[]")
return 'array()';
@ -92,7 +92,7 @@ function _default_value($default)
return str_replace('$', '\$', $default);
}
function _apply_value_filters($field_name, array $tokens, $val, $add_assoc_check = true)
function apply_value_filters($field_name, array $tokens, $val, $add_assoc_check = true)
{
$str = $val;
foreach($tokens as $token => $args_json)
@ -123,7 +123,7 @@ function _apply_value_filters($field_name, array $tokens, $val, $add_assoc_check
}
//TODO: move it to template
function _data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(), $as_is = false, $postfix = '', $indent = '')
function data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(), $as_is = false, $postfix = '', $indent = '')
{
$str = '';
$tmp_val = '$tmp_val__';
@ -131,7 +131,7 @@ function _data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(
$cond_indent = $indent." ";
$default_value_arg = array_key_exists('default', $tokens) ? _default_value($tokens['default']) : 'null';
$default_value_arg = array_key_exists('default', $tokens) ? default_value($tokens['default']) : 'null';
if($type instanceof \mtgMetaStruct)
{
$default = array_key_exists('default', $tokens) ? $tokens['default'] : null;
@ -156,21 +156,21 @@ function _data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(
if($type instanceof \mtgBuiltinType)
{
$str .= $cond_indent."{$tmp_val} = " . _apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."{$tmp_val} = " . apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."{$pname} = mtg_php_val_{$type}({$tmp_val});\n";
}
else if($type instanceof \mtgMetaStruct)
{
if(array_key_exists('virtual', $tokens))
{
$str .= $cond_indent."{$tmp_val} = " . _apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."{$tmp_val} = " . apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."\$tmp_sub_arr__ = mtg_php_val_arr({$tmp_val});\n";
$str .= $cond_indent."\$vclass__ = AutogenBundle::getClassName(mtg_php_val_uint32(mtg_php_array_extract_val(\$tmp_sub_arr__, \$assoc, 'vclass__')));\n";
$str .= $cond_indent."{$pname} = new \$vclass__(\$tmp_sub_arr__, \$assoc);\n";
}
else
{
$str .= $cond_indent."{$tmp_val} = " . _apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."{$tmp_val} = " . apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."\$tmp_sub_arr__ = mtg_php_val_arr({$tmp_val});\n";
$str .= $cond_indent."{$pname} = new {$type}(\$tmp_sub_arr__, \$assoc);\n";
}
@ -183,12 +183,12 @@ function _data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(
$str .= $cond_indent."{\n";
//NOTE: removing default for field
unset($tokens['default']);
$str .= _data2value('$tmp__', $type->getValue(), "\$tmp_arr_item__", '', $tokens, true, "{$pname}[] = \$tmp__;", $cond_indent." ") . "\n";
$str .= data2value('$tmp__', $type->getValue(), "\$tmp_arr_item__", '', $tokens, true, "{$pname}[] = \$tmp__;", $cond_indent." ") . "\n";
$str .= $cond_indent."}\n";
}
else if($type instanceof \mtgMetaEnum)
{
$str .= $cond_indent."{$tmp_val} = " . _apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$str .= $cond_indent."{$tmp_val} = " . apply_value_filters($name, $tokens, "{$tmp_val}"). ";\n";
$check_enum_validity = array_key_exists('is_enum_mask', $tokens) ? 'false' : 'true';
$str .= $cond_indent."{$pname} = mtg_php_val_enum('$type', {$tmp_val}, {$check_enum_validity});\n";
}
@ -204,7 +204,7 @@ function _data2value($name, \mtgType $type, $buf, $prefix = '', $tokens = array(
}
//TODO: move it to template
function _value2data($name, \mtgType $type, $buf, $prefix = '', $tokens = array(), $indent = '')
function value2data($name, \mtgType $type, $buf, $prefix = '', $tokens = array(), $indent = '')
{
$pname = $prefix.$name;
@ -248,7 +248,7 @@ function _value2data($name, \mtgType $type, $buf, $prefix = '', $tokens = array(
$str .= $indent."else\n";
$str .= $indent." foreach({$pname} as \$idx__ => \$arr_tmp_item__)\n";
$str .= $indent." {\n";
$str .= _value2data('$arr_tmp_item__', $type->getValue(), "\$arr_tmp__", "", $tokens, $indent." ") . "\n";
$str .= value2data('$arr_tmp_item__', $type->getValue(), "\$arr_tmp__", "", $tokens, $indent." ") . "\n";
$str .= $indent." if(\$assoc)\n";
$str .= $indent." {\n";
$str .= $indent." \$arr_tmp__[] = \$arr_tmp__['\$arr_tmp_item__'];\n";

186
src/data_utils.inc.php Normal file
View File

@ -0,0 +1,186 @@
<?php
namespace metagen_php;
use Exception;
function array_extract_val(&$arr, $assoc, $name, $default = null)
{
if(!is_array($arr))
throw new Exception("$name: Not an array");
if(!$assoc)
{
if(sizeof($arr) == 0)
{
if($default !== null)
return $default;
throw new Exception("$name: No next array item");
}
return array_shift($arr);
}
if(!isset($arr[$name]))
{
if($default !== null)
return $default;
throw new Exception("$name: No array item");
}
$val = $arr[$name];
unset($arr[$name]);
return $val;
}
function array_set_value(&$arr, $assoc, $name, $value)
{
if($assoc)
$arr[$name] = $value;
else
$arr[] = $value;
}
function val_string($val)
{
//special case for empty strings
if(is_bool($val) && $val === false)
return '';
if(!is_string($val))
throw new Exception("Bad item, not a string(" . serialize($val) . ")");
return $val;
}
function val_bool($val)
{
if(!is_bool($val))
throw new Exception("Bad item, not a bool(" . serialize($val) . ")");
return $val;
}
function val_float($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
return $val;
}
function val_double($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
return 1*$val;
}
function val_uint64($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected uint64, float given: $val");
return $val;
}
function val_int64($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected int64, float given: $val");
return $val;
}
function val_uint32($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected uint32, float given: $val");
if(($val < 0 && $val < -2147483648) || ($val > 0 && $val > 0xFFFFFFFF))
throw new Exception("Value not in range: $val");
return $val;
}
function val_int32($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected int32, float given: $val");
if($val > 2147483647 || $val < -2147483648)
throw new Exception("Value not in range: $val");
return $val;
}
function val_uint16($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected uint16, float given: $val");
if($val > 0xFFFF || $val < 0)
throw new Exception("Value not in range: $val");
return $val;
}
function val_int16($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected int16, float given: $val");
if($val > 32767 || $val < -32768)
throw new Exception("Value not in range: $val");
return $val;
}
function val_uint8($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected uint8, float given: $val");
if($val > 0xFF || $val < 0)
throw new Exception("Value not in range: $val");
return $val;
}
function val_int8($val)
{
if(!is_numeric($val))
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
$val = 1*$val;
if(is_float($val))
throw new Exception("Incompatible type, expected int8, float given: $val");
if($val > 127 || $val < -128)
throw new Exception("Value not in range: $val");
return $val;
}
function val_arr($val)
{
if(!is_array($val))
throw new Exception("Bad item, not an array(" . serialize($val) . ")");
return $val;
}
function val_enum($enum, $val, $check_enum_validity = true)
{
if(is_string($val))
return call_user_func_array(array($enum, "getValueByName"), array($val));
if(!is_numeric($val))
throw new Exception("Bad enum value, not a numeric or string(" . serialize($val) . ")");
if($check_enum_validity)
call_user_func_array(array($enum, "checkValidity"), array($val));
return $val;
}

View File

@ -1,10 +1,11 @@
<?php
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
{% if namespace is defined %}
namespace {{namespace}};
{% endif %}
{%- import "macro.twig" as macro -%}
//%bundle%
{{ macro.decl_units(meta) }}
class AutogenBundle
@ -13,19 +14,9 @@ class AutogenBundle
{
switch($id)
{
//%class_map%
{{ macro.class_map(meta) }}
default : throw new Exception("Can't find class for id: $id");
}
}
static function createRPC($code, $data)
{
switch($code)
{
//%packet_map%
default : throw new Exception("Can't find RPC for code: $code");
}
}
}

View File

@ -4,13 +4,14 @@
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaStruct') -%}
{{ _self.decl_struct(u.object) }}
{%- elseif u.object is instanceof('\\mtgMetaEnum') -%}
{{ _self.decl_enum(u.object) }}
{%- endif ~%}
{%- endfor ~%}
{% endmacro %}
{% macro decl_struct(o, extra = '') %}
{% macro decl_struct(o) %}
class {{o.name}} {{o.parent ? 'extends ' ~ o.parent.name}}
{
@ -47,8 +48,6 @@ class {{o.name}} {{o.parent ? 'extends ' ~ o.parent.name}}
return $flds;
}
{{extra}}
function __construct(&$data = null, $assoc = false)
{
{{_self.fields_init(o)}}
@ -105,7 +104,7 @@ class {{o.name}} {{o.parent ? 'extends ' ~ o.parent.name}}
function fill(&$data, $assoc = false, $virtual = false)
{
if($virtual)
mtg_php_array_set_value($data, $assoc, 'vclass__', $this->getClassId());
\metagen_php\array_set_value($data, $assoc, 'vclass__', $this->getClassId());
try
{
$__last_var = null;
@ -115,7 +114,7 @@ class {{o.name}} {{o.parent ? 'extends ' ~ o.parent.name}}
}
catch(Exception $e)
{
throw new Exception("[%class%]\n->$__last_var\n" . serialize($__last_val) . "\n\t" . $e->getMessage());
throw new Exception("[{{o.name}}]\n->$__last_var\n" . serialize($__last_val) . "\n\t" . $e->getMessage());
}
}
}
@ -195,7 +194,7 @@ array_merge(parent::CLASS_FIELDS_PROPS(),
new {{f.type}}()
{%- elseif f.type is instanceof('\\mtgMetaEnum') ~%}
{%- if has_token(f, 'default') -%}
{{f.type}}::{{token(f, 'default')|default_value}}
{{f.type}}::{{token(f, 'default')|replace({'"' : ''})|default_value}}
{%- else -%}
{{f.type}}::DEFAULT_VALUE
{%- endif -%}
@ -203,7 +202,7 @@ array_merge(parent::CLASS_FIELDS_PROPS(),
[]
{%- elseif f.type is instanceof('\\mtgBuiltinType') ~%}
{%- if has_token(f, 'default') -%}
mtg_php_val_{{f.type}}({{apply_value_filters(f.name, f.tokens, token(f, 'default')|default_value, false)}})
\metagen_php\val_{{f.type}}({{apply_value_filters(f.name, f.tokens, token(f, 'default')|default_value, false)}})
{%- elseif f.type.isstring -%}
''
{%- else -%}
@ -247,3 +246,134 @@ array_merge(parent::CLASS_FIELDS_PROPS(),
{{value2data(f.name, f.type, '$data', '$this->', f.tokens)}}
{%- endfor -%}
{% endmacro %}
{% macro decl_enum(o) %}
class {{o.name}}
{
const CLASS_ID = {{o.classid}};
{{ _self.enum_values(o) }}
const DEFAULT_VALUE = {{o.values | first}};
function getClassId()
{
return self::CLASS_ID;
}
static function isValueValid($value)
{
$values_list = self::getValuesList();
return in_array($value, self::$values_list_);
}
static private $values_map_;
static function getValueByName($name)
{
if(!self::$values_map_)
{
self::$values_map_ = array(
{{ _self.enum_values_map(o) }}
);
}
if(!isset(self::$values_map_[$name]))
throw new Exception("Value with name '$name' isn't defined in enum {{o.name}}. Accepted: " . implode(',', self::getNamesList()));
return self::$values_map_[$name];
}
static private $names_map_;
static function getNameByValue($value)
{
if(!self::$names_map_)
{
self::$names_map_ = array(
{{ _self.enum_names_map(o) }}
);
}
if(!isset(self::$names_map_[$value]))
throw new Exception("Value $value isn't defined in enum {{o.name}}. Accepted: " . implode(',', self::getValuesList()));
return self::$names_map_[$value];
}
// throws exception if $value is not valid numeric enum value
static function checkValidity($value)
{
if(!is_numeric($value))
throw new Exception("Numeric expected but got $value");
if(!self::isValueValid($value))
throw new Exception("Numeric value '$value' isn't value from enum {{o.name}}. Accepted numerics are " . implode(',', self::getValuesList()) . " but better to use one of names instead: " . implode(',', self::getNamesList()));
}
static private $values_list_;
static function getValuesList()
{
if(!self::$values_list_)
{
self::$values_list_ = array(
{{ _self.enum_values_list(o) }}
);
}
return self::$values_list_;
}
static private $names_list_;
static function getNamesList()
{
if(!self::$names_list_)
{
self::$names_list_ = array(
{{ _self.enum_names_list(o) }}
);
}
return self::$names_list_;
}
}
{% endmacro %}
{% macro enum_values(o) %}
{%- for n,v in o.values ~%}
const {{n}} = {{v}};
{%- endfor ~%}
{% endmacro %}
{% macro enum_values_map(o) %}
{%- for n,v in o.values ~%}
'{{n}}' => {{v}},
{%- endfor ~%}
{% endmacro %}
{% macro enum_names_map(o) %}
{%- for n,v in o.values ~%}
{{v}} => '{{n}}',
{%- endfor ~%}
{% endmacro %}
{% macro enum_values_list(o) %}
{%- for v in o.values ~%}
{{v}},
{%- endfor ~%}
{% endmacro %}
{% macro enum_names_list(o) %}
{%- for n,v in o.values ~%}
'{{n}}',
{%- endfor ~%}
{% endmacro %}
{% macro class_map(meta) %}
{%- for u in meta.getunits ~%}
case {{u.object.classid}} : return "{{u.object.name}}";
{%- endfor ~%}
{% endmacro %}