Skip to content

Parser #18532

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed

Parser #18532

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/customTargets.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

247 changes: 247 additions & 0 deletions .idea/editor.xml

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/php-src.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM ubuntu:noble

RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \
libxml2-dev libsqlite3-dev cmake gdb
WORKDIR /app

CMD while true; do sleep 3600; done
8 changes: 8 additions & 0 deletions Dockerfile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM ubuntu:noble

RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \
libxml2-dev libsqlite3-dev cmake gdb
WORKDIR /app

COPY ./sapi/cli/php php
RUN chmod +x /app/php
154 changes: 154 additions & 0 deletions LE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Generics syntax

```
interface Comparable {

}

interface Equatable {

}

int implements Comparable, Equatable;

class Foo {

class Bar {

}

}

enum FooBar<T> {

case Blum(int bam);
case Bam(T boom);

}

```

## Generic classes and interfaces

### Basic case
```lhp
class GenericTestClass<T> {
// T can be used as type alias inside of 'GenericTestClass'
public function __construct(
private T $property
) {}

public function getMyT(): T {
return $this->property;
}

public function setMyT(T $newValue) {
$this->property = $newValue;
}
}
```

### Multiple Generics
```lehp
class GenericTestClass<T, U> {
// T and U can now be used as type aliases inside of 'GenericTestClass'

public function __construct(
private T $first,
private U $second
) {}

public function getFirst(): T {
return $this->first;
}

public function setFirst(T $value) {
$this->first = $value;
}

public function getSecond(): U {
return $this->second;
}

public function setSecond(U $value) {
$this->second = $value;
}
}
```

### Bounded Gerics
```lehp
interface Serializable {
public function serialize(): string;
}

class BoundedGenericClass<T: Serializable> {
public function __construct(
private T $serializable
) {}

public function getSerialized(): string
{
return $this->serializable->serialize();
}

public function getAsSerializable(): Serializable
{
return (Serializable) $this->serializable; // cast optional
}

public function getObject(): T
{
return $this->serializable;
}
}
```

### A little bit more complex
```
interface Serializer<T> {
public function serialize(T $object): string;
}

class RandomAssSerializableContaier<T, K: Serializer<T>> {

private array $content = [];

public function __construct(
private K serializer
) {}

public function add(T $toAdd)
{
$content[] = $toAdd;
}

public function asSerializedList(): array
{
return array_map(
fn ($obj) $this->serializer->serialize($obj),
$this->content
);
}
}
```
## Functions
### Basic case
```
function noOp<T>(T $in): T
{
return $in;
}
```

### A bit more complex
```
interface Serializer<T> {
public function serialize(T $object): string;
}

function serialize<T, K: Serializer<T>>(T $in, K $serializer): string
{
return $serializer->serialize($in);
}
```
9 changes: 9 additions & 0 deletions Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,17 @@ struct _zend_inheritance_cache_entry {
zend_class_entry *traits_and_interfaces[1];
};

typedef struct _zend_type_parameter {
struct _zend_generic_type *elements;
size_t element_count;
zend_string *name;
} zend_type_parameter;

struct _zend_class_entry {
// TYPE HERE
char type;
size_t generic_type_count;
zend_type_parameter *generic_type;
zend_string *name;
/* class_entry or string depending on ZEND_ACC_LINKED */
union {
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum _zend_ast_kind {
ZEND_AST_ATTRIBUTE_GROUP,
ZEND_AST_MATCH_ARM_LIST,
ZEND_AST_MODIFIER_LIST,
ZEND_AST_GENERIC_TYPE_PARAM_LIST,

/* 0 child nodes */
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
Expand Down Expand Up @@ -155,6 +156,8 @@ enum _zend_ast_kind {
ZEND_AST_NAMED_ARG,
ZEND_AST_PARENT_PROPERTY_HOOK_CALL,

ZEND_AST_GENERIC_TYPE,

/* 3 child nodes */
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,
ZEND_AST_NULLSAFE_METHOD_CALL,
Expand Down
54 changes: 53 additions & 1 deletion Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -9043,6 +9043,53 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_
zend_type_release(type, 0);
}

// LEHP-RELEVANT
// compiles generic class params from the AST to the zend_class_entry
static void zend_compile_class_generic_params(zend_class_entry *class_entry, zend_ast *ast) {
ZEND_ASSERT(ast->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST);
// we know this is a list as it has GENERIC_LIST_TYPE
zend_ast_list *generic_param_list = zend_ast_get_list(ast);

class_entry->generic_type_count = generic_param_list->children;
class_entry->generic_type = ecalloc(generic_param_list->children, sizeof(zend_type_parameter));
for (uint32_t i = 0; i < generic_param_list->children; i++) {
zend_ast *param_ast = generic_param_list->child[i];
zend_type_parameter *type_param = &class_entry->generic_type[i];

if (param_ast->kind == ZEND_AST_ZVAL) {
// Simple type parameter T
zend_ast_zval *zval_ast = (zend_ast_zval*)param_ast;
if (Z_TYPE(zval_ast->val) != IS_STRING) {
zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string");
}
type_param->name = zend_string_copy(Z_STR(zval_ast->val));
type_param->element_count = 0;
type_param->elements = NULL;
zend_resolve_const_class_name_reference(zval_ast, "class name");
} else if (param_ast->kind == ZEND_AST_GENERIC_TYPE) {
// Type parameter with constraints like T:string
if (param_ast->child[0]->kind != ZEND_AST_ZVAL) {
zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a simple identifier");
}

zend_ast_zval *name_ast = (zend_ast_zval*)param_ast->child[0];
if (Z_TYPE(name_ast->val) != IS_STRING) {
zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string");
}

type_param->name = zend_string_copy(Z_STR(name_ast->val));

// LEHP-TODO add constraints to model
type_param->element_count = 0;
type_param->elements = NULL;

// TODO: In the future, process constraint list from param_ast->child[1]
} else {
zend_error_noreturn(E_COMPILE_ERROR, "Invalid generic type parameter");
}
}
}

static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */
{
zend_ast_decl *decl = (zend_ast_decl *) ast;
Expand Down Expand Up @@ -9134,7 +9181,12 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
CG(active_class_entry) = ce;

if (decl->child[3]) {
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
if(decl->child[3]->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST){
zend_compile_class_generic_params(ce, decl->child[3]);
} else {
// LEHP-TODO this should never happen, attributes are never passed as third child to zend_ast_create_decl(ZEND_AST_CLASS, ...)???
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
}
}

if (implements_ast) {
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,11 @@ typedef struct _zend_internal_arg_info {
} zend_internal_arg_info;

/* arg_info for user functions */
//LEHP-RELEVANT
typedef struct _zend_arg_info {
zend_string *name;
zend_type type;
//LEHP-ASSUMPTION: LEPHP code evaluated at runtime
zend_string *default_value;
} zend_arg_info;

Expand Down
Loading
Loading