public function parse_class() { $class_annotation = $this->last_annotation; $this->last_annotation = null; // abstract class $abstract = false; $final = false; while ($this->at(array(T_ABSTRACT, T_FINAL))) { if ($this->at(T_ABSTRACT)) { $abstract = true; } else { $final = true; } $this->accept(); $this->s(); } // class $this->accept(T_CLASS); $this->s(); $class = new ClassDef($this->parse_ident()); $class->set_abstract($abstract); $class->set_namespace($this->namespace); if ($class_annotation) { $class->set_annotation($class_annotation); } $this->s(); // superclass if ($this->at(T_EXTENDS)) { $this->accept(); $this->s(); $class->extend($this->parse_ident()); $this->s(); } // interfaces $req = T_IMPLEMENTS; while ($this->at($req)) { $this->accept(); $this->s(); $class->implement($this->parse_ident()); $this->s(); $req = ','; } $this->s(); $this->accept('{'); // forward declarations: before Forward::apply('before', $class); // const, var, methods, eval, mixin $this->s(); while ($this->at_class_part()) { if ($this->at(T_CONST)) { $this->accept(); $this->s(); $ident = $this->parse_ident(); $this->s(); $this->accept('='); $this->s(); $class->define_constant($ident, $this->parse_value()); $this->s(); $this->accept(';'); } elseif ($this->at_qualifier()) { $member_annotation = $this->last_annotation; $this->last_annotation = null; $access = 'public'; $static = false; $abstract = false; $final = false; while ($this->at_qualifier()) { switch ($this->current_token()) { case T_PUBLIC: $access = 'public'; break; case T_PRIVATE: $access = 'private'; break; case T_PROTECTED: $access = 'protected'; break; case T_STATIC: $static = true; break; case T_FINAL: $final = true; break; case T_ABSTRACT: $abstract = true; break; } $this->accept(); $this->s(); } if ($this->at(T_VARIABLE)) { $variable = $this->parse_variable($access, $static); if ($member_annotation) { $variable->set_annotation($member_annotation); } $class->add_variable($variable); $this->s(); if ($this->at(T_IMPLEMENTS)) { $this->accept(); $this->s(); $this->write_interface_delegate($class, $variable->get_name(), $this->parse_absolute_namespaced_ident()); $this->s(); while ($this->at(',')) { $this->accept(); $this->s(); $this->write_interface_delegate($class, $variable->get_name(), $this->parse_absolute_namespaced_ident()); $this->s(); } } else { while ($this->at(',')) { $this->accept(); $this->s(); $class->add_variable($this->parse_variable($access, $static)); $this->s(); } } $this->accept(';'); } elseif ($this->at(T_FUNCTION)) { $this->accept(); $this->s(); if ($this->at('&')) { $reference = true; $this->accept(); $this->s(); } else { $reference = false; } if ($this->at(T_CONSTANT_ENCAPSED_STRING)) { if ($access != 'public') { $this->error("pattern matched methods must be public"); } if ($final) { $this->error("pattern matched methods cannot be final"); } if ($abstract) { $this->error("pattern matched methods cannot be abstract"); } if ($reference) { $this->error("pattern matched methods cannot return by reference"); } $pattern = $this->current_text(); $this->accept(); $this->s(); $args = $this->parse_arg_list(); $this->s(); $body = substr($this->parse_block(), 1, -1); if ($static) { $class->add_static_pattern(new Literal($pattern), $args, $body); } else { $class->add_pattern(new Literal($pattern), $args, $body); } } else { $ident = $this->parse_ident(); $this->s(); $args = $this->parse_arg_list(); $this->s(); if ($this->at(';')) { $body = ''; $this->accept(); } else { $body = substr($this->parse_block(), 1, -1); } $method = new Method($ident); $method->set_access($access); $method->set_static($static); $method->set_final($final); $method->set_abstract($abstract); $method->set_reference_returned($reference); $method->set_arg_list($args); $method->set_body($body); $class->add_method($method); if ($member_annotation) { $method->set_annotation($member_annotation); } } } } elseif ($this->at(T_EVAL)) { $this->accept(); $this->s(); $code = $this->parse_block(); class_eval_without_scope($class, $code); } elseif ($this->at(T_INCLUDE)) { $this->accept(); $this->s(); $mixin_method = 'mixin'; if ($this->at(T_CONST)) { $mixin_method .= '_constants'; $this->accept(); $this->s(); } elseif ($this->at(T_STATIC)) { $mixin_method .= '_static'; $this->accept(); $this->s(); } $module = $this->parse_absolute_namespaced_ident(); $this->s(); $this->accept(';'); $class->{$mixin_method}($module); } $this->s(); } $this->s(); $this->accept('}'); // forward declarations: after Forward::apply('after', $class); return $class; }