This change allows the interpreter to execute tests and benchmarks
functions provided by packages.
The test subcommand is similar to the "go test" command and
all the relevant flags have been kept.
The ability to evaluate a directory or a package has also been added.
A new method Symbol to access exported symbol values of an interpreted
package has been added. This method is used by the test subcommand.
An EvalTest method has been added to evaluate all Go files, including "*_test.go".
The testing packages from the standard library have been added to stdlib used
symbols.
The Go parser is able to detect some (but not all) (re-)decleration errors,
if the DeclarationErrors flag is enabled, which was not done so far.
This PR therefore enables that flag, which allows the removal of some of
the now unneeded code that was recently added to support redeclarations.
Fixes#811
The recent changes that added some redeclaration checks implicitly added more
strictness related to namespaces and scopes which, among other things, broke
some uses that "accidentally" used to work.
For example, given
const script1 = `
import "fmt"
// more code
`
const script2 = `
import "fmt"
// some other code
`
If one Evals script1, then script2, with the same interpreter, without
specifying any scope, as the two fragments would be considered part of the same
(.go file) scope by default, a redeclaration error would be triggered because
import "fmt" is seen twice.
A work-around would have been to specify (a different) i.Name before each Eval
call, so that each script is considered as coming from a different .go file, and
hence are respectively in different scopes with respect to imports.
That lead us to realize we had to make specifying things such as file-scope, and
"incremental mode" (aka REPL), more obvious in the context of an Eval call.
In addition, we want to lay down the foundations for Yaegi being able to behave
more like the go tool wrt to various inputs, i.e. it should be able to take a
package directory, or an import path, as input, instead of just a .go file.
Hence the introduction of a new kind of Eval method (whose signature is not fixed yet):
func (interp *Interpreter) EvalPath(path string) (res reflect.Value, err error)
It partially solves the problem described above because:
1. the path given to EvalPath can be used as the file-scope hint mentioned
above, for now (even though the related implementation details might change).
2. Eval always runs in incremental mode, whereas EvalPath always runs in
non-incremental mode, hence clarifying the situation in that respect.
And to avoid confusion, the Name field of Interpreter is now non-exported,
since it is somewhat redundant with the path argument of EvalPath.
Note that #731 is not fully fixed (and might never be), as a requirement of the
proposed solution is to move the input bits of code into respective files
(instead of leaving them as strings).
Finally, some related bugfixes, documention changes, and some refactoring have
been included. Notably, there is no "empty scope" anymore, i.e. name defaults
to "_.go" when it is not specified.
Updates #731Fixes#778Fixes#798Fixes#789
Co-authored-by: Marc Vertes <mvertes@free.fr>
This adds type checking to CallExpr (excluding builtin type checking, as that is a PR in its own right) as well as handling any required constant type conversion.
This also changes constant strings and runes to be represented as `constant.Value`. Runes change `rval` type at CFG typing time to avoid having to type at AST time. There are also changes to importSpecs and `stdlib` to account for the string change. With this all `untyped` types should now be `constant.Value`s, although errors are still not returned if this is not the case to be sure we do not break things.
This also fixed a bug in `itype.methods` that would panic if the type was recursive.
* fix: constant definition loop on out of order vars
* fix: do not wire global varDecl
* fix: wire and execute global vars
* chore: add tests
* fix: refactor and lint
* fix: finish support of type assertions which was incomplete
TypeAssert was optimistically returning ok without verifying that
value could be converted to the required interface (in case of
type assert of an interface type), or not checking the type in
all conditions. There is now a working implements method for itype.
Fixes#640.
* style: appease lint
* fix: remove useless code block
* doc: improve comments
* avoid test conflict
Catch illegal combinations for all binary operators.
Memoize type conversion to reflect.type.
Add some unit tests for arithmetic and assign operations.
* feat: add support for named output variables
Function output parameters are located at the start of the function
frame, uninitialized, as they could be only set through return
statements.
Supporting named output variables requires to:
- identify and allocate symbols corresponding to output names:
done at ident parsing, with the funcRet() helper,
- compute the location of output name in the frame:
done with retRank() helper,
- initialize the frame entry with the zero value of symbol type,
as the symbol can be accessed and written at multiple times,
and return not setting the variable (opposite to unnamed).
Done with frameType() helper, which now takes into account
output parameters.
* refactor: simplify memory management
Perform function frame analysis at pre-order, instead of post-order, to
initialize memory frame, and track value types. Remove all operation
involving unitialized types. Frame memory layout is now build
incrementally, instead of having to perform dedicated tree walks on AST.
A Wrap represents the wrapper which allows objects created by the
interpreter to satify Go interfaces (despite limitations in reflect
which forbid dynamic method creation).
All the struct fields are functions, where the fied name corresponds
to the method name prefixed by "Do". The function signature must
be the same as the interface one.
A corresponding Wrap method Xyz which satisfies the interface must
exist and must invoke the DoXyz function.
To be usable, the interpreter should return a Wrap instance with
the relevant function fields filled. The application can then invoke
methods on it. The method calls will be forwarded to the interpreter.
Only the Wrap type definition needs to be exported to the interpreter
(not the interfaces and methods definitions)
A complete working example test is provided, and necessary corrections
in binary struct fields as functions.