stmt: initial array support

This commit is contained in:
mappu 2020-04-05 17:39:22 +12:00
parent 3743866d9f
commit 68b554a6ec
2 changed files with 112 additions and 6 deletions

16
fixtures/0004-arrays.php Normal file
View File

@ -0,0 +1,16 @@
<?php
$foo = array(1);
$foo = [2, 3, 4]; // overwrite
$foo[] = 5;
$bar = ["a" => "b", "c" => "d"];
$bar["x"] = 6;
function lookupIndex(): int {
return 0;
}
echo $bar[lookupIndex() + 1];

102
node.go
View File

@ -391,18 +391,34 @@ func convert(n_ node.Node) (string, error) {
//
case *assign.Assign:
lvalue, err := convert(n.Variable) // might be a more complicated lvalue
if err != nil {
return "", parseErr{n, err}
}
rvalue, err := convert(n.Expression)
if err != nil {
return "", parseErr{n, err}
}
// TODO this may need to use `:=`
return lvalue + " = " + rvalue, nil
if dimf, ok := n.Variable.(*expr.ArrayDimFetch); ok && dimf.Dim == nil {
// Special handling for the case of foo[] = bar
// Transform into append()
arrayVar, err := convert(dimf.Variable)
if err != nil {
return "", parseErr{dimf, err}
}
return arrayVar + ` = append(` + arrayVar + `, ` + rvalue + `)`, nil
} else {
// Normal assignment
lvalue, err := convert(n.Variable) // might be a more complicated lvalue
if err != nil {
return "", parseErr{n, err}
}
// TODO this may need to use `:=`
return lvalue + " = " + rvalue, nil
}
//
// expr
@ -497,6 +513,32 @@ func convert(n_ node.Node) (string, error) {
case *expr.ConstFetch:
return resolveName(n.Constant)
case *expr.Array:
return convertArrayLiteralCommon(n.Items)
case *expr.ShortArray:
return convertArrayLiteralCommon(n.Items)
case *expr.ArrayDimFetch:
// Might be x[foo], might be x[] (i.e. append() call)
// In order to make the append() transformation, we need to lookahead
// for ArrayDimFetch in the `*assign.Assign` case
vv, err := convert(n.Variable)
if err != nil {
return "", parseErr{n, err}
}
if n.Dim == nil {
return "", parseErr{n, fmt.Errorf("found '%s[]' outside of lvalue assignment context", vv)}
}
idx, err := convert(n.Dim)
if err != nil {
return "", parseErr{n, err}
}
return vv + `[` + idx + `]`, nil // Same syntax as PHP
//
// binary
//
@ -754,6 +796,54 @@ func convertFuncCallArgsCommon(args *node.ArgumentList) (string, error) {
return "(" + strings.Join(callParams, `, `) + ")", nil // expr only, no semicolon/newline
}
func convertArrayLiteralCommon(items []node.Node) (string, error) {
// Array literal
// We need to know the type. See if we can guess it from the first child element
// At least, we may be able to determine if this is a map or an array
entries := []string{}
keyType := unknownVarType
valType := unknownVarType
isMapType := false
for idx, itm_ := range items {
itm, ok := itm_.(*expr.ArrayItem)
if !ok {
return "", parseErr{itm_, fmt.Errorf("expected ArrayItem")}
}
if idx == 0 {
isMapType = (itm.Key != nil)
} else {
if isMapType != (itm.Key != nil) {
return "", parseErr{itm, fmt.Errorf("Can't represent array and map in a single type")}
}
}
vv, err := convert(itm.Val)
if err != nil {
return "", parseErr{itm, err}
}
if itm.Key != nil {
kv, err := convert(itm.Key)
if err != nil {
return "", parseErr{itm, err}
}
entries = append(entries, kv+`: `+vv+`,`)
} else {
entries = append(entries, vv+`,`)
}
}
if isMapType {
return `map[` + keyType + `]` + valType + `{` + strings.Join(entries, " ") + `}`, nil
} else {
return `[]` + valType + `{` + strings.Join(entries, " ") + `}`, nil
}
}
func convertFunctionCommon(params []node.Node, returnType node.Node, returnsRef bool, bodyStmts []node.Node) (string, error) {
// TODO scan function and see if it contains any return statements at all