node: support magic __FILE__, __LINE__ etc constants

This commit is contained in:
mappu 2020-04-09 19:39:23 +12:00
parent 0dfabadcf2
commit 314d65a459
2 changed files with 86 additions and 0 deletions

View File

@ -0,0 +1,24 @@
<?php
namespace myNamespace;
echo __file__;
echo __Line__;
echo __DIR__;
echo __NAMESPACE__;
function Foo() {
echo __FUNCTION__;
}
class Bar {
function Baz() {
echo __CLASS__ . "::" . __METHOD__ ;
echo Baz::class;
}
}
// TODO __TRAIT__

62
node.go
View File

@ -39,6 +39,10 @@ type conversionState struct {
currentClassName string
currentClassParentName string
currentErrHandler string
currentFunctionName string
currentMethodName string
currentNamespace string
currentTraitName string // TODO
importPackages map[string]struct{}
}
@ -125,6 +129,7 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
return "", err
}
packageName = namespace
this.currentNamespace = packageName
default:
this.currentErrHandler = "panic(err)\n" // top-level init/main behaviour
@ -273,6 +278,9 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
return "", parseErr{s, err}
}
prevMethodName := this.currentMethodName
this.currentMethodName = funcName
// TODO implement abstract methods as method functions
// Doc comment
@ -322,6 +330,9 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
}
// Reinstate stack
this.currentMethodName = prevMethodName
default:
return "", parseErr{s, fmt.Errorf("Class '%s' contained unexpected AST node; expected PropertyList / ClassMethod", className)}
}
@ -407,11 +418,18 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
funcName = toPublic(funcName)
// Convert body
prevFuncName := this.currentFunctionName
this.currentFunctionName = funcName
funcStmt, err := this.convertFunctionCommon(n.Params, n.ReturnType, n.ReturnsRef, n.Stmts)
if err != nil {
// FIXME an early-return will trash the `this` state around currentFunctionName, etc
// That's OK for now since we don't practically have any error handling than an unmitigated panic
return "", parseErr{n, err}
}
this.currentFunctionName = prevFuncName
ret := "func " + funcName + funcStmt + "\n"
return ret, nil
@ -1188,6 +1206,50 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
return strconv.Quote(rawValue), nil // Go source code quoting format
case *scalar.MagicConstant:
// magic constants are case-insensitive
switch strings.ToLower(n.Value) {
case `__file__`, `__dir__`:
// These are normally used in PHP for finding the absolute webroot directory (e.g. dirname(__FILE__) in index.php)
// The letter of the law would be to replace them with the *.go file/dir
// We could even emit a runtime call to get the Go file/line: @ref https://github.com/golang/go/issues/12876#issuecomment-146878684
// But in practice, the current working directory might be preferable
return `""`, nil
case `__line__`:
return fmt.Sprintf("%d", n.Position.StartLine), nil
case `__namespace__`:
return this.currentNamespace, nil
case `__function__`:
if this.currentFunctionName == "" {
return "", parseErr{n, fmt.Errorf("use of __FUNCTION__ outside of a function")}
}
return this.currentFunctionName, nil
case `__class__`:
if this.currentClassName == "" {
return "", parseErr{n, fmt.Errorf("use of __CLASS__ outside of a class")}
}
return this.currentClassName, nil
case `__method__`:
if this.currentMethodName == "" {
return "", parseErr{n, fmt.Errorf("use of __METHOD__ outside of a class method")}
}
return this.currentMethodName, nil
case `__trait__`:
if this.currentTraitName == "" {
return "", parseErr{n, fmt.Errorf("use of __TRAIT__ outside of a trait")}
}
return this.currentTraitName, nil
default:
return "", parseErr{n, fmt.Errorf("unrecognized magic constant '%s'", n.Value)}
}
//
//
//