node: support interfaces

This commit is contained in:
mappu 2020-04-07 21:53:00 +12:00
parent 8752e8349c
commit 19d7cc469a
3 changed files with 62 additions and 7 deletions

View File

@ -52,7 +52,7 @@ The goal is to produce idiomatic, maintainable Go code as part of a one-off conv
- *PHP variable names are case-sensitive, function names are case-insensitive* - *PHP variable names are case-sensitive, function names are case-insensitive*
- [X] Static methods - [X] Static methods
- [X] Inheritance *partial* - [X] Inheritance *partial*
- [ ] Interfaces - [X] Interfaces
- [X] super - [X] super
- [X] Need to track current conversion state through into function generator, to select the concrete parent - [X] Need to track current conversion state through into function generator, to select the concrete parent
- [X] parent:: - [X] parent::

View File

@ -0,0 +1,9 @@
<?php
interface Base {
function Foo();
}
class Child implements Base {
function Foo() {}
}

58
node.go
View File

@ -105,7 +105,7 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
} }
switch s.(type) { switch s.(type) {
case *stmt.Class, *stmt.Function: case *stmt.Class, *stmt.Function, *stmt.Interface:
// Declaration - emit immediately (hoist) // Declaration - emit immediately (hoist)
ret += sm + "\n" ret += sm + "\n"
@ -265,12 +265,56 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
// Create all member functions // Create all member functions
ret += strings.Join(memberFuncs, "\n\n") ret += strings.Join(memberFuncs, "\n\n")
if n.Implements != nil {
for _, ifaceName_ := range n.Implements.InterfaceNames {
ifaceName, err := this.resolveName(ifaceName_)
if err != nil {
return "", parseErr{ifaceName_, err}
}
// Add extra interface assertion statement
ret += "var _ " + ifaceName + " = &" + className + "{} // interface assertion\n"
}
}
// Done // Done
// Reinstate parent state before returning // Reinstate parent state before returning
this.currentClassName = prevClassName this.currentClassName = prevClassName
this.currentClassParentName = prevClassParentName this.currentClassParentName = prevClassParentName
return ret, nil return ret, nil
case *stmt.Interface:
ifaceName := n.InterfaceName.(*node.Identifier).Value
ret := "type " + ifaceName + " interface {\n"
for _, ss := range n.Stmts {
classMethod, ok := ss.(*stmt.ClassMethod)
if !ok {
return "", parseErr{ss, fmt.Errorf("expected stmt.ClassMethod")}
}
methodName, err := applyVisibilityModifier(classMethod.MethodName.(*node.Identifier).Value, classMethod.Modifiers)
if err != nil {
return "", parseErr{ss, err}
}
// TODO classMethod.PhpDocComment
arglist, err := this.convertFunctionCommon(classMethod.Params, classMethod.ReturnType, classMethod.ReturnsRef, nil)
if err != nil {
return "", parseErr{ss, err}
}
ret += methodName + arglist + "\n"
}
ret += "}\n"
return ret, nil
case *stmt.Function: case *stmt.Function:
// Top-level function definition // Top-level function definition
// TODO parse doc comment // TODO parse doc comment
@ -1154,12 +1198,14 @@ func (this *conversionState) convertFunctionCommon(params []node.Node, returnTyp
// Recurse through body statements // Recurse through body statements
fullBody, err := this.convert(stmt.NewStmtList(bodyStmts)) if bodyStmts != nil {
if err != nil { fullBody, err := this.convert(stmt.NewStmtList(bodyStmts))
return "", err if err != nil {
} return "", err
}
ret += fullBody + "\n" ret += fullBody + "\n"
}
// Done // Done
// No extra trailing newline in case this is part of a large expression // No extra trailing newline in case this is part of a large expression