node: support magic __FILE__, __LINE__ etc constants
This commit is contained in:
parent
0dfabadcf2
commit
314d65a459
24
fixtures/0011-magic-consts.php
Normal file
24
fixtures/0011-magic-consts.php
Normal 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
62
node.go
@ -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)}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user