8000 Implement interface defaults · xp-framework/compiler@6d7a8dd · GitHub
[go: up one dir, main page]

Skip to content

Commit 6d7a8dd

Browse files
committed
Implement interface defaults
1 parent d6bd2ad commit 6d7a8dd

File tree

4 files changed

+81
-4
lines changed
  • src
    • < 8000 div id=":Rumtddab:" class="PRIVATE_TreeView-item-content prc-TreeView-TreeViewItemContent-f0r0b">main/php/lang/ast/emit
  • test/php/lang/ast/unittest/emit
  • 4 files changed

    +81
    -4
    lines changed

    src/main/php/lang/ast/emit/Declaration.class.php

    Lines changed: 15 additions & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -1,6 +1,6 @@
    11
    <?php namespace lang\ast\emit;
    22

    3-
    use lang\ast\nodes\{EnumCase, Property};
    3+
    use lang\ast\nodes\{EnumCase, Property, Method};
    44

    55
    class Declaration extends Type {
    66
    private $type, $result;
    @@ -19,6 +19,20 @@ public function __construct($type, $result) {
    1919
    /** @return string */
    2020
    public function name() { return ltrim($this->type->name, '\\'); }
    2121

    22+
    /**
    23+
    * Returns whether this is an interface with default implementations
    24+
    *
    25+
    * @return bool
    26+
    */
    27+
    public function defaultImplementations() {
    28+
    if ('interface' === $this->type->kind) {
    29+
    foreach ($this->type->body as $member) {
    30+
    if ($member instanceof Method && null !== $member->body) return true;
    31+
    }
    32+
    }
    33+
    return false;
    34+
    }
    35+
    2236
    /**
    2337
    * Returns whether a given member is an enum case
    2438
    *

    src/main/php/lang/ast/emit/PHP.class.php

    Lines changed: 41 additions & 3 deletions
    Original file line numberDiff line numberDiff line change
    @@ -12,9 +12,10 @@
    1212
    Property,
    1313
    ScopeExpression,
    1414
    UnpackExpression,
    15+
    TraitDeclaration,
    1516
    Variable
    1617
    };
    17-
    use lang\ast\types\{IsUnion, IsFunction, IsArray, IsMap, IsNullable, IsExpression};
    18+
    use lang\ast\types\{IsUnion, IsFunction, IsArray, IsMap, IsNullable, IsValue, IsExpression};
    1819
    use lang\ast\{Emitter, Node, Type, Result};
    1920

    2021
    abstract class PHP extends Emitter {
    @@ -359,7 +360,7 @@ protected function emitEnumCase($result, $case) {
    359360
    protected function emitEnum($result, $enum) {
    360361
    array_unshift($result->type, $enum);
    361362
    array_unshift($result->meta, []);
    362-
    $result->locals= [[], []];
    363+
    $result->locals ?: $result->locals= [[], [], [], []];
    363364

    364365
    $enum->comment && $this->emitOne($result, $enum->comment);
    365366
    $enum->annotations && $this->emitOne($result, $enum->annotations);
    @@ -385,23 +386,27 @@ protected function emitEnum($result, $enum) {
    385386
    $this->emitInitializations($result, $result->locals[0]);
    386387
    $this->emitMeta($result, $enum->name, $enum->annotations, $enum->comment);
    387388
    $result->out->write('}} '.$enum->name->literal().'::__init();');
    389+
    388390
    array_shift($result->type);
    391+
    $result->locals= [];
    389392
    }
    390393

    391394
    protected function emitClass($result, $class) {
    392395
    array_unshift($result->type, $class);
    393396
    array_unshift($result->meta, []);
    394-
    $result->locals ?: $result->locals= [[], [], []];
    397+
    $result->locals ?: $result->locals= [[], [], [], []];
    395398

    396399
    $class->comment && $this->emitOne($result, $class->comment);
    397400
    $class->annotations && $this->emitOne($result, $class->annotations);
    398401
    $result->at($class->declared)->out->write(implode(' ', $class->modifiers).& 6D40 #39; class '.$class->declaration());
    399402
    $class->parent && $result->out->write(' extends '.$class->parent->literal());
    400403

    404+
    $defaults= [];
    401405
    if ($class->implements) {
    402406
    $list= '';
    403407
    foreach ($class->implements as $type) {
    404408
    $list.= ', '.$type->literal();
    409+
    $result->lookup($type->literal())->defaultImplementations() && $defaults[]= $type;
    405410
    }
    406411
    $result->out->write(' implements '.substr($list, 2));
    407412
    }
    @@ -447,10 +452,15 @@ protected function emitClass($result, $class) {
    447452
    $result->out->write('}');
    448453
    }
    449454

    455+
    foreach ($defaults as $trait) {
    456+
    $result->out->write('use __'.substr($trait->literal(), 1).'_Defaults;');
    457+
    }
    458+
    450459
    $result->out->write('static function __init() {');
    451460
    $this->emitInitializations($result, $result->locals[0]);
    452461
    $this->emitMeta($result, $class->name, $class->annotations, $class->comment);
    453462
    $result->out->write('}} '.$class->name->literal().'::__init();');
    463+
    454464
    array_shift($result->type);
    455465
    $result->locals= [];
    456466
    }
    @@ -509,7 +519,9 @@ protected function emitAnnotations($result, $annotations) {
    509519
    }
    510520

    511521
    protected function emitInterface($result, $interface) {
    522+
    array_unshift($result->type, $interface);
    512523
    array_unshift($result->meta, []);
    524+
    $result->locals ?: $result->locals= [[], [], [], []];
    513525

    514526
    $interface->comment && $this->emitOne($result, $interface->comment);
    515527
    $interface->annotations && $this->emitOne($result, $interface->annotations);
    @@ -522,10 +534,24 @@ protected function emitInterface($result, $interface) {
    522534
    $result->out->write('}');
    523535

    524536
    $this->emitMeta($result, $interface->name, $interface->annotations, $interface->comment);
    537+
    538+
    // Emit default implementations
    539+
    if ($result->locals[3]) {
    540+
    $this->emitOne($result, new TraitDeclaration(
    541+
    [],
    542+
    new IsValue('\\__'.$interface->declaration().'_Defaults'),
    543+
    $result->locals[3]
    544+
    ));
    545+
    }
    546+
    547+
    array_shift($result->type);
    548+
    $result->locals= [];
    525549
    }
    526550

    527551
    protected function emitTrait($result, $trait) {
    552+
    array_unshift($result->type, $trait);
    528553
    array_unshift($result->meta, []);
    554+
    $result->locals= [[], [], [], []];
    529555

    530556
    $trait->comment && $this->emitOne($result, $trait->comment);
    531557
    $trait->annotations && $this->emitOne($result, $trait->annotations);
    @@ -537,6 +563,9 @@ protected function emitTrait($result, $trait) {
    537563
    $result->out->write('}');
    538564

    539565
    $this->emitMeta($result, $trait->name, $trait->annotations, $trait->comment);
    566+
    567+
    array_shift($result->type);
    568+
    $result->locals= [];
    540569
    }
    541570

    542571
    protected function emitUse($result, $use) {
    @@ -626,6 +655,10 @@ protected function emitMethod($result, $method) {
    626655

    627656
    if (null === $method->body) {
    628657
    $result->out->write(';');
    658+
    $default= null;
    659+
    } else if ('interface' === $result->type[0]->kind) {
    660+
    $result->out->write(';');
    661+
    $default= $method;
    629662
    } else {
    630663
    $result->out->write(' {');
    631664
    $this->emitInitializations($result, $result->locals[1]);
    @@ -634,6 +667,7 @@ protected function emitMethod($result, $method) {
    634667
    }
    635668
    $this->emitAll($result, $method->body);
    636669
    $result->out->write('}');
    670+
    $default= null;
    637671
    }
    638672

    639673
    foreach ($promoted as $param) {
    @@ -646,6 +680,10 @@ protected function emitMethod($result, $method) {
    646680
    foreach ($virtual as $name => $access) {
    647681
    $result->locals[2][$name]= $access;
    648682
    }
    683+
    684+
    // Copy default implementations to class scope
    685+
    $default && $result->locals[3][]= $default;
    686+
    649687
    $result->meta[0][self::METHOD][$method->name]= $meta;
    650688
    }
    651689

    src/main/php/lang/ast/emit/Reflection.class.php

    Lines changed: 9 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -23,6 +23,15 @@ public function __construct($type) {
    2323
    /** @return string */
    2424
    public function name() { return $this->reflect->name; }
    2525

    26+
    /**
    27+
    * Returns whether this is an interface with default implementations
    28+
    *
    29+
    * @return bool
    30+
    */
    31+
    public function defaultImplementations() {
    32+
    return $this->reflect->isInterface() && trait_exists('__'.$this->reflect->name.'_Defaults', false);
    33+
    }
    34+
    2635
    /**
    2736
    * Returns whether a given member is an enum case
    2837
    *

    src/test/php/lang/ast/unittest/emit/TypeDeclarationTest.class.php

    Lines changed: 16 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -58,6 +58,22 @@ public function interface_type_with_method() {
    5858
    Assert::true($this->type('interface <T> { public function name(); }')->isInterface());
    5959
    }
    6060

    61+
    #[Test]
    62+
    public function interface_type_with_default_method() {
    63+
    $i= $this->type('interface <T> {
    64+
    public function all();
    65+
    public function filter($filter) {
    66+
    foreach ($this->all() as $element) {
    67+
    $filter($element) || yield $element;
    68+
    }
    69+
    }
    70+
    }');
    71+
    $t= $this->type('class <T> implements '.$i->literal().'{
    72+
    public function all() { return [1, 2, 3]; }
    73+
    }');
    74+
    Assert::equals([2], iterator_to_array($t->newInstance()->filter(function($i) { return $i % 2; })));
    75+
    }
    76+
    6177
    #[Test, Values(['public', 'private', 'protected'])]
    6278
    public function constant($modifiers) {
    6379
    $c= $this->type('class <T> { '.$modifiers.' const test = 1; }')->getConstant('test');

    0 commit comments

    Comments
     (0)
    0