From a84718215d35b1167d1c1521876a968fb28266c2 Mon Sep 17 00:00:00 2001 From: Pavel Shevaev Date: Wed, 27 Sep 2023 00:15:44 +0300 Subject: [PATCH] Improving structs and interfaces validation --- metagen.inc.php | 85 +++++++++++++++++++++++++++++++++++++++++++++---- parser.inc.php | 42 ++++++++++++++++-------- 2 files changed, 107 insertions(+), 20 deletions(-) diff --git a/metagen.inc.php b/metagen.inc.php index a12c0c3..e1ff14b 100644 --- a/metagen.inc.php +++ b/metagen.inc.php @@ -10,12 +10,11 @@ class mtgTypeRef implements mtgType private static $unresolved = array(); private $name; - private $file; - private $line; private $module; private $resolved; + private $origin; - function __construct($name_or_resolved, mtgMetaParsedModule $module = null, $file = '', $line= 0) + function __construct($name_or_resolved, mtgMetaParsedModule $module = null, mtgOrigin $origin = null) { if(is_object($name_or_resolved)) { @@ -33,8 +32,7 @@ class mtgTypeRef implements mtgType } $this->module = $module; - $this->file = $file; - $this->line = $line; + $this->origin = $origin ?? new mtgOrigin(); } @@ -67,7 +65,7 @@ class mtgTypeRef implements mtgType return $this->resolved; } else - throw new Exception("{$this->file}@{$this->line} : Symbol '{$this->name}' not found"); + throw new Exception("{$this->origin} : Symbol '{$this->name}' not found"); } function __toString() @@ -123,6 +121,12 @@ class mtgMetaInfo return $this->units[$id]; throw new Exception("Unit '$id' not found"); } + + function validate() + { + foreach($this->units as $u) + $u->object->validate($this); + } } abstract class mtgMetaUnit @@ -131,6 +135,8 @@ abstract class mtgMetaUnit protected $tokens = array(); + protected $origin; + function getTokens() { return $this->tokens; @@ -155,9 +161,38 @@ abstract class mtgMetaUnit { return array_key_exists($name, $this->tokens); } + + function setOrigin(mtgOrigin $origin) + { + $this->origin = $origin; + } + + function getOrigin() + { + return $this->origin; + } + + abstract function validate(mtgMetaInfo $meta); } -class mtgUserType extends mtgMetaUnit implements mtgType +class mtgOrigin +{ + public $file; + public $line; + + function __construct($file = '', $line = 0) + { + $this->file = $file; + $this->line = $line; + } + + function __toString() + { + return "{$this->file}@{$this->line}"; + } +} + +abstract class mtgUserType extends mtgMetaUnit implements mtgType { protected $name; @@ -214,6 +249,34 @@ class mtgMetaStruct extends mtgUserType $this->implements = $implements; } + function validate(mtgMetaInfo $meta) + { + $parent = $this->getParent(); + while($parent != null) + { + foreach($this->fields as $name => $_) + if($parent->hasField($name)) + throw new Exception("{$this->origin} : Parent struct '{$parent->getName()}' already has field '{$name}'"); + + foreach($this->funcs as $name => $_) + if($parent->hasFunc($name)) + throw new Exception("{$this->origin} : Parent struct '{$parent->getName()}' already has func '{$name}'"); + + $parent = $parent->getParent(); + } + + $s = new SplObjectStorage(); + foreach($this->getImplements() as $imp) + { + if(!($imp instanceof mtgMetaInterface)) + throw new Exception("{$this->origin} : Not an interface '{$imp}'"); + + if($s->contains($imp)) + throw new Exception("{$this->origin} : Duplicate interface reference '{$imp->getName()}'"); + $s->attach($imp); + } + } + function getParent() { return $this->parent ? $this->parent->resolve() : null; @@ -315,6 +378,8 @@ class mtgMetaInterface extends mtgUserType $this->tokens = $tokens; } + function validate(mtgMetaInfo $meta) {} + function getImplements() { if(!$this->implements) @@ -363,6 +428,8 @@ class mtgMetaFunc extends mtgMetaUnit implements mtgType $this->name = $name; } + function validate(mtgMetaInfo $meta) {} + function getMetaId() { return $this->name; @@ -445,6 +512,8 @@ class mtgMetaRPC extends mtgMetaUnit $this->tokens = $tokens; } + function validate(mtgMetaInfo $meta) {} + function getMetaId() { return $this->code; @@ -732,6 +801,8 @@ class mtgMetaEnum extends mtgUserType { private $values = array(); + function validate(mtgMetaInfo $meta) {} + function addValue($value_name, $value) { if(isset($this->values[$value_name])) diff --git a/parser.inc.php b/parser.inc.php index a1cc931..806c9f2 100644 --- a/parser.inc.php +++ b/parser.inc.php @@ -105,8 +105,6 @@ class mtgMetaInfoParser throw new Exception("No such file '$raw_file'"); $this->_parse($file); - - mtgTypeRef::checkAllResolved(); } private function _parse($file) @@ -206,26 +204,30 @@ class mtgMetaInfoParser if($this->token == self::T_Func) { + $origin = new mtgOrigin($this->file, $this->line); $func_type = $this->_parseFuncType(); - $type = new mtgTypeRef($func_type, $this->module, $this->file, $this->line); + $type = new mtgTypeRef($func_type, $this->module, $origin); } else if($this->token == self::T_Identifier) { + $origin = new mtgOrigin($this->file, $this->line); $type_name = $this->_parseDotName(); - $type = new mtgTypeRef($type_name, $this->module, $this->file, $this->line); + $type = new mtgTypeRef($type_name, $this->module, $origin); } else { + $origin = new mtgOrigin($this->file, $this->line); $type_name = $this->attribute; - $type = new mtgTypeRef(new mtgBuiltinType($type_name), $this->module, $this->file, $this->line); + $type = new mtgTypeRef(new mtgBuiltinType($type_name), $this->module, $origin); $this->_next(); } if($this->token == ord('[')) { + $origin = new mtgOrigin($this->file, $this->line); $this->_next(); $this->_checkThenNext(']'); - $type = new mtgTypeRef(new mtgArrType($type), $this->module, $this->file, $this->line); + $type = new mtgTypeRef(new mtgArrType($type), $this->module, $origin); } $types[] = $type; @@ -238,7 +240,7 @@ class mtgMetaInfoParser } if(sizeof($types) > 1) - return new mtgTypeRef(new mtgMultiType($types), $this->module, $this->file, $this->line); + return new mtgTypeRef(new mtgMultiType($types), $this->module, new mtgOrigin($this->file, $this->line)); else return $types[0]; } @@ -511,14 +513,16 @@ class mtgMetaInfoParser private function _parseStruct() { $this->_next(); + $struct_origin = new mtgOrigin($this->file, $this->line); $name = $this->_parseDotName(); $parent = null; if($this->token == self::T_Extends) { $this->_next(); + $origin = new mtgOrigin($this->file, $this->line); $parent_name = $this->_checkThenNext(self::T_Identifier); - $parent = new mtgTypeRef($parent_name, $this->module, $this->file, $this->line); + $parent = new mtgTypeRef($parent_name, $this->module, $origin); } $implements = array(); @@ -527,12 +531,14 @@ class mtgMetaInfoParser do { $this->_next(); + $origin = new mtgOrigin($this->file, $this->line); $if_name = $this->_checkThenNext(self::T_Identifier); - $implements[] = new mtgTypeRef($if_name, $this->module, $this->file, $this->line); + $implements[] = new mtgTypeRef($if_name, $this->module, $origin); } while($this->token == ord(',')); } $s = new mtgMetaStruct($name, array(), $parent, array(), $implements); + $s->setOrigin($struct_origin); $this->_addUnit(new mtgMetaInfoUnit($this->file, $s)); $tokens = $this->shared_tokens; @@ -577,10 +583,15 @@ class mtgMetaInfoParser $tokens = array_merge($tokens, $this->_parsePropTokens()); $s->setTokens($tokens); - $this->_next(); - $funcs = $this->_parseFuncs(); - foreach($funcs as $fn) - $s->addFunc($fn); + if($this->token !== self::T_End) + { + $this->_next(); + $funcs = $this->_parseFuncs(); + foreach($funcs as $fn) + $s->addFunc($fn); + } + else + $this->_next(); } private function _parseRPC() @@ -938,6 +949,11 @@ function mtg_parse_meta(array $meta_srcs, $valid_tokens = null, $inc_path = null $meta = new mtgMetaInfo(); foreach($meta_srcs as $src) mtg_load_meta($meta, $meta_parser, $src); + + $meta->validate(); + + mtgTypeRef::checkAllResolved(); + return $meta; }